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/12/28 20:05:07 UTC

[archiva-redback-core] 01/02: Adding password methods to v2 user service

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 9e977561ac87f54b40cbf548abd0947162ba74f1
Author: Martin Stockhammer <ma...@apache.org>
AuthorDate: Sat Dec 26 09:16:27 2020 +0100

    Adding password methods to v2 user service
---
 .../apache/archiva/redback/i18n/default.properties |   2 +-
 .../archiva/redback/rest/api/MessageKeys.java      |   8 ++
 .../redback/rest/api/model/{ => v2}/Group.java     |   2 +-
 .../redback/rest/api/model/v2/PasswordChange.java  |  85 ++++++++++++++++
 .../redback/rest/api/services/PasswordService.java |   2 +
 .../rest/api/services/RoleManagementService.java   |   3 +
 .../api/services/v2/AuthenticationService.java     |   4 +-
 .../redback/rest/api/services/v2/GroupService.java |  12 ++-
 .../rest/api/services/v2/PasswordService.java      |  66 ------------
 .../redback/rest/api/services/v2/RoleService.java  |   5 +
 .../redback/rest/api/services/v2/UserService.java  |  49 ++++++++-
 .../redback/rest/api/services/v2/UtilServices.java |  57 -----------
 .../services/DefaultLdapGroupMappingService.java   |   2 -
 .../rest/services/DefaultPasswordService.java      |   2 +
 .../services/DefaultRoleManagementService.java     |   2 +
 .../redback/rest/services/DefaultUtilServices.java |   2 +
 .../rest/services/v2/DefaultGroupService.java      |  11 +-
 .../rest/services/v2/DefaultUserService.java       | 113 +++++++++++++++++++--
 .../rest/services/RoleManagementServiceTest.java   |   1 -
 .../rest/services/v2/NativeGroupServiceTest.java   |   8 +-
 .../rest/services/v2/NativeUserServiceTest.java    |  81 ++++++++++++++-
 .../redback/rest/services/v2/UserServiceTest.java  |   2 +-
 22 files changed, 359 insertions(+), 160 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 fb6e5a8..774f1f2 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
@@ -293,7 +293,7 @@ requires.authentication.go.ahead=Go Ahead
 # --------------------------------------------------
 # validationNotification
 # --------------------------------------------------
-validation.notification.page.title=Validation Notification Page
+validation.notification.page.title=Validation AppNotification Page
 validation.notification.section.title=Validation Reminder
 validation.notification.message.1=A validation email has been sent to the email address you provided ({0}). Please check for the email validation link sent to you.
 validation.notification.message.2=This account ({0}) will remain locked until it is validated.
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 506ab8f..a5e018f 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
@@ -43,6 +43,14 @@ public interface MessageKeys
     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_PASSWORDCHANGE_CURRENT_EMPTY = "rb.user.passwordchange.current_empty";
+    String ERR_PASSWORDCHANGE_KEY_EMPTY = "rb.user.passwordchange.key_empty";
+    String ERR_PASSWORDCHANGE_NEW_EMPTY = "rb.user.passwordchange.new_empty";
+    String ERR_PASSWORDCHANGE_CONFIRMATION_EMPTY = "rb.user.passwordchange.confirmation_empty";
+    String ERR_PASSWORDCHANGE_BAD_CONFIRMATION = "rb.user.passwordchange.bad_confirmation";
+    String ERR_PASSWORDCHANGE_BAD_KEY = "rb.user.passwordchange.bad_key";
+    String ERR_PASSWORDCHANGE_USER_NOT_FOUND = "rb.user.passwordchange.bad_key";
+
 
     String ERR_LDAP_GENERIC = "rb.ldap.error";
     String ERR_ROLE_MAPPING = "rb.role.mapping.error";
diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/Group.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/Group.java
similarity index 98%
rename from redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/Group.java
rename to redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/Group.java
index 05d2e08..2646daf 100644
--- a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/Group.java
+++ b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/Group.java
@@ -1,4 +1,4 @@
-package org.apache.archiva.redback.rest.api.model;
+package org.apache.archiva.redback.rest.api.model.v2;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/PasswordChange.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/PasswordChange.java
new file mode 100644
index 0000000..380e1a4
--- /dev/null
+++ b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/PasswordChange.java
@@ -0,0 +1,85 @@
+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;
+
+/**
+ * Data provided to the REST service for updating the password of the current logged in user
+ *
+ * @author Martin Stockhammer <ma...@apache.org>
+ * @since 3.0
+ */
+@XmlRootElement( name = "passwordChange" )
+@Schema(name="PasswordChange", description = "Data for password change")
+public class PasswordChange implements Serializable
+{
+    private static final long serialVersionUID = -1173796138433747226L;
+    String currentPassword;
+    String userId;
+    String newPassword;
+    String newPasswordConfirmation;
+
+    @Schema(description = "The current password of the logged in user, or a initial registration key")
+    public String getCurrentPassword( )
+    {
+        return currentPassword;
+    }
+
+    public void setCurrentPassword( String currentPassword )
+    {
+        this.currentPassword = currentPassword;
+    }
+
+
+    @Schema(description = "The User Id for the user to change the password. Must match the current logged in user.")
+    public String getUserId( )
+    {
+        return userId;
+    }
+
+    public void setUserId( String userId )
+    {
+        this.userId = userId;
+    }
+
+    @Schema(description = "The new password to set")
+    public String getNewPassword( )
+    {
+        return newPassword;
+    }
+
+    public void setNewPassword( String newPassword )
+    {
+        this.newPassword = newPassword;
+    }
+
+    @Schema(description = "The new password to set as confirmation that it is typed correctly")
+    public String getNewPasswordConfirmation( )
+    {
+        return newPasswordConfirmation;
+    }
+
+    public void setNewPasswordConfirmation( String newPasswordConfirmation )
+    {
+        this.newPasswordConfirmation = newPasswordConfirmation;
+    }
+}
diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/PasswordService.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/PasswordService.java
index 9bf80f9..cfee706 100644
--- a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/PasswordService.java
+++ b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/PasswordService.java
@@ -28,9 +28,11 @@ import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.MediaType;
 
 /**
+ * @deprecated Use the new V2 password service {@link org.apache.archiva.redback.rest.api.services.v2.PasswordService}
  * @author Olivier Lamy
  * @since 1.4
  */
+@Deprecated
 @Path( "/passwordService/" )
 public interface PasswordService
 {
diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/RoleManagementService.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/RoleManagementService.java
index 60d04a9..2a2de7f 100644
--- a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/RoleManagementService.java
+++ b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/RoleManagementService.java
@@ -39,8 +39,11 @@ import javax.ws.rs.core.MediaType;
 import java.util.List;
 
 /**
+ *
+ * @deprecated Use the new v2 service {@link org.apache.archiva.redback.rest.api.services.v2.RoleService}
  * @author Olivier Lamy
  */
+@Deprecated
 @Path( "/roleManagementService/" )
 public interface RoleManagementService
 {
diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/AuthenticationService.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/AuthenticationService.java
index bf29e62..37a0022 100644
--- a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/AuthenticationService.java
+++ b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/AuthenticationService.java
@@ -31,7 +31,6 @@ import org.apache.archiva.redback.rest.api.model.v2.PingResult;
 import org.apache.archiva.redback.rest.api.model.v2.TokenRefreshRequest;
 import org.apache.archiva.redback.rest.api.model.v2.TokenRequest;
 import org.apache.archiva.redback.rest.api.model.v2.TokenResponse;
-import org.apache.archiva.redback.rest.api.model.User;
 import org.apache.archiva.redback.rest.api.model.v2.UserInfo;
 import org.apache.archiva.redback.rest.api.services.RedbackServiceException;
 
@@ -45,8 +44,9 @@ import javax.ws.rs.core.MediaType;
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 
 /**
- * Version 2 of authentication service
+ * Authentication service
  *
+ * @author Martin Stockhammer
  * @since 3.0
  */
 @Path( "/auth" )
diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/GroupService.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/GroupService.java
index 0f42930..86b87af 100644
--- a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/GroupService.java
+++ b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/GroupService.java
@@ -29,7 +29,7 @@ import org.apache.archiva.redback.authorization.RedbackAuthorization;
 import org.apache.archiva.redback.integration.security.role.RedbackRoleConstants;
 import org.apache.archiva.redback.rest.api.Constants;
 import org.apache.archiva.redback.rest.api.model.ActionStatus;
-import org.apache.archiva.redback.rest.api.model.Group;
+import org.apache.archiva.redback.rest.api.model.v2.Group;
 import org.apache.archiva.redback.rest.api.model.v2.PagedResult;
 import org.apache.archiva.redback.rest.api.model.v2.GroupMapping;
 import org.apache.archiva.redback.rest.api.services.RedbackServiceException;
@@ -46,12 +46,14 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 import java.util.List;
 
 /**
  * @author Olivier Lamy
- * @since 2.1
+ * @author Martin Stockhammer
+ * @since 3.0
  */
 @Path( "/groups" )
 @Tag(name = "v2")
@@ -103,7 +105,7 @@ public interface GroupService
             @ApiResponse( responseCode = "405", description = "Invalid input" )
         }
     )
-    ActionStatus addGroupMapping( @Parameter( description = "The data of the group mapping", required = true )
+    Response addGroupMapping( @Parameter( description = "The data of the group mapping", required = true )
                                       GroupMapping groupMapping, @Context UriInfo uriInfo )
         throws RedbackServiceException;
 
@@ -129,11 +131,11 @@ public interface GroupService
     @RedbackAuthorization( permissions = RedbackRoleConstants.CONFIGURATION_EDIT_OPERATION )
     @Operation( summary = "Updates a group mapping",
         responses = {
-            @ApiResponse( description = "If the update was successful" ),
+            @ApiResponse( responseCode = "200", description = "If the update was successful" ),
             @ApiResponse( responseCode = "404", description = "Group mapping not found" )
         }
     )
-    ActionStatus updateGroupMapping( @Parameter( description = "The group name", required = true )
+    Response updateGroupMapping( @Parameter( description = "The group name", required = true )
                                          @PathParam( "group" ) String groupName,
                                      @Parameter( description = "The updated role list of the group mapping", required = true )
                                          List<String> roles )
diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/PasswordService.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/PasswordService.java
deleted file mode 100644
index 7982f3d..0000000
--- a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/PasswordService.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package org.apache.archiva.redback.rest.api.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.authorization.RedbackAuthorization;
-import org.apache.archiva.redback.rest.api.model.User;
-import org.apache.archiva.redback.rest.api.services.RedbackServiceException;
-
-import javax.ws.rs.GET;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
-import javax.ws.rs.core.MediaType;
-
-/**
- * @author Olivier Lamy
- * @since 1.4
- */
-@Path( "/password/" )
-public interface PasswordService
-{
-
-    /**
-     * used to change the password on first user connection after registration use.
-     * the key is mandatory and a control will be done on the username provided.
-     * <b>need to be logged by {@link UserService#validateUserFromKey(String)}</b>
-     * @return username
-     */
-    @GET
-    @Path( "changePasswordWithKey" )
-    @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
-    @RedbackAuthorization( noRestriction = true, noPermission = true )
-    User changePasswordWithKey( @QueryParam( "password" ) String password,
-                                  @QueryParam( "passwordConfirmation" ) String passwordConfirmation,
-                                  @QueryParam( "key" ) String key )
-        throws RedbackServiceException;
-
-    /**
-     * used to change the password on passwordChangeRequired state.
-     */
-    @GET
-    @Path( "changePassword" )
-    @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
-    @RedbackAuthorization( noRestriction = true, noPermission = true )
-    User changePassword( @QueryParam( "userName" ) String userName,
-                            @QueryParam( "previousPassword" ) String previousPassword,
-                            @QueryParam( "password" ) String password,
-                            @QueryParam( "passwordConfirmation" ) String passwordConfirmation )
-        throws RedbackServiceException;
-}
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 86e20b9..a8be9ff 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
@@ -54,7 +54,12 @@ import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 import static org.apache.archiva.redback.rest.api.Constants.DEFAULT_PAGE_LIMIT;
 
 /**
+ *
+ * Service interface for role management.
+ *
  * @author Olivier Lamy
+ * @author Martin Stockhammer
+ * @since 3.0
  */
 @Path( "/roles" )
 @Tag(name = "v2")
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 243ca6e..5bb4811 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
@@ -33,6 +33,7 @@ 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.PasswordChange;
 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;
@@ -58,6 +59,7 @@ 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.Collection;
 import java.util.List;
 
@@ -65,6 +67,9 @@ import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 import static org.apache.archiva.redback.rest.api.Constants.DEFAULT_PAGE_LIMIT;
 import static org.apache.archiva.redback.users.UserManager.GUEST_USERNAME;
 
+/**
+ * Service interface for user management
+ */
 @Path( "/users" )
 @Tag(name = "v2")
 @Tag(name = "v2/Users")
@@ -426,7 +431,7 @@ public interface UserService
                 content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = RedbackRestError.class ))  )
         }
     )
-    ActionStatus removeFromCache( @PathParam( "userId" ) String userId )
+    Response removeFromCache( @PathParam( "userId" ) String userId )
         throws RedbackServiceException;
 
     /**
@@ -469,7 +474,7 @@ public interface UserService
                 content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = RedbackRestError.class ))  ),
         }
     )
-    ActionStatus resetPassword( @PathParam( "userId" )String userId )
+    Response resetPassword( @PathParam( "userId" )String userId )
         throws RedbackServiceException;
 
     /**
@@ -653,4 +658,44 @@ public interface UserService
     RoleTree getRoleTree( @PathParam( "userId" ) String username )
         throws RedbackServiceException;
 
+
+    @Path( "me/password/update" )
+    @POST
+    @Consumes({APPLICATION_JSON})
+    @RedbackAuthorization( noRestriction = true, noPermission = true )
+    @Operation( summary = "Changes a user password",
+        security = {
+            @SecurityRequirement( name = "Authenticated" )
+        },
+        responses = {
+            @ApiResponse( responseCode = "200",
+                description = "The password change was successful"
+            ),
+            @ApiResponse( responseCode = "401", description = "User is not logged in",
+                content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = RedbackRestError.class ))  ),
+            @ApiResponse( responseCode = "400", description = "Provided data is not valid",
+                content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = RedbackRestError.class ))  ),
+            @ApiResponse( responseCode = "403", description = "If the given user_id does not match",
+                content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = RedbackRestError.class ))  )
+        }
+    )
+    Response changePassword( PasswordChange passwordChange ) throws RedbackServiceException;
+
+    @Path( "{userId}/password/update" )
+    @POST
+    @Consumes({APPLICATION_JSON})
+    @RedbackAuthorization( noRestriction = true, noPermission = true )
+    @Operation( summary = "Changes a user password",
+        responses = {
+            @ApiResponse( responseCode = "200",
+                description = "The password change was successful"
+            ),
+            @ApiResponse( responseCode = "400", description = "Provided data is not valid",
+                content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = RedbackRestError.class ))  ),
+            @ApiResponse( responseCode = "403", description = "If the given user_id does not match",
+                content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = RedbackRestError.class ))  )
+        }
+    )
+    Response changePasswordUnauthenticated( @PathParam( "userId" ) String userId,  PasswordChange passwordChange ) throws RedbackServiceException;
+
 }
diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/UtilServices.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/UtilServices.java
deleted file mode 100644
index b8ffe38..0000000
--- a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/UtilServices.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package org.apache.archiva.redback.rest.api.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.authorization.RedbackAuthorization;
-import org.apache.archiva.redback.rest.api.services.RedbackServiceException;
-
-import javax.ws.rs.GET;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
-import javax.ws.rs.core.MediaType;
-import java.util.Properties;
-
-/**
- * @author Olivier Lamy
- * @since 1.4
- */
-@Path( "/utilServices/" )
-public interface UtilServices
-{
-
-    @Path( "getBundleResources" )
-    @GET
-    @Produces( { MediaType.TEXT_PLAIN } )
-    @RedbackAuthorization( noRestriction = true )
-    String getI18nResources( @QueryParam( "locale" ) String locale )
-        throws RedbackServiceException;
-
-    /**
-     * <b>not intended to be exposed as a REST service.</b>
-     * will load i18N resource org/apache/archiva/redback/users/messages in default en then in the asked locale.
-     * @param locale
-     * @return
-     * @throws RedbackServiceException
-     */
-    Properties getI18nProperties( String locale )
-        throws RedbackServiceException;
-
-
-}
diff --git a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultLdapGroupMappingService.java b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultLdapGroupMappingService.java
index b544b32..1d7a2f8 100644
--- a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultLdapGroupMappingService.java
+++ b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultLdapGroupMappingService.java
@@ -25,7 +25,6 @@ import org.apache.archiva.redback.common.ldap.connection.LdapException;
 import org.apache.archiva.redback.common.ldap.role.LdapRoleMapper;
 import org.apache.archiva.redback.common.ldap.role.LdapRoleMapperConfiguration;
 import org.apache.archiva.redback.rest.api.model.ActionStatus;
-import org.apache.archiva.redback.rest.api.model.Group;
 import org.apache.archiva.redback.rest.api.model.LdapGroupMapping;
 import org.apache.archiva.redback.rest.api.model.LdapGroupMappingUpdateRequest;
 import org.apache.archiva.redback.rest.api.model.StringList;
@@ -43,7 +42,6 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
-import java.util.stream.Collectors;
 
 /**
  * @deprecated Use new API version {@link org.apache.archiva.redback.rest.services.v2.DefaultGroupService}
diff --git a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultPasswordService.java b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultPasswordService.java
index 163fec5..4a8b3de 100644
--- a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultPasswordService.java
+++ b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultPasswordService.java
@@ -47,9 +47,11 @@ import java.util.Arrays;
 import java.util.List;
 
 /**
+ * @deprecated Use the new V2 version {@link org.apache.archiva.redback.rest.services.v2.DefaultAuthenticationService}
  * @author Olivier Lamy
  * @since 1.4
  */
+@Deprecated
 @Service("passwordService#rest")
 public class DefaultPasswordService
     implements PasswordService
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 6f936f6..900829e 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
@@ -65,9 +65,11 @@ import java.util.Set;
 import java.util.stream.Collectors;
 
 /**
+ * @deprecated Use the new V2 version {@link org.apache.archiva.redback.rest.services.v2.DefaultRoleService}
  * @author Olivier Lamy
  * @since 1.3
  */
+@Deprecated
 @Service("roleManagementService#rest")
 public class DefaultRoleManagementService
     implements RoleManagementService
diff --git a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultUtilServices.java b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultUtilServices.java
index 3f8d190..d023ba4 100644
--- a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultUtilServices.java
+++ b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultUtilServices.java
@@ -34,9 +34,11 @@ import java.util.Properties;
 import java.util.concurrent.ConcurrentHashMap;
 
 /**
+ * @deprecated There is no replacement
  * @author Olivier Lamy
  * @since 1.4
  */
+@Deprecated
 @Service( "utilServices#rest" )
 public class DefaultUtilServices
     implements UtilServices
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 a495d42..fb4072c 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
@@ -27,9 +27,8 @@ import org.apache.archiva.redback.common.ldap.role.LdapGroup;
 import org.apache.archiva.redback.common.ldap.role.LdapRoleMapper;
 import org.apache.archiva.redback.common.ldap.role.LdapRoleMapperConfiguration;
 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.Group;
+import org.apache.archiva.redback.rest.api.model.v2.Group;
 import org.apache.archiva.redback.rest.api.model.v2.GroupMapping;
 import org.apache.archiva.redback.rest.api.model.v2.PagedResult;
 import org.apache.archiva.redback.rest.api.services.RedbackServiceException;
@@ -182,7 +181,7 @@ public class DefaultGroupService
     }
 
     @Override
-    public ActionStatus addGroupMapping( GroupMapping ldapGroupMapping, UriInfo uriInfo)
+    public Response addGroupMapping( GroupMapping ldapGroupMapping, UriInfo uriInfo)
         throws RedbackServiceException
     {
         try
@@ -194,13 +193,13 @@ public class DefaultGroupService
             {
                 response.setHeader( "Location", uriInfo.getAbsolutePathBuilder( ).path( ldapGroupMapping.getGroupName( ) ).build( ).toString( ) );
             }
+            return Response.status( 201 ).build( );
         }
         catch ( MappingException e )
         {
             log.error( e.getMessage(), e );
             throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_ROLE_MAPPING, e.getMessage( ) ) );
         }
-        return ActionStatus.SUCCESS;
     }
 
     @Override
@@ -220,7 +219,7 @@ public class DefaultGroupService
     }
 
     @Override
-    public ActionStatus updateGroupMapping( String groupName, List<String> roles ) throws RedbackServiceException
+    public Response updateGroupMapping( String groupName, List<String> roles ) throws RedbackServiceException
     {
         try
         {
@@ -234,7 +233,7 @@ public class DefaultGroupService
         {
             ldapRoleMapperConfiguration.updateLdapMapping( groupName,
                 roles );
-            return ActionStatus.SUCCESS;
+            return Response.ok( ).build( );
         }
         catch ( MappingException 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 b89a550..e3a0ade 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
@@ -38,19 +38,20 @@ import org.apache.archiva.redback.policy.AccountLockedException;
 import org.apache.archiva.redback.policy.MustChangePasswordException;
 import org.apache.archiva.redback.policy.PasswordEncoder;
 import org.apache.archiva.redback.policy.PasswordRuleViolationException;
+import org.apache.archiva.redback.policy.PasswordRuleViolations;
 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.PasswordChange;
 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;
@@ -95,7 +96,6 @@ import java.security.Principal;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -103,7 +103,6 @@ import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import java.util.function.Function;
-import java.util.function.Predicate;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -503,7 +502,7 @@ public class DefaultUserService extends BaseRedbackService
     }
 
     @Override
-    public ActionStatus removeFromCache( String userId )
+    public Response removeFromCache( String userId )
         throws RedbackServiceException
     {
         if ( userAssignmentsCache != null )
@@ -529,7 +528,7 @@ public class DefaultUserService extends BaseRedbackService
             }
         }
 
-        return ActionStatus.SUCCESS;
+        return Response.ok( ).build( );
     }
 
     @Override
@@ -620,7 +619,7 @@ public class DefaultUserService extends BaseRedbackService
     }
 
     @Override
-    public ActionStatus resetPassword( String userId )
+    public Response resetPassword( String userId )
         throws RedbackServiceException
     {
         String username = userId;
@@ -660,7 +659,7 @@ public class DefaultUserService extends BaseRedbackService
             throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_USERMANAGER_FAIL, e.getMessage( ) ), 400 );
         }
 
-        return ActionStatus.SUCCESS;
+        return Response.ok( ).build( );
     }
 
     @Override
@@ -697,7 +696,7 @@ public class DefaultUserService extends BaseRedbackService
             if ( userManager.userExists( user.getUserId( ) ) )
             {
                 throw new RedbackServiceException(
-                    new ErrorMessage( "user.already.exists", new String[]{user.getUserId( )} ) );
+                    ErrorMessage.of( MessageKeys.ERR_USER_EXISTS, user.getUserId() ));
             }
 
             u = userManager.createUser( user.getUserId( ), user.getFullName( ), user.getEmail( ) );
@@ -973,6 +972,104 @@ public class DefaultUserService extends BaseRedbackService
         }
     }
 
+    @Override
+    public Response changePasswordUnauthenticated( String userId, PasswordChange passwordChange ) throws RedbackServiceException
+    {
+        changeUserPassword( userId, passwordChange );
+        return Response.ok( ).build( );
+    }
+
+    @Override
+    public Response changePassword( PasswordChange passwordChange ) throws RedbackServiceException
+    {
+        RedbackPrincipal principal = getPrincipal( );
+        if ( principal == null )
+        {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_AUTH_UNAUTHORIZED_REQUEST ), 401 );
+        }
+        String userId = principal.getUser( ).getUsername( );
+        changeUserPassword( userId, passwordChange );
+        return Response.ok( ).build( );
+    }
+
+    private List<ErrorMessage> getPasswordViolationMessages( PasswordRuleViolationException e )
+    {
+        PasswordRuleViolations violations = e.getViolations( );
+        List<ErrorMessage> errorMessages = new ArrayList<>( violations.getViolations( ).size( ) );
+        if ( violations != null )
+        {
+            for ( String violation : violations.getLocalizedViolations( ) )
+            {
+                errorMessages.add( new ErrorMessage( violation ) );
+            }
+        }
+        return errorMessages;
+    }
+
+    private void changeUserPassword(final String userId, final PasswordChange passwordChange) throws RedbackServiceException
+    {
+        if ( StringUtils.isEmpty( passwordChange.getCurrentPassword() ) )
+        {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_PASSWORDCHANGE_CURRENT_EMPTY ), 400 );
+        }
+        if ( passwordChange.getUserId( ) == null || ( !passwordChange.getUserId( ).equals( userId ) ) )
+        {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_USER_ID_INVALID ), 403 );
+        }
+
+        if ( StringUtils.isEmpty( passwordChange.getNewPassword() ) )
+        {
+            throw new RedbackServiceException( ErrorMessage.of(MessageKeys.ERR_PASSWORDCHANGE_NEW_EMPTY), 400 );
+        }
+        if ( StringUtils.isEmpty( passwordChange.getNewPasswordConfirmation() ) )
+        {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_PASSWORDCHANGE_CONFIRMATION_EMPTY ),
+                400 );
+        }
+        if ( !StringUtils.equals( passwordChange.getNewPassword(), passwordChange.getNewPasswordConfirmation() ) )
+        {
+            throw new RedbackServiceException(ErrorMessage.of( MessageKeys.ERR_PASSWORDCHANGE_BAD_CONFIRMATION ),
+                403 );
+        }
+
+        try
+        {
+            org.apache.archiva.redback.users.User u = securitySystem.getUserManager().findUser( userId );
+
+            String previousEncodedPassword = u.getEncodedPassword();
+
+            // check oldPassword with the current one
+
+            PasswordEncoder encoder = securitySystem.getPolicy().getPasswordEncoder();
+
+            if ( !encoder.isPasswordValid( previousEncodedPassword, passwordChange.getCurrentPassword() ) )
+            {
+
+                throw new RedbackServiceException( MessageKeys.ERR_AUTH_INVALID_CREDENTIALS,
+                    401 );
+            }
+
+            u.setPassword( passwordChange.getNewPassword() );
+            securitySystem.getUserManager().updateUser( u );
+        }
+        catch ( UserNotFoundException e )
+        {
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_USER_NOT_FOUND ),
+                400 );
+        }
+        catch ( UserManagerException e )
+        {
+            log.info( "UserManagerException: {}", e.getMessage() );
+            throw new RedbackServiceException( ErrorMessage.of( MessageKeys.ERR_USERMANAGER_FAIL, e.getMessage() ) );
+        }
+        catch ( PasswordRuleViolationException e )
+        {
+            throw new RedbackServiceException( getPasswordViolationMessages( e ), 401 );
+        }
+
+
+    }
+
     private Stream<Role> flattenRole( Role role )
     {
         return Stream.concat( Stream.of( role ), this.getChildren( role ).flatMap( this::flattenRole ) ).distinct( );
diff --git a/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/RoleManagementServiceTest.java b/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/RoleManagementServiceTest.java
index b2de0c3..e10e99d 100644
--- a/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/RoleManagementServiceTest.java
+++ b/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/RoleManagementServiceTest.java
@@ -46,7 +46,6 @@ public class RoleManagementServiceTest
 {
 
 
-    @Ignore
     @Test
     public void roleExist()
         throws Exception
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 5e8e855..db8dfc3 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
@@ -19,11 +19,10 @@ package org.apache.archiva.redback.rest.services.v2;
  */
 
 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.Group;
 import org.apache.archiva.redback.rest.api.model.v2.GroupMapping;
 import org.apache.archiva.redback.rest.services.BaseSetup;
 import org.apache.archiva.redback.rest.services.LdapInfo;
@@ -32,7 +31,6 @@ import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.MethodOrderer;
-import org.junit.jupiter.api.Order;
 import org.junit.jupiter.api.Tag;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.TestInstance;
@@ -399,7 +397,6 @@ public class NativeGroupServiceTest extends AbstractNativeRestServices
                 .post( "/mappings" )
                 .then( ).statusCode( 201 ).extract( ).response( );
             assertNotNull( response );
-            assertTrue( response.getBody( ).jsonPath( ).getBoolean( "success" ) );
             assertNotNull( response.getHeader( "Location" ) );
 
             assertTrue( UrlDecoder.urlDecode( response.getHeader( "Location" ), Charset.forName( "UTF-8" ), false ).endsWith( "/mappings/ldap group" ) );
@@ -426,7 +423,6 @@ public class NativeGroupServiceTest extends AbstractNativeRestServices
                 .post( "/mappings" )
                 .then( ).statusCode( 201 ).extract( ).response( );
             assertNotNull( response );
-            assertTrue( response.getBody( ).jsonPath( ).getBoolean( "success" ) );
             response = given( ).spec( getRequestSpec( token ) ).contentType( JSON )
                 .when( )
                 .get( "/mappings" )
@@ -480,7 +476,6 @@ public class NativeGroupServiceTest extends AbstractNativeRestServices
                 .put( "/mappings/archiva-admin" )
                 .then( )
                 .statusCode( 200 ).extract( ).response( );
-            assertTrue( response.getBody( ).jsonPath( ).getBoolean( "success" ) );
         } finally {
             // Put it back
             List<String> list = Arrays.asList( "System Administrator" );
@@ -506,7 +501,6 @@ public class NativeGroupServiceTest extends AbstractNativeRestServices
                 .put( "/mappings/archiva-admin" )
                 .then( )
                 .statusCode( 200 ).extract( ).response( );
-            assertTrue( response.getBody( ).jsonPath( ).getBoolean( "success" ) );
 
             response = given( ).spec( getRequestSpec( token ) ).contentType( JSON )
                 .when( )
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 4181d27..5571266 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
@@ -28,6 +28,7 @@ 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;
+import org.apache.commons.lang3.StringUtils;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.DisplayName;
@@ -1072,7 +1073,6 @@ public class NativeUserServiceTest extends AbstractNativeRestServices
                 .post( "aragorn/cache/clear" )
                 .then( ).statusCode( 200 ).extract( ).response( );
 
-            assertTrue( response.getBody( ).jsonPath( ).getBoolean( "success" ) );
         }
         finally
         {
@@ -1636,4 +1636,83 @@ public class NativeUserServiceTest extends AbstractNativeRestServices
                 .then( ).statusCode( 200 );
         }
     }
+
+    @Test
+    void changePassword()
+    {
+        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 {
+            Map<String, String> passwordChange = new HashMap<>( );
+            passwordChange.put( "user_id", "bilbo" );
+            passwordChange.put( "current_password", "pAssw0rD" );
+            passwordChange.put( "new_password", "pAsXXXw4Qz66D" );
+            passwordChange.put( "new_password_confirmation", "pAsXXXw4Qz66D" );
+            String userToken = getUserToken( "bilbo", "pAssw0rD" );
+            given( ).spec( getRequestSpec( userToken ) ).contentType( JSON )
+                .body( passwordChange )
+                .when( )
+                .post( "me/password/update" )
+                .then( ).statusCode( 200 );
+            userToken = getUserToken( "bilbo", "pAsXXXw4Qz66D" );
+            assertNotNull( userToken );
+
+        } finally {
+            given( ).spec( getRequestSpec( adminToken ) ).contentType( JSON )
+                .delete( "bilbo" )
+                .then( ).statusCode( 200 );
+        }
+    }
+
+    @Test
+    void changePasswordUnauthenticated()
+    {
+        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 {
+            Map<String, String> passwordChange = new HashMap<>( );
+            passwordChange.put( "user_id", "bilbo" );
+            passwordChange.put( "current_password", "pAssw0rD" );
+            passwordChange.put( "new_password", "pAsXXXw4Qz66D" );
+            passwordChange.put( "new_password_confirmation", "pAsXXXw4Qz66D" );
+            String userToken = getUserToken( "bilbo", "pAssw0rD" );
+            assertFalse( StringUtils.isEmpty( userToken ) );
+            given( ).spec( getRequestSpec(  ) ).contentType( JSON )
+                .body( passwordChange )
+                .when( )
+                .post( "bilbo/password/update" )
+                .then( ).statusCode( 200 );
+            userToken = getUserToken( "bilbo", "pAsXXXw4Qz66D" );
+            assertFalse( StringUtils.isEmpty( userToken ) );
+
+        } finally {
+            given( ).spec( getRequestSpec( adminToken ) ).contentType( JSON )
+                .delete( "bilbo" )
+                .then( ).statusCode( 200 );
+        }
+    }
 }
diff --git a/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/v2/UserServiceTest.java b/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/v2/UserServiceTest.java
index c673582..0cc32b3 100644
--- a/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/v2/UserServiceTest.java
+++ b/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/v2/UserServiceTest.java
@@ -420,7 +420,7 @@ public class UserServiceTest
 
             // assertTrue( service.validateUserFromKey( key ).isSuccess( ) );
 
-            assertTrue( service.resetPassword(u.getUserId() ).isSuccess( ) );
+            assertEquals(200, service.resetPassword(u.getUserId() ).getStatus() );
 
             emailMessages = assertService.getEmailMessageSended( );
             assertEquals( 2, emailMessages.size( ) );