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:20 UTC

[archiva-redback-core] branch master updated (7a13ce4 -> 2a8928e)

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 7a13ce4  Switching to least privilege for undefined resources on method annotations
     new 3d2f5e8  Fixing property name
     new 2a8928e  Improving user service v2

The 2 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:
 .../apache/archiva/redback/i18n/default.properties |   2 +-
 .../archiva/redback/i18n/default_de.properties     |   2 +-
 .../archiva/redback/i18n/default_fr.properties     |   2 +-
 .../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 ++++++++++++++++++---
 8 files changed, 372 insertions(+), 74 deletions(-)


[archiva-redback-core] 01/02: Fixing property name

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 3d2f5e88f75b475e1d8e4a8699e18d974b57c6af
Author: Martin Stockhammer <ma...@apache.org>
AuthorDate: Thu Sep 17 09:29:34 2020 +0200

    Fixing property name
---
 .../main/resources/org/apache/archiva/redback/i18n/default.properties   | 2 +-
 .../resources/org/apache/archiva/redback/i18n/default_de.properties     | 2 +-
 .../resources/org/apache/archiva/redback/i18n/default_fr.properties     | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/redback-integrations/redback-common-integrations/src/main/resources/org/apache/archiva/redback/i18n/default.properties b/redback-integrations/redback-common-integrations/src/main/resources/org/apache/archiva/redback/i18n/default.properties
index 42a437f..fb6e5a8 100644
--- a/redback-integrations/redback-common-integrations/src/main/resources/org/apache/archiva/redback/i18n/default.properties
+++ b/redback-integrations/redback-common-integrations/src/main/resources/org/apache/archiva/redback/i18n/default.properties
@@ -48,7 +48,7 @@ fullName.required=Full Name is required.
 email.required=Email Address is required.
 email.invalid=Email Address is invalid.
 password.required=Password is required.
-passwords.does.not.match=Passwords do not match.
+passwords.do.not.match=Passwords do not match.
 username.invalid.characters=Invalid characters found in Username.
 fullName.invalid.characters=Invalid characters found in Full Name.
 
diff --git a/redback-integrations/redback-common-integrations/src/main/resources/org/apache/archiva/redback/i18n/default_de.properties b/redback-integrations/redback-common-integrations/src/main/resources/org/apache/archiva/redback/i18n/default_de.properties
index d440d7e..73cf4c5 100644
--- a/redback-integrations/redback-common-integrations/src/main/resources/org/apache/archiva/redback/i18n/default_de.properties
+++ b/redback-integrations/redback-common-integrations/src/main/resources/org/apache/archiva/redback/i18n/default_de.properties
@@ -44,7 +44,7 @@ fullName.required=Es mu\u00DF ein vollst\u00E4ndiger Name angegeben werden.
 email.required=Es mu\u00DF eine E-Mail-Adresse angegeben werden.
 email.invalid=Ung\u00FCltige E-Mail-Adresse.
 password.required=Es mu\u00DF ein Passwort angegeben werden.
-passwords.does.not.match=Die Passw\u00F6rter stimmen nicht \u00FCberein.
+passwords.do.not.match=Die Passw\u00F6rter stimmen nicht \u00FCberein.
 
 # --------------------------------------------------
 # AccountAction
diff --git a/redback-integrations/redback-common-integrations/src/main/resources/org/apache/archiva/redback/i18n/default_fr.properties b/redback-integrations/redback-common-integrations/src/main/resources/org/apache/archiva/redback/i18n/default_fr.properties
index eb6b126..699322a 100644
--- a/redback-integrations/redback-common-integrations/src/main/resources/org/apache/archiva/redback/i18n/default_fr.properties
+++ b/redback-integrations/redback-common-integrations/src/main/resources/org/apache/archiva/redback/i18n/default_fr.properties
@@ -45,7 +45,7 @@ fullName.required=Le nom complet est obligatoire.
 email.required=L''adresse email est obligatoire.
 email.invalid=L''adresse email est invalide.
 password.required=Le mot de passe est obligatoire.
-passwords.does.not.match=Les mots de passe ne correspondent pas.
+passwords.do.not.match=Les mots de passe ne correspondent pas.
 
 # --------------------------------------------------
 # AccountAction


[archiva-redback-core] 02/02: Improving user 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 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 );
+    }
+
 }