From b3f826b9643d15bd10e9b1d3a9a3403e3f360fc8 Mon Sep 17 00:00:00 2001 From: alainbodiguel Date: Fri, 3 May 2024 14:00:29 +0200 Subject: [PATCH] Deactivate/update user --- .../java/io/arlas/iam/core/AuthService.java | 2 +- .../arlas/iam/impl/HibernateAuthService.java | 30 +++++-- .../iam/rest/model/input/UpdateUserDef.java | 4 + .../iam/rest/service/IAMRestService.java | 83 +++++++++++++++++-- .../java/io/arlas/iam/test/AuthEndpoints.java | 34 ++++++-- .../java/io/arlas/iam/test/AuthITUser.java | 25 ++++-- 6 files changed, 149 insertions(+), 29 deletions(-) diff --git a/arlas-iam-core/src/main/java/io/arlas/iam/core/AuthService.java b/arlas-iam-core/src/main/java/io/arlas/iam/core/AuthService.java index d8796d2..d7a9928 100644 --- a/arlas-iam-core/src/main/java/io/arlas/iam/core/AuthService.java +++ b/arlas-iam-core/src/main/java/io/arlas/iam/core/AuthService.java @@ -28,7 +28,7 @@ public interface AuthService { User resetUserPassword(UUID userId, String resetToken, String password) throws SendEmailException, NotFoundException; Optional readUser(UUID userId); - User updateUser(User user, String oldPassword, String newPassword) throws NonMatchingPasswordException; + User updateUser(User user, String oldPassword, String newPassword, String firstName, String lastName, String locale, String timezone) throws NonMatchingPasswordException; void deleteUser(UUID userId) throws NotAllowedException; Optional activateUser(UUID userId); diff --git a/arlas-iam-core/src/main/java/io/arlas/iam/impl/HibernateAuthService.java b/arlas-iam-core/src/main/java/io/arlas/iam/impl/HibernateAuthService.java index 1d75bf4..804d1ad 100644 --- a/arlas-iam-core/src/main/java/io/arlas/iam/impl/HibernateAuthService.java +++ b/arlas-iam-core/src/main/java/io/arlas/iam/impl/HibernateAuthService.java @@ -216,6 +216,7 @@ public void initDatabase() { admin.setLocale(initConf.locale); admin.setTimezone(initConf.timezone); admin.setVerified(true); + admin.setActive(true); admin = userDao.createUser(admin); admin.setRoles(importDefaultAdminRole(admin)); } else { @@ -265,7 +266,7 @@ public Optional readUser(UUID userId) { @Override public User readUser(UUID userId, boolean checkActiveVerified) throws NotFoundException { Optional user = readUser(userId); - if (user.isPresent() && user.get().isVerified() && user.get().isActive()) { + if (user.isPresent() && user.get().isVerified() && (!checkActiveVerified || user.get().isActive())) { return user.get(); } else { throw new NotFoundException("User not found."); @@ -393,14 +394,29 @@ public void deleteApiKey(User user, UUID ownerId, UUID oid, UUID apiKeyId) throw } @Override - public User updateUser(User user, String oldPassword, String newPassword) + public User updateUser(User user, String oldPassword, String newPassword, String firstName, String lastName, String locale, String timezone) throws NonMatchingPasswordException { - if (matches(oldPassword, user.getPassword())) { - user.setPassword(encode(newPassword)); - return userDao.updateUser(user); - } else { - throw new NonMatchingPasswordException("Old password does not match."); + if (oldPassword != null && newPassword != null) { + if (matches(oldPassword, user.getPassword())) { + user.setPassword(encode(newPassword)); + } else { + throw new NonMatchingPasswordException("Old password does not match."); + } + } + if (firstName != null && !firstName.equals(user.getFirstName())) { + user.setFirstName(firstName); } + if (lastName != null && !lastName.equals(user.getLastName())) { + user.setLastName(lastName); + } + if (locale != null && !locale.equals(user.getLocale())) { + user.setLocale(locale); + } + if (timezone != null && !timezone.equals(user.getTimezone())) { + user.setTimezone(timezone); + } + + return userDao.updateUser(user); } @Override diff --git a/arlas-iam-rest/src/main/java/io/arlas/iam/rest/model/input/UpdateUserDef.java b/arlas-iam-rest/src/main/java/io/arlas/iam/rest/model/input/UpdateUserDef.java index 5fcb82f..bbd27b6 100644 --- a/arlas-iam-rest/src/main/java/io/arlas/iam/rest/model/input/UpdateUserDef.java +++ b/arlas-iam-rest/src/main/java/io/arlas/iam/rest/model/input/UpdateUserDef.java @@ -3,6 +3,10 @@ public class UpdateUserDef { public String oldPassword; public String newPassword; + public String locale; + public String timezone; + public String firstName; + public String lastName; public UpdateUserDef(){} } diff --git a/arlas-iam-rest/src/main/java/io/arlas/iam/rest/service/IAMRestService.java b/arlas-iam-rest/src/main/java/io/arlas/iam/rest/service/IAMRestService.java index dad46d2..2495ff9 100644 --- a/arlas-iam-rest/src/main/java/io/arlas/iam/rest/service/IAMRestService.java +++ b/arlas-iam-rest/src/main/java/io/arlas/iam/rest/service/IAMRestService.java @@ -526,6 +526,72 @@ public Response readUser( .build(); } + @Timed + @Path("users/{id}/activate") + @POST + @Produces(UTF8JSON) + @Consumes(UTF8JSON) + @Operation( + security = @SecurityRequirement(name = "JWT"), + summary = "Activate the given user" + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "202", description = "Successful operation", + content = @Content(schema = @Schema(implementation = ArlasMessage.class))), + @ApiResponse(responseCode = "500", description = "Arlas Error.", + content = @Content(schema = @Schema(implementation = Error.class)))}) + + @UnitOfWork + public Response activateUser( + @Context UriInfo uriInfo, + @Context HttpHeaders headers, + @Context HttpServletRequest request, + + @Parameter(name = "id", required = true) + @PathParam(value = "id") String id + ) { + authService.activateUser(UUID.fromString(id)); + logUAM(request, headers, "users", "activate-user-account"); + return Response.accepted(uriInfo.getRequestUriBuilder().build()) + .entity(new ArlasMessage("User activated.")) + .type(MediaType.APPLICATION_JSON_TYPE) + .build(); + + } + + @Timed + @Path("users/{id}/deactivate") + @POST + @Produces(UTF8JSON) + @Consumes(UTF8JSON) + @Operation( + security = @SecurityRequirement(name = "JWT"), + summary = "Deactivate the given user" + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "202", description = "Successful operation", + content = @Content(schema = @Schema(implementation = ArlasMessage.class))), + @ApiResponse(responseCode = "500", description = "Arlas Error.", + content = @Content(schema = @Schema(implementation = Error.class)))}) + + @UnitOfWork + public Response deactivateUser( + @Context UriInfo uriInfo, + @Context HttpHeaders headers, + @Context HttpServletRequest request, + + @Parameter(name = "id", required = true) + @PathParam(value = "id") String id + ) throws NotAllowedException { + authService.deactivateUser(UUID.fromString(id)); + logUAM(request, headers, "users", "deactivate-user-account"); + return Response.accepted(uriInfo.getRequestUriBuilder().build()) + .entity(new ArlasMessage("User deactivated.")) + .type(MediaType.APPLICATION_JSON_TYPE) + .build(); + + } + @Timed @Path("users/{id}") @DELETE @@ -538,8 +604,6 @@ public Response readUser( @ApiResponses(value = { @ApiResponse(responseCode = "202", description = "Successful operation", content = @Content(schema = @Schema(implementation = ArlasMessage.class))), - @ApiResponse(responseCode = "400", description = "Non matching passwords.", - content = @Content(schema = @Schema(implementation = Error.class))), @ApiResponse(responseCode = "500", description = "Arlas Error.", content = @Content(schema = @Schema(implementation = Error.class)))}) @@ -569,7 +633,7 @@ public Response deleteUser( @Consumes(UTF8JSON) @Operation( security = @SecurityRequirement(name = "JWT"), - summary = "Update the logged in user" + summary = "Update the given user (absent attribute - null - are not updated)." ) @ApiResponses(value = { @ApiResponse(responseCode = "201", description = "Successful operation", @@ -592,14 +656,15 @@ public Response updateUser( @PathParam(value = "id") String id, @Parameter(name = "updateDef", required = true) - @NotNull @Valid UpdateUserDef updateDef + @NotNull @Valid UpdateUserDef ud ) throws NotFoundException, NonMatchingPasswordException { + LOGGER.info(ud.toString()); Response response = Response.created(uriInfo.getRequestUriBuilder().build()) - .entity(new UserData(authService.updateUser(getUser(headers, id), updateDef.oldPassword, updateDef.newPassword))) + .entity(new UserData(authService.updateUser(getUser(headers, id, false), ud.oldPassword, ud.newPassword, ud.firstName, ud.lastName, ud.locale, ud.timezone))) .type(MediaType.APPLICATION_JSON_TYPE) .build(); - logUAM(request, headers, "users", "change-password"); + logUAM(request, headers, "users", "update-user"); return response; } @@ -1979,7 +2044,11 @@ protected User getUser(HttpHeaders headers) throws NotFoundException { } protected User getUser(HttpHeaders headers, String id) throws NotFoundException { - checkLoggedInUser(headers, id); + return getUser(headers, id, true); + } + + protected User getUser(HttpHeaders headers, String id, boolean checkSame) throws NotFoundException { + if (checkSame) { checkLoggedInUser(headers, id); } return getUser(headers); } diff --git a/arlas-iam-tests/src/test/java/io/arlas/iam/test/AuthEndpoints.java b/arlas-iam-tests/src/test/java/io/arlas/iam/test/AuthEndpoints.java index d109c0e..61b234b 100644 --- a/arlas-iam-tests/src/test/java/io/arlas/iam/test/AuthEndpoints.java +++ b/arlas-iam-tests/src/test/java/io/arlas/iam/test/AuthEndpoints.java @@ -105,9 +105,9 @@ protected Response logout(String userId) { .delete(arlasAppPath.concat("session")); } - protected Response changePassword(String userId, String oldPassword, String password) { + protected Response changePassword(String actingId, String userId, String oldPassword, String password) { return given() - .header(AUTH_HEADER, getToken(userId)) + .header(AUTH_HEADER, getToken(actingId)) .contentType("application/json") .pathParam("id", userId) .body(String.format(""" @@ -116,6 +116,17 @@ protected Response changePassword(String userId, String oldPassword, String pass .put(arlasAppPath.concat("users/{id}")); } + protected Response updateUser(String userId, String firstName, String lastName, String locale, String timezone) { + return given() + .header(AUTH_HEADER, getToken(userId)) + .contentType("application/json") + .pathParam("id", userId) + .body(String.format(""" + {"firstName": "%s", "lastName": "%s", "locale": "%s", "timezone": "%s"} + """, firstName, lastName, locale, timezone)) + .put(arlasAppPath.concat("users/{id}")); + } + protected String getToken(String userId) { if (userId.equals(userId1)) { return "bearer " + token1; @@ -138,15 +149,20 @@ protected Response getUser(String actingId, String id) { .get(arlasAppPath.concat("users/{id}")); } - protected Response updateUser(String id, String p1, String p2) { + protected Response deactivateUser(String actingId, String targetId) { return given() - .header(AUTH_HEADER, getToken(userId1)) - .pathParam("id", id) - .body(String.format(""" - {"oldPassword":"%s","newPassword":"%s"} - """, p1, p2)) + .header(AUTH_HEADER, getToken(actingId)) + .pathParam("id", targetId) .contentType("application/json") - .put(arlasAppPath.concat("users/{id}")); + .post(arlasAppPath.concat("users/{id}/deactivate")); + } + + protected Response activateUser(String actingId, String targetId) { + return given() + .header(AUTH_HEADER, getToken(actingId)) + .pathParam("id", targetId) + .contentType("application/json") + .post(arlasAppPath.concat("users/{id}/activate")); } protected Response deleteUser(String actingId, String targetId) { diff --git a/arlas-iam-tests/src/test/java/io/arlas/iam/test/AuthITUser.java b/arlas-iam-tests/src/test/java/io/arlas/iam/test/AuthITUser.java index def8b30..8599fce 100644 --- a/arlas-iam-tests/src/test/java/io/arlas/iam/test/AuthITUser.java +++ b/arlas-iam-tests/src/test/java/io/arlas/iam/test/AuthITUser.java @@ -65,7 +65,7 @@ public void test013LoginWithInvalidPassword() { @Test public void test015ChangePassword() { - changePassword(userId1, "secret", "newsecret").then().statusCode(201) + changePassword(userId1, userId1, "secret", "newsecret").then().statusCode(201) .body("email", equalTo(USER1)); logout(USER1).then().statusCode(200); @@ -79,12 +79,17 @@ public void test015ChangePassword() { @Test public void test016ChangeWrongPassword() { - changePassword(userId1, "othersecret", "newsecret").then().statusCode(400); + changePassword(userId1, userId1, "othersecret", "newsecret").then().statusCode(400); } @Test - public void test017ChangePasswordOtherUser() { - updateUser(userId2, "password2", "newpassword2").then().statusCode(404); + public void test018UpdateUser() { + updateUser(userId1, "John", "Doe", "fr_FR", "Europe/London").then().statusCode(201) + .body("firstName", equalTo("John")) + .body("lastName", equalTo("Doe")) + .body("locale", equalTo("fr_FR")) + .body("timezone", equalTo("Europe/London")) + ; } // @Test @@ -465,7 +470,17 @@ public void test903DeleteUserNotSelf() { } @Test - public void test904DeleteUserSelf() { + public void test904DeactivateUser() { + deactivateUser(userId1, userId2).then().statusCode(202); + } + + @Test + public void test905ActivateUser() { + activateUser(userId1, userId2).then().statusCode(202); + } + + @Test + public void test906DeleteUserSelf() { deleteUser(userId1, userId1).then().statusCode(202); deleteUser(userId2, userId2).then().statusCode(202); }