PageRenderTime 69ms CodeModel.GetById 2ms app.highlight 58ms RepoModel.GetById 1ms app.codeStats 1ms

/src/main/java/org/nrg/xapi/rest/users/UsersApi.java

https://bitbucket.org/radiologics/xnat-web-old-v2
Java | 936 lines | 850 code | 70 blank | 16 comment | 119 complexity | 899edda1997a94a71ac1bd73463a03f6 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1/*
  2 * web: org.nrg.xapi.rest.users.UsersApi
  3 * XNAT http://www.xnat.org
  4 * Copyright (c) 2005-2017, Washington University School of Medicine and Howard Hughes Medical Institute
  5 * All Rights Reserved
  6 *
  7 * Released under the Simplified BSD.
  8 */
  9
 10package org.nrg.xapi.rest.users;
 11
 12import com.google.common.base.Function;
 13import com.google.common.collect.Lists;
 14import io.swagger.annotations.*;
 15import lombok.extern.slf4j.Slf4j;
 16import org.apache.commons.lang3.BooleanUtils;
 17import org.apache.commons.lang3.StringUtils;
 18import org.nrg.framework.annotations.XapiRestController;
 19import org.nrg.framework.exceptions.NrgServiceError;
 20import org.nrg.framework.exceptions.NrgServiceRuntimeException;
 21import org.nrg.framework.utilities.Patterns;
 22import org.nrg.xapi.authorization.UserGroupXapiAuthorization;
 23import org.nrg.xapi.authorization.UserResourceXapiAuthorization;
 24import org.nrg.xapi.exceptions.DataFormatException;
 25import org.nrg.xapi.exceptions.NotFoundException;
 26import org.nrg.xapi.exceptions.ResourceAlreadyExistsException;
 27import org.nrg.xapi.model.users.User;
 28import org.nrg.xapi.model.users.UserFactory;
 29import org.nrg.xapi.rest.*;
 30import org.nrg.xdat.security.UserGroupI;
 31import org.nrg.xdat.security.helpers.Groups;
 32import org.nrg.xdat.security.helpers.Users;
 33import org.nrg.xdat.security.services.PermissionsServiceI;
 34import org.nrg.xdat.security.services.RoleHolder;
 35import org.nrg.xdat.security.services.UserManagementServiceI;
 36import org.nrg.xdat.security.user.exceptions.UserInitException;
 37import org.nrg.xdat.security.user.exceptions.UserNotFoundException;
 38import org.nrg.xdat.services.AliasTokenService;
 39import org.nrg.xdat.turbine.utils.AdminUtils;
 40import org.nrg.xft.event.EventDetails;
 41import org.nrg.xft.event.EventUtils;
 42import org.nrg.xft.security.UserI;
 43import org.springframework.beans.factory.annotation.Autowired;
 44import org.springframework.http.ResponseEntity;
 45import org.springframework.jdbc.core.RowMapper;
 46import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
 47import org.springframework.security.core.session.SessionInformation;
 48import org.springframework.security.core.session.SessionRegistry;
 49import org.springframework.security.core.userdetails.UserDetails;
 50import org.springframework.web.bind.annotation.PathVariable;
 51import org.springframework.web.bind.annotation.RequestBody;
 52import org.springframework.web.bind.annotation.RequestMapping;
 53import org.springframework.web.bind.annotation.ResponseBody;
 54
 55import javax.annotation.Nullable;
 56import javax.servlet.http.HttpSession;
 57import java.sql.ResultSet;
 58import java.sql.SQLException;
 59import java.sql.Timestamp;
 60import java.util.*;
 61
 62import static org.nrg.xdat.security.helpers.AccessLevel.*;
 63import static org.springframework.http.HttpStatus.*;
 64import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
 65import static org.springframework.web.bind.annotation.RequestMethod.*;
 66
 67@SuppressWarnings({"SqlNoDataSourceInspection", "SqlResolve"})
 68@Api(description = "User Management API")
 69@XapiRestController
 70@RequestMapping(value = "/users")
 71@Slf4j
 72public class UsersApi extends AbstractXapiRestController {
 73    public static final RowMapper<User> USER_ROW_MAPPER = new RowMapper<User>() {
 74        @Override
 75        public User mapRow(final ResultSet resultSet, final int index) throws SQLException {
 76            final int       userId                  = resultSet.getInt("id");
 77            final String    username                = resultSet.getString("username");
 78            final String    firstName               = resultSet.getString("firstName");
 79            final String    lastName                = resultSet.getString("lastName");
 80            final String    email                   = resultSet.getString("email");
 81            final Timestamp lastModified            = resultSet.getTimestamp("last_modified");
 82            final Date      lastModifiedDate        = lastModified != null ? new Date(lastModified.getTime()) : null;
 83            final boolean   enabled                 = resultSet.getInt("enabled") == 1;
 84            final boolean   verified                = resultSet.getInt("verified") == 1;
 85            final Timestamp lastSuccessfulLogin     = resultSet.getTimestamp("lastSuccessfulLogin");
 86            final Date      lastSuccessfulLoginDate = lastSuccessfulLogin != null ? new Date(lastSuccessfulLogin.getTime()) : null;
 87            return new User(userId, username, firstName, lastName, email, null, null, null, true, lastModifiedDate, null, enabled, verified, lastSuccessfulLoginDate);
 88        }
 89    };
 90
 91    public static final String QUERY_USER_PROFILES = "SELECT enabled, login AS username, xdat_user_id AS id, firstname AS firstName, lastname AS lastName, email, verified, last_modified, auth.max_login AS lastSuccessfulLogin FROM xdat_user JOIN xdat_user_meta_data ON xdat_user.user_info=xdat_user_meta_data.meta_data_id JOIN (SELECT xdat_username, max(last_successful_login) max_login FROM xhbm_xdat_user_auth GROUP BY xdat_username) auth ON xdat_user.login=auth.xdat_username ORDER BY xdat_user.xdat_user_id";
 92    public static final String QUERY_CURRENT_USERS = "SELECT enabled, login AS username, xdat_user_id AS id, firstname AS firstName, lastname AS lastName, email, verified, last_modified, auth.max_login AS lastSuccessfulLogin FROM xdat_user JOIN xdat_user_meta_data ON xdat_user.user_info=xdat_user_meta_data.meta_data_id JOIN (SELECT xdat_username, max(last_successful_login) max_login FROM xhbm_xdat_user_auth GROUP BY xdat_username) auth ON xdat_user.login=auth.xdat_username WHERE (xdat_user.enabled=1 OR (max_login > (CURRENT_DATE - INTERVAL '1 year') OR (max_login IS NULL AND (xdat_user_meta_data.last_modified > (CURRENT_DATE - INTERVAL '1 year') ) ) )) ORDER BY xdat_user.xdat_user_id";
 93    public static final String QUERY_USER_PROFILE  = "SELECT enabled, login AS username, xdat_user_id AS id, firstname AS firstName, lastname AS lastName, email, verified, last_modified, auth.max_login AS lastSuccessfulLogin FROM xdat_user JOIN xdat_user_meta_data ON xdat_user.user_info=xdat_user_meta_data.meta_data_id JOIN (SELECT xdat_username, max(last_successful_login) max_login FROM xhbm_xdat_user_auth GROUP BY xdat_username) auth ON xdat_user.login=auth.xdat_username WHERE xdat_user.login=:username";
 94
 95    @Autowired
 96    public UsersApi(final UserManagementServiceI userManagementService,
 97                    final UserFactory factory,
 98                    final RoleHolder roleHolder,
 99                    final SessionRegistry sessionRegistry,
100                    final AliasTokenService aliasTokenService,
101                    final PermissionsServiceI permissionsService,
102                    final NamedParameterJdbcTemplate jdbcTemplate) {
103        super(userManagementService, roleHolder);
104        _sessionRegistry = sessionRegistry;
105        _aliasTokenService = aliasTokenService;
106        _permissionsService = permissionsService;
107        _factory = factory;
108        _jdbcTemplate = jdbcTemplate;
109    }
110
111    @ApiOperation(value = "Get list of users.", notes = "The primary users function returns a list of all users of the XNAT system. This includes just the username and nothing else. You can retrieve a particular user by adding the username to the REST API URL or a list of users with abbreviated user profiles by calling /xapi/users/profiles.", response = String.class, responseContainer = "List")
112    @ApiResponses({@ApiResponse(code = 200, message = "A list of usernames."),
113                   @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
114                   @ApiResponse(code = 403, message = "You do not have sufficient permissions to access the list of usernames."),
115                   @ApiResponse(code = 500, message = "An unexpected error occurred.")})
116    @XapiRequestMapping(produces = APPLICATION_JSON_VALUE, method = GET, restrictTo = Authorizer)
117    @AuthDelegate(UserResourceXapiAuthorization.class)
118    @ResponseBody
119    public ResponseEntity<List<String>> usersGet() {
120        return new ResponseEntity<List<String>>(new ArrayList<>(Users.getAllLogins()), OK);
121    }
122
123    @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")
124    @ApiResponses({@ApiResponse(code = 200, message = "A list of user profiles."),
125                   @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
126                   @ApiResponse(code = 403, message = "You do not have sufficient permissions to access the list of users."),
127                   @ApiResponse(code = 500, message = "An unexpected error occurred.")})
128    @XapiRequestMapping(value = "profiles", produces = APPLICATION_JSON_VALUE, method = GET, restrictTo = Authorizer)
129    @AuthDelegate(UserResourceXapiAuthorization.class)
130    @ResponseBody
131    public ResponseEntity<List<User>> usersProfilesGet() {
132        return new ResponseEntity<>(_jdbcTemplate.query(QUERY_USER_PROFILES, USER_ROW_MAPPER), OK);
133    }
134
135    @ApiOperation(value = "Get user profile.", notes = "The user profile function returns a user of the XNAT system with brief information.", response = User.class, responseContainer = "List")
136    @ApiResponses({@ApiResponse(code = 200, message = "A user profile."),
137                   @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
138                   @ApiResponse(code = 403, message = "You do not have sufficient permissions to access the user profile."),
139                   @ApiResponse(code = 500, message = "An unexpected error occurred.")})
140    @XapiRequestMapping(value = "profile/{username}", produces = APPLICATION_JSON_VALUE, method = GET, restrictTo = Authorizer)
141    @AuthDelegate(UserResourceXapiAuthorization.class)
142    @ResponseBody
143    public ResponseEntity<User> usersProfileGet(@ApiParam(value = "ID of the user to fetch", required = true) @PathVariable("username") @Username final String username) {
144        List<User> usersList = null;
145        String     regex     = "^[a-zA-Z0-9]+[a-zA-Z0-9._-]*$";
146        if (username.matches(regex)) {
147            usersList = _jdbcTemplate.query(QUERY_USER_PROFILE, new HashMap<String, Object>() {{
148                put("username", username);
149            }}, USER_ROW_MAPPER);
150        }
151        if (usersList != null && usersList.size() > 0) {
152            return new ResponseEntity<>(usersList.get(0), OK);
153        } else {
154            return new ResponseEntity<>(OK);
155        }
156    }
157
158    @ApiOperation(value = "Get list of users who are enabled or who have interacted with the site somewhat recently.", 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")
159    @ApiResponses({@ApiResponse(code = 200, message = "A list of user profiles."),
160                   @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
161                   @ApiResponse(code = 403, message = "You do not have sufficient permissions to access the list of usernames."),
162                   @ApiResponse(code = 500, message = "An unexpected error occurred.")})
163    @XapiRequestMapping(value = "current", produces = APPLICATION_JSON_VALUE, method = GET, restrictTo = Authorizer)
164    @AuthDelegate(UserResourceXapiAuthorization.class)
165    @ResponseBody
166    public ResponseEntity<List<User>> currentUsersProfilesGet() {
167        return new ResponseEntity<>(_jdbcTemplate.query(QUERY_CURRENT_USERS, USER_ROW_MAPPER), OK);
168    }
169
170    @ApiOperation(value = "Get list of active users.", notes = "Returns a map of usernames for users that have at least one currently active session, i.e. logged in or associated with a valid application session. The number of active sessions and a list of the session IDs is associated with each user.", response = Map.class, responseContainer = "Map")
171    @ApiResponses({@ApiResponse(code = 200, message = "A list of active users."),
172                   @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
173                   @ApiResponse(code = 403, message = "You do not have sufficient permissions to access the list of usernames."),
174                   @ApiResponse(code = 500, message = "An unexpected error occurred.")})
175    @XapiRequestMapping(value = "active", produces = APPLICATION_JSON_VALUE, method = GET, restrictTo = Admin)
176    @ResponseBody
177    public ResponseEntity<Map<String, Map<String, Object>>> getActiveUsers() {
178        final Map<String, Map<String, Object>> activeUsers = new HashMap<>();
179        for (final Object principal : _sessionRegistry.getAllPrincipals()) {
180            final String username;
181            if (principal instanceof String) {
182                username = (String) principal;
183            } else if (principal instanceof UserDetails) {
184                username = ((UserDetails) principal).getUsername();
185            } else {
186                username = principal.toString();
187            }
188            final List<SessionInformation> sessions = _sessionRegistry.getAllSessions(principal, false);
189
190            // Sometimes there are no sessions, which is weird but OK, we don't want to see those entries.
191            if (sessions.isEmpty()) {
192                continue;
193            }
194
195            final Map<String, Object> sessionData = new HashMap<>();
196            sessionData.put("sessions", Lists.transform(sessions, INFO_TO_ID_FUNCTION));
197            sessionData.put("count", sessions.size());
198
199            activeUsers.put(username, sessionData);
200        }
201        return new ResponseEntity<>(activeUsers, OK);
202    }
203
204    @ApiOperation(value = "Get information about active sessions for the indicated user.", notes = "Returns a map containing a list of session IDs and usernames for users that have at least one currently active session, i.e. logged in or associated with a valid application session. This also includes the number of active sessions for each user.", response = String.class, responseContainer = "List")
205    @ApiResponses({@ApiResponse(code = 200, message = "A list of active users."),
206                   @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
207                   @ApiResponse(code = 403, message = "You do not have sufficient permissions to access this user's sessions."),
208                   @ApiResponse(code = 404, message = "The indicated user has no active sessions or is not a valid user."),
209                   @ApiResponse(code = 500, message = "An unexpected error occurred.")})
210    @XapiRequestMapping(value = "active/{username}", produces = APPLICATION_JSON_VALUE, method = GET, restrictTo = User)
211    @ResponseBody
212    public ResponseEntity<List<String>> getUserActiveSessions(@ApiParam(value = "ID of the user to fetch", required = true) @PathVariable("username") @Username final String username) {
213        final Object located = locatePrincipalByUsername(username);
214        if (located == null) {
215            return new ResponseEntity<>(NOT_FOUND);
216        }
217        final List<SessionInformation> sessions = _sessionRegistry.getAllSessions(located, false);
218        if (sessions.isEmpty()) {
219            return new ResponseEntity<>(NOT_MODIFIED);
220        }
221        return new ResponseEntity<>(Lists.transform(sessions, INFO_TO_ID_FUNCTION), OK);
222
223    }
224
225    @ApiOperation(value = "Gets the user with the specified user ID.", notes = "Returns the serialized user object with the specified user ID.", response = User.class)
226    @ApiResponses({@ApiResponse(code = 200, message = "User successfully retrieved."),
227                   @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
228                   @ApiResponse(code = 403, message = "Not authorized to view this user."),
229                   @ApiResponse(code = 404, message = "User not found."),
230                   @ApiResponse(code = 500, message = "An unexpected error occurred.")})
231    @XapiRequestMapping(value = "{username}", produces = APPLICATION_JSON_VALUE, method = GET, restrictTo = Authorizer)
232    @AuthDelegate(UserResourceXapiAuthorization.class)
233    public ResponseEntity<User> getUser(@ApiParam(value = "Username of the user to fetch.", required = true) @PathVariable("username") @Username final String username) {
234        try {
235            final UserI user = getUserManagementService().getUser(username);
236            return user == null ? new ResponseEntity<User>(NOT_FOUND) : new ResponseEntity<>(_factory.getUser(user), OK);
237        } catch (UserInitException e) {
238            log.error("An error occurred initializing the user " + username, e);
239            return new ResponseEntity<>(INTERNAL_SERVER_ERROR);
240        } catch (UserNotFoundException e) {
241            return new ResponseEntity<>(NOT_FOUND);
242        }
243    }
244
245    @ApiOperation(value = "Creates a new user from the request body.", notes = "Returns the newly created user object.", response = User.class)
246    @ApiResponses({@ApiResponse(code = 201, message = "User successfully created."),
247                   @ApiResponse(code = 400, message = "The submitted data was invalid."),
248                   @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
249                   @ApiResponse(code = 403, message = "Not authorized to update this user."),
250                   @ApiResponse(code = 500, message = "An unexpected error occurred.")})
251    @XapiRequestMapping(produces = APPLICATION_JSON_VALUE, method = POST, restrictTo = Admin)
252    public ResponseEntity<User> createUser(@RequestBody final User model) throws DataFormatException, ResourceAlreadyExistsException {
253        validateUser(model);
254
255        final UserI user = getUserManagementService().createUser();
256
257        if (user == null) {
258            throw new NrgServiceRuntimeException(NrgServiceError.Unknown, "Failed to create a user object for user " + model.getUsername());
259        }
260
261        user.setLogin(model.getUsername());
262        user.setFirstname(model.getFirstName());
263        user.setLastname(model.getLastName());
264        user.setEmail(model.getEmail());
265        user.setPassword(model.getPassword());
266        user.setAuthorization(model.getAuthorization());
267
268        if (model.isEnabled() != null) {
269            user.setEnabled(model.isEnabled());
270        }
271        if (model.isVerified() != null) {
272            user.setVerified(model.isVerified());
273        }
274
275        try {
276            getUserManagementService().save(user, getSessionUser(), false, new EventDetails(EventUtils.CATEGORY.DATA, EventUtils.TYPE.WEB_SERVICE, Event.Added, "Requested by user " + getSessionUser().getUsername(), "Created new user " + user.getUsername() + " through XAPI user management API."));
277
278            if (model.isVerified() && model.isEnabled()) {
279                //When a user is enabled and verified, send a new user email
280                try {
281                    AdminUtils.sendNewUserEmailMessage(user.getUsername(), user.getEmail());
282                } catch (Exception e) {
283                    log.error("An error occurred trying to send email to the admin: new user '{}' created with email '{}'", user.getUsername(), user.getEmail(), e);
284                }
285            }
286            return new ResponseEntity<>(_factory.getUser(user), CREATED);
287        } catch (Exception e) {
288            log.error("Error occurred modifying user " + user.getLogin());
289        }
290        return new ResponseEntity<>(INTERNAL_SERVER_ERROR);
291    }
292
293    @ApiOperation(value = "Updates the user object with the specified username.", notes = "Returns the updated serialized user object with the specified username.", response = User.class)
294    @ApiResponses({@ApiResponse(code = 200, message = "User successfully updated."),
295                   @ApiResponse(code = 304, message = "The user object was not modified because no attributes were changed."),
296                   @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
297                   @ApiResponse(code = 403, message = "Not authorized to update this user."),
298                   @ApiResponse(code = 404, message = "User not found."),
299                   @ApiResponse(code = 500, message = "An unexpected error occurred.")})
300    @XapiRequestMapping(value = "{username}", produces = APPLICATION_JSON_VALUE, method = PUT, restrictTo = User)
301    public ResponseEntity<User> updateUser(@ApiParam(value = "The username of the user to create or update.", required = true) @PathVariable("username") @Username final String username, @RequestBody final User model) throws NotFoundException, UserInitException {
302        final UserI user;
303        try {
304            user = getUserManagementService().getUser(username);
305        } catch (UserNotFoundException e) {
306            throw new NotFoundException("User with username " + username + " was not found.");
307        }
308
309        if (user == null) {
310            throw new NrgServiceRuntimeException(NrgServiceError.Unknown, "Failed to retrieve user object for user " + username);
311        }
312        boolean oldEnabledFlag  = user.isEnabled();
313        boolean oldVerifiedFlag = user.isVerified();
314
315        boolean isDirty = false;
316        if ((StringUtils.isNotBlank(model.getUsername())) && (!StringUtils.equals(user.getUsername(), model.getUsername()))) {
317            return new ResponseEntity<>(BAD_REQUEST);
318        }
319        if ((StringUtils.isNotBlank(model.getFirstName())) && (!StringUtils.equals(user.getFirstname(), model.getFirstName()))) {
320            user.setFirstname(model.getFirstName());
321            isDirty = true;
322        }
323        if ((StringUtils.isNotBlank(model.getLastName())) && (!StringUtils.equals(user.getLastname(), model.getLastName()))) {
324            user.setLastname(model.getLastName());
325            isDirty = true;
326        }
327        if ((StringUtils.isNotBlank(model.getEmail())) && (!StringUtils.equals(user.getEmail(), model.getEmail()))) {
328            user.setEmail(model.getEmail());
329            isDirty = true;
330        }
331        // Don't do password compare: we can't.
332        if (StringUtils.isNotBlank(model.getPassword())) {
333            user.setPassword(model.getPassword());
334            isDirty = true;
335        }
336        if (model.getAuthorization() != null && !model.getAuthorization().equals(user.getAuthorization())) {
337            user.setAuthorization(model.getAuthorization());
338            isDirty = true;
339        }
340        final Boolean enabled = model.isEnabled();
341        if (enabled != null && enabled != user.isEnabled()) {
342            user.setEnabled(enabled);
343            if (!enabled) {
344                //When a user is disabled, deactivate all their AliasTokens
345                try {
346                    _aliasTokenService.deactivateAllTokensForUser(user.getLogin());
347                } catch (Exception e) {
348                    log.error("", e);
349                }
350            }
351            isDirty = true;
352        }
353        final Boolean verified = model.isVerified();
354        if (verified != null && verified != user.isVerified()) {
355            user.setVerified(verified);
356            isDirty = true;
357        }
358
359        if (!isDirty) {
360            return new ResponseEntity<>(NOT_MODIFIED);
361        }
362
363        try {
364            getUserManagementService().save(user, getSessionUser(), false, new EventDetails(EventUtils.CATEGORY.DATA, EventUtils.TYPE.WEB_SERVICE, Event.Modified, "", ""));
365            if (BooleanUtils.toBooleanDefaultIfNull(model.isVerified(), false) && BooleanUtils.toBooleanDefaultIfNull(model.isEnabled(), false) && (!oldEnabledFlag || !oldVerifiedFlag)) {
366                //When a user is enabled and verified, send a new user email
367                try {
368                    AdminUtils.sendNewUserEmailMessage(user.getUsername(), user.getEmail());
369                } catch (Exception e) {
370                    log.error("", e);
371                }
372            }
373            return new ResponseEntity<>(_factory.getUser(user), OK);
374        } catch (Exception e) {
375            log.error("Error occurred modifying user '{}'", user.getUsername(), e);
376            return new ResponseEntity<>(INTERNAL_SERVER_ERROR);
377        }
378    }
379
380    @ApiOperation(value = "Invalidates all active sessions associated with the specified username.", notes = "Returns a list of session IDs that were invalidated.", response = String.class, responseContainer = "List")
381    @ApiResponses({@ApiResponse(code = 200, message = "User successfully invalidated."),
382                   @ApiResponse(code = 304, message = "Indicated user has no active sessions, so no action was taken."),
383                   @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
384                   @ApiResponse(code = 403, message = "Not authorized to invalidate this user's sessions."),
385                   @ApiResponse(code = 404, message = "User not found."),
386                   @ApiResponse(code = 500, message = "An unexpected error occurred.")})
387    @XapiRequestMapping(value = "active/{username}", produces = APPLICATION_JSON_VALUE, method = DELETE, restrictTo = User)
388    public ResponseEntity<List<String>> invalidateUser(final HttpSession current, @ApiParam(value = "The username of the user to invalidate.", required = true) @PathVariable("username") @Username final String username) {
389        final UserI  user;
390        final String currentSessionId;
391        if (StringUtils.equals(getSessionUser().getUsername(), username)) {
392            user = getSessionUser();
393            currentSessionId = current.getId();
394        } else {
395            try {
396                user = getUserManagementService().getUser(username);
397                if (user == null) {
398                    return new ResponseEntity<>(NOT_FOUND);
399                }
400                currentSessionId = null;
401            } catch (UserInitException e) {
402                log.error("An error occurred initializing the user " + username, e);
403                return new ResponseEntity<>(INTERNAL_SERVER_ERROR);
404            } catch (UserNotFoundException e) {
405                return new ResponseEntity<>(NOT_FOUND);
406            }
407        }
408        final Object located = locatePrincipalByUsername(user.getUsername());
409        if (located == null) {
410            return new ResponseEntity<>(NOT_MODIFIED);
411        }
412        final List<SessionInformation> sessions = _sessionRegistry.getAllSessions(located, false);
413        if (sessions.size() == 0) {
414            return new ResponseEntity<>(NOT_MODIFIED);
415        }
416
417        final List<String> sessionIds = Lists.transform(sessions, INFO_TO_ID_INVALIDATOR_FUNCTION);
418        sessionIds.remove(currentSessionId);
419
420        return new ResponseEntity<>(sessionIds, OK);
421    }
422
423    @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)
424    @ApiResponses({@ApiResponse(code = 200, message = "User enabled status successfully retrieved."),
425                   @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
426                   @ApiResponse(code = 403, message = "Not authorized to get whether this user is enabled."),
427                   @ApiResponse(code = 404, message = "User not found."),
428                   @ApiResponse(code = 500, message = "An unexpected error occurred.")})
429    @XapiRequestMapping(value = "{username}/enabled", produces = APPLICATION_JSON_VALUE, method = GET, restrictTo = User)
430    public ResponseEntity<Boolean> usersIdEnabledGet(@ApiParam(value = "The ID of the user to retrieve the enabled status for.", required = true) @PathVariable("username") @Username final String username) {
431        try {
432            final UserI user = getUserManagementService().getUser(username);
433            if (user == null) {
434                return new ResponseEntity<>(NOT_FOUND);
435            }
436            return new ResponseEntity<>(user.isEnabled(), OK);
437        } catch (UserInitException e) {
438            log.error("An error occurred initializing the user " + username, e);
439            return new ResponseEntity<>(INTERNAL_SERVER_ERROR);
440        } catch (UserNotFoundException e) {
441            return new ResponseEntity<>(NOT_FOUND);
442        }
443    }
444
445    @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)
446    @ApiResponses({@ApiResponse(code = 200, message = "User enabled status successfully set."),
447                   @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
448                   @ApiResponse(code = 403, message = "Not authorized to enable or disable this user."),
449                   @ApiResponse(code = 404, message = "User not found."),
450                   @ApiResponse(code = 500, message = "An unexpected error occurred.")})
451    @XapiRequestMapping(value = "{username}/enabled/{flag}", produces = APPLICATION_JSON_VALUE, method = PUT, restrictTo = Admin)
452    public ResponseEntity<Boolean> usersIdEnabledFlagPut(@ApiParam(value = "ID of the user to fetch", required = true) @PathVariable("username") @Username final String username, @ApiParam(value = "The value to set for the enabled status.", required = true) @PathVariable("flag") Boolean flag) {
453        try {
454            final UserI user           = getUserManagementService().getUser(username);
455            boolean     oldEnabledFlag = user.isEnabled();
456            user.setEnabled(flag);
457            try {
458                getUserManagementService().save(user, getSessionUser(), false, new EventDetails(EventUtils.CATEGORY.DATA, EventUtils.TYPE.WEB_SERVICE, flag ? Event.Enabled : Event.Disabled, "", ""));
459                if (flag && !oldEnabledFlag && user.isVerified()) {
460                    //When a user is enabled, send a new user email if they're also verified
461                    try {
462                        AdminUtils.sendNewUserEmailMessage(username, user.getEmail());
463                    } catch (Exception e) {
464                        log.error("", e);
465                    }
466                }
467                return new ResponseEntity<>(OK);
468            } catch (Exception e) {
469                log.error("Error occurred " + (flag ? "enabling" : "disabling") + " user " + user.getLogin());
470            }
471            return new ResponseEntity<>(false, INTERNAL_SERVER_ERROR);
472        } catch (UserInitException e) {
473            log.error("An error occurred initializing the user " + username, e);
474            return new ResponseEntity<>(INTERNAL_SERVER_ERROR);
475        } catch (UserNotFoundException e) {
476            return new ResponseEntity<>(NOT_FOUND);
477        }
478    }
479
480    @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)
481    @ApiResponses({@ApiResponse(code = 200, message = "User verified status successfully retrieved."),
482                   @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
483                   @ApiResponse(code = 403, message = "Not authorized to view this user."),
484                   @ApiResponse(code = 404, message = "User not found."),
485                   @ApiResponse(code = 500, message = "An unexpected error occurred.")})
486    @XapiRequestMapping(value = "{username}/verified", produces = APPLICATION_JSON_VALUE, method = GET, restrictTo = User)
487    public ResponseEntity<Boolean> usersIdVerifiedGet(@ApiParam(value = "The ID of the user to retrieve the verified status for.", required = true) @PathVariable("username") @Username final String username) {
488        try {
489            final UserI user = getUserManagementService().getUser(username);
490            if (user == null) {
491                return new ResponseEntity<>(NOT_FOUND);
492            }
493            return new ResponseEntity<>(user.isVerified(), OK);
494        } catch (UserInitException e) {
495            log.error("An error occurred initializing the user " + username, e);
496            return new ResponseEntity<>(INTERNAL_SERVER_ERROR);
497        } catch (UserNotFoundException e) {
498            return new ResponseEntity<>(NOT_FOUND);
499        }
500    }
501
502    @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)
503    @ApiResponses({@ApiResponse(code = 200, message = "User verified status successfully set."),
504                   @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
505                   @ApiResponse(code = 403, message = "Not authorized to verify or un-verify this user."),
506                   @ApiResponse(code = 404, message = "User not found."),
507                   @ApiResponse(code = 500, message = "An unexpected error occurred.")})
508    @XapiRequestMapping(value = "{username}/verified/{flag}", produces = APPLICATION_JSON_VALUE, method = PUT, restrictTo = Admin)
509    public ResponseEntity<Boolean> usersIdVerifiedFlagPut(@ApiParam(value = "ID of the user to fetch", required = true) @PathVariable("username") @Username final String username, @ApiParam(value = "The value to set for the verified status.", required = true) @PathVariable("flag") Boolean flag) {
510        try {
511            final UserI user = getUserManagementService().getUser(username);
512            if (user == null) {
513                return new ResponseEntity<>(NOT_FOUND);
514            }
515            boolean oldVerifiedFlag = user.isVerified();
516            user.setVerified(flag);
517            try {
518                getUserManagementService().save(user, getSessionUser(), false, new EventDetails(EventUtils.CATEGORY.DATA, EventUtils.TYPE.WEB_SERVICE, flag ? Event.Enabled : Event.Disabled, "", ""));
519                if (flag && !oldVerifiedFlag && user.isEnabled()) {
520                    //When a user is verified, send a new user email if they're also enabled
521                    try {
522                        AdminUtils.sendNewUserEmailMessage(username, user.getEmail());
523                    } catch (Exception e) {
524                        log.error("", e);
525                    }
526                }
527                return new ResponseEntity<>(OK);
528            } catch (Exception e) {
529                log.error("Error occurred " + (flag ? "enabling" : "disabling") + " user " + user.getLogin());
530            }
531            return new ResponseEntity<>(false, INTERNAL_SERVER_ERROR);
532        } catch (UserInitException e) {
533            log.error("An error occurred initializing the user " + username, e);
534            return new ResponseEntity<>(INTERNAL_SERVER_ERROR);
535        } catch (UserNotFoundException e) {
536            return new ResponseEntity<>(NOT_FOUND);
537        }
538    }
539
540    @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)
541    @ApiResponses({@ApiResponse(code = 200, message = "User roles successfully retrieved."),
542                   @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
543                   @ApiResponse(code = 403, message = "Not authorized to view this user."),
544                   @ApiResponse(code = 404, message = "User not found."),
545                   @ApiResponse(code = 500, message = "An unexpected error occurred.")})
546    @XapiRequestMapping(value = "{username}/roles", produces = APPLICATION_JSON_VALUE, method = GET, restrictTo = User)
547    public ResponseEntity<Collection<String>> usersIdRolesGet(@ApiParam(value = "The ID of the user to retrieve the roles for.", required = true) @PathVariable("username") @Username final String username) {
548        final Collection<String> roles = getUserRoles(username);
549        return roles != null ? new ResponseEntity<>(roles, OK) : new ResponseEntity<Collection<String>>(FORBIDDEN);
550    }
551
552    @ApiOperation(value = "Adds one or more roles to a user.", notes = "Assigns one or more new roles to a user.", response = String.class, responseContainer = "List")
553    @ApiResponses({@ApiResponse(code = 200, message = "All specified user roles successfully added."),
554                   @ApiResponse(code = 202, message = "Some user roles successfully added, but some may have failed. Check the return value for roles that the service was unable to add."),
555                   @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
556                   @ApiResponse(code = 403, message = "Not authorized to add roles to this user."),
557                   @ApiResponse(code = 404, message = "User not found."),
558                   @ApiResponse(code = 500, message = "An unexpected error occurred.")})
559    @XapiRequestMapping(value = "{username}/roles", produces = APPLICATION_JSON_VALUE, method = PUT, restrictTo = Admin)
560    public ResponseEntity<Collection<String>> usersIdAddRoles(@ApiParam(value = "ID of the user to add a role to", required = true) @PathVariable("username") @Username final String username,
561                                                              @ApiParam(value = "The user's new roles.", required = true) @RequestBody final List<String> roles) {
562        final Collection<String> failed = new ArrayList<>();
563
564        try {
565            final UserI user = getUserManagementService().getUser(username);
566            if (user == null) {
567                return new ResponseEntity<>(NOT_FOUND);
568            }
569            for (final String role : roles) {
570                try {
571                    getRoleHolder().addRole(getSessionUser(), user, role);
572                } catch (Exception e) {
573                    failed.add(role);
574                    log.error("Error occurred adding role " + role + " to user " + user.getLogin() + ".", e);
575                }
576            }
577        } catch (UserInitException e) {
578            log.error("An error occurred initializing the user " + username, e);
579            return new ResponseEntity<>(INTERNAL_SERVER_ERROR);
580        } catch (UserNotFoundException e) {
581            return new ResponseEntity<>(NOT_FOUND);
582        }
583
584        if (failed.size() == 0) {
585            return new ResponseEntity<>(OK);
586        }
587
588        return new ResponseEntity<>(failed, ACCEPTED);
589    }
590
591    @ApiOperation(value = "Removes one or more roles from a user.", notes = "Removes one or more new roles from a user.", response = String.class, responseContainer = "List")
592    @ApiResponses({@ApiResponse(code = 200, message = "All specified user roles successfully removed."),
593                   @ApiResponse(code = 202, message = "Some user roles successfully removed, but some may have failed. Check the return value for roles that the service was unable to remove."),
594                   @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
595                   @ApiResponse(code = 403, message = "Not authorized to remove roles from this user."),
596                   @ApiResponse(code = 404, message = "User not found."),
597                   @ApiResponse(code = 500, message = "An unexpected error occurred.")})
598    @XapiRequestMapping(value = "{username}/roles", produces = APPLICATION_JSON_VALUE, method = DELETE, restrictTo = Admin)
599    public ResponseEntity<Collection<String>> usersIdRemoveRoles(@ApiParam(value = "ID of the user to remove role from", required = true) @PathVariable("username") @Username final String username,
600                                                                 @ApiParam(value = "The roles to be removed.", required = true) @RequestBody final List<String> roles) {
601        final Collection<String> failed = new ArrayList<>();
602
603        try {
604            final UserI user = getUserManagementService().getUser(username);
605            if (user == null) {
606                return new ResponseEntity<>(NOT_FOUND);
607            }
608            for (final String role : roles) {
609                try {
610                    getRoleHolder().deleteRole(getSessionUser(), user, role);
611                } catch (Exception e) {
612                    failed.add(role);
613                    log.error("Error occurred remove role " + role + " from user " + user.getLogin() + ".", e);
614                }
615            }
616        } catch (UserInitException e) {
617            log.error("An error occurred initializing the user " + username, e);
618            return new ResponseEntity<>(INTERNAL_SERVER_ERROR);
619        } catch (UserNotFoundException e) {
620            return new ResponseEntity<>(NOT_FOUND);
621        }
622
623        if (failed.size() == 0) {
624            return new ResponseEntity<>(OK);
625        }
626
627        return new ResponseEntity<>(failed, ACCEPTED);
628    }
629
630    @ApiOperation(value = "Adds a role to a user.", notes = "Assigns a new role to a user.", response = Boolean.class)
631    @ApiResponses({@ApiResponse(code = 200, message = "User role successfully added."),
632                   @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
633                   @ApiResponse(code = 403, message = "Not authorized to add a role to this user."),
634                   @ApiResponse(code = 404, message = "User not found."),
635                   @ApiResponse(code = 500, message = "An unexpected error occurred.")})
636    @XapiRequestMapping(value = "{username}/roles/{role}", produces = APPLICATION_JSON_VALUE, method = PUT, restrictTo = Admin)
637    public ResponseEntity<Boolean> usersIdAddRole(@ApiParam(value = "ID of the user to add a role to", required = true) @PathVariable("username") @Username final String username,
638                                                  @ApiParam(value = "The user's new role.", required = true) @PathVariable("role") final String role) {
639        try {
640            final UserI user = getUserManagementService().getUser(username);
641            if (user == null) {
642                return new ResponseEntity<>(NOT_FOUND);
643            }
644            try {
645                getRoleHolder().addRole(getSessionUser(), user, role);
646                return new ResponseEntity<>(OK);
647            } catch (Exception e) {
648                log.error("Error occurred adding role " + role + " to user " + user.getLogin() + ".");
649            }
650            return new ResponseEntity<>(false, INTERNAL_SERVER_ERROR);
651        } catch (UserInitException e) {
652            log.error("An error occurred initializing the user " + username, e);
653            return new ResponseEntity<>(INTERNAL_SERVER_ERROR);
654        } catch (UserNotFoundException e) {
655            return new ResponseEntity<>(NOT_FOUND);
656        }
657    }
658
659    @ApiOperation(value = "Remove a user's role.", notes = "Removes a user's role.", response = Boolean.class)
660    @ApiResponses({@ApiResponse(code = 200, message = "User role successfully removed."),
661                   @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
662                   @ApiResponse(code = 403, message = "Not authorized to remove a role from this user."),
663                   @ApiResponse(code = 404, message = "User not found."),
664                   @ApiResponse(code = 500, message = "An unexpected error occurred.")})
665    @XapiRequestMapping(value = "{username}/roles/{role}", produces = APPLICATION_JSON_VALUE, method = DELETE, restrictTo = Admin)
666    public ResponseEntity<Boolean> usersIdRemoveRole(@ApiParam(value = "ID of the user to delete a role from", required = true) @PathVariable("username") @Username final String username,
667                                                     @ApiParam(value = "The user role to delete.", required = true) @PathVariable("role") String role) {
668        try {
669            final UserI user = getUserManagementService().getUser(username);
670            if (user == null) {
671                return new ResponseEntity<>(NOT_FOUND);
672            }
673            try {
674                getRoleHolder().deleteRole(getSessionUser(), user, role);
675                return new ResponseEntity<>(OK);
676            } catch (Exception e) {
677                log.error("Error occurred removing role " + role + " from user " + user.getLogin() + ".");
678            }
679            return new ResponseEntity<>(false, INTERNAL_SERVER_ERROR);
680        } catch (UserInitException e) {
681            log.error("An error occurred initializing the user " + username, e);
682            return new ResponseEntity<>(INTERNAL_SERVER_ERROR);
683        } catch (UserNotFoundException e) {
684            return new ResponseEntity<>(NOT_FOUND);
685        }
686    }
687
688    @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)
689    @ApiResponses({@ApiResponse(code = 200, message = "User groups successfully retrieved."),
690                   @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
691                   @ApiResponse(code = 403, message = "Not authorized to get the groups for this user."),
692                   @ApiResponse(code = 404, message = "User not found."),
693                   @ApiResponse(code = 500, message = "An unexpected error occurred.")})
694    @XapiRequestMapping(value = "{username}/groups", produces = APPLICATION_JSON_VALUE, method = GET, restrictTo = User)
695    public ResponseEntity<Set<String>> usersIdGroupsGet(@ApiParam(value = "The ID of the user to retrieve the groups for.", required = true) @PathVariable("username") @Username final String username) {
696        try {
697            final UserI user = getUserManagementService().getUser(username);
698            if (user == null) {
699                return new ResponseEntity<>(NOT_FOUND);
700            }
701            Map<String, UserGroupI> groups = Groups.getGroupsForUser(user);
702            return new ResponseEntity<>(groups.keySet(), OK);
703        } catch (UserInitException e) {
704            log.error("An error occurred initializing the user " + username, e);
705            return new ResponseEntity<>(INTERNAL_SERVER_ERROR);
706        } catch (UserNotFoundException e) {
707            return new ResponseEntity<>(NOT_FOUND);
708        }
709    }
710
711    @ApiOperation(value = "Adds the user to one or more groups.", notes = "Assigns the user to one or more new groups.", response = String.class, responseContainer = "List")
712    @ApiResponses({@ApiResponse(code = 200, message = "User successfully added for all specified groups."),
713                   @ApiResponse(code = 202, message = "User was successfully added to some of the specified groups, but some may have failed. Check the return value for groups that the service was unable to add."),
714                   @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
715                   @ApiResponse(code = 403, message = "Not authorized to add this user to groups."),
716                   @ApiResponse(code = 404, message = "User not found."),
717                   @ApiResponse(code = 500, message = "An unexpected error occurred.")})
718    @XapiRequestMapping(value = "{username}/groups", produces = APPLICATION_JSON_VALUE, method = PUT, restrictTo = Authorizer)
719    @AuthDelegate(UserGroupXapiAuthorization.class)
720    public ResponseEntity<Collection<String>> usersIdAddGroups(@ApiParam(value = "ID of the user to add to the specified groups", required = true) @PathVariable("username") @Username final String username,
721                                                               @ApiParam(value = "The groups to which the user should be added.", required = true) @RestUserGroup @RequestBody final List<String> groups) {
722        final Collection<String> failed = new ArrayList<>();
723
724        try {
725            final UserI user = getUserManagementService().getUser(username);
726            if (user == null) {
727                return new ResponseEntity<>(NOT_FOUND);
728            }
729            for (final String group : groups) {
730                try {
731                    Groups.addUserToGroup(group, user, getSessionUser(), EventUtils.ADMIN_EVENT(getSessionUser()));
732                } catch (Exception e) {
733                    failed.add(group);
734                    log.error("Error occurred adding user " + user.getLogin() + " to group " + group + ".", e);
735                }
736            }
737        } catch (UserInitException e) {
738            log.error("An error occurred initializing the user " + username, e);
739            return new ResponseEntity<>(INTERNAL_SERVER_ERROR);
740        } catch (UserNotFoundException e) {
741            return new ResponseEntity<>(NOT_FOUND);
742        }
743
744        if (failed.size() == 0) {
745            return new ResponseEntity<>(OK);
746        }
747
748        return new ResponseEntity<>(failed, ACCEPTED);
749    }
750
751    @ApiOperation(value = "Removes the user from one or more groups.", notes = "Removes the user from one or more groups.", response = String.class, responseContainer = "List")
752    @ApiResponses({@ApiResponse(code = 200, message = "User successfully removed from all specified groups."),
753                   @ApiResponse(code = 202, message = "User was successfully removed from some of the specified groups, but some may have failed. Check the return value for groups that the service was unable to remove."),
754                   @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
755                   @ApiResponse(code = 403, message = "Not authorized to remove this user from groups."),
756                   @ApiResponse(code = 404, message = "User not found."),
757                   @ApiResponse(code = 500, message = "An unexpected error occurred.")})
758    @XapiRequestMapping(value = "{username}/groups", produces = APPLICATION_JSON_VALUE, method = DELETE, restrictTo = User)
759    public ResponseEntity<Collection<String>> usersIdRemoveGroups(@ApiParam(value = "ID 

Large files files are truncated, but you can click here to view the full file