From 9f7d0d64c7073f0adab2a06627dbed369e79011a Mon Sep 17 00:00:00 2001 From: Mike McKay <mfmckay@wustl.edu> Date: Thu, 5 May 2016 17:38:50 -0500 Subject: [PATCH] Added support for a bunch of new user REST calls to support the UI Will designed. --- .../java/org/nrg/xapi/model/users/User.java | 30 +++ .../org/nrg/xapi/rest/users/UsersApi.java | 250 ++++++++++++++++-- 2 files changed, 256 insertions(+), 24 deletions(-) diff --git a/src/main/java/org/nrg/xapi/model/users/User.java b/src/main/java/org/nrg/xapi/model/users/User.java index 862323e0..1e88b433 100644 --- a/src/main/java/org/nrg/xapi/model/users/User.java +++ b/src/main/java/org/nrg/xapi/model/users/User.java @@ -33,6 +33,8 @@ public class User { _salt = ""; _lastModified = null; _authorization = null; + _isEnabled = user.isEnabled(); + _isVerified = user.isVerified(); } public User(final XdatUser user) { @@ -119,6 +121,32 @@ public class User { _isAdmin = isAdmin; } + /** + * Whether the user is enabled. + **/ + @ApiModelProperty(value = "Whether the user is enabled.") + @JsonProperty("enabled") + public boolean isEnabled() { + return _isEnabled; + } + + public void setEnabled(final boolean isEnabled) { + _isEnabled = isEnabled; + } + + /** + * Whether the user is verified. + **/ + @ApiModelProperty(value = "Whether the user is verified.") + @JsonProperty("verified") + public boolean isVerified() { + return _isVerified; + } + + public void setVerified(final boolean isVerified) { + _isVerified = isVerified; + } + /** * The user's primary database (deprecated). **/ @@ -243,4 +271,6 @@ public class User { private String _salt = null; private Date _lastModified = null; private UserAuth _authorization = null; + private boolean _isEnabled; + private boolean _isVerified; } diff --git a/src/main/java/org/nrg/xapi/rest/users/UsersApi.java b/src/main/java/org/nrg/xapi/rest/users/UsersApi.java index 0b2cc17a..5dec06a5 100644 --- a/src/main/java/org/nrg/xapi/rest/users/UsersApi.java +++ b/src/main/java/org/nrg/xapi/rest/users/UsersApi.java @@ -5,10 +5,15 @@ import org.apache.commons.lang3.StringUtils; import org.nrg.framework.annotations.XapiRestController; import org.nrg.xapi.model.users.User; import org.nrg.xapi.rest.NotFoundException; +import org.nrg.xdat.XDAT; import org.nrg.xdat.rest.AbstractXnatRestApi; +import org.nrg.xdat.security.UserGroupI; +import org.nrg.xdat.security.helpers.Groups; +import org.nrg.xdat.security.helpers.Roles; import org.nrg.xdat.security.helpers.Users; import org.nrg.xdat.security.user.exceptions.UserInitException; import org.nrg.xdat.security.user.exceptions.UserNotFoundException; +import org.nrg.xdat.services.AliasTokenService; import org.nrg.xft.event.EventDetails; import org.nrg.xft.event.EventUtils; import org.nrg.xft.security.UserI; @@ -19,8 +24,7 @@ import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import java.util.ArrayList; -import java.util.List; +import java.util.*; @Api(description = "The XNAT POC User Management API") @XapiRestController @@ -36,9 +40,35 @@ public class UsersApi extends AbstractXnatRestApi { return new ResponseEntity<List<String>>(new ArrayList<>(Users.getAllLogins()), HttpStatus.OK); } + @ApiOperation(value = "Get list of user profiles.", notes = "The users' profiles function returns a list of all users of the XNAT system with brief information about each.", response = User.class, responseContainer = "List") + @ApiResponses({@ApiResponse(code = 200, message = "An array of user profiles"), @ApiResponse(code = 500, message = "Unexpected error")}) + @RequestMapping(value = {"/profiles"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = RequestMethod.GET) + @ResponseBody + public ResponseEntity<List<Map<String,String>>> usersProfilesGet() { + List<UserI> users = Users.getUsers(); + List<Map<String,String>> userMaps = new ArrayList<Map<String,String>>(); + for(UserI user : users){ + try{ + Map<String,String> userMap = new HashMap<String,String>(); + userMap.put("firstname",user.getFirstname()); + userMap.put("lastname",user.getLastname()); + userMap.put("username",user.getUsername()); + userMap.put("email",user.getEmail()); + userMap.put("id",String.valueOf(user.getID())); + userMap.put("enabled",String.valueOf(user.isEnabled())); + userMap.put("verified",String.valueOf(user.isVerified())); + userMaps.add(userMap); + } + catch(Exception e){ + _log.error("", e); + } + } + return new ResponseEntity<List<Map<String,String>>>(userMaps, HttpStatus.OK); + } + @ApiOperation(value = "Gets the user with the specified user ID.", notes = "Returns the serialized user object with the specified user ID.", response = User.class) @ApiResponses({@ApiResponse(code = 200, message = "User successfully retrieved."), @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."), @ApiResponse(code = 403, message = "Not authorized to view this user."), @ApiResponse(code = 404, message = "User not found."), @ApiResponse(code = 500, message = "Unexpected error")}) - @RequestMapping(value = {"/{id}"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.GET}) + @RequestMapping(value = {"/profiles/{id}"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.GET}) public ResponseEntity<User> usersIdGet(@ApiParam(value = "ID of the user to fetch", required = true) @PathVariable("id") String id) { HttpStatus status = isPermitted(id); if (status != null) { @@ -56,38 +86,49 @@ public class UsersApi extends AbstractXnatRestApi { } } - @ApiOperation(value = "Creates or updates the user object with the specified user ID.", notes = "Returns the updated serialized user object with the specified user ID.", response = User.class) + @ApiOperation(value = "Creates or updates the user object with the specified username.", notes = "Returns the updated serialized user object with the specified username.", response = User.class) @ApiResponses({@ApiResponse(code = 200, message = "User successfully created or updated."), @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."), @ApiResponse(code = 403, message = "Not authorized to create or update this user."), @ApiResponse(code = 404, message = "User not found."), @ApiResponse(code = 500, message = "Unexpected error")}) - @RequestMapping(value = {"/{id}"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.PUT}) - public ResponseEntity<User> usersIdPut(@ApiParam(value = "The ID of the user to create or update.", required = true) @PathVariable("id") String id, @RequestBody User model) throws NotFoundException { - HttpStatus status = isPermitted(id); + @RequestMapping(value = {"/profiles/{id}"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.PUT}) + public ResponseEntity<User> usersIdPut(@ApiParam(value = "The username of the user to create or update.", required = true) @PathVariable("id") String username, @RequestBody User model) throws NotFoundException { + HttpStatus status = isPermitted(username); if (status != null) { return new ResponseEntity<>(status); } - final UserI user; + UserI user = null; try { - user = Users.getUser(id); - } catch (UserInitException e) { - _log.error("An error occurred initializing the user " + id, e); - return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); - } catch (UserNotFoundException e) { - return new ResponseEntity<>(HttpStatus.NOT_FOUND); + user = Users.getUser(username); + } catch (Exception e) { + user = null; } if (user == null) { - return new ResponseEntity<>(HttpStatus.NOT_FOUND); + //Create new User + user = Users.createUser(); + user.setLogin(username); } - if ((StringUtils.isNotBlank(model.getFirstName())) && (StringUtils.equals(model.getFirstName(), user.getFirstname()))) { + if ((StringUtils.isNotBlank(model.getFirstName())) && (!StringUtils.equals(model.getFirstName(), user.getFirstname()))) { user.setFirstname(model.getFirstName()); } - if ((StringUtils.isNotBlank(model.getLastName())) && (StringUtils.equals(model.getLastName(), user.getLastname()))) { + if ((StringUtils.isNotBlank(model.getLastName())) && (!StringUtils.equals(model.getLastName(), user.getLastname()))) { user.setLastname(model.getLastName()); } - if ((StringUtils.isNotBlank(model.getEmail())) && (StringUtils.equals(model.getEmail(), user.getEmail()))) { + if ((StringUtils.isNotBlank(model.getEmail())) && (!StringUtils.equals(model.getEmail(), user.getEmail()))) { user.setEmail(model.getEmail()); } - if (StringUtils.isNotBlank(model.getPassword())) { - user.setPassword(model.getPassword()); + if (model.isEnabled()!=user.isEnabled()) { + user.setEnabled(model.isEnabled()); + if(!model.isEnabled()){ + //When a user is disabled, deactivate all their AliasTokens + try { + XDAT.getContextService().getBean(AliasTokenService.class).deactivateAllTokensForUser(user.getLogin()); + } catch (Exception e) { + _log.error("", e); + } + } + } + if (model.isVerified()!=user.isVerified()) { + user.setVerified(model.isVerified()); } + try { Users.save(user, getSessionUser(), false, new EventDetails(EventUtils.CATEGORY.DATA, EventUtils.TYPE.WEB_SERVICE, Event.Modified, "", "")); return new ResponseEntity<>(HttpStatus.OK); @@ -99,7 +140,7 @@ public class UsersApi extends AbstractXnatRestApi { @ApiOperation(value = "Returns whether the user with the specified user ID is enabled.", notes = "Returns true or false based on whether the specified user is enabled or not.", response = Boolean.class) @ApiResponses({@ApiResponse(code = 200, message = "User enabled status successfully retrieved."), @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."), @ApiResponse(code = 403, message = "Not authorized to view this user."), @ApiResponse(code = 404, message = "User not found."), @ApiResponse(code = 500, message = "Unexpected error")}) - @RequestMapping(value = {"/{id}/enabled"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.GET}) + @RequestMapping(value = {"/profiles/{id}/enabled"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.GET}) public ResponseEntity<Boolean> usersIdEnabledGet(@ApiParam(value = "The ID of the user to retrieve the enabled status for.", required = true) @PathVariable("id") String id) { HttpStatus status = isPermitted(id); if (status != null) { @@ -121,7 +162,7 @@ public class UsersApi extends AbstractXnatRestApi { @ApiOperation(value = "Sets the user's enabled state.", notes = "Sets the enabled state of the user with the specified user ID to the value of the flag parameter.", response = Boolean.class) @ApiResponses({@ApiResponse(code = 200, message = "User enabled status successfully set."), @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."), @ApiResponse(code = 403, message = "Not authorized to enable or disable this user."), @ApiResponse(code = 404, message = "User not found."), @ApiResponse(code = 500, message = "Unexpected error")}) - @RequestMapping(value = {"/{id}/enabled/{flag}"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.PUT}) + @RequestMapping(value = {"/profiles/{id}/enabled/{flag}"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.PUT}) public ResponseEntity<Boolean> usersIdEnabledFlagPut(@ApiParam(value = "ID of the user to fetch", required = true) @PathVariable("id") String id, @ApiParam(value = "The value to set for the enabled status.", required = true) @PathVariable("flag") Boolean flag) { HttpStatus status = isPermitted(id); if (status != null) { @@ -150,7 +191,7 @@ public class UsersApi extends AbstractXnatRestApi { @ApiOperation(value = "Returns whether the user with the specified user ID is verified.", notes = "Returns true or false based on whether the specified user is verified or not.", response = Boolean.class) @ApiResponses({@ApiResponse(code = 200, message = "User verified status successfully retrieved."), @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."), @ApiResponse(code = 403, message = "Not authorized to view this user."), @ApiResponse(code = 404, message = "User not found."), @ApiResponse(code = 500, message = "Unexpected error")}) - @RequestMapping(value = {"/{id}/verified"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.GET}) + @RequestMapping(value = {"/profiles/{id}/verified"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.GET}) public ResponseEntity<Boolean> usersIdVerifiedGet(@ApiParam(value = "The ID of the user to retrieve the verified status for.", required = true) @PathVariable("id") String id) { HttpStatus status = isPermitted(id); if (status != null) { @@ -172,7 +213,7 @@ public class UsersApi extends AbstractXnatRestApi { @ApiOperation(value = "Sets the user's verified state.", notes = "Sets the verified state of the user with the specified user ID to the value of the flag parameter.", response = Boolean.class) @ApiResponses({@ApiResponse(code = 200, message = "User verified status successfully set."), @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."), @ApiResponse(code = 403, message = "Not authorized to verify or un-verify this user."), @ApiResponse(code = 404, message = "User not found."), @ApiResponse(code = 500, message = "Unexpected error")}) - @RequestMapping(value = {"/{id}/verified/{flag}"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.PUT}) + @RequestMapping(value = {"/profiles/{id}/verified/{flag}"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.PUT}) public ResponseEntity<Boolean> usersIdVerifiedFlagPut(@ApiParam(value = "ID of the user to fetch", required = true) @PathVariable("id") String id, @ApiParam(value = "The value to set for the verified status.", required = true) @PathVariable("flag") Boolean flag) { HttpStatus status = isPermitted(id); if (status != null) { @@ -199,6 +240,167 @@ public class UsersApi extends AbstractXnatRestApi { } } + @ApiOperation(value = "Returns the roles for the user with the specified user ID.", notes = "Returns a collection of the user's roles.", response = Collection.class) + @ApiResponses({@ApiResponse(code = 200, message = "User roles successfully retrieved."), @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."), @ApiResponse(code = 403, message = "Not authorized to view this user."), @ApiResponse(code = 404, message = "User not found."), @ApiResponse(code = 500, message = "Unexpected error")}) + @RequestMapping(value = {"/profiles/{id}/roles"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.GET}) + public ResponseEntity<Collection<String>> usersIdRolesGet(@ApiParam(value = "The ID of the user to retrieve the roles for.", required = true) @PathVariable("id") String id) { + HttpStatus status = isPermitted(id); + if (status != null) { + return new ResponseEntity<>(status); + } + try { + final UserI user = Users.getUser(id); + if (user == null) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + Collection<String> roles = Roles.getRoles(user); + return new ResponseEntity<>(roles, HttpStatus.OK); + } catch (UserInitException e) { + _log.error("An error occurred initializing the user " + id, e); + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } catch (UserNotFoundException e) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } + + @ApiOperation(value = "Adds a role to a user.", notes = "Assigns a new role to a user.", response = Boolean.class) + @ApiResponses({@ApiResponse(code = 200, message = "User role successfully added."), @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."), @ApiResponse(code = 403, message = "Not authorized to enable or disable this user."), @ApiResponse(code = 404, message = "User not found."), @ApiResponse(code = 500, message = "Unexpected error")}) + @RequestMapping(value = {"/profiles/{id}/addrole/{role}"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.PUT}) + public ResponseEntity<Boolean> usersIdAddRole(@ApiParam(value = "ID of the user to add a role to", required = true) @PathVariable("id") String id, @ApiParam(value = "The user's new role.", required = true) @PathVariable("role") String role) { + HttpStatus status = isPermitted(id); + if (status != null) { + return new ResponseEntity<>(status); + } + try { + final UserI user = Users.getUser(id); + if (user == null) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + try { + Roles.addRole(getSessionUser(),user,role); + return new ResponseEntity<>(HttpStatus.OK); + } catch (Exception e) { + _log.error("Error occurred adding role "+role+" to user " + user.getLogin()+"."); + } + return new ResponseEntity<>(false, HttpStatus.INTERNAL_SERVER_ERROR); + } catch (UserInitException e) { + _log.error("An error occurred initializing the user " + id, e); + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } catch (UserNotFoundException e) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } + + + @ApiOperation(value = "Remove a user's role.", notes = "Removes a user's role.", response = Boolean.class) + @ApiResponses({@ApiResponse(code = 200, message = "User role successfully removed."), @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."), @ApiResponse(code = 403, message = "Not authorized to enable or disable this user."), @ApiResponse(code = 404, message = "User not found."), @ApiResponse(code = 500, message = "Unexpected error")}) + @RequestMapping(value = {"/profiles/{id}/removerole/{role}"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.PUT}) + public ResponseEntity<Boolean> usersIdRemoveRole(@ApiParam(value = "ID of the user to delete a role from", required = true) @PathVariable("id") String id, @ApiParam(value = "The user role to delete.", required = true) @PathVariable("role") String role) { + HttpStatus status = isPermitted(id); + if (status != null) { + return new ResponseEntity<>(status); + } + try { + final UserI user = Users.getUser(id); + if (user == null) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + try { + Roles.deleteRole(getSessionUser(),user,role); + return new ResponseEntity<>(HttpStatus.OK); + } catch (Exception e) { + _log.error("Error occurred removing role "+role+" from user " + user.getLogin()+"."); + } + return new ResponseEntity<>(false, HttpStatus.INTERNAL_SERVER_ERROR); + } catch (UserInitException e) { + _log.error("An error occurred initializing the user " + id, e); + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } catch (UserNotFoundException e) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } + + @ApiOperation(value = "Returns the groups for the user with the specified user ID.", notes = "Returns a collection of the user's groups.", response = Set.class) + @ApiResponses({@ApiResponse(code = 200, message = "User groups successfully retrieved."), @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."), @ApiResponse(code = 403, message = "Not authorized to view this user."), @ApiResponse(code = 404, message = "User not found."), @ApiResponse(code = 500, message = "Unexpected error")}) + @RequestMapping(value = {"/profiles/{id}/groups"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.GET}) + public ResponseEntity<Set<String>> usersIdGroupsGet(@ApiParam(value = "The ID of the user to retrieve the groups for.", required = true) @PathVariable("id") String id) { + HttpStatus status = isPermitted(id); + if (status != null) { + return new ResponseEntity<>(status); + } + try { + final UserI user = Users.getUser(id); + if (user == null) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + Map<String, UserGroupI> groups = Groups.getGroupsForUser(user); + return new ResponseEntity<>(groups.keySet(), HttpStatus.OK); + } catch (UserInitException e) { + _log.error("An error occurred initializing the user " + id, e); + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } catch (UserNotFoundException e) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } + + @ApiOperation(value = "Adds a user to a group.", notes = "Assigns user to a group.", response = Boolean.class) + @ApiResponses({@ApiResponse(code = 200, message = "User successfully added to group."), @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."), @ApiResponse(code = 403, message = "Not authorized to enable or disable this user."), @ApiResponse(code = 404, message = "User not found."), @ApiResponse(code = 500, message = "Unexpected error")}) + @RequestMapping(value = {"/profiles/{id}/addgroup/{group}"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.PUT}) + public ResponseEntity<Boolean> usersIdAddGroup(@ApiParam(value = "ID of the user to add to a group", required = true) @PathVariable("id") String id, @ApiParam(value = "The user's new group.", required = true) @PathVariable("group") String group) { + HttpStatus status = isPermitted(id); + if (status != null) { + return new ResponseEntity<>(status); + } + try { + final UserI user = Users.getUser(id); + if (user == null) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + try { + Groups.addUserToGroup(group, user, getSessionUser(),null); + return new ResponseEntity<>(HttpStatus.OK); + } catch (Exception e) { + _log.error("Error occurred adding user " + user.getLogin()+ " to group "+group+"."); + } + return new ResponseEntity<>(false, HttpStatus.INTERNAL_SERVER_ERROR); + } catch (UserInitException e) { + _log.error("An error occurred initializing the user " + id, e); + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } catch (UserNotFoundException e) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } + + + @ApiOperation(value = "Removes a user from a group.", notes = "Removes a user from a group.", response = Boolean.class) + @ApiResponses({@ApiResponse(code = 200, message = "User's group successfully removed."), @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."), @ApiResponse(code = 403, message = "Not authorized to enable or disable this user."), @ApiResponse(code = 404, message = "User not found."), @ApiResponse(code = 500, message = "Unexpected error")}) + @RequestMapping(value = {"/profiles/{id}/removegroup/{group}"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.PUT}) + public ResponseEntity<Boolean> usersIdRemoveGroup(@ApiParam(value = "ID of the user to remove from group", required = true) @PathVariable("id") String id, @ApiParam(value = "The group to remove the user from.", required = true) @PathVariable("group") String group) { + HttpStatus status = isPermitted(id); + if (status != null) { + return new ResponseEntity<>(status); + } + try { + final UserI user = Users.getUser(id); + if (user == null) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + try { + Groups.removeUserFromGroup(user, group, null); + return new ResponseEntity<>(HttpStatus.OK); + } catch (Exception e) { + _log.error("Error occurred removing user "+user.getLogin()+" from group " + group+"."); + } + return new ResponseEntity<>(false, HttpStatus.INTERNAL_SERVER_ERROR); + } catch (UserInitException e) { + _log.error("An error occurred initializing the user " + id, e); + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } catch (UserNotFoundException e) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } + + @SuppressWarnings("unused") public static class Event { public static String Added = "Added User"; -- GitLab