Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deactivate/update user #174

Merged
merged 1 commit into from
May 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public interface AuthService {
User resetUserPassword(UUID userId, String resetToken, String password) throws SendEmailException, NotFoundException;

Optional<User> 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<User> activateUser(UUID userId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -265,7 +266,7 @@ public Optional<User> readUser(UUID userId) {
@Override
public User readUser(UUID userId, boolean checkActiveVerified) throws NotFoundException {
Optional<User> 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.");
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(){}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)))})

Expand Down Expand Up @@ -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",
Expand All @@ -592,14 +656,14 @@ public Response updateUser(
@PathParam(value = "id") String id,

@Parameter(name = "updateDef", required = true)
@NotNull @Valid UpdateUserDef updateDef
@NotNull @Valid UpdateUserDef ud

) throws NotFoundException, NonMatchingPasswordException {
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;
}

Expand Down Expand Up @@ -1979,7 +2043,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);
}

Expand Down
34 changes: 25 additions & 9 deletions arlas-iam-tests/src/test/java/io/arlas/iam/test/AuthEndpoints.java
Original file line number Diff line number Diff line change
Expand Up @@ -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("""
Expand All @@ -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;
Expand All @@ -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) {
Expand Down
25 changes: 20 additions & 5 deletions arlas-iam-tests/src/test/java/io/arlas/iam/test/AuthITUser.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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
Expand Down Expand Up @@ -465,7 +470,17 @@ public void test903DeleteUserNotSelf() {
}

@Test
public void test904DeleteUserSelf() {
public void test904DeactivateUser() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what happens if a user deactivate/activate itself ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

he'll have to ask an administrator to reactivate him

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);
}
Expand Down
Loading