diff --git a/application-engine/pom.xml b/application-engine/pom.xml index 59fbb902e4e..90d560be4f4 100644 --- a/application-engine/pom.xml +++ b/application-engine/pom.xml @@ -361,6 +361,12 @@ spring-boot-starter-data-elasticsearch + + + org.springframework.boot + spring-boot-starter-validation + + @@ -483,12 +489,6 @@ guava 33.5.0-jre - - - - org.springframework.boot - spring-boot-starter-validation - tools.jackson.core jackson-core diff --git a/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy index 9f36d82df2a..5cf8af4b75c 100644 --- a/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy +++ b/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy @@ -36,6 +36,7 @@ import com.netgrif.application.engine.menu.services.interfaces.DashboardManageme import com.netgrif.application.engine.menu.services.interfaces.IMenuItemService import com.netgrif.application.engine.objects.auth.domain.Group import com.netgrif.application.engine.objects.auth.domain.LoggedUser +import com.netgrif.application.engine.objects.auth.domain.QGroup import com.netgrif.application.engine.objects.petrinet.domain.I18nString import com.netgrif.application.engine.objects.petrinet.domain.PetriNet import com.netgrif.application.engine.objects.petrinet.domain.Transition @@ -101,6 +102,7 @@ import org.springframework.data.domain.Page import org.springframework.data.domain.PageRequest import org.springframework.data.domain.Pageable import com.netgrif.application.engine.objects.utils.Nullable +import org.springframework.data.util.Pair import java.time.ZoneId import java.util.stream.Collectors @@ -3055,4 +3057,61 @@ class ActionDelegate extends DelegateExpando { IStorageService storageService = storageResolverService.resolve(storageField.storageType) return storageService.getPath(aCase.stringId, fileFieldId, fileName) } + + Group findGroupByIdentifier(String identifier) { + return groupService.findByIdentifier(identifier).orElse(null) + } + + Group findGroupById(String groupId) { + return groupService.findById(groupId) + } + + Page findGroups(Closure predicate = {it.identifier.isNotNull()}, Pageable pageable = Pageable.unpaged()) { + QGroup qGroup = new QGroup("group") + return groupService.findByPredicate(predicate(qGroup), pageable) + } + + Group createGroup(String identifier, String title = "", AbstractUser owner = userService.getLoggedOrSystem()) { + return groupService.create(identifier, title, owner) + } + + void deleteGroup(Group group) { + groupService.delete(group) + } + + Group saveGroup(Group group) { + return groupService.save(group) + } + + Group addUserToGroup(String groupId, String userId, String realmId) { + return groupService.addUser(groupId, userId, realmId) + } + + Group removeUserFromGroup(String groupId, String userId, String realmId) { + return groupService.removeUser(groupId, userId, realmId) + } + + Group addAuthorityToGroup(String groupId, String authorityId) { + return groupService.addAuthority(groupId, authorityId) + } + + Group removeAuthorityFromGroup(String groupId, String authorityId) { + return groupService.removeAuthority(groupId, authorityId) + } + + Group addRoleToGroup(String groupId, String roleId) { + return groupService.addRole(groupId, roleId) + } + + Group removeRoleFromGroup(String groupId, String roleId) { + return groupService.removeRole(groupId, roleId) + } + + Pair addSubGroup(String groupId, String subGroupId) { + return groupService.addSubgroup(groupId, subGroupId) + } + + Pair removeSubGroup(String groupId, String subGroupId) { + return groupService.removeSubgroup(groupId, subGroupId) + } } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/auth/service/RegistrationService.java b/application-engine/src/main/java/com/netgrif/application/engine/auth/service/RegistrationService.java index 4ae6841c4ca..286735383a7 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/auth/service/RegistrationService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/auth/service/RegistrationService.java @@ -150,7 +150,7 @@ public AbstractUser createNewUser(NewUserRequest newUser) { if (newUser.groups != null && !newUser.groups.isEmpty()) { for (String group : newUser.groups) { - groupService.addUser(user, group); + groupService.addUser(group, user); } } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/auth/service/UserResourceHelperService.java b/application-engine/src/main/java/com/netgrif/application/engine/auth/service/UserResourceHelperService.java index de08201760a..884a448c1ad 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/auth/service/UserResourceHelperService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/auth/service/UserResourceHelperService.java @@ -1,9 +1,9 @@ package com.netgrif.application.engine.auth.service; +import com.netgrif.application.engine.auth.web.responsebodies.UserDto; import com.netgrif.application.engine.objects.auth.domain.AbstractUser; import com.netgrif.application.engine.objects.auth.domain.LoggedUser; import com.netgrif.application.engine.auth.service.interfaces.IUserResourceHelperService; -import com.netgrif.application.engine.auth.web.responsebodies.User; import com.netgrif.application.engine.auth.web.responsebodies.UserResource; import com.netgrif.application.engine.impersonation.service.interfaces.IImpersonationService; import lombok.extern.slf4j.Slf4j; @@ -32,20 +32,20 @@ public UserResource getResource(LoggedUser loggedUser, Locale locale, boolean sm // User result = loggedUser.isImpersonating() ? // getLocalisedUser(user, getImpersonated(loggedUser, small), locale) : // getLocalisedUser(user, locale); - User result = getLocalisedUser(user, locale); + UserDto result = getLocalisedUser(user, locale); return new UserResource(result, "profile"); } @Override - public User getLocalisedUser(AbstractUser user, AbstractUser impersonated, Locale locale) { - User localisedUser = getLocalisedUser(user, locale); - User impersonatedUser = userFactory.getUser(impersonated, locale); + public UserDto getLocalisedUser(AbstractUser user, AbstractUser impersonated, Locale locale) { + UserDto localisedUser = getLocalisedUser(user, locale); + UserDto impersonatedUser = userFactory.getUser(impersonated, locale); localisedUser.setImpersonated(impersonatedUser); return localisedUser; } @Override - public User getLocalisedUser(AbstractUser user, Locale locale) { + public UserDto getLocalisedUser(AbstractUser user, Locale locale) { return userFactory.getUser(user, locale); } 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 deleted file mode 100644 index 81feafc98d7..00000000000 --- a/application-engine/src/main/java/com/netgrif/application/engine/auth/service/interfaces/IAuthorizationService.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.netgrif.application.engine.auth.service.interfaces; - -public interface IAuthorizationService { - boolean hasAuthority(String authority); -} diff --git a/application-engine/src/main/java/com/netgrif/application/engine/auth/service/interfaces/IUserResourceHelperService.java b/application-engine/src/main/java/com/netgrif/application/engine/auth/service/interfaces/IUserResourceHelperService.java index f82c5efe437..2ace8f75e66 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/auth/service/interfaces/IUserResourceHelperService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/auth/service/interfaces/IUserResourceHelperService.java @@ -1,6 +1,6 @@ package com.netgrif.application.engine.auth.service.interfaces; -import com.netgrif.application.engine.auth.web.responsebodies.User; +import com.netgrif.application.engine.auth.web.responsebodies.UserDto; import com.netgrif.application.engine.auth.web.responsebodies.UserResource; import com.netgrif.application.engine.objects.auth.domain.AbstractUser; import com.netgrif.application.engine.objects.auth.domain.LoggedUser; @@ -10,7 +10,7 @@ public interface IUserResourceHelperService { UserResource getResource(LoggedUser loggedUser, Locale locale, boolean small); - User getLocalisedUser(AbstractUser user, AbstractUser impersonated, Locale locale); + UserDto getLocalisedUser(AbstractUser user, AbstractUser impersonated, Locale locale); - User getLocalisedUser(AbstractUser user, Locale locale); + UserDto getLocalisedUser(AbstractUser user, Locale locale); } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/auth/web/GroupController.java b/application-engine/src/main/java/com/netgrif/application/engine/auth/web/GroupController.java new file mode 100644 index 00000000000..330c47835c4 --- /dev/null +++ b/application-engine/src/main/java/com/netgrif/application/engine/auth/web/GroupController.java @@ -0,0 +1,461 @@ +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; +import com.netgrif.application.engine.auth.service.GroupService; +import com.netgrif.application.engine.auth.service.RealmService; +import com.netgrif.application.engine.auth.service.UserFactory; +import com.netgrif.application.engine.auth.service.UserService; +import com.netgrif.application.engine.auth.web.responsebodies.UserDto; +import com.netgrif.application.engine.objects.auth.domain.Group; +import com.netgrif.application.engine.objects.auth.domain.AbstractUser; +import com.netgrif.application.engine.objects.auth.domain.Realm; +import com.netgrif.application.engine.objects.dto.request.group.CreateGroupRequestDto; +import com.netgrif.application.engine.objects.dto.request.group.GroupSearchRequestDto; +import com.netgrif.application.engine.objects.dto.request.group.UpdateGroupRequestDto; +import com.netgrif.application.engine.objects.dto.response.group.GroupDto; +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 jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +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.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.*; +import java.util.stream.Collectors; + +@Slf4j +@RestController +@RequestMapping("api/groups") +@ConditionalOnProperty( + value = "netgrif.engine.group.web.enabled", + havingValue = "true", + matchIfMissing = true +) +@Tag(name = "Group") +@RequiredArgsConstructor +public class GroupController { + + private final GroupService groupService; + private final UserService userService; + private final ProcessRoleService processRoleService; + private final RealmService realmService; + private final UserFactory userFactory; + + @PreAuthorize("@authorizationServiceImpl.hasAuthority('ADMIN')") + @Operation(summary = "Get page of groups from defined realm", + description = "Caller must have the ADMIN role", + security = {@SecurityRequirement(name = "X-Auth-Token")}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Retrieve page of groups from defined realm"), + @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), + @ApiResponse(responseCode = "500", description = "Internal server error") + }) + @GetMapping(path = "/{realmId}/all", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity> getAllGroupsOfRealm(@PathVariable("realmId") String realmId, Pageable pageable, Locale locale) { + Page groups = groupService.findAllFromRealm(realmId, pageable); + return ResponseEntity.ok(transformPageContent(groups.getContent(), pageable, locale)); + } + + @PreAuthorize("@authorizationServiceImpl.hasAuthority('ADMIN')") + @Operation(summary = "Create new group", + description = "Caller must have the ADMIN role", + security = {@SecurityRequirement(name = "X-Auth-Token")}) + @ApiResponses(value = { + @ApiResponse(responseCode = "201", description = "New group created successfully"), + @ApiResponse(responseCode = "400", description = "Request data invalid"), + @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), + @ApiResponse(responseCode = "500", description = "Internal server error") + }) + @PostMapping(produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity createGroup(@Valid @RequestBody CreateGroupRequestDto request) { + if (request == null) { + return ResponseEntity.badRequest().build(); + } + if (!realmExists(request.realmId())) { + String message = "Cannot create group, realm with id [" + request.realmId() + "] does not exist"; + log.error(message); + return ResponseEntity.badRequest().build(); + } + AbstractUser user = userService.findById(request.ownerId(), null); + if (user == null) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ResponseMessage.createErrorMessage("User with id [%s] not found".formatted(request.ownerId()))); + } + try { + groupService.create(request.identifier(), request.displayName(), user); + return ResponseEntity.ok(ResponseMessage.createSuccessMessage("Group created successfully")); + } catch (IllegalArgumentException e) { + return ResponseEntity.badRequest().body(ResponseMessage.createErrorMessage(e.getMessage())); + } catch (Exception e) { + return ResponseEntity.internalServerError().body(ResponseMessage.createErrorMessage("Failed to create group with identifier [%s]".formatted(request.identifier()))); + } + } + + @PreAuthorize("@authorizationServiceImpl.hasAuthority('ADMIN')") + @Operation(summary = "Delete group defined by id", + description = "Caller must have the ADMIN role", + security = {@SecurityRequirement(name = "X-Auth-Token")}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "New group deleted successfully"), + @ApiResponse(responseCode = "400", description = "Request data invalid"), + @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), + @ApiResponse(responseCode = "500", description = "Internal server error") + }) + @DeleteMapping("/{id}") + public ResponseEntity deleteGroup(@PathVariable("id") String groupId) { + try { + Group group = groupService.findById(groupId); + groupService.delete(group); + return ResponseEntity.ok("Group with id [" + groupId + "] deleted successfully"); + } catch (IllegalArgumentException e) { + String message = "Failed to delete group with id [" + groupId + "]"; + log.error(message, e); + return ResponseEntity.badRequest().body(message); + } + } + + @PreAuthorize("@authorizationServiceImpl.hasAuthority('ADMIN')") + @Operation(summary = "Get group by id", + description = "Caller must have the ADMIN role", + security = {@SecurityRequirement(name = "X-Auth-Token")}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Group retrieved successfully"), + @ApiResponse(responseCode = "400", description = "Group with given id does not exist"), + @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), + @ApiResponse(responseCode = "500", description = "Internal server error") + }) + @GetMapping("/{id}") + public ResponseEntity getGroup(@PathVariable("id") String groupId, Locale locale) { + try { + Group group = groupService.findById(groupId); + return ResponseEntity.ok(GroupDto.fromGroup(group, locale)); + } catch (IllegalArgumentException e) { + log.error("Cannot get group with id [{}]", groupId, e); + return ResponseEntity.badRequest().build(); + } + } + + @PreAuthorize("@authorizationServiceImpl.hasAuthority('ADMIN')") + @Operation(summary = "Get paged of group members", + description = "Caller must have the ADMIN role", + security = {@SecurityRequirement(name = "X-Auth-Token")}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Group members retrieved successfully"), + @ApiResponse(responseCode = "400", description = "Group with given id does not exist"), + @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), + @ApiResponse(responseCode = "500", description = "Internal server error") + }) + @GetMapping("/{id}/users") + public ResponseEntity> getMembersOfGroup(@PathVariable("id") String groupId, Pageable pageable, Locale locale) { + try { + Group group = groupService.findById(groupId); + Page groupMembers = userService.findAllByIds(group.getMemberIds(), group.getRealmId(), pageable).map(u -> userFactory.getUser(u, locale)); + return ResponseEntity.ok(groupMembers); + } catch (IllegalArgumentException e) { + log.error("Cannot get members of group with id [{}]", groupId, e); + return ResponseEntity.badRequest().build(); + } + } + + @PreAuthorize("@authorizationServiceImpl.hasAuthority('ADMIN')") + @Operation(summary = "Assigns multiple users to group", + description = "Caller must have the ADMIN role", + security = {@SecurityRequirement(name = "X-Auth-Token")}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Users assigned to group successfully"), + @ApiResponse(responseCode = "400", description = "Group with given id does not exist"), + @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), + @ApiResponse(responseCode = "500", description = "Internal server error") + }) + @PatchMapping("/{id}/users/assign") + public ResponseEntity assignUsersToGroup(@PathVariable("id") String groupId, @RequestBody Set userIds) { + try { + groupService.assignUsersToGroup(groupId, userIds); + return ResponseEntity.ok(ResponseMessage.createSuccessMessage("Selected users assigned to group " + groupId)); + } catch (IllegalArgumentException e) { + String message = "Failed to assign members to group [%s]".formatted(groupId); + log.error(message, e); + return ResponseEntity.badRequest().body(ResponseMessage.createErrorMessage("Assigning members to group " + groupId + " has failed!")); + } + } + + @PreAuthorize("@authorizationServiceImpl.hasAuthority('ADMIN')") + @Operation(summary = "Add user to group", + description = "Caller must have the ADMIN role", + security = {@SecurityRequirement(name = "X-Auth-Token")}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "User added to group successfully"), + @ApiResponse(responseCode = "400", description = "Group with given id does not exist"), + @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), + @ApiResponse(responseCode = "500", description = "Internal server error") + }) + @PatchMapping("/{id}/users/add/{userId}") + public ResponseEntity addUserToGroup(@PathVariable("id") String groupId, @PathVariable("userId") String userId) { + try { + Group group = groupService.findById(groupId); + AbstractUser user = userService.findById(userId, group.getRealmId()); + groupService.addUser(group, user); + return ResponseEntity.ok("Added user [" + userId + "] to group [" + groupId + "]"); + } catch (IllegalArgumentException e) { + String message = "Failed to add member [%s] to group [%s]".formatted(userId, groupId); + log.error(message, e); + return ResponseEntity.badRequest().body(message); + } + } + + @PreAuthorize("@authorizationServiceImpl.hasAuthority('ADMIN')") + @Operation(summary = "Remove user from group", + description = "Caller must have the ADMIN role", + security = {@SecurityRequirement(name = "X-Auth-Token")}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "User removed from group successfully"), + @ApiResponse(responseCode = "400", description = "Group or user with given id does not exist"), + @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), + @ApiResponse(responseCode = "500", description = "Internal server error") + }) + @PatchMapping("/{id}/users/remove/{userId}") + public ResponseEntity removeUserFromGroup(@PathVariable("id") String groupId, @PathVariable("userId") String userId) { + try { + Group group = groupService.findById(groupId); + AbstractUser user = userService.findById(userId, group.getRealmId()); + groupService.removeUser(group, user); + return ResponseEntity.ok("User [" + userId + "] removed from group [" + groupId + "]"); + } catch (IllegalArgumentException e) { + String message = "Failed to remove member [%s] from group [%s]".formatted(userId, groupId); + log.error(message, e); + return ResponseEntity.badRequest().body("Failed to remove member from group: " + e.getMessage()); + } + } + + @PreAuthorize("@authorizationServiceImpl.hasAuthority('ADMIN')") + @Operation(summary = "Assign roles to the group", description = "Assigns roles based on request body to group based on roleIds", security = {@SecurityRequirement(name = "X-Auth-Token")}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Selected roles assigned successfully"), + @ApiResponse(responseCode = "400", description = "Requested roles or group with defined id does not exist"), + @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), + @ApiResponse(responseCode = "500", description = "Internal server error") + }) + @PatchMapping(value = "/{id}/roles/assign", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity assignRolesToGroup(@PathVariable("id") String groupId, @RequestBody Set roleIds) { + try { + Group group = groupService.findById(groupId); + processRoleService.assignRolesToGroup(group, roleIds.stream().map(ProcessResourceId::new).collect(Collectors.toSet())); + log.info("Process roles {} assigned to group with id [{}]", roleIds, groupId); + return ResponseEntity.ok(ResponseMessage.createSuccessMessage("Selected roles assigned to group " + groupId)); + } catch (IllegalArgumentException e) { + String message = "Assigning roles to group [" + groupId + "] has failed!"; + log.error(message, e); + return ResponseEntity.badRequest().body(ResponseMessage.createErrorMessage("Assigning roles to group " + groupId + " has failed!")); + } + } + + @PreAuthorize("@authorizationServiceImpl.hasAuthority('ADMIN')") + @Operation(summary = "Adds roles to the group", description = "Adds roles based on request body to group based on roleIds", security = {@SecurityRequirement(name = "X-Auth-Token")}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Selected roles added successfully"), + @ApiResponse(responseCode = "400", description = "Requested roles or group with defined id does not exist"), + @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), + @ApiResponse(responseCode = "500", description = "Internal server error") + }) + @PatchMapping(value = "/{id}/roles/add", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity addRolesToGroup(@PathVariable("id") String groupId, @RequestBody Set roleIds) { + try { + roleIds.forEach(roleId -> groupService.addRole(groupId, roleId)); + log.info("Process roles {} added to group with id [{}]", roleIds, groupId); + return ResponseEntity.ok(ResponseMessage.createSuccessMessage("Selected roles added to group " + groupId)); + } catch (IllegalArgumentException e) { + String message = "Adding roles to group [" + groupId + "] has failed!"; + log.error(message, e); + return ResponseEntity.badRequest().body(ResponseMessage.createErrorMessage("Adding roles to group " + groupId + " has failed!")); + } + } + + @PreAuthorize("@authorizationServiceImpl.hasAuthority('ADMIN')") + @Operation(summary = "Revokes roles to the group", description = "Revokes roles based on request body from group based on id", security = {@SecurityRequirement(name = "X-Auth-Token")}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Selected roles revoked successfully"), + @ApiResponse(responseCode = "400", description = "Requested roles or group with defined id does not exist"), + @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), + @ApiResponse(responseCode = "500", description = "Internal server error") + }) + @PatchMapping(value = "/{id}/roles/revoke", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity revokeRolesFromGroup(@PathVariable("id") String groupId, @RequestBody Set roleIds) { + try { + roleIds.forEach(roleId -> groupService.removeRole(groupId, roleId)); + log.info("Process roles {} revoked from group with id [{}]", roleIds, groupId); + return ResponseEntity.ok(ResponseMessage.createSuccessMessage("Selected roles revoked from group " + groupId)); + } catch (IllegalArgumentException e) { + String message = "Revoking roles from group [" + groupId + "] has failed!"; + log.error(message, e); + return ResponseEntity.badRequest().body(ResponseMessage.createErrorMessage("Revoking roles from group " + groupId + " has failed!")); + } + } + + @PreAuthorize("@authorizationServiceImpl.hasAuthority('ADMIN')") + @Operation(summary = "Adds authority to the group", description = "Adds authority based on request body to group based on id", security = {@SecurityRequirement(name = "X-Auth-Token")}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Selected authorities added successfully"), + @ApiResponse(responseCode = "400", description = "Requested authorities or group with defined id does not exist"), + @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), + @ApiResponse(responseCode = "500", description = "Internal server error") + }) + @PatchMapping(value = "/{id}/authorities/add", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity addAuthorityToGroup(@PathVariable("id") String groupId, @RequestBody Set authorityIds) { + try { + authorityIds.forEach(authorityId -> groupService.addAuthority(groupId, authorityId)); + log.info("Authorities {} added to group with id [{}]", authorityIds, groupId); + return ResponseEntity.ok(ResponseMessage.createSuccessMessage("Selected authorities added to group " + groupId)); + } catch (IllegalArgumentException e) { + String message = "Adding authorities to group [" + groupId + "] has failed!"; + log.error(message, e); + return ResponseEntity.badRequest().body(ResponseMessage.createErrorMessage("Adding authorities to group " + groupId + " has failed!")); + } + } + + @PreAuthorize("@authorizationServiceImpl.hasAuthority('ADMIN')") + @Operation(summary = "Revokes authority from the group", description = "Revokes authority based on request body from group based on roleIds", security = {@SecurityRequirement(name = "X-Auth-Token")}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Selected authorities revoked successfully"), + @ApiResponse(responseCode = "400", description = "Requested authorities or group with defined id does not exist"), + @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), + @ApiResponse(responseCode = "500", description = "Internal server error") + }) + @PatchMapping(value = "/{id}/authorities/revoke", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity revokeAuthorityFromGroup(@PathVariable("id") String groupId, @RequestBody Set authorityIds) { + try { + authorityIds.forEach(authorityId -> groupService.removeAuthority(groupId, authorityId)); + log.info("Authorities {} revoked from group with id [{}]", authorityIds, groupId); + return ResponseEntity.ok(ResponseMessage.createSuccessMessage("Selected authorities revoked from group " + groupId)); + } catch (IllegalArgumentException e) { + String message = "Revoking authorities to group [" + groupId + "] has failed!"; + log.error(message, e); + return ResponseEntity.badRequest().body(ResponseMessage.createErrorMessage("Revoking authorities to group " + groupId + " has failed!")); + } + } + + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Groups retrieved successfully"), + @ApiResponse(responseCode = "400", description = "Invalid group data"), + @ApiResponse(responseCode = "500", description = "Internal server error") + }) + @Operation(summary = "Generic group search", security = {@SecurityRequirement(name = "X-Auth-Token")}) + @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity> search(GroupSearchRequestDto query, Pageable pageable, Locale locale) { + List groups = groupService.search(query, pageable).getContent(); + return ResponseEntity.ok(transformPageContent(groups, pageable, locale)); + } + + @PreAuthorize("@authorizationServiceImpl.hasAuthority('ADMIN')") + @Operation(summary = "Updates group", description = "Updates group according to incoming parameters", security = {@SecurityRequirement(name = "X-Auth-Token")}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Group updated successfully"), + @ApiResponse(responseCode = "400", description = "Invalid group parameters"), + @ApiResponse(responseCode = "404", description = "Group not found"), + @ApiResponse(responseCode = "500", description = "Internal server error") + }) + @PutMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity updateGroup(@RequestBody UpdateGroupRequestDto groupUpdate) { + try { + Group group = groupService.findById(groupUpdate.id()); + if (groupUpdate.identifier() != null) { + group.setIdentifier(groupUpdate.identifier()); + } + if (groupUpdate.displayName() != null) { + group.setDisplayName(groupUpdate.displayName()); + } + groupService.save(group); + return ResponseEntity.ok(ResponseMessage.createSuccessMessage("Group with id [%s] updated successfully".formatted(groupUpdate.id()))); + } catch (IllegalArgumentException e) { + return ResponseEntity.notFound().build(); + } catch (Exception e) { + return ResponseEntity.internalServerError().body(ResponseMessage.createErrorMessage("Failed to update group with id [%s]".formatted(groupUpdate.id()))); + } + } + + @PreAuthorize("@authorizationServiceImpl.hasAuthority('ADMIN')") + @Operation(summary = "Assigns subgroups to group", description = "Removes existing subgroups and assigns new ones to group based on path param and request body", security = {@SecurityRequirement(name = "X-Auth-Token")}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Selected subgroups was successfully assigned to group"), + @ApiResponse(responseCode = "400", description = "Requested group or groups with defined id do not exist"), + @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), + @ApiResponse(responseCode = "500", description = "Internal server error") + }) + @PatchMapping(value = "/{id}/groups/assign", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity assignSubgroupsToGroup(@PathVariable("id") String groupId, @RequestBody Set subgroupIds) { + try { + groupService.assignSubgroups(groupId, subgroupIds); + log.info("Subgroups {} assigned to group with id [{}]", subgroupIds, groupId); + return ResponseEntity.ok(ResponseMessage.createSuccessMessage("Selected subgroups was successfully assigned to group")); + } catch (IllegalArgumentException e) { + String message = "Adding subgroups to group [" + groupId + "] has failed!"; + log.error(message, e); + return ResponseEntity.badRequest().body(ResponseMessage.createErrorMessage("Adding subgroups to group " + groupId + " has failed!")); + } + } + + @PreAuthorize("@authorizationServiceImpl.hasAuthority('ADMIN')") + @Operation(summary = "Adds subgroups to group", description = "Add subgroups to group based on path param and request body", security = {@SecurityRequirement(name = "X-Auth-Token")}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Selected subgroups was successfully added to group"), + @ApiResponse(responseCode = "400", description = "Requested group or groups with defined id do not exist"), + @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), + @ApiResponse(responseCode = "500", description = "Internal server error") + }) + @PatchMapping(value = "/{id}/groups/add", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity addSubgroupsToGroup(@PathVariable("id") String groupId, @RequestBody Set subgroupIds) { + try { + subgroupIds.forEach(subgroupId -> groupService.addSubgroup(groupId, subgroupId)); + log.info("Subgroups {} added to group with id [{}]", subgroupIds, groupId); + return ResponseEntity.ok(ResponseMessage.createSuccessMessage("Selected subgroups was successfully added to group")); + } catch (IllegalArgumentException e) { + String message = "Adding subgroups to group [" + groupId + "] has failed!"; + log.error(message, e); + return ResponseEntity.badRequest().body(ResponseMessage.createErrorMessage("Adding subgroups to group " + groupId + " has failed!")); + } + } + + @PreAuthorize("@authorizationServiceImpl.hasAuthority('ADMIN')") + @Operation(summary = "Removes subgroups from group", description = "Removes subgroups from group based on path param and request body", security = {@SecurityRequirement(name = "X-Auth-Token")}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Selected subgroups was successfully removed from group"), + @ApiResponse(responseCode = "400", description = "Requested group or groups with defined id do not exist"), + @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), + @ApiResponse(responseCode = "500", description = "Internal server error") + }) + @PatchMapping(value = "/{id}/groups/remove", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity removeSubgroupsFromGroup(@PathVariable("id") String groupId, @RequestBody Set subgroupIds) { + try { + subgroupIds.forEach(subgroupId -> groupService.removeSubgroup(groupId, subgroupId)); + log.info("Subgroups {} removed from group with id [{}]", subgroupIds, groupId); + return ResponseEntity.ok(ResponseMessage.createSuccessMessage("Selected subgroups was successfully removed from group")); + } catch (IllegalArgumentException e) { + String message = "Removing subgroups from group [" + groupId + "] has failed!"; + log.error(message, e); + return ResponseEntity.badRequest().body(ResponseMessage.createErrorMessage("Removing subgroups from group " + groupId + " has failed!")); + } + } + + private Page transformPageContent(List groups, Pageable pageable, Locale locale) { + return new PageImpl<>(groups.stream().map(group -> GroupDto.fromGroup(group, locale)).toList(), pageable, groups.size()); + } + + private boolean realmExists(String realmId) { + if (realmId == null) { + return false; + } + Optional realm = realmService.getRealmById(realmId); + return realm.isPresent(); + } +} 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..8d1213dac48 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 @@ -5,7 +5,7 @@ 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.auth.web.responsebodies.UserDto; import com.netgrif.application.engine.objects.auth.domain.AbstractUser; import com.netgrif.application.engine.objects.auth.domain.LoggedUser; import com.netgrif.application.engine.objects.preferences.Preferences; @@ -53,7 +53,7 @@ public class PublicUserController { @ApiResponse(responseCode = "500", description = "Internal server error") }) @GetMapping(value = "/me", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity getLoggedUser(Authentication auth) { + public ResponseEntity getLoggedUser(Authentication auth) { LoggedUser loggedUser = (LoggedUser) auth.getPrincipal(); AbstractUser user; try { @@ -67,7 +67,7 @@ public ResponseEntity getLoggedUser(Authentication auth) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).build(); } - return ResponseEntity.ok(User.createUser(user)); + return ResponseEntity.ok(UserDto.createUser(user)); } @ApiResponses(value = { @@ -77,7 +77,7 @@ 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, Authentication auth) { 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(), @@ -127,12 +127,12 @@ public ResponseEntity savePreferences(@RequestBody PreferencesRequest pr } } - private Page changeToResponse(Page users, Pageable pageable) { + 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(); + public List changeType(List users) { + return users.stream().map(UserDto::createUser).toList(); } } 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 1fd5f887d09..ef9e01bfc6a 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 @@ -7,7 +7,7 @@ import com.netgrif.application.engine.auth.web.requestbodies.UserCreateRequest; 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.auth.web.responsebodies.UserDto; 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; @@ -54,7 +54,7 @@ public class UserController { private final RealmService realmService; private final UserFactory userFactory; - @PreAuthorize("@authorizationService.hasAuthority('ADMIN')") + @PreAuthorize("@authorizationServiceImpl.hasAuthority('ADMIN')") @Operation(summary = "Create a new user", description = "Creates a new user in the realm specified by id.") @ApiResponses(value = { @ApiResponse(responseCode = "201", description = "User successfully created"), @@ -63,7 +63,7 @@ public class UserController { @ApiResponse(responseCode = "500", description = "Internal server error") }) @PostMapping("/{realmId}") - public ResponseEntity createUser(@PathVariable String realmId, @RequestBody UserCreateRequest request, Locale locale) { + public ResponseEntity createUser(@PathVariable String realmId, @RequestBody UserCreateRequest request, Locale locale) { try { if (!realmExists(realmId)) { log.error("Realm with id [{}] not found", realmId); @@ -96,7 +96,7 @@ public ResponseEntity createUser(@PathVariable String realmId, @RequestBod @ApiResponse(responseCode = "500", description = "Internal server error") }) @GetMapping("/{realmId}/all") - public ResponseEntity> getAllUsers(@PathVariable String realmId, Pageable pageable, Locale locale) { + public ResponseEntity> getAllUsers(@PathVariable String realmId, Pageable pageable, Locale locale) { if (!realmExists(realmId)) { log.error("Realm with id [{}] not found", realmId); return ResponseEntity.badRequest().build(); @@ -112,7 +112,7 @@ public ResponseEntity> getAllUsers(@PathVariable String realmId, Page @ApiResponse(responseCode = "500", description = "Internal server error") }) @GetMapping(value = "/me", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity getLoggedUser(Authentication auth, Locale locale) { + public ResponseEntity getLoggedUser(Authentication auth, Locale locale) { LoggedUser loggedUser = (LoggedUser) auth.getPrincipal(); AbstractUser user; try { @@ -136,7 +136,7 @@ public ResponseEntity getLoggedUser(Authentication auth, Locale locale) { }) @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, Locale locale) { + public ResponseEntity> search(@RequestBody UserSearchRequestBody query, Pageable pageable, Authentication auth, Locale locale) { 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(), @@ -154,7 +154,7 @@ public ResponseEntity> search(@RequestBody UserSearchRequestBody quer @ApiResponse(responseCode = "500", description = "Internal server error") }) @GetMapping(value = "/{realmId}/{id}", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity getUser(@PathVariable("realmId") String realmId, @PathVariable("id") String userId, Locale locale) { + public ResponseEntity getUser(@PathVariable("realmId") String realmId, @PathVariable("id") String userId, Locale locale) { LoggedUser actualUser = userService.getLoggedUserFromContext(); // TODO: impersonation // LoggedUser loggedUser = actualUser.getSelfOrImpersonated(); @@ -209,7 +209,7 @@ public ResponseEntity getUser(@PathVariable("realmId") String realmId, @Pa // Page page = userService.findAllActiveByProcessRoles(roleResourceIds, pageable); // return ResponseEntity.ok(); // } - @PreAuthorize("@authorizationService.hasAuthority('ADMIN')") + @PreAuthorize("@authorizationServiceImpl.hasAuthority('ADMIN')") @Operation(summary = "Assign roles to the user", description = "Caller must have the ADMIN role", security = {@SecurityRequirement(name = "X-Auth-Token")}) @PutMapping(value = "/{realmId}/{id}/roles", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) @ApiResponses(value = { @@ -253,7 +253,7 @@ public ResponseEntity assignRolesToUser(@PathVariable("realmId" // } // } // - @PreAuthorize("@authorizationService.hasAuthority('ADMIN')") + @PreAuthorize("@authorizationServiceImpl.hasAuthority('ADMIN')") @Operation(summary = "Get all authorities of the system", description = "Caller must have the ADMIN role", security = {@SecurityRequirement(name = "X-Auth-Token")}) @@ -267,7 +267,7 @@ public ResponseEntity> getAllAuthorities() { return ResponseEntity.ok(authorityService.findAll(Pageable.unpaged()).stream().toList()); } - @PreAuthorize("@authorizationService.hasAuthority('ADMIN')") + @PreAuthorize("@authorizationServiceImpl.hasAuthority('ADMIN')") @Operation(summary = "Assign authority to the user", description = "Caller must have the ADMIN role", security = {@SecurityRequirement(name = "X-Auth-Token")}) @@ -327,11 +327,11 @@ public ResponseEntity savePreferences(@RequestBody PreferencesR } } - private Page changeToResponse(Page users, Pageable pageable, Locale locale) { + private Page changeToResponse(Page users, Pageable pageable, Locale locale) { return new PageImpl<>(changeType(users.getContent(), locale), pageable, users.getTotalElements()); } - public List changeType(List users, Locale locale) { + public List changeType(List users, Locale locale) { return users.stream().map(u -> userFactory.getUser(u, locale)).toList(); } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/auth/web/responsebodies/UserResource.java b/application-engine/src/main/java/com/netgrif/application/engine/auth/web/responsebodies/UserResource.java index da82aaab178..7ad5da3faed 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/auth/web/responsebodies/UserResource.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/auth/web/responsebodies/UserResource.java @@ -5,9 +5,9 @@ import java.util.ArrayList; -public class UserResource extends EntityModel { +public class UserResource extends EntityModel { - public UserResource(User content, String selfRel) { + public UserResource(UserDto content, String selfRel) { super(content, new ArrayList<>()); } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/elastic/web/ElasticController.java b/application-engine/src/main/java/com/netgrif/application/engine/elastic/web/ElasticController.java index 6a4e9235999..55be6883dfd 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/elastic/web/ElasticController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/elastic/web/ElasticController.java @@ -79,7 +79,7 @@ public void setIndexService(IElasticIndexService indexService) { this.indexService = indexService; } - @PreAuthorize("@authorizationService.hasAuthority('ADMIN')") + @PreAuthorize("@authorizationServiceImpl.hasAuthority('ADMIN')") @Operation(summary = "Reindex specified cases", description = "Caller must have the ADMIN role", security = {@SecurityRequirement(name = "BasicAuth")}) @@ -117,7 +117,7 @@ public MessageResource reindex(@RequestBody Map searchBody, Auth } } - @PreAuthorize("@authorizationService.hasAuthority('ADMIN')") + @PreAuthorize("@authorizationServiceImpl.hasAuthority('ADMIN')") @Operation(summary = "Reindex all or stale cases with bulk index", description = "Reindex all or stale cases (specified by IndexParams.indexAll param) with bulk index. Caller must have the ADMIN role", security = {@SecurityRequirement(name = "BasicAuth")}) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/orgstructure/web/GroupController.java b/application-engine/src/main/java/com/netgrif/application/engine/orgstructure/web/GroupController.java index 5fca05b1095..9670d43b324 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/orgstructure/web/GroupController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/orgstructure/web/GroupController.java @@ -1,56 +1,56 @@ -package com.netgrif.application.engine.orgstructure.web; - -import com.netgrif.application.engine.auth.service.GroupService; -import com.netgrif.application.engine.orgstructure.web.responsebodies.Group; -import com.netgrif.application.engine.orgstructure.web.responsebodies.GroupsResource; -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 org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.data.domain.Pageable; -import org.springframework.hateoas.MediaTypes; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -@RestController -@RequestMapping("/api/group") -@ConditionalOnProperty( - value = "netgrif.engine.security.web.group-enabled", - havingValue = "true", - matchIfMissing = true -) -@Tag(name = "Group") -public class GroupController { - - private final GroupService service; - - public GroupController(GroupService service) { - this.service = service; - } - - @PreAuthorize("@authorizationService.hasAuthority('ADMIN')") - @Operation(summary = "Get all groups in the system", - description = "Caller must have the ADMIN role", - security = {@SecurityRequirement(name = "BasicAuth")}) - @GetMapping(value = "/all", produces = MediaTypes.HAL_JSON_VALUE) - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "OK"), - @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), - }) - public GroupsResource getAllGroups() { - List groups = service.findAll(Pageable.unpaged()).getContent(); - Set groupResponse = groups.stream() - .map(g -> new Group(g.getStringId(), g.getDisplayName())) - .collect(Collectors.toCollection(HashSet::new)); - return new GroupsResource(groupResponse); - } -} +//package com.netgrif.application.engine.orgstructure.web; +// +//import com.netgrif.application.engine.auth.service.GroupService; +//import com.netgrif.application.engine.orgstructure.web.responsebodies.Group; +//import com.netgrif.application.engine.orgstructure.web.responsebodies.GroupsResource; +//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 org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +//import org.springframework.data.domain.Pageable; +//import org.springframework.hateoas.MediaTypes; +//import org.springframework.security.access.prepost.PreAuthorize; +//import org.springframework.web.bind.annotation.GetMapping; +//import org.springframework.web.bind.annotation.RequestMapping; +//import org.springframework.web.bind.annotation.RestController; +// +//import java.util.HashSet; +//import java.util.List; +//import java.util.Set; +//import java.util.stream.Collectors; +// +//@RestController +//@RequestMapping("/api/group") +//@ConditionalOnProperty( +// value = "netgrif.engine.security.web.group-enabled", +// havingValue = "true", +// matchIfMissing = true +//) +//@Tag(name = "Group") +//public class GroupController { +// +// private final GroupService service; +// +// public GroupController(GroupService service) { +// this.service = service; +// } +// +// @PreAuthorize("@authorizationService.hasAuthority('ADMIN')") +// @Operation(summary = "Get all groups in the system", +// description = "Caller must have the ADMIN role", +// security = {@SecurityRequirement(name = "BasicAuth")}) +// @GetMapping(value = "/all", produces = MediaTypes.HAL_JSON_VALUE) +// @ApiResponses(value = { +// @ApiResponse(responseCode = "200", description = "OK"), +// @ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"), +// }) +// public GroupsResource getAllGroups() { +// List groups = service.findAll(Pageable.unpaged()).getContent(); +// Set groupResponse = groups.stream() +// .map(g -> new Group(g.getStringId(), g.getDisplayName())) +// .collect(Collectors.toCollection(HashSet::new)); +// return new GroupsResource(groupResponse); +// } +//} diff --git a/application-engine/src/main/java/com/netgrif/application/engine/orgstructure/web/responsebodies/GroupsResource.java b/application-engine/src/main/java/com/netgrif/application/engine/orgstructure/web/responsebodies/GroupsResource.java index a6039cd6bde..c61b891c669 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/orgstructure/web/responsebodies/GroupsResource.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/orgstructure/web/responsebodies/GroupsResource.java @@ -1,19 +1,19 @@ -package com.netgrif.application.engine.orgstructure.web.responsebodies; - -import com.netgrif.application.engine.orgstructure.web.GroupController; -import org.springframework.hateoas.CollectionModel; -import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; - - -public class GroupsResource extends CollectionModel { - - public GroupsResource(Iterable content) { - super(content); - buildLinks(); - } - - private void buildLinks() { - add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(GroupController.class) - .getAllGroups()).withSelfRel()); - } -} \ No newline at end of file +//package com.netgrif.application.engine.orgstructure.web.responsebodies; +// +//import com.netgrif.application.engine.orgstructure.web.GroupController; +//import org.springframework.hateoas.CollectionModel; +//import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; +// +// +//public class GroupsResource extends CollectionModel { +// +// public GroupsResource(Iterable content) { +// super(content); +// buildLinks(); +// } +// +// private void buildLinks() { +// add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(GroupController.class) +// .getAllGroups()).withSelfRel()); +// } +//} \ No newline at end of file diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/ProcessRoleService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/ProcessRoleService.java index d1d07bcdc22..759e14f8fb1 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/ProcessRoleService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/ProcessRoleService.java @@ -516,4 +516,60 @@ private ObjectId extractObjectId(String caseId) { return new ObjectId(objectIdPart); } + + private void deleteRolesOfNetFromUser(PetriNet net, List deletedRoleIds, Set deletedRoleStringIds, LoggedUser loggedUser) { + Pageable realmPageable = PageRequest.of(0, paginationProperties.getBackendPageSize()); + Page realms; + do { + realms = realmService.getSmallRealm(realmPageable); + + realms.forEach(realm -> { + Pageable usersPageable = PageRequest.of(0, paginationProperties.getBackendPageSize()); + Page users; + do { + users = this.userService.findAllByProcessRoles(new HashSet<>(deletedRoleIds), realm.getName(), usersPageable); + + for (AbstractUser user : users) { + log.info("[{}]: Removing deleted roles of Petri net {} version {} from user {} with id {}", + net.getStringId(), net.getIdentifier(), net.getVersion().toString(), user.getFullName(), user.getStringId()); + + if (user.getProcessRoles().isEmpty()) { + continue; + } + + Set newRoles = user.getProcessRoles().stream() + .filter(role -> !deletedRoleStringIds.contains(role.getStringId())) + .map(ProcessRole::get_id) + .collect(Collectors.toSet()); + this.assignRolesToUser(user, newRoles, loggedUser); + } + + usersPageable = usersPageable.next(); + } while (users.hasNext()); + }); + + realmPageable = realmPageable.next(); + } while (realms.hasNext()); + } + + private void deleteRolesOfNetFromGroup(PetriNet net, List deletedRoleIds, Set deletedRoleStringIds) { + Pageable groupPageable = PageRequest.of(0, paginationProperties.getBackendPageSize()); + Page groups; + do { + groups = groupService.findAllByProcessRoles(new HashSet<>(deletedRoleIds), groupPageable); + for (Group group : groups) { + log.info("[{}]: Removing deleted roles of Petri net {} version {} from group {} with id {}", + net.getStringId(), net.getIdentifier(), net.getVersion().toString(), group.getFullName(), group.getStringId()); + if (group.getProcessRoles().isEmpty()) { + continue; + } + Set newRoles = group.getProcessRoles().stream() + .filter(role -> !deletedRoleStringIds.contains(role.getStringId())) + .map(ProcessRole::get_id) + .collect(Collectors.toSet()); + assignRolesToGroup(group, newRoles); + } + groupPageable = groupPageable.next(); + } while (groups.hasNext()); + } } 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 d4b062650c2..29497470846 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 @@ -95,7 +95,7 @@ public static String decodeUrl(String s1) { } } - @PreAuthorize("@authorizationService.hasAuthority('ADMIN')") + @PreAuthorize("@authorizationServiceImpl.hasAuthority('ADMIN')") @Operation(summary = "Import new process", description = "Caller must have the ADMIN role. Imports an entirely new process or a new version of an existing process.", security = {@SecurityRequirement(name = "BasicAuth")}) @@ -182,7 +182,7 @@ public TransactionsResource getTransactions(@PathVariable("netId") String netId, return new TransactionsResource(net.getTransactions().values(), netId, locale); } - @PreAuthorize("@authorizationService.hasAuthority('ADMIN')") + @PreAuthorize("@authorizationServiceImpl.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) { diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/ProcessRoleController.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/ProcessRoleController.java index 77cac9d06fd..7d10c6f9f8d 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/ProcessRoleController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/ProcessRoleController.java @@ -34,7 +34,7 @@ public class ProcessRoleController { private final ProcessRoleService processRoleService; - @PreAuthorize("@authorizationService.hasAuthority('ADMIN')") + @PreAuthorize("@authorizationServiceImpl.hasAuthority('ADMIN')") @Operation(summary = "Delete global role", security = {@SecurityRequirement(name = "X-Auth-Token")}) @Parameter(name = "id", description = "Id of the global role to be deleted", required = true, example = "GcdIZcAPUc6jh7i2-68d683f80dc9384aa6791a64") diff --git a/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/GroupRunner.java b/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/GroupRunner.java index a404fc17d94..b9783d6cfe2 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/GroupRunner.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/GroupRunner.java @@ -3,6 +3,7 @@ import com.netgrif.application.engine.auth.config.GroupConfigurationProperties; import com.netgrif.application.engine.auth.service.UserService; import com.netgrif.application.engine.auth.service.GroupService; +import com.netgrif.application.engine.objects.auth.domain.Group; import com.netgrif.application.engine.startup.ApplicationEngineStartupRunner; import com.netgrif.application.engine.startup.annotation.RunnerOrder; import lombok.RequiredArgsConstructor; @@ -11,6 +12,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Component; +import java.util.Optional; + @Slf4j @Component @RunnerOrder(110) @@ -24,12 +27,16 @@ public class GroupRunner implements ApplicationEngineStartupRunner { @Override public void run(ApplicationArguments args) throws Exception { - createDefaultGroup(); + if (groupProperties.isSystemEnabled()) { + createDefaultGroup(); + } } protected void createDefaultGroup() { - if (groupProperties.isSystemEnabled()) { - groupService.create(userService.getLoggedOrSystem()); + Optional systemGroupOpt = groupService.findByIdentifier(userService.getSystem().getUsername()); + if (systemGroupOpt.isEmpty()) { + groupService.create(userService.getSystem()); + log.info("Default system group created."); } } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/SuperCreatorRunner.java b/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/SuperCreatorRunner.java index bb449182e42..91ae07fdb56 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/SuperCreatorRunner.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/SuperCreatorRunner.java @@ -108,7 +108,7 @@ public void setAllToSuperUser() { } public void setAllGroups() { - groupService.findAll(Pageable.unpaged()).forEach(g -> groupService.addUser(getSuperUser(), g)); + groupService.findAll(Pageable.unpaged()).forEach(g -> groupService.addUser(g, getSuperUser())); } public void setAllProcessRoles() { diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FilterImportExportService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FilterImportExportService.java index 31ac9674498..6304faca42c 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FilterImportExportService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FilterImportExportService.java @@ -259,6 +259,11 @@ protected Map performImport(FilterImportExportList filterList) t QTask.task.transitionId.eq(IMPORT_FILTER_TRANSITION) .and(QTask.task.caseId.eq(filterCase.get().getStringId())) ); + + if (importedFilterTask == null) { + return; + } + importedFilterTaskIds.put(filter.getCaseId(), importedFilterTask.getStringId()); // TODO: delete after fixed issue: https://netgrif.atlassian.net/jira/servicedesk/projects/NGSD/issues/ 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 9d4615b06e4..77e3d6a5054 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 @@ -188,7 +188,7 @@ public PagedModel findAllByAuthor(@PathVariable("id") String autho return resources; } - @PreAuthorize("@authorizationService.hasAuthority('ADMIN')") + @PreAuthorize("@authorizationServiceImpl.hasAuthority('ADMIN')") @Operation(summary = "Reload tasks of case", description = "Caller must have the ADMIN role", security = {@SecurityRequirement(name = "BasicAuth")}) diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/action/ActionDelegateTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/action/ActionDelegateTest.groovy index 97aedd3daf0..79c5bfa0329 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/action/ActionDelegateTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/action/ActionDelegateTest.groovy @@ -98,8 +98,12 @@ class ActionDelegateTest { void importFiltersTest(){ prepareFilterImportFile() - List actionDelegateList = actionDelegate.importFilters() - assert actionDelegateList.size() == 2 + actionDelegate.importFilters() + List filters = workflowService.search( + QCase.case$.processIdentifier.eq(FilterRunner.FILTER_PETRI_NET_IDENTIFIER), + Pageable.ofSize(4) + ).content + assert filters.size() == 4 } private void prepareFilterImportFile() { diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/action/AssignActionTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/action/AssignActionTest.groovy index 560eae5f304..7488a4ddeb8 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/action/AssignActionTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/action/AssignActionTest.groovy @@ -99,7 +99,7 @@ class AssignActionTest { auths = importHelper.createAuthorities(["user": Authority.user, "admin": Authority.admin]) - importHelper.createUser(new com.netgrif.application.engine.adapter.spring.auth.domain.User(firstName: "Test", lastName: "Integration", email: USER_EMAIL, password: USER_PASSWORD, state: UserState.ACTIVE), + importHelper.createUser(new com.netgrif.application.engine.adapter.spring.auth.domain.User(firstName: "Test", lastName: "Integration", username: USER_EMAIL, email: USER_EMAIL, password: USER_PASSWORD, state: UserState.ACTIVE), [auths.get("user"), auths.get("admin")] as Authority[], // [org] as Group[], [] as ProcessRole[]) diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/action/AssignRemoveTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/action/AssignRemoveTest.groovy index 5797a309360..505705ab2ae 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/action/AssignRemoveTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/action/AssignRemoveTest.groovy @@ -76,7 +76,7 @@ class AssignRemoveTest { assert netOptional.getNet() != null; def net = netOptional.getNet() def userAuthorities = importHelper.createAuthorities(["user": Authority.user]) - def testUser = importHelper.createUser(new User(firstName: "Test", lastName: "Integration", email: USER_EMAIL, password: "password", state: UserState.ACTIVE), + def testUser = importHelper.createUser(new User(firstName: "Test", lastName: "Integration", username: USER_EMAIL, email: USER_EMAIL, password: "password", state: UserState.ACTIVE), [userAuthorities.get("user")] as Authority[], [] as ProcessRole[]) def loggedUser = ActorTransformer.toLoggedUser(testUser) diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/auth/LoginAttemptsTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/auth/LoginAttemptsTest.groovy index 86a256ac0bc..ab8a4fdc353 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/auth/LoginAttemptsTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/auth/LoginAttemptsTest.groovy @@ -1,6 +1,7 @@ package com.netgrif.application.engine.auth import com.netgrif.application.engine.TestHelper +import com.netgrif.application.engine.adapter.spring.auth.domain.User import com.netgrif.application.engine.auth.service.UserService import com.netgrif.application.engine.configuration.properties.SecurityConfigurationProperties import com.netgrif.application.engine.objects.auth.domain.Authority @@ -62,7 +63,7 @@ class LoginAttemptsTest { .build() auths = importHelper.createAuthorities(["user": Authority.user, "admin": Authority.admin]) - importHelper.createUser(new com.netgrif.application.engine.adapter.spring.auth.domain.User(firstName: "Test", lastName: "Integration", username: USER_EMAIL, email: USER_EMAIL, password: USER_PASSWORD, state: UserState.ACTIVE), + importHelper.createUser(new User(firstName: "Test", lastName: "Integration", username: USER_EMAIL, email: USER_EMAIL, password: USER_PASSWORD, state: UserState.ACTIVE), [auths.get("user"), auths.get("admin")] as Authority[], [] as ProcessRole[]) } diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/auth/TaskAuthorizationServiceTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/auth/TaskAuthorizationServiceTest.groovy index 644852ad01f..38eff5d9eb9 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/auth/TaskAuthorizationServiceTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/auth/TaskAuthorizationServiceTest.groovy @@ -1,6 +1,7 @@ package com.netgrif.application.engine.auth import com.netgrif.application.engine.TestHelper +import com.netgrif.application.engine.adapter.spring.auth.domain.User import com.netgrif.application.engine.auth.service.GroupService import com.netgrif.application.engine.auth.service.UserService import com.netgrif.application.engine.importer.service.Importer @@ -181,7 +182,7 @@ class TaskAuthorizationServiceTest { this.netWithUserRefs = netWithUserRefs.getNet() def auths = importHelper.createAuthorities(["user": Authority.user]) - testUser = importHelper.createUser(new com.netgrif.application.engine.adapter.spring.auth.domain.User(firstName: "Role", lastName: "User", email: USER_EMAIL, password: "password", state: UserState.ACTIVE), + testUser = importHelper.createUser(new User(firstName: "Role", lastName: "User", username: USER_EMAIL, email: USER_EMAIL, password: "password", state: UserState.ACTIVE), [auths.get("user")] as Authority[], // [org] as Group[], [] as ProcessRole[] diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/auth/UserServiceTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/auth/UserServiceTest.groovy index 6313dc671bd..6a9b90fd267 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/auth/UserServiceTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/auth/UserServiceTest.groovy @@ -116,10 +116,10 @@ class UserServiceTest { User otherGroupMember = createUser("other-group-member@netgrif.com") def defaultSystemGroup = groupService.getDefaultSystemGroup() - groupService.removeUser(requester, defaultSystemGroup) - groupService.removeUser(sharedGroupMember, defaultSystemGroup) - groupService.removeUser(otherGroupMember, defaultSystemGroup) - groupService.addUser(sharedGroupMember, groupService.getDefaultUserGroup(requester)) + groupService.removeUser(defaultSystemGroup, requester) + groupService.removeUser(defaultSystemGroup, sharedGroupMember) + groupService.removeUser(defaultSystemGroup, otherGroupMember) + groupService.addUser(groupService.getDefaultUserGroup(requester), sharedGroupMember) sharedGroupMember = (User) userService.addRole(sharedGroupMember, dummyRole.get_id()) otherGroupMember = (User) userService.addRole(otherGroupMember, dummyRole.get_id()) diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/auth/WorkflowAuthorizationServiceTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/auth/WorkflowAuthorizationServiceTest.groovy index 11d44504a7b..f0c9214e1e7 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/auth/WorkflowAuthorizationServiceTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/auth/WorkflowAuthorizationServiceTest.groovy @@ -148,11 +148,10 @@ class WorkflowAuthorizationServiceTest { this.netWithUserRefs = netWithUserRefs.getNet() def auths = importHelper.createAuthorities(["user": Authority.user, "admin": Authority.admin]) - testUser = importHelper.createUser(new User(firstName: "Role", lastName: "User", email: USER_EMAIL, password: "password", state: UserState.ACTIVE), - [auths.get("user")]as Authority[], + testUser = importHelper.createUser(new User(firstName: "Role", lastName: "User", username: USER_EMAIL, email: USER_EMAIL, password: "password", state: UserState.ACTIVE), [auths.get("user")]as Authority[], // [org] as Group[], [] as ProcessRole[]) - AbstractUser adminUser = importHelper.createUser(new User(firstName: "Admin", lastName: "User", email: ADMIN_EMAIL, password: "password", state: UserState.ACTIVE), + AbstractUser adminUser = importHelper.createUser(new User(firstName: "Admin", lastName: "User", username: ADMIN_EMAIL, email: ADMIN_EMAIL, password: "password", state: UserState.ACTIVE), [auths.get("admin")] as Authority[], [] as ProcessRole[]) userAuth = new UsernamePasswordAuthenticationToken(ActorTransformer.toLoggedUser(testUser), "password", testUser.authoritySet as List) diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/elastic/ElasticSearchTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/elastic/ElasticSearchTest.groovy index 9ad69cb4b58..f96f3cde973 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/elastic/ElasticSearchTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/elastic/ElasticSearchTest.groovy @@ -2,6 +2,7 @@ package com.netgrif.application.engine.elastic import com.netgrif.application.engine.ApplicationEngine import com.netgrif.application.engine.TestHelper +import com.netgrif.application.engine.adapter.spring.auth.domain.User import com.netgrif.application.engine.elastic.domain.ElasticCaseRepository import com.netgrif.application.engine.elastic.service.interfaces.IElasticCaseMappingService import com.netgrif.application.engine.elastic.service.interfaces.IElasticCaseService @@ -119,7 +120,7 @@ class ElasticSearchTest { // def org = importHelper.createGroup("Test") def auths = importHelper.createAuthorities(["user": Authority.user, "admin": Authority.admin]) // def processRoles = importHelper.getProcessRoles(net.get()) - def testUser = importHelper.createUser(new com.netgrif.application.engine.adapter.spring.auth.domain.User(firstName: "Test", lastName: "Integration", email: USER_EMAIL, password: USER_PASSW, state: UserState.ACTIVE), + def testUser = importHelper.createUser(new User(firstName: "Test", lastName: "Integration", username: USER_EMAIL, email: USER_EMAIL, password: USER_PASSW, state: UserState.ACTIVE), [auths.get("user")] as Authority[], [net.roles.values().find { it.importId == "process_role" }] as ProcessRole[]) auth = new UsernamePasswordAuthenticationToken(ActorTransformer.toLoggedUser(testUser), USER_PASSW, testUser.authoritySet as List) diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/insurance/mvc/InsuranceTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/insurance/mvc/InsuranceTest.groovy index 1c1cc1c1c02..b8cb3fe6b31 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/insurance/mvc/InsuranceTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/insurance/mvc/InsuranceTest.groovy @@ -2,6 +2,7 @@ package com.netgrif.application.engine.insurance.mvc import com.netgrif.application.engine.ApplicationEngine import com.netgrif.application.engine.TestHelper +import com.netgrif.application.engine.adapter.spring.auth.domain.AuthorityImpl import com.netgrif.application.engine.adapter.spring.auth.domain.User import com.netgrif.application.engine.adapter.spring.petrinet.service.ProcessRoleService import com.netgrif.application.engine.auth.service.UserService @@ -139,10 +140,10 @@ class InsuranceTest { def auths = importHelper.createAuthorities(["user": Authority.user, "admin": Authority.admin]) def processRoles = ImportHelper.getProcessRolesByImportId(net.getNet(), ["agent": "1", "company": "2"]) - def testUser = importHelper.createUser(new User(firstName: "Test", lastName: "Integration", email: USER_EMAIL, password: "password", state: UserState.ACTIVE), + def testUser = importHelper.createUser(new User(firstName: "Test", lastName: "Integration", username: USER_EMAIL, email: USER_EMAIL, password: "password", state: UserState.ACTIVE), [auths.get("user"), auths.get("admin")] as Authority[], [processRoles.get("agent"), processRoles.get("company")] as ProcessRole[]) - auth = new UsernamePasswordAuthenticationToken(ActorTransformer.toLoggedUser(testUser), "password", testUser.authoritySet as List) + auth = new UsernamePasswordAuthenticationToken(ActorTransformer.toLoggedUser(testUser), "password", (Set) testUser.authoritySet) auth.setDetails(new WebAuthenticationDetails(new MockHttpServletRequest())); mapper = net.getNet().dataSet.collectEntries { [(it.value.importId as int): (it.key)] } diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/menu/MenuImportExportTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/menu/MenuImportExportTest.groovy index 29c357877bd..b2fb1519240 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/menu/MenuImportExportTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/menu/MenuImportExportTest.groovy @@ -212,7 +212,7 @@ class MenuImportExportTest { private User createDummyUser() { def auths = importHelper.createAuthorities(["user": Authority.user, "admin": Authority.admin]) - return importHelper.createUser(new User(firstName: "Dummy", lastName: "User", email: DUMMY_USER_MAIL, username: DUMMY_USER_MAIL, password: DUMMY_USER_PASSWORD, state: UserState.ACTIVE), + return importHelper.createUser(new User(firstName: "Dummy", lastName: "User", username: DUMMY_USER_MAIL, email: DUMMY_USER_MAIL, password: DUMMY_USER_PASSWORD, state: UserState.ACTIVE), [auths.get("user")] as Authority[], [] as ProcessRole[]) } diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/orgstructure/groups/GroupServiceTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/orgstructure/groups/GroupServiceTest.groovy index ca02b6e69ec..77ada928732 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/orgstructure/groups/GroupServiceTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/orgstructure/groups/GroupServiceTest.groovy @@ -1,21 +1,33 @@ package com.netgrif.application.engine.orgstructure.groups import com.netgrif.application.engine.TestHelper +import com.netgrif.application.engine.adapter.spring.auth.domain.User +import com.netgrif.application.engine.adapter.spring.petrinet.service.ProcessRoleService import com.netgrif.application.engine.auth.service.GroupService import com.netgrif.application.engine.auth.service.UserService import com.netgrif.application.engine.objects.auth.constants.UserConstants +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.Group import com.netgrif.application.engine.objects.auth.domain.QGroup import com.netgrif.application.engine.objects.auth.domain.enums.UserState +import com.netgrif.application.engine.objects.petrinet.domain.PetriNet +import com.netgrif.application.engine.objects.petrinet.domain.VersionType import com.netgrif.application.engine.objects.petrinet.domain.roles.ProcessRole +import com.netgrif.application.engine.objects.workflow.domain.eventoutcomes.petrinetoutcomes.ImportPetriNetEventOutcome +import com.netgrif.application.engine.petrinet.params.ImportPetriNetParams +import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService import com.netgrif.application.engine.startup.ImportHelper import com.netgrif.application.engine.startup.runner.GroupRunner import com.netgrif.application.engine.utils.FullPageRequest +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest +import org.springframework.data.domain.Page +import org.springframework.data.domain.Pageable +import org.springframework.data.util.Pair import org.springframework.test.context.ActiveProfiles import org.springframework.test.context.junit.jupiter.SpringExtension @@ -37,65 +49,84 @@ class GroupServiceTest { UserService userService @Autowired - private ImportHelper importHelper + ProcessRoleService processRoleService + + @Autowired + ImportHelper importHelper + + @Autowired + IPetriNetService petriNetService @Autowired TestHelper testHelper - @Test - void groupTest() { + AbstractUser dummy, customer + + @BeforeEach + void init() { testHelper.truncateDbs() def auths = importHelper.createAuthorities(["user": Authority.user, "admin": Authority.admin]) - importHelper.createUser(new com.netgrif.application.engine.adapter.spring.auth.domain.User(firstName: "Dummy", lastName: "User", email: DUMMY_USER_MAIL, username: DUMMY_USER_MAIL, password: "password", state: UserState.ACTIVE), + dummy = importHelper.createUser(new User(firstName: "Dummy", lastName: "User", email: DUMMY_USER_MAIL, username: DUMMY_USER_MAIL, password: "password", state: UserState.ACTIVE), [auths.get("user")] as Authority[], [] as ProcessRole[]) - importHelper.createUser(new com.netgrif.application.engine.adapter.spring.auth.domain.User(firstName: "Customer", lastName: "User", email: CUSTOMER_USER_MAIL, username: CUSTOMER_USER_MAIL, password: "password", state: UserState.ACTIVE), + customer = importHelper.createUser(new User(firstName: "Customer", lastName: "User", email: CUSTOMER_USER_MAIL, username: CUSTOMER_USER_MAIL, password: "password", state: UserState.ACTIVE), [auths.get("user")] as Authority[], [] as ProcessRole[]) - Group customGroup = createGroup() - if (customGroup == null) { - throw new NullPointerException() - } - - List allGroups = findAllGroups() - assert !allGroups.isEmpty() - - List byPredicate = findGroup() - assert !byPredicate.isEmpty() - - Group addedUserGroup = addUser() - assert !addedUserGroup.getMemberIds().isEmpty() - - Group removedUserGroup = removeUser() - assert !removedUserGroup.getMemberIds().isEmpty() } - Group createGroup() { - return groupService.create("CUSTOM_GROUP_1", "CUSTOM_GROUP_1", userService.findUserByUsername(DUMMY_USER_MAIL, null).get()) + @Test + void createGroup() { + groupService.create("CUSTOM_GROUP_1", "CUSTOM_GROUP_1", userService.findUserByUsername(DUMMY_USER_MAIL, null).get()) + Optional groupOpt = groupService.findByIdentifier("CUSTOM_GROUP_1") + assert groupOpt.isPresent() + } - List findGroup() { + @Test + void findGroup() { QGroup qGroup = new QGroup("group") - return groupService.findByPredicate(qGroup.ownerUsername.eq(DUMMY_USER_MAIL), new FullPageRequest()).getContent() + Page groupPage = groupService.findByPredicate(qGroup.ownerUsername.eq(DUMMY_USER_MAIL), Pageable.ofSize(1)) + assert !groupPage.isEmpty() } - List findAllGroups() { - return groupService.findAll() as List + @Test + void addAndRemoveUser() { + QGroup qGroup = new QGroup("group") + Group group = groupService.findByPredicate(qGroup.identifier.eq(DUMMY_USER_MAIL), new FullPageRequest()).getContent().get(0) + group = groupService.addUser(group, userService.findUserByUsername(CUSTOMER_USER_MAIL, null).get()) + assert group.getMemberIds().size() == 2 + group = groupService.removeUser(group, userService.findUserByUsername(CUSTOMER_USER_MAIL, null).get()) + assert group.getMemberIds().size() == 1 } - Group addUser() { - QGroup qGroup = new QGroup("group") - Group group = groupService.findByPredicate(qGroup.identifier.eq("CUSTOM_GROUP_1"), new FullPageRequest()).getContent().get(0) - groupService.addUser(userService.findUserByUsername(CUSTOMER_USER_MAIL, null).get(), group) - groupService.addUser(userService.findUserByUsername(UserConstants.SYSTEM_USER_USERNAME, null).get(), group) - return group + @Test + void addAndRemoveRole() { + ImportPetriNetEventOutcome netWithRoleOutcome = petriNetService.importPetriNet(ImportPetriNetParams.with() + .xmlFile(new FileInputStream("src/test/resources/simple_role.xml")) + .releaseType(VersionType.MAJOR) + .author(userService.getSystem()) + .build()) + ProcessRole role = netWithRoleOutcome.getNet().getRoles().values().find { it.importId == "simple_role"} + Group group = groupService.create("addAndRemoveRole", "Add and remove role test group", dummy) + group = groupService.addRole(group.getStringId(), role.getStringId()) + assert group.getProcessRoles().any {it.getStringId() == role.getStringId()} + group = groupService.removeRole(group.getStringId(), role.getStringId()) + assert !group.getProcessRoles().any() {it.getStringId() == role.getStringId()} } - Group removeUser() { - QGroup qGroup = new QGroup("group") - Group group = groupService.findByPredicate(qGroup.identifier.eq("CUSTOM_GROUP_1"), new FullPageRequest()).getContent().get(0) - groupService.removeUser(userService.findUserByUsername(CUSTOMER_USER_MAIL, null).get(), group) - return group + @Test + void addAndRemoveSubgroup() { + Group group = groupService.findByIdentifier(dummy.getUsername()).orElse(null) + assert group != null + Group subGroup = groupService.create("addAndRemoveSubgroup", "Add and remove role test group", dummy) + + Pair groupPair = groupService.addSubgroup(group.getStringId(), subGroup.getStringId()) + assert groupPair.getFirst().getSubgroupIds().contains(subGroup.getStringId()) + assert groupPair.getSecond().getGroupIds().contains(group.getStringId()) + + groupPair = groupService.removeSubgroup(group.getStringId(), subGroup.getStringId()) + assert !groupPair.getFirst().getSubgroupIds().contains(subGroup.getStringId()) + assert !groupPair.getSecond().getGroupIds().contains(group.getStringId()) } } diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/permissions/ElasticSearchViewPermissionTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/permissions/ElasticSearchViewPermissionTest.groovy index 85faa76a3f7..5da0c91b22b 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/permissions/ElasticSearchViewPermissionTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/permissions/ElasticSearchViewPermissionTest.groovy @@ -105,7 +105,7 @@ class ElasticSearchViewPermissionTest { userAuthority = authorityService.getOrCreate(Authority.user) - testUser = importHelper.createUser(new com.netgrif.application.engine.adapter.spring.auth.domain.User(firstName: "Role", lastName: "User", email: USER_EMAIL, password: "password", state: UserState.ACTIVE), + testUser = importHelper.createUser(new com.netgrif.application.engine.adapter.spring.auth.domain.User(firstName: "Role", lastName: "User", username: USER_EMAIL, email: USER_EMAIL, password: "password", state: UserState.ACTIVE), [userAuthority] as Authority[], [] as ProcessRole[]) } diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/permissions/QueryDSLViewPermissionTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/permissions/QueryDSLViewPermissionTest.groovy index babbe92faa7..4c7b0590e33 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/permissions/QueryDSLViewPermissionTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/permissions/QueryDSLViewPermissionTest.groovy @@ -1,6 +1,7 @@ package com.netgrif.application.engine.permissions import com.netgrif.application.engine.TestHelper +import com.netgrif.application.engine.adapter.spring.auth.domain.User import com.netgrif.application.engine.auth.service.AuthorityService import com.netgrif.application.engine.auth.service.GroupService import com.netgrif.application.engine.auth.service.UserService @@ -98,7 +99,7 @@ class QueryDSLViewPermissionTest { userAuthority = authorityService.getOrCreate(Authority.user) - testUser = importHelper.createUser(new com.netgrif.application.engine.adapter.spring.auth.domain.User(firstName: "Role", lastName: "User", email: USER_EMAIL, password: "password", state: UserState.ACTIVE), + testUser = importHelper.createUser(new User(firstName: "Role", lastName: "User", username: USER_EMAIL, email: USER_EMAIL, password: "password", state: UserState.ACTIVE), [userAuthority] as Authority[], [] as ProcessRole[]) } diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/dataset/FileFieldTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/dataset/FileFieldTest.groovy index 81a7a63cf0a..e850b4dc045 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/dataset/FileFieldTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/dataset/FileFieldTest.groovy @@ -134,10 +134,7 @@ class FileFieldTest { def auths = importHelper.createAuthorities(["user": Authority.user, "admin": Authority.admin]) - def adminUser = importHelper.createUser(new com.netgrif.application.engine.adapter.spring.auth.domain.User(firstName: "Admin", lastName: "User", username: UserConstants.ADMIN_USER_USERNAME, email: UserConstants.ADMIN_USER_EMAIL, password: "password", state: UserState.ACTIVE), - [auths.get("admin")] as Authority[], -// [] as Group[], - [] as ProcessRole[]) + def adminUser = userService.findUserByUsername(UserConstants.ADMIN_USER_USERNAME, null).get() auth = new UsernamePasswordAuthenticationToken(ActorTransformer.toLoggedUser(adminUser), "password", [auths.get("admin")] as List) auth.setDetails(new WebAuthenticationDetails(new MockHttpServletRequest())) diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/dataset/FileListFieldTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/dataset/FileListFieldTest.groovy index b49f1e0486a..936ed60c18b 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/dataset/FileListFieldTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/dataset/FileListFieldTest.groovy @@ -134,10 +134,7 @@ class FileListFieldTest { def auths = importHelper.createAuthorities(["user": Authority.user, "admin": Authority.admin]) - def adminUser = importHelper.createUser(new com.netgrif.application.engine.adapter.spring.auth.domain.User(firstName: "Admin", lastName: "User", username: UserConstants.ADMIN_USER_USERNAME, email: USER_EMAIL, password: "password", state: UserState.ACTIVE), - [auths.get("admin")] as Authority[], -// [] as Group[], - [] as ProcessRole[]) + def adminUser = userService.findUserByUsername(UserConstants.ADMIN_USER_USERNAME, null).get() auth = new UsernamePasswordAuthenticationToken(ActorTransformer.toLoggedUser(adminUser), "password", [auths.get("admin")] as List) auth.setDetails(new WebAuthenticationDetails(new MockHttpServletRequest())) diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/roles/ProcessRoleTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/roles/ProcessRoleTest.groovy index 4b971b991e9..90c2d30d336 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/roles/ProcessRoleTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/roles/ProcessRoleTest.groovy @@ -97,7 +97,7 @@ class ProcessRoleTest { def auths = importHelper.createAuthorities(["user": Authority.user, "admin": Authority.admin]) def processRoles = userProcessRoleRepository.findAllByProcessId(this.netId, Pageable.unpaged()).content - AbstractUser viewUser = importHelper.createUser(new User(firstName: "Test", lastName: "Integration", email: USER_EMAIL_VIEW, password: "password", state: UserState.ACTIVE), + AbstractUser viewUser = importHelper.createUser(new User(firstName: "Test", lastName: "Integration", username: USER_EMAIL_VIEW, email: USER_EMAIL_VIEW, password: "password", state: UserState.ACTIVE), [auths.get("user")] as Authority[], [processRoles.find { it.getStringId() == net.getNet().roles.values().find { @@ -105,11 +105,11 @@ class ProcessRoleTest { }.stringId }] as ProcessRole[]) - AbstractUser performUser = importHelper.createUser(new User(firstName: "Test", lastName: "Integration", email: USER_EMAIL_PERFORM, password: "password", state: UserState.ACTIVE), + AbstractUser performUser = importHelper.createUser(new User(firstName: "Test", lastName: "Integration", username: USER_EMAIL_PERFORM, email: USER_EMAIL_PERFORM, password: "password", state: UserState.ACTIVE), [auths.get("user")] as Authority[], [processRoles.find { it.getStringId() == net.getNet().roles.values().find { it.name.defaultValue == "Perform" }.stringId }] as ProcessRole[]) - AbstractUser bothUser = importHelper.createUser(new User(firstName: "Test", lastName: "Integration", email: USER_EMAIL_BOTH, password: "password", state: UserState.ACTIVE), + AbstractUser bothUser = importHelper.createUser(new User(firstName: "Test", lastName: "Integration", username: USER_EMAIL_BOTH, email: USER_EMAIL_BOTH, password: "password", state: UserState.ACTIVE), [auths.get("user")] as Authority[], [processRoles.find { it.getStringId() == net.getNet().roles.values().find { it.name.defaultValue == "View" }.stringId }, processRoles.find { it.getStringId() == net.getNet().roles.values().find { it.name.defaultValue == "Perform" }.stringId }] as ProcessRole[]) diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/CachePetriNetServiceTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/CachePetriNetServiceTest.groovy index 2d2fa7d1f91..dfb2a68d55e 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/CachePetriNetServiceTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/CachePetriNetServiceTest.groovy @@ -60,7 +60,7 @@ class CachePetriNetServiceTest { void setup() { testHelper.truncateDbs() def auths = importHelper.createAuthorities(["user": Authority.user, "admin": Authority.admin]) - importHelper.createUser(new com.netgrif.application.engine.adapter.spring.auth.domain.User(firstName: "Customer", lastName: "User", email: CUSTOMER_USER_MAIL, password: "password", state: UserState.ACTIVE), + importHelper.createUser(new com.netgrif.application.engine.adapter.spring.auth.domain.User(firstName: "Customer", lastName: "User", username: CUSTOMER_USER_MAIL, email: CUSTOMER_USER_MAIL, password: "password", state: UserState.ACTIVE), [auths.get("user")] as Authority[], [] as ProcessRole[]) } 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 d0b1d59ed1b..5bd87b4d7a4 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 @@ -2,6 +2,7 @@ package com.netgrif.application.engine.petrinet.web import com.netgrif.application.engine.TestHelper import com.netgrif.application.engine.adapter.spring.auth.domain.AuthorityImpl +import com.netgrif.application.engine.adapter.spring.auth.domain.User import com.netgrif.application.engine.auth.service.UserService import com.netgrif.application.engine.ipc.TaskApiTest import com.netgrif.application.engine.objects.auth.domain.ActorTransformer @@ -33,6 +34,7 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders import org.springframework.web.context.WebApplicationContext import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status @@ -98,7 +100,7 @@ class PetriNetControllerTest { def auths = importHelper.createAuthorities(["user": Authority.user, "admin": Authority.admin]) - def simpleUser = importHelper.createUser(new com.netgrif.application.engine.adapter.spring.auth.domain.User(firstName: "Role", lastName: "User", email: USER_EMAIL, password: "password", state: UserState.ACTIVE), + def simpleUser = importHelper.createUser(new User(firstName: "Role", lastName: "User", username: USER_EMAIL, email: USER_EMAIL, password: "password", state: UserState.ACTIVE), [auths.get("user")] as Authority[], // [] as Group[], [] as ProcessRole[]) @@ -106,7 +108,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 com.netgrif.application.engine.adapter.spring.auth.domain.User(firstName: "Admin", lastName: "User", email: ADMIN_EMAIL, password: "password", state: UserState.ACTIVE), + def adminUser = importHelper.createUser(new User(firstName: "Admin", lastName: "User", username: ADMIN_EMAIL, email: ADMIN_EMAIL, password: "password", state: UserState.ACTIVE), [auths.get("admin")] as Authority[], // [] as Group[], [] as ProcessRole[]) diff --git a/application-engine/src/test/java/com/netgrif/application/engine/auth/service/RegistrationServiceUtilityTest.java b/application-engine/src/test/java/com/netgrif/application/engine/auth/service/RegistrationServiceUtilityTest.java index 533cf4958f4..afdef1093b0 100644 --- a/application-engine/src/test/java/com/netgrif/application/engine/auth/service/RegistrationServiceUtilityTest.java +++ b/application-engine/src/test/java/com/netgrif/application/engine/auth/service/RegistrationServiceUtilityTest.java @@ -164,7 +164,7 @@ void createNewUserInitializesInactiveUserWithRolesAndGroups() { verify(userService).addDefaultAuthorities(any(AbstractUser.class)); verify(userService).addRole(any(AbstractUser.class), eq("role-a")); verify(userService).addRole(any(AbstractUser.class), eq(defaultRole.getStringId())); - verify(groupService).addUser(saved, "group-a"); + verify(groupService).addUser("group-a", saved); } @Test diff --git a/application-engine/src/test/java/com/netgrif/application/engine/auth/web/AuthenticationControllerTest.java b/application-engine/src/test/java/com/netgrif/application/engine/auth/web/AuthenticationControllerTest.java index 91646774334..36a86995a60 100644 --- a/application-engine/src/test/java/com/netgrif/application/engine/auth/web/AuthenticationControllerTest.java +++ b/application-engine/src/test/java/com/netgrif/application/engine/auth/web/AuthenticationControllerTest.java @@ -7,6 +7,7 @@ import com.netgrif.application.engine.auth.web.requestbodies.ChangePasswordRequest; import com.netgrif.application.engine.auth.web.requestbodies.NewUserRequest; import com.netgrif.application.engine.auth.web.requestbodies.RegistrationRequest; +import com.netgrif.application.engine.auth.web.responsebodies.UserDto; import com.netgrif.application.engine.configuration.properties.SecurityConfigurationProperties; import com.netgrif.application.engine.mail.interfaces.IMailAttemptService; import com.netgrif.application.engine.mail.interfaces.IMailService; @@ -312,8 +313,8 @@ void changePasswordUpdatesSelfAndReloadsSecurityContext() { @Test void loginUsesSecurityContextAuthentication() { - com.netgrif.application.engine.auth.web.responsebodies.User responseUser = - org.mockito.Mockito.mock(com.netgrif.application.engine.auth.web.responsebodies.User.class); + UserDto responseUser = + org.mockito.Mockito.mock(UserDto.class); when(loggedUser.getStringId()).thenReturn("self"); when(userService.findById("self", null)).thenReturn(user); when(userFactory.getUser(user, Locale.ENGLISH)).thenReturn(responseUser); diff --git a/application-engine/src/test/java/com/netgrif/application/engine/auth/web/UserControllerTest.java b/application-engine/src/test/java/com/netgrif/application/engine/auth/web/UserControllerTest.java index adaeb2d6ed5..7246e219d2d 100644 --- a/application-engine/src/test/java/com/netgrif/application/engine/auth/web/UserControllerTest.java +++ b/application-engine/src/test/java/com/netgrif/application/engine/auth/web/UserControllerTest.java @@ -6,7 +6,7 @@ import com.netgrif.application.engine.auth.web.requestbodies.UserCreateRequest; 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.auth.web.responsebodies.UserDto; 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; @@ -103,14 +103,14 @@ void savePreferencesStoresPreferencesForLoggedUser() { @Test void createUserReturnsCreatedUserWhenRealmExistsAndUsernameIsFree() { AbstractUser domainUser = domainUser("john"); - User responseUser = new User(domainUser); + UserDto responseUser = new UserDto(domainUser); UserCreateRequest request = createRequest("john"); when(realmService.getRealmById("realm")).thenReturn(Optional.of(realm("realm"))); when(userService.findUserByUsername("john", "realm")).thenReturn(Optional.empty()); when(userService.createUser("john", "john@example.com", "John", "User", "secret", "realm")).thenReturn(domainUser); when(userFactory.getUser(domainUser, Locale.ENGLISH)).thenReturn(responseUser); - ResponseEntity response = controller.createUser("realm", request, Locale.ENGLISH); + ResponseEntity response = controller.createUser("realm", request, Locale.ENGLISH); assertEquals(HttpStatus.CREATED, response.getStatusCode()); assertSame(responseUser, response.getBody()); @@ -133,13 +133,13 @@ void createUserReportsBadRealmConflictAndUnexpectedFailure() { @Test void getAllUsersTransformsUsersWhenRealmExists() { AbstractUser domainUser = domainUser("john"); - User responseUser = new User(domainUser); + UserDto responseUser = new UserDto(domainUser); Pageable pageable = PageRequest.of(0, 10); when(realmService.getRealmById("realm")).thenReturn(Optional.of(realm("realm"))); when(userService.findAllUsers("realm", pageable)).thenReturn(new PageImpl<>(List.of(domainUser), pageable, 1)); when(userFactory.getUser(domainUser, Locale.ENGLISH)).thenReturn(responseUser); - ResponseEntity> response = controller.getAllUsers("realm", pageable, Locale.ENGLISH); + ResponseEntity> response = controller.getAllUsers("realm", pageable, Locale.ENGLISH); assertEquals(HttpStatus.OK, response.getStatusCode()); assertEquals(1, response.getBody().getTotalElements()); @@ -151,14 +151,14 @@ void getAllUsersTransformsUsersWhenRealmExists() { @Test void getLoggedUserHandlesSuccessMissingAndBadIds() { AbstractUser domainUser = domainUser("john"); - User responseUser = new User(domainUser); + UserDto responseUser = new UserDto(domainUser); when(authentication.getPrincipal()).thenReturn(loggedUser); when(loggedUser.getStringId()).thenReturn("john-id"); when(loggedUser.getRealmId()).thenReturn("realm"); when(userService.findById("john-id", "realm")).thenReturn(domainUser); when(userFactory.getUser(domainUser, Locale.ENGLISH)).thenReturn(responseUser); - ResponseEntity ok = controller.getLoggedUser(authentication, Locale.ENGLISH); + ResponseEntity ok = controller.getLoggedUser(authentication, Locale.ENGLISH); assertEquals(HttpStatus.OK, ok.getStatusCode()); assertSame(responseUser, ok.getBody()); @@ -176,7 +176,7 @@ void getLoggedUserHandlesSuccessMissingAndBadIds() { @Test void searchMapsRoleStringsToProcessResourceIds() { AbstractUser domainUser = domainUser("john"); - User responseUser = new User(domainUser); + UserDto responseUser = new UserDto(domainUser); Pageable pageable = PageRequest.of(0, 10); String roleId = new ProcessResourceId().toString(); String negativeRoleId = new ProcessResourceId().toString(); @@ -189,7 +189,7 @@ void searchMapsRoleStringsToProcessResourceIds() { .thenReturn(new PageImpl<>(List.of(domainUser), pageable, 1)); when(userFactory.getUser(domainUser, Locale.ENGLISH)).thenReturn(responseUser); - ResponseEntity> response = controller.search(query, pageable, authentication, Locale.ENGLISH); + ResponseEntity> response = controller.search(query, pageable, authentication, Locale.ENGLISH); assertEquals(HttpStatus.OK, response.getStatusCode()); assertSame(responseUser, response.getBody().getContent().getFirst()); @@ -198,7 +198,7 @@ void searchMapsRoleStringsToProcessResourceIds() { @Test void getUserAllowsAdminOrSelfAndRejectsForeignOrInvalidIds() { AbstractUser domainUser = domainUser("john"); - User responseUser = new User(domainUser); + UserDto responseUser = new UserDto(domainUser); when(userService.getLoggedUserFromContext()).thenReturn(loggedUser); when(loggedUser.isAdmin()).thenReturn(false); when(loggedUser.getId()).thenReturn(new ObjectId("64b000000000000000000001")); @@ -209,7 +209,7 @@ void getUserAllowsAdminOrSelfAndRejectsForeignOrInvalidIds() { when(loggedUser.isAdmin()).thenReturn(true); when(userService.findById("64b000000000000000000001", "realm")).thenReturn(domainUser); when(userFactory.getUser(domainUser, Locale.ENGLISH)).thenReturn(responseUser); - ResponseEntity admin = controller.getUser("realm", "64b000000000000000000001", Locale.ENGLISH); + ResponseEntity admin = controller.getUser("realm", "64b000000000000000000001", Locale.ENGLISH); assertEquals(HttpStatus.OK, admin.getStatusCode()); assertSame(responseUser, admin.getBody()); diff --git a/application-engine/src/test/java/com/netgrif/application/engine/workflow/web/VariableArcsTest.java b/application-engine/src/test/java/com/netgrif/application/engine/workflow/web/VariableArcsTest.java index 750af39c7c3..d754f2cd295 100644 --- a/application-engine/src/test/java/com/netgrif/application/engine/workflow/web/VariableArcsTest.java +++ b/application-engine/src/test/java/com/netgrif/application/engine/workflow/web/VariableArcsTest.java @@ -133,6 +133,7 @@ public void before() throws Exception { user.setCredential("password", passwordCredential); user.setState(UserState.ACTIVE); user.setEmail("VariableArcsTest@test.com"); + user.setUsername("VariableArcsTest@test.com"); testUser = importHelper.createUser(user, new Authority[]{authorityService.getOrCreate(Authority.user)}, new ProcessRole[]{}); diff --git a/application-engine/src/test/resources/simple_role.xml b/application-engine/src/test/resources/simple_role.xml new file mode 100644 index 00000000000..eb843ee34f0 --- /dev/null +++ b/application-engine/src/test/resources/simple_role.xml @@ -0,0 +1,14 @@ + + simple_role + 1.0.0 + SMR + Simple Role + device_hub + true + true + false + + simple_role + Simple role + + \ 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 374d6473b55..edd72f9bc77 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,6 +1,7 @@ package com.netgrif.application.engine.objects.auth.domain; import java.time.Duration; +import java.util.Set; /** * Transformer class responsible for converting between different user/actor representations @@ -27,6 +28,14 @@ public interface LoggedUserFactory { * @return newly created LoggedUser instance */ LoggedUser create(); + + default void resolveProcessRoles(AbstractActor user) {}; + + default void resolveProcessRolesRecursively(AbstractActor actor, Set processRoleIds) {}; + + default void resolveAuthorities(AbstractActor user) {}; + + default void resolveAuthoritiesRecursively(AbstractActor actor, Set authorityIds) {}; } /** @@ -62,6 +71,8 @@ public static LoggedUser toLoggedUser(AbstractUser user) { loggedUser.setProcessRoles(user.getProcessRoles()); loggedUser.setAttributes(user.getAttributes()); loggedUser.setGroupIds(user.getGroupIds()); + factory.resolveProcessRoles(loggedUser); + factory.resolveAuthorities(loggedUser); return loggedUser; } diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/domain/Group.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/domain/Group.java index 7c7ba44f1ed..707287100a9 100644 --- a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/domain/Group.java +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/domain/Group.java @@ -1,5 +1,7 @@ package com.netgrif.application.engine.objects.auth.domain; +import com.netgrif.application.engine.objects.annotations.EnsureCollection; +import com.netgrif.application.engine.objects.annotations.Indexed; import com.querydsl.core.annotations.QueryEntity; import lombok.Getter; import lombok.Setter; @@ -16,30 +18,35 @@ */ @Getter @QueryEntity +@EnsureCollection public abstract class Group extends AbstractActor implements Serializable { /** * The unique identifier of the group. */ @Setter + @Indexed private String identifier; /** * The display name of the group shown in the user interface. */ @Setter + @Indexed private String displayName; /** * The unique identifier of the group owner. */ @Setter + @Indexed private String ownerId; /** * The username of the group owner. */ @Setter + @Indexed private String ownerUsername; /** @@ -169,4 +176,18 @@ public void removeSubgroupId(String groupId) { public void setSubgroupIds(Set subgroupIds) { this.subgroupIds = subgroupIds == null ? new HashSet<>() : new HashSet<>(subgroupIds); } + + public Set getMemberIds() { + if (memberIds == null) { + memberIds = new HashSet<>(); + } + return memberIds; + } + + public Set getSubgroupIds() { + if (subgroupIds == null) { + subgroupIds = new HashSet<>(); + } + return subgroupIds; + } } \ No newline at end of file diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/dto/GroupSearchDto.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/dto/GroupSearchDto.java deleted file mode 100644 index a8560fabf19..00000000000 --- a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/dto/GroupSearchDto.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.netgrif.application.engine.objects.auth.dto; - -import lombok.Data; - -@Data -public class GroupSearchDto { - private String fullText; - private String realmId; -} diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/dto/request/group/CreateGroupRequestDto.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/dto/request/group/CreateGroupRequestDto.java new file mode 100644 index 00000000000..de899412cd5 --- /dev/null +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/dto/request/group/CreateGroupRequestDto.java @@ -0,0 +1,14 @@ +package com.netgrif.application.engine.objects.dto.request.group; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; + +import java.io.Serializable; + +public record CreateGroupRequestDto( + @NotBlank(message = "Title is mandatory") String displayName, + @NotBlank(message = "Realm ID is mandatory") String realmId, + @NotBlank(message = "Identifier is mandatory") String identifier, + @Pattern(regexp = "^[a-fA-F0-9]{24}$") String ownerId) implements Serializable { + +} diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/dto/request/group/GroupSearchRequestDto.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/dto/request/group/GroupSearchRequestDto.java new file mode 100644 index 00000000000..aedb8dd2335 --- /dev/null +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/dto/request/group/GroupSearchRequestDto.java @@ -0,0 +1,21 @@ +package com.netgrif.application.engine.objects.dto.request.group; + +import com.netgrif.application.engine.objects.auth.domain.Group; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.Set; + +/** + * DTO for {@link Group} search request + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class GroupSearchRequestDto { + private Set ids; + private String realmId; + private String fullText; +} \ No newline at end of file diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/dto/request/group/UpdateGroupRequestDto.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/dto/request/group/UpdateGroupRequestDto.java new file mode 100644 index 00000000000..d66181b4f15 --- /dev/null +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/dto/request/group/UpdateGroupRequestDto.java @@ -0,0 +1,4 @@ +package com.netgrif.application.engine.objects.dto.request.group; + +public record UpdateGroupRequestDto(String id, String identifier, String displayName) { +} diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/dto/response/authority/AuthorityDto.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/dto/response/authority/AuthorityDto.java new file mode 100644 index 00000000000..a6c46f88ac3 --- /dev/null +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/dto/response/authority/AuthorityDto.java @@ -0,0 +1,17 @@ +package com.netgrif.application.engine.objects.dto.response.authority; + +import com.netgrif.application.engine.objects.auth.domain.Authority; +import org.bson.types.ObjectId; + +import java.io.Serializable; + +/** + * DTO for {@link Authority} + */ +public record AuthorityDto(ObjectId id, String name, String stringId) implements Serializable { + + public static AuthorityDto fromAuthority(Authority authority) { + return new AuthorityDto(authority.get_id(), authority.getName(), authority.getStringId()); + } + +} \ No newline at end of file diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/dto/response/group/GroupDto.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/dto/response/group/GroupDto.java new file mode 100644 index 00000000000..6b42e67e096 --- /dev/null +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/dto/response/group/GroupDto.java @@ -0,0 +1,30 @@ +package com.netgrif.application.engine.objects.dto.response.group; + +import com.netgrif.application.engine.objects.auth.domain.Group; +import com.netgrif.application.engine.objects.dto.response.authority.AuthorityDto; +import com.netgrif.application.engine.objects.dto.response.petrinet.ProcessRoleDto; + +import java.io.Serializable; +import java.util.Locale; +import java.util.Set; +import java.util.stream.Collectors; + +public record GroupDto(String id, + String displayName, + String identifier, + String ownerUsername, + Set authoritySet, + Set processRoles, + Set groupIds, + Set subGroupIds) implements Serializable { + + public static GroupDto fromGroup(Group group, Locale locale) { + return new GroupDto(group.getStringId(), group.getDisplayName(), group.getIdentifier(), group.getOwnerUsername(), + group.getAuthoritySet().stream().map(AuthorityDto::fromAuthority).collect(Collectors.toSet()), + group.getProcessRoles().stream().map(processRole -> new ProcessRoleDto(processRole, locale)).collect(Collectors.toSet()), + group.getGroupIds(), + group.getSubgroupIds() + ); + } + +} diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/dto/response/petrinet/ProcessRoleDto.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/dto/response/petrinet/ProcessRoleDto.java new file mode 100644 index 00000000000..5c820c76913 --- /dev/null +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/dto/response/petrinet/ProcessRoleDto.java @@ -0,0 +1,28 @@ +package com.netgrif.application.engine.objects.dto.response.petrinet; + +import com.netgrif.application.engine.objects.petrinet.domain.roles.ProcessRole; + +import java.io.Serializable; +import java.util.Locale; + +public record ProcessRoleDto(String stringId, String name, String description, String importId, String netImportId, + String netVersion, String netStringId, boolean global) implements Serializable { + + /** + * This constructor doesn't set attributes regarding the Petri net. + *

+ * Use the ProcessRoleFactory to create instances that have these attributes set. + */ + public ProcessRoleDto(ProcessRole role, Locale locale) { + this(role.getStringId(), role.getLocalisedName(locale), role.getDescription(), role.getImportId(), null, null, null, role.isGlobal()); + } + + public ProcessRoleDto(ProcessRole role, Locale locale, String netImportId, + String netVersion, String netStringId) { + this(role.getStringId(), role.getLocalisedName(locale), role.getDescription(), role.getImportId(),netImportId, netVersion, netStringId, role.isGlobal()); + } + + public ProcessRoleDto(String id, String name, String description, boolean global) { + this(id, name, description, null, null, null, null, global); + } +} diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/auth/service/DefaultLoggedUserFactory.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/auth/service/DefaultLoggedUserFactory.java deleted file mode 100644 index a27c227fd02..00000000000 --- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/auth/service/DefaultLoggedUserFactory.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.netgrif.application.engine.adapter.spring.auth.service; - -import com.netgrif.application.engine.adapter.spring.auth.domain.LoggedUserImpl; -import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; -import com.netgrif.application.engine.objects.auth.domain.LoggedUser; -import org.springframework.stereotype.Component; - -@Component -public class DefaultLoggedUserFactory implements ActorTransformer.LoggedUserFactory { - - @Override - public LoggedUser create() { - return new LoggedUserImpl(); - } -} diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/configuration/LoggedUserConfiguration.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/configuration/LoggedUserConfiguration.java deleted file mode 100644 index 0e60422b87b..00000000000 --- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/configuration/LoggedUserConfiguration.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.netgrif.application.engine.adapter.spring.configuration; - -import com.netgrif.application.engine.adapter.spring.auth.service.DefaultLoggedUserFactory; -import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; -import jakarta.annotation.PostConstruct; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class LoggedUserConfiguration { - - @PostConstruct - public void initializeLoggedUserFactory() { - ActorTransformer.setLoggedUserFactory(new DefaultLoggedUserFactory()); - } -} diff --git a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/config/LoggedUserConfiguration.java b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/config/LoggedUserConfiguration.java new file mode 100644 index 00000000000..41e24bb144b --- /dev/null +++ b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/config/LoggedUserConfiguration.java @@ -0,0 +1,24 @@ +package com.netgrif.application.engine.auth.config; + +import com.netgrif.application.engine.adapter.spring.petrinet.service.ProcessRoleService; +import com.netgrif.application.engine.auth.service.AuthorityService; +import com.netgrif.application.engine.auth.service.DefaultLoggedUserFactory; +import com.netgrif.application.engine.auth.service.GroupService; +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 GroupService groupService; + private final ProcessRoleService processRoleService; + private final AuthorityService authorityService; + + @PostConstruct + public void initializeLoggedUserFactory() { + ActorTransformer.setLoggedUserFactory(new DefaultLoggedUserFactory(groupService, processRoleService, authorityService)); + } +} diff --git a/application-engine/src/main/java/com/netgrif/application/engine/auth/service/AuthorizationService.java b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/AuthorizationServiceImpl.java similarity index 81% rename from application-engine/src/main/java/com/netgrif/application/engine/auth/service/AuthorizationService.java rename to nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/AuthorizationServiceImpl.java index 260f705d5e4..8c44f423047 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/auth/service/AuthorizationService.java +++ b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/AuthorizationServiceImpl.java @@ -1,12 +1,11 @@ package com.netgrif.application.engine.auth.service; -import com.netgrif.application.engine.auth.service.interfaces.IAuthorizationService; import com.netgrif.application.engine.objects.auth.domain.LoggedUser; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service -public class AuthorizationService implements IAuthorizationService { +public class AuthorizationServiceImpl implements AuthorizationService { @Autowired private UserService userService; diff --git a/nae-user-ce/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 new file mode 100644 index 00000000000..c8919a1bfa5 --- /dev/null +++ b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/DefaultLoggedUserFactory.java @@ -0,0 +1,80 @@ +package com.netgrif.application.engine.auth.service; + +import com.netgrif.application.engine.adapter.spring.auth.domain.LoggedUserImpl; +import com.netgrif.application.engine.adapter.spring.petrinet.service.ProcessRoleService; +import com.netgrif.application.engine.objects.auth.domain.*; +import com.netgrif.application.engine.objects.petrinet.domain.roles.ProcessRole; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component; + +import java.util.Set; + +@Component +@RequiredArgsConstructor +public class DefaultLoggedUserFactory implements ActorTransformer.LoggedUserFactory { + + private final GroupService groupService; + + private final ProcessRoleService processRoleService; + + private final AuthorityService authorityService; + + @Override + public LoggedUser create() { + return new LoggedUserImpl(); + } + + @Override + public void resolveProcessRoles(AbstractActor user) { + Set processRoleIds = user.getProcessRoleIds(); + user.getGroupIds().forEach(groupId -> { + resolveProcessRolesRecursively(groupService.findById(groupId), processRoleIds); + }); + + user.getProcessRoleIds().forEach(processRoleId -> { + ProcessRole role = processRoleService.findById(processRoleId); + if (role != null) { + user.getProcessRoles().add(role); + } + }); + } + + @Override + public void resolveProcessRolesRecursively(AbstractActor actor, Set processRoleIds) { + processRoleIds.addAll(actor.getProcessRoleIds()); + if (!actor.getGroupIds().isEmpty()) { + actor.getGroupIds().forEach(groupId -> { + Group group = groupService.findById(groupId); + processRoleIds.addAll(group.getProcessRoleIds()); + resolveProcessRolesRecursively(group, processRoleIds); + }); + } + } + + @Override + public void resolveAuthorities(AbstractActor user) { + Set authorityIds = user.getAuthorityIds(); + user.getGroupIds().forEach(groupId -> { + resolveAuthoritiesRecursively(groupService.findById(groupId), authorityIds); + }); + + user.getAuthorityIds().forEach(authorityId -> { + Authority authority = authorityService.getOne(authorityId); + if (authority != null) { + user.getAuthoritySet().add(authority); + } + }); + } + + @Override + public void resolveAuthoritiesRecursively(AbstractActor actor, Set authorityIds) { + authorityIds.addAll(actor.getAuthorityIds()); + if (!actor.getGroupIds().isEmpty()) { + actor.getGroupIds().forEach(groupId -> { + Group group = groupService.findById(groupId); + authorityIds.addAll(group.getAuthorityIds()); + resolveAuthoritiesRecursively(group, authorityIds); + }); + } + } +} diff --git a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/GroupServiceImpl.java b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/GroupServiceImpl.java index 513e96090a4..2657328bb00 100644 --- a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/GroupServiceImpl.java +++ b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/GroupServiceImpl.java @@ -1,14 +1,19 @@ package com.netgrif.application.engine.auth.service; +import com.netgrif.application.engine.adapter.spring.petrinet.service.ProcessRoleService; import com.netgrif.application.engine.adapter.spring.utils.PaginationProperties; import com.netgrif.application.engine.auth.config.GroupConfigurationProperties; import com.netgrif.application.engine.auth.provider.CollectionNameProvider; import com.netgrif.application.engine.auth.repository.GroupRepository; 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.Group; -import com.netgrif.application.engine.objects.auth.dto.GroupSearchDto; import com.netgrif.application.engine.objects.common.ResourceNotFoundException; import com.netgrif.application.engine.objects.common.ResourceNotFoundExceptionCode; +import com.netgrif.application.engine.objects.dto.request.group.GroupSearchRequestDto; +import com.netgrif.application.engine.objects.petrinet.domain.roles.ProcessRole; +import com.netgrif.application.engine.objects.workflow.domain.ProcessResourceId; +import org.springframework.context.annotation.Lazy; import org.springframework.data.domain.PageImpl; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; @@ -21,6 +26,7 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.util.Pair; +import org.springframework.util.Assert; import java.time.LocalDateTime; import java.util.*; @@ -46,6 +52,8 @@ public class GroupServiceImpl implements GroupService { private MongoTemplate mongoTemplate; + private ProcessRoleService processRoleService; + @Autowired public void setCollectionNameProvider(CollectionNameProvider collectionNameProvider) { this.collectionNameProvider = collectionNameProvider; @@ -81,6 +89,12 @@ public void setPaginationProperties(PaginationProperties paginationProperties) { this.paginationProperties = paginationProperties; } + @Lazy + @Autowired + public void setProcessRoleService(ProcessRoleService processRoleService) { + this.processRoleService = processRoleService; + } + @Override public void delete(Group group) { if (!groupRepository.existsById(group.getStringId())) { @@ -122,7 +136,11 @@ public void delete(Group group) { @Override public Group save(Group group) { - log.debug("Saving group: [{}]", group.getStringId()); + if (groupRepository.existsById(group.getStringId())) { + log.info("Updating group: [{}]", group.getIdentifier()); + } else { + log.info("Saving new group: [{}]", group.getIdentifier()); + } group.setModifiedAt(LocalDateTime.now()); return groupRepository.save(group); } @@ -173,8 +191,10 @@ public Optional findByIdentifier(String identifier) { @Override public Group getDefaultSystemGroup() { - if (defaultSystemGroup == null) { + if (!groupRepository.existsByIdentifier(groupConfigurationProperties.getDefaultGroupIdentifier())) { defaultSystemGroup = create(groupConfigurationProperties.getDefaultGroupIdentifier(), groupConfigurationProperties.getDefaultGroupTitle(), userService.getSystem()); + } else if (defaultSystemGroup == null) { + defaultSystemGroup = findByIdentifier(groupConfigurationProperties.getDefaultGroupIdentifier()).orElseThrow(() -> new IllegalStateException("Default system group does not exist")); } return defaultSystemGroup; } @@ -191,6 +211,12 @@ public Group create(AbstractUser groupOwner) { @Override public Group create(String identifier, String title, AbstractUser groupOwner) { + if (identifier == null || identifier.isBlank()) { + throw new IllegalArgumentException("Group identifier cannot be null or blank."); + } + if (groupRepository.existsByIdentifier(identifier)) { + throw new IllegalArgumentException("Group with identifier [%s] already exists.".formatted(identifier)); + } log.info("Creating default group for user: [{}]", groupOwner.getStringId()); Group group = new com.netgrif.application.engine.adapter.spring.auth.domain.Group(identifier, groupOwner.getRealmId()); group.setOwnerId(groupOwner.getStringId()); @@ -224,28 +250,48 @@ public Group getDefaultUserGroup(AbstractUser user) { @Override public void addUserToDefaultSystemGroup(AbstractUser user) { log.info("Adding user [{}] to default group", user.getStringId()); - addUser(user, getDefaultSystemGroup()); + addUser(getDefaultSystemGroup(), user); + } + + @Override + public Group assignUsersToGroup(String groupId, Set userIds) { + userIds = userIds == null ? new HashSet<>() : userIds; + Group group = this.findById(groupId); + Set currentGroupMemberIds = group.getMemberIds(); + + Set removableMemberIds = new HashSet<>(currentGroupMemberIds); + removableMemberIds.removeAll(userIds); + + Set newMemberIds = new HashSet<>(userIds); + newMemberIds.removeAll(currentGroupMemberIds); + + removableMemberIds.forEach(toBeRemovedId -> removeUser(group, userService.findById(toBeRemovedId, group.getRealmId()))); + newMemberIds.forEach(toBeAddedId -> addUser(group, userService.findById(toBeAddedId, group.getRealmId()))); + return group; } @Override - public Group addUser(String userId, String groupId, String realmId) { - return addUser(userService.findById(userId, realmId), groupId); + public Group addUser(String groupId, String userId, String realmId) { + return addUser(groupId, userService.findById(userId, realmId)); } @Override - public Group addUser(String userId, Group group, String realmId) { + public Group addUser(Group group, String userId, String realmId) { AbstractUser user = userService.findById(userId, realmId); - return addUser(user, group); + return addUser(group, user); } @Override - public Group addUser(AbstractUser user, String groupIdentifier) { - Group group = findByIdentifier(groupIdentifier).orElseThrow(() -> new IllegalArgumentException("Group with identifier [%s] not found. ".formatted(groupIdentifier))); - return addUser(user, group); + public Group addUser(String groupId, AbstractUser user) { + Group group = findById(groupId); + return addUser(group, user); } @Override - public Group addUser(AbstractUser user, Group group) { + public Group addUser(Group group, AbstractUser user) { + Assert.notNull(user, "User cannot be null"); + Assert.notNull(group, "Group cannot be null"); + log.info("Adding user [{}] to group [{}]", user.getStringId(), group.getStringId()); user.addGroupId(group.getStringId()); group.addMemberId(user.getStringId()); @@ -254,13 +300,21 @@ public Group addUser(AbstractUser user, Group group) { } @Override - public Group removeUser(AbstractUser user, String groupIdentifier) { - Group group = findByIdentifier(groupIdentifier).orElseThrow(() -> new IllegalArgumentException("Group with identifier [%s] not found. ".formatted(groupIdentifier))); - return removeUser(user, group); + public Group removeUser(String groupId, String userId, String realmId) { + return removeUser(groupId, userService.findById(userId, realmId)); + } + + @Override + public Group removeUser(String groupId, AbstractUser user) { + Group group = findById(groupId); + return removeUser(group, user); } @Override - public Group removeUser(AbstractUser user, Group group) { + public Group removeUser(Group group, AbstractUser user) { + Assert.notNull(user, "User cannot be null"); + Assert.notNull(group, "Group cannot be null"); + log.info("Removing user [{}] from group [{}]", user.getStringId(), group.getStringId()); user.removeGroupId(group.getStringId()); group.removeMemberId(user.getStringId()); @@ -299,19 +353,75 @@ public Page findAllFromRealmIn(Collection realmIds, Pageable page } @Override - public Group assignAuthority(String groupId, String authorityId) { + public Group assignAuthorities(String groupId, Set authorityIds) { + Group group = this.findById(groupId); + Set currentAuthorityIds = group.getAuthorityIds(); + + Set removableAuthorityIds = new HashSet<>(currentAuthorityIds); + removableAuthorityIds.removeAll(authorityIds); + + Set newAuthorityIds = new HashSet<>(authorityIds); + newAuthorityIds.removeAll(currentAuthorityIds); + + removableAuthorityIds.forEach(toBeRemovedId -> removeAuthority(groupId, toBeRemovedId)); + newAuthorityIds.forEach(toBeAddedId -> addAuthority(groupId, toBeAddedId)); + return group; + } + + @Override + public Group addAuthority(String groupId, String authorityId) { Group group = findById(groupId); - group.addAuthority(authorityService.getOne(authorityId)); + Authority authority = authorityService.getOne(authorityId); + return addAuthority(group, authority); + } + + @Override + public Group addAuthority(Group group, Authority authority) { + Assert.notNull(group, "Group cannot be null"); + Assert.notNull(authority, "Authority cannot be null"); + group.addAuthority(authority); + return save(group); + } + + @Override + public Group removeAuthority(String groupId, String authorityId) { + Group group = findById(groupId); + Authority authority = authorityService.getOne(authorityId); + return removeAuthority(group, authority); + } + + @Override + public Group removeAuthority(Group group, Authority authority) { + Assert.notNull(group, "Group cannot be null"); + Assert.notNull(authority, "Authority cannot be null"); + group.removeAuthority(authority); return save(group); } + @Override + public Group assignSubgroups(String parentGroupId, Set childGroupIds) { + Group parentGroup = this.findById(parentGroupId); + Set currentSubgroupIds = parentGroup.getSubgroupIds(); + + Set removableGroupIds = new HashSet<>(currentSubgroupIds); + removableGroupIds.removeAll(childGroupIds); + + Set newSubgroupIds = new HashSet<>(childGroupIds); + newSubgroupIds.removeAll(currentSubgroupIds); + + removableGroupIds.forEach(toBeRemovedId -> removeSubgroup(parentGroupId, toBeRemovedId)); + newSubgroupIds.forEach(toBeAddedId -> addSubgroup(parentGroupId, toBeAddedId)); + return parentGroup; + } + @Override public Pair addSubgroup(String parentGroupId, String childGroupId) { if (parentGroupId.equals(childGroupId)) { throw new IllegalArgumentException("Trying to add group to itself [%s]!".formatted(parentGroupId)); } Group parentGroup = this.findById(parentGroupId); - return this.addSubgroup(parentGroup, childGroupId); + Group childGroup = this.findById(childGroupId); + return this.addSubgroup(parentGroup, childGroup); } @Override @@ -334,10 +444,18 @@ public Pair addSubgroup(String parentGroupId, Group childGroup) { @Override public Pair addSubgroup(Group parentGroup, Group childGroup) { + Assert.notNull(parentGroup, "Parent group cannot be null"); + Assert.notNull(childGroup, "Child group cannot be null"); + // TODO: maybe handle groups cycles here? if (parentGroup.getStringId().equals(childGroup.getStringId())) { throw new IllegalArgumentException("Trying to add group to itself [%s]!".formatted(parentGroup.getStringId())); } + + if (parentGroup.getRealmId() != null && !parentGroup.getRealmId().equals(childGroup.getRealmId())) { + throw new IllegalArgumentException("Trying to add group [%s] to parent group [%s] from different realm!".formatted(childGroup.getStringId(), parentGroup.getStringId())); + } + parentGroup.addSubGroupId(childGroup.getStringId()); childGroup.addGroupId(parentGroup.getStringId()); log.info("Adding group [{}] to parent group [{}]", childGroup.getStringId(), parentGroup.getStringId()); @@ -346,6 +464,50 @@ public Pair addSubgroup(Group parentGroup, Group childGroup) { return Pair.of(parentGroup, childGroup); } + @Override + public Pair removeSubgroup(String parentGroupId, String childGroupId) { + if (parentGroupId.equals(childGroupId)) { + throw new IllegalArgumentException("Trying to remove group from itself [%s]!".formatted(parentGroupId)); + } + Group parentGroup = this.findById(parentGroupId); + Group childGroup = this.findById(childGroupId); + return this.removeSubgroup(parentGroup, childGroup); + } + + @Override + public Pair removeSubgroup(Group parentGroup, String childGroupId) { + if (parentGroup.getStringId().equals(childGroupId)) { + throw new IllegalArgumentException("Trying to remove group from itself [%s]!".formatted(parentGroup.getStringId())); + } + Group childGroup = this.findById(childGroupId); + return this.removeSubgroup(parentGroup, childGroup); + } + + @Override + public Pair removeSubgroup(String parentGroupId, Group childGroup) { + if (childGroup.getStringId().equals(parentGroupId)) { + throw new IllegalArgumentException("Trying to remove group from itself [%s]!".formatted(childGroup.getStringId())); + } + Group parentGroup = this.findById(parentGroupId); + return this.removeSubgroup(parentGroup, childGroup); + } + + @Override + public Pair removeSubgroup(Group parentGroup, Group childGroup) { + Assert.notNull(parentGroup, "Parent group cannot be null"); + Assert.notNull(childGroup, "Child group cannot be null"); + + if (parentGroup.getStringId().equals(childGroup.getStringId())) { + throw new IllegalArgumentException("Trying to remove group from itself [%s]!".formatted(parentGroup.getStringId())); + } + parentGroup.removeSubgroupId(childGroup.getStringId()); + childGroup.removeGroupId(parentGroup.getStringId()); + log.info("Removing group [{}] from parent group [{}]", childGroup.getStringId(), parentGroup.getStringId()); + this.save(parentGroup); + this.save(childGroup); + return Pair.of(parentGroup, childGroup); + } + @Override public List getGroupParentGroupsById(String groupId) { Group group = this.findById(groupId); @@ -388,9 +550,13 @@ public String getGroupOwnerEmail(String groupId) { } @Override - public Page search(GroupSearchDto searchDto, Pageable pageable) { + public Page search(GroupSearchRequestDto searchDto, Pageable pageable) { List filters = new ArrayList<>(); - if (searchDto.getFullText() != null && !searchDto.getFullText().isBlank()) { + if (searchDto != null && searchDto.getIds() != null) { + Criteria criteria = Criteria.where("_id").in(searchDto.getIds()); + filters.add(criteria); + } + if (searchDto != null && searchDto.getFullText() != null && !searchDto.getFullText().isBlank()) { Criteria criteria = new Criteria().orOperator( Criteria.where("identifier").regex(searchDto.getFullText(), "i"), Criteria.where("displayName").regex(searchDto.getFullText(), "i"), @@ -398,7 +564,7 @@ public Page search(GroupSearchDto searchDto, Pageable pageable) { ); filters.add(criteria); } - if (searchDto.getRealmId() != null && !searchDto.getRealmId().isBlank()) { + if (searchDto != null && searchDto.getRealmId() != null && !searchDto.getRealmId().isBlank()) { filters.add(Criteria.where("realmId").regex(searchDto.getRealmId(), "i")); } Query query = Query.query(filters.isEmpty() ? new Criteria() : new Criteria().andOperator(filters.toArray(new Criteria[0]))); @@ -407,6 +573,42 @@ public Page search(GroupSearchDto searchDto, Pageable pageable) { return new PageImpl<>(groups, pageable, count); } + @Override + public Group addRole(String groupId, String roleId) { + Group group = findById(groupId); + ProcessRole role = processRoleService.findById(new ProcessResourceId(roleId)); + return addRole(group, role); + } + + @Override + public Group addRole(Group group, ProcessRole processRole) { + Assert.notNull(group, "Group cannot be null"); + Assert.notNull(processRole, "Process role cannot be null"); + group.addProcessRole(processRole); + return save(group); + } + + @Override + public Group removeRole(String groupId, String roleId) { + Group group = findById(groupId); + ProcessRole role = processRoleService.findById(new ProcessResourceId(roleId)); + return removeRole(group, role); + } + + @Override + public Group removeRole(Group group, ProcessRole processRole) { + Assert.notNull(group, "Group cannot be null"); + Assert.notNull(processRole, "Process role cannot be null"); + + group.removeProcessRole(processRole); + return save(group); + } + + @Override + public Page findAllByProcessRoles(Collection roleIds, Pageable pageable) { + return groupRepository.findAllByProcessRoles__idIn(roleIds, pageable); + } + protected String getGroupOwnerEmail(Group groupCase) { return userService.findById(groupCase.getOwnerId(), groupCase.getRealmId()).getEmail(); } diff --git a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/UserFactoryImpl.java b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/UserFactoryImpl.java index 2f909d90944..f57ba8c30a0 100644 --- a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/UserFactoryImpl.java +++ b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/UserFactoryImpl.java @@ -2,9 +2,11 @@ import com.netgrif.application.engine.adapter.spring.petrinet.service.ProcessRoleService; import com.netgrif.application.engine.adapter.spring.petrinet.web.responsebodies.ProcessRole; -import com.netgrif.application.engine.auth.web.responsebodies.User; +import com.netgrif.application.engine.auth.web.responsebodies.UserDto; import com.netgrif.application.engine.objects.auth.domain.AbstractUser; +import com.netgrif.application.engine.objects.dto.response.group.GroupDto; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Pageable; import java.util.Locale; import java.util.stream.Collectors; @@ -17,9 +19,12 @@ public class UserFactoryImpl implements UserFactory { @Autowired private ProcessRoleFactory processRoleFactory; + @Autowired + private GroupService groupService; + @Override - public User getUser(AbstractUser user, Locale locale) { - User result = getUser(user); + public UserDto getUser(AbstractUser user, Locale locale) { + UserDto result = UserDto.createUser(user, groupService.findAllByIds(user.getGroupIds(), Pageable.unpaged()).stream().map(group -> GroupDto.fromGroup(group, locale)).collect(Collectors.toList())); String defaultRoleId = processRoleService.getDefaultRole().getStringId(); String anonymousRoleId = processRoleService.getAnonymousRole().getStringId(); @@ -36,7 +41,4 @@ public User getUser(AbstractUser user, Locale locale) { return result; } - protected User getUser(AbstractUser user) { - return User.createUser(user); - } } 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 218f8b364ad..5da3e61ec0a 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 @@ -584,6 +584,7 @@ public AbstractUser createSystemUser() { system.setLastName(UserConstants.SYSTEM_USER_SURNAME); system.setState(UserState.ACTIVE); saveUser(system); + systemUser = system; } return system; } diff --git a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/repository/GroupRepository.java b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/repository/GroupRepository.java index aaa9a0749e1..ada0294fc5c 100644 --- a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/repository/GroupRepository.java +++ b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/repository/GroupRepository.java @@ -1,10 +1,13 @@ package com.netgrif.application.engine.auth.repository; import com.netgrif.application.engine.objects.auth.domain.Group; +import com.netgrif.application.engine.objects.auth.domain.User; +import com.netgrif.application.engine.objects.workflow.domain.ProcessResourceId; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.repository.MongoRepository; import org.springframework.data.querydsl.QuerydslPredicateExecutor; @@ -21,6 +24,15 @@ @Repository public interface GroupRepository extends MongoRepository, QuerydslPredicateExecutor { + + /** + * Checks if a {@link Group} entity exists with the given identifier. + * + * @param identifier the unique identifier of the group + * @return {@code true} if a group with the specified identifier exists, otherwise {@code false} + */ + boolean existsByIdentifier(String identifier); + /** * Finds paginated list of all {@link Group} entities that have the given owner ID. * @@ -58,6 +70,8 @@ public interface GroupRepository extends MongoRepository, Queryds Page findAllByRealmIdIn(Collection realmIds, Pageable pageable); + Page findAllByProcessRoles__idIn(Collection rolesId, Pageable pageable); + void removeAllByRealmIdIn(Collection realmIds); void removeAllByRealmId(String realmId); diff --git a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/AuthorizationService.java b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/AuthorizationService.java new file mode 100644 index 00000000000..c8d5dca211f --- /dev/null +++ b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/AuthorizationService.java @@ -0,0 +1,5 @@ +package com.netgrif.application.engine.auth.service; + +public interface AuthorizationService { + boolean hasAuthority(String authority); +} diff --git a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/GroupService.java b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/GroupService.java index 81f5a20714c..aae89e778f2 100644 --- a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/GroupService.java +++ b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/GroupService.java @@ -1,8 +1,11 @@ package com.netgrif.application.engine.auth.service; 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.Group; -import com.netgrif.application.engine.objects.auth.dto.GroupSearchDto; +import com.netgrif.application.engine.objects.dto.request.group.GroupSearchRequestDto; +import com.netgrif.application.engine.objects.petrinet.domain.roles.ProcessRole; +import com.netgrif.application.engine.objects.workflow.domain.ProcessResourceId; import org.springframework.data.mongodb.core.query.Query; import com.querydsl.core.types.Predicate; import org.springframework.data.domain.Page; @@ -12,6 +15,7 @@ import java.util.Collection; import java.util.Optional; import java.util.List; +import java.util.Set; /** * Service interface for managing user groups in the application. @@ -83,7 +87,14 @@ public interface GroupService { * @return the {@link Group} with the specified ID */ Group findById(String id); - + + /** + * Finds all groups matching the specified collection of IDs with pagination support. + * + * @param ids collection of group IDs to search for + * @param pageable pagination information + * @return a page of {@link Group}s that match the provided IDs + */ Page findAllByIds(Collection ids, Pageable pageable); /** @@ -139,14 +150,24 @@ public interface GroupService { Group getDefaultSystemGroup(); /** - * Adds a user to a group within a specific realm. + * Assigns a set of users to a group based on its ID. + *

+ * This method updates the membership of the specified group by adding the provided user IDs. * + * @param groupId the ID of the group to which the users will be assigned + * @param userIds a set of user IDs to assign to the group + * @return the updated {@link Group} object including the newly assigned users + */ + Group assignUsersToGroup(String groupId, Set userIds); + + /** + * Adds a user to a group within a specific realm. + * @param groupId id of the group * @param userId ID of the user to add - * @param groupId ID of the group * @param realmId ID of the realm * @return the updated {@link Group} */ - Group addUser(String userId, String groupId, String realmId); + Group addUser(String groupId, String userId, String realmId); /** * Adds a user to a group within a specific realm. @@ -156,16 +177,15 @@ public interface GroupService { * @param realmId ID of the realm * @return the updated {@link Group} */ - Group addUser(String userId, Group group, String realmId); + Group addUser(Group group, String userId, String realmId); /** * Adds a user to a group specified by identifier. - * + * @param groupId id of the target group * @param user the user to add - * @param groupIdentifier identifier of the target group * @return the updated {@link Group} */ - Group addUser(AbstractUser user, String groupIdentifier); + Group addUser(String groupId, AbstractUser user); /** * Adds a user to a specific group. @@ -174,25 +194,34 @@ public interface GroupService { * @param group the group to add the user to * @return the updated {@link Group} */ - Group addUser(AbstractUser user, Group group); + Group addUser(Group group, AbstractUser user); + + /** + * Removes a user from a group within a specific realm. + * @param groupId the unique identifier of the group + * @param userId the ID of the user to remove + * @param realmId the ID of the realm where the group exists + * @return the updated {@link Group} + */ + Group removeUser(String groupId, String userId, String realmId); /** - * Removes a user from a group specified by identifier. + * Removes a user from a group specified by its unique identifier. * - * @param user the user to remove - * @param groupIdentifier identifier of the target group + * @param user the user to be removed + * @param groupIdentifier the unique identifier of the target group * @return the updated {@link Group} */ - Group removeUser(AbstractUser user, String groupIdentifier); + Group removeUser(String groupId, AbstractUser user); /** * Removes a user from a specific group. * - * @param user the user to remove - * @param group the group to remove the user from + * @param user the user to be removed + * @param group the group from which the user will be removed * @return the updated {@link Group} */ - Group removeUser(AbstractUser user, Group group); + Group removeUser(Group group, AbstractUser user); /** * Finds groups matching a given predicate with pagination. @@ -203,8 +232,17 @@ public interface GroupService { */ Page findByPredicate(Predicate predicate, Pageable pageable); + /** + * Finds groups matching the specified MongoDB query with pagination support. + * + * @param query the MongoDB query defining the criteria for searching groups + * @param pageable pagination information + * @return a page of {@link Group}s that match the query criteria + */ Page findByQuery(Query query, Pageable pageable); + Group assignAuthorities(String groupId, Set authorityIds); + /** * Assigns an authority to a group. * @@ -212,7 +250,52 @@ public interface GroupService { * @param authorityId ID of the authority to assign * @return the updated {@link Group} */ - Group assignAuthority(String groupId, String authorityId); + Group addAuthority(String groupId, String authorityId); + + + /** + * Assigns a specific authority to the provided group. + *

+ * This method updates the group by adding the specified authority to its list of associated authorities. + * + * @param group the {@link Group} object to which the authority will be added + * @param authority the {@link Authority} to be assigned to the group + * @return the updated {@link Group} object with the newly assigned authority + */ + Group addAuthority(Group group, Authority authority); + + /** + * Removes a specific authority from a group. + * + * @param groupId the ID of the group from which the authority will be removed + * @param authorityId the ID of the authority to remove + * @return the updated {@link Group} object without the specified authority + */ + Group removeAuthority(String groupId, String authorityId); + + + /** + * Removes a specific authority from the provided group. + *

+ * This method updates the group by removing the specified authority. + * + * @param group the {@link Group} object from which the authority will be removed + * @param authority the {@link Authority} to be removed from the group + * @return the updated {@link Group} object without the specified authority + */ + Group removeAuthority(Group group, Authority authority); + + /** + * Assigns multiple subgroups to a parent group. + *

+ * This method establishes relationships between the specified child groups + * and the parent group identified by its ID. + * + * @param parentGroupId the ID of the parent group to which subgroups will be assigned + * @param childGroupIds the list of IDs of the subgroups to be assigned to the parent group + * @return the updated {@link Group} representing the parent group with its new subgroup assignments + */ + Group assignSubgroups(String parentGroupId, Set childGroupIds); /** * Adds a subgroup relationship between two groups. @@ -250,6 +333,43 @@ public interface GroupService { */ Pair addSubgroup(Group parentGroup, Group childGroup); + + /** + * Removes a subgroup relationship between two groups by their IDs. + * + * @param parentGroupId the ID of the parent group + * @param childGroupId the ID of the child group + * @return a {@link Pair} containing both the updated parent and child {@link Group}s + */ + Pair removeSubgroup(String parentGroupId, String childGroupId); + + /** + * Removes a subgroup relationship between a parent group object and a child group by its ID. + * + * @param parentGroup the parent group object + * @param childGroupId the ID of the child group + * @return a {@link Pair} containing both the updated parent and child {@link Group}s + */ + Pair removeSubgroup(Group parentGroup, String childGroupId); + + /** + * Removes a subgroup relationship between a parent group by its ID and a child group object. + * + * @param parentGroupId the ID of the parent group + * @param childGroup the child group object + * @return a {@link Pair} containing both the updated parent and child {@link Group}s + */ + Pair removeSubgroup(String parentGroupId, Group childGroup); + + /** + * Removes a subgroup relationship between two group objects. + * + * @param parentGroup the parent group object + * @param childGroup the child group object + * @return a {@link Pair} containing both the updated parent and child {@link Group}s + */ + Pair removeSubgroup(Group parentGroup, Group childGroup); + /** * Retrieves all parent groups of a specified group. * @@ -302,9 +422,49 @@ public interface GroupService { /** * Searches for groups based on the provided search criteria and pageable details. * - * @param searchDto the search criteria encapsulated in a {@link GroupSearchDto} + * @param searchDto the search criteria encapsulated in a {@link GroupSearchRequestDto} * @param pageable pagination information for the results * @return a page of {@link Group} objects that match the search criteria */ - Page search(GroupSearchDto searchDto, Pageable pageable); + Page search(GroupSearchRequestDto searchDto, Pageable pageable); + + /** + * Adds a role to a specified group. + * + * @param groupId the ID of the group to which the role will be added + * @param roleId the ID of the role to add to the group + * @return the updated {@link Group} object with the new role assigned + */ + Group addRole(String groupId, String roleId); + + + /** + * Assigns a specific process role to a group. + * + * @param group the {@link Group} to which the role will be added + * @param processRole the {@link ProcessRole} to assign to the group + * @return the updated {@link Group} object with the assigned role + */ + Group addRole(Group group, ProcessRole processRole); + + /** + * Removes a role from a specified group. + * + * @param groupId the ID of the group from which the role will be removed + * @param roleId the ID of the role to remove from the group + * @return the updated {@link Group} object without the specified role + */ + Group removeRole(String groupId, String roleId); + + + /** + * Removes a specified process role from a group. + * + * @param group the {@link Group} from which the role will be removed + * @param processRole the {@link ProcessRole} to remove from the group + * @return the updated {@link Group} object without the specified role + */ + Group removeRole(Group group, ProcessRole processRole); + + Page findAllByProcessRoles(Collection roleIds, Pageable pageable); } diff --git a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/UserFactory.java b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/UserFactory.java index 8d5b3d73092..4243ee96d68 100644 --- a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/UserFactory.java +++ b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/UserFactory.java @@ -1,7 +1,7 @@ package com.netgrif.application.engine.auth.service; -import com.netgrif.application.engine.auth.web.responsebodies.User; +import com.netgrif.application.engine.auth.web.responsebodies.UserDto; import com.netgrif.application.engine.objects.auth.domain.AbstractUser; import java.util.Locale; @@ -12,5 +12,5 @@ public interface UserFactory { * @param locale the locale for translations * @return a full version of the user response object, that has all of its attributes set */ - User getUser(AbstractUser user, Locale locale); + UserDto getUser(AbstractUser user, Locale locale); } diff --git a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/web/responsebodies/User.java b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/web/responsebodies/UserDto.java similarity index 81% rename from nae-user-common/src/main/java/com/netgrif/application/engine/auth/web/responsebodies/User.java rename to nae-user-common/src/main/java/com/netgrif/application/engine/auth/web/responsebodies/UserDto.java index 42605f8591e..30124755ec9 100644 --- a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/web/responsebodies/User.java +++ b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/web/responsebodies/UserDto.java @@ -6,15 +6,18 @@ import com.netgrif.application.engine.objects.auth.domain.Authority; import com.netgrif.application.engine.objects.auth.domain.Credential; import com.netgrif.application.engine.objects.auth.domain.enums.UserState; +import com.netgrif.application.engine.objects.dto.response.group.GroupDto; import lombok.Data; import java.time.LocalDateTime; +import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @Data -public class User { +public class UserDto { public static final String ATTR_ENABLED_CREDENTIALS = "enabledCredentials"; private String id; @@ -28,15 +31,16 @@ public class User { private Set authorities; private Set processRoles; private Set negativeProcessRoles; - private Set nextGroups; - private User impersonated; + private Set groupIds; + private Set groups; + private UserDto impersonated; private LocalDateTime createdAt; private Map> attributes; private boolean enabled; private boolean emailVerified; protected UserState state; - public User(AbstractUser user) { + public UserDto(AbstractUser user) { Attribute> enabledCredentialsAttribute = new Attribute<>(); if (user instanceof com.netgrif.application.engine.objects.auth.domain.User domainUser) { Map> credentials = domainUser.getCredentials(); @@ -73,10 +77,18 @@ public User(AbstractUser user) { } } - public static User createUser(AbstractUser user) { - User result = new User(user); + public static UserDto createUser(AbstractUser user) { + UserDto result = new UserDto(user); result.setAuthorities(user.getAuthoritySet()); - result.setNextGroups(user.getGroupIds()); + result.setGroupIds(user.getGroupIds()); + return result; + } + + public static UserDto createUser(AbstractUser user, List groups) { + UserDto result = createUser(user); + if (groups != null) { + result.setGroups(new HashSet<>(groups)); + } return result; } }