This is an automated email from the ASF dual-hosted git repository. martin_s pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/archiva-redback-core.git
commit 23e2edc80ef62231fb6df6c9f4f64674b14a619f Author: Martin Stockhammer <[email protected]> AuthorDate: Sun Dec 20 16:46:54 2020 +0100 Updating Role REST v2 service --- .../redback/rest/api/model/v2/Permission.java | 4 + .../redback/rest/api/model/v2/RoleInfo.java | 30 +--- .../redback/rest/api/services/v2/RoleService.java | 83 +++++++---- .../interceptors/RequestValidationInterceptor.java | 2 +- .../rest/services/v2/BaseRedbackService.java | 90 +++++++++++- .../rest/services/v2/DefaultRoleService.java | 32 +++- .../rest/services/v2/DefaultUserService.java | 49 +------ .../rest/services/v2/NativeRoleServiceTest.java | 163 +++++++++++++++------ 8 files changed, 305 insertions(+), 148 deletions(-) diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/Permission.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/Permission.java index daf5f65..3900bc5 100644 --- a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/Permission.java +++ b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/Permission.java @@ -60,6 +60,10 @@ public class Permission this.permanent = permission.isPermanent(); } + public static Permission of( org.apache.archiva.redback.rbac.Permission perm ) { + return new Permission( perm ); + } + @Schema(name="name", description = "The identifier of the permission") public String getName() { diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/RoleInfo.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/RoleInfo.java index 01a2698..11ead1a 100644 --- a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/RoleInfo.java +++ b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/RoleInfo.java @@ -28,6 +28,7 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; /** * Result object for role information. @@ -36,7 +37,7 @@ import java.util.Objects; * @since 3.0 */ @XmlRootElement( name = "role" ) -@Schema(name="RoleInfo",description = "Information about role") +@Schema(name="RoleInfo",description = "Information about role.") public class RoleInfo extends BaseRoleInfo implements Serializable { @@ -57,26 +58,6 @@ public class RoleInfo extends BaseRoleInfo */ private List<String> parentRoleIds = new ArrayList<>(0); - /** - * The ids of all the assigned users. - */ - protected List<BaseUserInfo> assignedUsers = new ArrayList<>( 0 ); - - @Schema( description = "List of user ids that are assigned to this role.") - public List<BaseUserInfo> getAssignedUsers( ) - { - return assignedUsers; - } - - public void setAssignedUsers( List<BaseUserInfo> assignedUsers ) - { - this.assignedUsers = assignedUsers; - } - - public void addAssignedUser( BaseUserInfo id) { - this.assignedUsers.add( id ); - } - public RoleInfo() { // no op @@ -85,6 +66,12 @@ public class RoleInfo extends BaseRoleInfo public static RoleInfo of( Role rbacRole) { RoleInfo role = BaseRoleInfo.of( rbacRole, new RoleInfo( ) ); + if(rbacRole.getPermissions()!=null) + { + role.permissions = rbacRole.getPermissions( ).stream( ).map( rbacPerm -> + Permission.of( rbacPerm ) + ).collect( Collectors.toList( ) ); + } return role; } @@ -151,7 +138,6 @@ public class RoleInfo extends BaseRoleInfo sb.append( ", childRoleNames=" ).append( childRoleIds ); sb.append( ", permissions=" ).append( permissions ); sb.append( ", parentRoleNames=" ).append( parentRoleIds ); - sb.append( ", assignedUsers=" ).append( assignedUsers ); sb.append( ", permanent=" ).append( isPermanent( ) ); sb.append( ", modelId='" ).append( modelId ).append( '\'' ); sb.append( ", resource='" ).append( resource ).append( '\'' ); diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/RoleService.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/RoleService.java index c88bc61..3aec8ba 100644 --- a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/RoleService.java +++ b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/RoleService.java @@ -33,6 +33,7 @@ import org.apache.archiva.redback.rest.api.model.v2.PagedResult; import org.apache.archiva.redback.rest.api.model.v2.Role; import org.apache.archiva.redback.rest.api.model.v2.RoleInfo; import org.apache.archiva.redback.rest.api.model.v2.RoleTemplate; +import org.apache.archiva.redback.rest.api.model.v2.UserInfo; import org.apache.archiva.redback.rest.api.services.RedbackServiceException; import javax.ws.rs.DELETE; @@ -99,7 +100,7 @@ public interface RoleService @GET @Produces( { APPLICATION_JSON } ) @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION ) - @Operation( summary = "Returns information about a specific role. Use HTTP HEAD method for checking, if the resource exists.", + @Operation( summary = "Returns the definition about a specific role.", security = { @SecurityRequirement( name = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION @@ -124,7 +125,7 @@ public interface RoleService @HEAD @Produces( { APPLICATION_JSON } ) @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION ) - @Operation( summary = "Returns information about a specific role. Use HTTP HEAD method for checking, if the resource exists.", + @Operation( summary = "Checks, if the role with the given id exists.", security = { @SecurityRequirement( name = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION @@ -273,70 +274,71 @@ public interface RoleService throws RedbackServiceException; + /** - * Assigns the role indicated by the roleId to the given principal + * Assigns the templated role indicated by the templateId * - * @param roleId + * fails if the templated role has not been created + * + * @param templateId + * @param resource * @param userId */ - @Path( "{roleId}/user/{userId}" ) + @Path( "templates/{templateId}/{resource}/user/{userId}" ) @PUT @Produces( { APPLICATION_JSON } ) @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION ) - @Operation( summary = "Assigns a role to a given user", + @Operation( summary = "Assigns a template role instance to a given user", security = { @SecurityRequirement( name = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION ) }, responses = { @ApiResponse( responseCode = "200", - description = "If the role was assigned" + description = "If the role instance was assigned" ), - @ApiResponse( responseCode = "404", description = "Role does not exist", + @ApiResponse( responseCode = "404", description = "Role instance does not exist", content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = RedbackRestError.class )) ), @ApiResponse( responseCode = "403", description = "The authenticated user has not the permission for role assignment.", content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = RedbackRestError.class )) ) } ) - RoleInfo assignRole( @PathParam( "roleId" ) String roleId, @PathParam( "userId" ) String userId ) + RoleInfo assignTemplatedRole( @PathParam( "templateId" ) String templateId, + @PathParam( "resource" ) String resource, + @PathParam( "userId" ) String userId ) throws RedbackServiceException; /** - * Assigns the templated role indicated by the templateId - * - * fails if the templated role has not been created + * Assigns the role indicated by the roleId to the given principal * - * @param templateId - * @param resource + * @param roleId * @param userId */ - @Path( "templates/{templateId}/{resource}/user/{userId}" ) + @Path( "{roleId}/user/{userId}" ) @PUT @Produces( { APPLICATION_JSON } ) @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION ) - @Operation( summary = "Assigns a template role instance to a given user", + @Operation( summary = "Assigns a role to a given user", security = { @SecurityRequirement( name = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION ) }, responses = { @ApiResponse( responseCode = "200", - description = "If the role instance was assigned" + description = "If the role was assigned" ), - @ApiResponse( responseCode = "404", description = "Role instance does not exist", + @ApiResponse( responseCode = "404", description = "Role does not exist", content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = RedbackRestError.class )) ), @ApiResponse( responseCode = "403", description = "The authenticated user has not the permission for role assignment.", content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = RedbackRestError.class )) ) } ) - RoleInfo assignTemplatedRole( @PathParam( "templateId" ) String templateId, - @PathParam( "resource" ) String resource, - @PathParam( "userId" ) String userId ) + RoleInfo assignRole( @PathParam( "roleId" ) String roleId, @PathParam( "userId" ) String userId ) throws RedbackServiceException; /** - * Unassigns the role indicated by the role id from the given principal + * Deletes the assignment of a role to a user. * - * @param roleId - * @param userId + * @param roleId the role id + * @param userId the user id * @throws RedbackServiceException */ @Path( "{roleId}/user/{userId}" ) @@ -357,9 +359,40 @@ public interface RoleService content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = RedbackRestError.class )) ) } ) - RoleInfo unassignRole( @PathParam( "roleId" ) String roleId, @PathParam( "userId" ) String userId ) + RoleInfo deleteRoleAssignment( @PathParam( "roleId" ) String roleId, @PathParam( "userId" ) String userId ) throws RedbackServiceException; + @Path("{roleId}/user") + @GET + @Produces({APPLICATION_JSON}) + @RedbackAuthorization(permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION) + @Operation( summary = "Returns the users assigned to the given role", + parameters = { + @Parameter(name = "q", description = "Search term"), + @Parameter(name = "offset", description = "The offset of the first element returned"), + @Parameter(name = "limit", description = "Maximum number of items to return in the response"), + @Parameter(name = "orderBy", description = "List of attribute used for sorting (user_id, fullName, email, created"), + @Parameter(name = "order", description = "The sort order. Either ascending (asc) or descending (desc)") + }, + security = { + @SecurityRequirement( name = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION ) + }, + responses = { + @ApiResponse( responseCode = "200", + description = "If the users could be retrieved" + ), + @ApiResponse( responseCode = "404", description = "Role instance does not exist", + content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = RedbackRestError.class )) ), + @ApiResponse( responseCode = "403", description = "The authenticated user has not the permission for role assignment.", + content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = RedbackRestError.class )) ) + } + ) + PagedResult<UserInfo> getRoleUsers(@PathParam( "roleId" ) String roleId, + @QueryParam("q") @DefaultValue( "" ) String searchTerm, + @QueryParam( "offset" ) @DefaultValue( "0" ) Integer offset, + @QueryParam( "limit" ) @DefaultValue( value = DEFAULT_PAGE_LIMIT ) Integer limit, + @QueryParam( "orderBy") @DefaultValue( "id" ) List<String> orderBy, + @QueryParam("order") @DefaultValue( "asc" ) String order) throws RedbackServiceException; /** * Updates a role. Attributes that are empty or null will be ignored. diff --git a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/interceptors/RequestValidationInterceptor.java b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/interceptors/RequestValidationInterceptor.java index 7553cf0..fb3e72e 100644 --- a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/interceptors/RequestValidationInterceptor.java +++ b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/interceptors/RequestValidationInterceptor.java @@ -127,7 +127,7 @@ public class RequestValidationInterceptor "origin, content-type, accept, authorization"); responseContext.getHeaders().add( "Access-Control-Allow-Methods", - "GET, POST, PUT, DELETE, OPTIONS, HEAD"); + "GET, POST, PUT, DELETE, OPTIONS, HEAD, PATCH"); } private class HeaderValidationInfo diff --git a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/v2/BaseRedbackService.java b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/v2/BaseRedbackService.java index 4dc9ab3..48de1bb 100644 --- a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/v2/BaseRedbackService.java +++ b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/v2/BaseRedbackService.java @@ -24,18 +24,26 @@ import org.apache.archiva.redback.rbac.Role; import org.apache.archiva.redback.rest.api.MessageKeys; import org.apache.archiva.redback.rest.api.model.ErrorMessage; import org.apache.archiva.redback.rest.api.model.v2.BaseUserInfo; +import org.apache.archiva.redback.rest.api.model.v2.PagedResult; import org.apache.archiva.redback.rest.api.model.v2.RoleInfo; +import org.apache.archiva.redback.rest.api.model.v2.UserInfo; import org.apache.archiva.redback.rest.api.services.RedbackServiceException; import org.apache.archiva.redback.users.User; import org.apache.archiva.redback.users.UserManager; import org.apache.archiva.redback.users.UserManagerException; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Named; import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Optional; +import java.util.function.BiPredicate; +import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -44,8 +52,36 @@ import java.util.stream.Stream; */ public class BaseRedbackService { + protected static final String[] DEFAULT_SEARCH_FIELDS = {"user_id", "full_name", "email"}; + protected static final Map<String, BiPredicate<String, User>> USER_FILTER_MAP = new HashMap<>( ); + protected static final Map<String, Comparator<User>> USER_ORDER_MAP = new HashMap<>( ); + protected static final QueryHelper<User> USER_QUERY_HELPER; private static final Logger log = LoggerFactory.getLogger( BaseRedbackService.class ); + + static + { + // The simple Comparator.comparing(attribute) is not null safe + // As there are attributes that may have a null value, we have to use a comparator with nullsLast(naturalOrder) + // and the wrapping Comparator.nullsLast(Comparator.comparing(attribute)) does not work, because the attribute is not checked by the nullsLast-Comparator + USER_ORDER_MAP.put( "id", Comparator.comparing( org.apache.archiva.redback.users.User::getId, Comparator.nullsLast( Comparator.naturalOrder( ) ) ) ); + USER_ORDER_MAP.put( "user_id", Comparator.comparing( org.apache.archiva.redback.users.User::getUsername, Comparator.nullsLast( Comparator.naturalOrder( ) ) ) ); + USER_ORDER_MAP.put( "full_name", Comparator.comparing( org.apache.archiva.redback.users.User::getFullName, Comparator.nullsLast( Comparator.naturalOrder( ) ) ) ); + USER_ORDER_MAP.put( "email", Comparator.comparing( org.apache.archiva.redback.users.User::getEmail, Comparator.nullsLast( Comparator.naturalOrder( ) ) ) ); + USER_ORDER_MAP.put( "created", Comparator.comparing( org.apache.archiva.redback.users.User::getAccountCreationDate, Comparator.nullsLast( Comparator.naturalOrder( ) ) ) ); + USER_ORDER_MAP.put( "last_login", Comparator.comparing( org.apache.archiva.redback.users.User::getLastLoginDate, Comparator.nullsLast( Comparator.naturalOrder( ) ) ) ); + USER_ORDER_MAP.put( "validated", Comparator.comparing( org.apache.archiva.redback.users.User::isValidated, Comparator.nullsLast( Comparator.naturalOrder( ) ) ) ); + USER_ORDER_MAP.put( "locked", Comparator.comparing( org.apache.archiva.redback.users.User::isLocked, Comparator.nullsLast( Comparator.naturalOrder( ) ) ) ); + USER_ORDER_MAP.put( "password_change_required", Comparator.comparing( org.apache.archiva.redback.users.User::isPasswordChangeRequired, Comparator.nullsLast( Comparator.naturalOrder( ) ) ) ); + USER_ORDER_MAP.put( "last_password_change", Comparator.comparing( org.apache.archiva.redback.users.User::getLastPasswordChange, Comparator.nullsLast( Comparator.naturalOrder( ) ) ) ); + + USER_FILTER_MAP.put( "user_id", ( String q, org.apache.archiva.redback.users.User u ) -> StringUtils.containsIgnoreCase( u.getUsername( ), q ) ); + USER_FILTER_MAP.put( "full_name", ( String q, org.apache.archiva.redback.users.User u ) -> StringUtils.containsIgnoreCase( u.getFullName( ), q ) ); + USER_FILTER_MAP.put( "email", ( String q, org.apache.archiva.redback.users.User u ) -> StringUtils.containsIgnoreCase( u.getEmail( ), q ) ); + + USER_QUERY_HELPER = new QueryHelper<>( USER_FILTER_MAP, USER_ORDER_MAP, DEFAULT_SEARCH_FIELDS ); + } + protected RBACManager rbacManager; protected UserManager userManager; @@ -62,7 +98,6 @@ public class BaseRedbackService RoleInfo role = RoleInfo.of( rbacRole ); role.setParentRoleIds( getParentRoles( rbacRole ) ); role.setChildRoleIds( getChildRoles( rbacRole ) ); - role.setAssignedUsers( getAssignedUsersRecursive( rbacRole ) ); return role; } catch ( RbacManagerException e ) @@ -72,6 +107,10 @@ public class BaseRedbackService } } + protected boolean isAscending(String order) { + return !"desc".equals( order ); + } + protected List<String> getParentRoles( org.apache.archiva.redback.rbac.Role rbacRole ) throws RbacManagerException { return new ArrayList<>( rbacManager.getParentRoleIds( rbacRole ).keySet( )); @@ -96,6 +135,33 @@ public class BaseRedbackService } } + protected List<User> getAssignedRedbackUsersRecursive( org.apache.archiva.redback.rbac.Role rbacRole ) throws RbacManagerException + { + try + { + return rbacManager.getUserAssignmentsForRoles( recurseRoles( rbacRole ).map( role -> role.getId( ) ).collect( Collectors.toList( ) ) ) + .stream( ).map( assignment -> getRedbackUser( assignment.getPrincipal( ) ) ).collect( Collectors.toList( ) ); + } + catch ( RuntimeException e ) + { + log.error( "Could not recurse roles for assignments {}", e.getMessage( ) ); + throw new RbacManagerException( e.getCause( ) ); + } + } + + protected User getRedbackUser(String userId) throws RuntimeException { + try + { + return userManager.findUser( userId, true ); + } + catch ( UserManagerException e ) + { + throw new RuntimeException( e ); + } + } + + + private Stream<Role> recurseRoles( Role startRole ) { return Stream.concat( Stream.of( startRole ), getParentRoleStream( startRole ).flatMap( this::recurseRoles ) ).distinct( ); @@ -133,7 +199,6 @@ public class BaseRedbackService RoleInfo role = RoleInfo.of( rbacRole ); role.setParentRoleIds( getParentRoles( rbacRole ) ); role.setChildRoleIds( getChildRoles( rbacRole ) ); - role.setAssignedUsers( getAssignedUsersRecursive( rbacRole ) ); return Optional.of( role ); } catch ( RbacManagerException e ) @@ -142,4 +207,25 @@ public class BaseRedbackService return Optional.empty( ); } } + + protected UserInfo getRestUser( User user ) + { + if ( user == null ) + { + return null; + } + return new UserInfo( user ); + } + + protected PagedResult<UserInfo> getUserInfoPagedResult( List<? extends User> rawUsers, String q, Integer offset, Integer limit, List<String> orderBy, boolean ascending) + { + Predicate<User> filter = USER_QUERY_HELPER.getQueryFilter( q ); + long size = rawUsers.stream( ).filter( filter ).count( ); + List<UserInfo> users = rawUsers.stream( ) + .filter( filter ) + .sorted( USER_QUERY_HELPER.getComparator( orderBy, ascending ) ).skip( offset ).limit( limit ) + .map( user -> getRestUser( user ) ) + .collect( Collectors.toList( ) ); + return new PagedResult<>( (int) size, offset, limit, users ); + } } diff --git a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/v2/DefaultRoleService.java b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/v2/DefaultRoleService.java index 43adc13..ea0c34c 100644 --- a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/v2/DefaultRoleService.java +++ b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/v2/DefaultRoleService.java @@ -27,6 +27,7 @@ import org.apache.archiva.redback.rest.api.model.v2.PagedResult; import org.apache.archiva.redback.rest.api.model.v2.Role; import org.apache.archiva.redback.rest.api.model.v2.RoleInfo; import org.apache.archiva.redback.rest.api.model.v2.RoleTemplate; +import org.apache.archiva.redback.rest.api.model.v2.UserInfo; import org.apache.archiva.redback.rest.api.services.RedbackServiceException; import org.apache.archiva.redback.rest.api.services.v2.RoleService; import org.apache.archiva.redback.role.PermanentRoleDeletionInvalid; @@ -34,9 +35,8 @@ import org.apache.archiva.redback.role.RoleExistsException; import org.apache.archiva.redback.role.RoleManager; import org.apache.archiva.redback.role.RoleManagerException; import org.apache.archiva.redback.role.RoleNotFoundException; -import org.apache.archiva.redback.role.model.ModelApplication; -import org.apache.archiva.redback.role.model.ModelTemplate; import org.apache.archiva.redback.role.util.RoleModelUtils; +import org.apache.archiva.redback.users.User; import org.apache.archiva.redback.users.UserManager; import org.apache.archiva.redback.users.UserManagerException; import org.apache.archiva.redback.users.UserNotFoundException; @@ -83,7 +83,7 @@ public class DefaultRoleService extends BaseRedbackService @Context private UriInfo uriInfo; - private static final String[] DEFAULT_SEARCH_FIELDS = {"name", "description"}; + private static final String[] DEFAULT_SEARCH_FIELDS = {"id", "name", "description"}; private static final Map<String, BiPredicate<String, org.apache.archiva.redback.rbac.Role>> FILTER_MAP = new HashMap<>( ); private static final Map<String, Comparator<org.apache.archiva.redback.rbac.Role>> ORDER_MAP = new HashMap<>( ); private static final QueryHelper<org.apache.archiva.redback.rbac.Role> QUERY_HELPER; @@ -92,6 +92,7 @@ public class DefaultRoleService extends BaseRedbackService { QUERY_HELPER = new QueryHelper<>( FILTER_MAP, ORDER_MAP, DEFAULT_SEARCH_FIELDS ); + QUERY_HELPER.addStringFilter( "id", org.apache.archiva.redback.rbac.Role::getId ); QUERY_HELPER.addStringFilter( "name", org.apache.archiva.redback.rbac.Role::getName ); QUERY_HELPER.addStringFilter( "description", org.apache.archiva.redback.rbac.Role::getDescription ); QUERY_HELPER.addBooleanFilter( "assignable", org.apache.archiva.redback.rbac.Role::isAssignable ); @@ -103,6 +104,7 @@ public class DefaultRoleService extends BaseRedbackService QUERY_HELPER.addNullsafeFieldComparator( "id", org.apache.archiva.redback.rbac.Role::getId ); QUERY_HELPER.addNullsafeFieldComparator( "resource", org.apache.archiva.redback.rbac.Role::getResource ); QUERY_HELPER.addNullsafeFieldComparator( "assignable", org.apache.archiva.redback.rbac.Role::isAssignable ); + QUERY_HELPER.addNullsafeFieldComparator( "description", org.apache.archiva.redback.rbac.Role::getDescription ); QUERY_HELPER.addNullsafeFieldComparator( "template_instance", org.apache.archiva.redback.rbac.Role::isTemplateInstance ); } @@ -121,7 +123,7 @@ public class DefaultRoleService extends BaseRedbackService @Override public PagedResult<RoleInfo> getAllRoles( String searchTerm, Integer offset, Integer limit, List<String> orderBy, String order ) throws RedbackServiceException { - boolean ascending = !"desc".equals( order ); + boolean ascending = isAscending( order ); try { // UserQuery does not work here, because the configurable user manager does only return the query for @@ -389,7 +391,7 @@ public class DefaultRoleService extends BaseRedbackService } @Override - public RoleInfo unassignRole( String roleId, String userId ) + public RoleInfo deleteRoleAssignment( String roleId, String userId ) throws RedbackServiceException { try @@ -415,8 +417,28 @@ public class DefaultRoleService extends BaseRedbackService } catch ( RbacObjectNotFoundException e ) { + throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_ROLE_NOT_FOUND, e.getMessage( ) ), 404 ); + } + catch ( RbacManagerException e ) + { throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_RBACMANAGER_FAIL, e.getMessage( ) ) ); } + } + + @Override + public PagedResult<UserInfo> getRoleUsers( String roleId, String searchTerm, Integer offset, Integer limit, List<String> orderBy, String order ) throws RedbackServiceException + { + boolean ascending = isAscending( order ); + try + { + org.apache.archiva.redback.rbac.Role rbacRole = rbacManager.getRoleById( roleId ); + List<User> rawUsers = getAssignedRedbackUsersRecursive( rbacRole ); + return getUserInfoPagedResult( rawUsers, searchTerm, offset, limit, orderBy, ascending ); + } + catch ( RbacObjectNotFoundException e ) + { + throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_ROLE_NOT_FOUND, e.getMessage( ) ), 404 ); + } catch ( RbacManagerException e ) { throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_RBACMANAGER_FAIL, e.getMessage( ) ) ); diff --git a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/v2/DefaultUserService.java b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/v2/DefaultUserService.java index f32d595..b89a550 100644 --- a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/v2/DefaultUserService.java +++ b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/v2/DefaultUserService.java @@ -102,7 +102,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; -import java.util.function.BiPredicate; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -118,33 +117,6 @@ public class DefaultUserService extends BaseRedbackService private static final String VALID_USERNAME_CHARS = "[a-zA-Z_0-9\\-.@]*"; private static final String[] INVALID_CREATE_USER_NAMES = {"admin", "guest", "me"}; - private static final String[] DEFAULT_SEARCH_FIELDS = {"user_id", "full_name", "email"}; - private static final Map<String, BiPredicate<String, org.apache.archiva.redback.users.User>> FILTER_MAP = new HashMap<>( ); - private static final Map<String, Comparator<org.apache.archiva.redback.users.User>> ORDER_MAP = new HashMap<>( ); - private static final QueryHelper<org.apache.archiva.redback.users.User> QUERY_HELPER; - - static - { - // The simple Comparator.comparing(attribute) is not null safe - // As there are attributes that may have a null value, we have to use a comparator with nullsLast(naturalOrder) - // and the wrapping Comparator.nullsLast(Comparator.comparing(attribute)) does not work, because the attribute is not checked by the nullsLast-Comparator - ORDER_MAP.put( "id", Comparator.comparing( org.apache.archiva.redback.users.User::getId, Comparator.nullsLast( Comparator.naturalOrder( ) ) ) ); - ORDER_MAP.put( "user_id", Comparator.comparing( org.apache.archiva.redback.users.User::getUsername, Comparator.nullsLast( Comparator.naturalOrder( ) ) ) ); - ORDER_MAP.put( "full_name", Comparator.comparing( org.apache.archiva.redback.users.User::getFullName, Comparator.nullsLast( Comparator.naturalOrder( ) ) ) ); - ORDER_MAP.put( "email", Comparator.comparing( org.apache.archiva.redback.users.User::getEmail, Comparator.nullsLast( Comparator.naturalOrder( ) ) ) ); - ORDER_MAP.put( "created", Comparator.comparing( org.apache.archiva.redback.users.User::getAccountCreationDate, Comparator.nullsLast( Comparator.naturalOrder( ) ) ) ); - ORDER_MAP.put( "last_login", Comparator.comparing( org.apache.archiva.redback.users.User::getLastLoginDate, Comparator.nullsLast( Comparator.naturalOrder( ) ) ) ); - ORDER_MAP.put( "validated", Comparator.comparing( org.apache.archiva.redback.users.User::isValidated, Comparator.nullsLast( Comparator.naturalOrder( ) ) ) ); - ORDER_MAP.put( "locked", Comparator.comparing( org.apache.archiva.redback.users.User::isLocked, Comparator.nullsLast( Comparator.naturalOrder( ) ) ) ); - ORDER_MAP.put( "password_change_required", Comparator.comparing( org.apache.archiva.redback.users.User::isPasswordChangeRequired, Comparator.nullsLast( Comparator.naturalOrder( ) ) ) ); - ORDER_MAP.put( "last_password_change", Comparator.comparing( org.apache.archiva.redback.users.User::getLastPasswordChange, Comparator.nullsLast( Comparator.naturalOrder( ) ) ) ); - - FILTER_MAP.put( "user_id", ( String q, org.apache.archiva.redback.users.User u ) -> StringUtils.containsIgnoreCase( u.getUsername( ), q ) ); - FILTER_MAP.put( "full_name", ( String q, org.apache.archiva.redback.users.User u ) -> StringUtils.containsIgnoreCase( u.getFullName( ), q ) ); - FILTER_MAP.put( "email", ( String q, org.apache.archiva.redback.users.User u ) -> StringUtils.containsIgnoreCase( u.getEmail( ), q ) ); - - QUERY_HELPER = new QueryHelper<>( FILTER_MAP, ORDER_MAP, DEFAULT_SEARCH_FIELDS ); - } private SecuritySystem securitySystem; @@ -395,20 +367,13 @@ public class DefaultUserService extends BaseRedbackService Integer limit, List<String> orderBy, String order ) throws RedbackServiceException { - boolean ascending = !"desc".equals( order ); + boolean ascending = isAscending( order ); try { // UserQuery does not work here, because the configurable user manager does only return the query for // the first user manager in the list. So we have to fetch the whole user list List<? extends org.apache.archiva.redback.users.User> rawUsers = userManager.getUsers( ); - Predicate<org.apache.archiva.redback.users.User> filter = QUERY_HELPER.getQueryFilter( q ); - long size = rawUsers.stream( ).filter( filter ).count( ); - List<UserInfo> users = rawUsers.stream( ) - .filter( filter ) - .sorted( QUERY_HELPER.getComparator( orderBy, ascending ) ).skip( offset ).limit( limit ) - .map( user -> getRestUser( user ) ) - .collect( Collectors.toList( ) ); - return new PagedResult<>( (int) size, offset, limit, users ); + return getUserInfoPagedResult( rawUsers, q, offset, limit, orderBy, ascending ); } catch ( UserManagerException e ) { @@ -416,6 +381,7 @@ public class DefaultUserService extends BaseRedbackService } } + @Override public UserInfo updateMe( SelfUserData user ) throws RedbackServiceException @@ -573,15 +539,6 @@ public class DefaultUserService extends BaseRedbackService return new PingResult( true ); } - private UserInfo getRestUser( org.apache.archiva.redback.users.User user ) - { - if ( user == null ) - { - return null; - } - return new UserInfo( user ); - } - @Override public UserInfo createAdminUser( User adminUser ) throws RedbackServiceException diff --git a/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/v2/NativeRoleServiceTest.java b/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/v2/NativeRoleServiceTest.java index 936a160..125b8f0 100644 --- a/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/v2/NativeRoleServiceTest.java +++ b/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/v2/NativeRoleServiceTest.java @@ -19,8 +19,14 @@ package org.apache.archiva.redback.rest.services.v2; */ import io.restassured.response.Response; +import io.restassured.response.ResponseBodyExtractionOptions; +import org.apache.archiva.redback.rest.api.model.User; +import org.apache.archiva.redback.rest.api.model.v2.BaseUserInfo; +import org.apache.archiva.redback.rest.api.model.v2.PagedResult; +import org.apache.archiva.redback.rest.api.model.v2.Permission; import org.apache.archiva.redback.rest.api.model.v2.RoleInfo; import org.apache.archiva.redback.rest.api.model.v2.RoleTemplate; +import org.apache.archiva.redback.rest.api.model.v2.UserInfo; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; @@ -38,8 +44,8 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; -import static io.restassured.RestAssured.get; import static io.restassured.RestAssured.given; import static io.restassured.http.ContentType.JSON; import static org.apache.archiva.redback.rest.api.Constants.DEFAULT_PAGE_LIMIT; @@ -142,26 +148,26 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices void deleteTemplatedRole( ) { String token = getAdminToken( ); - given( ).spec( getRequestSpec( token ) ).contentType( JSON ) - .when( ) - .put( "templates/archiva-repository-manager/repository05" ) - .then( ).statusCode( 201 ).extract( ).response( ); - given( ).spec( getRequestSpec( token ) ).contentType( JSON ) - .when( ) - .delete( "templates/archiva-repository-manager/repository01" ) - .then( ).statusCode( 404 ); - given( ).spec( getRequestSpec( token ) ).contentType( JSON ) - .when( ) - .delete( "templates/archiva-repository-manager/repository05" ) - .then( ).statusCode( 200 ); - given( ).spec( getRequestSpec( token ) ).contentType( JSON ) - .when( ) - .delete( "templates/archiva-repository-manager/repository05" ) - .then( ).statusCode( 404 ); - given( ).spec( getRequestSpec( token ) ).contentType( JSON ) - .when( ) - .delete( "templates/archiva-repository-observer/repository05" ) - .then( ).statusCode( 200 ); + given( ).spec( getRequestSpec( token ) ).contentType( JSON ) + .when( ) + .put( "templates/archiva-repository-manager/repository05" ) + .then( ).statusCode( 201 ).extract( ).response( ); + given( ).spec( getRequestSpec( token ) ).contentType( JSON ) + .when( ) + .delete( "templates/archiva-repository-manager/repository01" ) + .then( ).statusCode( 404 ); + given( ).spec( getRequestSpec( token ) ).contentType( JSON ) + .when( ) + .delete( "templates/archiva-repository-manager/repository05" ) + .then( ).statusCode( 200 ); + given( ).spec( getRequestSpec( token ) ).contentType( JSON ) + .when( ) + .delete( "templates/archiva-repository-manager/repository05" ) + .then( ).statusCode( 404 ); + given( ).spec( getRequestSpec( token ) ).contentType( JSON ) + .when( ) + .delete( "templates/archiva-repository-observer/repository05" ) + .then( ).statusCode( 200 ); } @@ -343,12 +349,20 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices { String token = getAdminToken( ); Response response = given( ).spec( getRequestSpec( token ) ).contentType( JSON ) - .when( ).get( "archiva-system-administrator" ).then( ).statusCode( 200 ).extract( ).response( ); + .when( ).get( "archiva-system-administrator" ).prettyPeek( ).then( ).statusCode( 200 ).extract( ).response( ); assertNotNull( response ); RoleInfo roleInfo = response.getBody( ).jsonPath( ).getObject( "", RoleInfo.class ); assertNotNull( roleInfo ); assertEquals( "archiva-system-administrator", roleInfo.getId( ) ); assertEquals( "Archiva System Administrator", roleInfo.getName( ) ); + List<Permission> perms = roleInfo.getPermissions( ); + assertNotNull( perms ); + assertTrue( perms.size( ) > 0 ); + assertTrue( perms.stream( ).filter( perm -> "archiva-manage-configuration".equals( perm.getName( ) ) ).findAny( ).isPresent( ) ); + List<String> childs = roleInfo.getChildRoleIds( ); + assertNotNull( childs ); + assertTrue( childs.size( ) > 0 ); + assertTrue( childs.stream( ).filter( id -> "archiva-global-repository-manager".equals( id ) ).findAny( ).isPresent( ) ); } @Test @@ -463,6 +477,47 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices } + @Test + void getAssignedUsers( ) + { + String token = getAdminToken( ); + Map<String, Object> jsonAsMap = new HashMap<>( ); + jsonAsMap.put( "user_id", "aragorn" ); + jsonAsMap.put( "email", "[email protected]" ); + jsonAsMap.put( "full_name", "Aragorn King of Gondor " ); + jsonAsMap.put( "password", "pAssw0rD" ); + + try + { + given( ).spec( getRequestSpec( token, getUserServicePath( ) ) ).contentType( JSON ) + .body( jsonAsMap ) + .when( ) + .post( ) + .then( ).statusCode( 201 ); + given( ).spec( getRequestSpec( token ) ).contentType( JSON ) + .when( ) + .put( "system-administrator/user/aragorn" ) + .then( ).statusCode( 200 ); + Response result = given( ).spec( getRequestSpec( token ) ).contentType( JSON ) + .when( ) + .get( "system-administrator/user" ) + .prettyPeek() + .then( ).statusCode( 200 ).extract( ).response( ); + assertNotNull(result); + PagedResult<UserInfo> userResult = result.getBody( ).jsonPath( ).getObject( "", PagedResult.class ); + assertNotNull( userResult ); + assertEquals( 2, userResult.getPagination( ).getTotalCount( ) ); + List<UserInfo> users = result.getBody( ).jsonPath( ).getList( "data", UserInfo.class ); + assertArrayEquals( new String[] {"admin","aragorn"}, users.stream( ).map( BaseUserInfo::getUserId ).sorted().toArray(String[]::new) ); + } + finally + { + given( ).spec( getRequestSpec( token, getUserServicePath( ) ) ).contentType( JSON ) + .when( ) + .delete( "aragorn" ).then( ).statusCode( 200 ); + } + + } @Test void assignRole( ) @@ -503,7 +558,7 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices { given( ).spec( getRequestSpec( token, getUserServicePath( ) ) ).contentType( JSON ) .when( ) - .delete( "aragorn" ).then().statusCode( 200 ); + .delete( "aragorn" ).then( ).statusCode( 200 ); } } @@ -546,7 +601,7 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices { given( ).spec( getRequestSpec( token, getUserServicePath( ) ) ).contentType( JSON ) .when( ) - .delete( "aragorn" ).then().statusCode( 200 ); + .delete( "aragorn" ).then( ).statusCode( 200 ); } } @@ -605,13 +660,13 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices { given( ).spec( getRequestSpec( token, getUserServicePath( ) ) ).contentType( JSON ) .when( ) - .delete( "aragorn" ).then().statusCode( 200 ); + .delete( "aragorn" ).then( ).statusCode( 200 ); given( ).spec( getRequestSpec( token ) ).contentType( JSON ) .when( ) - .delete( "templates/archiva-repository-manager/repository11" ).then().statusCode( 200 ); + .delete( "templates/archiva-repository-manager/repository11" ).then( ).statusCode( 200 ); given( ).spec( getRequestSpec( token ) ).contentType( JSON ) .when( ) - .delete( "templates/archiva-repository-observer/repository11" ).then().statusCode( 200 ); + .delete( "templates/archiva-repository-observer/repository11" ).then( ).statusCode( 200 ); } } @@ -659,7 +714,7 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices { given( ).spec( getRequestSpec( token, getUserServicePath( ) ) ).contentType( JSON ) .when( ) - .delete( "aragorn" ).then().statusCode( 200 ); + .delete( "aragorn" ).then( ).statusCode( 200 ); } } @@ -709,13 +764,13 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices { given( ).spec( getRequestSpec( token, getUserServicePath( ) ) ).contentType( JSON ) .when( ) - .delete( "aragorn" ).then().statusCode( 200 ); + .delete( "aragorn" ).then( ).statusCode( 200 ); given( ).spec( getRequestSpec( token ) ).contentType( JSON ) .when( ) - .delete( "templates/archiva-repository-manager/repository12" ).then().statusCode( 200 ); + .delete( "templates/archiva-repository-manager/repository12" ).then( ).statusCode( 200 ); given( ).spec( getRequestSpec( token ) ).contentType( JSON ) .when( ) - .delete( "templates/archiva-repository-observer/repository12" ).then().statusCode( 200 ); + .delete( "templates/archiva-repository-observer/repository12" ).then( ).statusCode( 200 ); } } @@ -748,7 +803,13 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices assertEquals( "This description was updated.", updatedRole.getDescription( ) ); assertEquals( true, updatedRole.isAssignable( ) ); assertEquals( false, updatedRole.isPermanent( ) ); - assertArrayEquals( roleInfo.getAssignedUsers( ).toArray( ), updatedRole.getAssignedUsers( ).toArray( ) ); + response = given().spec(getRequestSpec(token)).contentType( JSON ) + .when() + .get("archiva-repository-manager.repository13/user") + .then() + .extract( ).response( ); + List<UserInfo> userList = response.getBody( ).jsonPath( ).getList( "data", UserInfo.class ); + assertEquals( 1, userList.size( ) ); } finally { @@ -812,8 +873,15 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices assertEquals( "New description", updatedRole.getDescription( ) ); assertEquals( false, updatedRole.isAssignable( ) ); assertEquals( true, updatedRole.isPermanent( ) ); - assertEquals( 2, updatedRole.getAssignedUsers( ).size() ); - assertTrue( updatedRole.getAssignedUsers( ).stream( ).filter( user -> "aragorn".equals( user.getUserId( ) ) ).findAny().isPresent() ); + + response = given().spec(getRequestSpec(token)).contentType( JSON ) + .when() + .get("archiva-repository-manager.repository14/user") + .then() + .extract( ).response( ); + List<UserInfo> userList = response.getBody( ).jsonPath( ).getList( "data", UserInfo.class ); + assertEquals( 2, userList.size( ) ); + assertTrue( userList.stream( ).filter( user -> "aragorn".equals( user.getUserId( ) ) ).findAny( ).isPresent( ) ); } finally { @@ -829,7 +897,7 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices given( ).spec( getRequestSpec( token, getUserServicePath( ) ) ).contentType( JSON ) .when( ) - .delete( "aragorn" ).then().statusCode( 200 ); + .delete( "aragorn" ).then( ).statusCode( 200 ); given( ).spec( getRequestSpec( token ) ).contentType( JSON ) .when( ) @@ -937,20 +1005,21 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices void updateRoleNotExist( ) { String token = getAdminToken( ); - Map<String, Object> jsonAsMap = new HashMap<>( ); - jsonAsMap.put( "id", "abcdefg" ); - jsonAsMap.put( "name", "abcdefg" ); - jsonAsMap.put( "description", "This description was updated." ); - given( ).spec( getRequestSpec( token ) ).contentType( JSON ) - .when( ) - .body( jsonAsMap ) - .patch( "abcdefg" ) - .then( ).statusCode( 404 ); + Map<String, Object> jsonAsMap = new HashMap<>( ); + jsonAsMap.put( "id", "abcdefg" ); + jsonAsMap.put( "name", "abcdefg" ); + jsonAsMap.put( "description", "This description was updated." ); + given( ).spec( getRequestSpec( token ) ).contentType( JSON ) + .when( ) + .body( jsonAsMap ) + .patch( "abcdefg" ) + .then( ).statusCode( 404 ); } @Test - void getTemplates() { + void getTemplates( ) + { String token = getAdminToken( ); Response response = given( ).spec( getRequestSpec( token ) ).contentType( JSON ) .when( ) @@ -959,8 +1028,8 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices assertNotNull( response ); List<RoleTemplate> templates = response.getBody( ).jsonPath( ).getList( "", RoleTemplate.class ); assertEquals( 2, templates.size( ) ); - assertTrue( templates.stream( ).filter( tmpl -> "archiva-repository-manager".equals( tmpl.getId( ) ) ).findAny().isPresent() ); - assertTrue( templates.stream( ).filter( tmpl -> "archiva-repository-observer".equals( tmpl.getId( ) ) ).findAny().isPresent() ); + assertTrue( templates.stream( ).filter( tmpl -> "archiva-repository-manager".equals( tmpl.getId( ) ) ).findAny( ).isPresent( ) ); + assertTrue( templates.stream( ).filter( tmpl -> "archiva-repository-observer".equals( tmpl.getId( ) ) ).findAny( ).isPresent( ) ); } }
