You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@archiva.apache.org by ma...@apache.org on 2020/11/26 21:20:30 UTC

[archiva-redback-core] branch master updated (04e2abd -> f86d739)

This is an automated email from the ASF dual-hosted git repository.

martin_s pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/archiva-redback-core.git.


    from 04e2abd  Changing jdk version and distribution for travis-ci
     new de23f72  Refactoring of role API and new Role V2 REST service
     new ec82e1a  Additional tests for role service
     new 86670fc  Moving template instance id method
     new 1ce5d0b  Additional tests for role service v2
     new f86d739  Switching to role id for userassignment

The 5 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 README.adoc                                        |  11 +
 .../redback/common/ldap/role/LdapRoleMapper.java   |   1 -
 .../security/LockedAdminEnvironmentCheck.java      |   2 +-
 .../security/role/RedbackRoleConstants.java        |   1 +
 .../apache/archiva/redback/rest/api/Constants.java |   2 +-
 .../archiva/redback/rest/api/MessageKeys.java      |  13 +-
 .../rest/api/model/{ => v2}/Application.java       |  26 +-
 .../{Application.java => v2/BaseGroupInfo.java}    |  71 +-
 .../redback/rest/api/model/v2/BaseRoleInfo.java    | 262 ++++++++
 .../redback/rest/api/model/v2/BaseUserInfo.java    |  59 +-
 .../archiva/redback/rest/api/model/v2/Role.java    | 104 +++
 .../redback/rest/api/model/v2/RoleInfo.java        | 182 ++++++
 .../redback/rest/api/model/v2/RoleTree.java        |  74 +++
 .../redback/rest/api/model/v2/UserInfo.java        |  36 +-
 .../redback/rest/api/services/v2/RoleService.java  | 446 +++++++------
 .../redback/rest/api/services/v2/UserService.java  |  61 +-
 .../services/DefaultRoleManagementService.java     |  38 +-
 .../rest/services/v2/BaseRedbackService.java       | 145 +++++
 .../rest/services/v2/DefaultGroupService.java      |   8 +-
 .../rest/services/v2/DefaultRoleService.java       | 477 ++++++++++++++
 .../rest/services/v2/DefaultUserService.java       | 185 ++++--
 .../redback/rest/services/v2/QueryHelper.java      | 168 +++++
 .../src/main/resources/META-INF/spring-context.xml |   2 +-
 .../services/v2/AbstractNativeRestServices.java    |   2 +-
 .../rest/services/v2/NativeGroupServiceTest.java   |   5 +-
 .../rest/services/v2/NativeRoleServiceTest.java    | 724 +++++++++++++++++++++
 .../rest/services/v2/NativeUserServiceTest.java    | 132 +++-
 .../archiva/redback/rbac/AbstractRBACManager.java  | 107 ++-
 .../apache/archiva/redback/rbac/AbstractRole.java  |   9 +-
 .../redback/rbac/AbstractUserAssignment.java       |  28 +
 .../apache/archiva/redback/rbac/RBACManager.java   |  30 +-
 .../archiva/redback/rbac/RBACObjectAssertions.java |   6 +-
 .../java/org/apache/archiva/redback/rbac/Role.java |  26 +
 .../archiva/redback/rbac/UserAssignment.java       |  17 +-
 .../redback/rbac/cached/CachedRbacManager.java     |  26 +-
 .../archiva/redback/rbac/jpa/JpaRbacManager.java   |  24 +-
 .../archiva/redback/rbac/jpa/model/JpaRole.java    |  44 +-
 .../redback/rbac/jpa/model/JpaUserAssignment.java  |  23 +-
 .../archiva/redback/rbac/jpa/model/RoleId.java     |  49 +-
 .../archiva/redback/rbac/ldap/LdapRbacManager.java | 170 ++++-
 .../redback/rbac/memory/MemoryRbacManager.java     |   8 +-
 .../archiva/redback/rbac/memory/MemoryRole.java    |  22 +
 .../redback/rbac/memory/MemoryUserAssignment.java  |  17 +
 .../archiva/redback/role/DefaultRoleManager.java   |  60 +-
 ...agerException.java => RoleExistsException.java} |  17 +-
 .../apache/archiva/redback/role/RoleManager.java   |   4 +-
 ...erException.java => RoleNotFoundException.java} |  17 +-
 .../role/processor/DefaultRoleModelProcessor.java  |   3 +-
 .../template/DefaultRoleTemplateProcessor.java     |  40 +-
 .../role/template/RoleTemplateProcessor.java       |  26 +-
 .../archiva/redback/role/util/RoleModelUtils.java  |   4 +
 .../redback/role/AbstractRoleManagerTest.java      |   8 +-
 .../AbstractRbacManagerPerformanceTestCase.java    |  14 +-
 .../redback/tests/AbstractRbacManagerTestCase.java |  75 ++-
 .../archiva/redback/tests/utils/RBACDefaults.java  |   7 +
 55 files changed, 3471 insertions(+), 647 deletions(-)
 copy redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/{ => v2}/Application.java (75%)
 copy redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/{Application.java => v2/BaseGroupInfo.java} (52%)
 create mode 100644 redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/BaseRoleInfo.java
 copy redback-authentication/redback-authentication-api/src/main/java/org/apache/archiva/redback/authentication/StringToken.java => redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/BaseUserInfo.java (52%)
 create mode 100644 redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/Role.java
 create mode 100644 redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/RoleInfo.java
 create mode 100644 redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/RoleTree.java
 create mode 100644 redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/v2/BaseRedbackService.java
 create mode 100644 redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/v2/DefaultRoleService.java
 create mode 100644 redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/v2/QueryHelper.java
 create mode 100644 redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/v2/NativeRoleServiceTest.java
 copy redback-authentication/redback-authentication-api/src/main/java/org/apache/archiva/redback/authentication/BearerTokenAuthenticationDataSource.java => redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/model/RoleId.java (54%)
 copy redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/{RoleManagerException.java => RoleExistsException.java} (76%)
 copy redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/{RoleManagerException.java => RoleNotFoundException.java} (76%)


[archiva-redback-core] 04/05: Additional tests for role service v2

Posted by ma...@apache.org.
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 1ce5d0be47434a2bf110cff1ff140aee929746f7
Author: Martin Stockhammer <ma...@apache.org>
AuthorDate: Wed Nov 25 20:29:43 2020 +0100

    Additional tests for role service v2
---
 .../redback/rest/api/services/v2/RoleService.java  |  20 +--
 .../rest/services/v2/DefaultRoleService.java       |  55 ++++++-
 .../rest/services/v2/NativeRoleServiceTest.java    | 172 ++++++++++++++++-----
 3 files changed, 196 insertions(+), 51 deletions(-)

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 bbb2502..de25961 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
@@ -285,7 +285,7 @@ public interface RoleService
      * @param roleId
      * @param userId
      */
-    @Path( "{roleId}/assign/{userId}" )
+    @Path( "{roleId}/user/{userId}" )
     @PUT
     @Produces( { APPLICATION_JSON } )
     @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
@@ -313,10 +313,10 @@ public interface RoleService
      *
      * @param templateId
      * @param resource
-     * @param principal
+     * @param userId
      */
-    @Path( "template/{templateId}/{resource}/assign/{userId}" )
-    @POST
+    @Path( "template/{templateId}/{resource}/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",
@@ -333,19 +333,19 @@ public interface RoleService
                 content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = RedbackRestError.class )) )
         }
     )
-    RoleInfo assignTemplatedRole( @QueryParam( "templateId" ) String templateId,
-                                 @QueryParam( "resource" ) String resource,
-                                 @QueryParam( "principal" ) String principal )
+    RoleInfo assignTemplatedRole( @PathParam( "templateId" ) String templateId,
+                                 @PathParam( "resource" ) String resource,
+                                 @PathParam( "userId" ) String userId )
         throws RedbackServiceException;
 
     /**
      * Unassigns the role indicated by the role id from the given principal
      *
      * @param roleId
-     * @param principal
+     * @param userId
      * @throws RedbackServiceException
      */
-    @Path( "{roleId}/{userId}" )
+    @Path( "{roleId}/user/{userId}" )
     @DELETE
     @Produces( { APPLICATION_JSON } )
     @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
@@ -363,7 +363,7 @@ public interface RoleService
                 content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = RedbackRestError.class )) )
         }
     )
-    RoleInfo unassignRole( @QueryParam( "roleId" ) String roleId, @QueryParam( "principal" ) String principal )
+    RoleInfo unassignRole( @PathParam( "roleId" ) String roleId, @PathParam( "userId" ) String userId )
         throws RedbackServiceException;
 
 
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 a4077b7..a1fae18 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
@@ -40,6 +40,7 @@ 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.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;
@@ -358,33 +359,75 @@ public class DefaultRoleService extends BaseRedbackService
 
 
     @Override
-    public RoleInfo assignTemplatedRole( String templateId, String resource, String principal )
+    public RoleInfo assignTemplatedRole( String templateId, String resource, String userId )
         throws RedbackServiceException
     {
         try
         {
-            roleManager.assignTemplatedRole( templateId, resource, principal );
+            userManager.findUser( userId );
+            roleManager.assignTemplatedRole( templateId, resource, userId );
+            String roleId = RoleModelUtils.getRoleId( templateId, resource );
+            return getRoleInfo( rbacManager.getRoleById( roleId ) );
+
+        }
+        catch ( RoleNotFoundException e ) {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_ROLE_NOT_FOUND, e.getMessage( ) ), 404 );
         }
         catch ( RoleManagerException e )
         {
             throw new RedbackServiceException( e.getMessage() );
         }
-        return null;
+        catch ( UserNotFoundException e )
+        {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_USER_NOT_FOUND, e.getMessage( ) ), 404 );
+        }
+        catch ( UserManagerException e )
+        {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_USERMANAGER_FAIL, e.getMessage( ) ) );
+        }
+        catch ( RbacObjectNotFoundException e )
+        {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_RBACMANAGER_FAIL, e.getMessage( ) ) );
+        }
+        catch ( RbacManagerException e )
+        {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_RBACMANAGER_FAIL, e.getMessage( ) ) );
+        }
     }
 
     @Override
-    public RoleInfo unassignRole( String roleId, String principal )
+    public RoleInfo unassignRole( String roleId, String userId )
         throws RedbackServiceException
     {
         try
         {
-            roleManager.unassignRole( roleId, principal );
+            userManager.findUser( userId );
+            roleManager.unassignRole( roleId, userId );
+            return getRoleInfo( rbacManager.getRoleById( roleId ) );
+        }
+        catch ( RoleNotFoundException e ) {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_ROLE_NOT_FOUND, e.getMessage( ) ), 404 );
         }
         catch ( RoleManagerException e )
         {
             throw new RedbackServiceException( e.getMessage() );
         }
-        return null;
+        catch ( UserNotFoundException e )
+        {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_USER_NOT_FOUND, e.getMessage( ) ), 404 );
+        }
+        catch ( UserManagerException e )
+        {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_USERMANAGER_FAIL, e.getMessage( ) ) );
+        }
+        catch ( RbacObjectNotFoundException e )
+        {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_RBACMANAGER_FAIL, e.getMessage( ) ) );
+        }
+        catch ( RbacManagerException e )
+        {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_RBACMANAGER_FAIL, e.getMessage( ) ) );
+        }
     }
 
     @Override
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 9d4b43f..863b871 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
@@ -140,8 +140,6 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices
     void deleteTemplatedRole( )
     {
         String token = getAdminToken( );
-        try
-        {
             given( ).spec( getRequestSpec( token ) ).contentType( JSON )
                 .when( )
                 .put( "template/archiva-repository-manager/repository05" )
@@ -158,15 +156,11 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices
                 .when( )
                 .delete( "template/archiva-repository-manager/repository05" )
                 .then( ).statusCode( 404 );
-        }
-        finally
-        {
             given( ).spec( getRequestSpec( token ) ).contentType( JSON )
                 .when( )
                 .delete( "template/archiva-repository-observer/repository05" )
                 .then( ).statusCode( 200 );
 
-        }
     }
 
     @Test
@@ -175,25 +169,25 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices
         String token = getAdminToken( );
         given( ).spec( getRequestSpec( token ) ).contentType( JSON )
             .when( )
-            .put( "template/archiva-repository-observer/repository01" )
+            .put( "template/archiva-repository-observer/repository06" )
             .then( ).statusCode( 201 );
         try
         {
             given( ).spec( getRequestSpec( token ) ).contentType( JSON )
                 .when( )
-                .head( "template/archiva-repository-observer/repository01" )
+                .head( "template/archiva-repository-observer/repository06" )
                 .then( ).statusCode( 200 );
 
             given( ).spec( getRequestSpec( token ) ).contentType( JSON )
                 .when( )
-                .head( "archiva-repository-observer.repository01" )
+                .head( "archiva-repository-observer.repository06" )
                 .then( ).statusCode( 200 );
         }
         finally
         {
             given( ).spec( getRequestSpec( token ) ).contentType( JSON )
                 .when( )
-                .delete( "template/archiva-repository-observer/repository01" )
+                .delete( "template/archiva-repository-observer/repository06" )
                 .then( ).statusCode( 200 );
         }
 
@@ -382,39 +376,39 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices
         {
             given( ).spec( getRequestSpec( token ) ).contentType( JSON )
                 .when( )
-                .put( "template/archiva-repository-manager/repository01" )
+                .put( "template/archiva-repository-manager/repository07" )
                 .then( ).statusCode( 201 );
             given( ).spec( getRequestSpec( token ) ).contentType( JSON )
-                .when( ).head( "template/archiva-repository-observer/repository01" ).then( ).statusCode( 200 );
+                .when( ).head( "template/archiva-repository-observer/repository07" ).then( ).statusCode( 200 );
 
             Response response = given( ).spec( getRequestSpec( token ) ).contentType( JSON )
-                .when( ).post( "template/archiva-repository-manager/repository01/moveto/repository02" ).then( ).statusCode( 201 ).extract( ).response( );
+                .when( ).post( "template/archiva-repository-manager/repository07/moveto/repository08" ).then( ).statusCode( 201 ).extract( ).response( );
             RoleInfo role = response.getBody( ).jsonPath( ).getObject( "", RoleInfo.class );
             assertNotNull( role );
-            assertEquals( "archiva-repository-manager.repository02", role.getId( ) );
-            assertEquals( "repository02", role.getResource( ) );
+            assertEquals( "archiva-repository-manager.repository08", role.getId( ) );
+            assertEquals( "repository08", role.getResource( ) );
             given( ).spec( getRequestSpec( token ) ).contentType( JSON )
-                .when( ).head( "template/archiva-repository-manager/repository01" ).then( ).statusCode( 404 );
+                .when( ).head( "template/archiva-repository-manager/repository07" ).then( ).statusCode( 404 );
             // Child templates are copied and not moved
             given( ).spec( getRequestSpec( token ) ).contentType( JSON )
-                .when( ).head( "template/archiva-repository-observer/repository01" ).then( ).statusCode( 200 );
+                .when( ).head( "template/archiva-repository-observer/repository07" ).then( ).statusCode( 200 );
             given( ).spec( getRequestSpec( token ) ).contentType( JSON )
-                .when( ).head( "template/archiva-repository-observer/repository02" ).then( ).statusCode( 200 );
+                .when( ).head( "template/archiva-repository-observer/repository08" ).then( ).statusCode( 200 );
 
         }
         finally
         {
             given( ).spec( getRequestSpec( token ) ).contentType( JSON )
                 .when( )
-                .delete( "template/archiva-repository-manager/repository02" )
+                .delete( "template/archiva-repository-manager/repository08" )
                 .then( ).statusCode( 200 );
             given( ).spec( getRequestSpec( token ) ).contentType( JSON )
                 .when( )
-                .delete( "template/archiva-repository-observer/repository01" )
+                .delete( "template/archiva-repository-observer/repository07" )
                 .then( ).statusCode( 200 );
             given( ).spec( getRequestSpec( token ) ).contentType( JSON )
                 .when( )
-                .delete( "template/archiva-repository-observer/repository02" )
+                .delete( "template/archiva-repository-observer/repository08" )
                 .then( ).statusCode( 200 );
 
         }
@@ -429,34 +423,37 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices
         {
             given( ).spec( getRequestSpec( token ) ).contentType( JSON )
                 .when( )
-                .put( "template/archiva-repository-manager/repository01" )
+                .put( "template/archiva-repository-manager/repository09" )
                 .then( ).statusCode( 201 );
             given( ).spec( getRequestSpec( token ) ).contentType( JSON )
                 .when( )
-                .put( "template/archiva-repository-manager/repository02" )
+                .put( "template/archiva-repository-manager/repository10" )
                 .then( ).statusCode( 201 );
             Response response = given( ).spec( getRequestSpec( token ) ).contentType( JSON )
                 .when( ).redirects( ).follow( false )
-                .post( "template/archiva-repository-manager/repository01/moveto/repository02" ).then( ).statusCode( 303 )
+                .post( "template/archiva-repository-manager/repository09/moveto/repository10" ).then( ).statusCode( 303 )
                 .extract( ).response( );
-            System.out.println( response.getHeader( "Location" ) );
-            assertTrue( response.getHeader( "Location" ).endsWith( "/roles/template/archiva-repository-manager/repository02" ) );
+            assertTrue( response.getHeader( "Location" ).endsWith( "/roles/template/archiva-repository-manager/repository10" ) );
             given( ).spec( getRequestSpec( token ) ).contentType( JSON )
-                .when( ).head( "template/archiva-repository-manager/repository01" ).then( ).statusCode( 200 );
+                .when( ).head( "template/archiva-repository-manager/repository09" ).then( ).statusCode( 200 );
         }
         finally
         {
             given( ).spec( getRequestSpec( token ) ).contentType( JSON )
                 .when( )
-                .delete( "template/archiva-repository-manager/repository01" )
+                .delete( "template/archiva-repository-manager/repository09" )
                 .then( ).statusCode( 200 );
             given( ).spec( getRequestSpec( token ) ).contentType( JSON )
                 .when( )
-                .delete( "template/archiva-repository-manager/repository02" )
+                .delete( "template/archiva-repository-observer/repository09" )
                 .then( ).statusCode( 200 );
             given( ).spec( getRequestSpec( token ) ).contentType( JSON )
                 .when( )
-                .delete( "template/archiva-repository-observer/repository02" )
+                .delete( "template/archiva-repository-manager/repository10" )
+                .then( ).statusCode( 200 );
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .delete( "template/archiva-repository-observer/repository10" )
                 .then( ).statusCode( 200 );
 
         }
@@ -490,7 +487,7 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices
             assertFalse( roles.stream( ).filter( role -> "system-administrator".equals( role.getId( ) ) ).findAny( ).isPresent( ) );
             given( ).spec( getRequestSpec( token ) ).contentType( JSON )
                 .when( )
-                .put( "system-administrator/assign/aragorn" )
+                .put( "system-administrator/user/aragorn" )
                 .prettyPeek( )
                 .then( ).statusCode( 200 );
             response = given( ).spec( getRequestSpec( token, getUserServicePath( ) ) ).contentType( JSON )
@@ -504,7 +501,7 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices
         {
             given( ).spec( getRequestSpec( token, getUserServicePath( ) ) ).contentType( JSON )
                 .when( )
-                .delete( "aragorn" ).getBody( );
+                .delete( "aragorn" ).then().statusCode( 200 );
         }
     }
 
@@ -534,7 +531,7 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices
             assertFalse( roles.stream( ).filter( role -> "abcdefg".equals( role.getId( ) ) ).findAny( ).isPresent( ) );
             given( ).spec( getRequestSpec( token ) ).contentType( JSON )
                 .when( )
-                .put( "abcdefg/assign/aragorn" )
+                .put( "abcdefg/user/aragorn" )
                 .prettyPeek( )
                 .then( ).statusCode( 404 );
             response = given( ).spec( getRequestSpec( token, getUserServicePath( ) ) ).contentType( JSON )
@@ -548,7 +545,7 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices
         {
             given( ).spec( getRequestSpec( token, getUserServicePath( ) ) ).contentType( JSON )
                 .when( )
-                .delete( "aragorn" ).getBody( );
+                .delete( "aragorn" ).then().statusCode( 200 );
         }
     }
 
@@ -558,9 +555,114 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices
         String token = getAdminToken( );
         given( ).spec( getRequestSpec( token ) ).contentType( JSON )
             .when( )
-            .put( "system-administrator/assign/aragorn" )
+            .put( "system-administrator/user/aragorn" )
             .prettyPeek( )
             .then( ).statusCode( 404 );
     }
 
+
+    @Test
+    void assignTemplatedRole( )
+    {
+        String token = getAdminToken( );
+        Map<String, Object> jsonAsMap = new HashMap<>( );
+        jsonAsMap.put( "user_id", "aragorn" );
+        jsonAsMap.put( "email", "aragorn@lordoftherings.org" );
+        jsonAsMap.put( "full_name", "Aragorn King of Gondor " );
+        jsonAsMap.put( "password", "pAssw0rD" );
+
+        try
+        {
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .put( "template/archiva-repository-manager/repository11" )
+                .then( ).statusCode( 201 );
+
+            given( ).spec( getRequestSpec( token, getUserServicePath( ) ) ).contentType( JSON )
+                .body( jsonAsMap )
+                .when( )
+                .post( )
+                .then( ).statusCode( 201 );
+
+            Response response = given( ).spec( getRequestSpec( token, getUserServicePath( ) ) ).contentType( JSON )
+                .when( )
+                .get( "aragorn/roles" )
+                .then( ).statusCode( 200 ).extract( ).response( );
+            List<RoleInfo> roles = response.getBody( ).jsonPath( ).getList( "", RoleInfo.class );
+            assertFalse( roles.stream( ).filter( role -> "archiva-repository-manager.repository11".equals( role.getId( ) ) ).findAny( ).isPresent( ) );
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .put( "template/archiva-repository-manager/repository11/user/aragorn" )
+                .prettyPeek( )
+                .then( ).statusCode( 200 );
+            response = given( ).spec( getRequestSpec( token, getUserServicePath( ) ) ).contentType( JSON )
+                .when( )
+                .get( "aragorn/roles" )
+                .then( ).statusCode( 200 ).extract( ).response( );
+            roles = response.getBody( ).jsonPath( ).getList( "", RoleInfo.class );
+            assertTrue( roles.stream( ).filter( role -> "archiva-repository-manager.repository11".equals( role.getId( ) ) ).findAny( ).isPresent( ) );
+        }
+        finally
+        {
+            given( ).spec( getRequestSpec( token, getUserServicePath( ) ) ).contentType( JSON )
+                .when( )
+                .delete( "aragorn" ).then().statusCode( 200 );
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .delete( "template/archiva-repository-manager/repository11" ).then().statusCode( 200 );
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .delete( "template/archiva-repository-observer/repository11" ).then().statusCode( 200 );
+
+        }
+    }
+
+    @Test
+    void unAssignRole( )
+    {
+        String token = getAdminToken( );
+        Map<String, Object> jsonAsMap = new HashMap<>( );
+        jsonAsMap.put( "user_id", "aragorn" );
+        jsonAsMap.put( "email", "aragorn@lordoftherings.org" );
+        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 response = given( ).spec( getRequestSpec( token, getUserServicePath( ) ) ).contentType( JSON )
+                .when( )
+                .get( "aragorn/roles" )
+                .then( ).statusCode( 200 ).extract( ).response( );
+            List<RoleInfo> roles = response.getBody( ).jsonPath( ).getList( "", RoleInfo.class );
+            assertTrue( roles.stream( ).filter( role -> "system-administrator".equals( role.getId( ) ) ).findAny( ).isPresent( ) );
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .delete( "system-administrator/user/aragorn" )
+                .then( ).statusCode( 200 );
+            response = given( ).spec( getRequestSpec( token, getUserServicePath( ) ) ).contentType( JSON )
+                .when( )
+                .get( "aragorn/roles" )
+                .then( ).statusCode( 200 ).extract( ).response( );
+            roles = response.getBody( ).jsonPath( ).getList( "", RoleInfo.class );
+            assertFalse( roles.stream( ).filter( role -> "system-administrator".equals( role.getId( ) ) ).findAny( ).isPresent( ) );
+        }
+        finally
+        {
+            given( ).spec( getRequestSpec( token, getUserServicePath( ) ) ).contentType( JSON )
+                .when( )
+                .delete( "aragorn" ).then().statusCode( 200 );
+        }
+    }
+
+
 }


[archiva-redback-core] 05/05: Switching to role id for userassignment

Posted by ma...@apache.org.
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 f86d7396248a1d467d1047a0217778b50311b0b6
Author: Martin Stockhammer <ma...@apache.org>
AuthorDate: Thu Nov 26 22:20:10 2020 +0100

    Switching to role id for userassignment
---
 .../security/LockedAdminEnvironmentCheck.java      |   2 +-
 .../security/role/RedbackRoleConstants.java        |   1 +
 .../services/DefaultRoleManagementService.java     |  35 ++++--
 .../rest/services/v2/BaseRedbackService.java       |   2 +-
 .../rest/services/v2/DefaultUserService.java       |  14 +--
 .../rest/services/v2/NativeGroupServiceTest.java   |   5 +-
 .../rest/services/v2/NativeRoleServiceTest.java    |  56 +++++++++
 .../archiva/redback/rbac/AbstractRBACManager.java  |  24 ++--
 .../redback/rbac/AbstractUserAssignment.java       |  28 +++++
 .../apache/archiva/redback/rbac/RBACManager.java   |   4 +-
 .../archiva/redback/rbac/RBACObjectAssertions.java |   6 +-
 .../archiva/redback/rbac/UserAssignment.java       |  17 ++-
 .../redback/rbac/cached/CachedRbacManager.java     |   4 +-
 .../archiva/redback/rbac/jpa/JpaRbacManager.java   |   6 +-
 .../redback/rbac/jpa/model/JpaUserAssignment.java  |  23 +++-
 .../archiva/redback/rbac/ldap/LdapRbacManager.java | 127 +++++++++++++++++----
 .../redback/rbac/memory/MemoryRbacManager.java     |   8 +-
 .../redback/rbac/memory/MemoryUserAssignment.java  |  17 +++
 .../archiva/redback/role/DefaultRoleManager.java   |  35 +++---
 .../redback/role/AbstractRoleManagerTest.java      |   8 +-
 .../AbstractRbacManagerPerformanceTestCase.java    |  12 +-
 .../redback/tests/AbstractRbacManagerTestCase.java |  69 +++++------
 .../archiva/redback/tests/utils/RBACDefaults.java  |   4 +
 23 files changed, 368 insertions(+), 139 deletions(-)

diff --git a/redback-integrations/redback-common-integrations/src/main/java/org/apache/archiva/redback/integration/checks/security/LockedAdminEnvironmentCheck.java b/redback-integrations/redback-common-integrations/src/main/java/org/apache/archiva/redback/integration/checks/security/LockedAdminEnvironmentCheck.java
index 3580637..3494823 100644
--- a/redback-integrations/redback-common-integrations/src/main/java/org/apache/archiva/redback/integration/checks/security/LockedAdminEnvironmentCheck.java
+++ b/redback-integrations/redback-common-integrations/src/main/java/org/apache/archiva/redback/integration/checks/security/LockedAdminEnvironmentCheck.java
@@ -74,7 +74,7 @@ public class LockedAdminEnvironmentCheck
         if ( !checked && !userManager.isReadOnly() )
         {
             List<String> roles = new ArrayList<String>();
-            roles.add( RedbackRoleConstants.SYSTEM_ADMINISTRATOR_ROLE );
+            roles.add( RedbackRoleConstants.SYSTEM_ADMINISTRATOR_ROLE_ID );
 
             List<? extends UserAssignment> systemAdminstrators;
             try
diff --git a/redback-integrations/redback-integrations-security/src/main/java/org/apache/archiva/redback/integration/security/role/RedbackRoleConstants.java b/redback-integrations/redback-integrations-security/src/main/java/org/apache/archiva/redback/integration/security/role/RedbackRoleConstants.java
index 44c9fd2..7ffb1a8 100644
--- a/redback-integrations/redback-integrations-security/src/main/java/org/apache/archiva/redback/integration/security/role/RedbackRoleConstants.java
+++ b/redback-integrations/redback-integrations-security/src/main/java/org/apache/archiva/redback/integration/security/role/RedbackRoleConstants.java
@@ -31,6 +31,7 @@ public interface RedbackRoleConstants
 
     // roles
     public static final String SYSTEM_ADMINISTRATOR_ROLE = "System Administrator";
+    public static final String SYSTEM_ADMINISTRATOR_ROLE_ID = "system-administrator";
 
     public static final String USER_ADMINISTRATOR_ROLE = "User Administrator";
 
diff --git a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultRoleManagementService.java b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultRoleManagementService.java
index 40ec9a3..6f936f6 100644
--- a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultRoleManagementService.java
+++ b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultRoleManagementService.java
@@ -60,7 +60,9 @@ import java.util.Comparator;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  * @author Olivier Lamy
@@ -390,13 +392,14 @@ public class DefaultRoleManagementService
             org.apache.archiva.redback.rbac.Role rbacRole = rbacManager.getRole( roleName );
             Role role = new Role( rbacRole );
 
-            Map<String, ? extends org.apache.archiva.redback.rbac.Role> parentRoles = rbacManager.getParentRoleNames( rbacRole );
-            for ( String parentRoleName : parentRoles.keySet() )
+            Map<String, ? extends org.apache.archiva.redback.rbac.Role> parentRoleIds = rbacManager.getParentRoleIds( rbacRole );
+            for ( String parentRoleId : parentRoleIds.keySet() )
             {
-                role.getParentRoleNames().add( parentRoleName );
+                org.apache.archiva.redback.rbac.Role rbacParentRole = rbacManager.getRoleById( parentRoleId );
+                role.getParentRoleNames().add( rbacParentRole.getName() );
             }
 
-            List<? extends UserAssignment> userAssignments = rbacManager.getUserAssignmentsForRoles( Arrays.asList( roleName ) );
+            List<? extends UserAssignment> userAssignments = rbacManager.getUserAssignmentsForRoles( Arrays.asList( rbacRole.getId() ) );
 
             if ( userAssignments != null )
             {
@@ -417,7 +420,7 @@ public class DefaultRoleManagementService
             if ( !role.getParentRoleNames().isEmpty() )
             {
                 List<? extends UserAssignment> userParentAssignments =
-                    rbacManager.getUserAssignmentsForRoles( parentRoles.keySet() );
+                    rbacManager.getUserAssignmentsForRoles( parentRoleIds.keySet() );
                 if ( userParentAssignments != null )
                 {
                     for ( UserAssignment userAssignment : userParentAssignments )
@@ -507,7 +510,8 @@ public class DefaultRoleManagementService
                     assignment = rbacManager.createUserAssignment( username );
                 }
 
-                assignment.addRoleName( role.getName() );
+                org.apache.archiva.redback.rbac.Role rbacRole = rbacManager.getRole( role.getName( ) );
+                assignment.addRoleId( rbacRole.getId() );
                 assignment = rbacManager.saveUserAssignment( assignment );
                 log.info( "{} role assigned to {}", role.getName(), username );
             }
@@ -548,7 +552,8 @@ public class DefaultRoleManagementService
                     assignment = rbacManager.createUserAssignment( username );
                 }
 
-                assignment.removeRoleName( role.getName() );
+                org.apache.archiva.redback.rbac.Role rbacRole = rbacManager.getRole( role.getName( ) );
+                assignment.removeRoleId( rbacRole.getId() );
                 assignment = rbacManager.saveUserAssignment( assignment );
                 log.info( "{} role unassigned to {}", role.getName(), username );
             }
@@ -724,10 +729,18 @@ public class DefaultRoleManagementService
             {
                 assignment = rbacManager.createUserAssignment( username );
             }
-
-            assignment.setRoleNames( user.getAssignedRoles() );
-
-            assignment = rbacManager.saveUserAssignment( assignment );
+            List<String> assignedRoleIds = user.getAssignedRoles().stream().map(roleName -> {
+                try
+                {
+                    return Optional.of( rbacManager.getRole( roleName ).getId( ) );
+                }
+                catch ( RbacManagerException e )
+                {
+                    return Optional.<String>empty( );
+                }
+            } ).filter( Optional::isPresent ).map(Optional::get).collect( Collectors.toList());
+            assignment.setRoleIds( assignedRoleIds );
+            rbacManager.saveUserAssignment( assignment );
 
         }
         catch ( RbacManagerException e )
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 63318bb..4dc9ab3 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
@@ -86,7 +86,7 @@ public class BaseRedbackService
     {
         try
         {
-            return rbacManager.getUserAssignmentsForRoles( recurseRoles( rbacRole ).map( role -> role.getName( ) ).collect( Collectors.toList( ) ) )
+            return rbacManager.getUserAssignmentsForRoles( recurseRoles( rbacRole ).map( role -> role.getId( ) ).collect( Collectors.toList( ) ) )
                 .stream( ).map( assignment -> getUserInfo( assignment.getPrincipal( ) ) ).collect( Collectors.toList( ) );
         }
         catch ( RuntimeException e )
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 5561d5c..f32d595 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
@@ -984,20 +984,20 @@ public class DefaultUserService extends BaseRedbackService
 
         try
         {
-            final Set<String> assignedRoleNames = new HashSet( rbacManager.getUserAssignment( username ).getRoleNames( ) );
+            final Set<String> assignedRoleIds = new HashSet( rbacManager.getUserAssignment( username ).getRoleIds( ) );
             // We have to reuse the BaseRoleInfo objects, because the roles are not returned starting from the roots
-            final Map<String, BaseRoleInfo> roleNameCache = new HashMap<>( );
+            final Map<String, BaseRoleInfo> roleIdCache = new HashMap<>( );
             List<BaseRoleInfo> roleList = rbacManager.getAllRoles( ).stream( ).flatMap( this::flattenRole ).map( role ->
             {
-                BaseRoleInfo roleInfo = roleNameCache.computeIfAbsent( role.getName( ), s -> new BaseRoleInfo( ) );
+                BaseRoleInfo roleInfo = roleIdCache.computeIfAbsent( role.getId( ), s -> new BaseRoleInfo( ) );
                 // Setting the role data, as there may be child role objects that are not completely initialized
                 roleInfo = BaseRoleInfo.of( role, roleInfo );
                 roleInfo.setApplicationId( roleApplicationMap.get( role.getId( ) ) );
-                roleInfo.setAssigned( assignedRoleNames.contains( role.getName( ) ) );
-                roleInfo.setChildren( role.getChildRoleNames( ).stream( )
-                    .map( roleName ->
+                roleInfo.setAssigned( assignedRoleIds.contains( role.getId( ) ) );
+                roleInfo.setChildren( role.getChildRoleIds( ).stream( )
+                    .map( roleId ->
                     {
-                        BaseRoleInfo childRoleInfo = roleNameCache.computeIfAbsent( roleName, s -> BaseRoleInfo.ofName( roleName ) );
+                        BaseRoleInfo childRoleInfo = roleIdCache.computeIfAbsent( roleId, s -> BaseRoleInfo.ofId( roleId ) );
                         childRoleInfo.setChild( true );
                         return childRoleInfo;
                     } )
diff --git a/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/v2/NativeGroupServiceTest.java b/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/v2/NativeGroupServiceTest.java
index 96d25dd..5e8e855 100644
--- a/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/v2/NativeGroupServiceTest.java
+++ b/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/v2/NativeGroupServiceTest.java
@@ -22,6 +22,7 @@ import io.restassured.filter.log.UrlDecoder;
 import io.restassured.http.ContentType;
 import io.restassured.response.Response;
 import org.apache.archiva.components.apacheds.ApacheDs;
+import org.apache.archiva.redback.rest.api.Constants;
 import org.apache.archiva.redback.rest.api.model.Group;
 import org.apache.archiva.redback.rest.api.model.v2.GroupMapping;
 import org.apache.archiva.redback.rest.services.BaseSetup;
@@ -323,7 +324,7 @@ public class NativeGroupServiceTest extends AbstractNativeRestServices
         List<Group> data = response.body( ).jsonPath( ).getList(  "data", Group.class );
         assertNotNull( data );
         assertEquals( Integer.valueOf( 0 ), response.body( ).jsonPath( ).get( "pagination.offset" ) );
-        assertEquals( Integer.valueOf( 1000 ), response.body( ).jsonPath( ).get( "pagination.limit" ) );
+        assertEquals( Integer.valueOf( Constants.DEFAULT_PAGE_LIMIT ), response.body( ).jsonPath( ).get( "pagination.limit" ) );
         assertEquals( Integer.valueOf( 6 ), response.body( ).jsonPath( ).get( "pagination.total_count" ) );
         assertEquals( 6, data.size( ) );
         String[] values = data.stream( ).map( ldapInfo -> ldapInfo.getName( ) ).sorted( ).collect( Collectors.toList( ) ).toArray( new String[0] );
@@ -363,7 +364,7 @@ public class NativeGroupServiceTest extends AbstractNativeRestServices
         List<Group> data = response.body( ).jsonPath( ).getList(  "data", Group.class );
         assertNotNull( data );
         assertEquals( Integer.valueOf( 2 ), response.body( ).jsonPath( ).get( "pagination.offset" ) );
-        assertEquals( Integer.valueOf( 1000 ), response.body( ).jsonPath( ).get( "pagination.limit" ) );
+        assertEquals( Integer.valueOf( Constants.DEFAULT_PAGE_LIMIT ), response.body( ).jsonPath( ).get( "pagination.limit" ) );
         assertEquals( Integer.valueOf( 6 ), response.body( ).jsonPath( ).get( "pagination.total_count" ) );
         assertEquals( 4, data.size( ) );
         String[] values = data.stream( ).map( ldapInfo -> ldapInfo.getName( ) ).sorted( ).collect( Collectors.toList( ) ).toArray( new String[0] );
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 863b871..b62b4fa 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
@@ -664,5 +664,61 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices
         }
     }
 
+    @Test
+    void unAssignTemplatedRole( )
+    {
+        String token = getAdminToken( );
+        Map<String, Object> jsonAsMap = new HashMap<>( );
+        jsonAsMap.put( "user_id", "aragorn" );
+        jsonAsMap.put( "email", "aragorn@lordoftherings.org" );
+        jsonAsMap.put( "full_name", "Aragorn King of Gondor " );
+        jsonAsMap.put( "password", "pAssw0rD" );
+
+        try
+        {
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .put( "template/archiva-repository-manager/repository12" )
+                .then( ).statusCode( 201 );
+            given( ).spec( getRequestSpec( token, getUserServicePath( ) ) ).contentType( JSON )
+                .body( jsonAsMap )
+                .when( )
+                .post( )
+                .then( ).statusCode( 201 );
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .put( "template/archiva-repository-manager/repository12/user/aragorn" )
+                .then( ).statusCode( 200 );
+            Response response = given( ).spec( getRequestSpec( token, getUserServicePath( ) ) ).contentType( JSON )
+                .when( )
+                .get( "aragorn/roles" )
+                .then( ).statusCode( 200 ).extract( ).response( );
+            List<RoleInfo> roles = response.getBody( ).jsonPath( ).getList( "", RoleInfo.class );
+            assertTrue( roles.stream( ).filter( role -> "archiva-repository-manager.repository12".equals( role.getId( ) ) ).findAny( ).isPresent( ) );
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .delete( "archiva-repository-manager.repository12/user/aragorn" )
+                .then( ).statusCode( 200 );
+            response = given( ).spec( getRequestSpec( token, getUserServicePath( ) ) ).contentType( JSON )
+                .when( )
+                .get( "aragorn/roles" )
+                .then( ).statusCode( 200 ).extract( ).response( );
+            roles = response.getBody( ).jsonPath( ).getList( "", RoleInfo.class );
+            assertFalse( roles.stream( ).filter( role -> "archiva-repository-manager.repository12".equals( role.getId( ) ) ).findAny( ).isPresent( ) );
+        }
+        finally
+        {
+            given( ).spec( getRequestSpec( token, getUserServicePath( ) ) ).contentType( JSON )
+                .when( )
+                .delete( "aragorn" ).then().statusCode( 200 );
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .delete( "template/archiva-repository-manager/repository12" ).then().statusCode( 200 );
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .delete( "template/archiva-repository-observer/repository12" ).then().statusCode( 200 );
+
+        }
+    }
 
 }
diff --git a/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/AbstractRBACManager.java b/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/AbstractRBACManager.java
index bdecd29..656a05b 100644
--- a/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/AbstractRBACManager.java
+++ b/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/AbstractRBACManager.java
@@ -432,17 +432,17 @@ public abstract class AbstractRBACManager
 
         Set<Permission> permissionSet = new HashSet<Permission>();
 
-        if ( ua.getRoleNames() != null )
+        if ( ua.getRoleIds() != null )
         {
             boolean childRoleNamesUpdated = false;
 
-            Iterator<String> it = ua.getRoleNames().listIterator();
+            Iterator<String> it = ua.getRoleIds().listIterator();
             while ( it.hasNext() )
             {
-                String roleName = it.next();
+                String roleId = it.next();
                 try
                 {
-                    Role role = getRole( roleName );
+                    Role role = getRoleById( roleId );
                     gatherUniquePermissions( role, permissionSet );
                 }
                 catch ( RbacObjectNotFoundException e )
@@ -563,17 +563,17 @@ public abstract class AbstractRBACManager
     {
         Set<Role> roleSet = new HashSet<Role>();
 
-        if ( ua.getRoleNames() != null )
+        if ( ua.getRoleIds() != null )
         {
             boolean childRoleNamesUpdated = false;
 
-            Iterator<String> it = ua.getRoleNames().listIterator();
+            Iterator<String> it = ua.getRoleIds().listIterator();
             while ( it.hasNext() )
             {
-                String roleName = it.next();
+                String roleId = it.next();
                 try
                 {
-                    Role role = getRole( roleName );
+                    Role role = getRoleById( roleId );
 
                     if ( !roleSet.contains( role ) )
                     {
@@ -650,17 +650,17 @@ public abstract class AbstractRBACManager
     {
         Set<Role> roleSet = new HashSet<Role>();
 
-        if ( ua != null && ua.getRoleNames() != null )
+        if ( ua != null && ua.getRoleIds() != null )
         {
             boolean childRoleNamesUpdated = false;
 
-            Iterator<String> it = ua.getRoleNames().listIterator();
+            Iterator<String> it = ua.getRoleIds().listIterator();
             while ( it.hasNext() )
             {
-                String roleName = it.next();
+                String roleId = it.next();
                 try
                 {
-                    Role role = getRole( roleName );
+                    Role role = getRoleById( roleId );
 
                     gatherEffectiveRoles( role, roleSet );
                 }
diff --git a/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/AbstractUserAssignment.java b/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/AbstractUserAssignment.java
index 80dabf0..8b8d194 100644
--- a/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/AbstractUserAssignment.java
+++ b/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/AbstractUserAssignment.java
@@ -33,6 +33,12 @@ public abstract class AbstractUserAssignment
         addRoleName( role.getName() );
     }
 
+    @Override
+    public void addRoleId( Role role )
+    {
+        addRoleId( role.getId( ) );
+    }
+
     public void addRoleName( String roleName )
     {
         List<String> names = getRoleNames();
@@ -43,6 +49,16 @@ public abstract class AbstractUserAssignment
         setRoleNames( names );
     }
 
+    @Override
+    public void addRoleId( String roleId )
+    {
+        final List<String> ids  = getRoleIds( );
+        if (!ids.contains( roleId )) {
+            ids.add( roleId );
+        }
+        setRoleIds( ids );
+    }
+
     public void removeRoleName( Role role )
     {
         removeRoleName( role.getName() );
@@ -52,4 +68,16 @@ public abstract class AbstractUserAssignment
     {
         getRoleNames().remove( roleName );
     }
+
+    @Override
+    public void removeRoleId( Role role )
+    {
+        removeRoleId( role.getId() );
+    }
+
+    @Override
+    public void removeRoleId( String roleId )
+    {
+        getRoleIds( ).remove( roleId );
+    }
 }
diff --git a/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/RBACManager.java b/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/RBACManager.java
index 16ac43d..289371b 100644
--- a/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/RBACManager.java
+++ b/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/RBACManager.java
@@ -433,10 +433,10 @@ public interface RBACManager
 
     /**
      * Returns the assignments for the given roles
-     * @param roleNames collection of role names
+     * @param roleIds collection of role names
      * @throws RbacManagerException if the access to the backend datastore failed
      */
-    List<? extends UserAssignment> getUserAssignmentsForRoles( Collection<String> roleNames )
+    List<? extends UserAssignment> getUserAssignmentsForRoles( Collection<String> roleIds )
         throws RbacManagerException;
 
     /**
diff --git a/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/RBACObjectAssertions.java b/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/RBACObjectAssertions.java
index 560b0f1..16bb780 100644
--- a/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/RBACObjectAssertions.java
+++ b/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/RBACObjectAssertions.java
@@ -140,7 +140,7 @@ public class RBACObjectAssertions
             throw new RbacObjectInvalidException( scope, "UserAssigment.principal cannot be empty." );
         }
 
-        if ( assignment.getRoleNames() == null )
+        if ( assignment.getRoleIds() == null )
         {
             throw new RbacObjectInvalidException( scope, "UserAssignment.roles cannot be null." );
         }
@@ -153,11 +153,11 @@ public class RBACObjectAssertions
         }
           */
         int i = 0;
-        for ( String name : assignment.getRoleNames() )
+        for ( String name : assignment.getRoleIds() )
         {
             if ( StringUtils.isEmpty( name ) )
             {
-                throw new RbacObjectInvalidException( scope, "UserAssignment.rolename[" + i + "] cannot be empty." );
+                throw new RbacObjectInvalidException( scope, "UserAssignment.roleid[" + i + "] cannot be empty." );
             }
             i++;
         }
diff --git a/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/UserAssignment.java b/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/UserAssignment.java
index 6d68c0a..5d1b974 100644
--- a/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/UserAssignment.java
+++ b/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/UserAssignment.java
@@ -48,7 +48,9 @@ public interface UserAssignment
      * @return List of &lt;{@link String}&gt; objects representing the Role Names.
      */
     List<String> getRoleNames();
-    
+
+    List<String> getRoleIds();
+
     /**
      * Add a rolename to this assignment.
      * 
@@ -62,6 +64,13 @@ public interface UserAssignment
      * @param roleName the role name.
      */
     void addRoleName( String roleName );
+
+    void addRoleId( Role role );
+    /**
+     * Adds a role id to this assignment
+     * @param roleId
+     */
+    void addRoleId( String roleId );
     
     /**
      * Remove a rolename from this assignment.
@@ -77,6 +86,10 @@ public interface UserAssignment
      */
     void removeRoleName( String roleName );
 
+    void removeRoleId( Role role );
+
+    void removeRoleId( String roleId );
+
     /**
      * Set the user principal object for this association.
      * 
@@ -92,6 +105,8 @@ public interface UserAssignment
      * @param roles the List of &lt;{@link String}&gt; objects representing the Role Names.
      */
     void setRoleNames( List<String> roles );
+
+    void setRoleIds( List<String> roles );
     
     /**
      * Test to see if the object is a permanent object or not.
diff --git a/redback-rbac/redback-rbac-providers/redback-rbac-cached/src/main/java/org/apache/archiva/redback/rbac/cached/CachedRbacManager.java b/redback-rbac/redback-rbac-providers/redback-rbac-cached/src/main/java/org/apache/archiva/redback/rbac/cached/CachedRbacManager.java
index 7fbbcdd..9a6258c 100644
--- a/redback-rbac/redback-rbac-providers/redback-rbac-cached/src/main/java/org/apache/archiva/redback/rbac/cached/CachedRbacManager.java
+++ b/redback-rbac/redback-rbac-providers/redback-rbac-cached/src/main/java/org/apache/archiva/redback/rbac/cached/CachedRbacManager.java
@@ -479,11 +479,11 @@ public class CachedRbacManager
     }
 
     @Override
-    public List<? extends UserAssignment> getUserAssignmentsForRoles( Collection<String> roleNames )
+    public List<? extends UserAssignment> getUserAssignmentsForRoles( Collection<String> roleIds )
         throws RbacManagerException
     {
         log.debug( "NOT CACHED - .getUserAssignmentsForRoles(Collection)" );
-        return this.rbacImpl.getUserAssignmentsForRoles( roleNames );
+        return this.rbacImpl.getUserAssignmentsForRoles( roleIds );
     }
 
     @Override
diff --git a/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/JpaRbacManager.java b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/JpaRbacManager.java
index 8bd3212..9253867 100644
--- a/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/JpaRbacManager.java
+++ b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/JpaRbacManager.java
@@ -466,11 +466,11 @@ public class JpaRbacManager extends AbstractRBACManager  {
     }
 
     @Override
-    public List<? extends UserAssignment> getUserAssignmentsForRoles(Collection<String> roleNames) throws RbacManagerException {
+    public List<? extends UserAssignment> getUserAssignmentsForRoles(Collection<String> roleIds ) throws RbacManagerException {
         try {
             final EntityManager em = getEm();
-            TypedQuery<JpaUserAssignment> q = em.createQuery("SELECT ua FROM JpaUserAssignment ua WHERE ua.roleNames IN :roles", JpaUserAssignment.class);
-            q.setParameter("roles", roleNames);
+            TypedQuery<JpaUserAssignment> q = em.createQuery("SELECT ua FROM JpaUserAssignment ua WHERE ua.roleIds IN :roles", JpaUserAssignment.class);
+            q.setParameter("roles", roleIds );
             return q.getResultList();
         } catch (Exception ex) {
             log.error("Query failed: {}",ex.getMessage(),ex);
diff --git a/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/model/JpaUserAssignment.java b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/model/JpaUserAssignment.java
index 7e64bdd..dcce5e3 100644
--- a/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/model/JpaUserAssignment.java
+++ b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/model/JpaUserAssignment.java
@@ -56,7 +56,8 @@ public class JpaUserAssignment extends AbstractUserAssignment implements UserAss
                     @JoinColumn(name = "PRINCIPAL_OID", referencedColumnName = "PRINCIPAL", nullable = false)
             }
     )
-    private List<String> roleNames = new ArrayList<String>();
+    private List<String> roleIds = new ArrayList<>( );
+
     @Column(name="PERMANENT", nullable = false)
     private Boolean permanent = false;
 
@@ -69,18 +70,30 @@ public class JpaUserAssignment extends AbstractUserAssignment implements UserAss
     }
 
     @Override
+    public List<String> getRoleNames( )
+    {
+        return roleIds;
+    }
+
+    @Override
     public void setPrincipal(String principal) {
         this.principal = principal;
     }
 
     @Override
-    public List<String> getRoleNames() {
-        return roleNames;
+    public void setRoleNames( List<String> roles )
+    {
+        this.roleIds = roles;
+    }
+
+    @Override
+    public List<String> getRoleIds() {
+        return roleIds;
     }
 
     @Override
-    public void setRoleNames(List<String> roleNames) {
-        this.roleNames = roleNames;
+    public void setRoleIds( List<String> roleIds ) {
+        this.roleIds = roleIds;
     }
 
     @Override
diff --git a/redback-rbac/redback-rbac-providers/redback-rbac-ldap/src/main/java/org/apache/archiva/redback/rbac/ldap/LdapRbacManager.java b/redback-rbac/redback-rbac-providers/redback-rbac-ldap/src/main/java/org/apache/archiva/redback/rbac/ldap/LdapRbacManager.java
index c5d9e8a..f38a46d 100644
--- a/redback-rbac/redback-rbac-providers/redback-rbac-ldap/src/main/java/org/apache/archiva/redback/rbac/ldap/LdapRbacManager.java
+++ b/redback-rbac/redback-rbac-providers/redback-rbac-ldap/src/main/java/org/apache/archiva/redback/rbac/ldap/LdapRbacManager.java
@@ -62,6 +62,7 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -688,7 +689,17 @@ public class LdapRbacManager
         {
             ldapConnection = ldapConnectionFactory.getConnection();
             context = ldapConnection.getDirContext();
-            List<String> roles = ldapRoleMapper.getRoles( username, context, getRealRoles() );
+            List<String> roles = ldapRoleMapper.getRoles( username, context, getRealRoles() )
+                .stream( ).map( roleName -> {
+                    try
+                    {
+                        return Optional.of( rbacImpl.getRole( roleName ).getId() );
+                    }
+                    catch ( RbacManagerException e )
+                    {
+                        return Optional.<String>empty( );
+                    }
+                } ).filter( Optional::isPresent ).map( Optional::get ).collect( Collectors.toList() );
 
             ua = new UserAssignmentImpl( username, roles );
 
@@ -714,11 +725,11 @@ public class LdapRbacManager
     }
 
     @Override
-    public List<? extends UserAssignment> getUserAssignmentsForRoles( Collection<String> roleNames )
+    public List<? extends UserAssignment> getUserAssignmentsForRoles( Collection<String> roleIds )
         throws RbacManagerException
     {
         // TODO from ldap
-        return this.rbacImpl.getUserAssignmentsForRoles( roleNames );
+        return this.rbacImpl.getUserAssignmentsForRoles( roleIds );
     }
 
     @Override
@@ -1114,27 +1125,41 @@ public class LdapRbacManager
 
             List<String> currentUserRoles =
                 ldapRoleMapper.getRoles( userAssignment.getPrincipal(), context, getRealRoles() );
+            Map<String, String> currentUserIds = currentUserRoles.stream( ).map( roleName -> {
+                try
+                {
+                    return Optional.of( rbacImpl.getRole( roleName ) );
+                }
+                catch ( RbacManagerException e )
+                {
+                    return Optional.<Role>empty( );
+                }
+            } ).filter( Optional::isPresent ).map(Optional::get)
+                .collect( Collectors.toMap( Role::getName, Role::getId ) );
 
-            for ( String role : userAssignment.getRoleNames() )
+            for ( String roleId : userAssignment.getRoleIds() )
             {
-                if ( !currentUserRoles.contains( role ) && writableLdap )
+                Role rbacRole = rbacImpl.getRoleById( roleId );
+                String roleName = rbacRole.getName( );
+                if ( !currentUserRoles.contains( roleName ) && writableLdap )
                 {
                     // role exists in ldap ?
-                    if ( !allRoles.contains( role ) )
+                    if ( !allRoles.contains( roleName ) )
                     {
-                        ldapRoleMapper.saveRole( role, context );
-                        allRoles.add( role );
+                        ldapRoleMapper.saveRole( roleName, context );
+                        allRoles.add( roleName );
                     }
-                    ldapRoleMapper.saveUserRole( role, userAssignment.getPrincipal(), context );
-                    currentUserRoles.add( role );
+                    ldapRoleMapper.saveUserRole( roleName, userAssignment.getPrincipal(), context );
+                    currentUserRoles.add( roleName );
+                    currentUserIds.put( roleName, rbacRole.getId( ) );
                 }
             }
 
-            for ( String role : currentUserRoles )
+            for ( String roleName : currentUserRoles )
             {
-                if ( !userAssignment.getRoleNames().contains( role ) && writableLdap )
+                if ( !userAssignment.getRoleIds().contains( currentUserIds.get(roleName) ) && writableLdap )
                 {
-                    ldapRoleMapper.removeUserRole( role, userAssignment.getPrincipal(), context );
+                    ldapRoleMapper.removeUserRole( roleName, userAssignment.getPrincipal(), context );
                 }
             }
 
@@ -1491,21 +1516,21 @@ public class LdapRbacManager
     {
         private String username;
 
-        private List<String> roleNames;
+        private List<String> roleIds;
 
         private boolean permanent;
 
-        private UserAssignmentImpl( String username, Collection<String> roleNames )
+        private UserAssignmentImpl( String username, Collection<String> roleIds )
         {
             this.username = username;
 
-            if ( roleNames == null )
+            if ( roleIds == null )
             {
-                this.roleNames = new ArrayList<String>();
+                this.roleIds = new ArrayList<>( );
             }
             else
             {
-                this.roleNames = new ArrayList<String>( roleNames );
+                this.roleIds = new ArrayList<>( roleIds );
             }
         }
 
@@ -1518,7 +1543,13 @@ public class LdapRbacManager
         @Override
         public List<String> getRoleNames()
         {
-            return this.roleNames;
+            return this.roleIds;
+        }
+
+        @Override
+        public List<String> getRoleIds( )
+        {
+            return this.roleIds;
         }
 
         @Override
@@ -1528,7 +1559,7 @@ public class LdapRbacManager
             {
                 return;
             }
-            this.roleNames.add( role.getName() );
+            this.roleIds.add( role.getName() );
         }
 
         @Override
@@ -1538,7 +1569,27 @@ public class LdapRbacManager
             {
                 return;
             }
-            this.roleNames.add( roleName );
+            this.roleIds.add( roleName );
+        }
+
+        @Override
+        public void addRoleId( Role role )
+        {
+            if ( role == null )
+            {
+                return;
+            }
+            this.roleIds.add( role.getId() );
+        }
+
+        @Override
+        public void addRoleId( String roleId )
+        {
+            if ( roleId == null )
+            {
+                return;
+            }
+            this.roleIds.add( roleId );
         }
 
         @Override
@@ -1548,7 +1599,7 @@ public class LdapRbacManager
             {
                 return;
             }
-            this.roleNames.remove( role.getName() );
+            this.roleIds.remove( role.getName() );
         }
 
         @Override
@@ -1558,7 +1609,27 @@ public class LdapRbacManager
             {
                 return;
             }
-            this.roleNames.remove( roleName );
+            this.roleIds.remove( roleName );
+        }
+
+        @Override
+        public void removeRoleId( Role role )
+        {
+            if ( role == null )
+            {
+                return;
+            }
+            this.roleIds.remove( role.getId() );
+        }
+
+        @Override
+        public void removeRoleId( String roleId )
+        {
+            if ( roleId == null )
+            {
+                return;
+            }
+            this.roleIds.remove( roleId );
         }
 
         @Override
@@ -1570,7 +1641,13 @@ public class LdapRbacManager
         @Override
         public void setRoleNames( List<String> roles )
         {
-            this.roleNames = roles;
+            this.roleIds = roles;
+        }
+
+        @Override
+        public void setRoleIds( List<String> roles )
+        {
+            this.roleIds = roles;
         }
 
         @Override
@@ -1591,7 +1668,7 @@ public class LdapRbacManager
             final StringBuilder sb = new StringBuilder();
             sb.append( "UserAssignmentImpl" );
             sb.append( "{username='" ).append( username ).append( '\'' );
-            sb.append( ", roleNames=" ).append( roleNames );
+            sb.append( ", roleNames=" ).append( roleIds );
             sb.append( ", permanent=" ).append( permanent );
             sb.append( '}' );
             return sb.toString();
diff --git a/redback-rbac/redback-rbac-providers/redback-rbac-memory/src/main/java/org/apache/archiva/redback/rbac/memory/MemoryRbacManager.java b/redback-rbac/redback-rbac-providers/redback-rbac-memory/src/main/java/org/apache/archiva/redback/rbac/memory/MemoryRbacManager.java
index 235d3ab..9ac875c 100644
--- a/redback-rbac/redback-rbac-providers/redback-rbac-memory/src/main/java/org/apache/archiva/redback/rbac/memory/MemoryRbacManager.java
+++ b/redback-rbac/redback-rbac-providers/redback-rbac-memory/src/main/java/org/apache/archiva/redback/rbac/memory/MemoryRbacManager.java
@@ -515,18 +515,18 @@ public class MemoryRbacManager
     }
 
     @Override
-    public List<UserAssignment> getUserAssignmentsForRoles( Collection<String> roleNames )
+    public List<UserAssignment> getUserAssignmentsForRoles( Collection<String> roleIds )
         throws RbacManagerException
     {
 
         List<UserAssignment> allUserAssignments = getAllUserAssignments();
-        List<UserAssignment> userAssignments = new ArrayList<UserAssignment>( allUserAssignments.size() );
+        List<UserAssignment> userAssignments = new ArrayList<>( allUserAssignments.size( ) );
 
         for ( UserAssignment ua : allUserAssignments )
         {
-            for ( String roleName : roleNames )
+            for ( String roleId : roleIds )
             {
-                if ( ua.getRoleNames().contains( roleName ) )
+                if ( ua.getRoleIds().contains( roleId ) )
                 {
                     userAssignments.add( ua );
                     break;
diff --git a/redback-rbac/redback-rbac-providers/redback-rbac-memory/src/main/java/org/apache/archiva/redback/rbac/memory/MemoryUserAssignment.java b/redback-rbac/redback-rbac-providers/redback-rbac-memory/src/main/java/org/apache/archiva/redback/rbac/memory/MemoryUserAssignment.java
index 49e01d2..fcf46a9 100644
--- a/redback-rbac/redback-rbac-providers/redback-rbac-memory/src/main/java/org/apache/archiva/redback/rbac/memory/MemoryUserAssignment.java
+++ b/redback-rbac/redback-rbac-providers/redback-rbac-memory/src/main/java/org/apache/archiva/redback/rbac/memory/MemoryUserAssignment.java
@@ -97,6 +97,17 @@ public class MemoryUserAssignment
         return this.roles;
     }
 
+    @Override
+    public List<String> getRoleIds( )
+    {
+        if ( this.roles == null )
+        {
+            this.roles = new ArrayList<String>( 0 );
+        }
+
+        return this.roles;
+    }
+
     /**
      * Method hashCode
      */
@@ -127,6 +138,12 @@ public class MemoryUserAssignment
         this.roles = roles;
     }
 
+    @Override
+    public void setRoleIds( List<String> roles )
+    {
+        this.roles = roles;
+    }
+
     /**
      * Method toString
      */
diff --git a/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/DefaultRoleManager.java b/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/DefaultRoleManager.java
index d83e642..5969633 100644
--- a/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/DefaultRoleManager.java
+++ b/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/DefaultRoleManager.java
@@ -205,9 +205,9 @@ public class DefaultRoleManager
             Role role = rbacManager.getRoleById( roleId );
 
             for ( UserAssignment assignment : rbacManager.getUserAssignmentsForRoles(
-                Arrays.asList( role.getName() ) ) )
+                Arrays.asList( role.getId() ) ) )
             {
-                assignment.removeRoleName( role );
+                assignment.removeRoleId( role );
                 rbacManager.saveUserAssignment( assignment );
             }
 
@@ -240,16 +240,19 @@ public class DefaultRoleManager
         String oldRoleName = template.getNamePrefix() + template.getDelimiter() + oldResource;
         String newRoleName = template.getNamePrefix() + template.getDelimiter() + newResource;
 
+        String oldRoleId = RoleModelUtils.getRoleId( templateId, oldResource );
+        String newRoleId = RoleModelUtils.getRoleId( templateId, newResource );
+
         try
         {
             Role role = rbacManager.getRole( oldRoleName );
 
             // remove the user assignments
             for ( UserAssignment assignment : rbacManager.getUserAssignmentsForRoles(
-                Arrays.asList( role.getName() ) ) )
+                Arrays.asList( role.getId() ) ) )
             {
-                assignment.removeRoleName( oldRoleName );
-                assignment.addRoleName( newRoleName );
+                assignment.removeRoleId( oldRoleId );
+                assignment.addRoleId( newRoleId );
                 rbacManager.saveUserAssignment( assignment );
             }
         }
@@ -286,7 +289,7 @@ public class DefaultRoleManager
                 userAssignment = rbacManager.createUserAssignment( principal );
             }
 
-            userAssignment.addRoleName( modelRole.getName() );
+            userAssignment.addRoleId( modelRole.getId() );
             rbacManager.saveUserAssignment( userAssignment );
         }
         catch ( RbacManagerException e )
@@ -301,6 +304,7 @@ public class DefaultRoleManager
     {
         try
         {
+            Role role = rbacManager.getRole( roleName );
             UserAssignment userAssignment;
 
             if ( rbacManager.userAssignmentExists( principal ) )
@@ -317,7 +321,7 @@ public class DefaultRoleManager
                 throw new RoleManagerException( "Unable to assign role: " + roleName + " does not exist." );
             }
 
-            userAssignment.addRoleName( roleName );
+            userAssignment.addRoleId( role.getId() );
             rbacManager.saveUserAssignment( userAssignment );
         }
         catch ( RbacManagerException e )
@@ -355,7 +359,7 @@ public class DefaultRoleManager
                 userAssignment = rbacManager.createUserAssignment( principal );
             }
 
-            userAssignment.addRoleName( modelTemplate.getNamePrefix() + modelTemplate.getDelimiter() + resource );
+            userAssignment.addRoleId( RoleModelUtils.getRoleId( modelTemplate.getId(),   resource ) );
             rbacManager.saveUserAssignment( userAssignment );
         }
         catch ( RbacManagerException e )
@@ -368,15 +372,10 @@ public class DefaultRoleManager
     public void unassignRole( String roleId, String principal )
         throws RoleManagerException
     {
-        ModelRole modelRole = RoleModelUtils.getModelRole( blessedModel, roleId );
-
-        if ( modelRole == null )
-        {
-            throw new RoleNotFoundException( "Unable to assign role: " + roleId + " does not exist." );
-        }
 
         try
         {
+            rbacManager.getRoleById( roleId );
             UserAssignment userAssignment;
 
             if ( rbacManager.userAssignmentExists( principal ) )
@@ -389,9 +388,12 @@ public class DefaultRoleManager
                     "UserAssignment for principal " + principal + "does not exist, can't unassign role." );
             }
 
-            userAssignment.removeRoleName( modelRole.getName() );
+            userAssignment.removeRoleId( roleId );
             rbacManager.saveUserAssignment( userAssignment );
         }
+        catch (RoleNotFoundException e) {
+            throw new RoleNotFoundException( "Unable to unassign role: " + roleId + " does not exist." );
+        }
         catch ( RbacManagerException e )
         {
             throw new RoleManagerException( "Unable to unassign role: unable to manage user assignment", e );
@@ -421,7 +423,8 @@ public class DefaultRoleManager
                 throw new RoleManagerException( "Unable to unassign role: " + roleName + " does not exist." );
             }
 
-            userAssignment.removeRoleName( roleName );
+            Role rbacRole = rbacManager.getRole( roleName );
+            userAssignment.removeRoleId( rbacRole.getId() );
             rbacManager.saveUserAssignment( userAssignment );
         }
         catch ( RbacManagerException e )
diff --git a/redback-rbac/redback-rbac-role-manager/src/test/java/org/apache/archiva/redback/role/AbstractRoleManagerTest.java b/redback-rbac/redback-rbac-role-manager/src/test/java/org/apache/archiva/redback/role/AbstractRoleManagerTest.java
index 50b303a..9bc074c 100644
--- a/redback-rbac/redback-rbac-role-manager/src/test/java/org/apache/archiva/redback/role/AbstractRoleManagerTest.java
+++ b/redback-rbac/redback-rbac-role-manager/src/test/java/org/apache/archiva/redback/role/AbstractRoleManagerTest.java
@@ -110,14 +110,14 @@ public abstract class AbstractRoleManagerTest
 
         UserAssignment assignment = rbacManager.getUserAssignment( principal );
 
-        List<String> assignments = assignment.getRoleNames();
+        List<String> assignments = assignment.getRoleIds();
 
         assertEquals( 3, assignments.size() );
 
-        for ( String roleName : assignments )
+        for ( String roleId : assignments )
         {
-            logger.info( roleName );
-            assertTrue( "Test Role".equals( roleName ) || "Foo 2 - frigid".equals( roleName ) || "Test Role 1".equals( roleName ) );
+            logger.info( roleId );
+            assertTrue( "test-role".equals( roleId ) || "test-template-2.frigid".equals( roleId ) || "test-role-1".equals( roleId ) );
         }
     }
 
diff --git a/redback-rbac/redback-rbac-tests/src/main/java/org/apache/archiva/redback/tests/AbstractRbacManagerPerformanceTestCase.java b/redback-rbac/redback-rbac-tests/src/main/java/org/apache/archiva/redback/tests/AbstractRbacManagerPerformanceTestCase.java
index 7d1512f..0c37200 100644
--- a/redback-rbac/redback-rbac-tests/src/main/java/org/apache/archiva/redback/tests/AbstractRbacManagerPerformanceTestCase.java
+++ b/redback-rbac/redback-rbac-tests/src/main/java/org/apache/archiva/redback/tests/AbstractRbacManagerPerformanceTestCase.java
@@ -176,7 +176,7 @@ public class AbstractRbacManagerPerformanceTestCase
         // Setup User / Assignment with 1 role.
         String username = "bob";
         UserAssignment assignment = manager.createUserAssignment( username );
-        assignment.addRoleName( devRole );
+        assignment.addRoleId( devRole );
         assignment = manager.saveUserAssignment( assignment );
 
         assertEquals( 1, manager.getAllUserAssignments().size() );
@@ -187,11 +187,11 @@ public class AbstractRbacManagerPerformanceTestCase
         assertEquals( 2, manager.getAllRoles().size() );
 
         // assign the same role again to the same user
-        assignment.addRoleName( devRole.getName() );
+        assignment.addRoleId( devRole.getId() );
         manager.saveUserAssignment( assignment );
 
         // we certainly shouldn't have 2 roles here now
-        assertEquals( 1, assignment.getRoleNames().size() );
+        assertEquals( 1, assignment.getRoleIds().size() );
 
         String bobId = assignment.getPrincipal();
 
@@ -203,7 +203,7 @@ public class AbstractRbacManagerPerformanceTestCase
         manager.saveRole( devPlusRole );
 
         assignment = manager.createUserAssignment( username );
-        assignment.addRoleName( devRole );
+        assignment.addRoleId( devRole );
         assignment = manager.saveUserAssignment( assignment );
 
         assertEquals( 2, manager.getAllUserAssignments().size() );
@@ -214,11 +214,11 @@ public class AbstractRbacManagerPerformanceTestCase
         assertEquals( 2, manager.getAllRoles().size() );
 
         // assign the same role again to the same user
-        assignment.addRoleName( devRole.getName() );
+        assignment.addRoleId( devRole.getId() );
         manager.saveUserAssignment( assignment );
 
         // we certainly shouldn't have 2 roles here now
-        assertEquals( 1, assignment.getRoleNames().size() );
+        assertEquals( 1, assignment.getRoleIds().size() );
 
         String janetId = assignment.getPrincipal();
 
diff --git a/redback-rbac/redback-rbac-tests/src/main/java/org/apache/archiva/redback/tests/AbstractRbacManagerTestCase.java b/redback-rbac/redback-rbac-tests/src/main/java/org/apache/archiva/redback/tests/AbstractRbacManagerTestCase.java
index bca655e..4994e21 100644
--- a/redback-rbac/redback-rbac-tests/src/main/java/org/apache/archiva/redback/tests/AbstractRbacManagerTestCase.java
+++ b/redback-rbac/redback-rbac-tests/src/main/java/org/apache/archiva/redback/tests/AbstractRbacManagerTestCase.java
@@ -104,7 +104,7 @@ public abstract class AbstractRbacManagerTestCase
     private Role getDeveloperRole()
         throws RbacManagerException
     {
-        Role role = rbacManager.createRole( "DEVELOPER" );
+        Role role = rbacManager.createRole( "developer","DEVELOPER" );
         role.setAssignable( true );
 
         Permission perm = rbacManager.createPermission( "EDIT_MY_USER", "EDIT", "User:Self" );
@@ -117,7 +117,7 @@ public abstract class AbstractRbacManagerTestCase
     private Role getProjectAdminRole()
         throws RbacManagerException
     {
-        Role role = rbacManager.createRole( "PROJECT_ADMIN" );
+        Role role = rbacManager.createRole( "project-admin","PROJECT_ADMIN" );
         role.setAssignable( true );
 
         Permission perm = rbacManager.createPermission( "EDIT_PROJECT", "EDIT", "Project:Foo" );
@@ -129,7 +129,8 @@ public abstract class AbstractRbacManagerTestCase
 
     private Role getSuperDeveloperRole()
     {
-        Role role = rbacManager.createRole( "SUPER_DEVELOPER" );
+        Role role = rbacManager.createRole( "super-developer","SUPER_DEVELOPER" );
+        role.setId( "super-developer" );
         role.setAssignable( true );
 
         return role;
@@ -402,7 +403,7 @@ public abstract class AbstractRbacManagerTestCase
 
         UserAssignment assignment = manager.createUserAssignment( adminPrincipal );
 
-        assignment.addRoleName( adminRole );
+        assignment.addRoleId( adminRole );
 
         manager.saveUserAssignment( assignment );
 
@@ -436,10 +437,10 @@ public abstract class AbstractRbacManagerTestCase
         // don't use admin as ldap group need at least one member
         String adminPrincipal = "theadmin";
         UserAssignment assignment = manager.createUserAssignment( adminPrincipal );
-        assignment.addRoleName( adminRole );
+        assignment.addRoleId( adminRole );
         assignment = manager.saveUserAssignment( assignment );
 
-        assertEquals( 1, assignment.getRoleNames().size() );
+        assertEquals( 1, assignment.getRoleIds().size() );
         assertEquals( 1, manager.getAssignedRoles( adminPrincipal ).size() );
     }
 
@@ -462,7 +463,7 @@ public abstract class AbstractRbacManagerTestCase
 
         UserAssignment ua = manager.createUserAssignment( adminPrincipal );
 
-        ua.addRoleName( admin );
+        ua.addRoleId( admin );
 
         manager.saveUserAssignment( ua );
 
@@ -536,7 +537,7 @@ public abstract class AbstractRbacManagerTestCase
         // Setup User / Assignment with 1 role.
         String username = "bob";
         UserAssignment assignment = manager.createUserAssignment( username );
-        assignment.addRoleName( developerRole );
+        assignment.addRoleId( developerRole );
         manager.saveUserAssignment( assignment );
 
         assertEquals( incAssignements( 1 ), manager.getAllUserAssignments().size() );
@@ -544,41 +545,41 @@ public abstract class AbstractRbacManagerTestCase
 
         // Create another role add it to manager.
         Role projectAdmin = getProjectAdminRole();
-        String projectAdminRoleName = projectAdmin.getName();
+        String projectAdminRoleId = projectAdmin.getId();
         manager.saveRole( projectAdmin );
 
         // Get User Assignment, add a second role
         UserAssignment bob = manager.getUserAssignment( username );
-        bob.addRoleName( projectAdminRoleName );
+        bob.addRoleId( projectAdminRoleId );
         bob = manager.saveUserAssignment( bob );
 
         assertEquals( incAssignements( 1 ), manager.getAllUserAssignments().size() );
         assertEquals( 2, manager.getAllRoles().size() );
-        assertEquals( 2, bob.getRoleNames().size() );
+        assertEquals( 2, bob.getRoleIds().size() );
         assertEquals( 0, manager.getUnassignedRoles( bob.getPrincipal() ).size() );
 
-        List<String> roles = bob.getRoleNames();
+        List<String> roles = bob.getRoleIds();
         assertEquals( 2, roles.size() );
 
         // Remove 1 role from bob, end up with 1 role for bob.
-        roles.remove( projectAdminRoleName );
+        roles.remove( projectAdminRoleId );
         assertEquals( 1, roles.size() );
-        bob.setRoleNames( roles );
+        bob.setRoleIds( roles );
         bob = manager.saveUserAssignment( bob );
-        assertEquals( "Should only have 1 role under bob now.", 1, bob.getRoleNames().size() );
+        assertEquals( "Should only have 1 role under bob now.", 1, bob.getRoleIds().size() );
         assertEquals( "Should have 2 total roles still.", 2, manager.getAllRoles().size() );
         assertEquals( "Should have 1 assignable role", 1, manager.getUnassignedRoles( bob.getPrincipal() ).size() );
 
         // Fetch bob again. see if role is missing.
         UserAssignment cousin = manager.getUserAssignment( username );
-        assertEquals( 1, cousin.getRoleNames().size() );
+        assertEquals( 1, cousin.getRoleIds().size() );
 
-        assertEquals( "Should only have 1 role under bob now.", 1, cousin.getRoleNames().size() );
+        assertEquals( "Should only have 1 role under bob now.", 1, cousin.getRoleIds().size() );
         assertEquals( "Should have 2 total roles still.", 2, manager.getAllRoles().size() );
 
         // remove the last role
-        roles.remove( developerRole.getName() );
-        bob.setRoleNames( roles );
+        roles.remove( developerRole.getId() );
+        bob.setRoleIds( roles );
         bob = manager.saveUserAssignment( bob );
         assertEquals( "Should have 2 assignable roles.", 2, manager.getUnassignedRoles( bob.getPrincipal() ).size() );
 
@@ -602,18 +603,18 @@ public abstract class AbstractRbacManagerTestCase
         // Setup User / Assignment with 1 role.
         String username = "bob";
         UserAssignment assignment = manager.createUserAssignment( username );
-        assignment.addRoleName( devRole );
+        assignment.addRoleId( devRole );
         assignment = manager.saveUserAssignment( assignment );
 
         assertEquals( incAssignements( 1 ), manager.getAllUserAssignments().size() );
         assertEquals( 1, manager.getAllRoles().size() );
 
         // assign the same role again to the same user
-        assignment.addRoleName( devRole.getName() );
+        assignment.addRoleId( devRole.getId() );
         manager.saveUserAssignment( assignment );
 
         // we certainly shouldn't have 2 roles here now
-        assertEquals( 1, assignment.getRoleNames().size() );
+        assertEquals( 1, assignment.getRoleIds().size() );
 
         /* Assert some event tracker stuff */
         assertEventTracker( 1, 0, 1, 0, true, true );
@@ -639,7 +640,7 @@ public abstract class AbstractRbacManagerTestCase
         // Setup User / Assignment with 1 role.
         String username = "bob";
         UserAssignment assignment = manager.createUserAssignment( username );
-        assignment.addRoleName( devRole );
+        assignment.addRoleId( devRole );
         assignment = manager.saveUserAssignment( assignment );
 
         assertEquals( incAssignements( 1 ), manager.getAllUserAssignments().size() );
@@ -650,11 +651,11 @@ public abstract class AbstractRbacManagerTestCase
         assertEquals( 2, manager.getAllRoles().size() );
 
         // assign the same role again to the same user
-        assignment.addRoleName( devRole.getName() );
+        assignment.addRoleId( devRole.getId() );
         manager.saveUserAssignment( assignment );
 
         // we certainly shouldn't have 2 roles here now
-        assertEquals( 1, assignment.getRoleNames().size() );
+        assertEquals( 1, assignment.getRoleIds().size() );
 
         /* Assert some event tracker stuff */
         assertEventTracker( 2, 0, 1, 0, true, true );
@@ -678,12 +679,12 @@ public abstract class AbstractRbacManagerTestCase
         String username = "bob";
 
         UserAssignment assignment = manager.createUserAssignment( username );
-        assignment.addRoleName( developerRole.getName() );
-        assignment.addRoleName( projectAdminRole.getName() );
-        assignment.addRoleName( adminRole.getName() );
+        assignment.addRoleId( developerRole.getId() );
+        assignment.addRoleId( projectAdminRole.getId() );
+        assignment.addRoleId( adminRole.getId() );
         assignment = manager.saveUserAssignment( assignment );
 
-        assertThat( assignment.getRoleNames() ).isNotNull().isNotEmpty().hasSize( 3 );
+        assertThat( assignment.getRoleIds() ).isNotNull().isNotEmpty().hasSize( 3 );
         assertThat( manager.getAllUserAssignments() ).isNotNull().isNotEmpty().hasSize( incAssignements( 1 ) );
 
         assertThat( manager.getAllRoles() ).isNotNull().isNotEmpty().hasSize( 3 );
@@ -718,7 +719,7 @@ public abstract class AbstractRbacManagerTestCase
         manager.saveRole( getAdminRole() );
         manager.saveRole( getProjectAdminRole() );
         Role added = manager.saveRole( getDeveloperRole() );
-        String roleName = added.getName();
+        String roleId = added.getId();
 
         assertThat( manager.getAllRoles() ).isNotNull().isNotEmpty().hasSize( 3 );
         assertThat( manager.getAllPermissions() ).isNotNull().isNotEmpty().hasSize( 3 );
@@ -727,7 +728,7 @@ public abstract class AbstractRbacManagerTestCase
         String username = "bob";
 
         UserAssignment assignment = manager.createUserAssignment( username );
-        assignment.addRoleName( roleName );
+        assignment.addRoleId( roleId );
         manager.saveUserAssignment( assignment );
 
         assertThat( manager.getAllUserAssignments() ).isNotNull().isNotEmpty().hasSize( incAssignements( 1 ) );
@@ -816,7 +817,7 @@ public abstract class AbstractRbacManagerTestCase
         String username = "bob";
 
         UserAssignment assignment = rbacManager.createUserAssignment( username );
-        assignment.addRoleName( "Developer" );
+        assignment.addRoleId( "developer" );
         rbacManager.saveUserAssignment( assignment );
 
         assertEquals( incAssignements( 1 ), rbacManager.getAllUserAssignments().size() );
@@ -824,7 +825,7 @@ public abstract class AbstractRbacManagerTestCase
         assertEquals( 6, rbacManager.getAllPermissions().size() );
 
         // Get the List of Assigned Roles for user bob.
-        Role devel = rbacManager.getRole( "Developer" );
+        Role devel = rbacManager.getRoleById( "developer" );
         assertNotNull( devel );
 
         // First Depth.
@@ -850,7 +851,7 @@ public abstract class AbstractRbacManagerTestCase
         String username = "bob";
 
         UserAssignment assignment = rbacManager.createUserAssignment( username );
-        assignment.addRoleName( "Developer" );
+        assignment.addRoleId( "developer" );
         rbacManager.saveUserAssignment( assignment );
 
         assertEquals( incAssignements( 1 ), rbacManager.getAllUserAssignments().size() );
diff --git a/redback-rbac/redback-rbac-tests/src/main/java/org/apache/archiva/redback/tests/utils/RBACDefaults.java b/redback-rbac/redback-rbac-tests/src/main/java/org/apache/archiva/redback/tests/utils/RBACDefaults.java
index a451485..024a664 100644
--- a/redback-rbac/redback-rbac-tests/src/main/java/org/apache/archiva/redback/tests/utils/RBACDefaults.java
+++ b/redback-rbac/redback-rbac-tests/src/main/java/org/apache/archiva/redback/tests/utils/RBACDefaults.java
@@ -149,6 +149,7 @@ public class RBACDefaults
         if ( !manager.roleExists( "User Administrator" ) )
         {
             Role userAdmin = manager.createRole( "User Administrator" );
+            userAdmin.setId( "user-administrator" );
             userAdmin.addPermission( manager.getPermission( "Edit All Users" ) );
             userAdmin.addPermission( manager.getPermission( "Remove Roles" ) );
             userAdmin.setAssignable( true );
@@ -158,6 +159,7 @@ public class RBACDefaults
         if ( !manager.roleExists( "System Administrator" ) )
         {
             Role admin = manager.createRole( "System Administrator" );
+            admin.setId( "system-administrator" );
             admin.addChildRoleName( "User Administrator" );
             admin.addChildRoleId( "user-administrator" );
             admin.addPermission( manager.getPermission( "Edit Configuration" ) );
@@ -171,6 +173,7 @@ public class RBACDefaults
         if ( !manager.roleExists( "Trusted Developer" ) )
         {
             Role developer = manager.createRole( "Trusted Developer" );
+            developer.setId( "trusted-developer" );
             developer.addChildRoleName( "System Administrator" );
             developer.addChildRoleId( "system-administrator" );
             developer.addPermission( manager.getPermission( "Run Indexer" ) );
@@ -181,6 +184,7 @@ public class RBACDefaults
         if ( !manager.roleExists( "Developer" ) )
         {
             Role developer = manager.createRole( "Developer" );
+            developer.setId( "developer" );
             developer.addChildRoleName( "Trusted Developer" );
             developer.addChildRoleId( "trusted-developer" );
             developer.addPermission( manager.getPermission( "Run Indexer" ) );


[archiva-redback-core] 03/05: Moving template instance id method

Posted by ma...@apache.org.
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 86670fc6499c258405220fae46c3d0ce4408aabf
Author: Martin Stockhammer <ma...@apache.org>
AuthorDate: Wed Nov 25 20:29:30 2020 +0100

    Moving template instance id method
---
 .../main/java/org/apache/archiva/redback/role/DefaultRoleManager.java | 4 ++--
 .../archiva/redback/role/template/DefaultRoleTemplateProcessor.java   | 2 +-
 .../java/org/apache/archiva/redback/role/util/RoleModelUtils.java     | 4 ++++
 3 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/DefaultRoleManager.java b/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/DefaultRoleManager.java
index 9fbf5c9..d83e642 100644
--- a/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/DefaultRoleManager.java
+++ b/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/DefaultRoleManager.java
@@ -334,7 +334,7 @@ public class DefaultRoleManager
 
         if ( modelTemplate == null )
         {
-            throw new RoleManagerException( "Unable to assign role: " + templateId + " does not exist." );
+            throw new RoleNotFoundException( "Unable to assign role: " + templateId + " does not exist." );
         }
         try
         {
@@ -372,7 +372,7 @@ public class DefaultRoleManager
 
         if ( modelRole == null )
         {
-            throw new RoleManagerException( "Unable to assign role: " + roleId + " does not exist." );
+            throw new RoleNotFoundException( "Unable to assign role: " + roleId + " does not exist." );
         }
 
         try
diff --git a/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/template/DefaultRoleTemplateProcessor.java b/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/template/DefaultRoleTemplateProcessor.java
index f7533c1..dd801d1 100644
--- a/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/template/DefaultRoleTemplateProcessor.java
+++ b/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/template/DefaultRoleTemplateProcessor.java
@@ -178,7 +178,7 @@ public class DefaultRoleTemplateProcessor
 
     @Override
     public String getRoleId( String templateId, String resource) {
-        return templateId + "." + resource;
+        return RoleModelUtils.getRoleId( templateId, resource );
     }
 
     @SuppressWarnings("unchecked")
diff --git a/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/util/RoleModelUtils.java b/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/util/RoleModelUtils.java
index 645cdcc..5c03a0b 100644
--- a/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/util/RoleModelUtils.java
+++ b/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/util/RoleModelUtils.java
@@ -372,4 +372,8 @@ public class RoleModelUtils
         return sortedGraph;
     }
 
+    public static String getRoleId( String templateId, String resource) {
+        return templateId + "." + resource;
+    }
+
 }


[archiva-redback-core] 01/05: Refactoring of role API and new Role V2 REST service

Posted by ma...@apache.org.
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 de23f72bf11a46ab0dd3b5f67d82d4101b20b897
Author: Martin Stockhammer <ma...@apache.org>
AuthorDate: Tue Nov 24 21:28:42 2020 +0100

    Refactoring of role API and new Role V2 REST service
---
 README.adoc                                        |  11 +
 .../redback/common/ldap/role/LdapRoleMapper.java   |   1 -
 .../apache/archiva/redback/rest/api/Constants.java |   2 +-
 .../archiva/redback/rest/api/MessageKeys.java      |  13 +-
 .../redback/rest/api/model/v2/Application.java     |  89 ++++
 .../redback/rest/api/model/v2/BaseGroupInfo.java   |  80 ++++
 .../redback/rest/api/model/v2/BaseRoleInfo.java    | 262 +++++++++++
 .../redback/rest/api/model/v2/BaseUserInfo.java    |  67 +++
 .../archiva/redback/rest/api/model/v2/Role.java    | 104 +++++
 .../redback/rest/api/model/v2/RoleInfo.java        | 182 ++++++++
 .../redback/rest/api/model/v2/RoleTree.java        |  74 +++
 .../redback/rest/api/model/v2/UserInfo.java        |  36 +-
 .../redback/rest/api/services/v2/RoleService.java  | 444 ++++++++++--------
 .../redback/rest/api/services/v2/UserService.java  |  61 ++-
 .../services/DefaultRoleManagementService.java     |   5 +-
 .../rest/services/v2/BaseRedbackService.java       | 145 ++++++
 .../rest/services/v2/DefaultGroupService.java      |   8 +-
 .../rest/services/v2/DefaultRoleService.java       | 434 ++++++++++++++++++
 .../rest/services/v2/DefaultUserService.java       | 185 +++++---
 .../redback/rest/services/v2/QueryHelper.java      | 168 +++++++
 .../src/main/resources/META-INF/spring-context.xml |   2 +-
 .../services/v2/AbstractNativeRestServices.java    |   2 +-
 .../rest/services/v2/NativeRoleServiceTest.java    | 505 +++++++++++++++++++++
 .../rest/services/v2/NativeUserServiceTest.java    | 132 +++++-
 .../archiva/redback/rbac/AbstractRBACManager.java  |  83 +++-
 .../apache/archiva/redback/rbac/AbstractRole.java  |   9 +-
 .../apache/archiva/redback/rbac/RBACManager.java   |  26 +-
 .../java/org/apache/archiva/redback/rbac/Role.java |  26 ++
 .../redback/rbac/cached/CachedRbacManager.java     |  22 +-
 .../archiva/redback/rbac/jpa/JpaRbacManager.java   |  18 +-
 .../archiva/redback/rbac/jpa/model/JpaRole.java    |  44 +-
 .../archiva/redback/rbac/jpa/model/RoleId.java     |  40 +-
 .../archiva/redback/rbac/ldap/LdapRbacManager.java |  45 +-
 .../archiva/redback/rbac/memory/MemoryRole.java    |  22 +
 .../archiva/redback/role/DefaultRoleManager.java   |  23 +-
 .../archiva/redback/role/RoleExistsException.java  |  16 +-
 .../apache/archiva/redback/role/RoleManager.java   |   4 +-
 .../redback/role/RoleNotFoundException.java        |  16 +-
 .../role/processor/DefaultRoleModelProcessor.java  |   3 +-
 .../template/DefaultRoleTemplateProcessor.java     |  40 +-
 .../role/template/RoleTemplateProcessor.java       |  26 +-
 .../AbstractRbacManagerPerformanceTestCase.java    |   2 +
 .../redback/tests/AbstractRbacManagerTestCase.java |   6 +-
 .../archiva/redback/tests/utils/RBACDefaults.java  |   3 +
 44 files changed, 3097 insertions(+), 389 deletions(-)

diff --git a/README.adoc b/README.adoc
index 568868b..646c17a 100644
--- a/README.adoc
+++ b/README.adoc
@@ -2,6 +2,17 @@ Archiva Redback - Documentation
 ===============================
 :toc:
 
+== Update Information for 3.0
+
+=== Database Schema Changes
+
+==== org.apache.archiva.redback.rbac.Role
+
+New fields:
+id, modelId, isTemplateInstance, resource
+
+Primary key changed from name to combined key id,name
+
 
 == How to build and publish the pages for the archiva web content
 
diff --git a/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/role/LdapRoleMapper.java b/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/role/LdapRoleMapper.java
index 1ee2cfe..43bea95 100644
--- a/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/role/LdapRoleMapper.java
+++ b/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/role/LdapRoleMapper.java
@@ -66,7 +66,6 @@ public interface LdapRoleMapper
     boolean hasRole( DirContext context, String role )
         throws MappingException;
 
-
     /**
      * @return the base dn which contains all ldap groups
      */
diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/Constants.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/Constants.java
index 3faaf8c..f70ad58 100644
--- a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/Constants.java
+++ b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/Constants.java
@@ -23,7 +23,7 @@ package org.apache.archiva.redback.rest.api;
  */
 public interface Constants
 {
-    String DEFAULT_PAGE_LIMIT = "1000";
+    String DEFAULT_PAGE_LIMIT = "100";
 
 
 }
diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/MessageKeys.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/MessageKeys.java
index 6f7219a..51f762d 100644
--- a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/MessageKeys.java
+++ b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/MessageKeys.java
@@ -40,10 +40,19 @@ public interface MessageKeys
     String ERR_USER_ADMIN_EXISTS = "rb.user.admin.exists";
     String ERR_USER_ADMIN_BAD_NAME = "rb.user.admin.badname";
     String ERR_USER_NOT_FOUND = "rb.user.not_found";
+    String ERR_USER_BAD_PASSWORD = "rb.user.bad.password";
     String ERR_PASSWORD_VIOLATION = "rb.user.password_violation";
+
     String ERR_LDAP_GENERIC = "rb.ldap.error";
     String ERR_ROLE_MAPPING = "rb.role.mapping.error";
     String ERR_ROLE_MAPPING_NOT_FOUND = "rb.role.mapping.not_found";
+    String ERR_ROLE_NOT_FOUND = "rb.role.not_found";
+    // A template instance not found. With arguments templateId, resource
+    String ERR_ROLE_INSTANCE_NOT_FOUND = "rb.role.instance.not_found";
+    String ERR_ROLE_EXISTS = "rb.role.exists";
+    // A template instance exists. With arguments templateId, resource
+    String ERR_ROLE_INSTANCE_EXISTS = "rb.role.instance.exists";
+
     String ERR_AUTH_BAD_CODE = "rb.auth.bad_authorization_code";
     String ERR_AUTH_INVALID_CREDENTIALS = "rb.auth.invalid_credentials";
     String ERR_AUTH_FAIL_MSG = "rb.auth.fail";
@@ -52,9 +61,11 @@ public interface MessageKeys
     String ERR_AUTH_UNSUPPORTED_GRANT_TYPE = "rb.auth.unsupported_grant";
     String ERR_AUTH_INVALID_TOKEN = "rb.auth.invalid_token";
     String ERR_AUTH_UNAUTHORIZED_REQUEST = "rb.auth.unauthorized_request";
+
     String ERR_PASSWD_RESET_FAILED = "rb.passwd.reset.fail";
-    String ERR_USER_BAD_PASSWORD = "rb.user.bad.password";
+
     String ERR_REGISTRATION_KEY_INVALID = "rb.registration.key.invalid";
     String ERR_REGISTRATION_USER_VALIDATED = "rb.registration.user.validated";
     String ERR_REGISTRATION_ROLE_ASSIGNMENT_FAILED = "rb.registration.role.assignment.failed";
+
 }
diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/Application.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/Application.java
new file mode 100644
index 0000000..c38392c
--- /dev/null
+++ b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/Application.java
@@ -0,0 +1,89 @@
+package org.apache.archiva.redback.rest.api.model.v2;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import java.io.Serializable;
+
+/**
+ * @author Olivier Lamy
+ */
+@XmlRootElement( name = "application" )
+@Schema(name="Application", description = "A single application that is used for defining roles")
+public class Application
+    implements Serializable
+{
+    private static final long serialVersionUID = -4738856943947960583L;
+
+    private String version;
+    private String id;
+    private String description;
+    private String longDescription;
+
+    public Application()
+    {
+        // no op
+    }
+
+    @Schema(description = "The application version. Used to separate different sets of roles.")
+    public String getVersion()
+    {
+        return version;
+    }
+
+    public void setVersion( String version )
+    {
+        this.version = version;
+    }
+
+    @Schema(description = "The identifier of the application")
+    public String getId()
+    {
+        return id;
+    }
+
+    public void setId( String id )
+    {
+        this.id = id;
+    }
+
+    @Schema(description = "A short description.")
+    public String getDescription()
+    {
+        return description;
+    }
+
+    public void setDescription( String description )
+    {
+        this.description = description;
+    }
+
+    @Schema(description = "May be a longer explanation, of the application purpose and its defined roles.")
+    public String getLongDescription()
+    {
+        return longDescription;
+    }
+
+    public void setLongDescription( String longDescription )
+    {
+        this.longDescription = longDescription;
+    }
+}
diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/BaseGroupInfo.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/BaseGroupInfo.java
new file mode 100644
index 0000000..22ff830
--- /dev/null
+++ b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/BaseGroupInfo.java
@@ -0,0 +1,80 @@
+package org.apache.archiva.redback.rest.api.model.v2;/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import java.io.Serializable;
+
+/**
+ * Information about a group.
+ *
+ * @since 3.0
+ * @author Martin Stockhammer <ma...@apache.org>
+ */
+@Schema( name = "Group", description = "Group information" )
+public class BaseGroupInfo implements Serializable
+{
+    private static final long serialVersionUID = 2945927911204165322L;
+    private String id;
+    private String groupName;
+    private String description = "";
+
+    public BaseGroupInfo( )
+    {
+
+    }
+
+    public BaseGroupInfo( String id, String groupName )
+    {
+        this.id = id;
+        this.groupName = groupName;
+    }
+
+    @Schema(description = "The name of the group")
+    public String getGroupName( )
+    {
+        return groupName;
+    }
+
+    public void setGroupName( String groupName )
+    {
+        this.groupName = groupName;
+    }
+
+    @Schema( description = "The unique identifier of the group" )
+    public String getId( )
+    {
+        return id;
+    }
+
+    public void setId( String id )
+    {
+        this.id = id;
+    }
+
+    @Schema( description = "A description of the group" )
+    public String getDescription( )
+    {
+        return description;
+    }
+
+    public void setDescription( String description )
+    {
+        this.description = description;
+    }
+}
diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/BaseRoleInfo.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/BaseRoleInfo.java
new file mode 100644
index 0000000..126a5e0
--- /dev/null
+++ b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/BaseRoleInfo.java
@@ -0,0 +1,262 @@
+package org.apache.archiva.redback.rest.api.model.v2;/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import org.apache.archiva.redback.rbac.Role;
+import org.apache.archiva.redback.role.model.ModelRole;
+
+import javax.xml.bind.annotation.XmlTransient;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Basic role information. This class contains only the standard attributes used for displaying a role.
+ *
+ * @author Martin Stockhammer <ma...@apache.org>
+ */
+@Schema(name="BaseRoleInfo", description = "Basic role attributes")
+public class BaseRoleInfo implements Serializable
+{
+    private static final long serialVersionUID = -6725489773301720068L;
+
+    protected String id;
+    protected String name;
+    protected String description = "";
+    protected boolean permanent = false;
+    protected String modelId = "";
+    protected String resource = "";
+    protected boolean isTemplateInstance = false;
+    protected boolean assignable = true;
+    private String applicationId = "";
+    private boolean isChild = false;
+
+    protected boolean assigned = false;
+    private List<BaseRoleInfo> children = new ArrayList<>( 0 );
+
+    public BaseRoleInfo() {
+
+    }
+
+    public static BaseRoleInfo ofName(String name) {
+        BaseRoleInfo info = new BaseRoleInfo( );
+        info.setName( name );
+        return info;
+    }
+
+    public static BaseRoleInfo ofId(String id) {
+        BaseRoleInfo info = new BaseRoleInfo( );
+        info.setId( id );
+        return info;
+    }
+
+    public static BaseRoleInfo of(Role rbacRole) {
+        return of( rbacRole, new BaseRoleInfo( ) );
+    }
+
+
+    public static <T extends BaseRoleInfo>  T of( Role rbacRole, T role ) {
+        role.id = rbacRole.getId( );
+        role.name = rbacRole.getName( );
+        role.description = rbacRole.getDescription( ) == null ?"": rbacRole.getDescription();
+        role.permanent = rbacRole.isPermanent( );
+        role.modelId = rbacRole.getModelId( );
+        role.resource = rbacRole.getResource( );
+        role.isTemplateInstance = rbacRole.isTemplateInstance( );
+        role.assignable = rbacRole.isAssignable( );
+        return role;
+    }
+
+
+
+    @Schema(description = "The role name")
+    public String getName()
+    {
+        return name;
+    }
+
+    public void setName( String name )
+    {
+        this.name = name;
+    }
+
+    @Schema( description = "A description of the role" )
+    public String getDescription( )
+    {
+        return description;
+    }
+
+    public void setDescription( String description )
+    {
+        this.description = description;
+    }
+
+    @Schema( description = "True, if this role cannot be deleted.")
+    public boolean isPermanent()
+    {
+        return permanent;
+    }
+
+    public void setPermanent( boolean permanent )
+    {
+        this.permanent = permanent;
+    }
+
+    @Schema(description = "The identifier of this role")
+    public String getId( )
+    {
+        return id;
+    }
+
+    public void setId( String id )
+    {
+        this.id = id;
+    }
+
+    @Schema(description = "The model this role is derived from")
+    public String getModelId( )
+    {
+        return modelId;
+    }
+
+    public void setModelId( String modelId )
+    {
+        this.modelId = modelId;
+    }
+
+    @Schema(description = "The resource this model is built for, if it is built by a template.")
+    public String getResource( )
+    {
+        return resource;
+    }
+
+    public void setResource( String resource )
+    {
+        this.resource = resource;
+    }
+
+    @Schema(description = "True, if this is a instance of a role template")
+    public boolean isTemplateInstance( )
+    {
+        return isTemplateInstance;
+    }
+
+    public void setTemplateInstance( boolean templateInstance )
+    {
+        isTemplateInstance = templateInstance;
+    }
+
+    @Schema(description = "Roles that are children of this role. This field may not be populated, depending on the REST method call.")
+    public List<BaseRoleInfo> getChildren( )
+    {
+        return children;
+    }
+
+    public void setChildren( List<BaseRoleInfo> children )
+    {
+        this.children = children;
+    }
+
+    public void addChild(BaseRoleInfo child) {
+        if (!this.children.contains( child ))
+        {
+            this.children.add( child );
+        }
+    }
+
+    @Schema(description = "This attribute is only set at specific REST calls, that return roles, that are either assigned or not assigned to a given user.")
+    public boolean isAssigned( )
+    {
+        return assigned;
+    }
+
+    public void setAssigned( boolean assigned )
+    {
+        this.assigned = assigned;
+    }
+
+
+    @Override
+    public boolean equals( Object o )
+    {
+        if ( this == o ) return true;
+        if ( o == null || getClass( ) != o.getClass( ) ) return false;
+
+        BaseRoleInfo that = (BaseRoleInfo) o;
+
+        return id.equals( that.id );
+    }
+
+    @Schema( description = "If true, the role is assignable to users or roles. Otherwise, it can be used only as parent role.")
+    public boolean isAssignable()
+    {
+        return assignable;
+    }
+
+    public void setAssignable( boolean assignable )
+    {
+        this.assignable = assignable;
+    }
+
+    @Override
+    public int hashCode( )
+    {
+        return id.hashCode( );
+    }
+
+    @Override
+    public String toString( )
+    {
+        final StringBuilder sb = new StringBuilder( "BaseRoleInfo{" );
+        sb.append( "id='" ).append( id ).append( '\'' );
+        sb.append( ", name='" ).append( name ).append( '\'' );
+        sb.append( ", description='" ).append( description ).append( '\'' );
+        sb.append( ", permanent=" ).append( permanent );
+        sb.append( ", modelId='" ).append( modelId ).append( '\'' );
+        sb.append( ", resource='" ).append( resource ).append( '\'' );
+        sb.append( ", isTemplateInstance=" ).append( isTemplateInstance );
+        sb.append( '}' );
+        return sb.toString( );
+    }
+
+    @Schema(description = "Application id, where this role belongs to. This is only filled by certain REST methods.")
+    public String getApplicationId( )
+    {
+        return applicationId;
+    }
+
+    public void setApplicationId( String applicationId )
+    {
+        this.applicationId = applicationId;
+    }
+
+    public boolean isChild( )
+    {
+        return isChild;
+    }
+
+    public void setChild( boolean child )
+    {
+        isChild = child;
+    }
+
+    @XmlTransient
+    public boolean isNotChild() {
+        return !isChild;
+    }
+}
diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/BaseUserInfo.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/BaseUserInfo.java
new file mode 100644
index 0000000..7903a1c
--- /dev/null
+++ b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/BaseUserInfo.java
@@ -0,0 +1,67 @@
+package org.apache.archiva.redback.rest.api.model.v2;/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import javax.xml.bind.annotation.XmlElement;
+import java.io.Serializable;
+
+/**
+ * @author Martin Stockhammer <ma...@apache.org>
+ */
+@Schema( name = "BaseUserInfo", description = "Basic user information" )
+public class BaseUserInfo implements Serializable
+{
+    private static final long serialVersionUID = 4643187400578104895L;
+    protected String userId;
+    private String id;
+
+
+    public BaseUserInfo( )
+    {
+    }
+
+    public BaseUserInfo( String id , String userId )
+    {
+        this.userId = userId;
+        this.id = id;
+    }
+
+    @Schema( name = "user_id", description = "The user id" )
+    @XmlElement( name = "user_id" )
+    public String getUserId( )
+    {
+        return userId;
+    }
+
+    public void setUserId( String userId )
+    {
+        this.userId = userId;
+    }
+
+    @Schema( description = "User id that is unique over all user managers" )
+    public String getId( )
+    {
+        return id;
+    }
+
+    public void setId( String id )
+    {
+        this.id = id;
+    }
+}
diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/Role.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/Role.java
new file mode 100644
index 0000000..f5909a9
--- /dev/null
+++ b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/Role.java
@@ -0,0 +1,104 @@
+package org.apache.archiva.redback.rest.api.model.v2;/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class is used for role update. Contains only the role attributes, that can be updated.
+ *
+ * @author Martin Stockhammer <ma...@apache.org>
+ */
+@Schema(name="Role",description="Role attributes that are used for updating a role")
+public class Role implements Serializable
+{
+    private static final long serialVersionUID = 3238571295658509062L;
+
+    protected String name;
+    protected String id;
+    protected String description;
+    protected boolean permanent = false;
+    /**
+     * The ids of all the assigned users.
+     */
+    protected List<BaseUserInfo> assignedUsers = new ArrayList<>( 0 );
+
+    @Schema(description = "The role name")
+    public String getName()
+    {
+        return name;
+    }
+
+    public void setName( String name )
+    {
+        this.name = name;
+    }
+
+    @Schema( description = "A description of the role" )
+    public String getDescription( )
+    {
+        return description;
+    }
+
+    public void setDescription( String description )
+    {
+        this.description = description;
+    }
+
+    @Schema( description = "True, if this role cannot be deleted.")
+    public boolean isPermanent()
+    {
+        return permanent;
+    }
+
+    public void setPermanent( boolean permanent )
+    {
+        this.permanent = permanent;
+    }
+
+    @Schema(description = "The identifier of this role")
+    public String getId( )
+    {
+        return id;
+    }
+
+    public void setId( String id )
+    {
+        this.id = id;
+    }
+
+    @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 );
+    }
+
+
+}
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
new file mode 100644
index 0000000..aa77836
--- /dev/null
+++ b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/RoleInfo.java
@@ -0,0 +1,182 @@
+package org.apache.archiva.redback.rest.api.model.v2;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import org.apache.archiva.redback.rbac.Role;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlTransient;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Result object for role information.
+ *
+ * @author Martin Stockhammer
+ * @since 3.0
+ */
+@XmlRootElement( name = "role" )
+@Schema(name="RoleInfo",description = "Information about role")
+public class RoleInfo extends BaseRoleInfo
+    implements Serializable
+{
+    private static final long serialVersionUID = -3506615158923923845L;
+
+    /**
+     * Field childRoleNames
+     */
+    private List<String> childRoleIds = new ArrayList<>(0);
+
+    /**
+     * Field permissions
+     */
+    private List<Permission> permissions = new ArrayList<>(0);
+
+    /**
+     * The names of all parent roles
+     */
+    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
+    }
+
+
+    public static RoleInfo of( Role rbacRole) {
+        RoleInfo role = BaseRoleInfo.of( rbacRole, new RoleInfo( ) );
+        return role;
+    }
+
+    @XmlTransient
+    @Override
+    public List<BaseRoleInfo> getChildren( )
+    {
+        return super.getChildren( );
+    }
+
+    @Schema( description = "List of names of children roles")
+    public List<String> getChildRoleIds()
+    {
+        return childRoleIds;
+    }
+
+    public void setChildRoleIds( List<String> childRoleIds )
+    {
+        this.childRoleIds = childRoleIds;
+    }
+
+    @Schema( description = "List of permissions assigned to this role.")
+    public List<Permission> getPermissions()
+    {
+        return permissions;
+    }
+
+    public void setPermissions( List<Permission> permissions )
+    {
+        this.permissions = permissions;
+    }
+
+    @Schema(description = "List of names of roles that are parents of this role.")
+    public List<String> getParentRoleIds()
+    {
+        return parentRoleIds;
+    }
+
+    public void setParentRoleIds( List<String> parentRoleIds )
+    {
+        this.parentRoleIds = parentRoleIds;
+    }
+
+    @Override
+    public boolean isChild( )
+    {
+        return getParentRoleIds( ).size( ) > 0;
+    }
+
+    @Override
+    public int hashCode()
+    {
+        return getName( ) != null ? getName( ).hashCode() : 0;
+    }
+
+    @Override
+    public String toString( )
+    {
+        final StringBuilder sb = new StringBuilder( "RoleInfo{" );
+        sb.append( "name='" ).append( getName( ) ).append( '\'' );
+        sb.append( ", id='" ).append( getId( ) ).append( '\'' );
+        sb.append( ", description='" ).append( getDescription( ) ).append( '\'' );
+        sb.append( ", assignable=" ).append( assignable );
+        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( '\'' );
+        sb.append( ", isTemplateInstance=" ).append( isTemplateInstance );
+        sb.append( '}' );
+        return sb.toString( );
+    }
+
+    @Override
+    public boolean equals( Object o )
+    {
+        if ( this == o )
+        {
+            return true;
+        }
+        if ( o == null || getClass() != o.getClass() )
+        {
+            return false;
+        }
+
+        RoleInfo role = (RoleInfo) o;
+
+        return Objects.equals( getName( ), role.getName( ) );
+
+    }
+
+
+}
diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/RoleTree.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/RoleTree.java
new file mode 100644
index 0000000..1af65fd
--- /dev/null
+++ b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/RoleTree.java
@@ -0,0 +1,74 @@
+package org.apache.archiva.redback.rest.api.model.v2;/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Information about roles of a application.
+ *
+ * @author Martin Stockhammer <ma...@apache.org>
+ */
+@Schema(name="RoleTree",description = "Tree of roles defined. The root roles have no parent. Each role may have children recursively.")
+public class RoleTree implements Serializable
+{
+    private static final long serialVersionUID = 6893397477073625729L;
+
+    String userId;
+    Map<String, Application> applications;
+    List<BaseRoleInfo> rootRoles;
+
+    @Schema(description = "The user id for which the assigned flags are set on the roles.")
+    public String getUserId( )
+    {
+        return userId;
+    }
+
+    public void setUserId( String userId )
+    {
+        this.userId = userId;
+    }
+
+    @Schema(description = "Information about the applications that define roles. The keys of the map are the application ids.")
+    public Map<String, Application> getApplications( )
+    {
+        return applications;
+    }
+
+    public void setApplications( Map<String, Application> applications )
+    {
+        this.applications = applications;
+    }
+
+
+    @Schema(description = "The list of roles directly assigned to this application. Roles may contain children roles.")
+    public List<BaseRoleInfo> getRootRoles( )
+    {
+        return rootRoles;
+    }
+
+    public void setRootRoles( List<BaseRoleInfo> rootRoles )
+    {
+        this.rootRoles = rootRoles;
+    }
+
+
+}
diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/UserInfo.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/UserInfo.java
index 274848c..c6043d3 100644
--- a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/UserInfo.java
+++ b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/UserInfo.java
@@ -2,13 +2,11 @@ package org.apache.archiva.redback.rest.api.model.v2;
 
 import io.swagger.v3.oas.annotations.media.Schema;
 
-import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlRootElement;
 import java.io.Serializable;
 import java.time.Instant;
 import java.time.OffsetDateTime;
 import java.time.ZoneId;
-import java.util.List;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -31,14 +29,11 @@ import java.util.List;
 
 @XmlRootElement( name = "user" )
 @Schema(name="User", description = "User information data")
-public class UserInfo
+public class UserInfo extends BaseUserInfo
     implements Serializable
 {
 
     private static final long serialVersionUID = 822423853981984867L;
-    private String id;
-
-    private String userId;
 
     private String fullName;
 
@@ -125,18 +120,6 @@ public class UserInfo
     }
 
 
-    @Schema( name = "user_id", description = "The user id" )
-    @XmlElement( name = "user_id" )
-    public String getUserId( )
-    {
-        return userId;
-    }
-
-    public void setUserId( String userId )
-    {
-        this.userId = userId;
-    }
-
     @Schema( description = "The full name of the user" )
     public String getFullName( )
     {
@@ -282,22 +265,11 @@ public class UserInfo
         this.validationToken = validationToken;
     }
 
-    @Schema( description = "User id that is unique over all user managers")
-    public String getId( )
-    {
-        return id;
-    }
-
-    public void setId( String id )
-    {
-        this.id = id;
-    }
-
     @Override
     public String toString()
     {
         return "User{" +
-            "username='" + userId + '\'' +
+            "username='" + getUserId( ) + '\'' +
             ", fullName='" + fullName + '\'' +
             ", email='" + email + '\'' +
             ", validated=" + validated +
@@ -328,7 +300,7 @@ public class UserInfo
 
         UserInfo user = (UserInfo) o;
 
-        if ( !userId.equals( user.userId ) )
+        if ( !getUserId( ).equals( user.getUserId( ) ) )
         {
             return false;
         }
@@ -339,6 +311,6 @@ public class UserInfo
     @Override
     public int hashCode()
     {
-        return userId.hashCode();
+        return getUserId( ).hashCode();
     }
 }
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 82143f4..bbb2502 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
@@ -20,6 +20,7 @@ package org.apache.archiva.redback.rest.api.services.v2;
 
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.headers.Header;
 import io.swagger.v3.oas.annotations.media.Content;
 import io.swagger.v3.oas.annotations.media.Schema;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
@@ -31,7 +32,6 @@ import org.apache.archiva.redback.rest.api.model.ActionStatus;
 import org.apache.archiva.redback.rest.api.model.Application;
 import org.apache.archiva.redback.rest.api.model.ApplicationRoles;
 import org.apache.archiva.redback.rest.api.model.RedbackRestError;
-import org.apache.archiva.redback.rest.api.model.v2.AvailabilityStatus;
 import org.apache.archiva.redback.rest.api.model.Role;
 import org.apache.archiva.redback.rest.api.model.User;
 import org.apache.archiva.redback.rest.api.model.VerificationStatus;
@@ -40,14 +40,19 @@ import org.apache.archiva.redback.rest.api.model.v2.RoleInfo;
 import org.apache.archiva.redback.rest.api.services.RedbackServiceException;
 
 import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
 import javax.ws.rs.DefaultValue;
 import javax.ws.rs.GET;
+import javax.ws.rs.HEAD;
+import javax.ws.rs.PATCH;
 import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
 import java.util.List;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
@@ -60,15 +65,12 @@ import static org.apache.archiva.redback.rest.api.Constants.DEFAULT_PAGE_LIMIT;
 @Tag(name = "v2")
 @Tag(name = "v2/Roles")
 @SecurityRequirement(name = "BearerAuth")
-public interface RoleManagementService
+public interface RoleService
 {
 
-    /**
-     * @since 2.0
-     */
     @Path( "" )
     @GET
-    @Produces( { MediaType.APPLICATION_JSON } )
+    @Produces( { APPLICATION_JSON } )
     @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
     @Operation( summary = "Returns all roles defined. The result is paged.",
         parameters = {
@@ -99,75 +101,209 @@ public interface RoleManagementService
                                        @QueryParam("order") @DefaultValue( "asc" ) String order)
         throws RedbackServiceException;
 
-    @Path( "createTemplatedRole" )
+    @Path( "{roleId}" )
     @GET
-    @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
+    @Produces( { APPLICATION_JSON } )
     @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
-    ActionStatus createTemplatedRole( @QueryParam( "templateId" ) String templateId,
-                                      @QueryParam( "resource" ) String resource )
+    @Operation( summary = "Returns information about a specific role. Use HTTP HEAD method for checking, if the resource exists.",
+        security = {
+            @SecurityRequirement(
+                name = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION
+            )
+        },
+        responses = {
+            @ApiResponse( responseCode = "200",
+                description = "If role was found in the database",
+                content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = RoleInfo.class))
+            ),
+            @ApiResponse( responseCode = "404", description = "Role does not exist",
+                content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = RedbackRestError.class ))
+            ),
+            @ApiResponse( responseCode = "403", description = "Authenticated user is not permitted to gather the information",
+                content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = RedbackRestError.class )) )
+        }
+    )
+    RoleInfo getRole( @PathParam( "roleId" ) String roleId )
         throws RedbackServiceException;
 
-    /**
-     * removes a role corresponding to the role Id that was manufactured with the given resource
-     *
-     * it also removes any user assignments for that role
-     *
-     * @param templateId
-     * @param resource
-     */
-    @Path( "removeTemplatedRole" )
-    @GET
-    @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
+    @Path( "{roleId}" )
+    @HEAD
+    @Produces( { APPLICATION_JSON } )
     @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
-    ActionStatus removeTemplatedRole( @QueryParam( "templateId" ) String templateId,
-                                 @QueryParam( "resource" ) String resource )
+    @Operation( summary = "Returns information about a specific role. Use HTTP HEAD method for checking, if the resource exists.",
+        security = {
+            @SecurityRequirement(
+                name = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION
+            )
+        },
+        responses = {
+            @ApiResponse( responseCode = "200",
+                description = "If role was found in the database"
+            ),
+            @ApiResponse( responseCode = "404", description = "Role does not exist",
+                content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = RedbackRestError.class ))
+            ),
+            @ApiResponse( responseCode = "403", description = "Authenticated user is not permitted to gather the information",
+                content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = RedbackRestError.class )) )
+        }
+    )
+    Response checkRole( @PathParam( "roleId" ) String roleId )
         throws RedbackServiceException;
 
 
     /**
-     * allows for a role coming from a template to be renamed effectively swapping out the bits of it that
-     * were labeled with the oldResource with the newResource
-     *
-     * it also manages any user assignments for that role
+     * Moves a templated role from one resource to another resource
+     * @TODO: Not sure, if it makes sense to keep the child template at the source. Shouldn't we move the childs too?
      *
-     * @param templateId
-     * @param oldResource
-     * @param newResource
+     * @param templateId the template identifier
+     * @param oldResource the resource of the current role
+     * @param newResource the resource of the new role
      */
-    @Path( "updateRole" )
-    @GET
-    @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
+    @Path( "template/{templateId}/{oldResource}/moveto/{newResource}" )
+    @POST
+    @Produces( {APPLICATION_JSON} )
     @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
-    ActionStatus updateRole( @QueryParam( "templateId" ) String templateId, @QueryParam( "oldResource" ) String oldResource,
-                        @QueryParam( "newResource" ) String newResource )
+    @Operation( summary = "Moves a templated role from one resource to another resource. If the template has child templates," +
+        " then child instances will be created on for the destination resource. But the child instances on the source are not deleted.",
+        security = {
+            @SecurityRequirement(
+                name = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION
+            )
+        },
+        responses = {
+            @ApiResponse( responseCode = "201",
+                description = "If user creation was successful",
+                headers = {
+                    @Header( name="Location", description = "The URL of the moved role", schema = @Schema(type="string"))
+                },
+                content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = RoleInfo.class))
+            ),
+            @ApiResponse( responseCode = "404", description = "The source role does not exist" ),
+            @ApiResponse( responseCode = "303", description = "The destination role exists already" ),
+            @ApiResponse( responseCode = "403", description = "The authenticated user has not the permission to move the role.",
+                content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = RedbackRestError.class )) )
+
+        }
+    )
+    RoleInfo moveTemplatedRole( @PathParam( "templateId" ) String templateId, @PathParam( "oldResource" ) String oldResource,
+                                @PathParam( "newResource" ) String newResource )
+        throws RedbackServiceException;
+
+    @Path( "template/{templateId}/{resource}" )
+    @HEAD
+    @Produces( { APPLICATION_JSON} )
+    @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
+    @Operation( summary = "Checks, if a instance of the role template exists for the given resource",
+        security = {
+            @SecurityRequirement(
+                name = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION
+            )
+        },
+        responses = {
+            @ApiResponse( responseCode = "200",
+                description = "If the role instance exists"
+            ),
+            @ApiResponse( responseCode = "404", description = "Role does not exist",
+                content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = RedbackRestError.class ))
+            ),
+            @ApiResponse( responseCode = "403", description = "Authenticated user is not permitted to gather the information",
+                content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = RedbackRestError.class )) )
+        }
+    )
+    Response checkTemplateRole( @PathParam( "templateId" ) String templateId,
+                                @PathParam( "resource" ) String resource )
         throws RedbackServiceException;
 
+    @Path( "template/{templateId}/{resource}" )
+    @PUT
+    @Produces( { APPLICATION_JSON } )
+    @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
+    @Operation( summary = "Creates a role instance from a template for the given resource",
+        security = {
+            @SecurityRequirement(
+                name = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION
+            )
+        },
+        responses = {
+            @ApiResponse( responseCode = "201",
+                description = "If user creation was successful",
+                headers = {
+                    @Header( name = "Location", description = "The URL of the created role", schema = @Schema( type = "string" ) )
+                },
+                content = @Content( mediaType = APPLICATION_JSON, schema = @Schema( implementation = RoleInfo.class ) )
+            ),
+            @ApiResponse( responseCode = "200",
+                description = "If the role instance existed before and was updated",
+                headers = {
+                    @Header( name = "Location", description = "The URL of the updated role", schema = @Schema( type = "string" ) )
+                },
+                content = @Content( mediaType = APPLICATION_JSON, schema = @Schema( implementation = RoleInfo.class ) )
+            ),
+            @ApiResponse( responseCode = "404", description = "The template does not exist" ),
+            @ApiResponse( responseCode = "403", description = "The authenticated user has not the permission for role creation.",
+                content = @Content( mediaType = APPLICATION_JSON, schema = @Schema( implementation = RedbackRestError.class ) ) )
+
+        }
+    )
+    RoleInfo createTemplatedRole( @PathParam( "templateId" ) String templateId,
+                                      @PathParam( "resource" ) String resource )
+        throws RedbackServiceException;
 
     /**
-     * Assigns the role indicated by the roleId to the given principal
+     * Removes a role corresponding to the role Id that was manufactured with the given resource
+     * it also removes any user assignments for that role
      *
-     * @param roleId
-     * @param principal
+     * @param templateId
+     * @param resource
      */
-    @Path( "assignRole" )
-    @GET
-    @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
+    @Path( "template/{templateId}/{resource}" )
+    @DELETE
+    @Produces( { APPLICATION_JSON } )
     @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
-    ActionStatus assignRole( @QueryParam( "roleId" ) String roleId, @QueryParam( "principal" ) String principal )
+    @Operation( summary = "Deletes a role template instance",
+        security = {
+            @SecurityRequirement( name = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
+        },
+        responses = {
+            @ApiResponse( responseCode = "200",
+                description = "If role deletion was successful"
+            ),
+            @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 deletion.",
+                content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = RedbackRestError.class )) )
+        }
+    )
+    Response removeTemplatedRole( @PathParam(  "templateId" ) String templateId,
+                                 @PathParam( "resource" ) String resource )
         throws RedbackServiceException;
 
+
     /**
-     * Assigns the role indicated by the roleName to the given principal
+     * Assigns the role indicated by the roleId to the given principal
      *
-     * @param roleName
-     * @param principal
-     * @throws RedbackServiceException
+     * @param roleId
+     * @param userId
      */
-    @Path( "assignRoleByName" )
-    @GET
-    @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
+    @Path( "{roleId}/assign/{userId}" )
+    @PUT
+    @Produces( { APPLICATION_JSON } )
     @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
-    ActionStatus assignRoleByName( @QueryParam( "roleName" ) String roleName, @QueryParam( "principal" ) String principal )
+    @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 was assigned"
+            ),
+            @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 assignRole( @PathParam( "roleId" ) String roleId, @PathParam( "userId" ) String userId )
         throws RedbackServiceException;
 
     /**
@@ -179,11 +315,25 @@ public interface RoleManagementService
      * @param resource
      * @param principal
      */
-    @Path( "assignTemplatedRole" )
-    @GET
-    @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
+    @Path( "template/{templateId}/{resource}/assign/{userId}" )
+    @POST
+    @Produces( { APPLICATION_JSON } )
     @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
-    ActionStatus assignTemplatedRole( @QueryParam( "templateId" ) String templateId,
+    @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 instance was assigned"
+            ),
+            @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 assignTemplatedRole( @QueryParam( "templateId" ) String templateId,
                                  @QueryParam( "resource" ) String resource,
                                  @QueryParam( "principal" ) String principal )
         throws RedbackServiceException;
@@ -195,161 +345,53 @@ public interface RoleManagementService
      * @param principal
      * @throws RedbackServiceException
      */
-    @Path( "unassignRole" )
-    @GET
-    @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
-    @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
-    ActionStatus unassignRole( @QueryParam( "roleId" ) String roleId, @QueryParam( "principal" ) String principal )
-        throws RedbackServiceException;
-
-    /**
-     * Unassigns the role indicated by the role name from the given principal
-     *
-     * @param roleName
-     * @param principal
-     * @throws RedbackServiceException
-     */
-    @Path( "unassignRoleByName" )
-    @GET
-    @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
+    @Path( "{roleId}/{userId}" )
+    @DELETE
+    @Produces( { APPLICATION_JSON } )
     @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
-    ActionStatus unassignRoleByName( @QueryParam( "roleName" ) String roleName, @QueryParam( "principal" ) String principal )
-        throws RedbackServiceException;
-
-    /**
-     * true of a role exists with the given roleId
-     *
-     * @param roleId
-     * @return
-     * @throws RedbackServiceException
-     */
-    @Path( "roleExists" )
-    @GET
-    @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
-    @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
-    AvailabilityStatus roleExists( @QueryParam( "roleId" ) String roleId )
-        throws RedbackServiceException;
-
-    /**
-     * true of a role exists with the given roleId
-     *
-     * @param templateId
-     * @param resource
-     * @return
-     * @throws RedbackServiceException
-     */
-    @Path( "templatedRoleExists" )
-    @GET
-    @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
-    @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
-    AvailabilityStatus templatedRoleExists( @QueryParam( "templateId" ) String templateId,
-                                 @QueryParam( "resource" ) String resource )
+    @Operation( summary = "Removes a role assignment for the given role and user",
+        security = {
+            @SecurityRequirement( name = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
+        },
+        responses = {
+            @ApiResponse( responseCode = "200",
+                description = "If the role assignment was removed"
+            ),
+            @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 unassignRole( @QueryParam( "roleId" ) String roleId, @QueryParam( "principal" ) String principal )
         throws RedbackServiceException;
 
 
     /**
-     * Check a role template is complete in the RBAC store.
+     * Updates a role. Attributes that are empty or null will be ignored.
      *
-     * @param templateId the templated role
-     * @param resource   the resource to verify
-     * @throws RedbackServiceException
-     */
-    @Path( "verifyTemplatedRole" )
-    @GET
-    @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
-    @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
-    VerificationStatus verifyTemplatedRole( @QueryParam( "templateId" ) String templateId,
-                                            @QueryParam( "resource" ) String resource )
-        throws RedbackServiceException;
-
-    /**
-     * @since 1.4
-     */
-    @Path( "getEffectivelyAssignedRoles/{username}" )
-    @GET
-    @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
-    @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
-    List<Role> getEffectivelyAssignedRoles( @PathParam( "username" ) String username )
-        throws RedbackServiceException;
-
-
-
-
-    /**
-     * @since 2.0
-     */
-    @Path( "detailledAllRoles" )
-    @GET
-    @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
-    @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
-    List<Role> getDetailedAllRoles()
-        throws RedbackServiceException;
-
-
-    /**
-     * @since 2.0
-     */
-    @Path( "getApplications/{username}" )
-    @GET
-    @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
-    @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
-    List<Application> getApplications( @PathParam( "username" ) String username )
-        throws RedbackServiceException;
-
-
-    /**
-     * @since 2.0
+     * @since 3.0
      */
-    @Path( "getRole/{roleName}" )
-    @GET
-    @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
+    @Path( "{roleId}" )
+    @PATCH
+    @Produces( { APPLICATION_JSON } )
     @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
-    Role getRole( @PathParam( "roleName" ) String roleName )
-        throws RedbackServiceException;
-
-    /**
-     * @since 2.0
-     */
-    @Path( "updateRoleDescription" )
-    @GET
-    @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
-    @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
-    ActionStatus updateRoleDescription( @QueryParam( "roleName" ) String roleName,
-                                   @QueryParam( "roleDescription" ) String description )
-        throws RedbackServiceException;
-
-    /**
-     * update users assigned to a role
-     * @since 2.0
-     */
-    @Path( "updateRoleUsers" )
-    @POST
-    @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML } )
-    @Consumes( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML } )
-    @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
-    ActionStatus updateRoleUsers( Role role )
-        throws RedbackServiceException;
-
-    /**
-     * @since 2.0
-     */
-    @Path( "getApplicationRoles/{username}" )
-    @GET
-    @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML } )
-    @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
-    List<ApplicationRoles> getApplicationRoles( @PathParam( "username" ) String username )
-        throws RedbackServiceException;
+    @Operation( summary = "Creates or updates the given role",
+        security = {
+            @SecurityRequirement( name = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
+        },
+        responses = {
+            @ApiResponse( responseCode = "200",
+                description = "If the update was successful"
+            ),
+            @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 updateRole( @QueryParam("roleId") String roleId, Role role )
+    throws RedbackServiceException;
 
-    /**
-     * update roles assigned to a user
-     * @since 2.0
-     */
-    @Path( "updateUserRoles" )
-    @POST
-    @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML } )
-    @Consumes( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML } )
-    @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
-    ActionStatus updateUserRoles( User user )
-        throws RedbackServiceException;
 
 }
diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/UserService.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/UserService.java
index f6415bb..243ca6e 100644
--- a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/UserService.java
+++ b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/UserService.java
@@ -31,12 +31,15 @@ import io.swagger.v3.oas.annotations.tags.Tag;
 import org.apache.archiva.redback.authorization.RedbackAuthorization;
 import org.apache.archiva.redback.integration.security.role.RedbackRoleConstants;
 import org.apache.archiva.redback.rest.api.model.ActionStatus;
+import org.apache.archiva.redback.rest.api.model.Application;
 import org.apache.archiva.redback.rest.api.model.RedbackRestError;
-import org.apache.archiva.redback.rest.api.model.v2.Permission;
+import org.apache.archiva.redback.rest.api.model.v2.RoleTree;
 import org.apache.archiva.redback.rest.api.model.v2.AvailabilityStatus;
 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.PingResult;
 import org.apache.archiva.redback.rest.api.model.v2.RegistrationKey;
+import org.apache.archiva.redback.rest.api.model.v2.RoleInfo;
 import org.apache.archiva.redback.rest.api.model.v2.SelfUserData;
 import org.apache.archiva.redback.rest.api.model.v2.User;
 import org.apache.archiva.redback.rest.api.model.v2.UserInfo;
@@ -54,6 +57,7 @@ import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
 import java.util.Collection;
 import java.util.List;
 
@@ -594,4 +598,59 @@ public interface UserService
     )
     VerificationStatus validateUserRegistration( @PathParam( "userId" ) String userId, @PathParam( "key" ) String key )
         throws RedbackServiceException;
+
+
+    /**
+     * Returns all roles for a given user id. Recurses all child roles.
+     *
+     * @since 3.0
+     */
+    @Path( "{userId}/roles" )
+    @GET
+    @Produces( { MediaType.APPLICATION_JSON } )
+    @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
+    @Operation( summary = "Returns a list of all roles effectively assigned to the given user.",
+        responses = {
+            @ApiResponse( responseCode = "200",
+                description = "The list of roles assigned to the given user",
+                content = @Content(mediaType = APPLICATION_JSON, array = @ArraySchema(schema =
+                @Schema(implementation = org.apache.archiva.redback.rest.api.model.v2.RoleInfo.class )))
+            ),
+            @ApiResponse( responseCode = "404", description = "User 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 retrieving the information.",
+                content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = RedbackRestError.class )) )
+        }
+    )
+    List<RoleInfo> getEffectivelyAssignedRoles( @PathParam( "userId" ) String username )
+        throws RedbackServiceException;
+
+
+    /**
+     * @since 3.0
+     */
+    @Path( "{userId}/roletree" )
+    @GET
+    @Produces( { APPLICATION_JSON } )
+    @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
+    @Operation( summary = "Returns a list of all roles that are assigned, or can be assigned to the given user. "+
+        "This method sets the 'assigned' flag on all returned role objects.",
+        security = {
+            @SecurityRequirement( name = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
+        },
+        responses = {
+            @ApiResponse( responseCode = "200",
+                description = "The list of roles separated by application that are assigned or assignable for the given user",
+                content = @Content(mediaType = APPLICATION_JSON, array = @ArraySchema(schema =
+                @Schema(implementation = Application.class )))
+            ),
+            @ApiResponse( responseCode = "404", description = "User 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 retrieving the information.",
+                content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = RedbackRestError.class )) )
+        }
+    )
+    RoleTree getRoleTree( @PathParam( "userId" ) String username )
+        throws RedbackServiceException;
+
 }
diff --git a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultRoleManagementService.java b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultRoleManagementService.java
index a80439b..40ec9a3 100644
--- a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultRoleManagementService.java
+++ b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultRoleManagementService.java
@@ -29,7 +29,6 @@ import org.apache.archiva.redback.rbac.UserAssignment;
 import org.apache.archiva.redback.rest.api.model.ActionStatus;
 import org.apache.archiva.redback.rest.api.model.Application;
 import org.apache.archiva.redback.rest.api.model.ApplicationRoles;
-import org.apache.archiva.redback.rest.api.model.v2.AvailabilityStatus;
 import org.apache.archiva.redback.rest.api.model.ErrorMessage;
 import org.apache.archiva.redback.rest.api.model.Role;
 import org.apache.archiva.redback.rest.api.model.RoleTemplate;
@@ -127,7 +126,7 @@ public class DefaultRoleManagementService
     {
         try
         {
-            roleManager.updateRole( templateId, oldResource, newResource );
+            roleManager.moveTemplatedRole( templateId, oldResource, newResource );
         }
         catch ( RoleManagerException e )
         {
@@ -391,7 +390,7 @@ public class DefaultRoleManagementService
             org.apache.archiva.redback.rbac.Role rbacRole = rbacManager.getRole( roleName );
             Role role = new Role( rbacRole );
 
-            Map<String, ? extends org.apache.archiva.redback.rbac.Role> parentRoles = rbacManager.getParentRoles( rbacRole );
+            Map<String, ? extends org.apache.archiva.redback.rbac.Role> parentRoles = rbacManager.getParentRoleNames( rbacRole );
             for ( String parentRoleName : parentRoles.keySet() )
             {
                 role.getParentRoleNames().add( parentRoleName );
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
new file mode 100644
index 0000000..63318bb
--- /dev/null
+++ b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/v2/BaseRedbackService.java
@@ -0,0 +1,145 @@
+package org.apache.archiva.redback.rest.services.v2;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.archiva.redback.rbac.RBACManager;
+import org.apache.archiva.redback.rbac.RbacManagerException;
+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.RoleInfo;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.inject.Named;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * @author Martin Stockhammer <ma...@apache.org>
+ */
+public class BaseRedbackService
+{
+    private static final Logger log = LoggerFactory.getLogger( BaseRedbackService.class );
+
+    protected RBACManager rbacManager;
+    protected UserManager userManager;
+
+    public BaseRedbackService( @Named( value = "rbacManager#default" ) RBACManager rbacManager, @Named( value = "userManager#default" ) UserManager userManager )
+    {
+        this.rbacManager = rbacManager;
+        this.userManager = userManager;
+    }
+
+    protected RoleInfo getRoleInfo( org.apache.archiva.redback.rbac.Role rbacRole ) throws RedbackServiceException
+    {
+        try
+        {
+            RoleInfo role = RoleInfo.of( rbacRole );
+            role.setParentRoleIds( getParentRoles( rbacRole ) );
+            role.setChildRoleIds( getChildRoles( rbacRole ) );
+            role.setAssignedUsers( getAssignedUsersRecursive( rbacRole ) );
+            return role;
+        }
+        catch ( RbacManagerException e )
+        {
+            log.error( "Error while retrieving role information {}", e.getMessage( ), e );
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_RBACMANAGER_FAIL, e.getMessage( ) ) );
+        }
+    }
+
+    protected List<String> getParentRoles( org.apache.archiva.redback.rbac.Role rbacRole ) throws RbacManagerException
+    {
+        return new ArrayList<>( rbacManager.getParentRoleIds( rbacRole ).keySet( ));
+    }
+
+    protected List<String> getChildRoles( Role rbacRole) throws RbacManagerException
+    {
+        return new ArrayList<>( rbacManager.getChildRoleIds( rbacRole ).keySet( ) );
+    }
+
+    protected List<BaseUserInfo> getAssignedUsersRecursive( org.apache.archiva.redback.rbac.Role rbacRole ) throws RbacManagerException
+    {
+        try
+        {
+            return rbacManager.getUserAssignmentsForRoles( recurseRoles( rbacRole ).map( role -> role.getName( ) ).collect( Collectors.toList( ) ) )
+                .stream( ).map( assignment -> getUserInfo( assignment.getPrincipal( ) ) ).collect( Collectors.toList( ) );
+        }
+        catch ( RuntimeException e )
+        {
+            log.error( "Could not recurse roles for assignments {}", e.getMessage( ) );
+            throw new RbacManagerException( e.getCause( ) );
+        }
+    }
+
+    private Stream<Role> recurseRoles( Role startRole )
+    {
+        return Stream.concat( Stream.of( startRole ), getParentRoleStream( startRole ).flatMap( this::recurseRoles ) ).distinct( );
+    }
+
+    private Stream<? extends Role> getParentRoleStream( Role role )
+    {
+        try
+        {
+            return rbacManager.getParentRoleNames( role ).values( ).stream( );
+        }
+        catch ( RbacManagerException e )
+        {
+            throw new RuntimeException( e );
+        }
+    }
+
+    BaseUserInfo getUserInfo( String userId )
+    {
+        try
+        {
+            User user = userManager.findUser( userId );
+            return new BaseUserInfo( user.getId( ), user.getUsername( ) );
+        }
+        catch ( UserManagerException e )
+        {
+            throw new RuntimeException( e );
+        }
+    }
+
+    protected Optional<RoleInfo> getRoleInfoOptional( Role rbacRole )
+    {
+        try
+        {
+            RoleInfo role = RoleInfo.of( rbacRole );
+            role.setParentRoleIds( getParentRoles( rbacRole ) );
+            role.setChildRoleIds( getChildRoles( rbacRole ) );
+            role.setAssignedUsers( getAssignedUsersRecursive( rbacRole ) );
+            return Optional.of( role );
+        }
+        catch ( RbacManagerException e )
+        {
+            log.error( "Error while retrieving role information {}", e.getMessage( ), e );
+            return Optional.empty( );
+        }
+    }
+}
diff --git a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/v2/DefaultGroupService.java b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/v2/DefaultGroupService.java
index c512194..a495d42 100644
--- a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/v2/DefaultGroupService.java
+++ b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/v2/DefaultGroupService.java
@@ -61,6 +61,7 @@ import java.util.stream.Collectors;
  * @author Martin Stockhammer
  * @since 3.0
  */
+@SuppressWarnings( "SpringJavaAutowiredFieldsWarningInspection" )
 @Service("v2.groupService#rest")
 public class DefaultGroupService
     implements GroupService
@@ -85,7 +86,10 @@ public class DefaultGroupService
     @Named(value = "ldapConnectionFactory#configurable")
     private LdapConnectionFactory ldapConnectionFactory;
 
-    private static final Group getGroupFromLdap( LdapGroup ldapGroup ) {
+    public DefaultGroupService( ) {
+    }
+
+    private static Group getGroupFromLdap( LdapGroup ldapGroup ) {
         Group group = new Group( );
         group.setName( ldapGroup.getName() );
         group.setUniqueName( ldapGroup.getDn() );
@@ -128,7 +132,7 @@ public class DefaultGroupService
      * be found, it will set "" for the uniqueName
      *
      * @return the list of mapping
-     * @throws RedbackServiceException
+     * @throws RedbackServiceException if there was an error retrieving the mapping data
      */
     @Override
     public List<GroupMapping> getGroupMappings()
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
new file mode 100644
index 0000000..a4077b7
--- /dev/null
+++ b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/v2/DefaultRoleService.java
@@ -0,0 +1,434 @@
+package org.apache.archiva.redback.rest.services.v2;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.archiva.redback.integration.security.role.RedbackRoleConstants;
+import org.apache.archiva.redback.integration.util.RoleSorter;
+import org.apache.archiva.redback.rbac.Permission;
+import org.apache.archiva.redback.rbac.RBACManager;
+import org.apache.archiva.redback.rbac.RbacManagerException;
+import org.apache.archiva.redback.rbac.RbacObjectNotFoundException;
+import org.apache.archiva.redback.rbac.Resource;
+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.Role;
+import org.apache.archiva.redback.rest.api.model.RoleTemplate;
+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.services.RedbackServiceException;
+import org.apache.archiva.redback.rest.api.services.v2.RoleService;
+import org.apache.archiva.redback.rest.services.RedbackAuthenticationThreadLocal;
+import org.apache.archiva.redback.rest.services.RedbackRequestInformation;
+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.ModelTemplate;
+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;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+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.Predicate;
+import java.util.stream.Collectors;
+
+/**
+ * @author Olivier Lamy
+ * @since 1.3
+ */
+@Service("v2.roleService#rest")
+public class DefaultRoleService extends BaseRedbackService
+    implements RoleService
+{
+
+    private Logger log = LoggerFactory.getLogger( DefaultRoleService.class );
+
+    private RoleManager roleManager;
+
+    @Context
+    private HttpServletRequest httpServletRequest;
+
+    @Context
+    private HttpServletResponse httpServletResponse;
+
+    @Context
+    private UriInfo uriInfo;
+
+    private static final String[] DEFAULT_SEARCH_FIELDS = {"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;
+
+    static
+    {
+
+        QUERY_HELPER = new QueryHelper<>( FILTER_MAP, ORDER_MAP, DEFAULT_SEARCH_FIELDS );
+        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 );
+
+        // 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
+        QUERY_HELPER.addNullsafeFieldComparator( "name", org.apache.archiva.redback.rbac.Role::getName );
+        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( "template_instance", org.apache.archiva.redback.rbac.Role::isTemplateInstance );
+    }
+
+    @Inject
+    public DefaultRoleService( RoleManager roleManager,
+                               @Named(value = "rbacManager#default") RBACManager rbacManager,
+                               @Named(value = "userManager#default") UserManager userManager )
+    {
+        super( rbacManager, userManager );
+        this.roleManager = roleManager;
+
+        log.debug( "use rbacManager impl: {}", rbacManager.getClass().getName() );
+        log.debug( "use userManager impl: {}", userManager.getClass().getName() );
+    }
+
+    @Override
+    public PagedResult<RoleInfo> getAllRoles( String searchTerm, Integer offset, Integer limit, List<String> orderBy, String order ) throws RedbackServiceException
+    {
+        boolean ascending = !"desc".equals( 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 role list
+            List<? extends org.apache.archiva.redback.rbac.Role> rawRoles = rbacManager.getAllRoles( );
+            Predicate<org.apache.archiva.redback.rbac.Role> filter = QUERY_HELPER.getQueryFilter( searchTerm );
+            long size = rawRoles.stream( ).filter( filter ).count( );
+            List<RoleInfo> users = rawRoles.stream( )
+                .filter( filter )
+                .sorted( QUERY_HELPER.getComparator( orderBy, ascending ) ).skip( offset ).limit( limit )
+                .map( role -> {
+                    try
+                    {
+                        return Optional.of( getRoleInfo( role ) );
+                    }
+                    catch ( RedbackServiceException e )
+                    {
+                        return Optional.<RoleInfo>empty();
+                    }
+                } ).filter(Optional::isPresent)
+                .map(Optional::get)
+                .collect( Collectors.toList( ) );
+            return new PagedResult<>( (int) size, offset, limit, users );
+        }
+        catch ( RbacManagerException e )
+        {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_RBACMANAGER_FAIL , e.getMessage( )) );
+        }
+
+    }
+
+    @Override
+    public RoleInfo getRole( String roleId ) throws RedbackServiceException
+    {
+        try
+        {
+            org.apache.archiva.redback.rbac.Role rbacRole = rbacManager.getRoleById( roleId );
+            RoleInfo role = getRoleInfo( rbacRole );
+            return role;
+        }
+        catch ( RbacObjectNotFoundException e )
+        {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_ROLE_NOT_FOUND, roleId ), 404 );
+        }
+        catch ( RbacManagerException e )
+        {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_RBACMANAGER_FAIL, e.getMessage( ) ) );
+        }
+    }
+
+    @Override
+    public Response checkRole( String roleId ) throws RedbackServiceException
+    {
+        try
+        {
+            org.apache.archiva.redback.rbac.Role rbacRole = rbacManager.getRoleById( roleId );
+            if (rbacRole==null) {
+                return Response.status( 404 ).build();
+            } else
+            {
+                return Response.ok( ).build( );
+            }
+        }
+        catch ( RbacObjectNotFoundException e )
+        {
+            return Response.status( 404 ).build();
+        }
+        catch ( RbacManagerException e )
+        {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_RBACMANAGER_FAIL, e.getMessage( ) ) );
+        }
+    }
+
+
+
+    @Override
+    public RoleInfo moveTemplatedRole( String templateId, String oldResource, String newResource )
+        throws RedbackServiceException
+    {
+        try
+        {
+            if (StringUtils.isEmpty( templateId ) || StringUtils.isEmpty( oldResource ) || StringUtils.isEmpty( newResource )) {
+                throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_ROLE_NOT_FOUND ), 404 );
+            }
+            boolean sourceExists = roleManager.templatedRoleExists( templateId, oldResource );
+            if (!sourceExists) {
+                throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_ROLE_INSTANCE_NOT_FOUND, templateId, oldResource ), 404 );
+            }
+            boolean destExists = roleManager.templatedRoleExists( templateId, newResource );
+            if (destExists) {
+                httpServletResponse.setHeader( "Location", uriInfo.getAbsolutePathBuilder().path("../../..").path(newResource).build(  ).normalize().toString() );
+                throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_ROLE_INSTANCE_EXISTS, templateId, newResource ), 303 );
+            }
+            String roleId = roleManager.moveTemplatedRole( templateId, oldResource, newResource );
+            httpServletResponse.setHeader( "Location", uriInfo.getAbsolutePathBuilder().path("../../..").path(newResource).build(  ).normalize().toString() );
+            httpServletResponse.setStatus( 201 );
+            return getRoleInfo( rbacManager.getRoleById( roleId ) );
+        }
+        catch ( RoleExistsException e ) {
+            httpServletResponse.setHeader( "Location", uriInfo.getAbsolutePathBuilder().path("../../..").path(newResource).build(  ).normalize().toString() );
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_ROLE_INSTANCE_EXISTS, templateId, newResource ), 303 );
+        }
+        catch ( RoleManagerException e )
+        {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_ROLEMANAGER_FAIL, e.getMessage( ) ) );
+        }
+        catch ( RbacManagerException e )
+        {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_RBACMANAGER_FAIL, e.getMessage( ) ) );
+        }
+    }
+
+
+    @Override
+    public Response checkTemplateRole( String templateId, String resource )
+        throws RedbackServiceException
+    {
+        try
+        {
+            if (roleManager.templatedRoleExists( templateId, resource )) {
+                return Response.ok( ).build( );
+            } else {
+                return Response.status( 404 ).build();
+            }
+        }
+        catch ( RoleManagerException e )
+        {
+            throw new RedbackServiceException( e.getMessage() );
+        }
+
+    }
+
+    @Override
+    public RoleInfo createTemplatedRole( String templateId, String resource )
+        throws RedbackServiceException
+    {
+        if (StringUtils.isEmpty( templateId )) {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_ROLE_NOT_FOUND ), 404 );
+        }
+        if (StringUtils.isEmpty( resource )) {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_ROLE_NOT_FOUND ), 404 );
+        }
+        try
+        {
+            boolean exists = roleManager.templatedRoleExists( templateId, resource );
+            String roleId = roleManager.createTemplatedRole( templateId, resource );
+            httpServletResponse.setHeader( "Location", uriInfo.getAbsolutePathBuilder().path("../../..").path(roleId).build(  ).normalize().toString() );
+            if (exists)
+            {
+                httpServletResponse.setStatus( 200 );
+            } else {
+                httpServletResponse.setStatus( 201 );
+            }
+            return getRoleInfo( rbacManager.getRoleById( roleId ) );
+        } catch (RoleNotFoundException e) {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_ROLE_NOT_FOUND, templateId, resource ), 404 );
+        } catch (RoleExistsException e) {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_ROLE_INSTANCE_EXISTS, templateId, resource ), 303 );
+        }
+        catch ( RoleManagerException e )
+        {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_ROLEMANAGER_FAIL, e.getMessage( ) ) );
+        }
+        catch ( RbacManagerException e )
+        {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_RBACMANAGER_FAIL, e.getMessage( ) ) );
+        }
+    }
+
+    @Override
+    public Response removeTemplatedRole( String templateId, String resource )
+        throws RedbackServiceException
+    {
+
+        try
+        {
+            roleManager.removeTemplatedRole( templateId, resource );
+            return Response.ok( ).build( );
+        }
+        catch ( RoleNotFoundException e ) {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_ROLE_INSTANCE_NOT_FOUND, templateId, resource ), 404 );
+        }
+        catch ( RoleManagerException e )
+        {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_ROLEMANAGER_FAIL, e.getMessage( ) ) );
+        }
+    }
+
+
+
+    @Override
+    public RoleInfo assignRole( String roleId, String userId )
+        throws RedbackServiceException
+    {
+        try
+        {
+            userManager.findUser( userId );
+            roleManager.assignRole( roleId, userId );
+            return getRoleInfo( rbacManager.getRoleById( roleId ) );
+        }
+        catch ( RoleNotFoundException e ) {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_ROLE_NOT_FOUND, e.getMessage( ) ), 404 );
+        }
+        catch ( RoleManagerException e )
+        {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_ROLEMANAGER_FAIL, e.getMessage( ) ) );
+        }
+        catch ( UserNotFoundException e )
+        {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_USER_NOT_FOUND, e.getMessage( ) ), 404 );
+        }
+        catch ( UserManagerException e )
+        {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_USERMANAGER_FAIL, e.getMessage( ) ) );
+        }
+        catch ( RbacObjectNotFoundException e )
+        {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_RBACMANAGER_FAIL, e.getMessage( ) ) );
+        }
+        catch ( RbacManagerException e )
+        {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_RBACMANAGER_FAIL, e.getMessage( ) ) );
+        }
+    }
+
+
+    @Override
+    public RoleInfo assignTemplatedRole( String templateId, String resource, String principal )
+        throws RedbackServiceException
+    {
+        try
+        {
+            roleManager.assignTemplatedRole( templateId, resource, principal );
+        }
+        catch ( RoleManagerException e )
+        {
+            throw new RedbackServiceException( e.getMessage() );
+        }
+        return null;
+    }
+
+    @Override
+    public RoleInfo unassignRole( String roleId, String principal )
+        throws RedbackServiceException
+    {
+        try
+        {
+            roleManager.unassignRole( roleId, principal );
+        }
+        catch ( RoleManagerException e )
+        {
+            throw new RedbackServiceException( e.getMessage() );
+        }
+        return null;
+    }
+
+    @Override
+    public RoleInfo updateRole( String roleId, Role role ) throws RedbackServiceException
+    {
+        return null;
+    }
+
+
+
+//    public List<Role> getEffectivelyAssignedRoles( String username )
+//        throws RedbackServiceException
+//    {
+//        if ( StringUtils.isEmpty( username ) )
+//        {
+//            throw new RedbackServiceException( new ErrorMessage( "user.cannot.be.null" ) );
+//        }
+//        try
+//        {
+//            List<? extends org.apache.archiva.redback.rbac.Role> roles =
+//                filterAssignableRoles( rbacManager.getEffectivelyAssignedRoles( username ) );
+//
+//            List<Role> effectivelyAssignedRoles = new ArrayList<Role>( roles.size() );
+//
+//            for ( org.apache.archiva.redback.rbac.Role r : roles )
+//            {
+//                effectivelyAssignedRoles.add( new Role( r ) );
+//            }
+//
+//            Collections.sort( effectivelyAssignedRoles, RoleComparator.INSTANCE  );
+//
+//            return effectivelyAssignedRoles;
+//        }
+//        catch ( RbacManagerException rme )
+//        {
+//            // ignore, this can happen when the user has no roles assigned
+//        }
+//        return new ArrayList<Role>( 0 );
+//    }
+
+
+    //----------------------------------------------------------------
+    // Internal methods
+    //----------------------------------------------------------------
+
+
+}
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 b465809..5561d5c 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
@@ -41,17 +41,22 @@ import org.apache.archiva.redback.policy.PasswordRuleViolationException;
 import org.apache.archiva.redback.policy.UserSecurityPolicy;
 import org.apache.archiva.redback.rbac.RBACManager;
 import org.apache.archiva.redback.rbac.RbacManagerException;
+import org.apache.archiva.redback.rbac.Role;
 import org.apache.archiva.redback.rbac.UserAssignment;
 import org.apache.archiva.redback.rest.api.MessageKeys;
 import org.apache.archiva.redback.rest.api.model.ActionStatus;
 import org.apache.archiva.redback.rest.api.model.ErrorMessage;
+import org.apache.archiva.redback.rest.api.model.v2.Application;
 import org.apache.archiva.redback.rest.api.model.v2.AvailabilityStatus;
+import org.apache.archiva.redback.rest.api.model.v2.BaseRoleInfo;
 import org.apache.archiva.redback.rest.api.model.v2.Operation;
 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.PingResult;
 import org.apache.archiva.redback.rest.api.model.v2.RegistrationKey;
 import org.apache.archiva.redback.rest.api.model.v2.Resource;
+import org.apache.archiva.redback.rest.api.model.v2.RoleInfo;
+import org.apache.archiva.redback.rest.api.model.v2.RoleTree;
 import org.apache.archiva.redback.rest.api.model.v2.SelfUserData;
 import org.apache.archiva.redback.rest.api.model.v2.User;
 import org.apache.archiva.redback.rest.api.model.v2.UserInfo;
@@ -65,6 +70,7 @@ import org.apache.archiva.redback.rest.services.interceptors.RedbackPrincipal;
 import org.apache.archiva.redback.rest.services.utils.PasswordValidator;
 import org.apache.archiva.redback.role.RoleManager;
 import org.apache.archiva.redback.role.RoleManagerException;
+import org.apache.archiva.redback.role.model.ModelApplication;
 import org.apache.archiva.redback.system.SecuritySession;
 import org.apache.archiva.redback.system.SecuritySystem;
 import org.apache.archiva.redback.users.UserManager;
@@ -91,17 +97,19 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
+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;
+import java.util.stream.Stream;
 
 @Service( "v2.userService#rest" )
-public class DefaultUserService
+public class DefaultUserService extends BaseRedbackService
     implements UserService
 {
 
@@ -113,6 +121,7 @@ public class DefaultUserService
     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
     {
@@ -133,10 +142,9 @@ public class DefaultUserService
         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 ) );
-    }
-
 
-    private UserManager userManager;
+        QUERY_HELPER = new QueryHelper<>( FILTER_MAP, ORDER_MAP, DEFAULT_SEARCH_FIELDS );
+    }
 
     private SecuritySystem securitySystem;
 
@@ -174,9 +182,10 @@ public class DefaultUserService
     @Inject
     private Mailer mailer;
 
+
     @Inject
-    @Named( value = "rbacManager#default" )
-    private RBACManager rbacManager;
+    @Named( value = "v2.roleService#rest" )
+    private DefaultRoleService roleManagementService;
 
     private HttpAuthenticator httpAuthenticator;
 
@@ -196,10 +205,11 @@ public class DefaultUserService
     private SecurityContext securityContext;
 
     @Inject
-    public DefaultUserService( @Named( value = "userManager#default" ) UserManager userManager,
+    public DefaultUserService(@Named( value = "rbacManager#default" ) RBACManager rbacManager,
+                              @Named( value = "userManager#default" ) UserManager userManager ,
                                SecuritySystem securitySystem )
     {
-        this.userManager = userManager;
+        super( rbacManager, userManager );
         this.securitySystem = securitySystem;
     }
 
@@ -220,7 +230,8 @@ public class DefaultUserService
     public UserInfo createUser( User user )
         throws RedbackServiceException
     {
-        if (user==null) {
+        if ( user == null )
+        {
             throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_USER_ID_EMPTY ), 422 );
         }
         UserInfo result;
@@ -311,7 +322,8 @@ public class DefaultUserService
     public void deleteUser( String userId )
         throws RedbackServiceException
     {
-        if (StringUtils.isEmpty( userId )) {
+        if ( StringUtils.isEmpty( userId ) )
+        {
             throw new RedbackServiceException( MessageKeys.ERR_USER_ID_EMPTY, 404 );
         }
 
@@ -355,7 +367,8 @@ public class DefaultUserService
     public UserInfo getUser( String userId )
         throws RedbackServiceException
     {
-        if (StringUtils.isEmpty( userId)) {
+        if ( StringUtils.isEmpty( userId ) )
+        {
             throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_USER_ID_EMPTY ), 404 );
         }
         try
@@ -377,55 +390,6 @@ public class DefaultUserService
         }
     }
 
-    Comparator<org.apache.archiva.redback.users.User> getAttributeComparator( String attributeName )
-    {
-        return ORDER_MAP.get( attributeName );
-    }
-
-    Comparator<org.apache.archiva.redback.users.User> getComparator( List<String> orderBy, boolean ascending )
-    {
-        if ( ascending )
-        {
-            return orderBy.stream( ).map( ( String name ) -> getAttributeComparator( name ) ).filter( Objects::nonNull ).reduce( Comparator::thenComparing ).get( );
-        }
-        else
-        {
-            return orderBy.stream( ).map( ( String name ) -> getAttributeComparator( name ) == null ? null : getAttributeComparator( name ).reversed( ) ).filter( Objects::nonNull ).reduce( Comparator::thenComparing ).get( );
-        }
-    }
-
-    static Predicate<org.apache.archiva.redback.users.User> getFilter( final String attribute, final String queryToken )
-    {
-        if ( FILTER_MAP.containsKey( attribute ) )
-        {
-            return ( org.apache.archiva.redback.users.User u ) -> FILTER_MAP.get( attribute ).test( queryToken, u );
-        }
-        else
-        {
-            return Arrays.stream( DEFAULT_SEARCH_FIELDS )
-                .map( att -> getFilter( att, queryToken ) ).reduce( Predicate::or ).get( );
-        }
-    }
-
-    Predicate<org.apache.archiva.redback.users.User> getUserFilter( String queryTerms )
-    {
-        return Arrays.stream( queryTerms.split( "\\s+" ) )
-            .map( s -> {
-                    if ( s.contains( ":" ) )
-                    {
-                        String attr = StringUtils.substringBefore( s, ":" );
-                        String term = StringUtils.substringAfter( s, ":" );
-                        return getFilter( attr, term );
-                    }
-                    else
-                    {
-                        return Arrays.stream( DEFAULT_SEARCH_FIELDS )
-                            .map( att -> getFilter( att, s ) ).reduce( Predicate::or ).get( );
-                    }
-                }
-            ).reduce( Predicate::or ).get( );
-    }
-
     @Override
     public PagedResult<UserInfo> getUsers( String q, Integer offset,
                                            Integer limit, List<String> orderBy, String order )
@@ -437,11 +401,11 @@ public class DefaultUserService
             // 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 = getUserFilter( q );
+            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( getComparator( orderBy, ascending ) ).skip( offset ).limit( limit )
+                .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 );
@@ -975,6 +939,101 @@ public class DefaultUserService
     }
 
     @Override
+    public List<RoleInfo> getEffectivelyAssignedRoles( String username ) throws RedbackServiceException
+    {
+        try
+        {
+            return rbacManager.getEffectivelyAssignedRoles( username ).stream( )
+                .filter( org.apache.archiva.redback.rbac.Role::isAssignable )
+                .map( this::getRoleInfoOptional )
+                .filter( Optional::isPresent )
+                .map(Optional::get).collect( Collectors.toList());
+        }
+        catch ( RbacManagerException e )
+        {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_RBACMANAGER_FAIL, e.getMessage( ) ) );
+        }
+    }
+
+    private static final Application toApplication( ModelApplication app )
+    {
+        Application application = new Application( );
+        application.setId( app.getId( ) );
+        application.setVersion( app.getVersion( ) );
+        application.setDescription( app.getDescription( ) == null ? "" :app.getDescription() );
+        application.setLongDescription( app.getLongDescription( ) == null ? "" : app.getLongDescription( ) );
+        return application;
+    }
+
+    private List<Application> getAllApplications( )
+    {
+        return roleManager.getModel( ).getApplications( ).stream( ).map( DefaultUserService::toApplication ).collect( Collectors.toList( ) );
+    }
+
+
+    @Override
+    public RoleTree getRoleTree( final String username ) throws RedbackServiceException
+    {
+        final Map<String, String> roleApplicationMap = roleManager.getModel( ).getApplications( ).stream( )
+            .flatMap( modelApplication -> modelApplication.getRoles( ).stream( ).map( role -> {
+                BaseRoleInfo roleInfo = new BaseRoleInfo( );
+                roleInfo.setId( role.getId( ) );
+                roleInfo.setApplicationId( modelApplication.getId( ) );
+                return roleInfo;
+            } ) ).collect( Collectors.toMap( BaseRoleInfo::getId, BaseRoleInfo::getApplicationId ) );
+
+        try
+        {
+            final Set<String> assignedRoleNames = new HashSet( rbacManager.getUserAssignment( username ).getRoleNames( ) );
+            // We have to reuse the BaseRoleInfo objects, because the roles are not returned starting from the roots
+            final Map<String, BaseRoleInfo> roleNameCache = new HashMap<>( );
+            List<BaseRoleInfo> roleList = rbacManager.getAllRoles( ).stream( ).flatMap( this::flattenRole ).map( role ->
+            {
+                BaseRoleInfo roleInfo = roleNameCache.computeIfAbsent( role.getName( ), s -> new BaseRoleInfo( ) );
+                // Setting the role data, as there may be child role objects that are not completely initialized
+                roleInfo = BaseRoleInfo.of( role, roleInfo );
+                roleInfo.setApplicationId( roleApplicationMap.get( role.getId( ) ) );
+                roleInfo.setAssigned( assignedRoleNames.contains( role.getName( ) ) );
+                roleInfo.setChildren( role.getChildRoleNames( ).stream( )
+                    .map( roleName ->
+                    {
+                        BaseRoleInfo childRoleInfo = roleNameCache.computeIfAbsent( roleName, s -> BaseRoleInfo.ofName( roleName ) );
+                        childRoleInfo.setChild( true );
+                        return childRoleInfo;
+                    } )
+                    .collect( Collectors.toList( ) ) );
+                return roleInfo;
+            } ).collect( Collectors.toList( ) );
+            RoleTree roleTree = new RoleTree( );
+            roleTree.setApplications( getAllApplications( ).stream( ).collect( Collectors.toMap( Application::getId, Function.identity( ) ) ) );
+            roleTree.setRootRoles( roleList.stream( ).filter( BaseRoleInfo::isNotChild ).collect( Collectors.toList( ) ) );
+            roleTree.setUserId( username );
+            return roleTree;
+        }
+        catch ( RbacManagerException e )
+        {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_RBACMANAGER_FAIL, e.getMessage( ) ) );
+        }
+    }
+
+    private Stream<Role> flattenRole( Role role )
+    {
+        return Stream.concat( Stream.of( role ), this.getChildren( role ).flatMap( this::flattenRole ) ).distinct( );
+    }
+
+    private Stream<? extends Role> getChildren( Role role )
+    {
+        try
+        {
+            return rbacManager.getChildRoleNames( role ).values( ).stream( );
+        }
+        catch ( RbacManagerException e )
+        {
+            throw new RuntimeException( e );
+        }
+    }
+
+    @Override
     public Collection<Operation> getUserOperations( String userName )
         throws RedbackServiceException
     {
diff --git a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/v2/QueryHelper.java b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/v2/QueryHelper.java
new file mode 100644
index 0000000..ba417aa
--- /dev/null
+++ b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/v2/QueryHelper.java
@@ -0,0 +1,168 @@
+package org.apache.archiva.redback.rest.services.v2;/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.BiPredicate;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+/**
+ *
+ * Helper class that returns combined filter and comparison objects for ordering.
+ *
+ * The query term may be consist of simple query terms separated by whitespace or attribute queries
+ * in the form <code>attribute:query</code>, which means only the attribute is searched for the query string.
+ * <br />
+ * Example:
+ * <dl>
+ *     <dt>`user1 test`</dt>
+ *     <dd>
+ * searches for the tokens user1 and test in the default attributes.
+ * </dd>
+ * <dt>`user1 name:test`</dt>
+ * <dd>searches for the token user1 in the default attributes and for the token test in the attribute name.</dd>
+ * </dl>
+ *
+ *
+ * @since 3.0
+ * @author Martin Stockhammer <ma...@apache.org>
+ */
+public class QueryHelper<T>
+{
+
+    private final Map<String, BiPredicate<String, T>> FILTER_MAP;
+    private final Map<String, Comparator<T>> ORDER_MAP;
+    private final String[] DEFAULT_SEARCH_FIELDS;
+    private final Predicate<T> DEFAULT_FILTER = ( T att ) -> false;
+
+
+    /**
+     * Creates a new query helper with the given filters and comparators.
+     *
+     * @param filterMap a map of filters, where the key is the attribute name and the value is a predicate that matches
+     *                  the filter value and the object instance.
+     * @param orderMap a map of comparators, where key is the attribute name and the value is a comparator for the given
+     *                 object instance
+     * @param defaultSearchFields A array of attribute names, that are used as default search fields.
+     */
+    public QueryHelper(Map<String, BiPredicate<String, T>> filterMap, Map<String, Comparator<T>> orderMap,
+                       String[] defaultSearchFields)
+    {
+        this.FILTER_MAP = filterMap;
+        this.DEFAULT_SEARCH_FIELDS = defaultSearchFields;
+        this.ORDER_MAP = new HashMap<>( orderMap );
+    }
+
+    public <U extends Comparable<? super U>> void addNullsafeFieldComparator( String fieldName, Function<? super T, U> keyExtractor) {
+        ORDER_MAP.put( fieldName, Comparator.comparing( keyExtractor, Comparator.nullsLast( Comparator.naturalOrder( ) ) ) );
+    }
+
+    public void addStringFilter(String attribute, Function<? super T, String> keyExtractor) {
+        this.FILTER_MAP.put( attribute, ( String q, T r ) -> StringUtils.containsIgnoreCase( keyExtractor.apply( r ), q ) );
+    }
+
+    public void addBooleanFilter(String attribute, Function<? super T, Boolean> keyExtractor) {
+        this.FILTER_MAP.put( attribute, ( String q, T r ) -> Boolean.valueOf( q ) == keyExtractor.apply( r ) );
+    }
+
+    /**
+     * Get the comparator for a specific attribute.
+     * @param attributeName the name of the attribute.
+     * @return
+     */
+    Comparator<T> getAttributeComparator( String attributeName )
+    {
+        return ORDER_MAP.get( attributeName );
+    }
+
+    /**
+     * Get the combined order for the given attributes in the given order.
+     *
+     * @param orderBy the attributes to compare. The first attribute in the list will be used first for comparing.
+     * @param ascending
+     * @return
+     */
+    Comparator<T> getComparator( List<String> orderBy, boolean ascending )
+    {
+        if ( ascending )
+        {
+            return orderBy.stream( ).map( ( String name ) -> getAttributeComparator( name ) ).filter( Objects::nonNull )
+                .reduce( Comparator::thenComparing )
+                .orElseThrow( () -> new IllegalArgumentException( "No attribute ordering found" ) );
+        }
+
+        else
+        {
+            return orderBy.stream( ).map( ( String name ) -> getAttributeComparator( name ) == null ? null : getAttributeComparator( name )
+                .reversed( ) ).filter( Objects::nonNull ).reduce( Comparator::thenComparing )
+                .orElseThrow( () -> new IllegalArgumentException( "No attribute ordering found" ) );
+        }
+    }
+
+    /**
+     * Returns a query filter for a specific attribute and query token.
+     * @param attribute the attribute name to filter for.
+     * @param queryToken the search token.
+     * @return The predicate used to filter the token
+     */
+    Predicate<T> getAttributeQueryFilter( final String attribute, final String queryToken )
+    {
+        if ( FILTER_MAP.containsKey( attribute ) )
+        {
+            return ( T u ) -> FILTER_MAP.get( attribute ).test( queryToken, u );
+        }
+        else
+        {
+            return DEFAULT_FILTER;
+        }
+    }
+
+    /**
+     * Returns the combined query filter for the given query terms.
+     * The query terms may be either simple strings separated by whitespace or use the
+     * <code>attribute:query</code> syntax, that searches only the attribute for the query term.
+     * @param queryTerms the query string
+     * @return the combined query filter
+     */
+    Predicate<T> getQueryFilter( String queryTerms )
+    {
+        return Arrays.stream( queryTerms.split( "\\s+" ) )
+            .map( s -> {
+                    if ( s.contains( ":" ) )
+                    {
+                        String attr = StringUtils.substringBefore( s, ":" );
+                        String term = StringUtils.substringAfter( s, ":" );
+                        return getAttributeQueryFilter( attr, term );
+                    }
+                    else
+                    {
+                        return Arrays.stream( DEFAULT_SEARCH_FIELDS )
+                            .map( att -> getAttributeQueryFilter( att, s ) ).reduce( Predicate::or ).get( );
+                    }
+                }
+            ).reduce( Predicate::or ).get( );
+    }
+
+}
diff --git a/redback-integrations/redback-rest/redback-rest-services/src/main/resources/META-INF/spring-context.xml b/redback-integrations/redback-rest/redback-rest-services/src/main/resources/META-INF/spring-context.xml
index 5041bc4..ff90b95 100644
--- a/redback-integrations/redback-rest/redback-rest-services/src/main/resources/META-INF/spring-context.xml
+++ b/redback-integrations/redback-rest/redback-rest-services/src/main/resources/META-INF/spring-context.xml
@@ -93,7 +93,7 @@
     <jaxrs:serviceBeans>
       <ref bean="v2.userService#rest"/>
       <ref bean="v2.authenticationService#rest"/>
-      <ref bean="roleManagementService#rest"/>
+      <ref bean="v2.roleService#rest"/>
       <ref bean="utilServices#rest"/>
       <ref bean="passwordService#rest"/>
       <ref bean="v2.groupService#rest"/>
diff --git a/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/v2/AbstractNativeRestServices.java b/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/v2/AbstractNativeRestServices.java
index 2adc1e7..1523387 100644
--- a/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/v2/AbstractNativeRestServices.java
+++ b/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/v2/AbstractNativeRestServices.java
@@ -128,7 +128,7 @@ public abstract class AbstractNativeRestServices
     }
 
 
-    private String getServiceBasePath( )
+    protected String getServiceBasePath( )
     {
         return "/v2/redback";
     }
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
new file mode 100644
index 0000000..f3105d3
--- /dev/null
+++ b/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/v2/NativeRoleServiceTest.java
@@ -0,0 +1,505 @@
+package org.apache.archiva.redback.rest.services.v2;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import io.restassured.response.Response;
+import org.apache.archiva.redback.rest.api.model.v2.RoleInfo;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+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;
+import static org.hamcrest.Matchers.anyOf;
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * @author Martin Stockhammer <ma...@apache.org>
+ */
+@ExtendWith( SpringExtension.class )
+@ContextConfiguration(
+    locations = {"classpath:/ldap-spring-test.xml"} )
+@TestInstance( TestInstance.Lifecycle.PER_CLASS )
+@Tag( "rest-native" )
+@TestMethodOrder( MethodOrderer.Random.class )
+@DisplayName( "Native REST tests for V2 RoleService" )
+public class NativeRoleServiceTest extends AbstractNativeRestServices
+{
+    @Override
+    protected String getServicePath( )
+    {
+        return "/roles";
+    }
+
+    @BeforeAll
+    void setup( ) throws Exception
+    {
+        super.setupNative( );
+    }
+
+    @AfterAll
+    void destroy( ) throws Exception
+    {
+        super.shutdownNative( );
+    }
+
+    private String getUserServicePath()
+    {
+        return new StringBuilder( )
+            .append( getContextRoot( ) )
+            .append( getServiceBasePath( ) )
+            .append( "/users" ).toString( );
+    }
+
+
+
+    @Test
+    void createTemplatedRole( )
+    {
+        String token = getAdminToken( );
+        try
+        {
+            Response response = given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .put( "template/archiva-repository-manager/repository01" )
+                .then( ).statusCode( 201 ).extract( ).response( );
+            assertNotNull( response );
+            RoleInfo roleInfo = response.getBody( ).jsonPath( ).getObject( "", RoleInfo.class );
+            assertNotNull( response.getHeader( "Location" ) );
+            assertTrue( response.getHeader( "Location" ).endsWith( "/roles/" + roleInfo.getId( ) ) );
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .put( "template/archiva-repository-manager/repository01" )
+                .then( ).statusCode( 200 );
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .head( "template/archiva-repository-manager/repository01" )
+                .then( ).statusCode( 200 );
+            // Repository observer is child template of repository-manager and will be created too
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .head( "template/archiva-repository-observer/repository01" )
+                .then( ).statusCode( 200 );
+
+        }
+        finally
+        {
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .delete( "template/archiva-repository-manager/repository01" )
+                .then( ).statusCode( 200 );
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .delete( "template/archiva-repository-observer/repository01" )
+                .then( ).statusCode( 200 );
+        }
+    }
+
+    @Test
+    void createTemplatedRoleWithNonexistentTemplate( )
+    {
+        String token = getAdminToken( );
+        given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+            .when( )
+            .put( "template/abcdefg/repository01" )
+            .then( ).statusCode( 404 );
+    }
+
+    @Test
+    void deleteTemplatedRole( )
+    {
+        String token = getAdminToken( );
+        try
+        {
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .put( "template/archiva-repository-manager/repository05" )
+                .then( ).statusCode( 201 ).extract( ).response( );
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .delete( "template/archiva-repository-manager/repository01" )
+                .then( ).statusCode( 404 );
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .delete( "template/archiva-repository-manager/repository05" )
+                .then( ).statusCode( 200 );
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .delete( "template/archiva-repository-manager/repository05" )
+                .then( ).statusCode( 404 );
+        } finally
+        {
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .delete( "template/archiva-repository-observer/repository01" )
+                .then( ).statusCode( 200 );
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .delete( "template/archiva-repository-observer/repository05" )
+                .then( ).statusCode( 200 );
+
+        }
+    }
+
+    @Test
+    void checkTemplatedRole() {
+        String token = getAdminToken( );
+        given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+            .when( )
+            .put( "template/archiva-repository-observer/repository01" )
+            .then( ).statusCode( 201 );
+        try {
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .head( "template/archiva-repository-observer/repository01" )
+                .then( ).statusCode( 200 );
+
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .head( "archiva-repository-observer.repository01" )
+                .then( ).statusCode( 200 );
+        } finally
+        {
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .delete( "template/archiva-repository-observer/repository01" )
+                .then( ).statusCode( 200 );
+        }
+
+    }
+
+
+    @Nested
+    @DisplayName( "Test Role queries" )
+    @ContextConfiguration(
+        locations = {"classpath:/ldap-spring-test.xml"} )
+    @TestInstance( TestInstance.Lifecycle.PER_CLASS )
+    class TestRoleRetrieval
+    {
+        int roleInstances = 3;
+        String token;
+
+        @BeforeAll
+        void initRoles( )
+        {
+            this.token = getAdminToken( );
+            for ( int i = 0; i < roleInstances; i++ )
+            {
+                String suffix = String.format( "%03d", i );
+                given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                    .when( )
+                    .put( "template/archiva-repository-manager/repo" + suffix )
+                    .then( ).statusCode( 201 ).extract( ).response( );
+                given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                    .when( )
+                    .put( "template/archiva-repository-observer/repo" + suffix )
+                    .then( ).statusCode( anyOf( equalTo( 200 ), equalTo( 201 ) ) ).extract( ).response( );
+            }
+        }
+
+        @Test
+        void getMultipleRolesWithoutParams( )
+        {
+            Response response = given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( ).get( ).then( ).statusCode( 200 ).extract( ).response( );
+            assertNotNull( response );
+            List<RoleInfo> roleData = response.body( ).jsonPath( ).getList( "data", RoleInfo.class );
+            assertNotNull( roleData );
+            assertEquals( roleInstances * 2 + 9, roleData.size( ) );
+            assertEquals( Integer.valueOf( 0 ), response.body( ).jsonPath( ).get( "pagination.offset" ) );
+            assertEquals( Integer.valueOf( DEFAULT_PAGE_LIMIT ), response.body( ).jsonPath( ).get( "pagination.limit" ) );
+            assertEquals( Integer.valueOf( roleInstances * 2 + 9 ), response.body( ).jsonPath( ).get( "pagination.total_count" ) );
+
+        }
+
+        @Test
+        void getMultipleRolesWithPaging( )
+        {
+            HashMap<String, String> params = new HashMap<>( );
+            params.put( "limit", Integer.toString( 10 ) );
+            params.put( "offset", Integer.toString( 1 ) );
+            Response response = given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( ).params( params ).get( ).then( ).statusCode( 200 ).extract( ).response( );
+            List<RoleInfo> userData = response.body( ).jsonPath( ).getList( "data", RoleInfo.class );
+            assertNotNull( userData );
+            response.getBody( ).jsonPath( ).prettyPrint( );
+            assertEquals( 10, userData.size( ) );
+            assertEquals( Integer.valueOf( 1 ), response.body( ).jsonPath( ).get( "pagination.offset" ) );
+            assertEquals( Integer.valueOf( 10 ), response.body( ).jsonPath( ).get( "pagination.limit" ) );
+            assertEquals( Integer.valueOf( roleInstances * 2 + 9 ), response.body( ).jsonPath( ).get( "pagination.total_count" ) );
+        }
+
+        @Test
+        void getMultipleUsersWithPagingOrderByIdAndResource( )
+        {
+            HashMap<String, Object> params = new HashMap<>( );
+            params.put( "limit", Integer.toString( 8 ) );
+            params.put( "offset", Integer.toString( 5 ) );
+            params.put( "orderBy", Arrays.asList( "id", "resource" ) );
+            Response response = given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( ).params( params ).get( ).then( ).statusCode( 200 ).extract( ).response( );
+            List<RoleInfo> userData = response.body( ).jsonPath( ).getList( "data", RoleInfo.class );
+            assertNotNull( userData );
+            // admin user has toto@toto.org as email so is after aragorn
+            assertEquals( "repo002", userData.get( 0 ).getResource( ) );
+            assertEquals( "repo000", userData.get( 1 ).getResource( ) );
+            assertEquals( "archiva-repository-observer.repo000", userData.get( 1 ).getId( ) );
+            assertEquals( "archiva-system-administrator", userData.get( 4 ).getId( ) );
+            assertEquals( 8, userData.size( ) );
+            assertEquals( Integer.valueOf( 5 ), response.body( ).jsonPath( ).get( "pagination.offset" ) );
+            assertEquals( Integer.valueOf( 8 ), response.body( ).jsonPath( ).get( "pagination.limit" ) );
+            assertEquals( Integer.valueOf( roleInstances * 2 + 9 ), response.body( ).jsonPath( ).get( "pagination.total_count" ) );
+        }
+
+        @Test
+        void getMultipleUsersWithPagingOrderByIdAndResourceReverse( )
+        {
+            HashMap<String, Object> params = new HashMap<>( );
+            params.put( "limit", Integer.toString( 7 ) );
+            params.put( "offset", Integer.toString( 1 ) );
+            params.put( "orderBy", Arrays.asList( "id", "resource" ) );
+            params.put( "order", "desc" );
+            Response response = given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( ).params( params ).get( ).then( ).statusCode( 200 ).extract( ).response( );
+            response.getBody( ).jsonPath( ).prettyPrint( );
+            List<RoleInfo> userData = response.body( ).jsonPath( ).getList( "data", RoleInfo.class );
+            assertNotNull( userData );
+            // admin user has toto@toto.org as email so is after aragorn
+            assertEquals( "system-administrator", userData.get( 0 ).getId( ) );
+            assertEquals( "registered-user", userData.get( 1 ).getId( ) );
+            assertEquals( "guest", userData.get( 2 ).getId( ) );
+            assertEquals( "archiva-repository-observer.repo002", userData.get( 5 ).getId( ) );
+            assertEquals( 7, userData.size( ) );
+            assertEquals( Integer.valueOf( 1 ), response.body( ).jsonPath( ).get( "pagination.offset" ) );
+            assertEquals( Integer.valueOf( 7 ), response.body( ).jsonPath( ).get( "pagination.limit" ) );
+            assertEquals( Integer.valueOf( roleInstances * 2 + 9 ), response.body( ).jsonPath( ).get( "pagination.total_count" ) );
+        }
+
+        @Test
+        void getMultipleRolesWithPagingAndQuery( )
+        {
+            HashMap<String, String> params = new HashMap<>( );
+            params.put( "limit", Integer.toString( 10 ) );
+            params.put( "offset", Integer.toString( 0 ) );
+            params.put( "order", "asc" );
+            params.put( "q", "system" );
+            Response response = given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( ).params( params ).get( ).then( ).statusCode( 200 ).extract( ).response( );
+            List<RoleInfo> userData = response.body( ).jsonPath( ).getList( "data", RoleInfo.class );
+            assertNotNull( userData );
+            assertEquals( 2, userData.size( ) );
+            assertEquals( Integer.valueOf( 0 ), response.body( ).jsonPath( ).get( "pagination.offset" ) );
+            assertEquals( Integer.valueOf( 10 ), response.body( ).jsonPath( ).get( "pagination.limit" ) );
+            assertEquals( Integer.valueOf( 2 ), response.body( ).jsonPath( ).get( "pagination.total_count" ) );
+
+        }
+
+
+        @AfterAll
+        void cleanupRoles( )
+        {
+            for ( int i = 0; i < roleInstances; i++ )
+            {
+                String suffix = String.format( "%03d", i );
+                given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                    .when( ).delete( "template/archiva-repository-manager/repo" + suffix ).then( ).statusCode( 200 );
+                given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                    .when( ).delete( "template/archiva-repository-observer/repo" + suffix ).then( ).statusCode( 200 );
+            }
+
+        }
+    }
+
+    @Test
+    void getRole( )
+    {
+        String token = getAdminToken( );
+        Response response = given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+            .when( ).get( "archiva-system-administrator" ).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( ) );
+    }
+
+    @Test
+    void getNonExistingRole( )
+    {
+        String token = getAdminToken( );
+        given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+            .when( ).get( "abcdefg" ).then( ).statusCode( 404 );
+    }
+
+    @Test
+    void checkRole() {
+        String token = getAdminToken( );
+        Response response = given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+            .when( ).head( "archiva-system-administrator" ).then( ).statusCode( 200 ).extract( ).response( );
+        assertEquals(0,response.getBody( ).asByteArray().length);
+        given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+            .when( ).head( "abcdefg" ).then( ).statusCode( 404 );
+
+    }
+
+    @Test
+    void moveRole() {
+        String token = getAdminToken( );
+        try
+        {
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .put( "template/archiva-repository-manager/repository01" )
+                .then( ).statusCode( 201 );
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( ).head( "template/archiva-repository-observer/repository01" ).then( ).statusCode( 200 );
+
+            Response response = given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( ).post( "template/archiva-repository-manager/repository01/moveto/repository02" ).then( ).statusCode( 201 ).extract( ).response( );
+            RoleInfo role = response.getBody( ).jsonPath( ).getObject( "", RoleInfo.class );
+            assertNotNull( role );
+            assertEquals( "archiva-repository-manager.repository02", role.getId( ) );
+            assertEquals( "repository02", role.getResource( ) );
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( ).head( "template/archiva-repository-manager/repository01" ).then( ).statusCode( 404 );
+            // Child templates are copied and not moved
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( ).head( "template/archiva-repository-observer/repository01" ).then( ).statusCode( 200 );
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( ).head( "template/archiva-repository-observer/repository02" ).then( ).statusCode( 200 );
+
+        } finally
+        {
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .delete( "template/archiva-repository-manager/repository02" )
+                .then( ).statusCode( 200 );
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .delete( "template/archiva-repository-observer/repository01" )
+                .then( ).statusCode( 200 );
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .delete( "template/archiva-repository-observer/repository02" )
+                .then( ).statusCode( 200 );
+
+        }
+
+    }
+
+    @Test
+    void moveRoleToExistingDestination() {
+        String token = getAdminToken( );
+        try
+        {
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .put( "template/archiva-repository-manager/repository01" )
+                .then( ).statusCode( 201 );
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .put( "template/archiva-repository-manager/repository02" )
+                .then( ).statusCode( 201 );
+            Response response = given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( ).redirects( ).follow( false )
+                .post( "template/archiva-repository-manager/repository01/moveto/repository02" ).then( ).statusCode( 303 )
+                .extract( ).response( );
+            System.out.println( response.getHeader( "Location" ) );
+            assertTrue( response.getHeader( "Location" ).endsWith( "/roles/template/archiva-repository-manager/repository02" ) );
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( ).head( "template/archiva-repository-manager/repository01" ).then( ).statusCode( 200 );
+        } finally
+        {
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .delete( "template/archiva-repository-manager/repository01" )
+                .then( ).statusCode( 200 );
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .delete( "template/archiva-repository-manager/repository02" )
+                .then( ).statusCode( 200 );
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .delete( "template/archiva-repository-observer/repository02" )
+                .then( ).statusCode( 200 );
+
+        }
+
+    }
+
+
+    @Test
+    void assignRole() {
+        String token = getAdminToken( );
+        Map<String, Object> jsonAsMap = new HashMap<>( );
+        jsonAsMap.put( "user_id", "aragorn");
+        jsonAsMap.put( "email", "aragorn@lordoftherings.org" );
+        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 );
+
+            Response response = given( ).spec( getRequestSpec( token, getUserServicePath() ) ).contentType( JSON )
+                .when( )
+                .get( "aragorn/roles" )
+                .then( ).statusCode( 200 ).extract( ).response( );
+            List<RoleInfo> roles = response.getBody( ).jsonPath( ).getList( "", RoleInfo.class );
+            assertFalse( roles.stream( ).filter( role -> "system-administrator".equals( role.getId( ) ) ).findAny( ).isPresent( ) );
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .put( "system-administrator/assign/aragorn" )
+                .prettyPeek()
+                .then( ).statusCode( 200 );
+            response = given( ).spec( getRequestSpec( token, getUserServicePath() ) ).contentType( JSON )
+                .when( )
+                .get( "aragorn/roles" )
+                .then( ).statusCode( 200 ).extract( ).response( );
+            roles = response.getBody( ).jsonPath( ).getList( "", RoleInfo.class );
+            assertTrue( roles.stream( ).filter( role -> "system-administrator".equals( role.getId( ) ) ).findAny( ).isPresent( ) );
+        } finally
+        {
+            given( ).spec( getRequestSpec( token, getUserServicePath() ) ).contentType( JSON )
+                .when( )
+                .delete( "aragorn" ).getBody( );
+        }
+    }
+
+}
diff --git a/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/v2/NativeUserServiceTest.java b/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/v2/NativeUserServiceTest.java
index e8f6cef..4181d27 100644
--- a/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/v2/NativeUserServiceTest.java
+++ b/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/v2/NativeUserServiceTest.java
@@ -18,10 +18,13 @@ package org.apache.archiva.redback.rest.services.v2;
  * under the License.
  */
 
+import io.restassured.path.json.JsonPath;
 import io.restassured.response.Response;
+import org.apache.archiva.redback.rest.api.model.v2.BaseRoleInfo;
 import org.apache.archiva.redback.rest.api.model.v2.Operation;
 import org.apache.archiva.redback.rest.api.model.v2.Permission;
 import org.apache.archiva.redback.rest.api.model.v2.RegistrationKey;
+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.model.v2.VerificationStatus;
 import org.apache.archiva.redback.rest.services.mock.EmailMessage;
@@ -44,6 +47,7 @@ import java.util.Map;
 
 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;
 import static org.junit.jupiter.api.Assertions.*;
 
 /**
@@ -87,7 +91,7 @@ public class NativeUserServiceTest extends AbstractNativeRestServices
         assertNotNull( userData );
         assertEquals( 2, userData.size( ) );
         assertEquals( Integer.valueOf( 0 ), response.body( ).jsonPath( ).get( "pagination.offset" ) );
-        assertEquals( Integer.valueOf( 1000 ), response.body( ).jsonPath( ).get( "pagination.limit" ) );
+        assertEquals( Integer.valueOf( DEFAULT_PAGE_LIMIT ), response.body( ).jsonPath( ).get( "pagination.limit" ) );
         assertEquals( Integer.valueOf( 2 ), response.body( ).jsonPath( ).get( "pagination.total_count" ) );
     }
 
@@ -134,7 +138,7 @@ public class NativeUserServiceTest extends AbstractNativeRestServices
             assertEquals( "admin", userData.get( 0 ).getUserId( ) );
             assertEquals( userNum + 2, userData.size( ) );
             assertEquals( Integer.valueOf( 0 ), response.body( ).jsonPath( ).get( "pagination.offset" ) );
-            assertEquals( Integer.valueOf( 1000 ), response.body( ).jsonPath( ).get( "pagination.limit" ) );
+            assertEquals( Integer.valueOf( DEFAULT_PAGE_LIMIT ), response.body( ).jsonPath( ).get( "pagination.limit" ) );
             assertEquals( Integer.valueOf( userNum + 2 ), response.body( ).jsonPath( ).get( "pagination.total_count" ) );
 
         }
@@ -1508,4 +1512,128 @@ public class NativeUserServiceTest extends AbstractNativeRestServices
                 .then( ).statusCode( 200 );
         }
     }
+
+    @Test
+    void getUserRoles() {
+        String adminToken = getAdminToken( );
+
+        Map<String, Object> userMap = new HashMap<>( );
+        userMap.put( "user_id", "bilbo" );
+        userMap.put( "email", "bilbo@lordoftherings.org" );
+        userMap.put( "full_name", "Bilbo Beutlin" );
+        userMap.put( "validated", true );
+        userMap.put( "password", "pAssw0rD" );
+        userMap.put( "confirm_password", "pAssw0rD" );
+        given( ).spec( getRequestSpec( adminToken ) ).contentType( JSON )
+            .body( userMap )
+            .when( )
+            .post( )
+            .then( ).statusCode( 201 );
+
+
+        try
+        {
+            Response response = given( ).spec( getRequestSpec( adminToken ) ).contentType( JSON )
+                .when( )
+                .get( "bilbo/roles" )
+                .then( ).statusCode( 200 ).extract( ).response( );
+            assertNotNull( response );
+            JsonPath jsonPath = response.getBody( ).jsonPath( );
+            List<RoleInfo> roleList = jsonPath.getList( "", RoleInfo.class );
+            assertEquals( 1, roleList.size( ) );
+            assertEquals( "registered-user", roleList.get( 0 ).getId( ) );
+
+            response = given( ).spec( getRequestSpec( adminToken ) ).contentType( JSON )
+                .when( )
+                .get( "admin/roles" )
+                .then( ).statusCode( 200 ).extract( ).response( );
+            assertNotNull( response );
+            jsonPath = response.getBody( ).jsonPath( );
+            roleList = jsonPath.getList( "", RoleInfo.class );
+            jsonPath.prettyPrint( );
+            assertEquals( 4, roleList.size( ) );
+            assertTrue( roleList.stream( ).filter( role -> "system-administrator".equals( role.getId( ) ) ).findAny( ).isPresent( ) );
+            assertTrue( roleList.stream( ).filter( role -> "archiva-global-repository-manager".equals( role.getId( ) ) ).findAny( ).isPresent( ) );
+            assertTrue( roleList.stream( ).filter( role -> "archiva-global-repository-observer".equals( role.getId( ) ) ).findAny( ).isPresent( ) );
+            assertTrue( roleList.stream( ).filter( role -> "user-administrator".equals( role.getId( ) ) ).findAny( ).isPresent( ) );
+
+
+        }
+        finally
+        {
+            given( ).spec( getRequestSpec( adminToken ) ).contentType( JSON )
+                .delete( "bilbo" )
+                .then( ).statusCode( 200 );
+        }
+    }
+
+    @Test
+    void getRoleTree() {
+        String adminToken = getAdminToken( );
+
+        Map<String, Object> userMap = new HashMap<>( );
+        userMap.put( "user_id", "bilbo" );
+        userMap.put( "email", "bilbo@lordoftherings.org" );
+        userMap.put( "full_name", "Bilbo Beutlin" );
+        userMap.put( "validated", true );
+        userMap.put( "password", "pAssw0rD" );
+        userMap.put( "confirm_password", "pAssw0rD" );
+        given( ).spec( getRequestSpec( adminToken ) ).contentType( JSON )
+            .body( userMap )
+            .when( )
+            .post( )
+            .then( ).statusCode( 201 );
+
+
+        try
+        {
+            Response response = given( ).spec( getRequestSpec( adminToken ) ).contentType( JSON )
+                .when( )
+                .get( "bilbo/roletree" )
+                .then( ).statusCode( 200 ).extract( ).response( );
+            assertNotNull( response );
+            JsonPath jsonPath = response.getBody( ).jsonPath( );
+            assertTrue( jsonPath.getMap( "applications" ).containsKey( "System" ) );
+            assertTrue( jsonPath.getMap( "applications" ).containsKey( "Archiva" ) );
+            List<BaseRoleInfo> roleList = jsonPath.getList( "root_roles", BaseRoleInfo.class );
+            assertEquals( 3, roleList.size( ) );
+            assertTrue( roleList.stream( ).filter( role -> role.getId( ).equals( "guest" ) ).findFirst( ).isPresent( ) );
+            BaseRoleInfo registered = roleList.stream( ).filter( role -> role.getId( ).equals( "registered-user" ) ).findFirst( ).get( );
+            assertTrue( registered.isAssigned( ) );
+            BaseRoleInfo sysadmin = roleList.stream( ).filter( role -> role.getId( ).equals( "system-administrator" ) ).findFirst( ).get( );
+            assertFalse( sysadmin.isAssigned( ) );
+            assertFalse( sysadmin.isChild( ) );
+            assertEquals( 2, sysadmin.getChildren( ).size( ) );
+            assertTrue( sysadmin.getChildren( ).stream( ).filter( role -> role.getId( ).equals( "user-administrator" ) ).findFirst( ).isPresent( ) );
+
+
+            response = given( ).spec( getRequestSpec( adminToken ) ).contentType( JSON )
+                .when( )
+                .get( "admin/roletree" )
+                .then( ).statusCode( 200 ).extract( ).response( );
+            assertNotNull( response );
+            System.out.println( response.getBody( ).prettyPrint( ) );
+            jsonPath = response.getBody( ).jsonPath( );
+            assertTrue( jsonPath.getMap( "applications" ).containsKey( "System" ) );
+            assertTrue( jsonPath.getMap( "applications" ).containsKey( "Archiva" ) );
+            roleList = jsonPath.getList( "root_roles", BaseRoleInfo.class );
+            assertEquals( 3, roleList.size( ) );
+            assertTrue( roleList.stream( ).filter( role -> role.getId( ).equals( "guest" ) ).findFirst( ).isPresent( ) );
+            registered = roleList.stream( ).filter( role -> role.getId( ).equals( "registered-user" ) ).findFirst( ).get( );
+            assertFalse( registered.isAssigned( ) );
+            sysadmin = roleList.stream( ).filter( role -> role.getId( ).equals( "system-administrator" ) ).findFirst( ).get( );
+            assertTrue( sysadmin.isAssigned( ) );
+            assertFalse( sysadmin.isChild( ) );
+            assertEquals( 2, sysadmin.getChildren( ).size( ) );
+            assertTrue( sysadmin.getChildren( ).stream( ).filter( role -> role.getId( ).equals( "user-administrator" ) ).findFirst( ).isPresent( ) );
+
+
+        }
+        finally
+        {
+            given( ).spec( getRequestSpec( adminToken ) ).contentType( JSON )
+                .delete( "bilbo" )
+                .then( ).statusCode( 200 );
+        }
+    }
 }
diff --git a/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/AbstractRBACManager.java b/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/AbstractRBACManager.java
index 8ace7e6..bdecd29 100644
--- a/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/AbstractRBACManager.java
+++ b/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/AbstractRBACManager.java
@@ -33,6 +33,7 @@ import java.util.Map;
 import java.util.Set;
 import java.util.function.Function;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 /**
  * AbstractRBACManager
@@ -499,7 +500,7 @@ public abstract class AbstractRBACManager
 
         if ( role.hasChildRoles() )
         {
-            Map<String, ? extends Role> childRoles = getChildRoles( role );
+            Map<String, ? extends Role> childRoles = getChildRoleNames( role );
             Iterator<? extends Role> it = childRoles.values().iterator();
             while ( it.hasNext() )
             {
@@ -736,11 +737,11 @@ public abstract class AbstractRBACManager
         throws RbacObjectInvalidException, RbacManagerException
     {
         saveRole( childRole );
-        role.addChildRoleName( childRole.getName() );
+        role.addChildRole( childRole );
     }
 
     @Override
-    public Map<String, ? extends Role> getChildRoles( Role role )
+    public Map<String, ? extends Role> getChildRoleNames( Role role )
         throws RbacManagerException
     {
         Map<String, Role> childRoles = new HashMap<String, Role>();
@@ -797,7 +798,64 @@ public abstract class AbstractRBACManager
     }
 
     @Override
-    public Map<String, ? extends Role> getParentRoles( Role role )
+    public Map<String, ? extends Role> getChildRoleIds( Role role )
+        throws RbacManagerException
+    {
+        Map<String, Role> childRoles = new HashMap<String, Role>();
+
+        boolean childRoleNamesUpdated = false;
+
+        Iterator<String> it = role.getChildRoleIds().listIterator();
+
+        final List<String> updatedChildRoleList = new ArrayList<String>( role.getChildRoleIds().size() );
+
+        while ( it.hasNext() )
+        {
+            String roleId = it.next();
+            try
+            {
+                Role child = getRoleById(  roleId );
+                // archiva can change role manager but LDAP can be non writable so in such case
+                // some roles doesn't exists !!
+                if ( child != null )
+                {
+                    childRoles.put( child.getId(), child );
+                    updatedChildRoleList.add( roleId );
+                }
+                else
+                {
+                    log.warn(
+                        "error searching role with name '{}' probably some issues when migrating your role manager",
+                        roleId );
+                }
+            }
+            catch ( RbacObjectNotFoundException e )
+            {
+                // Found a bad roleName! - trigger new List save
+                //it.remove();
+                childRoleNamesUpdated = true;
+            }
+            catch ( RbacManagerException e )
+            {
+                if ( !( e.getCause() instanceof RbacObjectNotFoundException ) )
+                {
+                    throw e;
+                }
+                childRoleNamesUpdated = true;
+            }
+        }
+
+        if ( childRoleNamesUpdated )
+        {
+            role.setChildRoleIds( updatedChildRoleList );
+            saveRole( role );
+        }
+
+        return childRoles;
+    }
+
+    @Override
+    public Map<String, ? extends Role> getParentRoleNames( Role role )
         throws RbacManagerException
     {
         Map<String, Role> parentRoles = new HashMap<String, Role>();
@@ -823,6 +881,23 @@ public abstract class AbstractRBACManager
     }
 
     @Override
+    public Map<String, ? extends Role> getParentRoleIds( final Role role ) throws RbacManagerException
+    {
+        return getAllRoles( ).stream( ).filter( r -> !r.getId( ).equals( role.getId( ) ) )
+            .filter( r -> {
+                    try
+                    {
+                        return getEffectiveRoles( r ).stream( ).map( Role::getId ).filter( cRoleId -> cRoleId.equals( role.getId( ) ) ).findAny( ).isPresent( );
+                    }
+                    catch ( RbacManagerException e )
+                    {
+                        return false;
+                    }
+                }
+            ).distinct().collect( Collectors.toMap( Role::getId, Function.identity( ) ) );
+    }
+
+    @Override
     public Set<? extends Role> getEffectiveRoles( Role role )
         throws RbacObjectNotFoundException, RbacManagerException
     {
diff --git a/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/AbstractRole.java b/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/AbstractRole.java
index a90f66a..5736f5b 100644
--- a/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/AbstractRole.java
+++ b/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/AbstractRole.java
@@ -29,7 +29,7 @@ public abstract class AbstractRole
     @Override
     public boolean hasChildRoles()
     {
-        return ( getChildRoleNames() != null ) && !getChildRoleNames().isEmpty();
+        return ( getChildRoleIds() != null ) && !getChildRoleIds().isEmpty();
     }
 
     /**
@@ -57,4 +57,11 @@ public abstract class AbstractRole
 
         return result;
     }
+
+    @Override
+    public void addChildRole( Role child )
+    {
+        addChildRoleName( child.getName() );
+        addChildRoleId( child.getId() );
+    }
 }
diff --git a/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/RBACManager.java b/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/RBACManager.java
index 11b56db..16ac43d 100644
--- a/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/RBACManager.java
+++ b/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/RBACManager.java
@@ -155,21 +155,39 @@ public interface RBACManager
         throws RbacObjectInvalidException, RbacManagerException;
 
     /**
-     * Returns all the child roles of a given role.
+     * Returns all the child roles of a given role as (name, role) pairs.
      * @param role the parent role
      * @return the list of child roles
      * @throws RbacManagerException if the access to the backend datastore failed
      */
-    Map<String, ? extends Role> getChildRoles( Role role )
+    Map<String, ? extends Role> getChildRoleNames( Role role )
         throws RbacManagerException;
 
     /**
-     * Returns all the parent roles of a given role.
+     * Returns all the child roles of a given role as (role id, role) pairs.
+     * @param role the parent role
+     * @return the map of child roles as (role id, role) pairs
+     * @throws RbacManagerException if the access to the backend datastore failed
+     */
+    Map<String, ? extends Role> getChildRoleIds( Role role )
+        throws RbacManagerException;
+
+    /**
+     * Returns all the parent roles of a given role as map of (name, role)  elements.
      * @param role the role to check for parent roles
      * @return the list of parent roles that have <code>role</code> als child
      * @throws RbacManagerException if the access to the backend datastore failed
      */
-    Map<String, ? extends Role> getParentRoles( Role role )
+    Map<String, ? extends Role> getParentRoleNames( Role role )
+        throws RbacManagerException;
+
+    /**
+     * Returns all the parent roles of a given role as map of (id, role) elements.
+     * @param role the role to check for parents roles
+     * @return a map of (role id, role) pairs that have <code>role</code> as child
+     * @throws RbacManagerException if the access to the backend datastore failed
+     */
+    Map<String, ? extends Role> getParentRoleIds( Role role )
         throws RbacManagerException;
 
     /**
diff --git a/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/Role.java b/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/Role.java
index 407be44..f389d82 100644
--- a/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/Role.java
+++ b/redback-rbac/redback-rbac-model/src/main/java/org/apache/archiva/redback/rbac/Role.java
@@ -59,6 +59,24 @@ public interface Role
     List<String> getChildRoleNames();
 
     /**
+     * Adds a child role and sets the list of child names and child ids.
+     * @param child the child role
+     */
+    void addChildRole( Role child );
+
+    /**
+     * Adds a child role id
+     * @param id the id
+     */
+    void addChildRoleId( String id );
+
+    /**
+     * Returns the child role ids
+     * @return the list of child role ids
+     */
+    List<String> getChildRoleIds();
+
+    /**
      * Convenience method to see if Role has Child Roles.
      *
      * @return true if child roles exists and has any roles being tracked.
@@ -115,6 +133,12 @@ public interface Role
     void setChildRoleNames( List<String> names );
 
     /**
+     * Sets the list of child role ids
+     * @param ids
+     */
+    void setChildRoleIds( List<String> ids );
+
+    /**
      * Set the Description
      *
      * @param description the role description
@@ -220,4 +244,6 @@ public interface Role
      * @param resource the resource identifier. Must not be null.
      */
     void setResource( String resource );
+
+
 }
\ No newline at end of file
diff --git a/redback-rbac/redback-rbac-providers/redback-rbac-cached/src/main/java/org/apache/archiva/redback/rbac/cached/CachedRbacManager.java b/redback-rbac/redback-rbac-providers/redback-rbac-cached/src/main/java/org/apache/archiva/redback/rbac/cached/CachedRbacManager.java
index 8ffc360..7fbbcdd 100644
--- a/redback-rbac/redback-rbac-providers/redback-rbac-cached/src/main/java/org/apache/archiva/redback/rbac/cached/CachedRbacManager.java
+++ b/redback-rbac/redback-rbac-providers/redback-rbac-cached/src/main/java/org/apache/archiva/redback/rbac/cached/CachedRbacManager.java
@@ -291,19 +291,33 @@ public class CachedRbacManager
     }
 
     @Override
-    public Map<String, ? extends Role> getChildRoles( Role role )
+    public Map<String, ? extends Role> getChildRoleNames( Role role )
         throws RbacManagerException
     {
         log.debug( "NOT CACHED - .getChildRoles(Role)" );
-        return this.rbacImpl.getChildRoles( role );
+        return this.rbacImpl.getChildRoleNames( role );
     }
 
     @Override
-    public Map<String, ? extends Role> getParentRoles( Role role )
+    public Map<String, ? extends Role> getChildRoleIds( Role role ) throws RbacManagerException
+    {
+        log.debug( "NOT CACHED - .getChildRoles(Role)" );
+        return this.rbacImpl.getChildRoleIds( role );
+    }
+
+    @Override
+    public Map<String, ? extends Role> getParentRoleNames( Role role )
         throws RbacManagerException
     {
         log.debug( "NOT CACHED - .getParentRoles(Role)" );
-        return this.rbacImpl.getParentRoles( role );
+        return this.rbacImpl.getParentRoleNames( role );
+    }
+
+    @Override
+    public Map<String, ? extends Role> getParentRoleIds( Role role ) throws RbacManagerException
+    {
+        log.debug( "NOT CACHED - .getParentRoles(Role)" );
+        return this.rbacImpl.getParentRoleIds( role );
     }
 
     @Override
diff --git a/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/JpaRbacManager.java b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/JpaRbacManager.java
index 0c7a9da..8bd3212 100644
--- a/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/JpaRbacManager.java
+++ b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/JpaRbacManager.java
@@ -35,7 +35,7 @@ import org.apache.archiva.redback.rbac.jpa.model.JpaPermission;
 import org.apache.archiva.redback.rbac.jpa.model.JpaResource;
 import org.apache.archiva.redback.rbac.jpa.model.JpaRole;
 import org.apache.archiva.redback.rbac.jpa.model.JpaUserAssignment;
-import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.archiva.redback.rbac.jpa.model.RoleId;
 import org.springframework.stereotype.Service;
 
 import javax.persistence.EntityManager;
@@ -44,9 +44,6 @@ import javax.persistence.PersistenceContext;
 import javax.persistence.Query;
 import javax.persistence.TypedQuery;
 import javax.transaction.Transactional;
-import java.io.UnsupportedEncodingException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
@@ -138,8 +135,15 @@ public class JpaRbacManager extends AbstractRBACManager  {
 
     @Transactional
     @Override
-    public Map<String, ? extends Role> getChildRoles(Role role) throws RbacManagerException {
-        return super.getChildRoles(role);
+    public Map<String, ? extends Role> getChildRoleNames( Role role) throws RbacManagerException {
+        return super.getChildRoleNames(role);
+    }
+
+    @Transactional
+    @Override
+    public Map<String, ? extends Role> getChildRoleIds( Role role ) throws RbacManagerException
+    {
+        return super.getChildRoleIds( role );
     }
 
     @Transactional
@@ -219,7 +223,7 @@ public class JpaRbacManager extends AbstractRBACManager  {
             throw new RbacPermanentException( "Unable to delete permanent role [" + role.getName() + "]" );
         }
         final EntityManager em = getEm();
-        JpaRole myRole = em.find(JpaRole.class, role.getName());
+        JpaRole myRole = em.find(JpaRole.class, new RoleId( role.getId(), role.getName()));
         if (myRole == null) {
             throw new RbacObjectNotFoundException("Role not found "+role.getName());
         }
diff --git a/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/model/JpaRole.java b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/model/JpaRole.java
index d21eb6a..7b775ae 100644
--- a/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/model/JpaRole.java
+++ b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/model/JpaRole.java
@@ -21,6 +21,7 @@ package org.apache.archiva.redback.rbac.jpa.model;
 
 import org.apache.archiva.redback.rbac.AbstractRole;
 import org.apache.archiva.redback.rbac.Permission;
+import org.apache.archiva.redback.rbac.Role;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.core.annotation.Order;
@@ -37,15 +38,17 @@ import java.util.List;
 @Table(
         name="SECURITY_ROLES"
 )
+@IdClass( RoleId.class )
 public class JpaRole extends AbstractRole implements Serializable {
 
     private static final Logger log = LoggerFactory.getLogger( JpaRole.class );
     private static final long serialVersionUID = 4564608138465995665L;
 
     @Id
-    @Column(name="NAME")
+    @Column(name="NAME", unique = true)
     private String name;
-    @Column(name="ID", unique = true)
+    @Id
+    @Column( name = "ID", unique = true )
     private String id;
     @Column(name="DESCRIPTION")
     private String description;
@@ -75,6 +78,17 @@ public class JpaRole extends AbstractRole implements Serializable {
     )
     List<String> childRoleNames = new ArrayList<String>();
 
+    @ElementCollection(fetch = FetchType.EAGER)
+    @OrderColumn(name="INTEGER_IDX",nullable = false)
+    @Column(name="CHILD_IDS")
+    @CollectionTable(
+        name="SECURITY_ROLE_CHILDROLE_ID_MAP",
+        joinColumns = {
+            @JoinColumn(name="ID_OID",referencedColumnName = "ID", nullable = false)
+        }
+    )
+    List<String> childRoleIds = new ArrayList<String>();
+
     @Column(name="TEMPLATE_INSTANCE",nullable = false)
     private Boolean templateInstance = false;
 
@@ -102,11 +116,23 @@ public class JpaRole extends AbstractRole implements Serializable {
     }
 
     @Override
+    public void addChildRoleId( String id )
+    {
+        this.childRoleIds.add( id );
+    }
+
+    @Override
     public List<String> getChildRoleNames() {
         return childRoleNames;
     }
 
     @Override
+    public List<String> getChildRoleIds( )
+    {
+        return childRoleIds;
+    }
+
+    @Override
     public String getDescription() {
         return description;
     }
@@ -143,6 +169,13 @@ public class JpaRole extends AbstractRole implements Serializable {
     }
 
     @Override
+    public void setChildRoleIds( List<String> childRoleIds )
+    {
+        this.childRoleIds.clear();
+        this.childRoleIds.addAll( childRoleIds );
+    }
+
+    @Override
     public void setDescription(String description) {
         this.description=description;
 
@@ -245,12 +278,15 @@ public class JpaRole extends AbstractRole implements Serializable {
 
         JpaRole jpaRole = (JpaRole) o;
 
-        return name.equals( jpaRole.name );
+        if ( !name.equals( jpaRole.name ) ) return false;
+        return id.equals( jpaRole.id );
     }
 
     @Override
     public int hashCode( )
     {
-        return name.hashCode( );
+        int result = name.hashCode( );
+        result = 31 * result + id.hashCode( );
+        return result;
     }
 }
diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/Constants.java b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/model/RoleId.java
similarity index 50%
copy from redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/Constants.java
copy to redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/model/RoleId.java
index 3faaf8c..f63d77f 100644
--- a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/Constants.java
+++ b/redback-rbac/redback-rbac-providers/redback-rbac-jpa/src/main/java/org/apache/archiva/redback/rbac/jpa/model/RoleId.java
@@ -1,6 +1,4 @@
-package org.apache.archiva.redback.rest.api;
-
-/*
+package org.apache.archiva.redback.rbac.jpa.model;/*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,12 +16,44 @@ package org.apache.archiva.redback.rest.api;
  * under the License.
  */
 
+import java.io.Serializable;
+
 /**
  * @author Martin Stockhammer <ma...@apache.org>
  */
-public interface Constants
+public class RoleId implements Serializable
 {
-    String DEFAULT_PAGE_LIMIT = "1000";
+    private static final long serialVersionUID = -3358026083136193536L;
+    private String id;
+    private String name;
+
+    public RoleId( )
+    {
+    }
+
+    public RoleId( String id, String name )
+    {
+        this.id = id;
+        this.name = name;
+    }
+
+    @Override
+    public boolean equals( Object o )
+    {
+        if ( this == o ) return true;
+        if ( o == null || getClass( ) != o.getClass( ) ) return false;
+
+        RoleId roleId = (RoleId) o;
 
+        if ( !id.equals( roleId.id ) ) return false;
+        return name.equals( roleId.name );
+    }
 
+    @Override
+    public int hashCode( )
+    {
+        int result = id.hashCode( );
+        result = 31 * result + name.hashCode( );
+        return result;
+    }
 }
diff --git a/redback-rbac/redback-rbac-providers/redback-rbac-ldap/src/main/java/org/apache/archiva/redback/rbac/ldap/LdapRbacManager.java b/redback-rbac/redback-rbac-providers/redback-rbac-ldap/src/main/java/org/apache/archiva/redback/rbac/ldap/LdapRbacManager.java
index df05dcf..c5d9e8a 100644
--- a/redback-rbac/redback-rbac-providers/redback-rbac-ldap/src/main/java/org/apache/archiva/redback/rbac/ldap/LdapRbacManager.java
+++ b/redback-rbac/redback-rbac-providers/redback-rbac-ldap/src/main/java/org/apache/archiva/redback/rbac/ldap/LdapRbacManager.java
@@ -60,10 +60,8 @@ import javax.naming.directory.DirContext;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -474,17 +472,29 @@ public class LdapRbacManager
     }
 
     @Override
-    public Map<String, ? extends Role> getChildRoles( Role role )
+    public Map<String, ? extends Role> getChildRoleNames( Role role )
         throws RbacManagerException
     {
-        return this.rbacImpl.getChildRoles( role );
+        return this.rbacImpl.getChildRoleNames( role );
     }
 
     @Override
-    public Map<String, ? extends Role> getParentRoles( Role role )
+    public Map<String, ? extends Role> getChildRoleIds( Role role ) throws RbacManagerException
+    {
+        return this.rbacImpl.getChildRoleIds( role );
+    }
+
+    @Override
+    public Map<String, ? extends Role> getParentRoleNames( Role role )
         throws RbacManagerException
     {
-        return this.rbacImpl.getParentRoles( role );
+        return this.rbacImpl.getParentRoleNames( role );
+    }
+
+    @Override
+    public Map<String, ? extends Role> getParentRoleIds( Role role ) throws RbacManagerException
+    {
+        return this.rbacImpl.getParentRoleIds( role );
     }
 
     //
@@ -1241,9 +1251,10 @@ public class LdapRbacManager
         private boolean isTemplateInstance=false;
         private String resource="";
 
-        private List<Permission> permissions = new ArrayList<Permission>();
+        private List<Permission> permissions = new ArrayList<>();
 
-        private List<String> childRoleNames = new ArrayList<String>();
+        private List<String> childRoleNames = new ArrayList<>();
+        private List<String> childRoleIds = new ArrayList<>( );
 
         private RoleImpl( String name )
         {
@@ -1281,6 +1292,18 @@ public class LdapRbacManager
         }
 
         @Override
+        public void addChildRoleId( String id )
+        {
+            this.childRoleIds.add( id );
+        }
+
+        @Override
+        public List<String> getChildRoleIds( )
+        {
+            return this.childRoleIds;
+        }
+
+        @Override
         public String getDescription()
         {
             return this.description;
@@ -1323,6 +1346,12 @@ public class LdapRbacManager
         }
 
         @Override
+        public void setChildRoleIds( List<String> ids )
+        {
+
+        }
+
+        @Override
         public void setDescription( String description )
         {
             this.description = description;
diff --git a/redback-rbac/redback-rbac-providers/redback-rbac-memory/src/main/java/org/apache/archiva/redback/rbac/memory/MemoryRole.java b/redback-rbac/redback-rbac-providers/redback-rbac-memory/src/main/java/org/apache/archiva/redback/rbac/memory/MemoryRole.java
index c243af8..1eed3a0 100644
--- a/redback-rbac/redback-rbac-providers/redback-rbac-memory/src/main/java/org/apache/archiva/redback/rbac/memory/MemoryRole.java
+++ b/redback-rbac/redback-rbac-providers/redback-rbac-memory/src/main/java/org/apache/archiva/redback/rbac/memory/MemoryRole.java
@@ -61,6 +61,8 @@ public class MemoryRole
      */
     private List<String> childRoleNames = new ArrayList<>( 0 );
 
+    private List<String> childRoleIds = new ArrayList<>( 0 );
+
     /**
      * Field permissions
      */
@@ -110,6 +112,20 @@ public class MemoryRole
         return this.childRoleNames;
     }
 
+
+
+    @Override
+    public void addChildRoleId( String id )
+    {
+        this.childRoleIds.add( id );
+    }
+
+    @Override
+    public List<String> getChildRoleIds( )
+    {
+        return this.childRoleIds;
+    }
+
     @Override
     public String getDescription()
     {
@@ -199,6 +215,12 @@ public class MemoryRole
     }
 
     @Override
+    public void setChildRoleIds( List<String> ids )
+    {
+
+    }
+
+    @Override
     public boolean isPermanent()
     {
         return permanent;
diff --git a/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/DefaultRoleManager.java b/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/DefaultRoleManager.java
index 9d68db6..9fbf5c9 100644
--- a/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/DefaultRoleManager.java
+++ b/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/DefaultRoleManager.java
@@ -21,6 +21,7 @@ package org.apache.archiva.redback.role;
 
 import org.apache.archiva.redback.rbac.RBACManager;
 import org.apache.archiva.redback.rbac.RbacManagerException;
+import org.apache.archiva.redback.rbac.RbacObjectNotFoundException;
 import org.apache.archiva.redback.rbac.Role;
 import org.apache.archiva.redback.rbac.UserAssignment;
 import org.apache.archiva.redback.role.model.ModelApplication;
@@ -184,10 +185,10 @@ public class DefaultRoleManager
      * resolving the ${resource} expression
      */
     @Override
-    public void createTemplatedRole( String templateId, String resource )
+    public String createTemplatedRole( String templateId, String resource )
         throws RoleManagerException
     {
-        templateProcessor.create( blessedModel, templateId, resource );
+        return templateProcessor.create( blessedModel, templateId, resource );
     }
 
     /**
@@ -198,13 +199,10 @@ public class DefaultRoleManager
     public void removeTemplatedRole( String templateId, String resource )
         throws RoleManagerException
     {
-        ModelTemplate template = RoleModelUtils.getModelTemplate( blessedModel, templateId );
-
-        String roleName = template.getNamePrefix() + template.getDelimiter() + resource;
-
+        String roleId = templateProcessor.getRoleId( templateId, resource );
         try
         {
-            Role role = rbacManager.getRole( roleName );
+            Role role = rbacManager.getRoleById( roleId );
 
             for ( UserAssignment assignment : rbacManager.getUserAssignmentsForRoles(
                 Arrays.asList( role.getName() ) ) )
@@ -213,10 +211,12 @@ public class DefaultRoleManager
                 rbacManager.saveUserAssignment( assignment );
             }
 
+        } catch ( RbacObjectNotFoundException e) {
+            throw new RoleNotFoundException( e.getMessage( ), e );
         }
         catch ( RbacManagerException e )
         {
-            throw new RoleManagerException( "unable to remove role", e );
+            throw new RoleManagerException( "Unable to remove role", e );
         }
 
         templateProcessor.remove( blessedModel, templateId, resource );
@@ -229,11 +229,11 @@ public class DefaultRoleManager
      * because of the use of the name as an identifier
      */
     @Override
-    public void moveTemplatedRole( String templateId, String oldResource, String newResource )
+    public String moveTemplatedRole( String templateId, String oldResource, String newResource )
         throws RoleManagerException
     {
         // make the new role
-        templateProcessor.create( blessedModel, templateId, newResource );
+        String roleId = templateProcessor.create( blessedModel, templateId, newResource );
 
         ModelTemplate template = RoleModelUtils.getModelTemplate( blessedModel, templateId );
 
@@ -259,6 +259,7 @@ public class DefaultRoleManager
         }
 
         templateProcessor.remove( blessedModel, templateId, oldResource );
+        return roleId;
     }
 
     @Override
@@ -269,7 +270,7 @@ public class DefaultRoleManager
 
         if ( modelRole == null )
         {
-            throw new RoleManagerException( "Unable to assign role: " + roleId + " does not exist." );
+            throw new RoleNotFoundException( "Unable to assign role: " + roleId + " does not exist." );
         }
 
         try
diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/Constants.java b/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/RoleExistsException.java
similarity index 73%
copy from redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/Constants.java
copy to redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/RoleExistsException.java
index 3faaf8c..0403cb3 100644
--- a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/Constants.java
+++ b/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/RoleExistsException.java
@@ -1,6 +1,4 @@
-package org.apache.archiva.redback.rest.api;
-
-/*
+package org.apache.archiva.redback.role;/*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -21,9 +19,15 @@ package org.apache.archiva.redback.rest.api;
 /**
  * @author Martin Stockhammer <ma...@apache.org>
  */
-public interface Constants
+public class RoleExistsException extends RoleManagerException
 {
-    String DEFAULT_PAGE_LIMIT = "1000";
-
+    public RoleExistsException( String string )
+    {
+        super( string );
+    }
 
+    public RoleExistsException( String string, Throwable throwable )
+    {
+        super( string, throwable );
+    }
 }
diff --git a/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/RoleManager.java b/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/RoleManager.java
index 7c727e8..7448a8f 100644
--- a/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/RoleManager.java
+++ b/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/RoleManager.java
@@ -53,7 +53,7 @@ public interface RoleManager
      * @param resource
      * @throws RoleManagerException
      */
-    void createTemplatedRole( String templateId, String resource )
+    String createTemplatedRole( String templateId, String resource )
         throws RoleManagerException;
 
     /**
@@ -78,7 +78,7 @@ public interface RoleManager
      * @param newResource the new resource name
      * @throws RoleManagerException
      */
-    void moveTemplatedRole( String templateId, String oldResource, String newResource )
+    String moveTemplatedRole( String templateId, String oldResource, String newResource )
         throws RoleManagerException;
 
 
diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/Constants.java b/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/RoleNotFoundException.java
similarity index 73%
copy from redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/Constants.java
copy to redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/RoleNotFoundException.java
index 3faaf8c..e29c14a 100644
--- a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/Constants.java
+++ b/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/RoleNotFoundException.java
@@ -1,6 +1,4 @@
-package org.apache.archiva.redback.rest.api;
-
-/*
+package org.apache.archiva.redback.role;/*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -21,9 +19,15 @@ package org.apache.archiva.redback.rest.api;
 /**
  * @author Martin Stockhammer <ma...@apache.org>
  */
-public interface Constants
+public class RoleNotFoundException extends RoleManagerException
 {
-    String DEFAULT_PAGE_LIMIT = "1000";
-
+    public RoleNotFoundException( String string )
+    {
+        super( string );
+    }
 
+    public RoleNotFoundException( String string, Throwable throwable )
+    {
+        super( string, throwable );
+    }
 }
diff --git a/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/processor/DefaultRoleModelProcessor.java b/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/processor/DefaultRoleModelProcessor.java
index e0b4b65..b39c0c7 100644
--- a/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/processor/DefaultRoleModelProcessor.java
+++ b/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/processor/DefaultRoleModelProcessor.java
@@ -216,6 +216,7 @@ public class DefaultRoleModelProcessor
                         {
                             ModelRole childRoleProfile = RoleModelUtils.getModelRole( model, childRoleId );
                             role.addChildRoleName( childRoleProfile.getName() );
+                            role.addChildRoleId( childRoleProfile.getId() );
                         }
                     }
 
@@ -229,7 +230,7 @@ public class DefaultRoleModelProcessor
                         {
                             ModelRole parentModelRole = RoleModelUtils.getModelRole( model, parentRoleId );
                             Role parentRole = rbacManager.getRole( parentModelRole.getName() );
-                            parentRole.addChildRoleName( role.getName() );
+                            parentRole.addChildRole( role );
                             rbacManager.saveRole( parentRole );
                             allRoleNames.add( parentRole.getName() );
                         }
diff --git a/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/template/DefaultRoleTemplateProcessor.java b/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/template/DefaultRoleTemplateProcessor.java
index 3c43333..f7533c1 100644
--- a/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/template/DefaultRoleTemplateProcessor.java
+++ b/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/template/DefaultRoleTemplateProcessor.java
@@ -25,7 +25,9 @@ import org.apache.archiva.redback.rbac.RbacManagerException;
 import org.apache.archiva.redback.rbac.Resource;
 import org.apache.archiva.redback.rbac.Role;
 import org.apache.archiva.redback.rbac.RBACManager;
+import org.apache.archiva.redback.role.RoleExistsException;
 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.ModelOperation;
 import org.apache.archiva.redback.role.model.ModelPermission;
@@ -60,8 +62,9 @@ public class DefaultRoleTemplateProcessor
     @Named(value = "rbacManager#default")
     private RBACManager rbacManager;
 
+    @Override
     @SuppressWarnings("unchecked")
-    public void create( RedbackRoleModel model, String templateId, String resource )
+    public String create( final RedbackRoleModel model, final String templateId, final String resource )
         throws RoleManagerException
     {
         for ( ModelApplication application : model.getApplications() )
@@ -74,16 +77,16 @@ public class DefaultRoleTemplateProcessor
                     processResource( template, resource );
 
                     // templates are roles that have yet to be paired with a resource for creation
-                    processTemplate( model, template, resource );
+                    return processTemplate( model, template, resource );
 
-                    return;
                 }
             }
         }
 
-        throw new RoleManagerException( "unknown template '" + templateId + "'" );
+        throw new RoleNotFoundException( "unknown template '" + templateId + "'" );
     }
 
+    @Override
     @SuppressWarnings("unchecked")
     public void remove( RedbackRoleModel model, String templateId, String resource )
         throws RoleManagerException
@@ -106,11 +109,11 @@ public class DefaultRoleTemplateProcessor
     private void removeTemplatedRole( RedbackRoleModel model, ModelTemplate template, String resource )
         throws RoleManagerException
     {
-        String roleName = template.getNamePrefix() + template.getDelimiter() + resource;
+        String roleId = getRoleId( template.getId( ), resource );
 
         try
         {
-            Role role = rbacManager.getRole( roleName );
+            Role role = rbacManager.getRoleById( roleId );
 
             if ( !role.isPermanent() )
             {
@@ -142,12 +145,12 @@ public class DefaultRoleTemplateProcessor
             }
             else
             {
-                throw new RoleManagerException( "unable to remove role, it is flagged permanent" );
+                throw new RoleManagerException( "Unable to remove role, it is flagged permanent" );
             }
         }
         catch ( RbacManagerException e )
         {
-            throw new RoleManagerException( "unable to remove templated role: " + roleName, e );
+            throw new RoleManagerException( "Unable to remove templated role: " + roleId, e );
         }
         //catch ( RoleTemplateProcessorException e )
         //{
@@ -173,12 +176,17 @@ public class DefaultRoleTemplateProcessor
         }
     }
 
+    @Override
+    public String getRoleId( String templateId, String resource) {
+        return templateId + "." + resource;
+    }
+
     @SuppressWarnings("unchecked")
-    private void processTemplate( RedbackRoleModel model, ModelTemplate template, String resource )
+    private String processTemplate( RedbackRoleModel model, ModelTemplate template, String resource )
         throws RoleManagerException
     {
         final String templateName = template.getNamePrefix() + template.getDelimiter() + resource;
-        final String roleId = template.getId( ) + "." + resource;
+        final String roleId = getRoleId( template.getId( ), resource );
 
         List<Permission> permissions = processPermissions( model, template, resource );
 
@@ -190,7 +198,7 @@ public class DefaultRoleTemplateProcessor
         }
         catch ( RbacManagerException e )
         {
-            throw new RoleManagerException( e.getMessage(), e );
+            throw new RoleExistsException( e.getMessage(), e );
         }
 
         if ( !roleExists )
@@ -221,6 +229,7 @@ public class DefaultRoleTemplateProcessor
                     {
                         ModelRole childRoleProfile = RoleModelUtils.getModelRole( model, childRoleId );
                         role.addChildRoleName( childRoleProfile.getName() );
+                        role.addChildRoleId( childRoleProfile.getId() );
                     }
                 }
 
@@ -246,12 +255,14 @@ public class DefaultRoleTemplateProcessor
                         if ( rbacManager.roleExists( childRoleName ) )
                         {
                             role.addChildRoleName( childRoleName );
+                            role.addChildRoleId( getRoleId( childTemplateId, resource ) );
                         }
                         else
                         {
                             processTemplate( model, childModelTemplate, resource );
 
                             role.addChildRoleName( childRoleName );
+                            role.addChildRoleId( getRoleId( childTemplateId, resource ) );
                         }
                     }
                 }
@@ -270,7 +281,7 @@ public class DefaultRoleTemplateProcessor
                     {
                         ModelRole parentModelRole = RoleModelUtils.getModelRole( model, parentRoleId );
                         Role parentRole = rbacManager.getRole( parentModelRole.getName() );
-                        parentRole.addChildRoleName( role.getName() );
+                        parentRole.addChildRole( role );
                         rbacManager.saveRole( parentRole );
                     }
                 }
@@ -298,7 +309,7 @@ public class DefaultRoleTemplateProcessor
                         {
                             Role parentRole = rbacManager.getRole( parentRoleName );
 
-                            parentRole.addChildRoleName( role.getName() );
+                            parentRole.addChildRole( role );
                             rbacManager.saveRole( parentRole );
                         }
                         else
@@ -307,7 +318,7 @@ public class DefaultRoleTemplateProcessor
 
                             Role parentRole = rbacManager.getRole( parentRoleName );
 
-                            parentRole.addChildRoleName( role.getName() );
+                            parentRole.addChildRole( role );
                             rbacManager.saveRole( parentRole );
                         }
                     }
@@ -358,6 +369,7 @@ public class DefaultRoleTemplateProcessor
                 throw new RoleManagerException( "error updating role '" + templateName + "'", e );
             }
         }
+        return roleId;
     }
 
     @SuppressWarnings("unchecked")
diff --git a/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/template/RoleTemplateProcessor.java b/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/template/RoleTemplateProcessor.java
index 7f8e07d..e237a04 100644
--- a/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/template/RoleTemplateProcessor.java
+++ b/redback-rbac/redback-rbac-role-manager/src/main/java/org/apache/archiva/redback/role/template/RoleTemplateProcessor.java
@@ -30,9 +30,33 @@ import org.apache.archiva.redback.role.model.RedbackRoleModel;
 public interface RoleTemplateProcessor
 {
 
-    void create( RedbackRoleModel model, String templateId, String resource )
+    /**
+     * Creates a role instance from a template for the given resource and returns the id of the new role.
+     * @param model the model
+     * @param templateId the template identifier
+     * @param resource the resource to which the role is applied
+     * @return the id of the role
+     * @throws RoleManagerException if the access to the backend datastore failed
+     */
+    String create( RedbackRoleModel model, String templateId, String resource )
         throws RoleManagerException;
 
+    /**
+     * Removes the role instance that belongs to the template from the datastore
+     * @param model the model
+     * @param templateId the template identifier
+     * @param resource the resource to which the role is applied
+     * @throws RoleManagerException if the access to the backend datastore failed
+     */
     void remove( RedbackRoleModel model, String templateId, String resource )
         throws RoleManagerException;
+
+
+    /**
+     * Returns the role id that identifies the role that is a instance of the given template for the given resource.
+     * @param templateId the template identifier
+     * @param resource the resource
+     * @return the role identifier
+     */
+    String getRoleId( String templateId, String resource );
 }
diff --git a/redback-rbac/redback-rbac-tests/src/main/java/org/apache/archiva/redback/tests/AbstractRbacManagerPerformanceTestCase.java b/redback-rbac/redback-rbac-tests/src/main/java/org/apache/archiva/redback/tests/AbstractRbacManagerPerformanceTestCase.java
index 11eacdd..7d1512f 100644
--- a/redback-rbac/redback-rbac-tests/src/main/java/org/apache/archiva/redback/tests/AbstractRbacManagerPerformanceTestCase.java
+++ b/redback-rbac/redback-rbac-tests/src/main/java/org/apache/archiva/redback/tests/AbstractRbacManagerPerformanceTestCase.java
@@ -169,6 +169,7 @@ public class AbstractRbacManagerPerformanceTestCase
         Role devRole = getDeveloperRole();
         Role devPlusRole = getSuperDeveloperRole();
         devPlusRole.setChildRoleNames( Collections.singletonList( devRole.getName() ) );
+        devPlusRole.setChildRoleIds( Collections.singletonList( devRole.getId() ) );
         devRole = manager.saveRole( devRole );
         devPlusRole = manager.saveRole( devPlusRole );
 
@@ -197,6 +198,7 @@ public class AbstractRbacManagerPerformanceTestCase
         username = "janet";
 
         devPlusRole.setChildRoleNames( Collections.singletonList( devRole.getName() ) );
+        devPlusRole.setChildRoleIds( Collections.singletonList( devRole.getId() ) );
         devRole = manager.saveRole( devRole );
         manager.saveRole( devPlusRole );
 
diff --git a/redback-rbac/redback-rbac-tests/src/main/java/org/apache/archiva/redback/tests/AbstractRbacManagerTestCase.java b/redback-rbac/redback-rbac-tests/src/main/java/org/apache/archiva/redback/tests/AbstractRbacManagerTestCase.java
index 5ec4cc5..bca655e 100644
--- a/redback-rbac/redback-rbac-tests/src/main/java/org/apache/archiva/redback/tests/AbstractRbacManagerTestCase.java
+++ b/redback-rbac/redback-rbac-tests/src/main/java/org/apache/archiva/redback/tests/AbstractRbacManagerTestCase.java
@@ -91,7 +91,7 @@ public abstract class AbstractRbacManagerTestCase
     private Role getAdminRole()
         throws RbacManagerException
     {
-        Role role = rbacManager.createRole( "ADMIN" );
+        Role role = rbacManager.createRole( "admin", "ADMIN" );
         role.setAssignable( false );
 
         Permission perm = rbacManager.createPermission( "EDIT_ANY_USER", "EDIT", "User:*" );
@@ -375,6 +375,7 @@ public abstract class AbstractRbacManagerTestCase
         manager.saveRole( projectRole );
 
         develRole.addChildRoleName( projectRoleName );
+        develRole.addChildRoleId( projectRole.getId() );
 
         manager.saveRole( develRole );
 
@@ -428,7 +429,7 @@ public abstract class AbstractRbacManagerTestCase
 
         Role adminRole = getAdminRole();
 
-        adminRole.addChildRoleName( developerRole.getName() );
+        adminRole.addChildRole( developerRole );
 
         adminRole = manager.saveRole( adminRole );
 
@@ -631,6 +632,7 @@ public abstract class AbstractRbacManagerTestCase
         Role devRole = getDeveloperRole();
         Role devPlusRole = getSuperDeveloperRole();
         devPlusRole.setChildRoleNames( Collections.singletonList( devRole.getName() ) );
+        devPlusRole.setChildRoleIds( Collections.singletonList( devRole.getId() ) );
         manager.saveRole( devRole );
         manager.saveRole( devPlusRole );
 
diff --git a/redback-rbac/redback-rbac-tests/src/main/java/org/apache/archiva/redback/tests/utils/RBACDefaults.java b/redback-rbac/redback-rbac-tests/src/main/java/org/apache/archiva/redback/tests/utils/RBACDefaults.java
index 16c85af..a451485 100644
--- a/redback-rbac/redback-rbac-tests/src/main/java/org/apache/archiva/redback/tests/utils/RBACDefaults.java
+++ b/redback-rbac/redback-rbac-tests/src/main/java/org/apache/archiva/redback/tests/utils/RBACDefaults.java
@@ -159,6 +159,7 @@ public class RBACDefaults
         {
             Role admin = manager.createRole( "System Administrator" );
             admin.addChildRoleName( "User Administrator" );
+            admin.addChildRoleId( "user-administrator" );
             admin.addPermission( manager.getPermission( "Edit Configuration" ) );
             admin.addPermission( manager.getPermission( "Run Indexer" ) );
             admin.addPermission( manager.getPermission( "Add Repository" ) );
@@ -171,6 +172,7 @@ public class RBACDefaults
         {
             Role developer = manager.createRole( "Trusted Developer" );
             developer.addChildRoleName( "System Administrator" );
+            developer.addChildRoleId( "system-administrator" );
             developer.addPermission( manager.getPermission( "Run Indexer" ) );
             developer.setAssignable( true );
             manager.saveRole( developer );
@@ -180,6 +182,7 @@ public class RBACDefaults
         {
             Role developer = manager.createRole( "Developer" );
             developer.addChildRoleName( "Trusted Developer" );
+            developer.addChildRoleId( "trusted-developer" );
             developer.addPermission( manager.getPermission( "Run Indexer" ) );
             developer.setAssignable( true );
             manager.saveRole( developer );


[archiva-redback-core] 02/05: Additional tests for role service

Posted by ma...@apache.org.
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 ec82e1aae7376cd5fbab7cfcd49f5ee5b9744f0a
Author: Martin Stockhammer <ma...@apache.org>
AuthorDate: Tue Nov 24 22:43:34 2020 +0100

    Additional tests for role service
---
 .../rest/services/v2/NativeRoleServiceTest.java    | 109 ++++++++++++++++-----
 1 file changed, 85 insertions(+), 24 deletions(-)

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 f3105d3..9d4b43f 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
@@ -75,7 +75,7 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices
         super.shutdownNative( );
     }
 
-    private String getUserServicePath()
+    private String getUserServicePath( )
     {
         return new StringBuilder( )
             .append( getContextRoot( ) )
@@ -84,7 +84,6 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices
     }
 
 
-
     @Test
     void createTemplatedRole( )
     {
@@ -159,14 +158,11 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices
                 .when( )
                 .delete( "template/archiva-repository-manager/repository05" )
                 .then( ).statusCode( 404 );
-        } finally
+        }
+        finally
         {
             given( ).spec( getRequestSpec( token ) ).contentType( JSON )
                 .when( )
-                .delete( "template/archiva-repository-observer/repository01" )
-                .then( ).statusCode( 200 );
-            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
-                .when( )
                 .delete( "template/archiva-repository-observer/repository05" )
                 .then( ).statusCode( 200 );
 
@@ -174,13 +170,15 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices
     }
 
     @Test
-    void checkTemplatedRole() {
+    void checkTemplatedRole( )
+    {
         String token = getAdminToken( );
         given( ).spec( getRequestSpec( token ) ).contentType( JSON )
             .when( )
             .put( "template/archiva-repository-observer/repository01" )
             .then( ).statusCode( 201 );
-        try {
+        try
+        {
             given( ).spec( getRequestSpec( token ) ).contentType( JSON )
                 .when( )
                 .head( "template/archiva-repository-observer/repository01" )
@@ -190,7 +188,8 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices
                 .when( )
                 .head( "archiva-repository-observer.repository01" )
                 .then( ).statusCode( 200 );
-        } finally
+        }
+        finally
         {
             given( ).spec( getRequestSpec( token ) ).contentType( JSON )
                 .when( )
@@ -364,18 +363,20 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices
     }
 
     @Test
-    void checkRole() {
+    void checkRole( )
+    {
         String token = getAdminToken( );
         Response response = given( ).spec( getRequestSpec( token ) ).contentType( JSON )
             .when( ).head( "archiva-system-administrator" ).then( ).statusCode( 200 ).extract( ).response( );
-        assertEquals(0,response.getBody( ).asByteArray().length);
+        assertEquals( 0, response.getBody( ).asByteArray( ).length );
         given( ).spec( getRequestSpec( token ) ).contentType( JSON )
             .when( ).head( "abcdefg" ).then( ).statusCode( 404 );
 
     }
 
     @Test
-    void moveRole() {
+    void moveRole( )
+    {
         String token = getAdminToken( );
         try
         {
@@ -400,7 +401,8 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices
             given( ).spec( getRequestSpec( token ) ).contentType( JSON )
                 .when( ).head( "template/archiva-repository-observer/repository02" ).then( ).statusCode( 200 );
 
-        } finally
+        }
+        finally
         {
             given( ).spec( getRequestSpec( token ) ).contentType( JSON )
                 .when( )
@@ -420,7 +422,8 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices
     }
 
     @Test
-    void moveRoleToExistingDestination() {
+    void moveRoleToExistingDestination( )
+    {
         String token = getAdminToken( );
         try
         {
@@ -440,7 +443,8 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices
             assertTrue( response.getHeader( "Location" ).endsWith( "/roles/template/archiva-repository-manager/repository02" ) );
             given( ).spec( getRequestSpec( token ) ).contentType( JSON )
                 .when( ).head( "template/archiva-repository-manager/repository01" ).then( ).statusCode( 200 );
-        } finally
+        }
+        finally
         {
             given( ).spec( getRequestSpec( token ) ).contentType( JSON )
                 .when( )
@@ -461,23 +465,24 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices
 
 
     @Test
-    void assignRole() {
+    void assignRole( )
+    {
         String token = getAdminToken( );
         Map<String, Object> jsonAsMap = new HashMap<>( );
-        jsonAsMap.put( "user_id", "aragorn");
+        jsonAsMap.put( "user_id", "aragorn" );
         jsonAsMap.put( "email", "aragorn@lordoftherings.org" );
         jsonAsMap.put( "full_name", "Aragorn King of Gondor " );
         jsonAsMap.put( "password", "pAssw0rD" );
 
         try
         {
-            given( ).spec( getRequestSpec( token, getUserServicePath() ) ).contentType( JSON )
+            given( ).spec( getRequestSpec( token, getUserServicePath( ) ) ).contentType( JSON )
                 .body( jsonAsMap )
                 .when( )
                 .post( )
                 .then( ).statusCode( 201 );
 
-            Response response = given( ).spec( getRequestSpec( token, getUserServicePath() ) ).contentType( JSON )
+            Response response = given( ).spec( getRequestSpec( token, getUserServicePath( ) ) ).contentType( JSON )
                 .when( )
                 .get( "aragorn/roles" )
                 .then( ).statusCode( 200 ).extract( ).response( );
@@ -486,20 +491,76 @@ public class NativeRoleServiceTest extends AbstractNativeRestServices
             given( ).spec( getRequestSpec( token ) ).contentType( JSON )
                 .when( )
                 .put( "system-administrator/assign/aragorn" )
-                .prettyPeek()
+                .prettyPeek( )
                 .then( ).statusCode( 200 );
-            response = given( ).spec( getRequestSpec( token, getUserServicePath() ) ).contentType( JSON )
+            response = given( ).spec( getRequestSpec( token, getUserServicePath( ) ) ).contentType( JSON )
                 .when( )
                 .get( "aragorn/roles" )
                 .then( ).statusCode( 200 ).extract( ).response( );
             roles = response.getBody( ).jsonPath( ).getList( "", RoleInfo.class );
             assertTrue( roles.stream( ).filter( role -> "system-administrator".equals( role.getId( ) ) ).findAny( ).isPresent( ) );
-        } finally
+        }
+        finally
+        {
+            given( ).spec( getRequestSpec( token, getUserServicePath( ) ) ).contentType( JSON )
+                .when( )
+                .delete( "aragorn" ).getBody( );
+        }
+    }
+
+    @Test
+    void assignNonexistentRole( )
+    {
+        String token = getAdminToken( );
+        Map<String, Object> jsonAsMap = new HashMap<>( );
+        jsonAsMap.put( "user_id", "aragorn" );
+        jsonAsMap.put( "email", "aragorn@lordoftherings.org" );
+        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 );
+
+            Response response = given( ).spec( getRequestSpec( token, getUserServicePath( ) ) ).contentType( JSON )
+                .when( )
+                .get( "aragorn/roles" )
+                .then( ).statusCode( 200 ).extract( ).response( );
+            List<RoleInfo> roles = response.getBody( ).jsonPath( ).getList( "", RoleInfo.class );
+            assertFalse( roles.stream( ).filter( role -> "abcdefg".equals( role.getId( ) ) ).findAny( ).isPresent( ) );
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .when( )
+                .put( "abcdefg/assign/aragorn" )
+                .prettyPeek( )
+                .then( ).statusCode( 404 );
+            response = given( ).spec( getRequestSpec( token, getUserServicePath( ) ) ).contentType( JSON )
+                .when( )
+                .get( "aragorn/roles" )
+                .then( ).statusCode( 200 ).extract( ).response( );
+            roles = response.getBody( ).jsonPath( ).getList( "", RoleInfo.class );
+            assertFalse( roles.stream( ).filter( role -> "abcdefg".equals( role.getId( ) ) ).findAny( ).isPresent( ) );
+        }
+        finally
         {
-            given( ).spec( getRequestSpec( token, getUserServicePath() ) ).contentType( JSON )
+            given( ).spec( getRequestSpec( token, getUserServicePath( ) ) ).contentType( JSON )
                 .when( )
                 .delete( "aragorn" ).getBody( );
         }
     }
 
+    @Test
+    void assignRoleToNonexistentUser( )
+    {
+        String token = getAdminToken( );
+        given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+            .when( )
+            .put( "system-administrator/assign/aragorn" )
+            .prettyPeek( )
+            .then( ).statusCode( 404 );
+    }
+
 }