Update and test password flag methods for V2 REST user service
diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/PasswordStatus.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/PasswordStatus.java
similarity index 83%
rename from redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/PasswordStatus.java
rename to redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/PasswordStatus.java
index 7a20e36..be26e8c 100644
--- a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/PasswordStatus.java
+++ b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/v2/PasswordStatus.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
@@ -18,18 +18,23 @@
  * under the License.
  */
 
+import javax.xml.bind.annotation.XmlRootElement;
+
 /**
  * @author Martin Stockhammer <martin_s@apache.org>
  */
+@XmlRootElement( name="passwordStatus" )
 public class PasswordStatus
 {
     boolean changeRequired = false;
 
-    public PasswordStatus() {
+    public PasswordStatus( )
+    {
 
     }
 
-    public PasswordStatus(boolean changeRequired) {
+    public PasswordStatus( boolean changeRequired )
+    {
         this.changeRequired = changeRequired;
     }
 
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 87c2665..f1a9899 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
@@ -29,7 +29,6 @@
 import org.apache.archiva.redback.rest.api.model.v2.AvailabilityStatus;
 import org.apache.archiva.redback.rest.api.model.Operation;
 import org.apache.archiva.redback.rest.api.model.v2.PagedResult;
-import org.apache.archiva.redback.rest.api.model.PasswordStatus;
 import org.apache.archiva.redback.rest.api.model.Permission;
 import org.apache.archiva.redback.rest.api.model.v2.PingResult;
 import org.apache.archiva.redback.rest.api.model.ResetPasswordRequest;
@@ -193,7 +192,7 @@
     @io.swagger.v3.oas.annotations.Operation( summary = "Creates a user",
         responses = {
             @ApiResponse( responseCode = "200",
-                description = "If locking was successful"
+                description = "If unlocking was successful"
             ),
             @ApiResponse( responseCode = "404", description = "User does not exist" ),
         }
@@ -204,22 +203,48 @@
 
     /**
      */
-    @Path( "{userId}/password/status" )
-    @GET
+    @Path( "{userId}/password/require/set" )
+    @POST
     @Produces( { MediaType.APPLICATION_JSON } )
     @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_USER_EDIT_OPERATION )
-    PasswordStatus getPasswordStatus( @PathParam( "userId" ) String userId )
+    @io.swagger.v3.oas.annotations.Operation( summary = "Creates a user",
+        responses = {
+            @ApiResponse( responseCode = "200",
+                description = "If password change require flag was set"
+            ),
+            @ApiResponse( responseCode = "404", description = "User does not exist" ),
+        }
+    )
+    void setRequirePasswordChangeFlag( @PathParam( "userId" ) String userId )
         throws RedbackServiceException;
 
     /**
-     * update only the current user and this fields: fullname, email, password.
+     */
+    @Path( "{userId}/password/require/clear" )
+    @POST
+    @Produces( { MediaType.APPLICATION_JSON } )
+    @RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_USER_EDIT_OPERATION )
+    @io.swagger.v3.oas.annotations.Operation( summary = "Creates a user",
+        responses = {
+            @ApiResponse( responseCode = "200",
+                description = "If password change require flag was unset"
+            ),
+            @ApiResponse( responseCode = "404", description = "User does not exist" ),
+        }
+    )
+    void clearRequirePasswordChangeFlag( @PathParam( "userId" ) String userId )
+        throws RedbackServiceException;
+
+
+    /**
+     * update only the current logged in user and this fields: fullname, email, password.
      * The service verifies the current logged user with the one passed in the method
      */
     @Path( "me" )
     @PUT
     @Produces( { MediaType.APPLICATION_JSON } )
     @RedbackAuthorization( noPermission = true )
-    ActionStatus updateMe( @PathParam( "userId" ) String userId, User user )
+    ActionStatus updateMe( User user )
         throws RedbackServiceException;
 
     @Path( "___ping___" )
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 c3da00f..9f29c04 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
@@ -47,7 +47,7 @@
 import org.apache.archiva.redback.rest.api.model.v2.AvailabilityStatus;
 import org.apache.archiva.redback.rest.api.model.ErrorMessage;
 import org.apache.archiva.redback.rest.api.model.Operation;
-import org.apache.archiva.redback.rest.api.model.PasswordStatus;
+import org.apache.archiva.redback.rest.api.model.v2.PasswordStatus;
 import org.apache.archiva.redback.rest.api.model.Permission;
 import org.apache.archiva.redback.rest.api.model.v2.RegistrationKey;
 import org.apache.archiva.redback.rest.api.model.ResetPasswordRequest;
@@ -89,6 +89,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 
 import static org.apache.archiva.redback.rest.api.Constants.*;
@@ -344,7 +345,7 @@
     }
 
     @Override
-    public ActionStatus updateMe( String userId, User user )
+    public ActionStatus updateMe( User user )
         throws RedbackServiceException
     {
         // check username == one in the session
@@ -1034,16 +1035,18 @@
         return null;
     }
 
-    @Override
-    public void unlockUser( String userId )
-        throws RedbackServiceException
+
+    private void updateUser( String userId, Function<org.apache.archiva.redback.users.User, RedbackServiceException> updateFunction ) throws RedbackServiceException
     {
         try
         {
             org.apache.archiva.redback.users.User rawUser = userManager.findUser( userId, false );
             if ( rawUser != null )
             {
-                rawUser.setLocked( false );
+                RedbackServiceException result = updateFunction.apply( rawUser );
+                if (result!=null) {
+                    throw result;
+                }
                 userManager.updateUser( rawUser, false );
             } else {
                 throw new RedbackServiceException( ErrorMessage.of( ERR_USER_NOT_FOUND, userId ), 404 );
@@ -1057,6 +1060,16 @@
         {
             throw new RedbackServiceException( new ErrorMessage( e.getMessage() ) );
         }
+    }
+
+    @Override
+    public void unlockUser( String userId )
+        throws RedbackServiceException
+    {
+        updateUser( userId, user -> {
+            user.setLocked( false );
+            return null;
+        } );
         httpServletResponse.setStatus( 200 );
     }
 
@@ -1064,40 +1077,33 @@
     public void lockUser( String userId )
         throws RedbackServiceException
     {
-        try
-        {
-            org.apache.archiva.redback.users.User rawUser = userManager.findUser( userId, false );
-            if ( rawUser != null )
-            {
-                rawUser.setLocked( true );
-                userManager.updateUser( rawUser, false );
-            } else {
-                throw new RedbackServiceException( ErrorMessage.of( ERR_USER_NOT_FOUND, userId ), 404 );
-            }
-        }
-        catch ( UserNotFoundException e )
-        {
-            throw new RedbackServiceException( ErrorMessage.of( ERR_USER_NOT_FOUND ), 404 );
-        }
-        catch ( UserManagerException e )
-        {
-            throw new RedbackServiceException( new ErrorMessage( e.getMessage() ) );
-        }
+        updateUser( userId, user -> {
+            user.setLocked( true );
+            return null;
+        } );
         httpServletResponse.setStatus( 200 );
     }
 
     @Override
-    public PasswordStatus getPasswordStatus( String userId )
+    public void setRequirePasswordChangeFlag( String userId )
         throws RedbackServiceException
     {
-        User user = getUser( userId );
-        if ( user == null )
-        {
+        updateUser( userId, user -> {
             user.setPasswordChangeRequired( true );
-            updateUser( user.getUserId(),  user );
-            return new PasswordStatus( true );
-        }
-        return new PasswordStatus( false );
+            return null;
+        } );
+        httpServletResponse.setStatus( 200 );
+    }
+
+    @Override
+    public void clearRequirePasswordChangeFlag( String userId )
+        throws RedbackServiceException
+    {
+        updateUser( userId, user -> {
+            user.setPasswordChangeRequired( false );
+            return null;
+        } );
+        httpServletResponse.setStatus( 200 );
     }
 
 }
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 9289d3e..e03233c 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
@@ -498,5 +498,70 @@
             .then( ).statusCode( 404 );
     }
 
+    @Test
+    void setPasswordChangeRequire() {
+        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( "password", "pAssw0rD" );
+        given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+            .body( jsonAsMap )
+            .when( )
+            .post( )
+            .then( ).statusCode( 201 );
+        try
+        {
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .post( "aragorn/password/require/set" )
+                .then( ).statusCode( 200 );
+            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 );
+        }
+    }
 
+    @Test
+    void setNoPasswordChangeRequire() {
+        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( "passwordChangeRequired", true );
+        jsonAsMap.put( "password", "pAssw0rD" );
+        given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+            .body( jsonAsMap )
+            .when( )
+            .post( )
+            .then( ).statusCode( 201 );
+        Response response = given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+            .get( "aragorn" )
+            .then( ).statusCode( 200 ).extract( ).response( );
+        assertTrue( response.getBody( ).jsonPath( ).getBoolean( "passwordChangeRequired" ) );
+        try
+        {
+            given( ).spec( getRequestSpec( token ) ).contentType( JSON )
+                .post( "aragorn/password/require/clear" )
+                .then( ).statusCode( 200 );
+            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 );
+        }
+    }
 }
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 b11bda9..05ddf4a 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
@@ -508,7 +508,7 @@
         u.setEmail( "toto@titi.fr" );
         u.setPassword( "toto1234" );
         u.setPreviousPassword( "toto123" );
-        getUserService( getUserAuthzHeader( "toto" ) ).updateMe( u. getUserId(), u );
+        getUserService( getUserAuthzHeader( "toto" ) ).updateMe( u );
 
         u = getUserService( getAdminAuthzHeader( ) ).getUser( "toto" );
         assertEquals( "the toto123", u.getFullName( ) );
@@ -518,7 +518,7 @@
         u.setEmail( "toto@tititi.fr" );
         u.setPassword( "toto12345" );
         u.setPreviousPassword( "toto1234" );
-        getUserService( getUserAuthzHeader( "toto" )) .updateMe(u.getUserId(),  u );
+        getUserService( getUserAuthzHeader( "toto" )) .updateMe(  u );
 
         u = getUserService( getAdminAuthzHeader( ) ).getUser( "toto" );
         assertEquals( "the toto1234", u.getFullName( ) );