From be10d44fea2c03d16ea07ae0bb6d8dedc971f364 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Fri, 24 Oct 2025 13:11:01 +0200 Subject: [PATCH 01/14] [NAE-2241] Public authorization method does not exist - Introduced `anonymousAuthenticationKey` in `SecurityConfigurationProperties` for better anonymous user identification. - Updated `PublicTaskController` to use `ActorTransformer.toLoggedUser` for cleaner authorization checks. - Enhanced `LoggedUserConfiguration` to initialize the default user factory with `ActorTransformer.setUserFactory`. - Refactored `TaskAuthorizationService` to replace manual user transformation with `ActorTransformer.toUser` for consistency. - Overhauled `PrivateKeyReader` to use `Resource` and Apache Commons IO for improved file handling. - Added `isAnonymous` method to `AbstractActor` for accurate anonymous user detection. - Updated `UserServiceImpl` to handle anonymous users correctly using `ActorTransformer`. These changes ensure better modularity, maintainability, and extend the functionality of authorization and user handling mechanisms. --- application-engine/pom.xml | 10 +++ .../auth/service/DefaultUserFactory.java | 17 +++++ .../engine/auth/web/PublicUserController.java | 30 +++++---- .../engine/auth/web/UserController.java | 2 +- .../LoggedUserConfiguration.java | 2 + .../NaeSecurityConfiguration.java | 23 +++---- .../SecurityConfigurationProperties.java | 3 + .../security/PublicAuthenticationFilter.java | 12 +++- .../security/jwt/JwtService.java | 21 ++++-- .../security/jwt/PrivateKeyReader.java | 11 ++-- .../petrinet/service/PetriNetService.java | 2 +- .../web/PublicPetriNetController.java | 13 ++-- .../service/TaskAuthorizationService.java | 14 ++-- .../service/WorkflowAuthorizationService.java | 12 ++-- .../workflow/web/PublicTaskController.java | 18 +++--- .../web/PublicWorkflowController.java | 3 +- .../src/main/resources/application.yaml | 2 +- .../objects/auth/domain/AbstractActor.java | 11 ++++ .../objects/auth/domain/ActorTransformer.java | 64 ++++++++++++++++--- .../engine/auth/service/UserServiceImpl.java | 9 ++- 20 files changed, 191 insertions(+), 88 deletions(-) create mode 100644 application-engine/src/main/java/com/netgrif/application/engine/auth/service/DefaultUserFactory.java diff --git a/application-engine/pom.xml b/application-engine/pom.xml index 2bd5e7c5a55..6f1a6e4b41e 100644 --- a/application-engine/pom.xml +++ b/application-engine/pom.xml @@ -560,6 +560,16 @@ jackson-module-jsonSchema ${jackson.version} + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + ${jackson.version} + + + com.fasterxml.jackson.datatype + jackson-datatype-jdk8 + ${jackson.version} + io.minio minio diff --git a/application-engine/src/main/java/com/netgrif/application/engine/auth/service/DefaultUserFactory.java b/application-engine/src/main/java/com/netgrif/application/engine/auth/service/DefaultUserFactory.java new file mode 100644 index 00000000000..4c83e13f98f --- /dev/null +++ b/application-engine/src/main/java/com/netgrif/application/engine/auth/service/DefaultUserFactory.java @@ -0,0 +1,17 @@ +package com.netgrif.application.engine.auth.service; + +import com.netgrif.application.engine.objects.auth.domain.AbstractUser; +import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; +import com.netgrif.application.engine.objects.auth.domain.User; +import org.springframework.stereotype.Component; + +@Component +public class DefaultUserFactory implements ActorTransformer.UserFactory { + + @Override + public AbstractUser create() { + User user = new User(); + return user; + } +} + diff --git a/application-engine/src/main/java/com/netgrif/application/engine/auth/web/PublicUserController.java b/application-engine/src/main/java/com/netgrif/application/engine/auth/web/PublicUserController.java index 6d472275a12..d52bd9a6bc5 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/auth/web/PublicUserController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/auth/web/PublicUserController.java @@ -1,12 +1,14 @@ package com.netgrif.application.engine.auth.web; import com.netgrif.application.engine.auth.service.PreferencesService; +import com.netgrif.application.engine.auth.service.UserFactory; import com.netgrif.application.engine.auth.service.UserService; import com.netgrif.application.engine.auth.web.requestbodies.PreferencesRequest; import com.netgrif.application.engine.auth.web.requestbodies.UserSearchRequestBody; import com.netgrif.application.engine.auth.web.responsebodies.PreferencesResource; import com.netgrif.application.engine.auth.web.responsebodies.User; import com.netgrif.application.engine.objects.auth.domain.AbstractUser; +import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; import com.netgrif.application.engine.objects.auth.domain.LoggedUser; import com.netgrif.application.engine.objects.preferences.Preferences; import com.netgrif.application.engine.objects.workflow.domain.ProcessResourceId; @@ -24,10 +26,11 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.*; import java.util.List; +import java.util.Locale; @Slf4j @RestController @@ -37,7 +40,7 @@ matchIfMissing = true ) @Tag(name = "Public User Controller") -@RequestMapping("/api/public/user") +@RequestMapping("/api/public/users") public class PublicUserController { @Autowired @@ -46,6 +49,9 @@ public class PublicUserController { @Autowired private PreferencesService preferencesService; + @Autowired + private UserFactory userFactory; + @Operation(summary = "Get logged user", description = "Retrieves information of currently logged user") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "User retrieved successfully"), @@ -53,11 +59,11 @@ public class PublicUserController { @ApiResponse(responseCode = "500", description = "Internal server error") }) @GetMapping(value = "/me", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity getLoggedUser(Authentication auth) { - LoggedUser loggedUser = (LoggedUser) auth.getPrincipal(); + public ResponseEntity getLoggedUser(Locale locale) { + LoggedUser loggedUser = (LoggedUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); AbstractUser user; try { - user = userService.findById(loggedUser.getStringId(), loggedUser.getRealmId()); + user = ActorTransformer.toUser(loggedUser); if (user == null) { return ResponseEntity .status(HttpStatus.UNAUTHORIZED).build(); @@ -67,7 +73,7 @@ public ResponseEntity getLoggedUser(Authentication auth) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).build(); } - return ResponseEntity.ok(User.createUser(user)); + return ResponseEntity.ok(userFactory.getUser(user, locale)); } @ApiResponses(value = { @@ -77,13 +83,13 @@ public ResponseEntity getLoggedUser(Authentication auth) { }) @Operation(summary = "Generic user search", security = {@SecurityRequirement(name = "X-Auth-Token")}) @PostMapping(value = "/search", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity> search(@RequestBody UserSearchRequestBody query, Pageable pageable, Authentication auth) { + public ResponseEntity> search(@RequestBody UserSearchRequestBody query, Pageable pageable) { List roles = query.getRoles() == null ? null : query.getRoles().stream().map(ProcessResourceId::new).toList(); List negativeRoles = query.getNegativeRoles() == null ? null : query.getNegativeRoles().stream().map(ProcessResourceId::new).toList(); Page users = userService.searchAllCoMembers(query.getFulltext(), roles, negativeRoles, - (LoggedUser) auth.getPrincipal(), pageable); + (LoggedUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(), pageable); return ResponseEntity.ok(changeToResponse(users, pageable)); } @@ -94,8 +100,8 @@ public ResponseEntity> search(@RequestBody UserSearchRequestBody quer @ApiResponse(responseCode = "500", description = "Internal server error") }) @GetMapping(value = "/preferences", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity preferences(Authentication auth) { - String userId = ((LoggedUser) auth.getPrincipal()).getStringId(); + public ResponseEntity preferences() { + String userId = ((LoggedUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getStringId(); Preferences preferences = preferencesService.get(userId); if (preferences == null) { @@ -115,9 +121,9 @@ public ResponseEntity preferences(Authentication auth) { @ApiResponse(responseCode = "500", description = "Internal server error") }) @PostMapping(value = "/preferences", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity savePreferences(@RequestBody PreferencesRequest preferences, Authentication auth) { + public ResponseEntity savePreferences(@RequestBody PreferencesRequest preferences) { try { - String userId = ((LoggedUser) auth.getPrincipal()).getStringId(); + String userId = ((LoggedUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getStringId(); preferences.setUserId(userId); preferencesService.save(preferences.toPreferences()); return ResponseEntity.ok("User preferences saved"); diff --git a/application-engine/src/main/java/com/netgrif/application/engine/auth/web/UserController.java b/application-engine/src/main/java/com/netgrif/application/engine/auth/web/UserController.java index e03c3f168ef..ba9a266040c 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/auth/web/UserController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/auth/web/UserController.java @@ -1,4 +1,4 @@ -package com.netgrif.application.engine.auth.web; + package com.netgrif.application.engine.auth.web; import com.netgrif.application.engine.adapter.spring.common.web.responsebodies.ResponseMessage; import com.netgrif.application.engine.adapter.spring.petrinet.service.ProcessRoleService; diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/LoggedUserConfiguration.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/LoggedUserConfiguration.java index 5b0d768a100..2d7bb556f54 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/LoggedUserConfiguration.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/LoggedUserConfiguration.java @@ -1,6 +1,7 @@ package com.netgrif.application.engine.configuration; import com.netgrif.application.engine.auth.service.DefaultLoggedUserFactory; +import com.netgrif.application.engine.auth.service.DefaultUserFactory; import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; import jakarta.annotation.PostConstruct; import org.springframework.context.annotation.Configuration; @@ -11,5 +12,6 @@ public class LoggedUserConfiguration { @PostConstruct public void initializeLoggedUserFactory() { ActorTransformer.setLoggedUserFactory(new DefaultLoggedUserFactory()); + ActorTransformer.setUserFactory(new DefaultUserFactory()); } } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/NaeSecurityConfiguration.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/NaeSecurityConfiguration.java index 17a5affe1af..693c2751d38 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/NaeSecurityConfiguration.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/NaeSecurityConfiguration.java @@ -1,7 +1,6 @@ package com.netgrif.application.engine.configuration; import com.netgrif.application.engine.configuration.properties.SecurityConfigurationProperties; -import com.netgrif.application.engine.objects.auth.domain.Authority; import com.netgrif.application.engine.auth.service.AuthorityService; import com.netgrif.application.engine.auth.service.UserService; import com.netgrif.application.engine.configuration.security.ImpersonationRequestFilter; @@ -37,7 +36,6 @@ import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.ForwardedHeaderFilter; -import java.util.HashSet; import java.util.List; import static org.springframework.http.HttpMethod.OPTIONS; @@ -68,9 +66,6 @@ public class NaeSecurityConfiguration extends AbstractSecurityConfiguration { @Autowired private SecurityConfigurationProperties securityConfigurationProperties; - @Autowired - private SecurityConfigurationProperties properties; - @Autowired private ISecurityContextService securityContextService; @@ -83,11 +78,11 @@ public class NaeSecurityConfiguration extends AbstractSecurityConfiguration { @Autowired private AuthenticationManagerBuilder authenticationManagerBuilder; - private static final String ANONYMOUS_USER = "anonymousUser"; + @Bean public CorsConfigurationSource corsConfigurationSource() { - List allowedOrigins = properties.getAllowedOrigins(); + List allowedOrigins = securityConfigurationProperties.getAllowedOrigins(); CorsConfiguration config = new CorsConfiguration().applyPermitDefaultValues(); config.addAllowedMethod("*"); @@ -145,12 +140,12 @@ protected boolean isOpenRegistration() { @Override protected boolean isCsrfEnabled() { - return properties.isCsrf(); + return securityConfigurationProperties.isCsrf(); } @Override protected boolean isCorsEnabled() { - return properties.isCors(); + return securityConfigurationProperties.isCors(); } @Override @@ -170,19 +165,19 @@ protected Environment getEnvironment() { @Override protected SecurityConfigurationProperties getSecurityConfigProperties() { - return properties; + return securityConfigurationProperties; } protected PublicAuthenticationFilter createPublicAuthenticationFilter() throws Exception { - Authority authority = authorityService.getOrCreate(Authority.anonymous); return new PublicAuthenticationFilter( (ProviderManager) authenticationManager(authenticationManagerBuilder), - new AnonymousAuthenticationProvider(ANONYMOUS_USER), + new AnonymousAuthenticationProvider(securityConfigurationProperties.getAnonymousAuthenticationKey()), securityConfigurationProperties.getServerPatterns(), securityConfigurationProperties.getAnonymousExceptions(), jwtService, userService, - authorityService + authorityService, + securityConfigurationProperties ); } @@ -191,7 +186,7 @@ private SecurityContextFilter createSecurityContextFilter() { } private HostValidationRequestFilter hostValidationRequestFilter() { - return new HostValidationRequestFilter(properties); + return new HostValidationRequestFilter(securityConfigurationProperties); } private ImpersonationRequestFilter impersonationRequestFilter() { diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/SecurityConfigurationProperties.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/SecurityConfigurationProperties.java index a18f449ea96..393514c82ef 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/SecurityConfigurationProperties.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/SecurityConfigurationProperties.java @@ -68,6 +68,9 @@ public class SecurityConfigurationProperties { */ private String[] anonymousExceptions; + + private String anonymousAuthenticationKey = "anonymousUser"; + /** * Headers settings */ diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/PublicAuthenticationFilter.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/PublicAuthenticationFilter.java index 02791ccc93d..89e2bbe9254 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/PublicAuthenticationFilter.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/PublicAuthenticationFilter.java @@ -1,6 +1,7 @@ package com.netgrif.application.engine.configuration.security; import com.netgrif.application.engine.adapter.spring.auth.domain.AuthorityImpl; +import com.netgrif.application.engine.configuration.properties.SecurityConfigurationProperties; import com.netgrif.application.engine.objects.auth.domain.*; import com.netgrif.application.engine.auth.service.AuthorityService; import com.netgrif.application.engine.auth.service.UserService; @@ -11,7 +12,6 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; -import org.bson.types.ObjectId; import org.springframework.security.authentication.AnonymousAuthenticationProvider; import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.authentication.AuthenticationDetailsSource; @@ -39,10 +39,12 @@ public class PublicAuthenticationFilter extends OncePerRequestFilter { private final IJwtService jwtService; private final UserService userService; private final AuthorityService authorityService; + private final SecurityConfigurationProperties securityConfigurationProperties; public PublicAuthenticationFilter(ProviderManager authenticationManager, AnonymousAuthenticationProvider provider, String[] urls, String[] exceptions, IJwtService jwtService, - UserService userService, AuthorityService authorityService) { + UserService userService, AuthorityService authorityService, + SecurityConfigurationProperties securityConfigurationProperties) { this.authenticationManager = authenticationManager; this.authenticationManager.getProviders().add(provider); this.anonymousAccessUrls = urls; @@ -50,6 +52,7 @@ public PublicAuthenticationFilter(ProviderManager authenticationManager, Anonymo this.jwtService = jwtService; this.userService = userService; this.authorityService = authorityService; + this.securityConfigurationProperties = securityConfigurationProperties; } @Override @@ -65,7 +68,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse private void authenticate(HttpServletRequest request, String jwtToken) { AnonymousAuthenticationToken authRequest = new AnonymousAuthenticationToken( - "anonymous-user", + securityConfigurationProperties.getAnonymousAuthenticationKey(), jwtService.getLoggedUser(jwtToken, Authority.anonymous), Collections.singleton((AuthorityImpl) authorityService.getOrCreate(Authority.anonymous)) ); @@ -106,6 +109,9 @@ private LoggedUser resolveLoggedUser(String existingToken) { private LoggedUser createAnonymousUser() { User anonymousUser = new User(); + anonymousUser.setUsername("anonymous_" + anonymousUser.getStringId()); + anonymousUser.setFirstName("Anonymous"); + anonymousUser.setLastName("User"); anonymousUser.setState(UserState.ACTIVE); anonymousUser = (User) userService.saveUser(anonymousUser, null); return ActorTransformer.toLoggedUser(anonymousUser); diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/jwt/JwtService.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/jwt/JwtService.java index 693a719e88a..e81c7e2ab7d 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/jwt/JwtService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/jwt/JwtService.java @@ -1,5 +1,6 @@ package com.netgrif.application.engine.configuration.security.jwt; +import com.fasterxml.jackson.databind.ObjectMapper; import com.netgrif.application.engine.adapter.spring.auth.domain.LoggedUserImpl; import com.netgrif.application.engine.configuration.properties.SecurityConfigurationProperties; import com.netgrif.application.engine.objects.auth.domain.Attribute; @@ -10,6 +11,7 @@ import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.jackson.io.JacksonSerializer; import io.jsonwebtoken.security.Keys; import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; @@ -34,12 +36,14 @@ public class JwtService implements IJwtService { private final SecurityConfigurationProperties.JwtProperties properties; private final ProcessRoleService roleService; private final AuthorityService authorityService; + private ObjectMapper objectMapper; @PostConstruct private void resolveSecret() { + configureObjectMapper(); try { PrivateKeyReader reader = new PrivateKeyReader(properties.getAlgorithm()); - secret = reader.get(properties.getPrivateKey().getFile().getPath()).getEncoded(); + secret = reader.get(properties.getPrivateKey()).getEncoded(); } catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException e) { log.error("Error while resolving secret key: " + e.getMessage(), e); } @@ -56,6 +60,7 @@ public String tokenFrom(Map header, String subject, Map(objectMapper)) .compact(); } @@ -73,12 +78,13 @@ public LoggedUser getLoggedUser(String token, String authority) { LinkedHashMap userMap = (LinkedHashMap) extractAllClaims(token).get("user"); LoggedUser user = new LoggedUserImpl(); - user.setId(userMap.get("id").toString()); - user.setUsername(userMap.get("username").toString()); - user.setPassword(userMap.get("password").toString()); + user.setId((String) userMap.get("stringId")); + user.setUsername((String) userMap.get("username")); + user.setFirstName((String) userMap.get("firstName")); + user.setMiddleName((String) userMap.get("middleName")); + user.setLastName((String) userMap.get("lastName")); user.setAuthoritySet(Collections.singleton(authorityService.getOrCreate(authority))); user.setProcessRoles(Collections.singleton(roleService.getAnonymousRole())); - user.setFirstName(userMap.get("firstName").toString()); user.getAttributes().put("anonymous", new Attribute<>(true, false)); return user; @@ -105,4 +111,9 @@ private Claims extractAllClaims(String token) throws ExpiredJwtException { private Key getSignInKey() { return Keys.hmacShaKeyFor(secret); } + + private void configureObjectMapper() { + objectMapper = new ObjectMapper(); + objectMapper.findAndRegisterModules(); + } } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/jwt/PrivateKeyReader.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/jwt/PrivateKeyReader.java index e864e7e61f0..b4b570f76ea 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/jwt/PrivateKeyReader.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/jwt/PrivateKeyReader.java @@ -1,8 +1,10 @@ package com.netgrif.application.engine.configuration.security.jwt; +import org.apache.commons.io.IOUtils; +import org.springframework.core.io.Resource; + +import java.io.FileInputStream; import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; @@ -17,8 +19,9 @@ public PrivateKeyReader(String algorithm) throws NoSuchAlgorithmException { keyFactory = KeyFactory.getInstance(algorithm); } - public PrivateKey get(String filename) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException { - byte[] keyBytes = Files.readAllBytes(Paths.get(filename)); + public PrivateKey get(Resource resource) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException { + FileInputStream fileInputStream = new FileInputStream(resource.getFile()); + byte[] keyBytes = IOUtils.toByteArray(fileInputStream); PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes); return keyFactory.generatePrivate(spec); } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java index a4684616368..51979c21258 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java @@ -559,7 +559,7 @@ public void deletePetriNet(String processId, LoggedUser loggedUser, boolean forc private Criteria getProcessRolesCriteria(LoggedUser user) { return new Criteria().orOperator(user.getProcessRoles().stream() - .map(role -> Criteria.where("permissions." + role).exists(true)).toArray(Criteria[]::new)); + .map(role -> Criteria.where("permissions." + role.getStringId()).exists(true)).toArray(Criteria[]::new)); } @Override diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PublicPetriNetController.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PublicPetriNetController.java index ab2ce973c53..63d9c8b2c8c 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PublicPetriNetController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PublicPetriNetController.java @@ -14,13 +14,12 @@ import org.apache.commons.codec.binary.Base64; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.data.web.PagedResourcesAssembler; -import org.springframework.hateoas.Link; import org.springframework.hateoas.MediaTypes; -import org.springframework.hateoas.PagedModel; -import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.List; @@ -70,13 +69,9 @@ public PetriNetReferenceResource getOne(@PathVariable("identifier") String ident @Operation(summary = "Search processes") @PostMapping(value = "/search", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaTypes.HAL_JSON_VALUE) - public PagedModel searchPetriNets(@RequestBody PetriNetSearch criteria, Pageable pageable, PagedResourcesAssembler assembler, Locale locale) { + public ResponseEntity> searchPetriNets(@RequestBody PetriNetSearch criteria, Pageable pageable, PagedResourcesAssembler assembler, Locale locale) { Page nets = petriNetService.search(criteria, ActorTransformer.toLoggedUser(userService.getLoggedUser()), pageable, locale); -// Link selfLink = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(PublicPetriNetController.class) -// .searchPetriNets(criteria, pageable, assembler, locale)).withRel("search"); -// PagedModel resources = assembler.toModel(nets, new PetriNetReferenceResourceAssembler(), selfLink); -// PetriNetReferenceResourceAssembler.buildLinks(resources); - return PagedModel.of(nets.stream().map(PetriNetReferenceResource::new).toList(), new PagedModel.PageMetadata(pageable.getPageSize(), pageable.getPageNumber(), nets.getTotalElements())); + return ResponseEntity.ok(new PageImpl<>(nets.stream().map(PetriNetReferenceResource::new).toList(), pageable, nets.getTotalElements())); } @Operation(summary = "Get roles of process") diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/TaskAuthorizationService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/TaskAuthorizationService.java index 3ed448f14d1..83c1a6b57f8 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/TaskAuthorizationService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/TaskAuthorizationService.java @@ -1,8 +1,7 @@ package com.netgrif.application.engine.workflow.service; -import com.netgrif.application.engine.adapter.spring.auth.domain.LoggedUserImpl; -import com.netgrif.application.engine.auth.service.UserService; import com.netgrif.application.engine.objects.auth.domain.AbstractUser; +import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; import com.netgrif.application.engine.objects.auth.domain.LoggedUser; import com.netgrif.application.engine.objects.petrinet.domain.roles.RolePermission; import com.netgrif.application.engine.petrinet.domain.throwable.IllegalTaskStateException; @@ -21,12 +20,9 @@ public class TaskAuthorizationService extends AbstractAuthorizationService imple @Autowired ITaskService taskService; - @Autowired - UserService userService; - @Override public Boolean userHasAtLeastOneRolePermission(LoggedUser loggedUser, String taskId, RolePermission... permissions) { - return userHasAtLeastOneRolePermission(userService.transformToUser((LoggedUserImpl) loggedUser), taskService.findById(taskId), permissions); + return userHasAtLeastOneRolePermission(ActorTransformer.toUser(loggedUser), taskService.findById(taskId), permissions); } @Override @@ -47,7 +43,7 @@ public Boolean userHasAtLeastOneRolePermission(AbstractUser user, Task task, Rol @Override public Boolean userHasUserListPermission(LoggedUser loggedUser, String taskId, RolePermission... permissions) { - return userHasUserListPermission(userService.transformToUser((LoggedUserImpl) loggedUser), taskService.findById(taskId), permissions); + return userHasUserListPermission(ActorTransformer.toUser(loggedUser), taskService.findById(taskId), permissions); } @Override @@ -76,7 +72,7 @@ public Boolean userHasUserListPermission(AbstractUser user, Task task, RolePermi @Override public boolean isAssignee(LoggedUser loggedUser, String taskId) { - return isAssignee(userService.transformToUser((LoggedUserImpl) loggedUser), taskService.findById(taskId)); + return isAssignee(ActorTransformer.toUser(loggedUser), taskService.findById(taskId)); } @Override @@ -151,7 +147,7 @@ public boolean canCallCancel(LoggedUser loggedUser, String taskId) throws Illega Boolean userPerm = userHasUserListPermission(loggedUser, taskId, RolePermission.CANCEL); // TODO: impersonation // return loggedUser.getSelfOrImpersonated().isAdmin() || ((userPerm == null ? (rolePerm != null && rolePerm) : userPerm) && isAssignee(loggedUser, taskId)) && canAssignedCancel(userService.transformToUser((LoggedUserImpl) loggedUser), taskId); - return loggedUser.isAdmin() || ((userPerm == null ? (rolePerm != null && rolePerm) : userPerm) && isAssignee(loggedUser, taskId)) && canAssignedCancel(userService.transformToUser((LoggedUserImpl) loggedUser), taskId); + return loggedUser.isAdmin() || ((userPerm == null ? (rolePerm != null && rolePerm) : userPerm) && isAssignee(loggedUser, taskId)) && canAssignedCancel(ActorTransformer.toUser(loggedUser), taskId); } @Override diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowAuthorizationService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowAuthorizationService.java index e4438d1b863..0a37f2ff0fd 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowAuthorizationService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowAuthorizationService.java @@ -1,8 +1,7 @@ package com.netgrif.application.engine.workflow.service; -import com.netgrif.application.engine.adapter.spring.auth.domain.LoggedUserImpl; -import com.netgrif.application.engine.auth.service.UserService; import com.netgrif.application.engine.objects.auth.domain.AbstractUser; +import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; import com.netgrif.application.engine.objects.auth.domain.LoggedUser; import com.netgrif.application.engine.objects.petrinet.domain.PetriNet; import com.netgrif.application.engine.objects.petrinet.domain.roles.ProcessRolePermission; @@ -25,9 +24,6 @@ public class WorkflowAuthorizationService extends AbstractAuthorizationService i @Autowired private IPetriNetService petriNetService; - @Autowired - private UserService userService; - @Override public boolean canCallDelete(LoggedUser user, String caseId) { Case requestedCase = workflowService.findOne(caseId); @@ -35,8 +31,8 @@ public boolean canCallDelete(LoggedUser user, String caseId) { // Boolean rolePerm = userHasAtLeastOneRolePermission(userService.transformToUser((LoggedUserImpl) user.getSelfOrImpersonated()), requestedCase.getPetriNet(), ProcessRolePermission.DELETE); // Boolean userPerm = userHasUserListPermission(userService.transformToUser((LoggedUserImpl) user.getSelfOrImpersonated()), requestedCase, ProcessRolePermission.DELETE); // return user.getSelfOrImpersonated().isAdmin() || (userPerm == null ? (rolePerm != null && rolePerm) : userPerm); - Boolean rolePerm = userHasAtLeastOneRolePermission(userService.transformToUser(user), requestedCase.getPetriNet(), ProcessRolePermission.DELETE); - Boolean userPerm = userHasUserListPermission(userService.transformToUser(user), requestedCase, ProcessRolePermission.DELETE); + Boolean rolePerm = userHasAtLeastOneRolePermission(ActorTransformer.toUser(user), requestedCase.getPetriNet(), ProcessRolePermission.DELETE); + Boolean userPerm = userHasUserListPermission(ActorTransformer.toUser(user), requestedCase, ProcessRolePermission.DELETE); return user.isAdmin() || (userPerm == null ? (rolePerm != null && rolePerm) : userPerm); } @@ -44,7 +40,7 @@ public boolean canCallDelete(LoggedUser user, String caseId) { public boolean canCallCreate(LoggedUser user, String netId) { PetriNet net = petriNetService.getPetriNet(netId); // TODO: impersonation - return user.isAdmin() || userHasAtLeastOneRolePermission(userService.transformToUser(user), net, ProcessRolePermission.CREATE); + return user.isAdmin() || userHasAtLeastOneRolePermission(ActorTransformer.toUser(user), net, ProcessRolePermission.CREATE); } @Override diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/PublicTaskController.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/PublicTaskController.java index e6623f3dd9f..aa32ca242d7 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/PublicTaskController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/PublicTaskController.java @@ -50,14 +50,12 @@ public class PublicTaskController extends AbstractTaskController { final UserService userService; private final ITaskService taskService; - private final IDataService dataService; public PublicTaskController(ITaskService taskService, IDataService dataService, UserService userService) { super(taskService, dataService, null, userService); this.taskService = taskService; - this.dataService = dataService; this.userService = userService; } @@ -68,7 +66,7 @@ public List getTasksOfCase(@PathVariable("id") String caseId, Loc return this.taskService.findAllByCase(caseId, locale); } - @PreAuthorize("@taskAuthorizationService.canCallAssign(@userService.getAnonymousLogged(), #taskId)") + @PreAuthorize("@taskAuthorizationService.canCallAssign(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") @GetMapping(value = "/assign/{id}", produces = MediaTypes.HAL_JSON_VALUE) @Operation(summary = "Assign task", description = "Caller must be able to perform the task, or must be an ADMIN") @ApiResponses({@ApiResponse( @@ -83,7 +81,7 @@ public EntityModel assign(@PathVariable("id") String ta return super.assign(loggedUser, taskId, locale); } - @PreAuthorize("@taskAuthorizationService.canCallFinish(@userService.getAnonymousLogged(), #taskId)") + @PreAuthorize("@taskAuthorizationService.canCallFinish(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") @GetMapping(value = "/finish/{id}", produces = MediaTypes.HAL_JSON_VALUE) @Operation(summary = "Finish task", description = "Caller must be assigned to the task, or must be an ADMIN") @ApiResponses({@ApiResponse( @@ -98,7 +96,7 @@ public EntityModel finish(@PathVariable("id") String ta return super.finish(loggedUser, taskId, locale); } - @PreAuthorize("@taskAuthorizationService.canCallCancel(@userService.getAnonymousLogged(), #taskId)") + @PreAuthorize("@taskAuthorizationService.canCallCancel(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") @GetMapping(value = "/cancel/{id}", produces = MediaTypes.HAL_JSON_VALUE) @Operation(summary = "Cancel task", description = "Caller must be assigned to the task, or must be an ADMIN") @ApiResponses({@ApiResponse( @@ -121,7 +119,7 @@ public EntityModel getData(@PathVariable("id") String t } @Override - @PreAuthorize("@taskAuthorizationService.canCallSaveData(@userService.getAnonymousLogged(), #taskId)") + @PreAuthorize("@taskAuthorizationService.canCallSaveData(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") @PostMapping(value = "/{id}/data", consumes = "application/json;charset=UTF-8", produces = "application/json;charset=UTF-8") @Operation(summary = "Set task data", description = "Caller must be assigned to the task, or must be an ADMIN") @ApiResponses({@ApiResponse( @@ -135,7 +133,7 @@ public EntityModel setData(@PathVariable("id") String t return super.setData(taskId, dataBody, locale); } - @PreAuthorize("@taskAuthorizationService.canCallSaveFile(@userService.getAnonymousLogged(), #taskId)") + @PreAuthorize("@taskAuthorizationService.canCallSaveFile(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") @Operation(summary = "Upload file into the task", description = "Caller must be assigned to the task, or must be an ADMIN") @PostMapping(value = "/{id}/file", produces = MediaTypes.HAL_JSON_VALUE) @@ -154,7 +152,7 @@ public ResponseEntity getFile(@PathVariable("id") String taskId, @Requ return super.getFile(taskId, fieldId); } - @PreAuthorize("@taskAuthorizationService.canCallSaveFile(@userService.getAnonymousLogged(), #taskId)") + @PreAuthorize("@taskAuthorizationService.canCallSaveFile(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") @Operation(summary = "Remove file from the task", description = "Caller must be assigned to the task, or must be an ADMIN") @DeleteMapping(value = "/{id}/file", produces = MediaTypes.HAL_JSON_VALUE) @@ -174,7 +172,7 @@ public ResponseEntity getFilePreview(@PathVariable("id") String taskId } @Override - @PreAuthorize("@taskAuthorizationService.canCallSaveFile(@userService.getAnonymousLogged(), #taskId)") + @PreAuthorize("@taskAuthorizationService.canCallSaveFile(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") @Operation(summary = "Upload multiple files into the task", description = "Caller must be assigned to the task, or must be an ADMIN") @PostMapping(value = "/{id}/files", produces = MediaTypes.HAL_JSON_VALUE) @@ -193,7 +191,7 @@ public ResponseEntity getNamedFile(@PathVariable("id") String taskId, return super.getNamedFile(taskId, fieldId, fileName); } - @PreAuthorize("@taskAuthorizationService.canCallSaveFile(@userService.getAnonymousLogged(), #taskId)") + @PreAuthorize("@taskAuthorizationService.canCallSaveFile(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") @Operation(summary = "Remove file from tasks file list field value", description = "Caller must be assigned to the task, or must be an ADMIN") @DeleteMapping(value = "/{id}/file/named", produces = MediaTypes.HAL_JSON_VALUE) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/PublicWorkflowController.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/PublicWorkflowController.java index 0683e405e4b..2d2fc420145 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/PublicWorkflowController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/PublicWorkflowController.java @@ -21,6 +21,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; + import java.util.Locale; @Slf4j @@ -43,7 +44,7 @@ public PublicWorkflowController(IWorkflowService workflowService, UserService us this.workflowService = workflowService; } - @PreAuthorize("@workflowAuthorizationService.canCallCreate(@userService.getAnonymousLogged(), #body.netId)") + @PreAuthorize("@workflowAuthorizationService.canCallCreate(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #body.netId)") @PostMapping(value = "/case", consumes = "application/json;charset=UTF-8", produces = MediaTypes.HAL_JSON_VALUE) @Operation(summary = "Create new case") public EntityModel createCase(@RequestBody CreateCaseBody body, Locale locale) { diff --git a/application-engine/src/main/resources/application.yaml b/application-engine/src/main/resources/application.yaml index 876ae43853c..c82a3b10289 100644 --- a/application-engine/src/main/resources/application.yaml +++ b/application-engine/src/main/resources/application.yaml @@ -83,7 +83,7 @@ netgrif: jwt: expiration: 900000 algorithm: RSA - private-key: file:src/main/resources/certificates/private.der + private-key: classpath:certificates/private.der server-patterns: /api/auth/signup,/api/auth/token/verify,/api/auth/reset,/api/auth/recover,/api/public/**,/v3/api-docs/public,/manage/** anonymous-exceptions: /api/auth/signup,/api/auth/token/verify,/api/auth/reset,/api/auth/recover,/manage/** providers: NetgrifBasicAuthenticationProvider diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/domain/AbstractActor.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/domain/AbstractActor.java index 2b844d7d0e4..fc171e38c66 100644 --- a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/domain/AbstractActor.java +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/domain/AbstractActor.java @@ -308,4 +308,15 @@ public void removeGroupId(String groupId) { public boolean isAdmin() { return this.authoritySet.stream().anyMatch(it -> it.getName().equals(Authority.admin)); } + + + /** + * Checks if the actor has anonymous authority. + * This indicates whether the actor is granted permissions for anonymous access. + * + * @return true if the actor has anonymous authority, false otherwise + */ + public boolean isAnonymous() { + return authoritySet.stream().anyMatch(it -> it.getName().equals(Authority.anonymous)); + } } \ No newline at end of file diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/domain/ActorTransformer.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/domain/ActorTransformer.java index d1d69778a64..345c4e7f627 100644 --- a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/domain/ActorTransformer.java +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/domain/ActorTransformer.java @@ -1,5 +1,7 @@ package com.netgrif.application.engine.objects.auth.domain; +import lombok.Setter; + import java.time.Duration; /** @@ -12,11 +14,34 @@ public class ActorTransformer { /** * Factory for creating new LoggedUser instances. * Default implementation throws IllegalStateException if no factory is configured. + * The lambda expression provides a no-op factory that throws an exception to indicate + * that no valid LoggedUserFactory has been set. + *

+ * -- SETTER -- + * Sets the factory used for creating LoggedUser instances. + * + * @param f the LoggedUserFactory implementation to be used */ - private static LoggedUserFactory factory = () -> { + @Setter + private static LoggedUserFactory loggedUserFactory = () -> { throw new IllegalStateException("No LoggedUserFactory configured"); }; + + /** + * Factory for creating new AbstractUser instances. + * Default implementation throws IllegalStateException if no factory is configured. + * The lambda expression provides a no-op factory that throws an exception to indicate + * that no valid UserFactory has been set. + *

+ * -- SETTER -- + * Sets the factory used for creating AbstractUser instances. + **/ + @Setter + private static UserFactory userFactory = () -> { + throw new IllegalStateException("No UserFactory configured"); + }; + /** * Functional interface defining a factory method for creating new LoggedUser instances. */ @@ -29,12 +54,13 @@ public interface LoggedUserFactory { LoggedUser create(); } - /** - * Sets the factory used for creating LoggedUser instances. - * @param f the LoggedUserFactory implementation to be used - */ - public static void setLoggedUserFactory(LoggedUserFactory f) { - factory = f; + @FunctionalInterface + public interface UserFactory { + /** + * Creates a new AbstractUser instance. + * @return newly created AbstractUser instance + */ + AbstractUser create(); } /** @@ -43,7 +69,7 @@ public static void setLoggedUserFactory(LoggedUserFactory f) { * @return new LoggedUser instance with copied user data */ public static LoggedUser toLoggedUser(AbstractUser user) { - LoggedUser loggedUser = factory.create(); + LoggedUser loggedUser = loggedUserFactory.create(); loggedUser.setId(user.getStringId()); loggedUser.setRealmId(user.getRealmId()); loggedUser.setUsername(user.getUsername()); @@ -58,6 +84,28 @@ public static LoggedUser toLoggedUser(AbstractUser user) { return loggedUser; } + /** + * Converts a LoggedUser into an AbstractUser by copying all relevant user information. + * + * @param loggedUser the LoggedUser to transform + * @return a new AbstractUser instance with copied user data + */ + public static AbstractUser toUser(LoggedUser loggedUser) { + User user = (User) userFactory.create(); + user.setId(loggedUser.getStringId()); + user.setRealmId(loggedUser.getRealmId()); + user.setUsername(loggedUser.getUsername()); + user.setEmail(loggedUser.getEmail()); + user.setFirstName(loggedUser.getFirstName()); + user.setMiddleName(loggedUser.getMiddleName()); + user.setLastName(loggedUser.getLastName()); + user.setAuthoritySet(loggedUser.getAuthoritySet()); + user.setProcessRoles(loggedUser.getProcessRoles()); + user.setAttributes(loggedUser.getAttributes()); + user.setGroupIds(loggedUser.getGroupIds()); + return user; + } + /** * Transforms an AbstractUser into a LoggedUser with additional session-related information. * @param user the AbstractUser to transform diff --git a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/UserServiceImpl.java b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/UserServiceImpl.java index 9820e6296b5..422f663566b 100644 --- a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/UserServiceImpl.java +++ b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/UserServiceImpl.java @@ -468,8 +468,13 @@ public AbstractUser getLoggedOrSystem() { @Override public AbstractUser getLoggedUser() { LoggedUser loggedUser = getLoggedUserFromContext(); - Optional userOptional = findUserByUsername(loggedUser.getUsername(), loggedUser.getRealmId()); - AbstractUser user = userOptional.orElseThrow(() -> new IllegalArgumentException("User with username [%s] in realm [%s] is not present in the system.".formatted(loggedUser.getUsername(), loggedUser.getRealmId()))); + AbstractUser user; + if (loggedUser.isAnonymous()) { + user = ActorTransformer.toUser(loggedUser); + } else { + Optional userOptional = findUserByUsername(loggedUser.getUsername(), loggedUser.getRealmId()); + user = userOptional.orElseThrow(() -> new IllegalArgumentException("User with username [%s] in realm [%s] is not present in the system.".formatted(loggedUser.getUsername(), loggedUser.getRealmId()))); + } // TODO: impersonation // if (loggedUser.isImpersonating()) { // IUser impersonated = transformToUser((LoggedUserImpl) loggedUser.getImpersonated()); From 83606bc6f72981f7792e241f5ac6c5233aab7a72 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Wed, 29 Oct 2025 12:57:35 +0100 Subject: [PATCH 02/14] Add filters and enhancements for HTTP request processing Introduced `NetgrifHttpRequestTransformFilter` to wrap HTTP requests, enabling custom request handling. Further, added `NetgrifOncePerRequestFilter` as a base proxy filter and improved user authentication with `PublicAuthenticationFilter`. Updated various services to enable anonymous user support and public API access. --- .../engine/auth/web/UserController.java | 40 +++- .../NaeSecurityConfiguration.java | 38 +--- .../SecurityConfigurationProperties.java | 2 + .../security/PublicAuthenticationFilter.java | 171 ++++++++---------- .../startup/runner/DefaultRealmRunner.java | 8 +- .../engine/workflow/service/TaskService.java | 2 +- .../src/main/resources/application-dev.yaml | 2 +- .../src/main/resources/application.yaml | 3 +- nae-spring-core-adapter/pom.xml | 16 ++ .../spring/auth/domain/AnonymousUserRef.java | 6 + .../NetgrifHttpRequestTransformFilter.java | 39 ++++ .../filters/NetgrifOncePerRequestFilter.java | 55 ++++++ .../requests/NetgrifHttpServletRequest.java | 29 +++ .../service/AnonymousUserRefServiceImpl.java | 11 +- .../engine/auth/service/RealmServiceImpl.java | 1 + 15 files changed, 280 insertions(+), 143 deletions(-) create mode 100644 nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/configuration/filters/NetgrifHttpRequestTransformFilter.java create mode 100644 nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/configuration/filters/NetgrifOncePerRequestFilter.java create mode 100644 nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/configuration/filters/requests/NetgrifHttpServletRequest.java diff --git a/application-engine/src/main/java/com/netgrif/application/engine/auth/web/UserController.java b/application-engine/src/main/java/com/netgrif/application/engine/auth/web/UserController.java index ba9a266040c..5c2ae27ab03 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/auth/web/UserController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/auth/web/UserController.java @@ -8,10 +8,7 @@ import com.netgrif.application.engine.auth.web.requestbodies.UserSearchRequestBody; import com.netgrif.application.engine.auth.web.responsebodies.PreferencesResource; import com.netgrif.application.engine.auth.web.responsebodies.User; -import com.netgrif.application.engine.objects.auth.domain.AbstractUser; -import com.netgrif.application.engine.objects.auth.domain.Authority; -import com.netgrif.application.engine.objects.auth.domain.LoggedUser; -import com.netgrif.application.engine.objects.auth.domain.Realm; +import com.netgrif.application.engine.objects.auth.domain.*; import com.netgrif.application.engine.objects.preferences.Preferences; import com.netgrif.application.engine.objects.workflow.domain.ProcessResourceId; import io.swagger.v3.oas.annotations.Operation; @@ -30,6 +27,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.*; import java.util.*; @@ -112,10 +110,10 @@ public ResponseEntity> getAllUsers(@PathVariable String realmId, Page }) @GetMapping(value = "/me", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity getLoggedUser(Authentication auth, Locale locale) { - LoggedUser loggedUser = (LoggedUser) auth.getPrincipal(); + LoggedUser loggedUser = resolveAuthenticationToken(auth); AbstractUser user; try { - user = userService.findById(loggedUser.getStringId(), loggedUser.getRealmId()); + user = resolveLoggedUser(loggedUser); if (user == null) { return ResponseEntity .status(HttpStatus.UNAUTHORIZED).build(); @@ -294,11 +292,14 @@ public ResponseEntity assignAuthorityToUser(@PathVariable("real }) @GetMapping(value = "/preferences", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity preferences(Authentication auth) { - String userId = ((LoggedUser) auth.getPrincipal()).getStringId(); - Preferences preferences = preferencesService.get(userId); + LoggedUser loggedUser = resolveAuthenticationToken(auth); + if (loggedUser == null) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); + } + Preferences preferences = preferencesService.get(loggedUser.getStringId()); if (preferences == null) { - preferences = new com.netgrif.application.engine.adapter.spring.preferences.Preferences(userId); + preferences = new com.netgrif.application.engine.adapter.spring.preferences.Preferences(loggedUser.getStringId()); } PreferencesResource preferencesResource = PreferencesResource.withPreferences(preferences); @@ -337,4 +338,25 @@ private boolean realmExists(String realmId) { Optional realm = realmService.getRealmById(realmId); return realm.isPresent(); } + + private LoggedUser resolveAuthenticationToken(Authentication auth) { + if (auth != null) { + return (LoggedUser) auth.getPrincipal(); + } + auth = SecurityContextHolder.getContext().getAuthentication(); + if (auth != null) { + return (LoggedUser) auth.getPrincipal(); + } + return null; + } + + private AbstractUser resolveLoggedUser(LoggedUser loggedUser) { + if (loggedUser == null) { + return null; + } + if (loggedUser.isAnonymous()) { + return ActorTransformer.toUser(loggedUser); + } + return userService.findById(loggedUser.getStringId(), loggedUser.getRealmId()); + } } \ No newline at end of file diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/NaeSecurityConfiguration.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/NaeSecurityConfiguration.java index 693c2751d38..7ddf3401538 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/NaeSecurityConfiguration.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/NaeSecurityConfiguration.java @@ -1,14 +1,12 @@ package com.netgrif.application.engine.configuration; +import com.netgrif.application.engine.adapter.spring.configuration.filters.NetgrifHttpRequestTransformFilter; import com.netgrif.application.engine.configuration.properties.SecurityConfigurationProperties; -import com.netgrif.application.engine.auth.service.AuthorityService; -import com.netgrif.application.engine.auth.service.UserService; import com.netgrif.application.engine.configuration.security.ImpersonationRequestFilter; import com.netgrif.application.engine.configuration.security.PublicAuthenticationFilter; import com.netgrif.application.engine.configuration.security.RestAuthenticationEntryPoint; import com.netgrif.application.engine.configuration.security.SecurityContextFilter; import com.netgrif.application.engine.configuration.security.filter.HostValidationRequestFilter; -import com.netgrif.application.engine.configuration.security.jwt.IJwtService; import com.netgrif.application.engine.impersonation.service.interfaces.IImpersonationService; import com.netgrif.application.engine.security.service.ISecurityContextService; import lombok.extern.slf4j.Slf4j; @@ -19,10 +17,7 @@ import org.springframework.core.annotation.Order; import org.springframework.core.env.Environment; import org.springframework.http.HttpStatus; -import org.springframework.security.authentication.AnonymousAuthenticationProvider; import org.springframework.security.authentication.AuthenticationProvider; -import org.springframework.security.authentication.ProviderManager; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; @@ -54,15 +49,6 @@ public class NaeSecurityConfiguration extends AbstractSecurityConfiguration { @Autowired private RestAuthenticationEntryPoint authenticationEntryPoint; - @Autowired - private AuthorityService authorityService; - - @Autowired - private IJwtService jwtService; - - @Autowired - private UserService userService; - @Autowired private SecurityConfigurationProperties securityConfigurationProperties; @@ -76,9 +62,10 @@ public class NaeSecurityConfiguration extends AbstractSecurityConfiguration { private List authenticationProviders; @Autowired - private AuthenticationManagerBuilder authenticationManagerBuilder; - + private PublicAuthenticationFilter publicAuthenticationFilter; + @Autowired + private NetgrifHttpRequestTransformFilter netgrifHttpRequestTransformFilter; @Bean public CorsConfigurationSource corsConfigurationSource() { @@ -88,7 +75,7 @@ public CorsConfigurationSource corsConfigurationSource() { config.addAllowedMethod("*"); config.addAllowedHeader("*"); config.addExposedHeader("X-Auth-Token"); - config.addExposedHeader("X-Jwt-Token"); + config.addExposedHeader("X-Anonymous-Token"); config.setAllowCredentials(true); if (allowedOrigins == null || allowedOrigins.isEmpty()) { config.addAllowedOriginPattern("*"); @@ -109,10 +96,11 @@ public SecurityFilterChain configure(HttpSecurity http) throws Exception { .httpBasic(httpSecurityHttpBasicConfigurer -> httpSecurityHttpBasicConfigurer.authenticationEntryPoint(authenticationEntryPoint)) .addFilterBefore(new ForwardedHeaderFilter(), WebAsyncManagerIntegrationFilter.class) - .addFilterBefore(createPublicAuthenticationFilter(), BasicAuthenticationFilter.class) .addFilterAfter(createSecurityContextFilter(), BasicAuthenticationFilter.class) .addFilterAfter(impersonationRequestFilter(), BasicAuthenticationFilter.class) .addFilterAfter(hostValidationRequestFilter(), BasicAuthenticationFilter.class) + .addFilterBefore(publicAuthenticationFilter, SecurityContextFilter.class) + .addFilterBefore(netgrifHttpRequestTransformFilter, BasicAuthenticationFilter.class) .authorizeHttpRequests(requestMatcherRegistry -> requestMatcherRegistry .requestMatchers(getPatterns()).permitAll() @@ -168,18 +156,6 @@ protected SecurityConfigurationProperties getSecurityConfigProperties() { return securityConfigurationProperties; } - protected PublicAuthenticationFilter createPublicAuthenticationFilter() throws Exception { - return new PublicAuthenticationFilter( - (ProviderManager) authenticationManager(authenticationManagerBuilder), - new AnonymousAuthenticationProvider(securityConfigurationProperties.getAnonymousAuthenticationKey()), - securityConfigurationProperties.getServerPatterns(), - securityConfigurationProperties.getAnonymousExceptions(), - jwtService, - userService, - authorityService, - securityConfigurationProperties - ); - } private SecurityContextFilter createSecurityContextFilter() { return new SecurityContextFilter(securityContextService); diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/SecurityConfigurationProperties.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/SecurityConfigurationProperties.java index 393514c82ef..60c61cb336d 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/SecurityConfigurationProperties.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/SecurityConfigurationProperties.java @@ -421,6 +421,8 @@ public static class WebProperties { @Data public static class PublicProperties { + private boolean enabled = true; + /** * Public URL for web functionalities. */ diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/PublicAuthenticationFilter.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/PublicAuthenticationFilter.java index 89e2bbe9254..9cd381ac414 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/PublicAuthenticationFilter.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/PublicAuthenticationFilter.java @@ -1,133 +1,110 @@ package com.netgrif.application.engine.configuration.security; -import com.netgrif.application.engine.adapter.spring.auth.domain.AuthorityImpl; +import com.netgrif.application.engine.adapter.spring.auth.domain.AnonymousUser; +import com.netgrif.application.engine.adapter.spring.auth.domain.AnonymousUserRef; +import com.netgrif.application.engine.adapter.spring.auth.domain.LoggedUserImpl; +import com.netgrif.application.engine.adapter.spring.configuration.filters.NetgrifOncePerRequestFilter; +import com.netgrif.application.engine.adapter.spring.configuration.filters.requests.NetgrifHttpServletRequest; +import com.netgrif.application.engine.auth.service.AnonymousUserRefService; +import com.netgrif.application.engine.auth.service.RealmService; import com.netgrif.application.engine.configuration.properties.SecurityConfigurationProperties; import com.netgrif.application.engine.objects.auth.domain.*; import com.netgrif.application.engine.auth.service.AuthorityService; -import com.netgrif.application.engine.auth.service.UserService; -import com.netgrif.application.engine.configuration.security.jwt.IJwtService; -import com.netgrif.application.engine.objects.auth.domain.enums.UserState; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; -import org.springframework.security.authentication.AnonymousAuthenticationProvider; -import org.springframework.security.authentication.AnonymousAuthenticationToken; -import org.springframework.security.authentication.AuthenticationDetailsSource; -import org.springframework.security.authentication.ProviderManager; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.security.authentication.*; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; -import org.springframework.web.filter.OncePerRequestFilter; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.stereotype.Component; import java.io.IOException; -import java.util.Collections; -import java.util.Map; +import java.util.Arrays; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; @Slf4j -public class PublicAuthenticationFilter extends OncePerRequestFilter { +@Component +public class PublicAuthenticationFilter extends NetgrifOncePerRequestFilter { - private final static String JWT_HEADER_NAME = "X-Jwt-Token"; - private final static String BEARER = "Bearer "; - private final static String USER = "user"; - private final ProviderManager authenticationManager; - private final AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource(); - private final String[] anonymousAccessUrls; - private final String[] exceptions; - - private final IJwtService jwtService; - private final UserService userService; + private final AnonymousUserRefService anonymousUserRefService; private final AuthorityService authorityService; - private final SecurityConfigurationProperties securityConfigurationProperties; + private final RealmService realmService; - public PublicAuthenticationFilter(ProviderManager authenticationManager, AnonymousAuthenticationProvider provider, - String[] urls, String[] exceptions, IJwtService jwtService, - UserService userService, AuthorityService authorityService, + public PublicAuthenticationFilter(AnonymousUserRefService anonymousUserRefService, + AuthorityService authorityService, + RealmService realmService, SecurityConfigurationProperties securityConfigurationProperties) { - this.authenticationManager = authenticationManager; - this.authenticationManager.getProviders().add(provider); - this.anonymousAccessUrls = urls; - this.exceptions = exceptions; - this.jwtService = jwtService; - this.userService = userService; + this.anonymousUserRefService = anonymousUserRefService; this.authorityService = authorityService; - this.securityConfigurationProperties = securityConfigurationProperties; + this.realmService = realmService; + setRequestMatcher(Arrays.stream(securityConfigurationProperties.getServerPatterns()).map(AntPathRequestMatcher::new).collect(Collectors.toSet())); } @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - if (isPublicApi(request.getRequestURI())) { - String jwtToken = resolveValidToken(request, response); - authenticate(request, jwtToken); - response.setHeader(JWT_HEADER_NAME, BEARER + jwtToken); - log.info("Anonymous user was authenticated."); - } - filterChain.doFilter(request, response); - } + protected void doFilterInternal(NetgrifHttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + String path = request.getRequestURI(); + log.trace("PublicAnonymousAuthFilter handling {}", path); - private void authenticate(HttpServletRequest request, String jwtToken) { - AnonymousAuthenticationToken authRequest = new AnonymousAuthenticationToken( - securityConfigurationProperties.getAnonymousAuthenticationKey(), - jwtService.getLoggedUser(jwtToken, Authority.anonymous), - Collections.singleton((AuthorityImpl) authorityService.getOrCreate(Authority.anonymous)) - ); - authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request)); - Authentication authResult = this.authenticationManager.authenticate(authRequest); - SecurityContextHolder.getContext().setAuthentication(authResult); - } + Authentication current = SecurityContextHolder.getContext().getAuthentication(); + if (current != null && current.isAuthenticated()) { + log.debug("Existing authenticated principal: {}", current.getPrincipal()); + filterChain.doFilter(request, response); + return; + } - private String resolveValidToken(HttpServletRequest request, HttpServletResponse response) { - String jwtHeader = request.getHeader(JWT_HEADER_NAME); - String jwtToken = null; + Realm realm = realmService.getDefaultRealm().orElseThrow(); - if (jwtHeader == null || !jwtHeader.startsWith(BEARER)) { - log.warn("There is no JWT token or token is invalid."); - LoggedUser loggedUser = resolveLoggedUser(jwtToken); - jwtToken = jwtService.tokenFrom(Collections.emptyMap(), loggedUser.getUsername(), Map.of(USER, loggedUser)); - } else { - jwtToken = jwtHeader.replace(BEARER, ""); + log.debug("Loaded realm {} (publicAccess={})", realm.getName(), realm.isPublicAccess()); + if (!realm.isPublicAccess()) { + log.debug("Public access disabled for realm {}; skipping anon auth", realm.getName()); + filterChain.doFilter(request, response); + return; } - if (jwtService.isTokenExpired(jwtToken)) { - LoggedUser loggedUser = resolveLoggedUser(jwtToken); - jwtToken = jwtService.tokenFrom(Collections.emptyMap(), loggedUser.getUsername(), Map.of(USER, loggedUser)); - } - return jwtToken; - } + try { + log.trace("Performing anonymous public auth for realm {}", realm.getName()); + Optional refOpt = anonymousUserRefService.getRef(realm.getName()); + if (refOpt.isEmpty()) { + log.debug("AnonymousUserRef missing for realm {}; skipping anon auth", realm.getName()); + filterChain.doFilter(request, response); + return; + } + AnonymousUserRef ref = refOpt.get(); + Authority anonAuthority = authorityService.getOrCreate(Authority.anonymous); + log.debug("Using AnonymousUserRef id {} for realm {}", ref.getId(), realm.getName()); - private LoggedUser resolveLoggedUser(String existingToken) { - LoggedUser loggedUser; - if (existingToken != null) { - loggedUser = jwtService.getLoggedUser(existingToken, Authority.anonymous); - } else { - loggedUser = createAnonymousUser(); + AnonymousUser anonymousUser = new AnonymousUser(ref, anonAuthority); + LoggedUserImpl userDetails = (LoggedUserImpl) ActorTransformer.toLoggedUser(anonymousUser); + userDetails.setSessionTimeout(realm.getPublicSessionTimeout()); + log.debug("Created anonymous user details for user: {}", userDetails.getUsername()); + + AnonymousAuthenticationToken token = new AnonymousAuthenticationToken( + "engine", + userDetails, + userDetails.getAuthorities() + ); + token.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(token); + log.debug("Anonymous public auth succeeded for realm {}", realm.getName()); + } catch (Exception ex) { + log.debug("Anonymous public auth failed for realm {}: {}", realm.getName(), ex.getMessage(), ex); } - loggedUser.setPassword("N/A"); - return loggedUser; - } - private LoggedUser createAnonymousUser() { - User anonymousUser = new User(); - anonymousUser.setUsername("anonymous_" + anonymousUser.getStringId()); - anonymousUser.setFirstName("Anonymous"); - anonymousUser.setLastName("User"); - anonymousUser.setState(UserState.ACTIVE); - anonymousUser = (User) userService.saveUser(anonymousUser, null); - return ActorTransformer.toLoggedUser(anonymousUser); + filterChain.doFilter(request, response); } - private boolean isPublicApi(String path) { - for (String url : this.anonymousAccessUrls) { - if (path.matches(url.replace("*", ".*?"))) { - for (String ex : this.exceptions) { - if (path.matches(ex.replace("*", ".*?"))) { - return false; - } - } - return true; - } - } - return false; + @Bean + public FilterRegistrationBean publicAnonymousAuthFilterFilterRegistrationBean(PublicAuthenticationFilter filter) { + FilterRegistrationBean registration = new FilterRegistrationBean<>(filter); + registration.setEnabled(false); + return registration; } } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/DefaultRealmRunner.java b/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/DefaultRealmRunner.java index e116d4dc74d..bfbe64ddbf2 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/DefaultRealmRunner.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/DefaultRealmRunner.java @@ -1,6 +1,7 @@ package com.netgrif.application.engine.startup.runner; import com.netgrif.application.engine.auth.service.RealmService; +import com.netgrif.application.engine.configuration.properties.SecurityConfigurationProperties; import com.netgrif.application.engine.startup.ApplicationEngineStartupRunner; import com.netgrif.application.engine.startup.annotation.RunnerOrder; import com.netgrif.application.engine.objects.auth.domain.Realm; @@ -19,6 +20,8 @@ public class DefaultRealmRunner implements ApplicationEngineStartupRunner { private final RealmService realmService; + private final SecurityConfigurationProperties.WebProperties webProperties; + @Override public void run(ApplicationArguments args) throws Exception { if (realmService.getDefaultRealm().isPresent()) { @@ -29,6 +32,9 @@ public void run(ApplicationArguments args) throws Exception { createRequest.setDescription("Default realm"); createRequest.setAdminRealm(true); createRequest.setDefaultRealm(true); - realmService.createRealm(createRequest); + Realm realm = realmService.createRealm(createRequest); + if (webProperties.getPublicWeb().isEnabled()) { + realmService.enableAnonymUser(realm); + } } } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/TaskService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/TaskService.java index ac21c61ae5f..cb694fae65c 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/TaskService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/TaskService.java @@ -981,7 +981,7 @@ public SetDataEventOutcome getMainOutcome(Map outco } protected AbstractUser getUserFromLoggedUser(LoggedUser loggedUser) { - AbstractUser user = userService.findById(loggedUser.getStringId(), loggedUser.getRealmId()); + AbstractUser user = ActorTransformer.toUser(loggedUser); // TODO: impersonation // AbstractUser fromLogged = userService.transformToUser((LoggedUserImpl) loggedUser); // user.setImpersonated(fromLogged.getImpersonated()); diff --git a/application-engine/src/main/resources/application-dev.yaml b/application-engine/src/main/resources/application-dev.yaml index 3281960f14b..6dc2c0bb9e3 100644 --- a/application-engine/src/main/resources/application-dev.yaml +++ b/application-engine/src/main/resources/application-dev.yaml @@ -28,7 +28,7 @@ netgrif: swagger: enabled: true security: - server-patterns: /api/auth/signup,/api/auth/token/verify,/api/auth/reset,/api/auth/recover,/v3/api-docs,/v3/api-docs/**,/swagger-ui.html,/swagger-ui/**,/api/public/**,/manage/** + server-patterns: /api/auth/signup,/api/auth/token/verify,/api/auth/reset,/api/auth/recover,/v3/api-docs,/v3/api-docs/**,/swagger-ui.html,/swagger-ui/**,/api/public/**,/manage/**,/api/users/me,/api/users/preferences management: health: ldap: diff --git a/application-engine/src/main/resources/application.yaml b/application-engine/src/main/resources/application.yaml index c82a3b10289..a0439442be4 100644 --- a/application-engine/src/main/resources/application.yaml +++ b/application-engine/src/main/resources/application.yaml @@ -84,8 +84,7 @@ netgrif: expiration: 900000 algorithm: RSA private-key: classpath:certificates/private.der - server-patterns: /api/auth/signup,/api/auth/token/verify,/api/auth/reset,/api/auth/recover,/api/public/**,/v3/api-docs/public,/manage/** - anonymous-exceptions: /api/auth/signup,/api/auth/token/verify,/api/auth/reset,/api/auth/recover,/manage/** + server-patterns: /api/auth/signup,/api/auth/token/verify,/api/auth/reset,/api/auth/recover,/v3/api-docs/public,/manage/**,/api/users/me,/api/users/preferences providers: NetgrifBasicAuthenticationProvider swagger: enabled: false diff --git a/nae-spring-core-adapter/pom.xml b/nae-spring-core-adapter/pom.xml index baa42182723..92b24c1f088 100644 --- a/nae-spring-core-adapter/pom.xml +++ b/nae-spring-core-adapter/pom.xml @@ -77,6 +77,22 @@ spring-boot-starter-security provided + + org.springframework.boot + spring-boot-starter-tomcat + provided + + + org.springframework.boot + spring-boot-starter-validation + provided + + + org.jetbrains + annotations + 23.0.0 + compile + org.springframework.boot spring-boot-starter-data-mongodb diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/auth/domain/AnonymousUserRef.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/auth/domain/AnonymousUserRef.java index 663e913e7d5..65720750a1b 100644 --- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/auth/domain/AnonymousUserRef.java +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/auth/domain/AnonymousUserRef.java @@ -101,4 +101,10 @@ public AnonymousUserRef(String realmId) { this.realmId = realmId; this.createdAt = LocalDateTime.now(); } + + public AnonymousUserRef(String realmId, Set authorities, Set processRoles) { + this(realmId); + this.authorities = authorities; + this.processRoles = processRoles; + } } \ No newline at end of file diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/configuration/filters/NetgrifHttpRequestTransformFilter.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/configuration/filters/NetgrifHttpRequestTransformFilter.java new file mode 100644 index 00000000000..9653a0e250e --- /dev/null +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/configuration/filters/NetgrifHttpRequestTransformFilter.java @@ -0,0 +1,39 @@ +package com.netgrif.application.engine.adapter.spring.configuration.filters; + + +import com.netgrif.application.engine.adapter.spring.configuration.filters.requests.NetgrifHttpServletRequest; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +/** + * This filter wraps the {@link HttpServletRequest} object into {@link NetgrifHttpServletRequest} object + * */ +@Slf4j +@Component +public class NetgrifHttpRequestTransformFilter extends OncePerRequestFilter { + + @Override + protected void doFilterInternal(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + HttpServletRequest transformedRequest = new NetgrifHttpServletRequest(request); + log.debug("Http request was transformed to Netgrif http request wrapper"); + filterChain.doFilter(transformedRequest, response); + } + + @Bean + public FilterRegistrationBean netgrifHttpRequestTransformFilterFilterRegistrationBean(NetgrifHttpRequestTransformFilter filter) { + FilterRegistrationBean registration = new FilterRegistrationBean<>(filter); + registration.setEnabled(false); + return registration; + } +} diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/configuration/filters/NetgrifOncePerRequestFilter.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/configuration/filters/NetgrifOncePerRequestFilter.java new file mode 100644 index 00000000000..4f0cc76a44f --- /dev/null +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/configuration/filters/NetgrifOncePerRequestFilter.java @@ -0,0 +1,55 @@ +package com.netgrif.application.engine.adapter.spring.configuration.filters; + +import com.netgrif.application.engine.adapter.spring.configuration.filters.requests.NetgrifHttpServletRequest; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; +import org.springframework.security.web.util.matcher.RequestMatcher; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; +import java.util.Set; + +/** + * Proxy filter class to cast request object to {@link NetgrifHttpServletRequest} for further use + * */ +@Setter +@Slf4j +public abstract class NetgrifOncePerRequestFilter extends OncePerRequestFilter { + + /** + * If initialized, the filter will be applied only if the request path is matched. Otherwise, it will just continue to the next filter. + * */ + protected Set requestMatcher; + + protected abstract void doFilterInternal(NetgrifHttpServletRequest request, HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException; + + @Override + protected void doFilterInternal(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, + @NotNull FilterChain filterChain) throws ServletException, IOException { + if (requestNotMatches(request)) { + log.trace("Request did not match the required URIs: {}", this.requestMatcher); + filterChain.doFilter(request, response); + return; + } + + NetgrifHttpServletRequest typedRequest = (NetgrifHttpServletRequest) request; + doFilterInternal(typedRequest, response, filterChain); + } + + protected boolean requestNotMatches(@NotNull HttpServletRequest request) { + if (requestMatcher == null) { + return false; + } + boolean result = false; + for (RequestMatcher matcher : requestMatcher) { + result |= matcher.matches(request); + } + return !result; + } +} diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/configuration/filters/requests/NetgrifHttpServletRequest.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/configuration/filters/requests/NetgrifHttpServletRequest.java new file mode 100644 index 00000000000..2f14d248c93 --- /dev/null +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/configuration/filters/requests/NetgrifHttpServletRequest.java @@ -0,0 +1,29 @@ +package com.netgrif.application.engine.adapter.spring.configuration.filters.requests; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequestWrapper; + +import java.util.HashMap; +import java.util.Map; + +public class NetgrifHttpServletRequest extends HttpServletRequestWrapper { + + private final Map additionalParams; + + public NetgrifHttpServletRequest(HttpServletRequest request) { + this(request, new HashMap<>()); + } + + public NetgrifHttpServletRequest(HttpServletRequest request, Map additionalParams) { + super(request); + this.additionalParams = additionalParams; + } + + public Object getAdditionalParameter(String name) { + return this.additionalParams.get(name); + } + + public void addAdditionalParameter(String name, Object value) { + this.additionalParams.put(name, value); + } +} diff --git a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/AnonymousUserRefServiceImpl.java b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/AnonymousUserRefServiceImpl.java index f74f7df7ce7..c50d034deef 100644 --- a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/AnonymousUserRefServiceImpl.java +++ b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/AnonymousUserRefServiceImpl.java @@ -1,11 +1,14 @@ package com.netgrif.application.engine.auth.service; import com.netgrif.application.engine.adapter.spring.auth.domain.AnonymousUserRef; +import com.netgrif.application.engine.adapter.spring.petrinet.service.ProcessRoleService; import com.netgrif.application.engine.auth.repository.AnonymousUserRefRepository; +import com.netgrif.application.engine.objects.auth.domain.Authority; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.Optional; +import java.util.Set; @Service public class AnonymousUserRefServiceImpl implements AnonymousUserRefService { @@ -13,10 +16,16 @@ public class AnonymousUserRefServiceImpl implements AnonymousUserRefService { @Autowired private AnonymousUserRefRepository repository; + @Autowired + private AuthorityService authorityService; + + @Autowired + private ProcessRoleService processRoleService; + @Override public AnonymousUserRef getOrCreateRef(String realmId) { return repository.findByRealmId(realmId) - .orElseGet(() -> repository.save(new AnonymousUserRef(realmId))); + .orElseGet(() -> repository.save(new AnonymousUserRef(realmId, Set.of(authorityService.getOrCreate(Authority.anonymous)), Set.of(processRoleService.getAnonymousRole())))); } @Override diff --git a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/RealmServiceImpl.java b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/RealmServiceImpl.java index b38c2c00d90..160897a4cba 100644 --- a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/RealmServiceImpl.java +++ b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/RealmServiceImpl.java @@ -67,6 +67,7 @@ public Realm createRealm(Realm createRequest) { com.netgrif.application.engine.adapter.spring.auth.domain.Realm realm = new com.netgrif.application.engine.adapter.spring.auth.domain.Realm(createRequest.getName()); realm.setDescription(createRequest.getDescription()); realm.setAdminRealm(createRequest.isAdminRealm()); + realm.setPublicAccess(createRequest.isPublicAccess()); if (createRequest.isDefaultRealm() && getDefaultRealm().isEmpty()) { realm.setDefaultRealm(true); From 69d7f8506b89b13d6a21e79722f5b43adfad5506 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Mon, 12 Jan 2026 14:42:21 +0100 Subject: [PATCH 03/14] Add RealmFilter to extract and handle realms from requests Introduced a new RealmFilter to extract the realm information from HTTP headers or request bodies, fallback to a default realm if available, and attach it to the request. Updated related utility functions and PublicAuthenticationFilter to use the extracted realm for better request handling. --- .../security/PublicAuthenticationFilter.java | 10 +- .../security/filter/RealmFilter.java | 116 ++++++++++++++++++ .../engine/utils/HttpReqRespUtils.java | 68 ++++++++++ .../utils/HttpRequestParamConstants.java | 7 ++ 4 files changed, 199 insertions(+), 2 deletions(-) create mode 100644 application-engine/src/main/java/com/netgrif/application/engine/configuration/security/filter/RealmFilter.java create mode 100644 application-engine/src/main/java/com/netgrif/application/engine/utils/HttpReqRespUtils.java create mode 100644 application-engine/src/main/java/com/netgrif/application/engine/utils/HttpRequestParamConstants.java diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/PublicAuthenticationFilter.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/PublicAuthenticationFilter.java index 9cd381ac414..2feb6dea7cc 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/PublicAuthenticationFilter.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/PublicAuthenticationFilter.java @@ -10,6 +10,8 @@ import com.netgrif.application.engine.configuration.properties.SecurityConfigurationProperties; import com.netgrif.application.engine.objects.auth.domain.*; import com.netgrif.application.engine.auth.service.AuthorityService; +import com.netgrif.application.engine.utils.HttpReqRespUtils; +import com.netgrif.application.engine.utils.HttpRequestParamConstants; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletResponse; @@ -26,7 +28,6 @@ import java.io.IOException; import java.util.Arrays; import java.util.Optional; -import java.util.Set; import java.util.stream.Collectors; @Slf4j @@ -60,7 +61,12 @@ protected void doFilterInternal(NetgrifHttpServletRequest request, HttpServletRe return; } - Realm realm = realmService.getDefaultRealm().orElseThrow(); + Realm realm = HttpReqRespUtils.extractRealmFromRequest(request); + if (realm == null) { + log.debug("Realm unavailable for public request; skipping anon auth"); + filterChain.doFilter(request, response); + return; + } log.debug("Loaded realm {} (publicAccess={})", realm.getName(), realm.isPublicAccess()); if (!realm.isPublicAccess()) { diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/filter/RealmFilter.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/filter/RealmFilter.java new file mode 100644 index 00000000000..e5cae3b5588 --- /dev/null +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/filter/RealmFilter.java @@ -0,0 +1,116 @@ +package com.netgrif.application.engine.configuration.security.filter; + +import com.fasterxml.jackson.databind.JsonNode; +import com.netgrif.application.engine.adapter.spring.configuration.filters.NetgrifOncePerRequestFilter; +import com.netgrif.application.engine.adapter.spring.configuration.filters.requests.NetgrifHttpServletRequest; +import com.netgrif.application.engine.auth.service.RealmService; +import com.netgrif.application.engine.objects.auth.domain.Realm; +import com.netgrif.application.engine.utils.HttpReqRespUtils; +import com.netgrif.application.engine.utils.HttpRequestParamConstants; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.util.Optional; + +/** + * This filter extracts a realm from the request. It's saved by {@link NetgrifHttpServletRequest#addAdditionalParameter(String, Object)} + * under the key {@link HttpRequestParamConstants#REALM}. Realm can be null if none is found. + * */ +@Slf4j +@Component +@RequiredArgsConstructor +public class RealmFilter extends NetgrifOncePerRequestFilter { + + private final String REALM_ID_HEADER = "X-Realm-ID"; + private final String REALM_ID_BODY = "realmId"; + private final String REALM_NAME_BODY = "realName"; + + private final RealmService realmService; + + @Override + protected void doFilterInternal(NetgrifHttpServletRequest request, HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + log.debug("Trying to get realm id from the HTTP request..."); + + Optional realmOpt = findRealmInHeaders(request); + + if (realmOpt.isEmpty()) { + realmOpt = findRealmInBody(request); + } + + if (realmOpt.isEmpty()) { + log.debug("Realm could not be found in the request. Using the default realm if available."); + realmOpt = realmService.getDefaultRealm(); + } else { + log.debug("Realm was successfully found in the request"); + } + + if (realmOpt.isEmpty()) { + log.debug("No realm could be found. Continuing without realm."); + filterChain.doFilter(request, response); + return; + } + + log.trace("Selected realm: [{}]", realmOpt.get().getName()); + + request.addAdditionalParameter(HttpRequestParamConstants.REALM, realmOpt.get()); + + filterChain.doFilter(request, response); + } + + @Bean + public FilterRegistrationBean realmFilterFilterRegistrationBean(RealmFilter filter) { + FilterRegistrationBean registration = new FilterRegistrationBean<>(filter); + registration.setEnabled(false); + return registration; + } + + /** + * Finds realm by realm id in headers by {@link #REALM_ID_HEADER} + * */ + private Optional findRealmInHeaders(HttpServletRequest request) { + String realmId = request.getHeader(REALM_ID_HEADER); + if (realmId == null || realmId.isBlank()) { + return Optional.empty(); + } + return realmService.getRealmById(realmId); + } + + /** + * Finds realm by realm id {@link #REALM_ID_BODY} or realm name {@link #REALM_NAME_BODY} in request body + * */ + private Optional findRealmInBody(NetgrifHttpServletRequest request) { + JsonNode requestBodyJson = HttpReqRespUtils.extractBodyFromRequest(request); + if (requestBodyJson == null) { + return Optional.empty(); + } + + JsonNode realmIdNode = requestBodyJson.get(REALM_ID_BODY); + Optional realmOpt = Optional.empty(); + + if (isJsonNodeValueEmpty(realmIdNode)) { + realmOpt = realmService.getRealmById(String.valueOf(realmIdNode.textValue())); + } + + if (realmOpt.isEmpty()) { + JsonNode realmNameNode = requestBodyJson.get(REALM_NAME_BODY); + if (isJsonNodeValueEmpty(realmNameNode)) { + realmOpt = realmService.getRealmByName(realmNameNode.textValue()); + } + } + + return realmOpt; + } + + private static boolean isJsonNodeValueEmpty(JsonNode node) { + return node != null && node.textValue() != null && !node.textValue().isBlank(); + } +} \ No newline at end of file diff --git a/application-engine/src/main/java/com/netgrif/application/engine/utils/HttpReqRespUtils.java b/application-engine/src/main/java/com/netgrif/application/engine/utils/HttpReqRespUtils.java new file mode 100644 index 00000000000..00e198c527e --- /dev/null +++ b/application-engine/src/main/java/com/netgrif/application/engine/utils/HttpReqRespUtils.java @@ -0,0 +1,68 @@ +package com.netgrif.application.engine.utils; + +import com.fasterxml.jackson.databind.JsonNode; +import com.netgrif.application.engine.adapter.spring.configuration.filters.requests.NetgrifHttpServletRequest; +import com.netgrif.application.engine.auth.domain.NetgrifAuthenticationToken; +import com.netgrif.application.engine.objects.auth.domain.Realm; +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + + +public class HttpReqRespUtils { + + private static final String[] IP_HEADER_CANDIDATES = { + "X-Forwarded-For", + "Proxy-Client-IP", + "WL-Proxy-Client-IP", + "HTTP_X_FORWARDED_FOR", + "HTTP_X_FORWARDED", + "HTTP_X_CLUSTER_CLIENT_IP", + "HTTP_CLIENT_IP", + "HTTP_FORWARDED_FOR", + "HTTP_FORWARDED", + "HTTP_VIA", + "REMOTE_ADDR" + }; + + public static String getClientIpAddressIfServletRequestExists() { + + if (RequestContextHolder.getRequestAttributes() == null) { + return "0.0.0.0"; + } + + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + return getClientIpAddressIfServletRequestExists(request); + } + + public static String getClientIpAddressIfServletRequestExists(HttpServletRequest request) { + for (String header : IP_HEADER_CANDIDATES) { + String ipList = request.getHeader(header); + if (ipList != null && !ipList.isEmpty() && !"unknown".equalsIgnoreCase(ipList)) { + return ipList.split(",")[0]; + } + } + + return request.getRemoteAddr(); + } + + public static HttpServletRequest getRequestIfExists() { + if (RequestContextHolder.getRequestAttributes() == null) { + return null; + } + + return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + } + + public static Realm extractRealmFromRequest(NetgrifHttpServletRequest request) { + return (Realm) request.getAdditionalParameter(HttpRequestParamConstants.REALM); + } + + public static JsonNode extractBodyFromRequest(NetgrifHttpServletRequest request) { + return (JsonNode) request.getAdditionalParameter(HttpRequestParamConstants.REQUEST_BODY); + } + + public static NetgrifAuthenticationToken extractAuthReqTokenFromRequest(NetgrifHttpServletRequest request) { + return (NetgrifAuthenticationToken) request.getAdditionalParameter(HttpRequestParamConstants.AUTH_REQ_TOKEN); + } +} diff --git a/application-engine/src/main/java/com/netgrif/application/engine/utils/HttpRequestParamConstants.java b/application-engine/src/main/java/com/netgrif/application/engine/utils/HttpRequestParamConstants.java new file mode 100644 index 00000000000..9d4a73d08e9 --- /dev/null +++ b/application-engine/src/main/java/com/netgrif/application/engine/utils/HttpRequestParamConstants.java @@ -0,0 +1,7 @@ +package com.netgrif.application.engine.utils; + +public class HttpRequestParamConstants { + public static final String REQUEST_BODY = "request_body"; + public static final String REALM = "realm"; + public static final String AUTH_REQ_TOKEN = "auth_req_token"; +} From 8cd0f08b833dc50dac4feae0ed3c4d0cfd4f0721 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Tue, 13 Jan 2026 12:02:34 +0100 Subject: [PATCH 04/14] Refactor RealmFilter to remove @RequiredArgsConstructor Removed the @RequiredArgsConstructor annotation from RealmFilter and implemented an explicit constructor for dependency injection. Updated NaeSecurityConfiguration to autowire RealmFilter and included it in the security filter chain preceding NetgrifHttpRequestTransformFilter. --- .../engine/configuration/NaeSecurityConfiguration.java | 6 ++++++ .../engine/configuration/security/filter/RealmFilter.java | 5 ++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/NaeSecurityConfiguration.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/NaeSecurityConfiguration.java index 7ddf3401538..8ca754b496b 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/NaeSecurityConfiguration.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/NaeSecurityConfiguration.java @@ -7,6 +7,7 @@ import com.netgrif.application.engine.configuration.security.RestAuthenticationEntryPoint; import com.netgrif.application.engine.configuration.security.SecurityContextFilter; import com.netgrif.application.engine.configuration.security.filter.HostValidationRequestFilter; +import com.netgrif.application.engine.configuration.security.filter.RealmFilter; import com.netgrif.application.engine.impersonation.service.interfaces.IImpersonationService; import com.netgrif.application.engine.security.service.ISecurityContextService; import lombok.extern.slf4j.Slf4j; @@ -23,6 +24,7 @@ import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler; +import org.springframework.security.web.authentication.logout.LogoutFilter; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter; import org.springframework.stereotype.Controller; @@ -67,6 +69,9 @@ public class NaeSecurityConfiguration extends AbstractSecurityConfiguration { @Autowired private NetgrifHttpRequestTransformFilter netgrifHttpRequestTransformFilter; + @Autowired + private RealmFilter realmFilter; + @Bean public CorsConfigurationSource corsConfigurationSource() { List allowedOrigins = securityConfigurationProperties.getAllowedOrigins(); @@ -101,6 +106,7 @@ public SecurityFilterChain configure(HttpSecurity http) throws Exception { .addFilterAfter(hostValidationRequestFilter(), BasicAuthenticationFilter.class) .addFilterBefore(publicAuthenticationFilter, SecurityContextFilter.class) .addFilterBefore(netgrifHttpRequestTransformFilter, BasicAuthenticationFilter.class) + .addFilterBefore(realmFilter, NetgrifHttpRequestTransformFilter.class) .authorizeHttpRequests(requestMatcherRegistry -> requestMatcherRegistry .requestMatchers(getPatterns()).permitAll() diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/filter/RealmFilter.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/filter/RealmFilter.java index e5cae3b5588..c560ee55420 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/filter/RealmFilter.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/filter/RealmFilter.java @@ -26,7 +26,6 @@ * */ @Slf4j @Component -@RequiredArgsConstructor public class RealmFilter extends NetgrifOncePerRequestFilter { private final String REALM_ID_HEADER = "X-Realm-ID"; @@ -35,6 +34,10 @@ public class RealmFilter extends NetgrifOncePerRequestFilter { private final RealmService realmService; + public RealmFilter(RealmService realmService) { + this.realmService = realmService; + } + @Override protected void doFilterInternal(NetgrifHttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { From c24bbafecf0c3d74720bb69e202bda9605b2fb47 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Tue, 13 Jan 2026 17:11:32 +0100 Subject: [PATCH 05/14] Refactor RealmFilter to remove @RequiredArgsConstructor Removed the @RequiredArgsConstructor annotation from RealmFilter and implemented an explicit constructor for dependency injection. Updated NaeSecurityConfiguration to autowire RealmFilter and included it in the security filter chain preceding NetgrifHttpRequestTransformFilter. --- .../engine/configuration/NaeSecurityConfiguration.java | 7 +++---- .../configuration/security/PublicAuthenticationFilter.java | 1 - .../configuration/security/{filter => }/RealmFilter.java | 3 +-- .../engine/workflow/web/WorkflowController.java | 7 ++++++- application-engine/src/main/resources/application-dev.yaml | 2 +- .../configuration/filters/NetgrifOncePerRequestFilter.java | 2 +- 6 files changed, 12 insertions(+), 10 deletions(-) rename application-engine/src/main/java/com/netgrif/application/engine/configuration/security/{filter => }/RealmFilter.java (97%) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/NaeSecurityConfiguration.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/NaeSecurityConfiguration.java index 8ca754b496b..bb9d25cd9e4 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/NaeSecurityConfiguration.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/NaeSecurityConfiguration.java @@ -7,9 +7,10 @@ import com.netgrif.application.engine.configuration.security.RestAuthenticationEntryPoint; import com.netgrif.application.engine.configuration.security.SecurityContextFilter; import com.netgrif.application.engine.configuration.security.filter.HostValidationRequestFilter; -import com.netgrif.application.engine.configuration.security.filter.RealmFilter; +import com.netgrif.application.engine.configuration.security.RealmFilter; import com.netgrif.application.engine.impersonation.service.interfaces.IImpersonationService; import com.netgrif.application.engine.security.service.ISecurityContextService; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.security.SecurityProperties; @@ -24,7 +25,6 @@ import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler; -import org.springframework.security.web.authentication.logout.LogoutFilter; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter; import org.springframework.stereotype.Controller; @@ -39,7 +39,6 @@ @Slf4j -@Controller @Configuration @EnableWebSecurity @Order(SecurityProperties.DEFAULT_FILTER_ORDER) @@ -106,7 +105,7 @@ public SecurityFilterChain configure(HttpSecurity http) throws Exception { .addFilterAfter(hostValidationRequestFilter(), BasicAuthenticationFilter.class) .addFilterBefore(publicAuthenticationFilter, SecurityContextFilter.class) .addFilterBefore(netgrifHttpRequestTransformFilter, BasicAuthenticationFilter.class) - .addFilterBefore(realmFilter, NetgrifHttpRequestTransformFilter.class) + .addFilterBefore(realmFilter, PublicAuthenticationFilter.class) .authorizeHttpRequests(requestMatcherRegistry -> requestMatcherRegistry .requestMatchers(getPatterns()).permitAll() diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/PublicAuthenticationFilter.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/PublicAuthenticationFilter.java index 2feb6dea7cc..088737c6340 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/PublicAuthenticationFilter.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/PublicAuthenticationFilter.java @@ -11,7 +11,6 @@ import com.netgrif.application.engine.objects.auth.domain.*; import com.netgrif.application.engine.auth.service.AuthorityService; import com.netgrif.application.engine.utils.HttpReqRespUtils; -import com.netgrif.application.engine.utils.HttpRequestParamConstants; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletResponse; diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/filter/RealmFilter.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/RealmFilter.java similarity index 97% rename from application-engine/src/main/java/com/netgrif/application/engine/configuration/security/filter/RealmFilter.java rename to application-engine/src/main/java/com/netgrif/application/engine/configuration/security/RealmFilter.java index c560ee55420..917a04d4e23 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/filter/RealmFilter.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/RealmFilter.java @@ -1,4 +1,4 @@ -package com.netgrif.application.engine.configuration.security.filter; +package com.netgrif.application.engine.configuration.security; import com.fasterxml.jackson.databind.JsonNode; import com.netgrif.application.engine.adapter.spring.configuration.filters.NetgrifOncePerRequestFilter; @@ -11,7 +11,6 @@ import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/WorkflowController.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/WorkflowController.java index bf932161134..243deed0bce 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/WorkflowController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/WorkflowController.java @@ -1,5 +1,7 @@ package com.netgrif.application.engine.workflow.web; +import com.netgrif.application.engine.auth.service.UserService; +import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; import com.netgrif.application.engine.objects.auth.domain.LoggedUser; import com.netgrif.application.engine.elastic.service.interfaces.IElasticCaseService; import com.netgrif.application.engine.elastic.web.requestbodies.singleaslist.SingleCaseSearchRequestAsList; @@ -78,6 +80,9 @@ public class WorkflowController { @Autowired private IDataService dataService; + @Autowired + private UserService userService; + @PreAuthorize("@workflowAuthorizationService.canCallCreate(#auth.getPrincipal(), #body.netId)") @Operation(summary = "Create new case", security = {@SecurityRequirement(name = "BasicAuth")}) @@ -119,7 +124,7 @@ public PagedModel search2(@QuerydslPredicate(root = Case.class) Pr @Operation(summary = "Generic case search on Elasticsearch database, paginated", security = {@SecurityRequirement(name = "BasicAuth")}) @PostMapping(value = "/case/search", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaTypes.HAL_JSON_VALUE) public PagedModel search(@RequestBody SingleCaseSearchRequestAsList searchBody, @RequestParam(defaultValue = "OR") MergeFilterOperation operation, Pageable pageable, PagedResourcesAssembler assembler, Authentication auth, Locale locale) { - LoggedUser user = (LoggedUser) auth.getPrincipal(); + LoggedUser user = ActorTransformer.toLoggedUser(userService.getLoggedUser()); Page cases = elasticCaseService.search(searchBody.getList(), user, pageable, locale, operation == MergeFilterOperation.AND); Link selfLink = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(WorkflowController.class) diff --git a/application-engine/src/main/resources/application-dev.yaml b/application-engine/src/main/resources/application-dev.yaml index 6dc2c0bb9e3..9e07da7591e 100644 --- a/application-engine/src/main/resources/application-dev.yaml +++ b/application-engine/src/main/resources/application-dev.yaml @@ -28,7 +28,7 @@ netgrif: swagger: enabled: true security: - server-patterns: /api/auth/signup,/api/auth/token/verify,/api/auth/reset,/api/auth/recover,/v3/api-docs,/v3/api-docs/**,/swagger-ui.html,/swagger-ui/**,/api/public/**,/manage/**,/api/users/me,/api/users/preferences + server-patterns: /api/auth/signup,/api/auth/token/verify,/api/auth/reset,/api/auth/recover,/v3/api-docs,/v3/api-docs/**,/swagger-ui.html,/swagger-ui/**,/api/**,/manage/**,/api/users/me,/api/users/preferences management: health: ldap: diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/configuration/filters/NetgrifOncePerRequestFilter.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/configuration/filters/NetgrifOncePerRequestFilter.java index 4f0cc76a44f..de3cb326e5a 100644 --- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/configuration/filters/NetgrifOncePerRequestFilter.java +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/configuration/filters/NetgrifOncePerRequestFilter.java @@ -19,7 +19,7 @@ * */ @Setter @Slf4j -public abstract class NetgrifOncePerRequestFilter extends OncePerRequestFilter { +public abstract class NetgrifOncePerRequestFilter extends OncePerRequestFilter { /** * If initialized, the filter will be applied only if the request path is matched. Otherwise, it will just continue to the next filter. From 2c4a0841aedf02d999a1bdf8bf587393236b11b6 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Wed, 14 Jan 2026 13:21:07 +0100 Subject: [PATCH 06/14] - modified authorization method over controller endpoints to use security context and not auth parameter - modified controller method properties to remove unnecessary auth param - instead of auth param, the security context will be used, because of AnonymousAuthentication token --- .../auth/web/AuthenticationController.java | 5 +- .../petrinet/web/PetriNetController.java | 9 ++- .../workflow/web/AbstractTaskController.java | 52 +++++++------ .../workflow/web/PublicTaskController.java | 19 ++--- .../engine/workflow/web/TaskController.java | 73 +++++++++---------- .../responsebodies/ResourceLinkAssembler.java | 12 +-- 6 files changed, 88 insertions(+), 82 deletions(-) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/auth/web/AuthenticationController.java b/application-engine/src/main/java/com/netgrif/application/engine/auth/web/AuthenticationController.java index e1f1c07b70d..63357af6bd3 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/auth/web/AuthenticationController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/auth/web/AuthenticationController.java @@ -2,6 +2,7 @@ import com.netgrif.application.engine.objects.auth.domain.AbstractUser; import com.netgrif.application.engine.configuration.properties.SecurityConfigurationProperties; +import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; import com.netgrif.application.engine.workflow.web.responsebodies.MessageResource; import com.netgrif.application.engine.objects.auth.domain.LoggedUser; import com.netgrif.application.engine.auth.service.InvalidUserTokenException; @@ -131,8 +132,8 @@ public MessageResource verifyToken(@RequestBody String token) { @Operation(summary = "Verify validity of an authentication token") @GetMapping(value = "/verify", produces = MediaTypes.HAL_JSON_VALUE) - public MessageResource verifyAuthToken(Authentication auth) { - LoggedUser loggedUser = (LoggedUser) auth.getPrincipal(); + public MessageResource verifyAuthToken() { + LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedUser()); return MessageResource.successMessage("Auth Token successfully verified, for user [" + loggedUser.getId() + "] " + loggedUser.getName()); } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java index eb9cb4f90f9..fe58c3d5c2c 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java @@ -2,9 +2,11 @@ import com.netgrif.application.engine.AsyncRunner; import com.netgrif.application.engine.adapter.spring.petrinet.service.ProcessRoleService; +import com.netgrif.application.engine.auth.service.UserService; import com.netgrif.application.engine.elastic.service.interfaces.IElasticPetriNetService; import com.netgrif.application.engine.eventoutcomes.LocalisedEventOutcomeFactory; import com.netgrif.application.engine.importer.service.Importer; +import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; import com.netgrif.application.engine.objects.auth.domain.LoggedUser; import com.netgrif.application.engine.objects.petrinet.domain.PetriNet; import com.netgrif.application.engine.objects.petrinet.domain.PetriNetSearch; @@ -82,6 +84,9 @@ public class PetriNetController { @Autowired private AsyncRunner asyncRunner; + @Autowired + private UserService userService; + public static String decodeUrl(String s1) { try { if (s1 == null) @@ -122,8 +127,8 @@ public EntityModel importPetriNet( @Operation(summary = "Get all processes", security = {@SecurityRequirement(name = "BasicAuth")}) @GetMapping(produces = MediaTypes.HAL_JSON_VALUE) - public ResponseEntity> getAll(@RequestParam(value = "indentifier", required = false) String identifier, @RequestParam(value = "version", required = false) String version, Pageable pageable, Authentication auth, Locale locale) { - LoggedUser user = (LoggedUser) auth.getPrincipal(); + public ResponseEntity> getAll(@RequestParam(value = "indentifier", required = false) String identifier, @RequestParam(value = "version", required = false) String version, Pageable pageable, Locale locale) { + LoggedUser user = ActorTransformer.toLoggedUser(userService.getLoggedUser()); if (identifier != null && version == null) { return ResponseEntity.ok(service.getReferencesByIdentifier(identifier, user, locale, pageable)); } else if (identifier == null && version != null) { diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/AbstractTaskController.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/AbstractTaskController.java index eafa862bf8c..4c2f2da74df 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/AbstractTaskController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/AbstractTaskController.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.netgrif.application.engine.adapter.spring.auth.domain.LoggedUserImpl; import com.netgrif.application.engine.auth.service.UserService; +import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; import com.netgrif.application.engine.objects.auth.domain.LoggedUser; import com.netgrif.application.engine.elastic.service.interfaces.IElasticTaskService; import com.netgrif.application.engine.elastic.web.requestbodies.singleaslist.SingleElasticTaskSearchRequestAsList; @@ -69,11 +70,11 @@ public AbstractTaskController(ITaskService taskService, } - public PagedModel getAll(Authentication auth, Pageable pageable, PagedResourcesAssembler assembler, Locale locale) { - LoggedUser loggedUser = (LoggedUser) auth.getPrincipal(); + public PagedModel getAll(Pageable pageable, PagedResourcesAssembler assembler, Locale locale) { + LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedUser()); Page page = taskService.getAll(loggedUser, pageable, locale); Link selfLink = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(TaskController.class) - .getAll(auth, pageable, assembler, locale)).withRel("all"); + .getAll(pageable, assembler, locale)).withRel("all"); PagedModel resources = assembler.toModel(page, new TaskResourceAssembler(locale), selfLink); ResourceLinkAssembler.addLinks(resources, Task.class, selfLink.getRel().toString()); return PagedModel.of(page.stream().map(t -> new LocalisedTaskResource(new com.netgrif.application.engine.workflow.web.responsebodies.Task(t, locale))).toList(), new PagedModel.PageMetadata(pageable.getPageSize(), pageable.getPageNumber(), page.getTotalElements())); @@ -81,7 +82,6 @@ public PagedModel getAll(Authentication auth, Pageable pa public PagedModel getAllByCases(List cases, Pageable pageable, PagedResourcesAssembler assembler, Locale locale) { Page page = taskService.findByCases(pageable, cases); - Link selfLink = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(TaskController.class) .getAllByCases(cases, pageable, assembler, locale)).withRel("case"); PagedModel resources = assembler.toModel(page, new TaskResourceAssembler(locale), selfLink); @@ -100,8 +100,9 @@ public LocalisedTaskResource getOne(String taskId, Locale locale) { return new LocalisedTaskResource(new com.netgrif.application.engine.workflow.web.responsebodies.Task(task, locale)); } - public EntityModel assign(LoggedUser loggedUser, String taskId, Locale locale) { + public EntityModel assign(String taskId, Locale locale) { try { + LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedUser()); return EventOutcomeWithMessageResource.successMessage("LocalisedTask " + taskId + " assigned to " + loggedUser.getName(), LocalisedEventOutcomeFactory.from(taskService.assignTask(loggedUser, taskId), locale)); } catch (TransitionNotExecutableException e) { @@ -110,8 +111,9 @@ public EntityModel assign(LoggedUser loggedUser, String } } - public EntityModel delegate(LoggedUser loggedUser, String taskId, String delegatedId, Locale locale) { + public EntityModel delegate(String taskId, String delegatedId, Locale locale) { try { + LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedUser()); return EventOutcomeWithMessageResource.successMessage("LocalisedTask " + taskId + " assigned to [" + delegatedId + "]", LocalisedEventOutcomeFactory.from(taskService.delegateTask(loggedUser, delegatedId, taskId), locale)); } catch (Exception e) { @@ -120,9 +122,9 @@ public EntityModel delegate(LoggedUser loggedUser, Stri } } - public EntityModel finish(LoggedUser loggedUser, String taskId, Locale locale) { - + public EntityModel finish(String taskId, Locale locale) { try { + LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedUser()); return EventOutcomeWithMessageResource.successMessage("LocalisedTask " + taskId + " finished", LocalisedEventOutcomeFactory.from(taskService.finishTask(loggedUser, taskId), locale)); } catch (Exception e) { @@ -135,8 +137,9 @@ public EntityModel finish(LoggedUser loggedUser, String } } - public EntityModel cancel(LoggedUser loggedUser, String taskId, Locale locale) { + public EntityModel cancel(String taskId, Locale locale) { try { + LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedUser()); return EventOutcomeWithMessageResource.successMessage("LocalisedTask " + taskId + " canceled", LocalisedEventOutcomeFactory.from(taskService.cancelTask(loggedUser, taskId), locale)); } catch (Exception e) { @@ -149,28 +152,29 @@ public EntityModel cancel(LoggedUser loggedUser, String } } - public PagedModel getMy(Authentication auth, Pageable pageable, PagedResourcesAssembler assembler, Locale locale) { - Page page = taskService.findByUser(pageable, userService.transformToUser(((LoggedUserImpl) auth.getPrincipal()))); + public PagedModel getMy(Pageable pageable, PagedResourcesAssembler assembler, Locale locale) { + Page page = taskService.findByUser(pageable, userService.getLoggedUser()); Link selfLink = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(TaskController.class) - .getMy(auth, pageable, assembler, locale)).withRel("my"); + .getMy(pageable, assembler, locale)).withRel("my"); PagedModel resources = assembler.toModel(page, new TaskResourceAssembler(locale), selfLink); ResourceLinkAssembler.addLinks(resources, Task.class, selfLink.getRel().toString()); return PagedModel.of(page.stream().map(t -> new LocalisedTaskResource(new com.netgrif.application.engine.workflow.web.responsebodies.Task(t, locale))).toList(), new PagedModel.PageMetadata(pageable.getPageSize(), pageable.getPageNumber(), page.getTotalElements())); } - public PagedModel getMyFinished(Pageable pageable, Authentication auth, PagedResourcesAssembler assembler, Locale locale) { - Page page = taskService.findByUser(pageable, userService.transformToUser(((LoggedUserImpl) auth.getPrincipal()))); + public PagedModel getMyFinished(Pageable pageable, PagedResourcesAssembler assembler, Locale locale) { + Page page = taskService.findByUser(pageable, userService.getLoggedUser()); Link selfLink = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(TaskController.class) - .getMyFinished(pageable, auth, assembler, locale)).withRel("finished"); + .getMyFinished(pageable, assembler, locale)).withRel("finished"); PagedModel resources = assembler.toModel(page, new TaskResourceAssembler(locale), selfLink); ResourceLinkAssembler.addLinks(resources, Task.class, selfLink.getRel().toString()); return PagedModel.of(page.stream().map(t -> new LocalisedTaskResource(new com.netgrif.application.engine.workflow.web.responsebodies.Task(t, locale))).toList(), new PagedModel.PageMetadata(pageable.getPageSize(), pageable.getPageNumber(), page.getTotalElements())); } - public PagedModel search(Authentication auth, Pageable pageable, SingleTaskSearchRequestAsList searchBody, MergeFilterOperation operation, PagedResourcesAssembler assembler, Locale locale) { - Page tasks = taskService.search(searchBody.getList(), pageable, (LoggedUser) auth.getPrincipal(), locale, operation == MergeFilterOperation.AND); + public PagedModel search(Pageable pageable, SingleTaskSearchRequestAsList searchBody, MergeFilterOperation operation, PagedResourcesAssembler assembler, Locale locale) { + LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedUser()); + Page tasks = taskService.search(searchBody.getList(), pageable, loggedUser, locale, operation == MergeFilterOperation.AND); Link selfLink = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(TaskController.class) - .search(auth, pageable, searchBody, operation, assembler, locale)).withRel("search"); + .search(pageable, searchBody, operation, assembler, locale)).withRel("search"); PagedModel resources = assembler.toModel(tasks, new TaskResourceAssembler(locale), selfLink); ResourceLinkAssembler.addLinks(resources, Task.class, selfLink.getRel().toString()); return PagedModel.of(tasks.stream().map(t -> new LocalisedTaskResource(new com.netgrif.application.engine.workflow.web.responsebodies.Task(t, locale))).toList(), new PagedModel.PageMetadata(pageable.getPageSize(), pageable.getPageNumber(), tasks.getTotalElements())); @@ -185,17 +189,19 @@ public PagedModel searchPublic(LoggedUser loggedUser, Pag return PagedModel.of(tasks.stream().map(t -> new LocalisedTaskResource(new com.netgrif.application.engine.workflow.web.responsebodies.Task(t, locale))).toList(), new PagedModel.PageMetadata(pageable.getPageSize(), pageable.getPageNumber(), tasks.getTotalElements())); } - public PagedModel searchElastic(Authentication auth, Pageable pageable, SingleElasticTaskSearchRequestAsList searchBody, MergeFilterOperation operation, PagedResourcesAssembler assembler, Locale locale) { - Page tasks = searchService.search(searchBody.getList(), (LoggedUser) auth.getPrincipal(), pageable, locale, operation == MergeFilterOperation.AND); + public PagedModel searchElastic(Pageable pageable, SingleElasticTaskSearchRequestAsList searchBody, MergeFilterOperation operation, PagedResourcesAssembler assembler, Locale locale) { + LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedUser()); + Page tasks = searchService.search(searchBody.getList(), loggedUser, pageable, locale, operation == MergeFilterOperation.AND); Link selfLink = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(TaskController.class) - .searchElastic(auth, pageable, searchBody, operation, assembler, locale)).withRel("search_es"); + .searchElastic(pageable, searchBody, operation, assembler, locale)).withRel("search_es"); PagedModel resources = assembler.toModel(tasks, new TaskResourceAssembler(locale), selfLink); ResourceLinkAssembler.addLinks(resources, Task.class, selfLink.getRel().toString()); return PagedModel.of(tasks.stream().map(t -> new LocalisedTaskResource(new com.netgrif.application.engine.workflow.web.responsebodies.Task(t, locale))).toList(), new PagedModel.PageMetadata(pageable.getPageSize(), pageable.getPageNumber(), tasks.getTotalElements())); } - public CountResponse count(SingleElasticTaskSearchRequestAsList query, MergeFilterOperation operation, Authentication auth, Locale locale) { - long count = searchService.count(query.getList(), (LoggedUser) auth.getPrincipal(), locale, operation == MergeFilterOperation.AND); + public CountResponse count(SingleElasticTaskSearchRequestAsList query, MergeFilterOperation operation, Locale locale) { + LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedUser()); + long count = searchService.count(query.getList(), loggedUser, locale, operation == MergeFilterOperation.AND); return CountResponse.taskCount(count); } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/PublicTaskController.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/PublicTaskController.java index aa32ca242d7..27631b2922e 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/PublicTaskController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/PublicTaskController.java @@ -77,8 +77,7 @@ public List getTasksOfCase(@PathVariable("id") String caseId, Loc description = "Caller doesn't fulfill the authorisation requirements" )}) public EntityModel assign(@PathVariable("id") String taskId, Locale locale) { - LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedUser()); - return super.assign(loggedUser, taskId, locale); + return super.assign(taskId, locale); } @PreAuthorize("@taskAuthorizationService.canCallFinish(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") @@ -92,8 +91,7 @@ public EntityModel assign(@PathVariable("id") String ta description = "Caller doesn't fulfill the authorisation requirements" )}) public EntityModel finish(@PathVariable("id") String taskId, Locale locale) { - LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedUser()); - return super.finish(loggedUser, taskId, locale); + return super.finish(taskId, locale); } @PreAuthorize("@taskAuthorizationService.canCallCancel(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") @@ -107,8 +105,7 @@ public EntityModel finish(@PathVariable("id") String ta description = "Caller doesn't fulfill the authorisation requirements" )}) public EntityModel cancel(@PathVariable("id") String taskId, Locale locale) { - LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedUser()); - return super.cancel(loggedUser, taskId, locale); + return super.cancel(taskId, locale); } @Override @@ -203,9 +200,9 @@ public EntityModel deleteNamedFile(@PathVariable("id") return super.deleteNamedFile(requestBody.getParentTaskId(), requestBody.getFieldId(), requestBody.getFileName()); } - @Operation(summary = "Generic task search on Mongo database") - @PostMapping(value = "/search", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaTypes.HAL_JSON_VALUE) - public PagedModel search(Pageable pageable, @RequestBody SingleTaskSearchRequestAsList searchBody, @RequestParam(defaultValue = "OR") MergeFilterOperation operation, PagedResourcesAssembler assembler, Locale locale) { - return super.searchPublic(ActorTransformer.toLoggedUser(userService.getLoggedUser()), pageable, searchBody, operation, assembler, locale); - } +// @Operation(summary = "Generic task search on Mongo database") +// @PostMapping(value = "/search", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaTypes.HAL_JSON_VALUE) +// public PagedModel search(Pageable pageable, @RequestBody SingleTaskSearchRequestAsList searchBody, @RequestParam(defaultValue = "OR") MergeFilterOperation operation, PagedResourcesAssembler assembler, Locale locale) { +// return super.searchPublic(ActorTransformer.toLoggedUser(userService.getLoggedUser()), pageable, searchBody, operation, assembler, locale); +// } } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/TaskController.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/TaskController.java index bab31f35cfa..4384ba54d6a 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/TaskController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/TaskController.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.netgrif.application.engine.auth.service.UserService; +import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; import com.netgrif.application.engine.objects.auth.domain.LoggedUser; import com.netgrif.application.engine.elastic.service.interfaces.IElasticTaskService; import com.netgrif.application.engine.elastic.web.requestbodies.singleaslist.SingleElasticTaskSearchRequestAsList; @@ -62,8 +63,8 @@ public TaskController(ITaskService taskService, @Override @Operation(summary = "Get all tasks", security = {@SecurityRequirement(name = "BasicAuth")}) @GetMapping(produces = MediaTypes.HAL_JSON_VALUE) - public PagedModel getAll(Authentication auth, Pageable pageable, PagedResourcesAssembler assembler, Locale locale) { - return super.getAll(auth, pageable, assembler, locale); + public PagedModel getAll(Pageable pageable, PagedResourcesAssembler assembler, Locale locale) { + return super.getAll(pageable, assembler, locale); } @Override @@ -87,7 +88,7 @@ public LocalisedTaskResource getOne(@PathVariable("id") String taskId, Locale lo return super.getOne(taskId, locale); } - @PreAuthorize("@taskAuthorizationService.canCallAssign(#auth.getPrincipal(), #taskId)") + @PreAuthorize("@taskAuthorizationService.canCallAssign(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") @Operation(summary = "Assign task", description = "Caller must be able to perform the task, or must be an ADMIN", security = {@SecurityRequirement(name = "BasicAuth")}) @@ -96,12 +97,11 @@ public LocalisedTaskResource getOne(@PathVariable("id") String taskId, Locale lo @ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), }) - public EntityModel assign(Authentication auth, @PathVariable("id") String taskId, Locale locale) { - LoggedUser loggedUser = (LoggedUser) auth.getPrincipal(); - return super.assign(loggedUser, taskId, locale); + public EntityModel assign(@PathVariable("id") String taskId, Locale locale) { + return super.assign(taskId, locale); } - @PreAuthorize("@taskAuthorizationService.canCallDelegate(#auth.getPrincipal(), #taskId)") + @PreAuthorize("@taskAuthorizationService.canCallDelegate(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") @Operation(summary = "Delegate task", description = "Caller must be able to delegate the task, or must be an ADMIN", security = {@SecurityRequirement(name = "BasicAuth")}) @@ -110,12 +110,11 @@ public EntityModel assign(Authentication auth, @PathVar @ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), }) - public EntityModel delegate(Authentication auth, @PathVariable("id") String taskId, @RequestBody String delegatedId, Locale locale) { - LoggedUser loggedUser = (LoggedUser) auth.getPrincipal(); - return super.delegate(loggedUser, taskId, delegatedId, locale); + public EntityModel delegate(@PathVariable("id") String taskId, @RequestBody String delegatedId, Locale locale) { + return super.delegate(taskId, delegatedId, locale); } - @PreAuthorize("@taskAuthorizationService.canCallFinish(#auth.getPrincipal(), #taskId)") + @PreAuthorize("@taskAuthorizationService.canCallFinish(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") @Operation(summary = "Finish task", description = "Caller must be assigned to the task, or must be an ADMIN", security = {@SecurityRequirement(name = "BasicAuth")}) @@ -124,12 +123,11 @@ public EntityModel delegate(Authentication auth, @PathV @ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), }) - public EntityModel finish(Authentication auth, @PathVariable("id") String taskId, Locale locale) { - LoggedUser loggedUser = (LoggedUser) auth.getPrincipal(); - return super.finish(loggedUser, taskId, locale); + public EntityModel finish(@PathVariable("id") String taskId, Locale locale) { + return super.finish(taskId, locale); } - @PreAuthorize("@taskAuthorizationService.canCallCancel(#auth.getPrincipal(), #taskId)") + @PreAuthorize("@taskAuthorizationService.canCallCancel(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") @Operation(summary = "Cancel task", description = "Caller must be assigned to the task, or must be an ADMIN", security = {@SecurityRequirement(name = "BasicAuth")}) @@ -138,44 +136,43 @@ public EntityModel finish(Authentication auth, @PathVar @ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), }) - public EntityModel cancel(Authentication auth, @PathVariable("id") String taskId, Locale locale) { - LoggedUser loggedUser = (LoggedUser) auth.getPrincipal(); - return super.cancel(loggedUser, taskId, locale); + public EntityModel cancel(@PathVariable("id") String taskId, Locale locale) { + return super.cancel(taskId, locale); } @Override @Operation(summary = "Get all tasks assigned to logged user", security = {@SecurityRequirement(name = "BasicAuth")}) @GetMapping(value = "/my", produces = MediaTypes.HAL_JSON_VALUE) - public PagedModel getMy(Authentication auth, Pageable pageable, PagedResourcesAssembler assembler, Locale locale) { - return super.getMy(auth, pageable, assembler, locale); + public PagedModel getMy(Pageable pageable, PagedResourcesAssembler assembler, Locale locale) { + return super.getMy(pageable, assembler, locale); } @Override @Operation(summary = "Get all finished tasks by logged user", security = {@SecurityRequirement(name = "BasicAuth")}) @GetMapping(value = "/my/finished", produces = MediaTypes.HAL_JSON_VALUE) - public PagedModel getMyFinished(Pageable pageable, Authentication auth, PagedResourcesAssembler assembler, Locale locale) { - return super.getMyFinished(pageable, auth, assembler, locale); + public PagedModel getMyFinished(Pageable pageable, PagedResourcesAssembler assembler, Locale locale) { + return super.getMyFinished(pageable, assembler, locale); } @Override @Operation(summary = "Generic task search on Mongo database", security = {@SecurityRequirement(name = "BasicAuth")}) @PostMapping(value = "/search", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaTypes.HAL_JSON_VALUE) - public PagedModel search(Authentication auth, Pageable pageable, @RequestBody SingleTaskSearchRequestAsList searchBody, @RequestParam(defaultValue = "OR") MergeFilterOperation operation, PagedResourcesAssembler assembler, Locale locale) { - return super.search(auth, pageable, searchBody, operation, assembler, locale); + public PagedModel search(Pageable pageable, @RequestBody SingleTaskSearchRequestAsList searchBody, @RequestParam(defaultValue = "OR") MergeFilterOperation operation, PagedResourcesAssembler assembler, Locale locale) { + return super.search(pageable, searchBody, operation, assembler, locale); } @Override @Operation(summary = "Generic task search on Elasticsearch database", security = {@SecurityRequirement(name = "BasicAuth")}) @PostMapping(value = "/search_es", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaTypes.HAL_JSON_VALUE) - public PagedModel searchElastic(Authentication auth, Pageable pageable, @RequestBody SingleElasticTaskSearchRequestAsList searchBody, @RequestParam(defaultValue = "OR") MergeFilterOperation operation, PagedResourcesAssembler assembler, Locale locale) { - return super.searchElastic(auth, pageable, searchBody, operation, assembler, locale); + public PagedModel searchElastic(Pageable pageable, @RequestBody SingleElasticTaskSearchRequestAsList searchBody, @RequestParam(defaultValue = "OR") MergeFilterOperation operation, PagedResourcesAssembler assembler, Locale locale) { + return super.searchElastic(pageable, searchBody, operation, assembler, locale); } @Override @Operation(summary = "Count tasks by provided criteria", security = {@SecurityRequirement(name = "BasicAuth")}) @PostMapping(value = "/count", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) - public CountResponse count(@RequestBody SingleElasticTaskSearchRequestAsList query, @RequestParam(defaultValue = "OR") MergeFilterOperation operation, Authentication auth, Locale locale) { - return super.count(query, operation, auth, locale); + public CountResponse count(@RequestBody SingleElasticTaskSearchRequestAsList query, @RequestParam(defaultValue = "OR") MergeFilterOperation operation, Locale locale) { + return super.count(query, operation, locale); } @Override @@ -185,7 +182,7 @@ public EntityModel getData(@PathVariable("id") String t return super.getData(taskId, locale); } - @PreAuthorize("@taskAuthorizationService.canCallSaveData(#auth.getPrincipal(), #taskId)") + @PreAuthorize("@taskAuthorizationService.canCallSaveData(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") @Operation(summary = "Set task data", description = "Caller must be assigned to the task, or must be an ADMIN", security = {@SecurityRequirement(name = "BasicAuth")}) @@ -195,11 +192,11 @@ public EntityModel getData(@PathVariable("id") String t @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), }) - public EntityModel setData(Authentication auth, @PathVariable("id") String taskId, @RequestBody ObjectNode dataBody, Locale locale) { + public EntityModel setData(@PathVariable("id") String taskId, @RequestBody ObjectNode dataBody, Locale locale) { return super.setData(taskId, dataBody, locale); } - @PreAuthorize("@taskAuthorizationService.canCallSaveFile(#auth.getPrincipal(), #taskId)") + @PreAuthorize("@taskAuthorizationService.canCallSaveFile(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") @Operation(summary = "Upload file into the task", description = "Caller must be assigned to the task, or must be an ADMIN", security = {@SecurityRequirement(name = "BasicAuth")}) @@ -208,7 +205,7 @@ public EntityModel setData(Authentication auth, @PathVa @ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), }) - public EntityModel saveFile(Authentication auth, @PathVariable("id") String taskId, @RequestPart(value = "data") FileFieldRequest dataBody, @RequestPart(value = "file") MultipartFile multipartFile, Locale locale) { + public EntityModel saveFile(@PathVariable("id") String taskId, @RequestPart(value = "data") FileFieldRequest dataBody, @RequestPart(value = "file") MultipartFile multipartFile, Locale locale) { return super.saveFile(taskId, multipartFile, dataBody, locale); } @@ -218,7 +215,7 @@ public ResponseEntity getFile(@PathVariable("id") String taskId, @Requ return super.getFile(taskId, fieldId); } - @PreAuthorize("@taskAuthorizationService.canCallSaveFile(#auth.getPrincipal(), #taskId)") + @PreAuthorize("@taskAuthorizationService.canCallSaveFile(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") @Operation(summary = "Remove file from the task", description = "Caller must be assigned to the task, or must be an ADMIN", security = {@SecurityRequirement(name = "BasicAuth")}) @@ -227,11 +224,11 @@ public ResponseEntity getFile(@PathVariable("id") String taskId, @Requ @ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), }) - public EntityModel deleteFile(Authentication auth, @PathVariable("id") String taskId, @RequestBody FileFieldRequest requestBody) { + public EntityModel deleteFile(@PathVariable("id") String taskId, @RequestBody FileFieldRequest requestBody) { return super.deleteFile(requestBody.getParentTaskId(), requestBody.getFieldId()); } - @PreAuthorize("@taskAuthorizationService.canCallSaveFile(#auth.getPrincipal(), #taskId)") + @PreAuthorize("@taskAuthorizationService.canCallSaveFile(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") @Operation(summary = "Upload multiple files into the task", description = "Caller must be assigned to the task, or must be an ADMIN", security = {@SecurityRequirement(name = "BasicAuth")}) @@ -240,7 +237,7 @@ public EntityModel deleteFile(Authentication auth, @Pat @ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), }) - public EntityModel saveFiles(Authentication auth, @PathVariable("id") String taskId, @RequestPart(value = "data") FileFieldRequest requestBody, @RequestPart(value = "files") MultipartFile[] multipartFiles) { + public EntityModel saveFiles(@PathVariable("id") String taskId, @RequestPart(value = "data") FileFieldRequest requestBody, @RequestPart(value = "files") MultipartFile[] multipartFiles) { return super.saveFiles(taskId, multipartFiles, requestBody); } @@ -250,7 +247,7 @@ public ResponseEntity getNamedFile(@PathVariable("id") String taskId, return super.getNamedFile(taskId, fieldId, fileName); } - @PreAuthorize("@taskAuthorizationService.canCallSaveFile(#auth.getPrincipal(), #taskId)") + @PreAuthorize("@taskAuthorizationService.canCallSaveFile(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") @Operation(summary = "Remove file from tasks file list field value", description = "Caller must be assigned to the task, or must be an ADMIN", security = {@SecurityRequirement(name = "BasicAuth")}) @@ -259,7 +256,7 @@ public ResponseEntity getNamedFile(@PathVariable("id") String taskId, @ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), }) - public EntityModel deleteNamedFile(Authentication auth, @PathVariable("id") String taskId, @RequestBody FileFieldRequest requestBody) { + public EntityModel deleteNamedFile(@PathVariable("id") String taskId, @RequestBody FileFieldRequest requestBody) { return super.deleteNamedFile(requestBody.getParentTaskId(), requestBody.getFieldId(), requestBody.getFileName()); } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/responsebodies/ResourceLinkAssembler.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/responsebodies/ResourceLinkAssembler.java index 401b8306bbb..640eea84f72 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/responsebodies/ResourceLinkAssembler.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/responsebodies/ResourceLinkAssembler.java @@ -21,25 +21,25 @@ public static void addLinks(PagedModel pagedResources, Class type, String selfRe private static void addTasksLinks(PagedModel pagedResources, String selfRel) { if (!selfRel.equalsIgnoreCase("all")) pagedResources.add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(TaskController.class) - .getAll(null, null, null, null)).withRel("all")); + .getAll(null, null, null)).withRel("all")); if (!selfRel.equalsIgnoreCase("case")) pagedResources.add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(TaskController.class) .getAllByCases(null, null, null, null)).withRel("case")); if (!selfRel.equalsIgnoreCase("my")) pagedResources.add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(TaskController.class) - .getMy(null, null, null, null)).withRel("my")); + .getMy(null, null, null)).withRel("my")); if (!selfRel.equalsIgnoreCase("finished")) pagedResources.add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(TaskController.class) - .getMyFinished(null, null, null, null)).withRel("finished")); + .getMyFinished(null, null, null)).withRel("finished")); if (!selfRel.equalsIgnoreCase("search")) pagedResources.add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(TaskController.class) - .search(null, null, null, null, null, null)).withRel("search")); + .search(null, null, null, null, null)).withRel("search")); if (!selfRel.equalsIgnoreCase("search_es")) pagedResources.add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(TaskController.class) - .searchElastic(null, null, null, null, null, null)).withRel("search_es")); + .searchElastic(null, null, null, null, null)).withRel("search_es")); if (!selfRel.equalsIgnoreCase("count")) pagedResources.add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(TaskController.class) - .count(null, null, null, null)).withRel("count")); + .count(null, null, null)).withRel("count")); } private static void addCasesLinks(PagedModel pagedResources, String selfRel) { From 57e9f59f41edf82cf2bae6446caea9467fc9ee70 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Thu, 15 Jan 2026 10:25:02 +0100 Subject: [PATCH 07/14] Refactor user-related services and improve type consistency. Replaced `LoggedUser` with `AbstractUser` across services for enhanced flexibility and alignment. Introduced `@RequiredArgsConstructor` annotation to eliminate redundant constructors and streamline dependency injection. Various private methods were reorganized for clarity and reusability, ensuring consistent coding patterns. --- .../service/AbstractAuthorizationService.java | 2 +- .../service/TaskAuthorizationService.java | 79 ++-- .../service/WorkflowAuthorizationService.java | 12 +- .../interfaces/ITaskAuthorizationService.java | 18 +- .../IWorkflowAuthorizationService.java | 4 +- .../workflow/web/AbstractTaskController.java | 31 +- .../workflow/web/PublicTaskController.java | 418 +++++++++--------- .../engine/workflow/web/TaskController.java | 20 +- .../workflow/web/WorkflowController.java | 107 ++--- .../web/responsebodies/CaseResource.java | 2 +- .../responsebodies/LocalisedTaskResource.java | 8 +- .../responsebodies/ResourceLinkAssembler.java | 4 +- 12 files changed, 332 insertions(+), 373 deletions(-) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/AbstractAuthorizationService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/AbstractAuthorizationService.java index a24222a4929..268ce0ab043 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/AbstractAuthorizationService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/AbstractAuthorizationService.java @@ -31,7 +31,7 @@ protected Map getAggregatePermissions(AbstractUser user, Map> role, Map aggregatePermissions) { + private void aggregatePermission(Set userProcessRoleIDs, Map.Entry> role, Map aggregatePermissions) { if (userProcessRoleIDs.contains(role.getKey())) { for (Map.Entry permission : role.getValue().entrySet()) { if (aggregatePermissions.containsKey(permission.getKey())) { diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/TaskAuthorizationService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/TaskAuthorizationService.java index 83c1a6b57f8..a4898c9f74d 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/TaskAuthorizationService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/TaskAuthorizationService.java @@ -8,6 +8,7 @@ import com.netgrif.application.engine.objects.workflow.domain.Task; import com.netgrif.application.engine.workflow.service.interfaces.ITaskAuthorizationService; import com.netgrif.application.engine.workflow.service.interfaces.ITaskService; +import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -15,14 +16,14 @@ import java.util.Map; @Service +@RequiredArgsConstructor public class TaskAuthorizationService extends AbstractAuthorizationService implements ITaskAuthorizationService { - @Autowired - ITaskService taskService; + private final ITaskService taskService; @Override - public Boolean userHasAtLeastOneRolePermission(LoggedUser loggedUser, String taskId, RolePermission... permissions) { - return userHasAtLeastOneRolePermission(ActorTransformer.toUser(loggedUser), taskService.findById(taskId), permissions); + public Boolean userHasAtLeastOneRolePermission(AbstractUser user, String taskId, RolePermission... permissions) { + return userHasAtLeastOneRolePermission(user, taskService.findById(taskId), permissions); } @Override @@ -42,8 +43,8 @@ public Boolean userHasAtLeastOneRolePermission(AbstractUser user, Task task, Rol } @Override - public Boolean userHasUserListPermission(LoggedUser loggedUser, String taskId, RolePermission... permissions) { - return userHasUserListPermission(ActorTransformer.toUser(loggedUser), taskService.findById(taskId), permissions); + public Boolean userHasUserListPermission(AbstractUser user, String taskId, RolePermission... permissions) { + return userHasUserListPermission(user, taskService.findById(taskId), permissions); } @Override @@ -70,11 +71,6 @@ public Boolean userHasUserListPermission(AbstractUser user, Task task, RolePermi return Arrays.stream(permissions).anyMatch(permission -> hasPermission(userPermissions.get(permission.toString()))); } - @Override - public boolean isAssignee(LoggedUser loggedUser, String taskId) { - return isAssignee(ActorTransformer.toUser(loggedUser), taskService.findById(taskId)); - } - @Override public boolean isAssignee(AbstractUser user, String taskId) { return isAssignee(user, taskService.findById(taskId)); @@ -82,50 +78,43 @@ public boolean isAssignee(AbstractUser user, String taskId) { @Override public boolean isAssignee(AbstractUser user, Task task) { - if (!isAssigned(task)) + if (!isAssigned(task)) { return false; - else + } else { // TODO: impersonation // return task.getUserId().equals(user.getSelfOrImpersonated().getStringId()) || (Boolean) user.getAttributeValue("anonymous"); return task.getUserId().equals(user.getStringId()) || (Boolean) user.getAttributeValue("anonymous"); - } - - private boolean isAssigned(String taskId) { - return isAssigned(taskService.findById(taskId)); - } - - private boolean isAssigned(Task task) { - return task.getUserId() != null; + } } @Override - public boolean canCallAssign(LoggedUser loggedUser, String taskId) { - Boolean rolePerm = userHasAtLeastOneRolePermission(loggedUser, taskId, RolePermission.ASSIGN); - Boolean userPerm = userHasUserListPermission(loggedUser, taskId, RolePermission.ASSIGN); + public boolean canCallAssign(AbstractUser user, String taskId) { + Boolean rolePerm = userHasAtLeastOneRolePermission(user, taskId, RolePermission.ASSIGN); + Boolean userPerm = userHasUserListPermission(user, taskId, RolePermission.ASSIGN); // TODO: impersonation // return loggedUser.getSelfOrImpersonated().isAdmin() || (userPerm == null ? (rolePerm != null && rolePerm) : userPerm); - return loggedUser.isAdmin() || (userPerm == null ? (rolePerm != null && rolePerm) : userPerm); + return user.isAdmin() || (userPerm == null ? (rolePerm != null && rolePerm) : userPerm); } @Override - public boolean canCallDelegate(LoggedUser loggedUser, String taskId) { - Boolean rolePerm = userHasAtLeastOneRolePermission(loggedUser, taskId, RolePermission.DELEGATE); - Boolean userPerm = userHasUserListPermission(loggedUser, taskId, RolePermission.DELEGATE); + public boolean canCallDelegate(AbstractUser user, String taskId) { + Boolean rolePerm = userHasAtLeastOneRolePermission(user, taskId, RolePermission.DELEGATE); + Boolean userPerm = userHasUserListPermission(user, taskId, RolePermission.DELEGATE); // TODO: impersonation // return loggedUser.getSelfOrImpersonated().isAdmin() || (userPerm == null ? (rolePerm != null && rolePerm) : userPerm); - return loggedUser.isAdmin() || (userPerm == null ? (rolePerm != null && rolePerm) : userPerm); + return user.isAdmin() || (userPerm == null ? (rolePerm != null && rolePerm) : userPerm); } @Override - public boolean canCallFinish(LoggedUser loggedUser, String taskId) throws IllegalTaskStateException { + public boolean canCallFinish(AbstractUser user, String taskId) throws IllegalTaskStateException { if (!isAssigned(taskId)) throw new IllegalTaskStateException("Task with ID '" + taskId + "' cannot be finished, because it is not assigned!"); - Boolean rolePerm = userHasAtLeastOneRolePermission(loggedUser, taskId, RolePermission.FINISH); - Boolean userPerm = userHasUserListPermission(loggedUser, taskId, RolePermission.FINISH); + Boolean rolePerm = userHasAtLeastOneRolePermission(user, taskId, RolePermission.FINISH); + Boolean userPerm = userHasUserListPermission(user, taskId, RolePermission.FINISH); // TODO: impersonation // return loggedUser.getSelfOrImpersonated().isAdmin() || ((userPerm == null ? (rolePerm != null && rolePerm) : userPerm) && isAssignee(loggedUser, taskId)); - return loggedUser.isAdmin() || ((userPerm == null ? (rolePerm != null && rolePerm) : userPerm) && isAssignee(loggedUser, taskId)); + return user.isAdmin() || ((userPerm == null ? (rolePerm != null && rolePerm) : userPerm) && isAssignee(user, taskId)); } private boolean canAssignedCancel(AbstractUser user, String taskId) { @@ -139,28 +128,36 @@ private boolean canAssignedCancel(AbstractUser user, String taskId) { } @Override - public boolean canCallCancel(LoggedUser loggedUser, String taskId) throws IllegalTaskStateException { + public boolean canCallCancel(AbstractUser user, String taskId) throws IllegalTaskStateException { if (!isAssigned(taskId)) throw new IllegalTaskStateException("Task with ID '" + taskId + "' cannot be canceled, because it is not assigned!"); - Boolean rolePerm = userHasAtLeastOneRolePermission(loggedUser, taskId, RolePermission.CANCEL); - Boolean userPerm = userHasUserListPermission(loggedUser, taskId, RolePermission.CANCEL); + Boolean rolePerm = userHasAtLeastOneRolePermission(user, taskId, RolePermission.CANCEL); + Boolean userPerm = userHasUserListPermission(user, taskId, RolePermission.CANCEL); // TODO: impersonation // return loggedUser.getSelfOrImpersonated().isAdmin() || ((userPerm == null ? (rolePerm != null && rolePerm) : userPerm) && isAssignee(loggedUser, taskId)) && canAssignedCancel(userService.transformToUser((LoggedUserImpl) loggedUser), taskId); - return loggedUser.isAdmin() || ((userPerm == null ? (rolePerm != null && rolePerm) : userPerm) && isAssignee(loggedUser, taskId)) && canAssignedCancel(ActorTransformer.toUser(loggedUser), taskId); + return user.isAdmin() || ((userPerm == null ? (rolePerm != null && rolePerm) : userPerm) && isAssignee(user, taskId)) && canAssignedCancel(user, taskId); } @Override - public boolean canCallSaveData(LoggedUser loggedUser, String taskId) { + public boolean canCallSaveData(AbstractUser user, String taskId) { // TODO: impersonation // return loggedUser.getSelfOrImpersonated().isAdmin() || isAssignee(loggedUser, taskId); - return loggedUser.isAdmin() || isAssignee(loggedUser, taskId); + return user.isAdmin() || isAssignee(user, taskId); } @Override - public boolean canCallSaveFile(LoggedUser loggedUser, String taskId) { + public boolean canCallSaveFile(AbstractUser user, String taskId) { // TODO: impersonation // return loggedUser.getSelfOrImpersonated().isAdmin() || isAssignee(loggedUser, taskId); - return loggedUser.isAdmin() || isAssignee(loggedUser, taskId); + return user.isAdmin() || isAssignee(user, taskId); + } + + private boolean isAssigned(String taskId) { + return isAssigned(taskService.findById(taskId)); + } + + private boolean isAssigned(Task task) { + return task.getUserId() != null; } } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowAuthorizationService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowAuthorizationService.java index 0a37f2ff0fd..4891ceabe2a 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowAuthorizationService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowAuthorizationService.java @@ -1,8 +1,6 @@ package com.netgrif.application.engine.workflow.service; import com.netgrif.application.engine.objects.auth.domain.AbstractUser; -import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; -import com.netgrif.application.engine.objects.auth.domain.LoggedUser; import com.netgrif.application.engine.objects.petrinet.domain.PetriNet; import com.netgrif.application.engine.objects.petrinet.domain.roles.ProcessRolePermission; import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService; @@ -25,22 +23,22 @@ public class WorkflowAuthorizationService extends AbstractAuthorizationService i private IPetriNetService petriNetService; @Override - public boolean canCallDelete(LoggedUser user, String caseId) { + public boolean canCallDelete(AbstractUser user, String caseId) { Case requestedCase = workflowService.findOne(caseId); // TODO: impersonation // Boolean rolePerm = userHasAtLeastOneRolePermission(userService.transformToUser((LoggedUserImpl) user.getSelfOrImpersonated()), requestedCase.getPetriNet(), ProcessRolePermission.DELETE); // Boolean userPerm = userHasUserListPermission(userService.transformToUser((LoggedUserImpl) user.getSelfOrImpersonated()), requestedCase, ProcessRolePermission.DELETE); // return user.getSelfOrImpersonated().isAdmin() || (userPerm == null ? (rolePerm != null && rolePerm) : userPerm); - Boolean rolePerm = userHasAtLeastOneRolePermission(ActorTransformer.toUser(user), requestedCase.getPetriNet(), ProcessRolePermission.DELETE); - Boolean userPerm = userHasUserListPermission(ActorTransformer.toUser(user), requestedCase, ProcessRolePermission.DELETE); + Boolean rolePerm = userHasAtLeastOneRolePermission(user, requestedCase.getPetriNet(), ProcessRolePermission.DELETE); + Boolean userPerm = userHasUserListPermission(user, requestedCase, ProcessRolePermission.DELETE); return user.isAdmin() || (userPerm == null ? (rolePerm != null && rolePerm) : userPerm); } @Override - public boolean canCallCreate(LoggedUser user, String netId) { + public boolean canCallCreate(AbstractUser user, String netId) { PetriNet net = petriNetService.getPetriNet(netId); // TODO: impersonation - return user.isAdmin() || userHasAtLeastOneRolePermission(ActorTransformer.toUser(user), net, ProcessRolePermission.CREATE); + return user.isAdmin() || userHasAtLeastOneRolePermission(user, net, ProcessRolePermission.CREATE); } @Override diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/ITaskAuthorizationService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/ITaskAuthorizationService.java index 99b0c8e56b9..0edd405291b 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/ITaskAuthorizationService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/ITaskAuthorizationService.java @@ -7,30 +7,28 @@ import com.netgrif.application.engine.objects.workflow.domain.Task; public interface ITaskAuthorizationService { - Boolean userHasAtLeastOneRolePermission(LoggedUser loggedUser, String taskId, RolePermission... permissions); + Boolean userHasAtLeastOneRolePermission(AbstractUser user, String taskId, RolePermission... permissions); Boolean userHasAtLeastOneRolePermission(AbstractUser user, Task task, RolePermission... permissions); - Boolean userHasUserListPermission(LoggedUser loggedUser, String taskId, RolePermission... permissions); + Boolean userHasUserListPermission(AbstractUser user, String taskId, RolePermission... permissions); Boolean userHasUserListPermission(AbstractUser user, Task task, RolePermission... permissions); - boolean isAssignee(LoggedUser loggedUser, String taskId); - boolean isAssignee(AbstractUser user, String taskId); boolean isAssignee(AbstractUser user, Task task); - boolean canCallAssign(LoggedUser loggedUser, String taskId); + boolean canCallAssign(AbstractUser user, String taskId); - boolean canCallDelegate(LoggedUser loggedUser, String taskId); + boolean canCallDelegate(AbstractUser user, String taskId); - boolean canCallFinish(LoggedUser loggedUser, String taskId) throws IllegalTaskStateException; + boolean canCallFinish(AbstractUser user, String taskId) throws IllegalTaskStateException; - boolean canCallCancel(LoggedUser loggedUser, String taskId) throws IllegalTaskStateException; + boolean canCallCancel(AbstractUser user, String taskId) throws IllegalTaskStateException; - boolean canCallSaveData(LoggedUser loggedUser, String taskId); + boolean canCallSaveData(AbstractUser user, String taskId); - boolean canCallSaveFile(LoggedUser loggedUser, String taskId); + boolean canCallSaveFile(AbstractUser user, String taskId); } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IWorkflowAuthorizationService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IWorkflowAuthorizationService.java index f99f950a31e..712cabab6a1 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IWorkflowAuthorizationService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IWorkflowAuthorizationService.java @@ -8,9 +8,9 @@ public interface IWorkflowAuthorizationService { - boolean canCallDelete(LoggedUser user, String caseId); + boolean canCallDelete(AbstractUser user, String caseId); - boolean canCallCreate(LoggedUser user, String netId); + boolean canCallCreate(AbstractUser user, String netId); Boolean userHasAtLeastOneRolePermission(AbstractUser user, PetriNet net, ProcessRolePermission... permissions); diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/AbstractTaskController.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/AbstractTaskController.java index ed52489140f..7a3592ccd85 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/AbstractTaskController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/AbstractTaskController.java @@ -26,6 +26,7 @@ import com.netgrif.application.engine.workflow.web.requestbodies.file.FileFieldRequest; import com.netgrif.application.engine.workflow.web.requestbodies.singleaslist.SingleTaskSearchRequestAsList; import com.netgrif.application.engine.workflow.web.responsebodies.*; +import lombok.RequiredArgsConstructor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.i18n.LocaleContextHolder; @@ -48,6 +49,7 @@ import java.util.*; import java.util.stream.Collectors; +@RequiredArgsConstructor public abstract class AbstractTaskController { public static final Logger log = LoggerFactory.getLogger(TaskController.class); @@ -62,19 +64,6 @@ public abstract class AbstractTaskController { private final UserService userService; - public AbstractTaskController(ITaskService taskService, - IDataService dataService, - IElasticTaskService searchService, - IWorkflowService workflowService, - UserService userService) { - this.taskService = taskService; - this.dataService = dataService; - this.searchService = searchService; - this.workflowService = workflowService; - this.userService = userService; - } - - public PagedModel getAll(Pageable pageable, PagedResourcesAssembler assembler, Locale locale) { LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedUser()); Page page = taskService.getAll(loggedUser, pageable, locale); @@ -185,14 +174,14 @@ public PagedModel search(Pageable pageable, SingleTaskSea return PagedModel.of(tasks.stream().map(t -> new LocalisedTaskResource(new com.netgrif.application.engine.workflow.web.responsebodies.Task(t, locale))).toList(), new PagedModel.PageMetadata(pageable.getPageSize(), pageable.getPageNumber(), tasks.getTotalElements())); } - public PagedModel searchPublic(LoggedUser loggedUser, Pageable pageable, SingleTaskSearchRequestAsList searchBody, MergeFilterOperation operation, PagedResourcesAssembler assembler, Locale locale) { - Page tasks = taskService.search(searchBody.getList(), pageable, loggedUser, locale, operation == MergeFilterOperation.AND); - Link selfLink = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(PublicTaskController.class) - .searchPublic(loggedUser, pageable, searchBody, operation, assembler, locale)).withRel("search"); - PagedModel resources = assembler.toModel(tasks, new TaskResourceAssembler(locale), selfLink); - ResourceLinkAssembler.addLinks(resources, Task.class, selfLink.getRel().toString()); - return PagedModel.of(tasks.stream().map(t -> new LocalisedTaskResource(new com.netgrif.application.engine.workflow.web.responsebodies.Task(t, locale))).toList(), new PagedModel.PageMetadata(pageable.getPageSize(), pageable.getPageNumber(), tasks.getTotalElements())); - } +// public PagedModel searchPublic(LoggedUser loggedUser, Pageable pageable, SingleTaskSearchRequestAsList searchBody, MergeFilterOperation operation, PagedResourcesAssembler assembler, Locale locale) { +// Page tasks = taskService.search(searchBody.getList(), pageable, loggedUser, locale, operation == MergeFilterOperation.AND); +// Link selfLink = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(PublicTaskController.class) +// .searchPublic(loggedUser, pageable, searchBody, operation, assembler, locale)).withRel("search"); +// PagedModel resources = assembler.toModel(tasks, new TaskResourceAssembler(locale), selfLink); +// ResourceLinkAssembler.addLinks(resources, Task.class, selfLink.getRel().toString()); +// return PagedModel.of(tasks.stream().map(t -> new LocalisedTaskResource(new com.netgrif.application.engine.workflow.web.responsebodies.Task(t, locale))).toList(), new PagedModel.PageMetadata(pageable.getPageSize(), pageable.getPageNumber(), tasks.getTotalElements())); +// } public PagedModel searchElastic(Pageable pageable, SingleElasticTaskSearchRequestAsList searchBody, MergeFilterOperation operation, PagedResourcesAssembler assembler, Locale locale) { LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedUser()); diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/PublicTaskController.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/PublicTaskController.java index 0be929fdc54..47770cd2e20 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/PublicTaskController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/PublicTaskController.java @@ -1,210 +1,210 @@ -package com.netgrif.application.engine.workflow.web; - -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; -import com.netgrif.application.engine.objects.auth.domain.LoggedUser; -import com.netgrif.application.engine.auth.service.UserService; -import com.netgrif.application.engine.workflow.domain.MergeFilterOperation; - -import com.netgrif.application.engine.workflow.domain.eventoutcomes.response.EventOutcomeWithMessage; -import com.netgrif.application.engine.workflow.service.interfaces.IDataService; -import com.netgrif.application.engine.workflow.service.interfaces.ITaskService; -import com.netgrif.application.engine.workflow.service.interfaces.IWorkflowService; -import com.netgrif.application.engine.workflow.web.requestbodies.file.FileFieldRequest; -import com.netgrif.application.engine.workflow.web.requestbodies.singleaslist.SingleTaskSearchRequestAsList; -import com.netgrif.application.engine.workflow.web.responsebodies.LocalisedTaskResource; -import com.netgrif.application.engine.workflow.web.responsebodies.TaskReference; -import com.netgrif.application.engine.objects.workflow.domain.Task; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.responses.ApiResponses; -import io.swagger.v3.oas.annotations.tags.Tag; -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.core.io.Resource; -import org.springframework.data.domain.Pageable; -import org.springframework.data.web.PagedResourcesAssembler; -import org.springframework.hateoas.EntityModel; -import org.springframework.hateoas.MediaTypes; -import org.springframework.hateoas.PagedModel; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.security.core.Authentication; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.multipart.MultipartFile; - -import java.io.FileNotFoundException; -import java.util.List; -import java.util.Locale; - -@Slf4j -@RestController -@Tag(name = "Public Task Controller") -@ConditionalOnProperty( - value = "netgrif.engine.security.web.public-web.task-enabled", - havingValue = "true", - matchIfMissing = true -) -@RequestMapping({"/api/public/task"}) -public class PublicTaskController extends AbstractTaskController { - - final UserService userService; - private final ITaskService taskService; - - public PublicTaskController(ITaskService taskService, - IDataService dataService, - IWorkflowService workflowService, - UserService userService) { - super(taskService, dataService, null, workflowService, userService); - this.taskService = taskService; - this.userService = userService; - } - - @Override - @GetMapping(value = "/case/{id}", produces = "application/json;charset=UTF-8") - @Operation(summary = "Get tasks of the case") - public List getTasksOfCase(@PathVariable("id") String caseId, Locale locale) { - return this.taskService.findAllByCase(caseId, locale); - } - - @PreAuthorize("@taskAuthorizationService.canCallAssign(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") - @GetMapping(value = "/assign/{id}", produces = MediaTypes.HAL_JSON_VALUE) - @Operation(summary = "Assign task", description = "Caller must be able to perform the task, or must be an ADMIN") - @ApiResponses({@ApiResponse( - responseCode = "200", - description = "OK" - ), @ApiResponse( - responseCode = "403", - description = "Caller doesn't fulfill the authorisation requirements" - )}) - public EntityModel assign(@PathVariable("id") String taskId, Locale locale) { - return super.assign(taskId, locale); - } - - @PreAuthorize("@taskAuthorizationService.canCallFinish(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") - @GetMapping(value = "/finish/{id}", produces = MediaTypes.HAL_JSON_VALUE) - @Operation(summary = "Finish task", description = "Caller must be assigned to the task, or must be an ADMIN") - @ApiResponses({@ApiResponse( - responseCode = "200", - description = "OK" - ), @ApiResponse( - responseCode = "403", - description = "Caller doesn't fulfill the authorisation requirements" - )}) - public EntityModel finish(@PathVariable("id") String taskId, Locale locale) { - return super.finish(taskId, locale); - } - - @PreAuthorize("@taskAuthorizationService.canCallCancel(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") - @GetMapping(value = "/cancel/{id}", produces = MediaTypes.HAL_JSON_VALUE) - @Operation(summary = "Cancel task", description = "Caller must be assigned to the task, or must be an ADMIN") - @ApiResponses({@ApiResponse( - responseCode = "200", - description = "OK" - ), @ApiResponse( - responseCode = "403", - description = "Caller doesn't fulfill the authorisation requirements" - )}) - public EntityModel cancel(@PathVariable("id") String taskId, Locale locale) { - return super.cancel(taskId, locale); - } - - @Override - @GetMapping(value = "/{id}/data", produces = MediaTypes.HAL_JSON_VALUE) - @Operation(summary = "Get all task data") - public EntityModel getData(@PathVariable("id") String taskId, Locale locale) { - return super.getData(taskId, locale); - } - - @Override - @PreAuthorize("@taskAuthorizationService.canCallSaveData(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") - @PostMapping(value = "/{id}/data", consumes = "application/json;charset=UTF-8", produces = "application/json;charset=UTF-8") - @Operation(summary = "Set task data", description = "Caller must be assigned to the task, or must be an ADMIN") - @ApiResponses({@ApiResponse( - responseCode = "200", - description = "OK" - ), @ApiResponse( - responseCode = "403", - description = "Caller doesn't fulfill the authorisation requirements" - )}) - public EntityModel setData(@PathVariable("id") String taskId, @RequestBody ObjectNode dataBody, Locale locale) { - return super.setData(taskId, dataBody, locale); - } - - @PreAuthorize("@taskAuthorizationService.canCallSaveFile(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") - @Operation(summary = "Upload file into the task", - description = "Caller must be assigned to the task, or must be an ADMIN") - @PostMapping(value = "/{id}/file", produces = MediaTypes.HAL_JSON_VALUE) - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "OK"), - @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), - }) - public EntityModel saveFile(Authentication auth, @PathVariable("id") String taskId, @RequestPart(value = "data") FileFieldRequest dataBody, @RequestPart(value = "file") MultipartFile multipartFile, Locale locale){ - return super.saveFile(taskId, multipartFile, dataBody, locale); - } - - @Override - @Operation(summary = "Download task file field value") - @GetMapping(value = "/{id}/file", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) - public ResponseEntity getFile(@PathVariable("id") String taskId, @RequestParam("fieldId") String fieldId) throws FileNotFoundException { - return super.getFile(taskId, fieldId); - } - - @PreAuthorize("@taskAuthorizationService.canCallSaveFile(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") - @Operation(summary = "Remove file from the task", - description = "Caller must be assigned to the task, or must be an ADMIN") - @DeleteMapping(value = "/{id}/file", produces = MediaTypes.HAL_JSON_VALUE) - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "OK"), - @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), - }) - public EntityModel deleteFile(@PathVariable("id") String taskId, @RequestBody FileFieldRequest requestBody) { - return super.deleteFile(requestBody.getParentTaskId(), requestBody.getFieldId()); - } - - @Override - @Operation(summary = "Download preview for file field value") - @GetMapping(value = "/{id}/file_preview", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) - public ResponseEntity getFilePreview(@PathVariable("id") String taskId, @RequestParam("fieldId") String fieldId) throws FileNotFoundException { - return super.getFilePreview(taskId, fieldId); - } - - @Override - @PreAuthorize("@taskAuthorizationService.canCallSaveFile(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") - @Operation(summary = "Upload multiple files into the task", - description = "Caller must be assigned to the task, or must be an ADMIN") - @PostMapping(value = "/{id}/files", produces = MediaTypes.HAL_JSON_VALUE) - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "OK"), - @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), - }) - public EntityModel saveFiles(@PathVariable("id") String taskId, @RequestPart(value = "files") MultipartFile[] multipartFiles, @RequestPart(value = "data") FileFieldRequest requestBody) { - return super.saveFiles(taskId, multipartFiles, requestBody); - } - - @Override - @Operation(summary = "Download one file from tasks file list field value") - @GetMapping(value = "/{id}/file/named", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) - public ResponseEntity getNamedFile(@PathVariable("id") String taskId, @RequestParam("fieldId") String fieldId, @RequestParam("fileName") String fileName) throws FileNotFoundException { - return super.getNamedFile(taskId, fieldId, fileName); - } - - @PreAuthorize("@taskAuthorizationService.canCallSaveFile(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") - @Operation(summary = "Remove file from tasks file list field value", - description = "Caller must be assigned to the task, or must be an ADMIN") - @DeleteMapping(value = "/{id}/file/named", produces = MediaTypes.HAL_JSON_VALUE) - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "OK"), - @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), - }) - public EntityModel deleteNamedFile(@PathVariable("id") String taskId, @RequestBody FileFieldRequest requestBody) { - return super.deleteNamedFile(requestBody.getParentTaskId(), requestBody.getFieldId(), requestBody.getFileName()); - } - -// @Operation(summary = "Generic task search on Mongo database") -// @PostMapping(value = "/search", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaTypes.HAL_JSON_VALUE) -// public PagedModel search(Pageable pageable, @RequestBody SingleTaskSearchRequestAsList searchBody, @RequestParam(defaultValue = "OR") MergeFilterOperation operation, PagedResourcesAssembler assembler, Locale locale) { -// return super.searchPublic(ActorTransformer.toLoggedUser(userService.getLoggedUser()), pageable, searchBody, operation, assembler, locale); +//package com.netgrif.application.engine.workflow.web; +// +//import com.fasterxml.jackson.databind.node.ObjectNode; +//import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; +//import com.netgrif.application.engine.objects.auth.domain.LoggedUser; +//import com.netgrif.application.engine.auth.service.UserService; +//import com.netgrif.application.engine.workflow.domain.MergeFilterOperation; +// +//import com.netgrif.application.engine.workflow.domain.eventoutcomes.response.EventOutcomeWithMessage; +//import com.netgrif.application.engine.workflow.service.interfaces.IDataService; +//import com.netgrif.application.engine.workflow.service.interfaces.ITaskService; +//import com.netgrif.application.engine.workflow.service.interfaces.IWorkflowService; +//import com.netgrif.application.engine.workflow.web.requestbodies.file.FileFieldRequest; +//import com.netgrif.application.engine.workflow.web.requestbodies.singleaslist.SingleTaskSearchRequestAsList; +//import com.netgrif.application.engine.workflow.web.responsebodies.LocalisedTaskResource; +//import com.netgrif.application.engine.workflow.web.responsebodies.TaskReference; +//import com.netgrif.application.engine.objects.workflow.domain.Task; +//import io.swagger.v3.oas.annotations.Operation; +//import io.swagger.v3.oas.annotations.responses.ApiResponse; +//import io.swagger.v3.oas.annotations.responses.ApiResponses; +//import io.swagger.v3.oas.annotations.tags.Tag; +//import lombok.extern.slf4j.Slf4j; +//import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +//import org.springframework.core.io.Resource; +//import org.springframework.data.domain.Pageable; +//import org.springframework.data.web.PagedResourcesAssembler; +//import org.springframework.hateoas.EntityModel; +//import org.springframework.hateoas.MediaTypes; +//import org.springframework.hateoas.PagedModel; +//import org.springframework.http.MediaType; +//import org.springframework.http.ResponseEntity; +//import org.springframework.security.access.prepost.PreAuthorize; +//import org.springframework.security.core.Authentication; +//import org.springframework.web.bind.annotation.*; +//import org.springframework.web.multipart.MultipartFile; +// +//import java.io.FileNotFoundException; +//import java.util.List; +//import java.util.Locale; +// +//@Slf4j +//@RestController +//@Tag(name = "Public Task Controller") +//@ConditionalOnProperty( +// value = "netgrif.engine.security.web.public-web.task-enabled", +// havingValue = "true", +// matchIfMissing = true +//) +//@RequestMapping({"/api/public/task"}) +//public class PublicTaskController extends AbstractTaskController { +// +// final UserService userService; +// private final ITaskService taskService; +// +// public PublicTaskController(ITaskService taskService, +// IDataService dataService, +// IWorkflowService workflowService, +// UserService userService) { +// super(taskService, dataService, workflowService, null, userService); +// this.taskService = taskService; +// this.userService = userService; // } -} +// +// @Override +// @GetMapping(value = "/case/{id}", produces = "application/json;charset=UTF-8") +// @Operation(summary = "Get tasks of the case") +// public List getTasksOfCase(@PathVariable("id") String caseId, Locale locale) { +// return this.taskService.findAllByCase(caseId, locale); +// } +// +// @PreAuthorize("@taskAuthorizationService.canCallAssign(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") +// @GetMapping(value = "/assign/{id}", produces = MediaTypes.HAL_JSON_VALUE) +// @Operation(summary = "Assign task", description = "Caller must be able to perform the task, or must be an ADMIN") +// @ApiResponses({@ApiResponse( +// responseCode = "200", +// description = "OK" +// ), @ApiResponse( +// responseCode = "403", +// description = "Caller doesn't fulfill the authorisation requirements" +// )}) +// public EntityModel assign(@PathVariable("id") String taskId, Locale locale) { +// return super.assign(taskId, locale); +// } +// +// @PreAuthorize("@taskAuthorizationService.canCallFinish(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") +// @GetMapping(value = "/finish/{id}", produces = MediaTypes.HAL_JSON_VALUE) +// @Operation(summary = "Finish task", description = "Caller must be assigned to the task, or must be an ADMIN") +// @ApiResponses({@ApiResponse( +// responseCode = "200", +// description = "OK" +// ), @ApiResponse( +// responseCode = "403", +// description = "Caller doesn't fulfill the authorisation requirements" +// )}) +// public EntityModel finish(@PathVariable("id") String taskId, Locale locale) { +// return super.finish(taskId, locale); +// } +// +// @PreAuthorize("@taskAuthorizationService.canCallCancel(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") +// @GetMapping(value = "/cancel/{id}", produces = MediaTypes.HAL_JSON_VALUE) +// @Operation(summary = "Cancel task", description = "Caller must be assigned to the task, or must be an ADMIN") +// @ApiResponses({@ApiResponse( +// responseCode = "200", +// description = "OK" +// ), @ApiResponse( +// responseCode = "403", +// description = "Caller doesn't fulfill the authorisation requirements" +// )}) +// public EntityModel cancel(@PathVariable("id") String taskId, Locale locale) { +// return super.cancel(taskId, locale); +// } +// +// @Override +// @GetMapping(value = "/{id}/data", produces = MediaTypes.HAL_JSON_VALUE) +// @Operation(summary = "Get all task data") +// public EntityModel getData(@PathVariable("id") String taskId, Locale locale) { +// return super.getData(taskId, locale); +// } +// +// @Override +// @PreAuthorize("@taskAuthorizationService.canCallSaveData(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") +// @PostMapping(value = "/{id}/data", consumes = "application/json;charset=UTF-8", produces = "application/json;charset=UTF-8") +// @Operation(summary = "Set task data", description = "Caller must be assigned to the task, or must be an ADMIN") +// @ApiResponses({@ApiResponse( +// responseCode = "200", +// description = "OK" +// ), @ApiResponse( +// responseCode = "403", +// description = "Caller doesn't fulfill the authorisation requirements" +// )}) +// public EntityModel setData(@PathVariable("id") String taskId, @RequestBody ObjectNode dataBody, Locale locale) { +// return super.setData(taskId, dataBody, locale); +// } +// +// @PreAuthorize("@taskAuthorizationService.canCallSaveFile(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") +// @Operation(summary = "Upload file into the task", +// description = "Caller must be assigned to the task, or must be an ADMIN") +// @PostMapping(value = "/{id}/file", produces = MediaTypes.HAL_JSON_VALUE) +// @ApiResponses(value = { +// @ApiResponse(responseCode = "200", description = "OK"), +// @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), +// }) +// public EntityModel saveFile(Authentication auth, @PathVariable("id") String taskId, @RequestPart(value = "data") FileFieldRequest dataBody, @RequestPart(value = "file") MultipartFile multipartFile, Locale locale){ +// return super.saveFile(taskId, multipartFile, dataBody, locale); +// } +// +// @Override +// @Operation(summary = "Download task file field value") +// @GetMapping(value = "/{id}/file", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) +// public ResponseEntity getFile(@PathVariable("id") String taskId, @RequestParam("fieldId") String fieldId) throws FileNotFoundException { +// return super.getFile(taskId, fieldId); +// } +// +// @PreAuthorize("@taskAuthorizationService.canCallSaveFile(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") +// @Operation(summary = "Remove file from the task", +// description = "Caller must be assigned to the task, or must be an ADMIN") +// @DeleteMapping(value = "/{id}/file", produces = MediaTypes.HAL_JSON_VALUE) +// @ApiResponses(value = { +// @ApiResponse(responseCode = "200", description = "OK"), +// @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), +// }) +// public EntityModel deleteFile(@PathVariable("id") String taskId, @RequestBody FileFieldRequest requestBody) { +// return super.deleteFile(requestBody.getParentTaskId(), requestBody.getFieldId()); +// } +// +// @Override +// @Operation(summary = "Download preview for file field value") +// @GetMapping(value = "/{id}/file_preview", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) +// public ResponseEntity getFilePreview(@PathVariable("id") String taskId, @RequestParam("fieldId") String fieldId) throws FileNotFoundException { +// return super.getFilePreview(taskId, fieldId); +// } +// +// @Override +// @PreAuthorize("@taskAuthorizationService.canCallSaveFile(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") +// @Operation(summary = "Upload multiple files into the task", +// description = "Caller must be assigned to the task, or must be an ADMIN") +// @PostMapping(value = "/{id}/files", produces = MediaTypes.HAL_JSON_VALUE) +// @ApiResponses(value = { +// @ApiResponse(responseCode = "200", description = "OK"), +// @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), +// }) +// public EntityModel saveFiles(@PathVariable("id") String taskId, @RequestPart(value = "files") MultipartFile[] multipartFiles, @RequestPart(value = "data") FileFieldRequest requestBody) { +// return super.saveFiles(taskId, multipartFiles, requestBody); +// } +// +// @Override +// @Operation(summary = "Download one file from tasks file list field value") +// @GetMapping(value = "/{id}/file/named", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) +// public ResponseEntity getNamedFile(@PathVariable("id") String taskId, @RequestParam("fieldId") String fieldId, @RequestParam("fileName") String fileName) throws FileNotFoundException { +// return super.getNamedFile(taskId, fieldId, fileName); +// } +// +// @PreAuthorize("@taskAuthorizationService.canCallSaveFile(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") +// @Operation(summary = "Remove file from tasks file list field value", +// description = "Caller must be assigned to the task, or must be an ADMIN") +// @DeleteMapping(value = "/{id}/file/named", produces = MediaTypes.HAL_JSON_VALUE) +// @ApiResponses(value = { +// @ApiResponse(responseCode = "200", description = "OK"), +// @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), +// }) +// public EntityModel deleteNamedFile(@PathVariable("id") String taskId, @RequestBody FileFieldRequest requestBody) { +// return super.deleteNamedFile(requestBody.getParentTaskId(), requestBody.getFieldId(), requestBody.getFileName()); +// } +// +//// @Operation(summary = "Generic task search on Mongo database") +//// @PostMapping(value = "/search", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaTypes.HAL_JSON_VALUE) +//// public PagedModel search(Pageable pageable, @RequestBody SingleTaskSearchRequestAsList searchBody, @RequestParam(defaultValue = "OR") MergeFilterOperation operation, PagedResourcesAssembler assembler, Locale locale) { +//// return super.searchPublic(ActorTransformer.toLoggedUser(userService.getLoggedUser()), pageable, searchBody, operation, assembler, locale); +//// } +//} diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/TaskController.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/TaskController.java index 1ac8ea694a8..2be379cb96f 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/TaskController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/TaskController.java @@ -59,7 +59,7 @@ public TaskController(ITaskService taskService, IElasticTaskService searchService, IWorkflowService workflowService, UserService userService) { - super(taskService, dataService, searchService, workflowService, userService); + super(taskService, dataService, workflowService, searchService, userService); } @Override @@ -90,7 +90,7 @@ public LocalisedTaskResource getOne(@PathVariable("id") String taskId, Locale lo return super.getOne(taskId, locale); } - @PreAuthorize("@taskAuthorizationService.canCallAssign(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") + @PreAuthorize("@taskAuthorizationService.canCallAssign(@userService.getLoggedUser(), #taskId)") @Operation(summary = "Assign task", description = "Caller must be able to perform the task, or must be an ADMIN", security = {@SecurityRequirement(name = "BasicAuth")}) @@ -103,7 +103,7 @@ public EntityModel assign(@PathVariable("id") String ta return super.assign(taskId, locale); } - @PreAuthorize("@taskAuthorizationService.canCallDelegate(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") + @PreAuthorize("@taskAuthorizationService.canCallDelegate(@userService.getLoggedUser(), #taskId)") @Operation(summary = "Delegate task", description = "Caller must be able to delegate the task, or must be an ADMIN", security = {@SecurityRequirement(name = "BasicAuth")}) @@ -116,7 +116,7 @@ public EntityModel delegate(@PathVariable("id") String return super.delegate(taskId, delegatedId, locale); } - @PreAuthorize("@taskAuthorizationService.canCallFinish(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") + @PreAuthorize("@taskAuthorizationService.canCallFinish(@userService.getLoggedUser(), #taskId)") @Operation(summary = "Finish task", description = "Caller must be assigned to the task, or must be an ADMIN", security = {@SecurityRequirement(name = "BasicAuth")}) @@ -129,7 +129,7 @@ public EntityModel finish(@PathVariable("id") String ta return super.finish(taskId, locale); } - @PreAuthorize("@taskAuthorizationService.canCallCancel(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") + @PreAuthorize("@taskAuthorizationService.canCallCancel(@userService.getLoggedUser(), #taskId)") @Operation(summary = "Cancel task", description = "Caller must be assigned to the task, or must be an ADMIN", security = {@SecurityRequirement(name = "BasicAuth")}) @@ -184,7 +184,7 @@ public EntityModel getData(@PathVariable("id") String t return super.getData(taskId, locale); } - @PreAuthorize("@taskAuthorizationService.canCallSaveData(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") + @PreAuthorize("@taskAuthorizationService.canCallSaveData(@userService.getLoggedUser(), #taskId)") @Operation(summary = "Set task data", description = "Caller must be assigned to the task, or must be an ADMIN", security = {@SecurityRequirement(name = "BasicAuth")}) @@ -198,7 +198,7 @@ public EntityModel setData(@PathVariable("id") String t return super.setData(taskId, dataBody, locale); } - @PreAuthorize("@taskAuthorizationService.canCallSaveFile(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") + @PreAuthorize("@taskAuthorizationService.canCallSaveFile(@userService.getLoggedUser(), #taskId)") @Operation(summary = "Upload file into the task", description = "Caller must be assigned to the task, or must be an ADMIN", security = {@SecurityRequirement(name = "BasicAuth")}) @@ -217,7 +217,7 @@ public ResponseEntity getFile(@PathVariable("id") String taskId, @Requ return super.getFile(taskId, fieldId); } - @PreAuthorize("@taskAuthorizationService.canCallSaveFile(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") + @PreAuthorize("@taskAuthorizationService.canCallSaveFile(@userService.getLoggedUser(), #taskId)") @Operation(summary = "Remove file from the task", description = "Caller must be assigned to the task, or must be an ADMIN", security = {@SecurityRequirement(name = "BasicAuth")}) @@ -230,7 +230,7 @@ public EntityModel deleteFile(@PathVariable("id") Strin return super.deleteFile(requestBody.getParentTaskId(), requestBody.getFieldId()); } - @PreAuthorize("@taskAuthorizationService.canCallSaveFile(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") + @PreAuthorize("@taskAuthorizationService.canCallSaveFile(@userService.getLoggedUser(), #taskId)") @Operation(summary = "Upload multiple files into the task", description = "Caller must be assigned to the task, or must be an ADMIN", security = {@SecurityRequirement(name = "BasicAuth")}) @@ -249,7 +249,7 @@ public ResponseEntity getNamedFile(@PathVariable("id") String taskId, return super.getNamedFile(taskId, fieldId, fileName); } - @PreAuthorize("@taskAuthorizationService.canCallSaveFile(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") + @PreAuthorize("@taskAuthorizationService.canCallSaveFile(@userService.getLoggedUser(), #taskId)") @Operation(summary = "Remove file from tasks file list field value", description = "Caller must be assigned to the task, or must be an ADMIN", security = {@SecurityRequirement(name = "BasicAuth")}) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/WorkflowController.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/WorkflowController.java index 2e73e160fa7..9cfbc3b5c50 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/WorkflowController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/WorkflowController.java @@ -25,6 +25,8 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -63,36 +65,32 @@ matchIfMissing = true ) @Tag(name = "Workflow") +@RequiredArgsConstructor public class WorkflowController { private static final Logger log = LoggerFactory.getLogger(WorkflowController.class.getName()); - @Autowired - private IWorkflowService workflowService; + private final IWorkflowService workflowService; - @Autowired - private ITaskService taskService; + private final ITaskService taskService; - @Autowired - private IElasticCaseService elasticCaseService; + private final IElasticCaseService elasticCaseService; - @Autowired - private IDataService dataService; + private final IDataService dataService; - @Autowired - private UserService userService; + private final UserService userService; - @PreAuthorize("@workflowAuthorizationService.canCallCreate(#auth.getPrincipal(), #body.netId)") + @PreAuthorize("@workflowAuthorizationService.canCallCreate(@userService.getLoggedUser(), #body.netId)") @Operation(summary = "Create new case", security = {@SecurityRequirement(name = "BasicAuth")}) @PostMapping(value = "/case", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaTypes.HAL_JSON_VALUE) - public EntityModel createCase(@RequestBody CreateCaseBody body, Authentication auth, Locale locale) { - LoggedUser loggedUser = (LoggedUser) auth.getPrincipal(); + public EntityModel createCase(@RequestBody CreateCaseBody body, Locale locale) { + LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedUser()); try { CreateCaseEventOutcome outcome = workflowService.createCase(body.netId, body.title, body.color, loggedUser, locale); return EventOutcomeWithMessageResource.successMessage("Case with id " + outcome.getCase().getStringId() + " was created succesfully", LocalisedEventOutcomeFactory.from(outcome, locale)); - } catch (Exception e) { // TODO: 5. 2. 2017 change to custom exception + } catch (Exception e) { log.error("Creating case failed:", e); return EventOutcomeWithMessageResource.errorMessage("Creating case failed" + e.getMessage()); } @@ -104,9 +102,7 @@ public PagedModel getAll(Pageable pageable, PagedResourcesAssemble Page cases = workflowService.getAll(pageable); Link selfLink = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(WorkflowController.class) .getAll(pageable, assembler)).withRel("all"); - PagedModel resources = assembler.toModel(cases, new CaseResourceAssembler(), selfLink); - ResourceLinkAssembler.addLinks(resources, Case.class, selfLink.getRel().toString()); - return PagedModel.of(cases.stream().map(CaseResource::new).toList(), new PagedModel.PageMetadata(pageable.getPageSize(), pageable.getPageNumber(), cases.getTotalElements())); + return getCaseResources(pageable, assembler, cases, selfLink); } @Operation(summary = "Generic case search with QueryDSL predicate, paginated", security = {@SecurityRequirement(name = "BasicAuth")}) @@ -115,19 +111,17 @@ public PagedModel search2(@QuerydslPredicate(root = Case.class) Pr Page cases = workflowService.search(predicate, pageable); Link selfLink = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(WorkflowController.class) .search2(predicate, pageable, assembler)).withRel("search2"); - PagedModel resources = assembler.toModel(cases, new CaseResourceAssembler(), selfLink); - ResourceLinkAssembler.addLinks(resources, Case.class, selfLink.getRel().toString()); - return PagedModel.of(cases.stream().map(CaseResource::new).toList(), new PagedModel.PageMetadata(pageable.getPageSize(), pageable.getPageNumber(), cases.getTotalElements())); + return getCaseResources(pageable, assembler, cases, selfLink); } @Operation(summary = "Generic case search on Elasticsearch database, paginated", security = {@SecurityRequirement(name = "BasicAuth")}) @PostMapping(value = "/case/search", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaTypes.HAL_JSON_VALUE) - public PagedModel search(@RequestBody SingleCaseSearchRequestAsList searchBody, @RequestParam(defaultValue = "OR") MergeFilterOperation operation, Pageable pageable, PagedResourcesAssembler assembler, Authentication auth, Locale locale) { + public PagedModel search(@RequestBody SingleCaseSearchRequestAsList searchBody, @RequestParam(defaultValue = "OR") MergeFilterOperation operation, Pageable pageable, PagedResourcesAssembler assembler, Locale locale) { LoggedUser user = ActorTransformer.toLoggedUser(userService.getLoggedUser()); Page cases = elasticCaseService.search(searchBody.getList(), user, pageable, locale, operation == MergeFilterOperation.AND); Link selfLink = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(WorkflowController.class) - .search(searchBody, operation, pageable, assembler, auth, locale)).withRel("search"); + .search(searchBody, operation, pageable, assembler, locale)).withRel("search"); PagedModel resources = assembler.toModel(cases, new CaseResourceAssembler(), selfLink); ResourceLinkAssembler.addLinks(resources, ElasticCase.class, selfLink.getRel().toString()); @@ -136,20 +130,18 @@ public PagedModel search(@RequestBody SingleCaseSearchRequestAsLis @Operation(summary = "Generic case search on Mongo database, paginated", security = {@SecurityRequirement(name = "BasicAuth")}) @PostMapping(value = "/case/search_mongo", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaTypes.HAL_JSON_VALUE) - public PagedModel searchMongo(@RequestBody Map searchBody, Pageable pageable, Authentication auth, PagedResourcesAssembler assembler, Locale locale) { - Page cases = workflowService.search(searchBody, pageable, (LoggedUser) auth.getPrincipal(), locale); + public PagedModel searchMongo(@RequestBody Map searchBody, Pageable pageable, PagedResourcesAssembler assembler, Locale locale) { + Page cases = workflowService.search(searchBody, pageable, ActorTransformer.toLoggedUser(userService.getLoggedUser()), locale); Link selfLink = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(WorkflowController.class) - .searchMongo(searchBody, pageable, auth, assembler, locale)).withRel("search"); - PagedModel resources = assembler.toModel(cases, new CaseResourceAssembler(), selfLink); - ResourceLinkAssembler.addLinks(resources, Case.class, selfLink.getRel().toString()); - return PagedModel.of(cases.stream().map(CaseResource::new).toList(), new PagedModel.PageMetadata(pageable.getPageSize(), pageable.getPageNumber(), cases.getTotalElements())); + .searchMongo(searchBody, pageable, assembler, locale)).withRel("search"); + return getCaseResources(pageable, assembler, cases, selfLink); } @Operation(summary = "Get count of the cases", security = {@SecurityRequirement(name = "BasicAuth")}) @PostMapping(value = "/case/count", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) - public CountResponse count(@RequestBody SingleCaseSearchRequestAsList searchBody, @RequestParam(defaultValue = "OR") MergeFilterOperation operation, Authentication auth, Locale locale) { - long count = elasticCaseService.count(searchBody.getList(), (LoggedUser) auth.getPrincipal(), locale, operation == MergeFilterOperation.AND); + public CountResponse count(@RequestBody SingleCaseSearchRequestAsList searchBody, @RequestParam(defaultValue = "OR") MergeFilterOperation operation, Locale locale) { + long count = elasticCaseService.count(searchBody.getList(), ActorTransformer.toLoggedUser(userService.getLoggedUser()), locale, operation == MergeFilterOperation.AND); return CountResponse.caseCount(count); } @@ -168,9 +160,7 @@ public PagedModel findAllByAuthor(@PathVariable("id") String autho Page cases = workflowService.findAllByAuthor(authorId, petriNet, pageable); Link selfLink = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(WorkflowController.class) .findAllByAuthor(authorId, petriNet, assembler, pageable)).withRel("author"); - PagedModel resources = assembler.toModel(cases, new CaseResourceAssembler(), selfLink); - ResourceLinkAssembler.addLinks(resources, Case.class, selfLink.getRel().toString()); - return PagedModel.of(cases.stream().map(CaseResource::new).toList(), new PagedModel.PageMetadata(pageable.getPageSize(), pageable.getPageNumber(), cases.getTotalElements())); + return getCaseResources(pageable, assembler, cases, selfLink); } @PreAuthorize("@authorizationService.hasAuthority('ADMIN')") @@ -184,49 +174,29 @@ public PagedModel findAllByAuthor(@PathVariable("id") String autho }) public MessageResource reloadTasks(@PathVariable("id") String caseId) { try { - caseId = URLDecoder.decode(caseId, StandardCharsets.UTF_8.name()); + caseId = URLDecoder.decode(caseId, StandardCharsets.UTF_8); Case aCase = workflowService.findOne(caseId); taskService.reloadTasks(aCase); - return MessageResource.successMessage("Task reloaded in case [" + caseId + "]"); } catch (Exception e) { - log.error("Reloading tasks of case [" + caseId + "] failed:", e); + log.error("Reloading tasks of case [{}] failed:", caseId, e); return MessageResource.errorMessage("Reloading tasks in case " + caseId + " has failed!"); } } -// @Deprecated -// @PreAuthorize("@authorizationService.hasAuthority('ADMIN')") -// @Operation(summary = "Get all case data", security = {@SecurityRequirement(name = "BasicAuth")}) -// @GetMapping(value = "/case/{id}/data", produces = MediaTypes.HAL_JSON_VALUE) -// public DataFieldsResource getAllCaseData(@PathVariable("id") String caseId, Locale locale) { -// try { -// caseId = URLDecoder.decode(caseId, StandardCharsets.UTF_8.name()); -// return new DataFieldsResource(workflowService.getData(caseId), locale); -// } catch (UnsupportedEncodingException e) { -// log.error("Getting all case data of [" + caseId + "] failed:", e); -// return new DataFieldsResource(new ArrayList<>(), locale); -// } -// } - - @PreAuthorize("@workflowAuthorizationService.canCallDelete(#auth.getPrincipal(), #caseId)") + @PreAuthorize("@workflowAuthorizationService.canCallDelete(@userService.getLoggedUser(), #caseId)") @Operation(summary = "Delete case", security = {@SecurityRequirement(name = "BasicAuth")}) @DeleteMapping(value = "/case/{id}", produces = MediaTypes.HAL_JSON_VALUE) - public EntityModel deleteCase(Authentication auth, @PathVariable("id") String caseId, @RequestParam(defaultValue = "false") boolean deleteSubtree) { - try { - caseId = URLDecoder.decode(caseId, StandardCharsets.UTF_8.name()); - DeleteCaseEventOutcome outcome; - if (deleteSubtree) { - outcome = workflowService.deleteSubtreeRootedAt(caseId); - } else { - outcome = workflowService.deleteCase(caseId); - } - return EventOutcomeWithMessageResource.successMessage("Case " + caseId + " was deleted", - LocalisedEventOutcomeFactory.from(outcome, LocaleContextHolder.getLocale())); - } catch (UnsupportedEncodingException e) { - log.error("Deleting case [" + caseId + "] failed:", e); - return EventOutcomeWithMessageResource.errorMessage("Deleting case " + caseId + " has failed!"); + public EntityModel deleteCase(@PathVariable("id") String caseId, @RequestParam(defaultValue = "false") boolean deleteSubtree) { + caseId = URLDecoder.decode(caseId, StandardCharsets.UTF_8); + DeleteCaseEventOutcome outcome; + if (deleteSubtree) { + outcome = workflowService.deleteSubtreeRootedAt(caseId); + } else { + outcome = workflowService.deleteCase(caseId); } + return EventOutcomeWithMessageResource.successMessage("Case " + caseId + " was deleted", + LocalisedEventOutcomeFactory.from(outcome, LocaleContextHolder.getLocale())); } @Operation(summary = "Download case file field value", security = {@SecurityRequirement(name = "BasicAuth")}) @@ -264,4 +234,11 @@ public ResponseEntity getFileByName(@PathVariable("id") String caseId, .headers(headers) .body(new InputStreamResource(fileFieldInputStream.getInputStream())); } + + @NotNull + private PagedModel getCaseResources(Pageable pageable, PagedResourcesAssembler assembler, Page cases, Link selfLink) { + PagedModel resources = assembler.toModel(cases, new CaseResourceAssembler(), selfLink); + ResourceLinkAssembler.addLinks(resources, Case.class, selfLink.getRel().toString()); + return PagedModel.of(cases.stream().map(CaseResource::new).toList(), new PagedModel.PageMetadata(pageable.getPageSize(), pageable.getPageNumber(), cases.getTotalElements())); + } } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/responsebodies/CaseResource.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/responsebodies/CaseResource.java index dcd0a65f416..8502ddff959 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/responsebodies/CaseResource.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/responsebodies/CaseResource.java @@ -19,6 +19,6 @@ public CaseResource(Case content) { private void buildLinks() { add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder - .methodOn(WorkflowController.class).createCase(new CreateCaseBody(), null, LocaleContextHolder.getLocale())).withRel("create")); + .methodOn(WorkflowController.class).createCase(new CreateCaseBody(), LocaleContextHolder.getLocale())).withRel("create")); } } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/responsebodies/LocalisedTaskResource.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/responsebodies/LocalisedTaskResource.java index 36b0ce5d636..680ff1f2519 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/responsebodies/LocalisedTaskResource.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/responsebodies/LocalisedTaskResource.java @@ -26,13 +26,13 @@ private void buildLinks() { add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(TaskController.class) .getOne(task.getStringId(), null)).withSelfRel()); add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(TaskController.class) - .assign((Authentication) null, task.getStringId(), null)).withRel("assign")); + .assign(task.getStringId(), null)).withRel("assign")); add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(TaskController.class) - .delegate((Authentication) null, task.getStringId(), null, null)).withRel("delegate")); + .delegate(task.getStringId(), null, null)).withRel("delegate")); add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(TaskController.class) - .finish((Authentication) null, task.getStringId(), null)).withRel("finish")); + .finish(task.getStringId(), null)).withRel("finish")); add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(TaskController.class) - .cancel((Authentication) null, task.getStringId(), null)).withRel("cancel")); + .cancel(task.getStringId(), null)).withRel("cancel")); add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(TaskController.class) .getData(task.getStringId(), null)).withRel("data")); add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(TaskController.class) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/responsebodies/ResourceLinkAssembler.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/responsebodies/ResourceLinkAssembler.java index 640eea84f72..83e3da8fa96 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/responsebodies/ResourceLinkAssembler.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/responsebodies/ResourceLinkAssembler.java @@ -48,10 +48,10 @@ private static void addCasesLinks(PagedModel pagedResources, String selfRel) { .getAll(null, null)).withRel("all")); if (!selfRel.equalsIgnoreCase("search")) pagedResources.add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(WorkflowController.class) - .search(null, MergeFilterOperation.OR, null, null, null, null)).withRel("search")); + .search(null, MergeFilterOperation.OR, null, null, null)).withRel("search")); if (!selfRel.equalsIgnoreCase("count")) pagedResources.add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(WorkflowController.class) - .count(null, MergeFilterOperation.OR, null, null)).withRel("count")); + .count(null, MergeFilterOperation.OR, null)).withRel("count")); if (!selfRel.equalsIgnoreCase("author")) pagedResources.add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(WorkflowController.class) .findAllByAuthor(null, "", null, null)).withRel("author")); From 140a35d38f3bf742772a5857a9c28943a708de6a Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Tue, 20 Jan 2026 13:22:23 +0100 Subject: [PATCH 08/14] Refactor controllers and services for better dependency handling Removed deprecated authentication parameters from controllers, leveraging `ActorTransformer` for handling logged user context. Improved `AnonymousUser` handling by integrating `AnonymousUserRefService` to resolve missing users. Simplified method signatures and enhanced code maintainability. --- .../petrinet/web/PetriNetController.java | 10 +- .../web/PublicPetriNetController.java | 206 +++++++++--------- .../PetriNetReferenceResource.java | 4 +- .../TransitionReferencesResource.java | 2 +- .../web/PublicWorkflowController.java | 122 +++++------ .../spring/auth/domain/AnonymousUser.java | 2 +- .../engine/auth/service/UserServiceImpl.java | 15 +- 7 files changed, 187 insertions(+), 174 deletions(-) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java index 49534ac4886..1ba932db981 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java @@ -143,22 +143,22 @@ public ResponseEntity> getAll(@RequestParam(value = "ind @Operation(summary = "Get process by id", security = {@SecurityRequirement(name = "BasicAuth")}) @GetMapping(value = "/{id}", produces = MediaTypes.HAL_JSON_VALUE) - public PetriNetReferenceResource getOne(@PathVariable("id") String id, Authentication auth, Locale locale) { + public PetriNetReferenceResource getOne(@PathVariable("id") String id, Locale locale) { return new PetriNetReferenceResource(IPetriNetService.transformToReference(service.getPetriNet(decodeUrl(id)), locale)); } @Operation(summary = "Get process by identifier and version", security = {@SecurityRequirement(name = "BasicAuth")}) @GetMapping(value = "/{identifier}/{version}", produces = MediaTypes.HAL_JSON_VALUE) - public PetriNetReferenceResource getOne(@PathVariable("identifier") String identifier, @PathVariable("version") String version, Authentication auth, Locale locale) { + public PetriNetReferenceResource getOne(@PathVariable("identifier") String identifier, @PathVariable("version") String version, Locale locale) { String resolvedIdentifier = Base64.isBase64(identifier) ? new String(Base64.decodeBase64(identifier)) : identifier; - return new PetriNetReferenceResource(service.getReference(resolvedIdentifier, converter.convert(version), (LoggedUser) auth.getPrincipal(), locale)); + return new PetriNetReferenceResource(service.getReference(resolvedIdentifier, converter.convert(version), ActorTransformer.toLoggedUser(userService.getLoggedUserFromContext()), locale)); } @Operation(summary = "Get transitions of processes", security = {@SecurityRequirement(name = "BasicAuth")}) @GetMapping(value = "/transitions", produces = MediaTypes.HAL_JSON_VALUE) - public TransitionReferencesResource getTransitionReferences(@RequestParam List ids, Authentication auth, Locale locale) { + public TransitionReferencesResource getTransitionReferences(@RequestParam List ids, Locale locale) { ids.forEach(id -> id = decodeUrl(id)); - return new TransitionReferencesResource(service.getTransitionReferences(ids, (LoggedUser) auth.getPrincipal(), locale)); + return new TransitionReferencesResource(service.getTransitionReferences(ids, ActorTransformer.toLoggedUser(userService.getLoggedUserFromContext()), locale)); } @Operation(summary = "Get data fields of transitions", security = {@SecurityRequirement(name = "BasicAuth")}) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PublicPetriNetController.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PublicPetriNetController.java index 63d9c8b2c8c..719689442ea 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PublicPetriNetController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PublicPetriNetController.java @@ -1,103 +1,103 @@ -package com.netgrif.application.engine.petrinet.web; - -import com.netgrif.application.engine.auth.service.UserService; -import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; -import com.netgrif.application.engine.objects.petrinet.domain.PetriNet; -import com.netgrif.application.engine.objects.petrinet.domain.PetriNetSearch; -import com.netgrif.application.engine.petrinet.domain.version.StringToVersionConverter; -import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService; -import com.netgrif.application.engine.adapter.spring.petrinet.service.ProcessRoleService; -import com.netgrif.application.engine.petrinet.web.responsebodies.*; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.codec.binary.Base64; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageImpl; -import org.springframework.data.domain.Pageable; -import org.springframework.data.web.PagedResourcesAssembler; -import org.springframework.hateoas.MediaTypes; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -import java.util.List; -import java.util.Locale; - -import static com.netgrif.application.engine.petrinet.web.PetriNetController.decodeUrl; - -@Slf4j -@RestController -@ConditionalOnProperty( - value = "netgrif.engine.security.web.public-web.petri-net-enabled", - havingValue = "true", - matchIfMissing = true -) -@Tag(name = "Public PetriNet Controller") -@RequestMapping({"/api/public/petrinet"}) -public class PublicPetriNetController { - - private final IPetriNetService petriNetService; - - private final ProcessRoleService roleService; - - private final UserService userService; - - private final StringToVersionConverter converter; - - public PublicPetriNetController(IPetriNetService petriNetService, UserService userService, StringToVersionConverter converter, ProcessRoleService roleService) { - this.petriNetService = petriNetService; - this.converter = converter; - this.userService = userService; - this.roleService = roleService; - } - - @GetMapping(value = "/{id}", produces = MediaTypes.HAL_JSON_VALUE) - @Operation(summary = "Get process by id") - public PetriNetReferenceResource getOne(@PathVariable("id") String id, Locale locale) { - return new PetriNetReferenceResource(IPetriNetService.transformToReference(this.petriNetService.getPetriNet(decodeUrl(id)), locale)); - } - - @Operation(summary = "Get process by identifier and version") - @GetMapping(value = "/{identifier}/{version}", produces = MediaTypes.HAL_JSON_VALUE) - @ResponseBody - public PetriNetReferenceResource getOne(@PathVariable("identifier") String identifier, @PathVariable("version") String version, Locale locale) { - String resolvedIdentifier = Base64.isBase64(identifier) ? new String(Base64.decodeBase64(identifier)) : identifier; - return new PetriNetReferenceResource(this.petriNetService.getReference(resolvedIdentifier, this.converter.convert(version), ActorTransformer.toLoggedUser(userService.getLoggedUser()), locale)); - } - - @Operation(summary = "Search processes") - @PostMapping(value = "/search", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaTypes.HAL_JSON_VALUE) - public ResponseEntity> searchPetriNets(@RequestBody PetriNetSearch criteria, Pageable pageable, PagedResourcesAssembler assembler, Locale locale) { - Page nets = petriNetService.search(criteria, ActorTransformer.toLoggedUser(userService.getLoggedUser()), pageable, locale); - return ResponseEntity.ok(new PageImpl<>(nets.stream().map(PetriNetReferenceResource::new).toList(), pageable, nets.getTotalElements())); - } - - @Operation(summary = "Get roles of process") - @GetMapping(value = "/{netId}/roles", produces = MediaTypes.HAL_JSON_VALUE) - public ProcessRolesResource getRoles(@PathVariable("netId") String netId, Locale locale) { - netId = decodeUrl(netId); - return new ProcessRolesResource(roleService.findAllByNetStringId(netId), petriNetService.getPetriNet(netId).getPermissions(), netId, locale); - } - - @Operation(summary = "Get transactions of process") - @GetMapping(value = "/{netId}/transactions", produces = MediaTypes.HAL_JSON_VALUE) - public TransactionsResource getTransactions(@PathVariable("netId") String netId, Locale locale) { - PetriNet net = petriNetService.getPetriNet(decodeUrl(netId)); - return new TransactionsResource(net.getTransactions().values(), netId, locale); - } - - @Operation(summary = "Get data fields of transitions") - @PostMapping(value = "/data", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaTypes.HAL_JSON_VALUE) - public DataFieldReferencesResource getDataFieldReferences(@RequestBody List referenceBody, Locale locale) { - return new DataFieldReferencesResource(petriNetService.getDataFieldReferences(referenceBody, locale)); - } - - @Operation(summary = "Get transitions of processes") - @GetMapping(value = "/transitions", produces = MediaTypes.HAL_JSON_VALUE) - public TransitionReferencesResource getTransitionReferences(@RequestParam List ids, Locale locale) { - ids.forEach(PetriNetController::decodeUrl); - return new TransitionReferencesResource(petriNetService.getTransitionReferences(ids, ActorTransformer.toLoggedUser(userService.getLoggedUser()), locale)); - } -} +//package com.netgrif.application.engine.petrinet.web; +// +//import com.netgrif.application.engine.auth.service.UserService; +//import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; +//import com.netgrif.application.engine.objects.petrinet.domain.PetriNet; +//import com.netgrif.application.engine.objects.petrinet.domain.PetriNetSearch; +//import com.netgrif.application.engine.petrinet.domain.version.StringToVersionConverter; +//import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService; +//import com.netgrif.application.engine.adapter.spring.petrinet.service.ProcessRoleService; +//import com.netgrif.application.engine.petrinet.web.responsebodies.*; +//import io.swagger.v3.oas.annotations.Operation; +//import io.swagger.v3.oas.annotations.tags.Tag; +//import lombok.extern.slf4j.Slf4j; +//import org.apache.commons.codec.binary.Base64; +//import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +//import org.springframework.data.domain.Page; +//import org.springframework.data.domain.PageImpl; +//import org.springframework.data.domain.Pageable; +//import org.springframework.data.web.PagedResourcesAssembler; +//import org.springframework.hateoas.MediaTypes; +//import org.springframework.http.MediaType; +//import org.springframework.http.ResponseEntity; +//import org.springframework.web.bind.annotation.*; +// +//import java.util.List; +//import java.util.Locale; +// +//import static com.netgrif.application.engine.petrinet.web.PetriNetController.decodeUrl; +// +//@Slf4j +//@RestController +//@ConditionalOnProperty( +// value = "netgrif.engine.security.web.public-web.petri-net-enabled", +// havingValue = "true", +// matchIfMissing = true +//) +//@Tag(name = "Public PetriNet Controller") +//@RequestMapping({"/api/public/petrinet"}) +//public class PublicPetriNetController { +// +// private final IPetriNetService petriNetService; +// +// private final ProcessRoleService roleService; +// +// private final UserService userService; +// +// private final StringToVersionConverter converter; +// +// public PublicPetriNetController(IPetriNetService petriNetService, UserService userService, StringToVersionConverter converter, ProcessRoleService roleService) { +// this.petriNetService = petriNetService; +// this.converter = converter; +// this.userService = userService; +// this.roleService = roleService; +// } +// +// @GetMapping(value = "/{id}", produces = MediaTypes.HAL_JSON_VALUE) +// @Operation(summary = "Get process by id") +// public PetriNetReferenceResource getOne(@PathVariable("id") String id, Locale locale) { +// return new PetriNetReferenceResource(IPetriNetService.transformToReference(this.petriNetService.getPetriNet(decodeUrl(id)), locale)); +// } +// +// @Operation(summary = "Get process by identifier and version") +// @GetMapping(value = "/{identifier}/{version}", produces = MediaTypes.HAL_JSON_VALUE) +// @ResponseBody +// public PetriNetReferenceResource getOne(@PathVariable("identifier") String identifier, @PathVariable("version") String version, Locale locale) { +// String resolvedIdentifier = Base64.isBase64(identifier) ? new String(Base64.decodeBase64(identifier)) : identifier; +// return new PetriNetReferenceResource(this.petriNetService.getReference(resolvedIdentifier, this.converter.convert(version), ActorTransformer.toLoggedUser(userService.getLoggedUser()), locale)); +// } +// +// @Operation(summary = "Search processes") +// @PostMapping(value = "/search", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaTypes.HAL_JSON_VALUE) +// public ResponseEntity> searchPetriNets(@RequestBody PetriNetSearch criteria, Pageable pageable, PagedResourcesAssembler assembler, Locale locale) { +// Page nets = petriNetService.search(criteria, ActorTransformer.toLoggedUser(userService.getLoggedUser()), pageable, locale); +// return ResponseEntity.ok(new PageImpl<>(nets.stream().map(PetriNetReferenceResource::new).toList(), pageable, nets.getTotalElements())); +// } +// +// @Operation(summary = "Get roles of process") +// @GetMapping(value = "/{netId}/roles", produces = MediaTypes.HAL_JSON_VALUE) +// public ProcessRolesResource getRoles(@PathVariable("netId") String netId, Locale locale) { +// netId = decodeUrl(netId); +// return new ProcessRolesResource(roleService.findAllByNetStringId(netId), petriNetService.getPetriNet(netId).getPermissions(), netId, locale); +// } +// +// @Operation(summary = "Get transactions of process") +// @GetMapping(value = "/{netId}/transactions", produces = MediaTypes.HAL_JSON_VALUE) +// public TransactionsResource getTransactions(@PathVariable("netId") String netId, Locale locale) { +// PetriNet net = petriNetService.getPetriNet(decodeUrl(netId)); +// return new TransactionsResource(net.getTransactions().values(), netId, locale); +// } +// +// @Operation(summary = "Get data fields of transitions") +// @PostMapping(value = "/data", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaTypes.HAL_JSON_VALUE) +// public DataFieldReferencesResource getDataFieldReferences(@RequestBody List referenceBody, Locale locale) { +// return new DataFieldReferencesResource(petriNetService.getDataFieldReferences(referenceBody, locale)); +// } +// +// @Operation(summary = "Get transitions of processes") +// @GetMapping(value = "/transitions", produces = MediaTypes.HAL_JSON_VALUE) +// public TransitionReferencesResource getTransitionReferences(@RequestParam List ids, Locale locale) { +// ids.forEach(PetriNetController::decodeUrl); +// return new TransitionReferencesResource(petriNetService.getTransitionReferences(ids, ActorTransformer.toLoggedUser(userService.getLoggedUser()), locale)); +// } +//} diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/responsebodies/PetriNetReferenceResource.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/responsebodies/PetriNetReferenceResource.java index 9bc3f8e23b0..a65fe283c65 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/responsebodies/PetriNetReferenceResource.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/responsebodies/PetriNetReferenceResource.java @@ -14,11 +14,11 @@ public PetriNetReferenceResource(PetriNetReference content) { private void buildLinks() { add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder - .methodOn(PetriNetController.class).getOne(getContent().getStringId(), null, null)) + .methodOn(PetriNetController.class).getOne(getContent().getStringId(), null)) .withSelfRel()); add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder - .methodOn(PetriNetController.class).getOne(getContent().getIdentifier(), getContent().getVersion(), null, null)) + .methodOn(PetriNetController.class).getOne(getContent().getIdentifier(), getContent().getVersion(), null)) .withRel("identifier")); add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/responsebodies/TransitionReferencesResource.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/responsebodies/TransitionReferencesResource.java index 171ea104f23..f26325326d1 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/responsebodies/TransitionReferencesResource.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/responsebodies/TransitionReferencesResource.java @@ -17,6 +17,6 @@ public TransitionReferencesResource(Iterable content) { private void buildLinks() { add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(PetriNetController.class) - .getTransitionReferences(new ArrayList<>(), null, null)).withSelfRel()); + .getTransitionReferences(new ArrayList<>(), null)).withSelfRel()); } } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/PublicWorkflowController.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/PublicWorkflowController.java index 2d2fc420145..f26ea81c3d7 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/PublicWorkflowController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/PublicWorkflowController.java @@ -1,61 +1,61 @@ -package com.netgrif.application.engine.workflow.web; - -import com.netgrif.application.engine.eventoutcomes.LocalisedEventOutcomeFactory; -import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; -import com.netgrif.application.engine.objects.auth.domain.LoggedUser; -import com.netgrif.application.engine.auth.service.UserService; -import com.netgrif.application.engine.objects.workflow.domain.eventoutcomes.caseoutcomes.CreateCaseEventOutcome; -import com.netgrif.application.engine.workflow.domain.eventoutcomes.response.EventOutcomeWithMessage; -import com.netgrif.application.engine.workflow.domain.eventoutcomes.response.EventOutcomeWithMessageResource; -import com.netgrif.application.engine.workflow.service.interfaces.IWorkflowService; -import com.netgrif.application.engine.workflow.web.requestbodies.CreateCaseBody; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.hateoas.EntityModel; -import org.springframework.hateoas.MediaTypes; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - - -import java.util.Locale; - -@Slf4j -@RestController -@ConditionalOnProperty( - value = "netgrif.engine.security.web.public-web.case-enabled", - havingValue = "true", - matchIfMissing = true -) -@RequestMapping({"/api/public"}) -@Tag(name = "Public Workflow Controller") -public class PublicWorkflowController { - - private final IWorkflowService workflowService; - - private final UserService userService; - - public PublicWorkflowController(IWorkflowService workflowService, UserService userService) { - this.userService = userService; - this.workflowService = workflowService; - } - - @PreAuthorize("@workflowAuthorizationService.canCallCreate(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #body.netId)") - @PostMapping(value = "/case", consumes = "application/json;charset=UTF-8", produces = MediaTypes.HAL_JSON_VALUE) - @Operation(summary = "Create new case") - public EntityModel createCase(@RequestBody CreateCaseBody body, Locale locale) { - LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedUser()); - try { - CreateCaseEventOutcome outcome = this.workflowService.createCase(body.netId, body.title, body.color, loggedUser, locale); - return EventOutcomeWithMessageResource.successMessage("Case created successfully", - LocalisedEventOutcomeFactory.from(outcome, locale)); - } catch (Exception e) { - log.error("Creating case failed:" + e.getMessage(), e); - return EventOutcomeWithMessageResource.errorMessage("Creating case failed: " + e.getMessage()); - } - } -} +//package com.netgrif.application.engine.workflow.web; +// +//import com.netgrif.application.engine.eventoutcomes.LocalisedEventOutcomeFactory; +//import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; +//import com.netgrif.application.engine.objects.auth.domain.LoggedUser; +//import com.netgrif.application.engine.auth.service.UserService; +//import com.netgrif.application.engine.objects.workflow.domain.eventoutcomes.caseoutcomes.CreateCaseEventOutcome; +//import com.netgrif.application.engine.workflow.domain.eventoutcomes.response.EventOutcomeWithMessage; +//import com.netgrif.application.engine.workflow.domain.eventoutcomes.response.EventOutcomeWithMessageResource; +//import com.netgrif.application.engine.workflow.service.interfaces.IWorkflowService; +//import com.netgrif.application.engine.workflow.web.requestbodies.CreateCaseBody; +//import io.swagger.v3.oas.annotations.Operation; +//import io.swagger.v3.oas.annotations.tags.Tag; +//import lombok.extern.slf4j.Slf4j; +//import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +//import org.springframework.hateoas.EntityModel; +//import org.springframework.hateoas.MediaTypes; +//import org.springframework.security.access.prepost.PreAuthorize; +//import org.springframework.web.bind.annotation.PostMapping; +//import org.springframework.web.bind.annotation.RequestBody; +//import org.springframework.web.bind.annotation.RequestMapping; +//import org.springframework.web.bind.annotation.RestController; +// +// +//import java.util.Locale; +// +//@Slf4j +//@RestController +//@ConditionalOnProperty( +// value = "netgrif.engine.security.web.public-web.case-enabled", +// havingValue = "true", +// matchIfMissing = true +//) +//@RequestMapping({"/api/public"}) +//@Tag(name = "Public Workflow Controller") +//public class PublicWorkflowController { +// +// private final IWorkflowService workflowService; +// +// private final UserService userService; +// +// public PublicWorkflowController(IWorkflowService workflowService, UserService userService) { +// this.userService = userService; +// this.workflowService = workflowService; +// } +// +// @PreAuthorize("@workflowAuthorizationService.canCallCreate(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #body.netId)") +// @PostMapping(value = "/case", consumes = "application/json;charset=UTF-8", produces = MediaTypes.HAL_JSON_VALUE) +// @Operation(summary = "Create new case") +// public EntityModel createCase(@RequestBody CreateCaseBody body, Locale locale) { +// LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedUser()); +// try { +// CreateCaseEventOutcome outcome = this.workflowService.createCase(body.netId, body.title, body.color, loggedUser, locale); +// return EventOutcomeWithMessageResource.successMessage("Case created successfully", +// LocalisedEventOutcomeFactory.from(outcome, locale)); +// } catch (Exception e) { +// log.error("Creating case failed:" + e.getMessage(), e); +// return EventOutcomeWithMessageResource.errorMessage("Creating case failed: " + e.getMessage()); +// } +// } +//} diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/auth/domain/AnonymousUser.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/auth/domain/AnonymousUser.java index c641917e0c9..d6e915fb03f 100644 --- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/auth/domain/AnonymousUser.java +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/auth/domain/AnonymousUser.java @@ -36,7 +36,7 @@ public class AnonymousUser extends AbstractUser { * @param anonymousAuthority the authority to be assigned if no specific authorities are provided */ public AnonymousUser(AnonymousUserRef ref, Authority anonymousAuthority) { - this.id = new ObjectId(); + this.id = new ObjectId(ref.getId()); this.realmId = ref.getRealmId(); this.username = "anonymous@" + this.realmId; this.firstName = ref.getDisplayName(); diff --git a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/UserServiceImpl.java b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/UserServiceImpl.java index dd9ee6e196a..691369e0625 100644 --- a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/UserServiceImpl.java +++ b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/UserServiceImpl.java @@ -1,5 +1,7 @@ package com.netgrif.application.engine.auth.service; +import com.netgrif.application.engine.adapter.spring.auth.domain.AnonymousUser; +import com.netgrif.application.engine.adapter.spring.auth.domain.AnonymousUserRef; import com.netgrif.application.engine.adapter.spring.petrinet.service.ProcessRoleService; import com.netgrif.application.engine.adapter.spring.utils.PaginationProperties; import com.netgrif.application.engine.adapter.spring.workflow.service.FilterImportExportService; @@ -59,6 +61,8 @@ public class UserServiceImpl implements UserService { private RealmService realmService; + private AnonymousUserRefService anonymousUserRefService; + @Getter private PaginationProperties paginationProperties; @@ -120,6 +124,11 @@ public void setRealmService(RealmService realmService) { this.realmService = realmService; } + @Autowired + public void setAnonymousUserRefService(AnonymousUserRefService anonymousUserRefService) { + this.anonymousUserRefService = anonymousUserRefService; + } + @Override public AbstractUser saveUser(AbstractUser user, String realmId) { user.setRealmId(realmId); @@ -340,7 +349,11 @@ public AbstractUser findById(String id, String realmId) { log.debug("Finding user by ID [{}]", id); String collectionName = collectionNameProvider.getCollectionNameForRealm(realmId); Optional userOpt = userRepository.findById(new ObjectId(id), mongoTemplate, collectionName); - return userOpt.orElse(null); + if (userOpt.isPresent()) { + return userOpt.get(); + } + Optional anonymousUserRefOptional = anonymousUserRefService.getRef(realmId); + return anonymousUserRefOptional.map(anonymousUserRef -> new AnonymousUser(anonymousUserRef, authorityService.getOrCreate(Authority.anonymous))).orElse(null); } @Override From e260dbe851b63b25f47732c0c3cdec0be164b589 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Wed, 21 Jan 2026 09:07:41 +0100 Subject: [PATCH 09/14] Refactor authorization logic and enhance public API access Replaced specific authority checks with a broader `hasAnyAuthority` method, simplifying permission logic and allowing more flexible role-based access control. Added public endpoints to several APIs to extend functionality for anonymous or unauthorized users where appropriate. Removed unused imports for better code clarity and organization. --- .../auth/service/AuthorizationService.java | 11 ++++ .../interfaces/IAuthorizationService.java | 2 + .../security/PublicAuthenticationFilter.java | 4 -- .../service/PetriNetAuthorizationService.java | 6 +- .../IPetriNetAuthorizationService.java | 4 +- .../petrinet/web/PetriNetController.java | 42 ++++++++----- .../PetriNetReferenceResource.java | 3 +- .../engine/workflow/web/TaskController.java | 60 +++++++++++-------- .../workflow/web/WorkflowController.java | 18 ++++-- .../src/main/resources/application-dev.yaml | 2 +- .../src/main/resources/application.yaml | 2 +- .../application/engine/TestHelper.groovy | 2 +- .../filters/NetgrifOncePerRequestFilter.java | 3 +- 13 files changed, 97 insertions(+), 62 deletions(-) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/auth/service/AuthorizationService.java b/application-engine/src/main/java/com/netgrif/application/engine/auth/service/AuthorizationService.java index 260f705d5e4..74d96d90c6e 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/auth/service/AuthorizationService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/auth/service/AuthorizationService.java @@ -1,6 +1,9 @@ package com.netgrif.application.engine.auth.service; import com.netgrif.application.engine.auth.service.interfaces.IAuthorizationService; + +import java.util.Arrays; + import com.netgrif.application.engine.objects.auth.domain.LoggedUser; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -18,4 +21,12 @@ public boolean hasAuthority(String authority) { LoggedUser loggedUser = userService.getLoggedUserFromContext(); return loggedUser.getAuthoritySet().stream().anyMatch(it -> it.getAuthority().equals(authority)); } + + @Override + public boolean hasAnyAuthority(String... authority) { + // TODO: impersonation +// LoggedUser loggedUser = userService.getLoggedUserFromContext().getSelfOrImpersonated(); + LoggedUser loggedUser = userService.getLoggedUserFromContext(); + return loggedUser.getAuthoritySet().stream().anyMatch(it -> Arrays.stream(authority).anyMatch(a -> it.getAuthority().contains(a))); + } } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/auth/service/interfaces/IAuthorizationService.java b/application-engine/src/main/java/com/netgrif/application/engine/auth/service/interfaces/IAuthorizationService.java index 81feafc98d7..89ade3cf8a0 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/auth/service/interfaces/IAuthorizationService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/auth/service/interfaces/IAuthorizationService.java @@ -2,4 +2,6 @@ public interface IAuthorizationService { boolean hasAuthority(String authority); + + boolean hasAnyAuthority(String... authority); } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/PublicAuthenticationFilter.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/PublicAuthenticationFilter.java index 088737c6340..ad3a74772cd 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/PublicAuthenticationFilter.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/security/PublicAuthenticationFilter.java @@ -6,7 +6,6 @@ import com.netgrif.application.engine.adapter.spring.configuration.filters.NetgrifOncePerRequestFilter; import com.netgrif.application.engine.adapter.spring.configuration.filters.requests.NetgrifHttpServletRequest; import com.netgrif.application.engine.auth.service.AnonymousUserRefService; -import com.netgrif.application.engine.auth.service.RealmService; import com.netgrif.application.engine.configuration.properties.SecurityConfigurationProperties; import com.netgrif.application.engine.objects.auth.domain.*; import com.netgrif.application.engine.auth.service.AuthorityService; @@ -35,15 +34,12 @@ public class PublicAuthenticationFilter extends NetgrifOncePerRequestFilter { private final AnonymousUserRefService anonymousUserRefService; private final AuthorityService authorityService; - private final RealmService realmService; public PublicAuthenticationFilter(AnonymousUserRefService anonymousUserRefService, AuthorityService authorityService, - RealmService realmService, SecurityConfigurationProperties securityConfigurationProperties) { this.anonymousUserRefService = anonymousUserRefService; this.authorityService = authorityService; - this.realmService = realmService; setRequestMatcher(Arrays.stream(securityConfigurationProperties.getServerPatterns()).map(AntPathRequestMatcher::new).collect(Collectors.toSet())); } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetAuthorizationService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetAuthorizationService.java index ab5f17ad13e..1d74ef1dfc2 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetAuthorizationService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetAuthorizationService.java @@ -1,13 +1,13 @@ package com.netgrif.application.engine.petrinet.service; -import com.netgrif.application.engine.objects.auth.domain.LoggedUser; +import com.netgrif.application.engine.objects.auth.domain.AbstractUser; import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetAuthorizationService; import org.springframework.stereotype.Service; @Service public class PetriNetAuthorizationService implements IPetriNetAuthorizationService { @Override - public boolean canCallProcessDelete(LoggedUser loggedUser, String processId) { - return loggedUser.isAdmin(); + public boolean canCallProcessDelete(AbstractUser user, String processId) { + return user.isAdmin(); } } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetAuthorizationService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetAuthorizationService.java index 1621d9609d5..de5678f27e5 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetAuthorizationService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetAuthorizationService.java @@ -1,9 +1,9 @@ package com.netgrif.application.engine.petrinet.service.interfaces; -import com.netgrif.application.engine.objects.auth.domain.LoggedUser; +import com.netgrif.application.engine.objects.auth.domain.AbstractUser; public interface IPetriNetAuthorizationService { - boolean canCallProcessDelete(LoggedUser loggedUser, String processId); + boolean canCallProcessDelete(AbstractUser user, String processId); } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java index 1ba932db981..5b86b6646c4 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java @@ -125,6 +125,7 @@ public EntityModel importPetriNet( } } + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN')") @Operation(summary = "Get all processes", security = {@SecurityRequirement(name = "BasicAuth")}) @GetMapping(produces = MediaTypes.HAL_JSON_VALUE) public ResponseEntity> getAll(@RequestParam(value = "indentifier", required = false) String identifier, @RequestParam(value = "version", required = false) String version, Pageable pageable, Locale locale) { @@ -141,41 +142,47 @@ public ResponseEntity> getAll(@RequestParam(value = "ind } } + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN', 'ANONYMOUS')") @Operation(summary = "Get process by id", security = {@SecurityRequirement(name = "BasicAuth")}) - @GetMapping(value = "/{id}", produces = MediaTypes.HAL_JSON_VALUE) + @GetMapping(value = {"/{id}", "/public/{id}"}, produces = MediaTypes.HAL_JSON_VALUE) public PetriNetReferenceResource getOne(@PathVariable("id") String id, Locale locale) { return new PetriNetReferenceResource(IPetriNetService.transformToReference(service.getPetriNet(decodeUrl(id)), locale)); } + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN', 'ANONYMOUS')") @Operation(summary = "Get process by identifier and version", security = {@SecurityRequirement(name = "BasicAuth")}) - @GetMapping(value = "/{identifier}/{version}", produces = MediaTypes.HAL_JSON_VALUE) + @GetMapping(value = {"/{identifier}/{version}", "/public/{identifier}/{version}"}, produces = MediaTypes.HAL_JSON_VALUE) public PetriNetReferenceResource getOne(@PathVariable("identifier") String identifier, @PathVariable("version") String version, Locale locale) { String resolvedIdentifier = Base64.isBase64(identifier) ? new String(Base64.decodeBase64(identifier)) : identifier; return new PetriNetReferenceResource(service.getReference(resolvedIdentifier, converter.convert(version), ActorTransformer.toLoggedUser(userService.getLoggedUserFromContext()), locale)); } + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN', 'ANONYMOUS')") @Operation(summary = "Get transitions of processes", security = {@SecurityRequirement(name = "BasicAuth")}) - @GetMapping(value = "/transitions", produces = MediaTypes.HAL_JSON_VALUE) + @GetMapping(value = {"/transitions", "/public/transitions"}, produces = MediaTypes.HAL_JSON_VALUE) public TransitionReferencesResource getTransitionReferences(@RequestParam List ids, Locale locale) { ids.forEach(id -> id = decodeUrl(id)); return new TransitionReferencesResource(service.getTransitionReferences(ids, ActorTransformer.toLoggedUser(userService.getLoggedUserFromContext()), locale)); } + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN', 'ANONYMOUS')") @Operation(summary = "Get data fields of transitions", security = {@SecurityRequirement(name = "BasicAuth")}) - @PostMapping(value = "/data", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaTypes.HAL_JSON_VALUE) + @PostMapping(value = {"/data", "/public/data"}, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaTypes.HAL_JSON_VALUE) public DataFieldReferencesResource getDataFieldReferences(@RequestBody List referenceBody, Locale locale) { return new DataFieldReferencesResource(service.getDataFieldReferences(referenceBody, locale)); } + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN', 'ANONYMOUS')") @Operation(summary = "Get roles of process", security = {@SecurityRequirement(name = "BasicAuth")}) - @GetMapping(value = "/{netId}/roles", produces = MediaTypes.HAL_JSON_VALUE) + @GetMapping(value = {"/{netId}/roles", "/public/{netId}/roles"}, produces = MediaTypes.HAL_JSON_VALUE) public ProcessRolesResource getRoles(@PathVariable("netId") String netId, Locale locale) { netId = decodeUrl(netId); return new ProcessRolesResource(roleService.findAllByNetStringId(netId), service.getPetriNet(decodeUrl(netId)).getPermissions(), netId, locale); } + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN', 'ANONYMOUS')") @Operation(summary = "Get transactions of process", security = {@SecurityRequirement(name = "BasicAuth")}) - @GetMapping(value = "/{netId}/transactions", produces = MediaTypes.HAL_JSON_VALUE) + @GetMapping(value = {"/{netId}/transactions", "/public/{netId}/transactions"}, produces = MediaTypes.HAL_JSON_VALUE) public TransactionsResource getTransactions(@PathVariable("netId") String netId, Locale locale) { PetriNet net = service.getPetriNet(decodeUrl(netId)); return new TransactionsResource(net.getTransactions().values(), netId, locale); @@ -184,7 +191,7 @@ public TransactionsResource getTransactions(@PathVariable("netId") String netId, @PreAuthorize("@authorizationService.hasAuthority('ADMIN')") @Operation(summary = "Download process model", security = {@SecurityRequirement(name = "BasicAuth")}) @GetMapping(value = "/{netId}/file", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) - public FileSystemResource getNetFile(@PathVariable("netId") String netId, @RequestParam(value = "title", required = false) String title, Authentication auth, HttpServletResponse response) { + public FileSystemResource getNetFile(@PathVariable("netId") String netId, @RequestParam(value = "title", required = false) String title, HttpServletResponse response) { FileSystemResource fileResource = service.getFile(decodeUrl(netId), decodeUrl(title)); response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); response.setHeader("Content-Disposition", "attachment; filename=\"" + fileResource.getFilename() + Importer.FILE_EXTENSION + "\""); @@ -193,25 +200,27 @@ public FileSystemResource getNetFile(@PathVariable("netId") String netId, @Reque return fileResource; } + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN', 'ANONYMOUS')") @Operation(summary = "Search processes", security = {@SecurityRequirement(name = "BasicAuth")}) - @PostMapping(value = "/search", produces = MediaTypes.HAL_JSON_VALUE) + @PostMapping(value = {"/search", "/public/search"}, produces = MediaTypes.HAL_JSON_VALUE) public @ResponseBody - ResponseEntity> searchPetriNets(@RequestBody PetriNetSearch criteria, Authentication auth, Pageable pageable, PagedResourcesAssembler assembler, Locale locale) { - LoggedUser user = (LoggedUser) auth.getPrincipal(); + ResponseEntity> searchPetriNets(@RequestBody PetriNetSearch criteria, Pageable pageable, PagedResourcesAssembler assembler, Locale locale) { + LoggedUser user = ActorTransformer.toLoggedUser(userService.getLoggedUser()); Page nets = service.search(criteria, user, pageable, locale); return ResponseEntity.ok(new PageImpl<>(nets.stream().map(PetriNetReferenceResource::new).toList(), pageable, nets.getTotalElements())); } + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN')") @Operation(summary = "Search elastic processes", security = {@SecurityRequirement(name = "BasicAuth")}) @PostMapping(value = "/search_elastic", produces = MediaTypes.HAL_JSON_VALUE) public @ResponseBody - PagedModel searchElasticPetriNets(@RequestBody PetriNetSearch criteria, Authentication auth, Pageable pageable, PagedResourcesAssembler assembler, Locale locale) { - LoggedUser user = (LoggedUser) auth.getPrincipal(); + PagedModel searchElasticPetriNets(@RequestBody PetriNetSearch criteria, Pageable pageable, PagedResourcesAssembler assembler, Locale locale) { + LoggedUser user = ActorTransformer.toLoggedUser(userService.getLoggedUser()); // TODO: add Merge Filters and its operations Page nets = elasticService.search(criteria, user, pageable, locale, false); Link selfLink = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(PetriNetController.class) - .searchElasticPetriNets(criteria, auth, pageable, assembler, locale)).withRel("search_elastic"); + .searchElasticPetriNets(criteria, pageable, assembler, locale)).withRel("search_elastic"); // TODO doriesit linky pista ich zakomentoval PagedModel resources = assembler.toModel(nets, new PetriNetReferenceResourceAssembler(), selfLink); @@ -219,7 +228,7 @@ PagedModel searchElasticPetriNets(@RequestBody PetriN return resources; } - @PreAuthorize("@petriNetAuthorizationService.canCallProcessDelete(#auth.getPrincipal(), #processId)") + @PreAuthorize("@petriNetAuthorizationService.canCallProcessDelete(@userService.getLoggedUser(), #processId)") @Operation(summary = "Delete process", description = "Caller must have the ADMIN role. Removes the specified process, along with it's cases, tasks and process roles.", security = {@SecurityRequirement(name = "BasicAuth")}) @@ -228,13 +237,13 @@ PagedModel searchElasticPetriNets(@RequestBody PetriN @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements") }) @DeleteMapping(value = "/{id}", produces = MediaTypes.HAL_JSON_VALUE) - public MessageResource deletePetriNet(@PathVariable("id") String processId, @RequestParam(required = false) boolean force, Authentication auth) { + public MessageResource deletePetriNet(@PathVariable("id") String processId, @RequestParam(required = false) boolean force) { String decodedProcessId = decodeUrl(processId); if (Objects.equals(decodedProcessId, "")) { log.error("Deleting Petri net [" + processId + "] failed: could not decode process ID from URL"); return MessageResource.errorMessage("Deleting Petri net " + processId + " failed!"); } - LoggedUser user = (LoggedUser) auth.getPrincipal(); + LoggedUser user = ActorTransformer.toLoggedUser(userService.getLoggedUser()); asyncRunner.execute(() -> { if (force) { service.forceDeletePetriNet(decodedProcessId, user); @@ -245,6 +254,7 @@ public MessageResource deletePetriNet(@PathVariable("id") String processId, @Req return MessageResource.successMessage("Petri net " + decodedProcessId + " is being deleted"); } + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN')") @Operation(summary = "Get net by case id", security = {@SecurityRequirement(name = "BasicAuth")}) @GetMapping(value = "/case/{id}", produces = MediaTypes.HAL_JSON_VALUE) public PetriNetImportReference getOne(@PathVariable("id") String caseId) { diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/responsebodies/PetriNetReferenceResource.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/responsebodies/PetriNetReferenceResource.java index a65fe283c65..6966842cee6 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/responsebodies/PetriNetReferenceResource.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/responsebodies/PetriNetReferenceResource.java @@ -1,6 +1,5 @@ package com.netgrif.application.engine.petrinet.web.responsebodies; -import com.netgrif.application.engine.petrinet.web.responsebodies.PetriNetReference; import com.netgrif.application.engine.petrinet.web.PetriNetController; import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; @@ -30,7 +29,7 @@ private void buildLinks() { .withRel("transaction")); add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder - .methodOn(PetriNetController.class).getNetFile(getContent().getStringId(), getContent().getTitle(), null, null)) + .methodOn(PetriNetController.class).getNetFile(getContent().getStringId(), getContent().getTitle(), null)) .withRel("file")); } } \ No newline at end of file diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/TaskController.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/TaskController.java index 2be379cb96f..cdcd3ba233e 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/TaskController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/TaskController.java @@ -2,8 +2,6 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.netgrif.application.engine.auth.service.UserService; -import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; -import com.netgrif.application.engine.objects.auth.domain.LoggedUser; import com.netgrif.application.engine.elastic.service.interfaces.IElasticTaskService; import com.netgrif.application.engine.elastic.web.requestbodies.singleaslist.SingleElasticTaskSearchRequestAsList; import com.netgrif.application.engine.workflow.domain.MergeFilterOperation; @@ -34,7 +32,6 @@ import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; @@ -63,6 +60,7 @@ public TaskController(ITaskService taskService, } @Override + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN')") @Operation(summary = "Get all tasks", security = {@SecurityRequirement(name = "BasicAuth")}) @GetMapping(produces = MediaTypes.HAL_JSON_VALUE) public PagedModel getAll(Pageable pageable, PagedResourcesAssembler assembler, Locale locale) { @@ -70,6 +68,7 @@ public PagedModel getAll(Pageable pageable, PagedResource } @Override + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN')") @Operation(summary = "Get all tasks by cases", security = {@SecurityRequirement(name = "BasicAuth")}) @PostMapping(value = "/case", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaTypes.HAL_JSON_VALUE) public PagedModel getAllByCases(@RequestBody List cases, Pageable pageable, PagedResourcesAssembler assembler, Locale locale) { @@ -77,24 +76,26 @@ public PagedModel getAllByCases(@RequestBody List } @Override + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN', 'ANONYMOUS')") @Operation(summary = "Get tasks of the case", security = {@SecurityRequirement(name = "BasicAuth")}) - @GetMapping(value = "/case/{id}", produces = MediaType.APPLICATION_JSON_VALUE) + @GetMapping(value = {"/case/{id}", "/public/case/{id}"}, produces = MediaType.APPLICATION_JSON_VALUE) public List getTasksOfCase(@PathVariable("id") String caseId, Locale locale) { return super.getTasksOfCase(caseId, locale); } @Override + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN')") @Operation(summary = "Get task by id", security = {@SecurityRequirement(name = "BasicAuth")}) @GetMapping(value = "/{id}", produces = MediaTypes.HAL_JSON_VALUE) public LocalisedTaskResource getOne(@PathVariable("id") String taskId, Locale locale) { return super.getOne(taskId, locale); } - @PreAuthorize("@taskAuthorizationService.canCallAssign(@userService.getLoggedUser(), #taskId)") + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN', 'ANONYMOUS') && @taskAuthorizationService.canCallAssign(@userService.getLoggedUser(), #taskId)") @Operation(summary = "Assign task", description = "Caller must be able to perform the task, or must be an ADMIN", security = {@SecurityRequirement(name = "BasicAuth")}) - @GetMapping(value = "/assign/{id}", produces = MediaTypes.HAL_JSON_VALUE) + @GetMapping(value = {"/assign/{id}", "/public/assign/{id}"}, produces = MediaTypes.HAL_JSON_VALUE) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), @@ -103,7 +104,7 @@ public EntityModel assign(@PathVariable("id") String ta return super.assign(taskId, locale); } - @PreAuthorize("@taskAuthorizationService.canCallDelegate(@userService.getLoggedUser(), #taskId)") + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN') && @taskAuthorizationService.canCallDelegate(@userService.getLoggedUser(), #taskId)") @Operation(summary = "Delegate task", description = "Caller must be able to delegate the task, or must be an ADMIN", security = {@SecurityRequirement(name = "BasicAuth")}) @@ -116,11 +117,11 @@ public EntityModel delegate(@PathVariable("id") String return super.delegate(taskId, delegatedId, locale); } - @PreAuthorize("@taskAuthorizationService.canCallFinish(@userService.getLoggedUser(), #taskId)") + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN', 'ANONYMOUS') && @taskAuthorizationService.canCallFinish(@userService.getLoggedUser(), #taskId)") @Operation(summary = "Finish task", description = "Caller must be assigned to the task, or must be an ADMIN", security = {@SecurityRequirement(name = "BasicAuth")}) - @GetMapping(value = "/finish/{id}", produces = MediaTypes.HAL_JSON_VALUE) + @GetMapping(value = {"/finish/{id}", "/public/finish/{id}"}, produces = MediaTypes.HAL_JSON_VALUE) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), @@ -129,11 +130,11 @@ public EntityModel finish(@PathVariable("id") String ta return super.finish(taskId, locale); } - @PreAuthorize("@taskAuthorizationService.canCallCancel(@userService.getLoggedUser(), #taskId)") + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN', 'ANONYMOUS') && @taskAuthorizationService.canCallCancel(@userService.getLoggedUser(), #taskId)") @Operation(summary = "Cancel task", description = "Caller must be assigned to the task, or must be an ADMIN", security = {@SecurityRequirement(name = "BasicAuth")}) - @GetMapping(value = "/cancel/{id}", produces = MediaTypes.HAL_JSON_VALUE) + @GetMapping(value = {"/cancel/{id}", "/public/cancel/{id}"}, produces = MediaTypes.HAL_JSON_VALUE) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), @@ -143,6 +144,7 @@ public EntityModel cancel(@PathVariable("id") String ta } @Override + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN')") @Operation(summary = "Get all tasks assigned to logged user", security = {@SecurityRequirement(name = "BasicAuth")}) @GetMapping(value = "/my", produces = MediaTypes.HAL_JSON_VALUE) public PagedModel getMy(Pageable pageable, PagedResourcesAssembler assembler, Locale locale) { @@ -150,6 +152,7 @@ public PagedModel getMy(Pageable pageable, PagedResources } @Override + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN')") @Operation(summary = "Get all finished tasks by logged user", security = {@SecurityRequirement(name = "BasicAuth")}) @GetMapping(value = "/my/finished", produces = MediaTypes.HAL_JSON_VALUE) public PagedModel getMyFinished(Pageable pageable, PagedResourcesAssembler assembler, Locale locale) { @@ -157,6 +160,7 @@ public PagedModel getMyFinished(Pageable pageable, PagedR } @Override + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN')") @Operation(summary = "Generic task search on Mongo database", security = {@SecurityRequirement(name = "BasicAuth")}) @PostMapping(value = "/search", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaTypes.HAL_JSON_VALUE) public PagedModel search(Pageable pageable, @RequestBody SingleTaskSearchRequestAsList searchBody, @RequestParam(defaultValue = "OR") MergeFilterOperation operation, PagedResourcesAssembler assembler, Locale locale) { @@ -164,6 +168,7 @@ public PagedModel search(Pageable pageable, @RequestBody } @Override + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN')") @Operation(summary = "Generic task search on Elasticsearch database", security = {@SecurityRequirement(name = "BasicAuth")}) @PostMapping(value = "/search_es", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaTypes.HAL_JSON_VALUE) public PagedModel searchElastic(Pageable pageable, @RequestBody SingleElasticTaskSearchRequestAsList searchBody, @RequestParam(defaultValue = "OR") MergeFilterOperation operation, PagedResourcesAssembler assembler, Locale locale) { @@ -171,6 +176,7 @@ public PagedModel searchElastic(Pageable pageable, @Reque } @Override + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN')") @Operation(summary = "Count tasks by provided criteria", security = {@SecurityRequirement(name = "BasicAuth")}) @PostMapping(value = "/count", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public CountResponse count(@RequestBody SingleElasticTaskSearchRequestAsList query, @RequestParam(defaultValue = "OR") MergeFilterOperation operation, Locale locale) { @@ -178,17 +184,18 @@ public CountResponse count(@RequestBody SingleElasticTaskSearchRequestAsList que } @Override + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN', 'ANONYMOUS')") @Operation(summary = "Get all task data", security = {@SecurityRequirement(name = "BasicAuth")}) - @GetMapping(value = "/{id}/data", produces = MediaTypes.HAL_JSON_VALUE) + @GetMapping(value = {"/{id}/data", "/public/{id}/data"}, produces = MediaTypes.HAL_JSON_VALUE) public EntityModel getData(@PathVariable("id") String taskId, Locale locale) { return super.getData(taskId, locale); } - @PreAuthorize("@taskAuthorizationService.canCallSaveData(@userService.getLoggedUser(), #taskId)") + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN', 'ANONYMOUS') && @taskAuthorizationService.canCallSaveData(@userService.getLoggedUser(), #taskId)") @Operation(summary = "Set task data", description = "Caller must be assigned to the task, or must be an ADMIN", security = {@SecurityRequirement(name = "BasicAuth")}) - @PostMapping(value = "/{id}/data", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + @PostMapping(value = {"/{id}/data", "/public/{id}/data"}, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), @@ -198,11 +205,11 @@ public EntityModel setData(@PathVariable("id") String t return super.setData(taskId, dataBody, locale); } - @PreAuthorize("@taskAuthorizationService.canCallSaveFile(@userService.getLoggedUser(), #taskId)") + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN', 'ANONYMOUS') && @taskAuthorizationService.canCallSaveFile(@userService.getLoggedUser(), #taskId)") @Operation(summary = "Upload file into the task", description = "Caller must be assigned to the task, or must be an ADMIN", security = {@SecurityRequirement(name = "BasicAuth")}) - @PostMapping(value = "/{id}/file", produces = MediaTypes.HAL_JSON_VALUE) + @PostMapping(value = {"/{id}/file", "/public/{id}/file"}, produces = MediaTypes.HAL_JSON_VALUE) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), @@ -211,17 +218,18 @@ public EntityModel saveFile(@PathVariable("id") String return super.saveFile(taskId, multipartFile, dataBody, locale); } + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN', 'ANONYMOUS')") @Operation(summary = "Download task file field value", security = {@SecurityRequirement(name = "BasicAuth")}) - @GetMapping(value = "/{id}/file", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) + @GetMapping(value = {"/{id}/file", "/public/{id}/file"}, produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) public ResponseEntity getFile(@PathVariable("id") String taskId, @RequestParam("fieldId") String fieldId) throws FileNotFoundException { return super.getFile(taskId, fieldId); } - @PreAuthorize("@taskAuthorizationService.canCallSaveFile(@userService.getLoggedUser(), #taskId)") + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN', 'ANONYMOUS') && @taskAuthorizationService.canCallSaveFile(@userService.getLoggedUser(), #taskId)") @Operation(summary = "Remove file from the task", description = "Caller must be assigned to the task, or must be an ADMIN", security = {@SecurityRequirement(name = "BasicAuth")}) - @DeleteMapping(value = "/{id}/file", produces = MediaTypes.HAL_JSON_VALUE) + @DeleteMapping(value = {"/{id}/file", "/public/{id}/file"}, produces = MediaTypes.HAL_JSON_VALUE) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), @@ -230,11 +238,11 @@ public EntityModel deleteFile(@PathVariable("id") Strin return super.deleteFile(requestBody.getParentTaskId(), requestBody.getFieldId()); } - @PreAuthorize("@taskAuthorizationService.canCallSaveFile(@userService.getLoggedUser(), #taskId)") + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN', 'ANONYMOUS') && @taskAuthorizationService.canCallSaveFile(@userService.getLoggedUser(), #taskId)") @Operation(summary = "Upload multiple files into the task", description = "Caller must be assigned to the task, or must be an ADMIN", security = {@SecurityRequirement(name = "BasicAuth")}) - @PostMapping(value = "/{id}/files", produces = MediaTypes.HAL_JSON_VALUE) + @PostMapping(value = {"/{id}/files", "/public/{id}/files"}, produces = MediaTypes.HAL_JSON_VALUE) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), @@ -243,17 +251,18 @@ public EntityModel saveFiles(@PathVariable("id") String return super.saveFiles(taskId, multipartFiles, requestBody); } + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN', 'ANONYMOUS')") @Operation(summary = "Download one file from tasks file list field value", security = {@SecurityRequirement(name = "BasicAuth")}) - @GetMapping(value = "/{id}/file/named", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) + @GetMapping(value = {"/{id}/file/named", "/public/{id}/file/named"}, produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) public ResponseEntity getNamedFile(@PathVariable("id") String taskId, @RequestParam("fieldId") String fieldId, @RequestParam("fileName") String fileName) throws FileNotFoundException { return super.getNamedFile(taskId, fieldId, fileName); } - @PreAuthorize("@taskAuthorizationService.canCallSaveFile(@userService.getLoggedUser(), #taskId)") + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN', 'ANONYMOUS') && @taskAuthorizationService.canCallSaveFile(@userService.getLoggedUser(), #taskId)") @Operation(summary = "Remove file from tasks file list field value", description = "Caller must be assigned to the task, or must be an ADMIN", security = {@SecurityRequirement(name = "BasicAuth")}) - @DeleteMapping(value = "/{id}/file/named", produces = MediaTypes.HAL_JSON_VALUE) + @DeleteMapping(value = {"/{id}/file/named", "/public/{id}/file/named"}, produces = MediaTypes.HAL_JSON_VALUE) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), @@ -262,8 +271,9 @@ public EntityModel deleteNamedFile(@PathVariable("id") return super.deleteNamedFile(requestBody.getParentTaskId(), requestBody.getFieldId(), requestBody.getFileName()); } + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN', 'ANONYMOUS')") @Operation(summary = "Download preview for file field value", security = {@SecurityRequirement(name = "BasicAuth")}) - @GetMapping(value = "/{id}/file_preview", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) + @GetMapping(value = {"/{id}/file_preview", "/public/{id}/file_preview"}, produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) public ResponseEntity getFilePreview(@PathVariable("id") String taskId, @RequestParam("fieldId") String fieldId) throws FileNotFoundException { return super.getFilePreview(taskId, fieldId); } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/WorkflowController.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/WorkflowController.java index 9cfbc3b5c50..5c23caec8e9 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/WorkflowController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/WorkflowController.java @@ -29,7 +29,6 @@ import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.core.io.InputStreamResource; @@ -47,11 +46,9 @@ import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.*; import java.io.FileNotFoundException; -import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.util.Locale; @@ -81,9 +78,9 @@ public class WorkflowController { private final UserService userService; - @PreAuthorize("@workflowAuthorizationService.canCallCreate(@userService.getLoggedUser(), #body.netId)") + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN', 'ANONYMOUS') && @workflowAuthorizationService.canCallCreate(@userService.getLoggedUser(), #body.netId)") @Operation(summary = "Create new case", security = {@SecurityRequirement(name = "BasicAuth")}) - @PostMapping(value = "/case", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaTypes.HAL_JSON_VALUE) + @PostMapping(value = {"/case", "/public/case"}, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaTypes.HAL_JSON_VALUE) public EntityModel createCase(@RequestBody CreateCaseBody body, Locale locale) { LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedUser()); try { @@ -96,6 +93,7 @@ public EntityModel createCase(@RequestBody CreateCaseBo } } + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN')") @Operation(summary = "Get all cases of the system, paginated", security = {@SecurityRequirement(name = "BasicAuth")}) @GetMapping(value = "/all", produces = MediaTypes.HAL_JSON_VALUE) public PagedModel getAll(Pageable pageable, PagedResourcesAssembler assembler) { @@ -105,6 +103,7 @@ public PagedModel getAll(Pageable pageable, PagedResourcesAssemble return getCaseResources(pageable, assembler, cases, selfLink); } + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN')") @Operation(summary = "Generic case search with QueryDSL predicate, paginated", security = {@SecurityRequirement(name = "BasicAuth")}) @PostMapping(value = "/case/search2", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaTypes.HAL_JSON_VALUE) public PagedModel search2(@QuerydslPredicate(root = Case.class) Predicate predicate, Pageable pageable, PagedResourcesAssembler assembler) { @@ -114,6 +113,7 @@ public PagedModel search2(@QuerydslPredicate(root = Case.class) Pr return getCaseResources(pageable, assembler, cases, selfLink); } + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN')") @Operation(summary = "Generic case search on Elasticsearch database, paginated", security = {@SecurityRequirement(name = "BasicAuth")}) @PostMapping(value = "/case/search", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaTypes.HAL_JSON_VALUE) public PagedModel search(@RequestBody SingleCaseSearchRequestAsList searchBody, @RequestParam(defaultValue = "OR") MergeFilterOperation operation, Pageable pageable, PagedResourcesAssembler assembler, Locale locale) { @@ -128,6 +128,7 @@ public PagedModel search(@RequestBody SingleCaseSearchRequestAsLis return PagedModel.of(cases.stream().map(CaseResource::new).toList(), new PagedModel.PageMetadata(pageable.getPageSize(), pageable.getPageNumber(), cases.getTotalElements())); } + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN')") @Operation(summary = "Generic case search on Mongo database, paginated", security = {@SecurityRequirement(name = "BasicAuth")}) @PostMapping(value = "/case/search_mongo", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaTypes.HAL_JSON_VALUE) public PagedModel searchMongo(@RequestBody Map searchBody, Pageable pageable, PagedResourcesAssembler assembler, Locale locale) { @@ -138,6 +139,7 @@ public PagedModel searchMongo(@RequestBody Map sea } + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN')") @Operation(summary = "Get count of the cases", security = {@SecurityRequirement(name = "BasicAuth")}) @PostMapping(value = "/case/count", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public CountResponse count(@RequestBody SingleCaseSearchRequestAsList searchBody, @RequestParam(defaultValue = "OR") MergeFilterOperation operation, Locale locale) { @@ -145,6 +147,7 @@ public CountResponse count(@RequestBody SingleCaseSearchRequestAsList searchBody return CountResponse.caseCount(count); } + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN')") @Operation(summary = "Get case by id", security = {@SecurityRequirement(name = "BasicAuth")}) @GetMapping(value = "/case/{id}", produces = MediaTypes.HAL_JSON_VALUE) public CaseResource getOne(@PathVariable("id") String caseId) { @@ -154,6 +157,7 @@ public CaseResource getOne(@PathVariable("id") String caseId) { return new CaseResource(aCase); } + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN')") @Operation(summary = "Get all cases by user that created them, paginated", security = {@SecurityRequirement(name = "BasicAuth")}) @RequestMapping(value = "/case/author/{id}", method = RequestMethod.POST, consumes = MediaType.TEXT_PLAIN_VALUE, produces = MediaTypes.HAL_JSON_VALUE) public PagedModel findAllByAuthor(@PathVariable("id") String authorId, @RequestBody String petriNet, PagedResourcesAssembler assembler, Pageable pageable) { @@ -184,7 +188,7 @@ public MessageResource reloadTasks(@PathVariable("id") String caseId) { } } - @PreAuthorize("@workflowAuthorizationService.canCallDelete(@userService.getLoggedUser(), #caseId)") + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN') && @workflowAuthorizationService.canCallDelete(@userService.getLoggedUser(), #caseId)") @Operation(summary = "Delete case", security = {@SecurityRequirement(name = "BasicAuth")}) @DeleteMapping(value = "/case/{id}", produces = MediaTypes.HAL_JSON_VALUE) public EntityModel deleteCase(@PathVariable("id") String caseId, @RequestParam(defaultValue = "false") boolean deleteSubtree) { @@ -199,6 +203,7 @@ public EntityModel deleteCase(@PathVariable("id") Strin LocalisedEventOutcomeFactory.from(outcome, LocaleContextHolder.getLocale())); } + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN')") @Operation(summary = "Download case file field value", security = {@SecurityRequirement(name = "BasicAuth")}) @GetMapping(value = "/case/{id}/file", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) public ResponseEntity getFile(@PathVariable("id") String caseId, @RequestParam("fieldId") String fieldId) throws FileNotFoundException { @@ -217,6 +222,7 @@ public ResponseEntity getFile(@PathVariable("id") String caseId, @Requ .body(new InputStreamResource(fileFieldInputStream.getInputStream())); } + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN')") @Operation(summary = "Download one file from cases file list field value", security = {@SecurityRequirement(name = "BasicAuth")}) @GetMapping(value = "/case/{id}/file/named", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) public ResponseEntity getFileByName(@PathVariable("id") String caseId, @RequestParam("fieldId") String fieldId, @RequestParam("fileName") String name) throws FileNotFoundException { diff --git a/application-engine/src/main/resources/application-dev.yaml b/application-engine/src/main/resources/application-dev.yaml index d4170cbf11a..65456e12f96 100644 --- a/application-engine/src/main/resources/application-dev.yaml +++ b/application-engine/src/main/resources/application-dev.yaml @@ -28,7 +28,7 @@ netgrif: swagger: enabled: true security: - server-patterns: /api/auth/signup,/api/auth/token/verify,/api/auth/reset,/api/auth/recover,/v3/api-docs,/v3/api-docs/**,/swagger-ui.html,/swagger-ui/**,/api/**,/manage/**,/api/users/me,/api/users/preferences + server-patterns: /api/auth/signup,/api/auth/token/verify,/api/auth/reset,/api/auth/recover,/v3/api-docs,/v3/api-docs/**,/swagger-ui.html,/swagger-ui/**,/api/petrinet/public/**,/api/workflow/public/**,/api/task/public/**,/manage/**,/api/users/me,/api/users/preferences management: health: ldap: diff --git a/application-engine/src/main/resources/application.yaml b/application-engine/src/main/resources/application.yaml index 998c0f369bc..7211e116112 100644 --- a/application-engine/src/main/resources/application.yaml +++ b/application-engine/src/main/resources/application.yaml @@ -85,7 +85,7 @@ netgrif: expiration: 900000 algorithm: RSA private-key: classpath:certificates/private.der - server-patterns: /api/auth/signup,/api/auth/token/verify,/api/auth/reset,/api/auth/recover,/v3/api-docs/public,/manage/**,/api/users/me,/api/users/preferences + server-patterns: /api/auth/signup,/api/auth/token/verify,/api/auth/reset,/api/auth/recover,/v3/api-docs/public,/manage/**,/api/users/me,/api/users/preferences,/api/petrinet/public/**,/api/workflow/public/**,/api/task/public/** providers: NetgrifBasicAuthenticationProvider swagger: enabled: false diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/TestHelper.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/TestHelper.groovy index f6921558abe..f87c730a27d 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/TestHelper.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/TestHelper.groovy @@ -97,9 +97,9 @@ class TestHelper { petriNetService.evictAllCaches() defaultRoleRunner.run() + anonymousRoleRunner.run() elasticsearchRunner.run() defaultRealmRunner.run() - anonymousRoleRunner.run() systemUserRunner.run() groupRunner.run() filterRunner.run() diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/configuration/filters/NetgrifOncePerRequestFilter.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/configuration/filters/NetgrifOncePerRequestFilter.java index de3cb326e5a..6d599f641eb 100644 --- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/configuration/filters/NetgrifOncePerRequestFilter.java +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/configuration/filters/NetgrifOncePerRequestFilter.java @@ -30,7 +30,8 @@ protected abstract void doFilterInternal(NetgrifHttpServletRequest request, Http FilterChain filterChain) throws ServletException, IOException; @Override - protected void doFilterInternal(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, + protected void doFilterInternal(@NotNull HttpServletRequest request, + @NotNull HttpServletResponse response, @NotNull FilterChain filterChain) throws ServletException, IOException { if (requestNotMatches(request)) { log.trace("Request did not match the required URIs: {}", this.requestMatcher); From d4abf76c2258b3ddd60031e8c2979a42aa38bc3f Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Mon, 26 Jan 2026 15:52:05 +0100 Subject: [PATCH 10/14] Enhance task search capabilities and add new endpoint path Extended `TaskController` to include a new "/public/case" endpoint path for retrieving tasks by cases. Refactored task search logic to leverage new `TaskSearchRequest` and `TaskSearchCaseRequest` builders, improving flexibility and maintainability. Adjusted `AbstractTaskController` to support the updated search functionality with enhanced parameters. --- .../engine/workflow/web/AbstractTaskController.java | 9 +++++++-- .../application/engine/workflow/web/TaskController.java | 2 +- .../workflow/web/requestbodies/TaskSearchRequest.java | 2 ++ .../requestbodies/taskSearch/TaskSearchCaseRequest.java | 2 ++ 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/AbstractTaskController.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/AbstractTaskController.java index 7a3592ccd85..3405d124a71 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/AbstractTaskController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/AbstractTaskController.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.netgrif.application.engine.adapter.spring.auth.domain.LoggedUserImpl; import com.netgrif.application.engine.auth.service.UserService; +import com.netgrif.application.engine.elastic.web.requestbodies.ElasticTaskSearchRequest; import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; import com.netgrif.application.engine.objects.auth.domain.LoggedUser; import com.netgrif.application.engine.elastic.service.interfaces.IElasticTaskService; @@ -10,6 +11,8 @@ import com.netgrif.application.engine.eventoutcomes.LocalisedEventOutcomeFactory; import com.netgrif.application.engine.objects.petrinet.domain.dataset.FieldType; import com.netgrif.application.engine.objects.petrinet.domain.dataset.localised.LocalisedField; +import com.netgrif.application.engine.workflow.web.requestbodies.TaskSearchRequest; +import com.netgrif.application.engine.workflow.web.requestbodies.taskSearch.TaskSearchCaseRequest; import com.netgrif.application.engine.workflow.web.responsebodies.LocalisedTaskResource; import com.netgrif.application.engine.objects.petrinet.domain.throwable.TransitionNotExecutableException; import com.netgrif.application.engine.workflow.domain.IllegalArgumentWithChangedFieldsException; @@ -66,7 +69,7 @@ public abstract class AbstractTaskController { public PagedModel getAll(Pageable pageable, PagedResourcesAssembler assembler, Locale locale) { LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedUser()); - Page page = taskService.getAll(loggedUser, pageable, locale); + Page page = taskService.search(Collections.emptyList(), pageable, loggedUser, locale, Boolean.FALSE); Link selfLink = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(TaskController.class) .getAll(pageable, assembler, locale)).withRel("all"); PagedModel resources = assembler.toModel(page, new TaskResourceAssembler(locale), selfLink); @@ -75,7 +78,9 @@ public PagedModel getAll(Pageable pageable, PagedResource } public PagedModel getAllByCases(List cases, Pageable pageable, PagedResourcesAssembler assembler, Locale locale) { - Page page = taskService.findByCases(pageable, cases); + List requests = cases.stream().map(c -> TaskSearchRequest.from().useCase(List.of(TaskSearchCaseRequest.builder().id(c).build())).build()).toList(); + LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedUser()); + Page page = taskService.search(requests, pageable, loggedUser, locale, false); Link selfLink = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(TaskController.class) .getAllByCases(cases, pageable, assembler, locale)).withRel("case"); PagedModel resources = assembler.toModel(page, new TaskResourceAssembler(locale), selfLink); diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/TaskController.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/TaskController.java index cdcd3ba233e..15588296562 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/TaskController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/TaskController.java @@ -70,7 +70,7 @@ public PagedModel getAll(Pageable pageable, PagedResource @Override @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN')") @Operation(summary = "Get all tasks by cases", security = {@SecurityRequirement(name = "BasicAuth")}) - @PostMapping(value = "/case", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaTypes.HAL_JSON_VALUE) + @PostMapping(value = {"/case", "/public/case"}, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaTypes.HAL_JSON_VALUE) public PagedModel getAllByCases(@RequestBody List cases, Pageable pageable, PagedResourcesAssembler assembler, Locale locale) { return super.getAllByCases(cases, pageable, assembler, locale); } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/requestbodies/TaskSearchRequest.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/requestbodies/TaskSearchRequest.java index 65f48794c15..62abfe58c5f 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/requestbodies/TaskSearchRequest.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/requestbodies/TaskSearchRequest.java @@ -5,12 +5,14 @@ import com.netgrif.application.engine.workflow.web.requestbodies.taskSearch.PetriNet; import com.netgrif.application.engine.workflow.web.requestbodies.taskSearch.TaskSearchCaseRequest; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.NoArgsConstructor; import java.io.Serializable; import java.util.List; import java.util.Map; +@Builder(builderMethodName = "from") @NoArgsConstructor @AllArgsConstructor public class TaskSearchRequest implements Serializable { diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/requestbodies/taskSearch/TaskSearchCaseRequest.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/requestbodies/taskSearch/TaskSearchCaseRequest.java index c031c48208b..e1e9107f56e 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/requestbodies/taskSearch/TaskSearchCaseRequest.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/requestbodies/taskSearch/TaskSearchCaseRequest.java @@ -1,10 +1,12 @@ package com.netgrif.application.engine.workflow.web.requestbodies.taskSearch; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.NoArgsConstructor; import java.io.Serializable; +@Builder @NoArgsConstructor @AllArgsConstructor public class TaskSearchCaseRequest implements Serializable { From 8d3923f9c7314d5541b41ca44870e00c9d920c12 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Mon, 2 Feb 2026 15:45:25 +0100 Subject: [PATCH 11/14] Add AnonymousUserRefService bean to AuthBeansConfiguration The AnonymousUserRefServiceImpl class was decoupled from the @Service annotation and is now registered as a bean in AuthBeansConfiguration. This change also ensures the bean is only created if a UserFactory bean is missing, improving configurability and flexibility. --- .../engine/auth/config/AuthBeansConfiguration.java | 6 ++++++ .../engine/auth/service/AnonymousUserRefServiceImpl.java | 1 - 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/config/AuthBeansConfiguration.java b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/config/AuthBeansConfiguration.java index df6cd8901a4..8551c72b000 100644 --- a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/config/AuthBeansConfiguration.java +++ b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/config/AuthBeansConfiguration.java @@ -43,4 +43,10 @@ public PreferencesService preferencesService() { public UserFactory userFactory() { return new UserFactoryImpl(); } + + @Bean + @ConditionalOnMissingBean(UserFactory.class) + public AnonymousUserRefService anonymousUserRefService() { + return new AnonymousUserRefServiceImpl(); + } } diff --git a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/AnonymousUserRefServiceImpl.java b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/AnonymousUserRefServiceImpl.java index c50d034deef..06c773719b4 100644 --- a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/AnonymousUserRefServiceImpl.java +++ b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/AnonymousUserRefServiceImpl.java @@ -10,7 +10,6 @@ import java.util.Optional; import java.util.Set; -@Service public class AnonymousUserRefServiceImpl implements AnonymousUserRefService { @Autowired From b6b4550139da7cbb4042008937e082aea974f4f1 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Tue, 3 Feb 2026 12:25:26 +0100 Subject: [PATCH 12/14] [NAE-2241] Anonymous access - Enhanced `AuthorizationService`: - Corrected the `hasAnyAuthority` method to use `equals` instead of `contains`. - Updated pre-authorization checks in the `TaskController` to include the `ANONYMOUS` role. - Improved Anonymous User Support: - Modified `AnonymousUser` and `AnonymousUserRef` to streamline initialization and compatibility with `Serializable`. - Added a new factory method in `DefaultUserFactory` to create `AnonymousUser` instances based on `AnonymousUserRef`. - Revised `ActorTransformer` Factories: - Injected `LoggedUserFactory` and `UserFactory` into `LoggedUserConfiguration` for better dependency management. - Updated `ActorTransformer` to support `LoggedUser` with improved user conversion logic. - Miscellaneous: - Renamed constants and clarified field documentation for better readability and accuracy. --- .../auth/service/AuthorizationService.java | 2 +- .../auth/service/DefaultUserFactory.java | 17 -------- .../LoggedUserConfiguration.java | 11 ++++-- .../engine/workflow/web/TaskController.java | 2 +- .../objects/auth/domain/ActorTransformer.java | 16 +++++++- .../engine/objects/auth/domain/Authority.java | 2 +- .../spring/auth/domain/AnonymousUser.java | 26 ++++++++----- .../spring/auth/domain/AnonymousUserRef.java | 29 +++++++++++++- .../auth/config/AuthBeansConfiguration.java | 2 +- .../service/DefaultLoggedUserFactory.java | 0 .../auth/service/DefaultUserFactory.java | 39 +++++++++++++++++++ 11 files changed, 110 insertions(+), 36 deletions(-) delete mode 100644 application-engine/src/main/java/com/netgrif/application/engine/auth/service/DefaultUserFactory.java rename {application-engine => nae-user-ce}/src/main/java/com/netgrif/application/engine/auth/service/DefaultLoggedUserFactory.java (100%) create mode 100644 nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/DefaultUserFactory.java diff --git a/application-engine/src/main/java/com/netgrif/application/engine/auth/service/AuthorizationService.java b/application-engine/src/main/java/com/netgrif/application/engine/auth/service/AuthorizationService.java index 74d96d90c6e..f1c890b0b1b 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/auth/service/AuthorizationService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/auth/service/AuthorizationService.java @@ -27,6 +27,6 @@ public boolean hasAnyAuthority(String... authority) { // TODO: impersonation // LoggedUser loggedUser = userService.getLoggedUserFromContext().getSelfOrImpersonated(); LoggedUser loggedUser = userService.getLoggedUserFromContext(); - return loggedUser.getAuthoritySet().stream().anyMatch(it -> Arrays.stream(authority).anyMatch(a -> it.getAuthority().contains(a))); + return loggedUser.getAuthoritySet().stream().anyMatch(it -> Arrays.stream(authority).anyMatch(a -> it.getAuthority().equals(a))); } } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/auth/service/DefaultUserFactory.java b/application-engine/src/main/java/com/netgrif/application/engine/auth/service/DefaultUserFactory.java deleted file mode 100644 index 4c83e13f98f..00000000000 --- a/application-engine/src/main/java/com/netgrif/application/engine/auth/service/DefaultUserFactory.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.netgrif.application.engine.auth.service; - -import com.netgrif.application.engine.objects.auth.domain.AbstractUser; -import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; -import com.netgrif.application.engine.objects.auth.domain.User; -import org.springframework.stereotype.Component; - -@Component -public class DefaultUserFactory implements ActorTransformer.UserFactory { - - @Override - public AbstractUser create() { - User user = new User(); - return user; - } -} - diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/LoggedUserConfiguration.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/LoggedUserConfiguration.java index 2d7bb556f54..ecb58db2cfb 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/LoggedUserConfiguration.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/configuration/LoggedUserConfiguration.java @@ -1,17 +1,20 @@ package com.netgrif.application.engine.configuration; -import com.netgrif.application.engine.auth.service.DefaultLoggedUserFactory; -import com.netgrif.application.engine.auth.service.DefaultUserFactory; import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; import jakarta.annotation.PostConstruct; +import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Configuration; @Configuration +@RequiredArgsConstructor public class LoggedUserConfiguration { + private final ActorTransformer.LoggedUserFactory loggedUserFactory; + private final ActorTransformer.UserFactory userFactory; + @PostConstruct public void initializeLoggedUserFactory() { - ActorTransformer.setLoggedUserFactory(new DefaultLoggedUserFactory()); - ActorTransformer.setUserFactory(new DefaultUserFactory()); + ActorTransformer.setLoggedUserFactory(loggedUserFactory); + ActorTransformer.setUserFactory(userFactory); } } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/TaskController.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/TaskController.java index 15588296562..d729a460a2a 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/TaskController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/TaskController.java @@ -68,7 +68,7 @@ public PagedModel getAll(Pageable pageable, PagedResource } @Override - @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN')") + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN', 'ANONYMOUS')") @Operation(summary = "Get all tasks by cases", security = {@SecurityRequirement(name = "BasicAuth")}) @PostMapping(value = {"/case", "/public/case"}, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaTypes.HAL_JSON_VALUE) public PagedModel getAllByCases(@RequestBody List cases, Pageable pageable, PagedResourcesAssembler assembler, Locale locale) { diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/domain/ActorTransformer.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/domain/ActorTransformer.java index 345c4e7f627..ca9018c970b 100644 --- a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/domain/ActorTransformer.java +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/domain/ActorTransformer.java @@ -61,6 +61,20 @@ public interface UserFactory { * @return newly created AbstractUser instance */ AbstractUser create(); + + + /** + * Creates a new AbstractUser instance based on the provided LoggedUser instance. + * By default, this method throws an IllegalStateException, and must be explicitly + * implemented by the concrete UserFactory implementation. + * + * @param loggedUser the LoggedUser containing user details + * @return newly created AbstractUser instance representing the same user + * @throws IllegalStateException if the method is not implemented + */ + default AbstractUser create(LoggedUser loggedUser) { + throw new IllegalStateException("Method is not implemented"); + } } /** @@ -91,7 +105,7 @@ public static LoggedUser toLoggedUser(AbstractUser user) { * @return a new AbstractUser instance with copied user data */ public static AbstractUser toUser(LoggedUser loggedUser) { - User user = (User) userFactory.create(); + AbstractUser user = userFactory.create(loggedUser); user.setId(loggedUser.getStringId()); user.setRealmId(loggedUser.getRealmId()); user.setUsername(loggedUser.getUsername()); diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/domain/Authority.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/domain/Authority.java index 353764085c7..c60f9f9a3ab 100644 --- a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/domain/Authority.java +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/domain/Authority.java @@ -42,7 +42,7 @@ public abstract class Authority implements Serializable { /** * Constant representing the anonymous user role. */ - public static final String anonymous = "ANONYMOUS_USER"; + public static final String anonymous = "ANONYMOUS"; /** * MongoDB ObjectId of the authority. diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/auth/domain/AnonymousUser.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/auth/domain/AnonymousUser.java index d6e915fb03f..7682d7bbb2a 100644 --- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/auth/domain/AnonymousUser.java +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/auth/domain/AnonymousUser.java @@ -29,13 +29,7 @@ public class AnonymousUser extends AbstractUser { */ private final Map> attributes = new HashMap<>(); - /** - * Constructs a new anonymous user with specified reference and authority. - * - * @param ref the anonymous user reference containing basic user information - * @param anonymousAuthority the authority to be assigned if no specific authorities are provided - */ - public AnonymousUser(AnonymousUserRef ref, Authority anonymousAuthority) { + public AnonymousUser(AnonymousUserRef ref) { this.id = new ObjectId(ref.getId()); this.realmId = ref.getRealmId(); this.username = "anonymous@" + this.realmId; @@ -45,14 +39,28 @@ public AnonymousUser(AnonymousUserRef ref, Authority anonymousAuthority) { this.authoritySet = new HashSet<>(); if (ref.getAuthorities() != null && !ref.getAuthorities().isEmpty()) { this.authoritySet.addAll(ref.getAuthorities()); - } else { - this.authoritySet.add(anonymousAuthority); } this.processRoles = ref.getProcessRoles() != null ? new HashSet<>(ref.getProcessRoles()) : new HashSet<>(); this.groupIds = ref.getGroupIds() != null ? new HashSet<>(ref.getGroupIds()) : new HashSet<>(); } + /** + * Constructs a new anonymous user with specified reference and authority. + * + * @param ref the anonymous user reference containing basic user information + * @param anonymousAuthority the authority to be assigned if no specific authorities are provided + */ + public AnonymousUser(AnonymousUserRef ref, Authority anonymousAuthority) { + this(ref); + this.authoritySet = new HashSet<>(); + if (ref.getAuthorities() != null && !ref.getAuthorities().isEmpty()) { + this.authoritySet.addAll(ref.getAuthorities()); + } else { + this.authoritySet.add(anonymousAuthority); + } + } + /** * {@inheritDoc} * @return the username of the anonymous user diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/auth/domain/AnonymousUserRef.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/auth/domain/AnonymousUserRef.java index 65720750a1b..f40d7ce09aa 100644 --- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/auth/domain/AnonymousUserRef.java +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/auth/domain/AnonymousUserRef.java @@ -6,11 +6,14 @@ import com.netgrif.application.engine.objects.petrinet.domain.roles.ProcessRole; import lombok.Data; import org.bson.codecs.pojo.annotations.BsonIgnore; +import org.bson.types.ObjectId; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.index.Indexed; import org.springframework.data.mongodb.core.mapping.Document; import javax.validation.constraints.NotNull; +import java.io.Serial; +import java.io.Serializable; import java.time.Duration; import java.time.LocalDateTime; import java.util.HashSet; @@ -23,7 +26,13 @@ */ @Data @Document(collection = "anonym_user") -public class AnonymousUserRef { +public class AnonymousUserRef implements Serializable { + + /** + * Serial version UID for ensuring compatibility during deserialization. + */ + @Serial + private static final long serialVersionUID = 1239812903890129012L; /** * Unique identifier for the anonymous user. @@ -64,17 +73,30 @@ public class AnonymousUserRef { * Set of process roles assigned to this anonymous user. * @see ProcessRole */ + /** + * Set of process roles associated with this anonymous user. + * These roles determine task permissions and access levels. + * + * @see ProcessRole + */ private Set processRoles = new HashSet<>(); /** * Set of group identifiers this anonymous user belongs to. */ + /** + * Set of unique identifiers representing the groups this user belongs to. + */ private Set groupIds = new HashSet<>(); /** * Duration after which the anonymous user session times out. * Default value is 30 minutes. */ + /** + * Duration representing the session timeout for this anonymous user. + * This field is transient and not persisted in the database. + */ private transient Duration sessionTimeout = Duration.ofMinutes(30); /** @@ -83,6 +105,11 @@ public class AnonymousUserRef { * @see Group */ @BsonIgnore + /** + * Set of groups this anonymous user is associated with. + * This field is ignored during database persistence. + * @see Group + */ private Set groups = new HashSet<>(); /** diff --git a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/config/AuthBeansConfiguration.java b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/config/AuthBeansConfiguration.java index 8551c72b000..b6ffb508300 100644 --- a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/config/AuthBeansConfiguration.java +++ b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/config/AuthBeansConfiguration.java @@ -45,7 +45,7 @@ public UserFactory userFactory() { } @Bean - @ConditionalOnMissingBean(UserFactory.class) + @ConditionalOnMissingBean(AnonymousUserRefService.class) public AnonymousUserRefService anonymousUserRefService() { return new AnonymousUserRefServiceImpl(); } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/auth/service/DefaultLoggedUserFactory.java b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/DefaultLoggedUserFactory.java similarity index 100% rename from application-engine/src/main/java/com/netgrif/application/engine/auth/service/DefaultLoggedUserFactory.java rename to nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/DefaultLoggedUserFactory.java diff --git a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/DefaultUserFactory.java b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/DefaultUserFactory.java new file mode 100644 index 00000000000..22c3d10dbf0 --- /dev/null +++ b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/DefaultUserFactory.java @@ -0,0 +1,39 @@ +package com.netgrif.application.engine.auth.service; + +import com.netgrif.application.engine.adapter.spring.auth.domain.AnonymousUser; +import com.netgrif.application.engine.adapter.spring.auth.domain.AnonymousUserRef; +import com.netgrif.application.engine.objects.auth.domain.AbstractUser; +import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; +import com.netgrif.application.engine.objects.auth.domain.LoggedUser; +import com.netgrif.application.engine.objects.auth.domain.User; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Optional; + +@Component +@RequiredArgsConstructor +public class DefaultUserFactory implements ActorTransformer.UserFactory { + + private final AnonymousUserRefService anonymousUserRefService; + + @Override + public AbstractUser create() { + return new User(); + } + + @Override + public AbstractUser create(LoggedUser loggedUser) { + if (!loggedUser.isAnonymous() || loggedUser.getRealmId() == null) { + return create(); + } + Optional refOpt = anonymousUserRefService.getRef(loggedUser.getRealmId()); + if (refOpt.isEmpty()) { + throw new IllegalStateException("Anonymous user reference not found for realm [%s]".formatted(loggedUser.getRealmId())); + } + return new AnonymousUser(refOpt.get()); + } +} + From b4933a9c6d681dfe16e8240ad2158a03289edd2a Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Tue, 3 Feb 2026 13:29:49 +0100 Subject: [PATCH 13/14] Restrict access and add validation annotations in TaskController. Removed 'ANONYMOUS' authority from task retrieval endpoint to tighten access control. Added `@NotNull` and `@Size` annotations to enforce input validation for improved reliability. --- .../application/engine/workflow/web/TaskController.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/TaskController.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/TaskController.java index d729a460a2a..9427306bd89 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/TaskController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/TaskController.java @@ -20,6 +20,8 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -76,7 +78,7 @@ public PagedModel getAllByCases(@RequestBody List } @Override - @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN', 'ANONYMOUS')") + @PreAuthorize("@authorizationService.hasAnyAuthority('USER', 'ADMIN')") @Operation(summary = "Get tasks of the case", security = {@SecurityRequirement(name = "BasicAuth")}) @GetMapping(value = {"/case/{id}", "/public/case/{id}"}, produces = MediaType.APPLICATION_JSON_VALUE) public List getTasksOfCase(@PathVariable("id") String caseId, Locale locale) { From 1a11c5c977af3e7f932c1afffdc74ed8c3517343 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Tue, 3 Feb 2026 14:10:40 +0100 Subject: [PATCH 14/14] Remove public API controllers Deleted the `PublicPetriNetController`, `PublicTaskController`, `PublicUserController`, and `PublicWorkflowController` classes to streamline the codebase and eliminate unused public endpoints. Also updated test cases to reflect these changes by removing dependencies and ensuring existing functionality remains intact. --- .../engine/auth/web/PublicUserController.java | 144 ------------ .../web/PublicPetriNetController.java | 103 --------- .../workflow/web/PublicTaskController.java | 210 ------------------ .../web/PublicWorkflowController.java | 61 ----- .../web/PetriNetControllerTest.groovy | 4 +- .../engine/workflow/TaskControllerTest.groovy | 17 +- 6 files changed, 16 insertions(+), 523 deletions(-) delete mode 100644 application-engine/src/main/java/com/netgrif/application/engine/auth/web/PublicUserController.java delete mode 100644 application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PublicPetriNetController.java delete mode 100644 application-engine/src/main/java/com/netgrif/application/engine/workflow/web/PublicTaskController.java delete mode 100644 application-engine/src/main/java/com/netgrif/application/engine/workflow/web/PublicWorkflowController.java diff --git a/application-engine/src/main/java/com/netgrif/application/engine/auth/web/PublicUserController.java b/application-engine/src/main/java/com/netgrif/application/engine/auth/web/PublicUserController.java deleted file mode 100644 index d52bd9a6bc5..00000000000 --- a/application-engine/src/main/java/com/netgrif/application/engine/auth/web/PublicUserController.java +++ /dev/null @@ -1,144 +0,0 @@ -package com.netgrif.application.engine.auth.web; - -import com.netgrif.application.engine.auth.service.PreferencesService; -import com.netgrif.application.engine.auth.service.UserFactory; -import com.netgrif.application.engine.auth.service.UserService; -import com.netgrif.application.engine.auth.web.requestbodies.PreferencesRequest; -import com.netgrif.application.engine.auth.web.requestbodies.UserSearchRequestBody; -import com.netgrif.application.engine.auth.web.responsebodies.PreferencesResource; -import com.netgrif.application.engine.auth.web.responsebodies.User; -import com.netgrif.application.engine.objects.auth.domain.AbstractUser; -import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; -import com.netgrif.application.engine.objects.auth.domain.LoggedUser; -import com.netgrif.application.engine.objects.preferences.Preferences; -import com.netgrif.application.engine.objects.workflow.domain.ProcessResourceId; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.responses.ApiResponses; -import io.swagger.v3.oas.annotations.security.SecurityRequirement; -import io.swagger.v3.oas.annotations.tags.Tag; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageImpl; -import org.springframework.data.domain.Pageable; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.web.bind.annotation.*; - -import java.util.List; -import java.util.Locale; - -@Slf4j -@RestController -@ConditionalOnProperty( - value = "netgrif.engine.public.web.user-enabled", - havingValue = "true", - matchIfMissing = true -) -@Tag(name = "Public User Controller") -@RequestMapping("/api/public/users") -public class PublicUserController { - - @Autowired - private UserService userService; - - @Autowired - private PreferencesService preferencesService; - - @Autowired - private UserFactory userFactory; - - @Operation(summary = "Get logged user", description = "Retrieves information of currently logged user") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "User retrieved successfully"), - @ApiResponse(responseCode = "400", description = "Invalid user data"), - @ApiResponse(responseCode = "500", description = "Internal server error") - }) - @GetMapping(value = "/me", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity getLoggedUser(Locale locale) { - LoggedUser loggedUser = (LoggedUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); - AbstractUser user; - try { - user = ActorTransformer.toUser(loggedUser); - if (user == null) { - return ResponseEntity - .status(HttpStatus.UNAUTHORIZED).build(); - } - } catch (IllegalArgumentException e) { - log.error("Could not find user with id [{}]", loggedUser.getId(), e); - return ResponseEntity.status(HttpStatus.BAD_REQUEST).build(); - } - - return ResponseEntity.ok(userFactory.getUser(user, locale)); - } - - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "User retrieved successfully"), - @ApiResponse(responseCode = "400", description = "Invalid user data"), - @ApiResponse(responseCode = "500", description = "Internal server error") - }) - @Operation(summary = "Generic user search", security = {@SecurityRequirement(name = "X-Auth-Token")}) - @PostMapping(value = "/search", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity> search(@RequestBody UserSearchRequestBody query, Pageable pageable) { - List roles = query.getRoles() == null ? null : query.getRoles().stream().map(ProcessResourceId::new).toList(); - List negativeRoles = query.getNegativeRoles() == null ? null : query.getNegativeRoles().stream().map(ProcessResourceId::new).toList(); - Page users = userService.searchAllCoMembers(query.getFulltext(), - roles, - negativeRoles, - (LoggedUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(), pageable); - return ResponseEntity.ok(changeToResponse(users, pageable)); - } - - @Operation(summary = "Get logged user's preferences", security = {@SecurityRequirement(name = "X-Auth-Token")}) - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Returns preferences of logged user"), - @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), - @ApiResponse(responseCode = "500", description = "Internal server error") - }) - @GetMapping(value = "/preferences", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity preferences() { - String userId = ((LoggedUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getStringId(); - Preferences preferences = preferencesService.get(userId); - - if (preferences == null) { - preferences = new com.netgrif.application.engine.adapter.spring.preferences.Preferences(userId); - } - PreferencesResource preferencesResource = PreferencesResource.withPreferences(preferences); - - return ResponseEntity.ok(preferencesResource); - } - - - @Operation(summary = "Set preferences of logged user", security = {@SecurityRequirement(name = "X-Auth-Token")}) - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Saves preferences of logged user"), - @ApiResponse(responseCode = "400", description = "Preferences data are invalid"), - @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), - @ApiResponse(responseCode = "500", description = "Internal server error") - }) - @PostMapping(value = "/preferences", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity savePreferences(@RequestBody PreferencesRequest preferences) { - try { - String userId = ((LoggedUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getStringId(); - preferences.setUserId(userId); - preferencesService.save(preferences.toPreferences()); - return ResponseEntity.ok("User preferences saved"); - } catch (Exception e) { - log.error("Saving user preferences failed", e); - return ResponseEntity.badRequest().body("Saving user preferences failed"); - } - } - - private Page changeToResponse(Page users, Pageable pageable) { - return new PageImpl<>(changeType(users.getContent()), pageable, users.getTotalElements()); - } - - public List changeType(List users) { - return users.stream().map(User::createUser).toList(); - } - -} diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PublicPetriNetController.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PublicPetriNetController.java deleted file mode 100644 index 719689442ea..00000000000 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PublicPetriNetController.java +++ /dev/null @@ -1,103 +0,0 @@ -//package com.netgrif.application.engine.petrinet.web; -// -//import com.netgrif.application.engine.auth.service.UserService; -//import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; -//import com.netgrif.application.engine.objects.petrinet.domain.PetriNet; -//import com.netgrif.application.engine.objects.petrinet.domain.PetriNetSearch; -//import com.netgrif.application.engine.petrinet.domain.version.StringToVersionConverter; -//import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService; -//import com.netgrif.application.engine.adapter.spring.petrinet.service.ProcessRoleService; -//import com.netgrif.application.engine.petrinet.web.responsebodies.*; -//import io.swagger.v3.oas.annotations.Operation; -//import io.swagger.v3.oas.annotations.tags.Tag; -//import lombok.extern.slf4j.Slf4j; -//import org.apache.commons.codec.binary.Base64; -//import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -//import org.springframework.data.domain.Page; -//import org.springframework.data.domain.PageImpl; -//import org.springframework.data.domain.Pageable; -//import org.springframework.data.web.PagedResourcesAssembler; -//import org.springframework.hateoas.MediaTypes; -//import org.springframework.http.MediaType; -//import org.springframework.http.ResponseEntity; -//import org.springframework.web.bind.annotation.*; -// -//import java.util.List; -//import java.util.Locale; -// -//import static com.netgrif.application.engine.petrinet.web.PetriNetController.decodeUrl; -// -//@Slf4j -//@RestController -//@ConditionalOnProperty( -// value = "netgrif.engine.security.web.public-web.petri-net-enabled", -// havingValue = "true", -// matchIfMissing = true -//) -//@Tag(name = "Public PetriNet Controller") -//@RequestMapping({"/api/public/petrinet"}) -//public class PublicPetriNetController { -// -// private final IPetriNetService petriNetService; -// -// private final ProcessRoleService roleService; -// -// private final UserService userService; -// -// private final StringToVersionConverter converter; -// -// public PublicPetriNetController(IPetriNetService petriNetService, UserService userService, StringToVersionConverter converter, ProcessRoleService roleService) { -// this.petriNetService = petriNetService; -// this.converter = converter; -// this.userService = userService; -// this.roleService = roleService; -// } -// -// @GetMapping(value = "/{id}", produces = MediaTypes.HAL_JSON_VALUE) -// @Operation(summary = "Get process by id") -// public PetriNetReferenceResource getOne(@PathVariable("id") String id, Locale locale) { -// return new PetriNetReferenceResource(IPetriNetService.transformToReference(this.petriNetService.getPetriNet(decodeUrl(id)), locale)); -// } -// -// @Operation(summary = "Get process by identifier and version") -// @GetMapping(value = "/{identifier}/{version}", produces = MediaTypes.HAL_JSON_VALUE) -// @ResponseBody -// public PetriNetReferenceResource getOne(@PathVariable("identifier") String identifier, @PathVariable("version") String version, Locale locale) { -// String resolvedIdentifier = Base64.isBase64(identifier) ? new String(Base64.decodeBase64(identifier)) : identifier; -// return new PetriNetReferenceResource(this.petriNetService.getReference(resolvedIdentifier, this.converter.convert(version), ActorTransformer.toLoggedUser(userService.getLoggedUser()), locale)); -// } -// -// @Operation(summary = "Search processes") -// @PostMapping(value = "/search", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaTypes.HAL_JSON_VALUE) -// public ResponseEntity> searchPetriNets(@RequestBody PetriNetSearch criteria, Pageable pageable, PagedResourcesAssembler assembler, Locale locale) { -// Page nets = petriNetService.search(criteria, ActorTransformer.toLoggedUser(userService.getLoggedUser()), pageable, locale); -// return ResponseEntity.ok(new PageImpl<>(nets.stream().map(PetriNetReferenceResource::new).toList(), pageable, nets.getTotalElements())); -// } -// -// @Operation(summary = "Get roles of process") -// @GetMapping(value = "/{netId}/roles", produces = MediaTypes.HAL_JSON_VALUE) -// public ProcessRolesResource getRoles(@PathVariable("netId") String netId, Locale locale) { -// netId = decodeUrl(netId); -// return new ProcessRolesResource(roleService.findAllByNetStringId(netId), petriNetService.getPetriNet(netId).getPermissions(), netId, locale); -// } -// -// @Operation(summary = "Get transactions of process") -// @GetMapping(value = "/{netId}/transactions", produces = MediaTypes.HAL_JSON_VALUE) -// public TransactionsResource getTransactions(@PathVariable("netId") String netId, Locale locale) { -// PetriNet net = petriNetService.getPetriNet(decodeUrl(netId)); -// return new TransactionsResource(net.getTransactions().values(), netId, locale); -// } -// -// @Operation(summary = "Get data fields of transitions") -// @PostMapping(value = "/data", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaTypes.HAL_JSON_VALUE) -// public DataFieldReferencesResource getDataFieldReferences(@RequestBody List referenceBody, Locale locale) { -// return new DataFieldReferencesResource(petriNetService.getDataFieldReferences(referenceBody, locale)); -// } -// -// @Operation(summary = "Get transitions of processes") -// @GetMapping(value = "/transitions", produces = MediaTypes.HAL_JSON_VALUE) -// public TransitionReferencesResource getTransitionReferences(@RequestParam List ids, Locale locale) { -// ids.forEach(PetriNetController::decodeUrl); -// return new TransitionReferencesResource(petriNetService.getTransitionReferences(ids, ActorTransformer.toLoggedUser(userService.getLoggedUser()), locale)); -// } -//} diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/PublicTaskController.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/PublicTaskController.java deleted file mode 100644 index 47770cd2e20..00000000000 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/PublicTaskController.java +++ /dev/null @@ -1,210 +0,0 @@ -//package com.netgrif.application.engine.workflow.web; -// -//import com.fasterxml.jackson.databind.node.ObjectNode; -//import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; -//import com.netgrif.application.engine.objects.auth.domain.LoggedUser; -//import com.netgrif.application.engine.auth.service.UserService; -//import com.netgrif.application.engine.workflow.domain.MergeFilterOperation; -// -//import com.netgrif.application.engine.workflow.domain.eventoutcomes.response.EventOutcomeWithMessage; -//import com.netgrif.application.engine.workflow.service.interfaces.IDataService; -//import com.netgrif.application.engine.workflow.service.interfaces.ITaskService; -//import com.netgrif.application.engine.workflow.service.interfaces.IWorkflowService; -//import com.netgrif.application.engine.workflow.web.requestbodies.file.FileFieldRequest; -//import com.netgrif.application.engine.workflow.web.requestbodies.singleaslist.SingleTaskSearchRequestAsList; -//import com.netgrif.application.engine.workflow.web.responsebodies.LocalisedTaskResource; -//import com.netgrif.application.engine.workflow.web.responsebodies.TaskReference; -//import com.netgrif.application.engine.objects.workflow.domain.Task; -//import io.swagger.v3.oas.annotations.Operation; -//import io.swagger.v3.oas.annotations.responses.ApiResponse; -//import io.swagger.v3.oas.annotations.responses.ApiResponses; -//import io.swagger.v3.oas.annotations.tags.Tag; -//import lombok.extern.slf4j.Slf4j; -//import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -//import org.springframework.core.io.Resource; -//import org.springframework.data.domain.Pageable; -//import org.springframework.data.web.PagedResourcesAssembler; -//import org.springframework.hateoas.EntityModel; -//import org.springframework.hateoas.MediaTypes; -//import org.springframework.hateoas.PagedModel; -//import org.springframework.http.MediaType; -//import org.springframework.http.ResponseEntity; -//import org.springframework.security.access.prepost.PreAuthorize; -//import org.springframework.security.core.Authentication; -//import org.springframework.web.bind.annotation.*; -//import org.springframework.web.multipart.MultipartFile; -// -//import java.io.FileNotFoundException; -//import java.util.List; -//import java.util.Locale; -// -//@Slf4j -//@RestController -//@Tag(name = "Public Task Controller") -//@ConditionalOnProperty( -// value = "netgrif.engine.security.web.public-web.task-enabled", -// havingValue = "true", -// matchIfMissing = true -//) -//@RequestMapping({"/api/public/task"}) -//public class PublicTaskController extends AbstractTaskController { -// -// final UserService userService; -// private final ITaskService taskService; -// -// public PublicTaskController(ITaskService taskService, -// IDataService dataService, -// IWorkflowService workflowService, -// UserService userService) { -// super(taskService, dataService, workflowService, null, userService); -// this.taskService = taskService; -// this.userService = userService; -// } -// -// @Override -// @GetMapping(value = "/case/{id}", produces = "application/json;charset=UTF-8") -// @Operation(summary = "Get tasks of the case") -// public List getTasksOfCase(@PathVariable("id") String caseId, Locale locale) { -// return this.taskService.findAllByCase(caseId, locale); -// } -// -// @PreAuthorize("@taskAuthorizationService.canCallAssign(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") -// @GetMapping(value = "/assign/{id}", produces = MediaTypes.HAL_JSON_VALUE) -// @Operation(summary = "Assign task", description = "Caller must be able to perform the task, or must be an ADMIN") -// @ApiResponses({@ApiResponse( -// responseCode = "200", -// description = "OK" -// ), @ApiResponse( -// responseCode = "403", -// description = "Caller doesn't fulfill the authorisation requirements" -// )}) -// public EntityModel assign(@PathVariable("id") String taskId, Locale locale) { -// return super.assign(taskId, locale); -// } -// -// @PreAuthorize("@taskAuthorizationService.canCallFinish(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") -// @GetMapping(value = "/finish/{id}", produces = MediaTypes.HAL_JSON_VALUE) -// @Operation(summary = "Finish task", description = "Caller must be assigned to the task, or must be an ADMIN") -// @ApiResponses({@ApiResponse( -// responseCode = "200", -// description = "OK" -// ), @ApiResponse( -// responseCode = "403", -// description = "Caller doesn't fulfill the authorisation requirements" -// )}) -// public EntityModel finish(@PathVariable("id") String taskId, Locale locale) { -// return super.finish(taskId, locale); -// } -// -// @PreAuthorize("@taskAuthorizationService.canCallCancel(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") -// @GetMapping(value = "/cancel/{id}", produces = MediaTypes.HAL_JSON_VALUE) -// @Operation(summary = "Cancel task", description = "Caller must be assigned to the task, or must be an ADMIN") -// @ApiResponses({@ApiResponse( -// responseCode = "200", -// description = "OK" -// ), @ApiResponse( -// responseCode = "403", -// description = "Caller doesn't fulfill the authorisation requirements" -// )}) -// public EntityModel cancel(@PathVariable("id") String taskId, Locale locale) { -// return super.cancel(taskId, locale); -// } -// -// @Override -// @GetMapping(value = "/{id}/data", produces = MediaTypes.HAL_JSON_VALUE) -// @Operation(summary = "Get all task data") -// public EntityModel getData(@PathVariable("id") String taskId, Locale locale) { -// return super.getData(taskId, locale); -// } -// -// @Override -// @PreAuthorize("@taskAuthorizationService.canCallSaveData(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") -// @PostMapping(value = "/{id}/data", consumes = "application/json;charset=UTF-8", produces = "application/json;charset=UTF-8") -// @Operation(summary = "Set task data", description = "Caller must be assigned to the task, or must be an ADMIN") -// @ApiResponses({@ApiResponse( -// responseCode = "200", -// description = "OK" -// ), @ApiResponse( -// responseCode = "403", -// description = "Caller doesn't fulfill the authorisation requirements" -// )}) -// public EntityModel setData(@PathVariable("id") String taskId, @RequestBody ObjectNode dataBody, Locale locale) { -// return super.setData(taskId, dataBody, locale); -// } -// -// @PreAuthorize("@taskAuthorizationService.canCallSaveFile(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") -// @Operation(summary = "Upload file into the task", -// description = "Caller must be assigned to the task, or must be an ADMIN") -// @PostMapping(value = "/{id}/file", produces = MediaTypes.HAL_JSON_VALUE) -// @ApiResponses(value = { -// @ApiResponse(responseCode = "200", description = "OK"), -// @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), -// }) -// public EntityModel saveFile(Authentication auth, @PathVariable("id") String taskId, @RequestPart(value = "data") FileFieldRequest dataBody, @RequestPart(value = "file") MultipartFile multipartFile, Locale locale){ -// return super.saveFile(taskId, multipartFile, dataBody, locale); -// } -// -// @Override -// @Operation(summary = "Download task file field value") -// @GetMapping(value = "/{id}/file", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) -// public ResponseEntity getFile(@PathVariable("id") String taskId, @RequestParam("fieldId") String fieldId) throws FileNotFoundException { -// return super.getFile(taskId, fieldId); -// } -// -// @PreAuthorize("@taskAuthorizationService.canCallSaveFile(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") -// @Operation(summary = "Remove file from the task", -// description = "Caller must be assigned to the task, or must be an ADMIN") -// @DeleteMapping(value = "/{id}/file", produces = MediaTypes.HAL_JSON_VALUE) -// @ApiResponses(value = { -// @ApiResponse(responseCode = "200", description = "OK"), -// @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), -// }) -// public EntityModel deleteFile(@PathVariable("id") String taskId, @RequestBody FileFieldRequest requestBody) { -// return super.deleteFile(requestBody.getParentTaskId(), requestBody.getFieldId()); -// } -// -// @Override -// @Operation(summary = "Download preview for file field value") -// @GetMapping(value = "/{id}/file_preview", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) -// public ResponseEntity getFilePreview(@PathVariable("id") String taskId, @RequestParam("fieldId") String fieldId) throws FileNotFoundException { -// return super.getFilePreview(taskId, fieldId); -// } -// -// @Override -// @PreAuthorize("@taskAuthorizationService.canCallSaveFile(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") -// @Operation(summary = "Upload multiple files into the task", -// description = "Caller must be assigned to the task, or must be an ADMIN") -// @PostMapping(value = "/{id}/files", produces = MediaTypes.HAL_JSON_VALUE) -// @ApiResponses(value = { -// @ApiResponse(responseCode = "200", description = "OK"), -// @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), -// }) -// public EntityModel saveFiles(@PathVariable("id") String taskId, @RequestPart(value = "files") MultipartFile[] multipartFiles, @RequestPart(value = "data") FileFieldRequest requestBody) { -// return super.saveFiles(taskId, multipartFiles, requestBody); -// } -// -// @Override -// @Operation(summary = "Download one file from tasks file list field value") -// @GetMapping(value = "/{id}/file/named", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) -// public ResponseEntity getNamedFile(@PathVariable("id") String taskId, @RequestParam("fieldId") String fieldId, @RequestParam("fileName") String fileName) throws FileNotFoundException { -// return super.getNamedFile(taskId, fieldId, fileName); -// } -// -// @PreAuthorize("@taskAuthorizationService.canCallSaveFile(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #taskId)") -// @Operation(summary = "Remove file from tasks file list field value", -// description = "Caller must be assigned to the task, or must be an ADMIN") -// @DeleteMapping(value = "/{id}/file/named", produces = MediaTypes.HAL_JSON_VALUE) -// @ApiResponses(value = { -// @ApiResponse(responseCode = "200", description = "OK"), -// @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), -// }) -// public EntityModel deleteNamedFile(@PathVariable("id") String taskId, @RequestBody FileFieldRequest requestBody) { -// return super.deleteNamedFile(requestBody.getParentTaskId(), requestBody.getFieldId(), requestBody.getFileName()); -// } -// -//// @Operation(summary = "Generic task search on Mongo database") -//// @PostMapping(value = "/search", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaTypes.HAL_JSON_VALUE) -//// public PagedModel search(Pageable pageable, @RequestBody SingleTaskSearchRequestAsList searchBody, @RequestParam(defaultValue = "OR") MergeFilterOperation operation, PagedResourcesAssembler assembler, Locale locale) { -//// return super.searchPublic(ActorTransformer.toLoggedUser(userService.getLoggedUser()), pageable, searchBody, operation, assembler, locale); -//// } -//} diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/PublicWorkflowController.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/PublicWorkflowController.java deleted file mode 100644 index f26ea81c3d7..00000000000 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/PublicWorkflowController.java +++ /dev/null @@ -1,61 +0,0 @@ -//package com.netgrif.application.engine.workflow.web; -// -//import com.netgrif.application.engine.eventoutcomes.LocalisedEventOutcomeFactory; -//import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; -//import com.netgrif.application.engine.objects.auth.domain.LoggedUser; -//import com.netgrif.application.engine.auth.service.UserService; -//import com.netgrif.application.engine.objects.workflow.domain.eventoutcomes.caseoutcomes.CreateCaseEventOutcome; -//import com.netgrif.application.engine.workflow.domain.eventoutcomes.response.EventOutcomeWithMessage; -//import com.netgrif.application.engine.workflow.domain.eventoutcomes.response.EventOutcomeWithMessageResource; -//import com.netgrif.application.engine.workflow.service.interfaces.IWorkflowService; -//import com.netgrif.application.engine.workflow.web.requestbodies.CreateCaseBody; -//import io.swagger.v3.oas.annotations.Operation; -//import io.swagger.v3.oas.annotations.tags.Tag; -//import lombok.extern.slf4j.Slf4j; -//import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -//import org.springframework.hateoas.EntityModel; -//import org.springframework.hateoas.MediaTypes; -//import org.springframework.security.access.prepost.PreAuthorize; -//import org.springframework.web.bind.annotation.PostMapping; -//import org.springframework.web.bind.annotation.RequestBody; -//import org.springframework.web.bind.annotation.RequestMapping; -//import org.springframework.web.bind.annotation.RestController; -// -// -//import java.util.Locale; -// -//@Slf4j -//@RestController -//@ConditionalOnProperty( -// value = "netgrif.engine.security.web.public-web.case-enabled", -// havingValue = "true", -// matchIfMissing = true -//) -//@RequestMapping({"/api/public"}) -//@Tag(name = "Public Workflow Controller") -//public class PublicWorkflowController { -// -// private final IWorkflowService workflowService; -// -// private final UserService userService; -// -// public PublicWorkflowController(IWorkflowService workflowService, UserService userService) { -// this.userService = userService; -// this.workflowService = workflowService; -// } -// -// @PreAuthorize("@workflowAuthorizationService.canCallCreate(T(com.netgrif.application.engine.objects.auth.domain.ActorTransformer).toLoggedUser(@userService.getLoggedUser()), #body.netId)") -// @PostMapping(value = "/case", consumes = "application/json;charset=UTF-8", produces = MediaTypes.HAL_JSON_VALUE) -// @Operation(summary = "Create new case") -// public EntityModel createCase(@RequestBody CreateCaseBody body, Locale locale) { -// LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedUser()); -// try { -// CreateCaseEventOutcome outcome = this.workflowService.createCase(body.netId, body.title, body.color, loggedUser, locale); -// return EventOutcomeWithMessageResource.successMessage("Case created successfully", -// LocalisedEventOutcomeFactory.from(outcome, locale)); -// } catch (Exception e) { -// log.error("Creating case failed:" + e.getMessage(), e); -// return EventOutcomeWithMessageResource.errorMessage("Creating case failed: " + e.getMessage()); -// } -// } -//} diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/web/PetriNetControllerTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/web/PetriNetControllerTest.groovy index d8e270522ab..2bb207d7842 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/web/PetriNetControllerTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/web/PetriNetControllerTest.groovy @@ -94,7 +94,7 @@ class PetriNetControllerTest { def auths = importHelper.createAuthorities(["user": Authority.user, "admin": Authority.admin]) - def simpleUser = importHelper.createUser(new User(firstName: "Role", lastName: "User", email: USER_EMAIL, password: "password", state: UserState.ACTIVE), + def simpleUser = importHelper.createUser(new User(username: "simple", firstName: "Role", lastName: "User", email: USER_EMAIL, password: "password", state: UserState.ACTIVE), [auths.get("user")] as Authority[], // [] as Group[], [] as ProcessRole[]) @@ -102,7 +102,7 @@ class PetriNetControllerTest { userAuth = new UsernamePasswordAuthenticationToken(ActorTransformer.toLoggedUser(simpleUser), "password", [auths.get("user")] as List) userAuth.setDetails(new WebAuthenticationDetails(new MockHttpServletRequest())) - def adminUser = importHelper.createUser(new User(firstName: "Admin", lastName: "User", email: ADMIN_EMAIL, password: "password", state: UserState.ACTIVE), + def adminUser = importHelper.createUser(new User(username: "admin", firstName: "Admin", lastName: "User", email: ADMIN_EMAIL, password: "password", state: UserState.ACTIVE), [auths.get("admin")] as Authority[], // [] as Group[], [] as ProcessRole[]) diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/workflow/TaskControllerTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/workflow/TaskControllerTest.groovy index 8fd652ee551..85dd207f6e9 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/workflow/TaskControllerTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/workflow/TaskControllerTest.groovy @@ -3,9 +3,11 @@ package com.netgrif.application.engine.workflow import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.node.ObjectNode import com.netgrif.application.engine.TestHelper +import com.netgrif.application.engine.adapter.spring.auth.domain.AuthorityImpl import com.netgrif.application.engine.auth.service.AuthorityService import com.netgrif.application.engine.auth.service.UserService import com.netgrif.application.engine.elastic.service.interfaces.IElasticTaskService +import com.netgrif.application.engine.objects.auth.domain.AbstractUser import com.netgrif.application.engine.objects.auth.domain.ActorTransformer import com.netgrif.application.engine.objects.auth.domain.Authority import com.netgrif.application.engine.objects.auth.domain.User @@ -19,6 +21,7 @@ import com.netgrif.application.engine.objects.workflow.domain.DataField import com.netgrif.application.engine.objects.workflow.domain.Task import com.netgrif.application.engine.petrinet.service.ProcessRoleService import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService +import com.netgrif.application.engine.security.service.SecurityContextService import com.netgrif.application.engine.startup.ImportHelper import com.netgrif.application.engine.startup.runner.SuperCreatorRunner import com.netgrif.application.engine.utils.FullPageRequest @@ -37,6 +40,8 @@ import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.springframework.data.domain.Page import org.springframework.mock.web.MockMultipartFile +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken +import org.springframework.security.core.context.SecurityContextHolder import org.springframework.test.context.ActiveProfiles import org.springframework.test.context.junit.jupiter.SpringExtension @@ -102,15 +107,21 @@ class TaskControllerTest { @BeforeEach void init() { testHelper.truncateDbs() - userService.saveUser(new User( + def user = new User( firstName: "Dummy", lastName: "Netgrif", username: DUMMY_USER_MAIL, email: DUMMY_USER_MAIL, password: "superAdminPassword", state: UserState.ACTIVE, - authoritySet: [authorityService.getOrCreate(Authority.user)] as Set, - processRoles: [] as Set), null) + authoritySet: [authorityService.getOrCreate(Authority.user), authorityService.getOrCreate(Authority.admin)] as Set, + processRoles: [] as Set) + + user = userService.saveUser(user) + + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(ActorTransformer.toLoggedUser(user), ActorTransformer.toLoggedUser(user).getPassword(), ActorTransformer.toLoggedUser(user).getAuthoritySet() as Set); + SecurityContextHolder.getContext().setAuthentication(token) + importNets() }