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/09/17 07:30:22 UTC

[archiva-redback-core] 02/02: Improving user service v2

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 2a8928e2dbd0b2c3213a43ea99aaeb9422808fe9
Author: Martin Stockhammer <ma...@apache.org>
AuthorDate: Thu Sep 17 09:30:04 2020 +0200

    Improving user service v2
---
 .../rest/api/model/v2/AvailabilityStatus.java      |   3 +
 .../redback/rest/api/services/v2/UserService.java  | 189 +++++++++++++---
 .../redback/rest/services/DefaultUserService.java  |   2 +-
 .../rest/services/v2/DefaultUserService.java       |   4 +-
 .../rest/services/v2/NativeUserServiceTest.java    | 242 ++++++++++++++++++---
 5 files changed, 369 insertions(+), 71 deletions(-)

diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/AvailabilityStatus.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/AvailabilityStatus.java
index 5d544bc..b219f42 100644
--- a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/AvailabilityStatus.java
+++ b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/AvailabilityStatus.java
@@ -24,6 +24,9 @@ import java.time.OffsetDateTime;
 import java.time.ZoneId;
 
 /**
+ * Returns a status of availability (does exist, or does not exist) of a given object.
+ * If the object exists, the creation date of the object may be returned.
+ *
  * @author Martin Stockhammer <ma...@apache.org>
  */
 @XmlRootElement(name="availabilityStatus")
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 bb103e3..9d9240d 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
@@ -64,7 +64,21 @@ public interface UserService
     @GET
     @Produces( { MediaType.APPLICATION_JSON } )
     @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_USER_EDIT_OPERATION,
-    resource = "{userId}" )
+        resource = "{userId}" )
+    @io.swagger.v3.oas.annotations.Operation( summary = "Returns information about a specific user",
+        security = {
+            @SecurityRequirement(
+                name = RedbackRoleConstants.USER_MANAGEMENT_USER_EDIT_OPERATION
+            )
+        },
+        responses = {
+            @ApiResponse( responseCode = "200",
+                description = "If user was found in the database"
+            ),
+            @ApiResponse( responseCode = "404", description = "User does not exist" ),
+            @ApiResponse( responseCode = "403", description = "Authenticated user is not permitted to gather the information" )
+        }
+    )
     User getUser( @PathParam( "userId" ) String userId )
         throws RedbackServiceException;
 
@@ -73,6 +87,19 @@ public interface UserService
     @GET
     @Produces( { MediaType.APPLICATION_JSON } )
     @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_USER_LIST_OPERATION )
+    @io.swagger.v3.oas.annotations.Operation( summary = "Returns all users defined. The result is paged.",
+        security = {
+            @SecurityRequirement(
+                name = RedbackRoleConstants.USER_MANAGEMENT_USER_LIST_OPERATION
+            )
+        },
+        responses = {
+            @ApiResponse( responseCode = "200",
+                description = "If the list could be returned"
+            ),
+            @ApiResponse( responseCode = "403", description = "Authenticated user is not permitted to gather the information" )
+        }
+    )
     PagedResult<User> getUsers( @QueryParam( "offset" ) @DefaultValue( "0" ) Integer offset,
                                       @QueryParam( "limit" ) @DefaultValue( value = DEFAULT_PAGE_LIMIT ) Integer limit)
         throws RedbackServiceException;
@@ -83,6 +110,11 @@ public interface UserService
     @Consumes( { MediaType.APPLICATION_JSON } )
     @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_USER_CREATE_OPERATION )
     @io.swagger.v3.oas.annotations.Operation( summary = "Creates a user",
+        security = {
+            @SecurityRequirement(
+                name = RedbackRoleConstants.USER_MANAGEMENT_USER_CREATE_OPERATION
+            )
+        },
         responses = {
             @ApiResponse( responseCode = "201",
                 description = "If user creation was successful",
@@ -101,6 +133,46 @@ public interface UserService
     User createUser( User user )
         throws RedbackServiceException;
 
+    @Path( "{userId}" )
+    @DELETE
+    @Produces( { MediaType.APPLICATION_JSON } )
+    @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_USER_DELETE_OPERATION )
+    @io.swagger.v3.oas.annotations.Operation( summary = "Deletes a given user",
+        security = {
+            @SecurityRequirement( name = RedbackRoleConstants.USER_MANAGEMENT_USER_DELETE_OPERATION )
+        },
+        responses = {
+            @ApiResponse( responseCode = "200",
+                description = "If user deletion was successful"
+            ),
+            @ApiResponse( responseCode = "404", description = "User does not exist" ),
+            @ApiResponse( responseCode = "403", description = "The authenticated user has not the permission for deletion." )
+        }
+    )
+    void deleteUser( @PathParam( "userId" ) String userId )
+        throws RedbackServiceException;
+
+    @Path( "{userId}" )
+    @PUT
+    @Produces( {MediaType.APPLICATION_JSON} )
+    @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_USER_EDIT_OPERATION )
+    @io.swagger.v3.oas.annotations.Operation( summary = "Updates an existing user",
+        security = {
+            @SecurityRequirement( name = RedbackRoleConstants.USER_MANAGEMENT_USER_EDIT_OPERATION )
+        },
+        responses = {
+            @ApiResponse( responseCode = "200",
+                description = "If update was successful"
+            ),
+            @ApiResponse( responseCode = "404", description = "User does not exist" ),
+            @ApiResponse( responseCode = "422", description = "Update data was not valid. E.g. password violations." ),
+            @ApiResponse( responseCode = "403", description = "The authenticated user has not the permission for update." )
+        }
+    )
+    User updateUser( @PathParam( "userId" ) String userId, User user )
+        throws RedbackServiceException;
+
+
 
     /**
      * will create admin user only if not exists !! if exists will return false
@@ -110,7 +182,7 @@ public interface UserService
     @Produces( { MediaType.APPLICATION_JSON } )
     @Consumes( { MediaType.APPLICATION_JSON } )
     @RedbackAuthorization( noRestriction = true )
-    @io.swagger.v3.oas.annotations.Operation( summary = "Creates a user",
+    @io.swagger.v3.oas.annotations.Operation( summary = "Creates the admin user, if it does not exist",
         responses = {
             @ApiResponse( responseCode = "201",
                 description = "If user creation was successful",
@@ -133,42 +205,17 @@ public interface UserService
     @GET
     @Produces( { MediaType.APPLICATION_JSON } )
     @RedbackAuthorization( noRestriction = true )
-    AvailabilityStatus getAdminStatus()
-        throws RedbackServiceException;
-
-
-    @Path( "{userId}" )
-    @DELETE
-    @Produces( { MediaType.APPLICATION_JSON } )
-    @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_USER_DELETE_OPERATION )
-    @io.swagger.v3.oas.annotations.Operation( summary = "Creates a user",
+    @io.swagger.v3.oas.annotations.Operation( summary = "Returns the availability status of the admin user. ",
         responses = {
             @ApiResponse( responseCode = "200",
-                description = "If user deletion was successful"
-            ),
-            @ApiResponse( responseCode = "404", description = "User does not exist" ),
-            @ApiResponse( responseCode = "403", description = "The authenticated user has not the permission for deletion." )
+                description = "If status can be retrieved"
+            )
         }
     )
-    void deleteUser( @PathParam( "userId" ) String userId )
+    AvailabilityStatus getAdminStatus()
         throws RedbackServiceException;
 
-    @Path( "{userId}" )
-    @PUT
-    @Produces( {MediaType.APPLICATION_JSON} )
-    @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_USER_EDIT_OPERATION )
-    @io.swagger.v3.oas.annotations.Operation( summary = "Updates an existing user",
-        responses = {
-            @ApiResponse( responseCode = "200",
-                description = "If update was successful"
-            ),
-            @ApiResponse( responseCode = "404", description = "User does not exist" ),
-            @ApiResponse( responseCode = "422", description = "Update data was not valid. E.g. password violations." ),
-            @ApiResponse( responseCode = "403", description = "The authenticated user has not the permission for update." )
-        }
-    )
-    User updateUser( @PathParam( "userId" ) String userId, User user )
-        throws RedbackServiceException;
+
 
     /**
      */
@@ -177,6 +224,9 @@ public interface UserService
     @Produces( { MediaType.APPLICATION_JSON } )
     @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_USER_EDIT_OPERATION )
     @io.swagger.v3.oas.annotations.Operation( summary = "Creates a user",
+        security = {
+            @SecurityRequirement( name = RedbackRoleConstants.USER_MANAGEMENT_USER_EDIT_OPERATION )
+        },
         responses = {
             @ApiResponse( responseCode = "200",
                 description = "If locking was successful"
@@ -195,6 +245,9 @@ public interface UserService
     @Produces( { MediaType.APPLICATION_JSON } )
     @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_USER_EDIT_OPERATION )
     @io.swagger.v3.oas.annotations.Operation( summary = "Unlocks a user",
+        security = {
+            @SecurityRequirement( name = RedbackRoleConstants.USER_MANAGEMENT_USER_EDIT_OPERATION )
+        },
         responses = {
             @ApiResponse( responseCode = "200",
                 description = "If unlocking was successful"
@@ -213,7 +266,10 @@ public interface UserService
     @POST
     @Produces( { MediaType.APPLICATION_JSON } )
     @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_USER_EDIT_OPERATION )
-    @io.swagger.v3.oas.annotations.Operation( summary = "Creates a user",
+    @io.swagger.v3.oas.annotations.Operation( summary = "Sets the requirePassword flag for a given user",
+        security = {
+            @SecurityRequirement( name = RedbackRoleConstants.USER_MANAGEMENT_USER_EDIT_OPERATION )
+        },
         responses = {
             @ApiResponse( responseCode = "200",
                 description = "If password change require flag was set"
@@ -232,7 +288,10 @@ public interface UserService
     @POST
     @Produces( { MediaType.APPLICATION_JSON } )
     @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_USER_EDIT_OPERATION )
-    @io.swagger.v3.oas.annotations.Operation( summary = "Creates a user",
+    @io.swagger.v3.oas.annotations.Operation( summary = "Clears the requirePassword flag for a given user",
+        security = {
+            @SecurityRequirement( name = RedbackRoleConstants.USER_MANAGEMENT_USER_EDIT_OPERATION )
+        },
         responses = {
             @ApiResponse( responseCode = "200",
                 description = "If password change require flag was unset"
@@ -294,6 +353,9 @@ public interface UserService
     @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_USER_EDIT_OPERATION,
     resource = "{userId}")
     @io.swagger.v3.oas.annotations.Operation( summary = "Clears the cache for the user",
+        security = {
+            @SecurityRequirement( name = RedbackRoleConstants.USER_MANAGEMENT_USER_EDIT_OPERATION )
+        },
         responses = {
             @ApiResponse( responseCode = "200",
                 description = "If the cache was cleared properly"
@@ -317,7 +379,7 @@ public interface UserService
             @ApiResponse( responseCode = "200",
                 description = "If the registration was successful, a registration key is returned"
             ),
-            @ApiResponse( responseCode = "400", description = "If the registration request has invalid data" ),
+            @ApiResponse( responseCode = "422", description = "If the the provided user data is not valid" ),
         }
     )
     RegistrationKey registerUser( @PathParam( "userId" ) String userId, UserRegistrationRequest userRegistrationRequest )
@@ -332,6 +394,14 @@ public interface UserService
     @Produces( { MediaType.APPLICATION_JSON } )
     @Consumes( { MediaType.APPLICATION_JSON } )
     @RedbackAuthorization( noRestriction = true, noPermission = true )
+    @io.swagger.v3.oas.annotations.Operation( summary = "Asks for a password reset of the given user. This generates a reset email sent to the stored address of the given user.",
+        responses = {
+            @ApiResponse( responseCode = "200",
+                description = "If the password reset email was sent"
+            ),
+            @ApiResponse( responseCode = "404", description = "User does not exist" ),
+        }
+    )
     ActionStatus resetPassword( @PathParam( "userId" )String userId, ResetPasswordRequest resetPasswordRequest )
         throws RedbackServiceException;
 
@@ -340,7 +410,20 @@ public interface UserService
     @Path( "{userId}/permissions" )
     @GET
     @Produces( { MediaType.APPLICATION_JSON } )
-    @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_USER_LIST_OPERATION )
+    @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_USER_LIST_OPERATION,
+        resource = "{userId}")
+    @io.swagger.v3.oas.annotations.Operation( summary = "Returns a list of permissions assigned to the given user.",
+        security = {
+            @SecurityRequirement( name = RedbackRoleConstants.USER_MANAGEMENT_USER_LIST_OPERATION )
+        },
+        responses = {
+            @ApiResponse( responseCode = "200",
+                description = "If the list could be returned"
+            ),
+            @ApiResponse( responseCode = "404", description = "User does not exist" ),
+            @ApiResponse( responseCode = "403", description = "Logged in user does not have the permission to get this information." ),
+        }
+    )
     Collection<Permission> getUserPermissions( @PathParam( "userId" ) String userName )
         throws RedbackServiceException;
 
@@ -351,6 +434,18 @@ public interface UserService
     @GET
     @Produces( { MediaType.APPLICATION_JSON } )
     @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_USER_LIST_OPERATION )
+    @io.swagger.v3.oas.annotations.Operation( summary = "Returns a list of privileged operations assigned to the given user.",
+        security = {
+            @SecurityRequirement( name = RedbackRoleConstants.USER_MANAGEMENT_USER_LIST_OPERATION )
+        },
+        responses = {
+            @ApiResponse( responseCode = "200",
+                description = "If the list could be returned"
+            ),
+            @ApiResponse( responseCode = "404", description = "User does not exist" ),
+            @ApiResponse( responseCode = "403", description = "Logged in user does not have the permission to get this information." ),
+        }
+    )
     Collection<Operation> getUserOperations( @PathParam( "userId" ) String userName )
         throws RedbackServiceException;
 
@@ -362,6 +457,13 @@ public interface UserService
     @GET
     @Produces( { MediaType.APPLICATION_JSON } )
     @RedbackAuthorization( noRestriction = true, noPermission = true )
+    @io.swagger.v3.oas.annotations.Operation( summary = "Returns a list of permissions assigned to the logged in user.",
+        responses = {
+            @ApiResponse( responseCode = "200",
+                description = "If the list could be returned"
+            )
+        }
+    )
     Collection<Permission> getCurrentUserPermissions( )
         throws RedbackServiceException;
 
@@ -373,6 +475,13 @@ public interface UserService
     @GET
     @Produces( { MediaType.APPLICATION_JSON } )
     @RedbackAuthorization( noRestriction = true, noPermission = true )
+    @io.swagger.v3.oas.annotations.Operation( summary = "Returns a list of privileged operations assigned to the logged in user.",
+        responses = {
+            @ApiResponse( responseCode = "200",
+                description = "If the list could be returned"
+            )
+        }
+    )
     Collection<Operation> getCurrentUserOperations( )
         throws RedbackServiceException;
 
@@ -381,6 +490,14 @@ public interface UserService
     @GET
     @Produces( {MediaType.APPLICATION_JSON} )
     @RedbackAuthorization( noRestriction = true, noPermission = true )
+    @io.swagger.v3.oas.annotations.Operation( summary = "Validate the user registration for the given userid by checking the provided key.",
+        responses = {
+            @ApiResponse( responseCode = "200",
+                description = "If the verification was successful"
+            ),
+            @ApiResponse( responseCode = "404", description = "No user registration was found for the given id and key" ),
+        }
+    )
     VerificationStatus validateUserRegistration( @PathParam( "userId" ) String userId, @PathParam( "key" ) String key )
         throws RedbackServiceException;
 }
diff --git a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultUserService.java b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultUserService.java
index 5307282..da26a8b 100644
--- a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultUserService.java
+++ b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultUserService.java
@@ -916,7 +916,7 @@ public class DefaultUserService
 
         if ( !StringUtils.equals( user.getPassword(), user.getConfirmPassword() ) )
         {
-            redbackServiceException.addErrorMessage( new ErrorMessage( "passwords.does.not.match", null ) );
+            redbackServiceException.addErrorMessage( new ErrorMessage( "passwords.do.not.match", null ) );
         }
 
         try
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 39df291..2c55882 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
@@ -927,7 +927,7 @@ public class DefaultUserService
         throws RedbackServiceException
     {
         RedbackServiceException redbackServiceException =
-            new RedbackServiceException( "issues during validating user" );
+            new RedbackServiceException( "issues during validating user", 422 );
         if ( StringUtils.isEmpty( user.getUserId() ) )
         {
             redbackServiceException.addErrorMessage( new ErrorMessage( "username.required", null ) );
@@ -952,7 +952,7 @@ public class DefaultUserService
 
         if ( !StringUtils.equals( user.getPassword(), user.getConfirmPassword() ) )
         {
-            redbackServiceException.addErrorMessage( new ErrorMessage( "passwords.does.not.match", null ) );
+            redbackServiceException.addErrorMessage( new ErrorMessage( "passwords.do.not.match", null ) );
         }
 
         try
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 6d2ff68..35df00f 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
@@ -259,36 +259,6 @@ public class NativeUserServiceTest extends AbstractNativeRestServices
     }
 
     @Test
-    void createExistingAdminUser( )
-    {
-        String token = null;
-        Map<String, Object> jsonAsMap = new HashMap<>( );
-        jsonAsMap.put( "user_id", "admin" );
-        jsonAsMap.put( "email", "admin@lordoftherings.org" );
-        jsonAsMap.put( "fullName", "Admin" );
-        jsonAsMap.put( "password", "pAssw0rD" );
-        Response response = given( ).spec( getRequestSpec( token ) ).contentType( JSON )
-            .body( jsonAsMap )
-            .when( )
-            .redirects( ).follow( false )
-            .post( "admin" )
-            .then( ).statusCode( 303 ).extract( ).response( );
-        assertTrue( response.getHeader( "Location" ).endsWith( "/users/admin" ) );
-    }
-
-    @Test
-    void checkAdminStatus( )
-    {
-        String token = null;
-        Response response = given( ).spec( getRequestSpec( token ) ).contentType( JSON )
-            .get( "admin/status" )
-            .then( ).statusCode( 200 ).extract( ).response( );
-        assertNotNull( response );
-        assertTrue( response.body( ).jsonPath( ).getBoolean( "exists" ) );
-        assertNotNull( response.body( ).jsonPath( ).get( "since" ) );
-    }
-
-    @Test
     void deleteUser( )
     {
         String token = getAdminToken( );
@@ -334,7 +304,7 @@ public class NativeUserServiceTest extends AbstractNativeRestServices
         try
         {
             String token = null;
-            Response response = given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
                 .delete( "aragorn" )
                 .then( ).statusCode( 401 ).extract( ).response( );
         }
@@ -436,6 +406,38 @@ public class NativeUserServiceTest extends AbstractNativeRestServices
         }
     }
 
+
+    @Test
+    void createExistingAdminUser( )
+    {
+        String token = null;
+        Map<String, Object> jsonAsMap = new HashMap<>( );
+        jsonAsMap.put( "user_id", "admin" );
+        jsonAsMap.put( "email", "admin@lordoftherings.org" );
+        jsonAsMap.put( "fullName", "Admin" );
+        jsonAsMap.put( "password", "pAssw0rD" );
+        Response response = given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+            .body( jsonAsMap )
+            .when( )
+            .redirects( ).follow( false )
+            .post( "admin" )
+            .then( ).statusCode( 303 ).extract( ).response( );
+        assertTrue( response.getHeader( "Location" ).endsWith( "/users/admin" ) );
+    }
+
+    @Test
+    void checkAdminStatus( )
+    {
+        String token = null;
+        Response response = given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+            .get( "admin/status" )
+            .then( ).statusCode( 200 ).extract( ).response( );
+        assertNotNull( response );
+        assertTrue( response.body( ).jsonPath( ).getBoolean( "exists" ) );
+        assertNotNull( response.body( ).jsonPath( ).get( "since" ) );
+    }
+
+
     @Test
     void lockUser( )
     {
@@ -559,7 +561,7 @@ public class NativeUserServiceTest extends AbstractNativeRestServices
     }
 
     @Test
-    void setNoPasswordChangeRequire( )
+    void clearPasswordChangeRequire( )
     {
         String token = getAdminToken( );
         Map<String, Object> jsonAsMap = new HashMap<>( );
@@ -596,6 +598,134 @@ public class NativeUserServiceTest extends AbstractNativeRestServices
         }
     }
 
+    @Test
+    void setPasswordChangeRequireNonExistingUser( )
+    {
+        String token = getAdminToken( );
+        given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .post( "aragorn2/password/require/set" )
+                .then( ).statusCode( 404 );
+    }
+
+    @Test
+    void clearPasswordChangeRequireNonExistingUser( )
+    {
+        String token = getAdminToken( );
+        given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+            .post( "aragorn2/password/require/clear" )
+            .then( ).statusCode( 404 );
+    }
+
+    @Test
+    void setPasswordChangeRequireNoPermission( )
+    {
+        String token = getAdminToken( );
+        Map<String, Object> jsonAsMap = new HashMap<>( );
+        jsonAsMap.put( "user_id", "aragorn" );
+        jsonAsMap.put( "email", "aragorn@lordoftherings.org" );
+        jsonAsMap.put( "fullName", "Aragorn King of Gondor" );
+        jsonAsMap.put( "locked", false );
+        jsonAsMap.put( "validated", true );
+        jsonAsMap.put( "passwordChangeRequired", false );
+        jsonAsMap.put( "password", "pAssw0rD" );
+        given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+            .body( jsonAsMap )
+            .when( )
+            .post( )
+            .then( ).statusCode( 201 );
+
+        jsonAsMap = new HashMap<>( );
+        jsonAsMap.put( "user_id", "elrond" );
+        jsonAsMap.put( "email", "elrond@lordoftherings.org" );
+        jsonAsMap.put( "fullName", "Elrond King of Elves" );
+        jsonAsMap.put( "locked", false );
+        jsonAsMap.put( "validated", true );
+        jsonAsMap.put( "passwordChangeRequired", false );
+        jsonAsMap.put( "password", "pAssw0rDElrond" );
+        given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+            .body( jsonAsMap )
+            .when( )
+            .post( )
+            .then( ).statusCode( 201 );
+
+
+        try
+        {
+            String userToken = getUserToken( "elrond", "pAssw0rDElrond" );
+            given( ).spec( getRequestSpec( userToken ) ).contentType( JSON )
+                .post( "aragorn/password/require/set" )
+                .then( ).statusCode( 403 );
+            Response response = given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .get( "aragorn" )
+                .then( ).statusCode( 200 ).extract( ).response( );
+            assertFalse( response.getBody( ).jsonPath( ).getBoolean( "passwordChangeRequired" ) );
+        }
+        finally
+        {
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .delete( "aragorn" )
+                .then( ).statusCode( 200 );
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .delete( "elrond" )
+                .then( ).statusCode( 200 );
+        }
+    }
+
+    @Test
+    void clearPasswordChangeRequireNoPermission( )
+    {
+        String token = getAdminToken( );
+        Map<String, Object> jsonAsMap = new HashMap<>( );
+        jsonAsMap.put( "user_id", "aragorn" );
+        jsonAsMap.put( "email", "aragorn@lordoftherings.org" );
+        jsonAsMap.put( "fullName", "Aragorn King of Gondor" );
+        jsonAsMap.put( "locked", false );
+        jsonAsMap.put( "validated", true );
+        jsonAsMap.put( "passwordChangeRequired", true );
+        jsonAsMap.put( "password", "pAssw0rD" );
+        given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+            .body( jsonAsMap )
+            .when( )
+            .post( )
+            .then( ).statusCode( 201 );
+
+        jsonAsMap = new HashMap<>( );
+        jsonAsMap.put( "user_id", "elrond" );
+        jsonAsMap.put( "email", "elrond@lordoftherings.org" );
+        jsonAsMap.put( "fullName", "Elrond King of Elves" );
+        jsonAsMap.put( "locked", false );
+        jsonAsMap.put( "validated", true );
+        jsonAsMap.put( "passwordChangeRequired", false );
+        jsonAsMap.put( "password", "pAssw0rDElrond" );
+        given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+            .body( jsonAsMap )
+            .when( )
+            .post( )
+            .then( ).statusCode( 201 );
+
+
+        try
+        {
+            String userToken = getUserToken( "elrond", "pAssw0rDElrond" );
+            given( ).spec( getRequestSpec( userToken ) ).contentType( JSON )
+                .post( "aragorn/password/require/clear" )
+                .then( ).statusCode( 403 );
+            Response response = given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .get( "aragorn" )
+                .then( ).statusCode( 200 ).extract( ).response( );
+            assertTrue( response.getBody( ).jsonPath( ).getBoolean( "passwordChangeRequired" ) );
+        }
+        finally
+        {
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .delete( "aragorn" )
+                .then( ).statusCode( 200 );
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .delete( "elrond" )
+                .then( ).statusCode( 200 );
+        }
+    }
+
 
     @Test
     void updateMe( )
@@ -812,4 +942,52 @@ public class NativeUserServiceTest extends AbstractNativeRestServices
         }
     }
 
+    @Test
+    void register( )
+    {
+        String adminToken = getAdminToken( );
+        Map<String, Object> requestMap = new HashMap<>( );
+
+        Map<String, Object> userMap = new HashMap<>( );
+
+        userMap.put( "user_id", "bilbo" );
+        userMap.put( "email", "bilbo@lordoftherings.org" );
+        userMap.put( "fullName", "Bilbo Beutlin" );
+        userMap.put( "validated", true );
+        userMap.put( "password", "pAssw0rD" );
+        userMap.put( "confirmPassword", "pAssw0rD" );
+        requestMap.put( "user", userMap );
+        requestMap.put( "applicationUrl", "http://localhost" );
+
+        given( ).spec( getRequestSpec( adminToken ) ).contentType( JSON )
+            .body( requestMap )
+            .when( )
+            .post( "bilbo/register" )
+            .then( ).statusCode( 200 );
+    }
+
+    @Test
+    void registerWithInvalidData( )
+    {
+        String adminToken = getAdminToken( );
+        Map<String, Object> requestMap = new HashMap<>( );
+
+        Map<String, Object> userMap = new HashMap<>( );
+
+        userMap.put( "user_id", "bilbo" );
+        userMap.put( "email", "bilbo@lordoftherings.org" );
+        userMap.put( "fullName", "Bilbo Beutlin" );
+        userMap.put( "validated", true );
+        userMap.put( "password", "pAssw0rD" );
+        userMap.put( "confirmPassword", "xxx" );
+        requestMap.put( "user", userMap );
+        requestMap.put( "applicationUrl", "http://localhost" );
+
+        given( ).spec( getRequestSpec( adminToken ) ).contentType( JSON )
+            .body( requestMap )
+            .when( )
+            .post( "bilbo/register" )
+            .then( ).statusCode( 422 );
+    }
+
 }