From 3d64b757915533f808dfc26747a324b43dcb7b6e Mon Sep 17 00:00:00 2001
From: Rick Herrick <jrherrick@wustl.edu>
Date: Tue, 6 Sep 2016 13:07:54 -0500
Subject: [PATCH] XNAT-4301 XNAT-4394 XNAT-4500 Added @XnatMixIn class mapping
 processing to RootConfig. Cleaned up User class to ignore properties we don't
 want to see. Adjusted ResultSet processing to fix investigator API error.

---
 build.gradle                                  |   2 +-
 .../java/org/nrg/xapi/model/users/User.java   |  57 +--------
 .../org/nrg/xapi/rest/data/Investigator.java  |  24 ++--
 .../rest/notifications/NotificationsApi.java  |   5 +-
 .../org/nrg/xapi/rest/users/UsersApi.java     | 108 +++++++++++++-----
 .../xnat/configuration/AutomationConfig.java  |   6 +-
 .../org/nrg/xnat/configuration/OrmConfig.java |   2 +-
 .../listeners/PipelineEmailHandlerAbst.java   |   2 +-
 .../xnat/initialization/DatabaseConfig.java   |   2 +-
 .../nrg/xnat/initialization/RootConfig.java   |  16 ++-
 .../xnat/initialization/SecurityConfig.java   |   8 +-
 .../initialization/XnatWebAppInitializer.java |   2 +-
 .../resources/ProjectUserListResource.java    |   5 +-
 .../resources/RestMockCallMapRestlet.java     |   7 +-
 .../restlet/resources/SecureResource.java     |   4 +-
 15 files changed, 126 insertions(+), 124 deletions(-)

diff --git a/build.gradle b/build.gradle
index 808fae3f..173b0b42 100644
--- a/build.gradle
+++ b/build.gradle
@@ -11,7 +11,7 @@ def vPostgreSQL = '9.4.1209.jre7'
 def vServletApi = '3.1.0'
 def vTomcat = '7.0.68'
 def vCargo = '1.4.18'
-def vSlf4j = '1.7.15'
+def vSlf4j = '1.7.21'
 def vLog4j = '1.2.17'
 def vJunit = '4.12'
 // def vSaxon = '9.7.0-7'
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 1e88b433..53d039c0 100644
--- a/src/main/java/org/nrg/xapi/model/users/User.java
+++ b/src/main/java/org/nrg/xapi/model/users/User.java
@@ -1,7 +1,6 @@
 package org.nrg.xapi.model.users;
 
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import org.apache.commons.lang3.StringUtils;
@@ -13,6 +12,7 @@ import org.nrg.xft.security.UserI;
 import java.util.Date;
 
 @ApiModel(description = "Contains the properties that define a user on the system.")
+@JsonIgnoreProperties(value = {"FullName", "Password", "Salt", "XdatUser"}, ignoreUnknown = true)
 public class User {
     public User() {
     }
@@ -45,7 +45,6 @@ public class User {
      * The user's unique key.
      **/
     @ApiModelProperty(value = "The user's unique key.")
-    @JsonProperty("id")
     public Integer getId() {
         return _id;
     }
@@ -58,7 +57,6 @@ public class User {
      * The user's login name.
      **/
     @ApiModelProperty(value = "The user's login name.")
-    @JsonProperty("username")
     public String getUsername() {
         return _username;
     }
@@ -71,7 +69,6 @@ public class User {
      * The user's first name.
      **/
     @ApiModelProperty(value = "The user's first name.")
-    @JsonProperty("firstName")
     public String getFirstName() {
         return _firstName;
     }
@@ -85,7 +82,6 @@ public class User {
      * The user's last name.
      **/
     @ApiModelProperty(value = "The user's last name.")
-    @JsonProperty("lastName")
     public String getLastName() {
         return _lastName;
     }
@@ -99,7 +95,6 @@ public class User {
      * The user's _email address.
      **/
     @ApiModelProperty(value = "The user's email address.")
-    @JsonProperty("email")
     public String getEmail() {
         return _email;
     }
@@ -112,7 +107,6 @@ public class User {
      * Whether the user is a site administrator.
      **/
     @ApiModelProperty(value = "Whether the user is a site administrator.")
-    @JsonProperty("admin")
     public boolean isAdmin() {
         return _isAdmin;
     }
@@ -125,7 +119,6 @@ public class User {
      * Whether the user is enabled.
      **/
     @ApiModelProperty(value = "Whether the user is enabled.")
-    @JsonProperty("enabled")
     public boolean isEnabled() {
         return _isEnabled;
     }
@@ -138,7 +131,6 @@ public class User {
      * Whether the user is verified.
      **/
     @ApiModelProperty(value = "Whether the user is verified.")
-    @JsonProperty("verified")
     public boolean isVerified() {
         return _isVerified;
     }
@@ -148,24 +140,9 @@ public class User {
     }
 
     /**
-     * The user's primary database (deprecated).
-     **/
-    @ApiModelProperty(value = "The user's primary database (deprecated).")
-    @JsonProperty("dbName")
-    public String getDbName() {
-        return _dbName;
-    }
-
-    @SuppressWarnings("unused")
-    public void setDbName(String dbName) {
-        _dbName = dbName;
-    }
-
-    /**
-     * The user's encrypted _password.
+     * The user's encrypted password.
      **/
     @ApiModelProperty(value = "The user's encrypted password.")
-    @JsonProperty("password")
     public String getPassword() {
         return _password;
     }
@@ -178,7 +155,6 @@ public class User {
      * The _salt used to encrypt the user's _password.
      **/
     @ApiModelProperty(value = "The salt used to encrypt the user's password.")
-    @JsonProperty("salt")
     public String getSalt() {
         return _salt;
     }
@@ -192,7 +168,6 @@ public class User {
      * The date and time the user record was last modified.
      **/
     @ApiModelProperty(value = "The date and time the user record was last modified.")
-    @JsonProperty("lastModified")
     public Date getLastModified() {
         return _lastModified;
     }
@@ -205,7 +180,6 @@ public class User {
      * The user's authorization record used when logging in.
      **/
     @ApiModelProperty(value = "The user's authorization record used when logging in.")
-    @JsonProperty("authorization")
     public UserAuth getAuthorization() {
         return _authorization;
     }
@@ -215,34 +189,11 @@ public class User {
         _authorization = authorization;
     }
 
-    @JsonIgnore
+    @ApiModelProperty(value = "The user's full name.")
     public String getFullName() {
         return String.format("%s %s", getFirstName(), getLastName());
     }
 
-    @JsonIgnore
-    public XdatUser getXDATUser(final UserI requester) {
-        final XdatUser user;
-        if (StringUtils.isNotBlank(_username)) {
-            user = XDATUser.getXdatUsersByLogin(_username, requester, true);
-        } else if (_id != null) {
-            user = AutoXdatUser.getXdatUsersByXdatUserId(_id, requester, true);
-        } else {
-            user = null;
-        }
-        if (user != null) {
-            return user;
-        }
-        final XDATUser newUser = new XDATUser();
-        newUser.setLogin(_username);
-        newUser.setFirstname(_firstName);
-        newUser.setLastname(_lastName);
-        newUser.setEmail(_email);
-        newUser.setPrimaryPassword(_password);
-        newUser.setSalt(_salt);
-        return newUser;
-    }
-
     @Override
     public String toString() {
 
diff --git a/src/main/java/org/nrg/xapi/rest/data/Investigator.java b/src/main/java/org/nrg/xapi/rest/data/Investigator.java
index 1a9ca3ac..aa0d4792 100644
--- a/src/main/java/org/nrg/xapi/rest/data/Investigator.java
+++ b/src/main/java/org/nrg/xapi/rest/data/Investigator.java
@@ -34,15 +34,15 @@ public class Investigator implements XnatInvestigatordataI {
     }
 
     public Investigator(final ResultSet resultSet) throws SQLException {
-        _xnatInvestigatordataId = resultSet.getInt(0);
-        _id = resultSet.getString(1);
-        _title = resultSet.getString(2);
-        _firstname = resultSet.getString(2);
-        _lastname = resultSet.getString(2);
-        _institution = resultSet.getString(2);
-        _department = resultSet.getString(2);
-        _email = resultSet.getString(2);
-        _phone = resultSet.getString(2);
+        _xnatInvestigatordataId = resultSet.getInt(1);
+        _id = resultSet.getString(2);
+        _title = resultSet.getString(3);
+        _firstname = resultSet.getString(4);
+        _lastname = resultSet.getString(5);
+        _institution = resultSet.getString(6);
+        _department = resultSet.getString(7);
+        _email = resultSet.getString(8);
+        _phone = resultSet.getString(9);
         _primaryProjects.addAll(getProjectIds(resultSet.getArray(10)));
         _investigatorProjects.addAll(getProjectIds(resultSet.getArray(11)));
     }
@@ -192,7 +192,11 @@ public class Investigator implements XnatInvestigatordataI {
         if (array == null) {
             return Collections.emptyList();
         }
-        return Arrays.asList((String[]) array.getArray());
+        final String[] projectIds = (String[]) array.getArray();
+        if (projectIds.length == 0) {
+            return Collections.emptyList();
+        }
+        return Arrays.asList(projectIds);
     }
 
     private final Integer _xnatInvestigatordataId;
diff --git a/src/main/java/org/nrg/xapi/rest/notifications/NotificationsApi.java b/src/main/java/org/nrg/xapi/rest/notifications/NotificationsApi.java
index e9a01c25..0d569362 100644
--- a/src/main/java/org/nrg/xapi/rest/notifications/NotificationsApi.java
+++ b/src/main/java/org/nrg/xapi/rest/notifications/NotificationsApi.java
@@ -1,6 +1,5 @@
 package org.nrg.xapi.rest.notifications;
 
-import com.fasterxml.jackson.core.type.TypeReference;
 import io.swagger.annotations.*;
 import org.apache.commons.lang3.StringUtils;
 import org.nrg.framework.annotations.XapiRestController;
@@ -26,7 +25,6 @@ import org.springframework.web.bind.annotation.*;
 import javax.inject.Inject;
 import javax.servlet.http.HttpServletRequest;
 import java.io.IOException;
-import java.util.HashMap;
 import java.util.Map;
 import java.util.Properties;
 
@@ -117,7 +115,7 @@ public class NotificationsApi extends AbstractXapiRestController {
                 } else if (StringUtils.equals(name, "emailRecipientUpdate")) {
                     _notificationsPrefs.setEmailRecipientUpdate(properties.getProperty(name));
                 } else if (StringUtils.equals(name, "smtpServer")) {
-                    _notificationsPrefs.setSmtpServer(_serializer.deserializeJson(properties.getProperty(name), TYPE_REF_HASHMAP_STRING_STRING));
+                    _notificationsPrefs.setSmtpServer(_serializer.deserializeJson(properties.getProperty(name), SerializerService.TYPE_REF_MAP_STRING_STRING));
                 } else {
                     _notificationsPrefs.set(properties.getProperty(name), name);
                 }
@@ -829,7 +827,6 @@ public class NotificationsApi extends AbstractXapiRestController {
 
     private static final Logger                                 _log                           = LoggerFactory.getLogger(NotificationsApi.class);
     private static final String                                 NOT_SET                        = "NotSet";
-    private final static TypeReference<HashMap<String, String>> TYPE_REF_HASHMAP_STRING_STRING = new TypeReference<HashMap<String, String>>() {};
 
     private final NotificationsPreferences _notificationsPrefs;
     private final JavaMailSenderImpl       _javaMailSender;
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 af585d8c..e2d6628a 100644
--- a/src/main/java/org/nrg/xapi/rest/users/UsersApi.java
+++ b/src/main/java/org/nrg/xapi/rest/users/UsersApi.java
@@ -41,8 +41,11 @@ public class UsersApi extends AbstractXapiRestController {
         _preferences = preferences;
     }
 
-    @ApiOperation(value = "Get list of users.", notes = "The primary users function returns a list of all users of the XNAT system.", response = User.class, responseContainer = "List")
-    @ApiResponses({@ApiResponse(code = 200, message = "An array of users"), @ApiResponse(code = 500, message = "Unexpected error")})
+    @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")
+    @ApiResponses({@ApiResponse(code = 200, message = "A list of usernames."),
+                   @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
+                   @ApiResponse(code = 403, message = "You do not have sufficient permissions to access the list of usernames."),
+                   @ApiResponse(code = 500, message = "An unexpected error occurred.")})
     @RequestMapping(produces = {MediaType.APPLICATION_JSON_VALUE}, method = RequestMethod.GET)
     @ResponseBody
     public ResponseEntity<List<String>> usersGet() {
@@ -56,7 +59,10 @@ public class UsersApi extends AbstractXapiRestController {
     }
 
     @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")})
+    @ApiResponses({@ApiResponse(code = 200, message = "A list of user profiles."),
+                   @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."),
+                   @ApiResponse(code = 403, message = "You do not have sufficient permissions to access the list of usernames."),
+                   @ApiResponse(code = 500, message = "An unexpected error occurred.")})
     @RequestMapping(value = {"profiles"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = RequestMethod.GET)
     @ResponseBody
     public ResponseEntity<List<Map<String, String>>> usersProfilesGet() {
@@ -90,8 +96,12 @@ public class UsersApi extends AbstractXapiRestController {
     }
 
     @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 = {"/profiles/{id}"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.GET})
+    @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 = "An unexpected error occurred.")})
+    @RequestMapping(value = {"{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) {
@@ -110,8 +120,12 @@ public class UsersApi extends AbstractXapiRestController {
     }
 
     @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 = {"/profiles/{id}"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.PUT})
+    @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 = "An unexpected error occurred.")})
+    @RequestMapping(value = {"{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) {
@@ -162,8 +176,12 @@ public class UsersApi extends AbstractXapiRestController {
     }
 
     @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 = {"/profiles/{id}/enabled"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.GET})
+    @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 = "An unexpected error occurred.")})
+    @RequestMapping(value = {"{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) {
@@ -184,8 +202,12 @@ public class UsersApi extends AbstractXapiRestController {
     }
 
     @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 = {"/profiles/{id}/enabled/{flag}"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.PUT})
+    @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 = "An unexpected error occurred.")})
+    @RequestMapping(value = {"{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) {
@@ -213,8 +235,12 @@ public class UsersApi extends AbstractXapiRestController {
     }
 
     @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 = {"/profiles/{id}/verified"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.GET})
+    @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 = "An unexpected error occurred.")})
+    @RequestMapping(value = {"{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) {
@@ -235,8 +261,12 @@ public class UsersApi extends AbstractXapiRestController {
     }
 
     @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 = {"/profiles/{id}/verified/{flag}"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.PUT})
+    @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 = "An unexpected error occurred.")})
+    @RequestMapping(value = {"{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) {
@@ -264,8 +294,12 @@ public class UsersApi extends AbstractXapiRestController {
     }
 
     @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})
+    @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 = "An unexpected error occurred.")})
+    @RequestMapping(value = {"{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") final String id) {
         HttpStatus status = isPermitted(id);
         if (status != null) {
@@ -276,8 +310,12 @@ public class UsersApi extends AbstractXapiRestController {
     }
 
     @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}/roles/{role}"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.PUT})
+    @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 = "An unexpected error occurred.")})
+    @RequestMapping(value = {"{id}/roles/{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) {
@@ -304,8 +342,12 @@ public class UsersApi extends AbstractXapiRestController {
     }
 
     @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}/roles/{role}"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.DELETE})
+    @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 = "An unexpected error occurred.")})
+    @RequestMapping(value = {"{id}/roles/{role}"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.DELETE})
     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) {
@@ -332,8 +374,12 @@ public class UsersApi extends AbstractXapiRestController {
     }
 
     @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})
+    @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 = "An unexpected error occurred.")})
+    @RequestMapping(value = {"{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) {
@@ -355,8 +401,12 @@ public class UsersApi extends AbstractXapiRestController {
     }
 
     @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}/groups/{group}"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.PUT})
+    @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 = "An unexpected error occurred.")})
+    @RequestMapping(value = {"{id}/groups/{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") final String group) {
         HttpStatus status = isPermitted(id);
         if (status != null) {
@@ -383,8 +433,12 @@ public class UsersApi extends AbstractXapiRestController {
     }
 
     @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}/groups/{group}"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.DELETE})
+    @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 = "An unexpected error occurred.")})
+    @RequestMapping(value = {"{id}/groups/{group}"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.DELETE})
     public ResponseEntity<Boolean> usersIdRemoveGroup(@ApiParam(value = "ID of the user to remove from group", required = true) @PathVariable("id") final String id, @ApiParam(value = "The group to remove the user from.", required = true) @PathVariable("group") final String group) {
         HttpStatus status = isPermitted(id);
         if (status != null) {
diff --git a/src/main/java/org/nrg/xnat/configuration/AutomationConfig.java b/src/main/java/org/nrg/xnat/configuration/AutomationConfig.java
index 9920b3e9..a41ad015 100644
--- a/src/main/java/org/nrg/xnat/configuration/AutomationConfig.java
+++ b/src/main/java/org/nrg/xnat/configuration/AutomationConfig.java
@@ -2,6 +2,8 @@ package org.nrg.xnat.configuration;
 
 import org.nrg.automation.runners.ScriptRunnerOutputAdapter;
 import org.nrg.automation.services.ScriptRunnerService;
+import org.nrg.automation.services.ScriptService;
+import org.nrg.automation.services.ScriptTriggerService;
 import org.nrg.automation.services.impl.DefaultScriptRunnerService;
 import org.nrg.framework.exceptions.NrgServiceException;
 import org.nrg.framework.orm.hibernate.HibernateEntityPackageList;
@@ -19,8 +21,8 @@ import java.util.Map;
 public class AutomationConfig {
 
     @Bean
-    public ScriptRunnerService scriptRunnerService() {
-        final DefaultScriptRunnerService service = new DefaultScriptRunnerService();
+    public ScriptRunnerService scriptRunnerService(final ScriptService scriptService, final ScriptTriggerService triggerService) {
+        final DefaultScriptRunnerService service = new DefaultScriptRunnerService(scriptService, triggerService);
         service.setRunnerPackages(Collections.singletonList("org.nrg.automation.runners"));
         return service;
     }
diff --git a/src/main/java/org/nrg/xnat/configuration/OrmConfig.java b/src/main/java/org/nrg/xnat/configuration/OrmConfig.java
index 519d9507..f91917d6 100644
--- a/src/main/java/org/nrg/xnat/configuration/OrmConfig.java
+++ b/src/main/java/org/nrg/xnat/configuration/OrmConfig.java
@@ -7,7 +7,7 @@ import org.nrg.framework.exceptions.NrgServiceError;
 import org.nrg.framework.exceptions.NrgServiceException;
 import org.nrg.framework.orm.hibernate.AggregatedAnnotationSessionFactoryBean;
 import org.nrg.framework.orm.hibernate.PrefixedTableNamingStrategy;
-import org.nrg.framework.utilities.Beans;
+import org.nrg.framework.beans.Beans;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.config.PropertiesFactoryBean;
diff --git a/src/main/java/org/nrg/xnat/event/listeners/PipelineEmailHandlerAbst.java b/src/main/java/org/nrg/xnat/event/listeners/PipelineEmailHandlerAbst.java
index ecc8b48d..e461b620 100644
--- a/src/main/java/org/nrg/xnat/event/listeners/PipelineEmailHandlerAbst.java
+++ b/src/main/java/org/nrg/xnat/event/listeners/PipelineEmailHandlerAbst.java
@@ -137,7 +137,7 @@ public abstract class PipelineEmailHandlerAbst extends WorkflowStatusEventHandle
 
                     if (StringUtils.isNotBlank(comments)) {
                         try {
-                            params.putAll(getSerializer().deserializeJson(comments, new TypeReference<HashMap<String, String>>(){}));
+                            params.putAll(getSerializer().deserializeJson(comments, SerializerService.TYPE_REF_MAP_STRING_STRING));
                         } catch (Exception e1) {
                             // Do nothing. This isn't necessarily a problem.
                             params.put("comments",comments);
diff --git a/src/main/java/org/nrg/xnat/initialization/DatabaseConfig.java b/src/main/java/org/nrg/xnat/initialization/DatabaseConfig.java
index 4b717f6b..b69d715b 100644
--- a/src/main/java/org/nrg/xnat/initialization/DatabaseConfig.java
+++ b/src/main/java/org/nrg/xnat/initialization/DatabaseConfig.java
@@ -4,7 +4,7 @@ import org.apache.commons.dbcp2.BasicDataSource;
 import org.apache.commons.lang3.StringUtils;
 import org.nrg.framework.exceptions.NrgServiceError;
 import org.nrg.framework.exceptions.NrgServiceException;
-import org.nrg.framework.utilities.Beans;
+import org.nrg.framework.beans.Beans;
 import org.postgresql.Driver;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
diff --git a/src/main/java/org/nrg/xnat/initialization/RootConfig.java b/src/main/java/org/nrg/xnat/initialization/RootConfig.java
index 03dd06e6..f26e745a 100644
--- a/src/main/java/org/nrg/xnat/initialization/RootConfig.java
+++ b/src/main/java/org/nrg/xnat/initialization/RootConfig.java
@@ -8,11 +8,11 @@ import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
 import com.fasterxml.jackson.databind.SerializationFeature;
 import com.fasterxml.jackson.datatype.hibernate4.Hibernate4Module;
 import org.apache.commons.beanutils.BeanUtils;
+import org.nrg.framework.beans.Beans;
 import org.nrg.framework.datacache.SerializerRegistry;
 import org.nrg.framework.exceptions.NrgServiceException;
 import org.nrg.framework.services.ContextService;
 import org.nrg.framework.services.SerializerService;
-import org.nrg.prefs.beans.PreferenceBeanMixIn;
 import org.nrg.xdat.preferences.SiteConfigPreferences;
 import org.nrg.xnat.configuration.ApplicationConfig;
 import org.nrg.xnat.helpers.prearchive.PrearcConfig;
@@ -40,7 +40,7 @@ import java.util.Map;
 import java.util.Properties;
 
 /**
- * Configuration for the XNAT root application context. This contains all of the basic infrastructure for initializing
+ * Configuration for the XNAT root application context. This contains all of the F infrastructure for initializing
  * and bootstrapping the site, including data source configuration, transaction and session management, and site
  * configuration preferences.
  * <p>
@@ -102,7 +102,7 @@ public class RootConfig {
     }
 
     @Bean
-    public Jackson2ObjectMapperBuilder objectMapperBuilder() {
+    public Jackson2ObjectMapperBuilder objectMapperBuilder() throws NrgServiceException {
         return new Jackson2ObjectMapperBuilder()
                 .serializationInclusion(JsonInclude.Include.NON_NULL)
                 .failOnEmptyBeans(false)
@@ -113,10 +113,8 @@ public class RootConfig {
     }
 
     @Bean
-    public Map<Class<?>, Class<?>> mixIns() {
-        final Map<Class<?>, Class<?>> mixIns = new HashMap<>();
-        mixIns.put(SiteConfigPreferences.class, PreferenceBeanMixIn.class);
-        return mixIns;
+    public Map<Class<?>, Class<?>> mixIns() throws NrgServiceException {
+        return Beans.getMixIns();
     }
 
     @Bean
@@ -138,8 +136,8 @@ public class RootConfig {
     }
 
     @Bean
-    public SerializerService serializerService() {
-        return new SerializerService();
+    public SerializerService serializerService(final Jackson2ObjectMapperBuilder objectMapperBuilder) {
+        return new SerializerService(objectMapperBuilder);
     }
 
     @Bean
diff --git a/src/main/java/org/nrg/xnat/initialization/SecurityConfig.java b/src/main/java/org/nrg/xnat/initialization/SecurityConfig.java
index 6bd28e33..68b90f65 100644
--- a/src/main/java/org/nrg/xnat/initialization/SecurityConfig.java
+++ b/src/main/java/org/nrg/xnat/initialization/SecurityConfig.java
@@ -127,7 +127,7 @@ public class SecurityConfig {
     public FilterSecurityInterceptorBeanPostProcessor filterSecurityInterceptorBeanPostProcessor(final SerializerService serializer, final SiteConfigPreferences preferences) throws IOException {
         final Resource resource = RESOURCE_LOADER.getResource("classpath:META-INF/xnat/security/configured-urls.yaml");
         try (final InputStream inputStream = resource.getInputStream()) {
-            final HashMap<String, ArrayList<String>>         urlMap        = serializer.deserializeYaml(inputStream, TYPE_REFERENCE);
+            final HashMap<String, ArrayList<String>>         urlMap        = serializer.deserializeYaml(inputStream, SerializerService.TYPE_REF_MAP_STRING_LIST_STRING);
             final FilterSecurityInterceptorBeanPostProcessor postProcessor = new FilterSecurityInterceptorBeanPostProcessor(preferences);
             postProcessor.setOpenUrls(urlMap.get("openUrls"));
             postProcessor.setAdminUrls(urlMap.get("adminUrls"));
@@ -236,7 +236,5 @@ public class SecurityConfig {
         return list;
     }
 
-    private static final ResourceLoader                                    RESOURCE_LOADER = new DefaultResourceLoader();
-    private static final TypeReference<HashMap<String, ArrayList<String>>> TYPE_REFERENCE  = new TypeReference<HashMap<String, ArrayList<String>>>() {
-    };
-}
\ No newline at end of file
+    private static final ResourceLoader RESOURCE_LOADER = new DefaultResourceLoader();
+}
diff --git a/src/main/java/org/nrg/xnat/initialization/XnatWebAppInitializer.java b/src/main/java/org/nrg/xnat/initialization/XnatWebAppInitializer.java
index a6ac8673..51b1ab3f 100644
--- a/src/main/java/org/nrg/xnat/initialization/XnatWebAppInitializer.java
+++ b/src/main/java/org/nrg/xnat/initialization/XnatWebAppInitializer.java
@@ -6,7 +6,7 @@ import org.apache.axis.transport.http.AxisServlet;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.turbine.Turbine;
 import org.nrg.framework.exceptions.NrgServiceRuntimeException;
-import org.nrg.framework.processors.XnatPluginBean;
+import org.nrg.framework.beans.XnatPluginBean;
 import org.nrg.xdat.servlet.XDATAjaxServlet;
 import org.nrg.xdat.servlet.XDATServlet;
 import org.nrg.xnat.restlet.servlet.XNATRestletServlet;
diff --git a/src/main/java/org/nrg/xnat/restlet/resources/ProjectUserListResource.java b/src/main/java/org/nrg/xnat/restlet/resources/ProjectUserListResource.java
index fd02b112..1ccc3039 100644
--- a/src/main/java/org/nrg/xnat/restlet/resources/ProjectUserListResource.java
+++ b/src/main/java/org/nrg/xnat/restlet/resources/ProjectUserListResource.java
@@ -13,6 +13,7 @@ package org.nrg.xnat.restlet.resources;
 import org.apache.commons.lang3.StringUtils;
 import org.nrg.config.services.ConfigService;
 import org.nrg.framework.constants.Scope;
+import org.nrg.framework.services.SerializerService;
 import org.nrg.xdat.XDAT;
 import org.nrg.xdat.om.XnatProjectdata;
 import org.nrg.xdat.security.helpers.Permissions;
@@ -101,7 +102,7 @@ public class ProjectUserListResource extends SecureResource {
         final String config = configService.getConfigContents("user-resource-whitelist", "whitelist.json", Scope.Project, projectId);
         if (!StringUtils.isBlank(config)) {
             try {
-                List<String> projectUserResourceWhitelist = getSerializer().deserializeJson(config, TYPE_REFERENCE_LIST_STRING);
+                List<String> projectUserResourceWhitelist = getSerializer().deserializeJson(config, SerializerService.TYPE_REF_LIST_STRING);
                 if (projectUserResourceWhitelist != null) {
                     return projectUserResourceWhitelist.contains(getUser().getUsername());
                 }
@@ -112,4 +113,4 @@ public class ProjectUserListResource extends SecureResource {
 
         return false;
     }
-}
\ No newline at end of file
+}
diff --git a/src/main/java/org/nrg/xnat/restlet/resources/RestMockCallMapRestlet.java b/src/main/java/org/nrg/xnat/restlet/resources/RestMockCallMapRestlet.java
index 86936594..7d509e39 100644
--- a/src/main/java/org/nrg/xnat/restlet/resources/RestMockCallMapRestlet.java
+++ b/src/main/java/org/nrg/xnat/restlet/resources/RestMockCallMapRestlet.java
@@ -10,10 +10,10 @@
  */
 package org.nrg.xnat.restlet.resources;
 
-import com.fasterxml.jackson.core.type.TypeReference;
-import org.apache.commons.lang3.StringUtils;
 import com.fasterxml.jackson.core.JsonFactory;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.commons.lang3.StringUtils;
+import org.nrg.framework.services.SerializerService;
 import org.nrg.xdat.XDAT;
 import org.restlet.Context;
 import org.restlet.data.MediaType;
@@ -33,7 +33,6 @@ import java.util.Map;
 
 public class RestMockCallMapRestlet extends SecureResource {
 
-    private final static TypeReference<HashMap<String, String>> TYPE_REFERENCE = new TypeReference<HashMap<String, String>>() {};
     private final static ObjectMapper MAPPER = new ObjectMapper(new JsonFactory());
 
     private final String _call;
@@ -47,7 +46,7 @@ public class RestMockCallMapRestlet extends SecureResource {
         }
 
         try {
-            return MAPPER.readValue(configuration, TYPE_REFERENCE);
+            return MAPPER.readValue(configuration, SerializerService.TYPE_REF_MAP_STRING_STRING);
         } catch (IOException ignored) {
             return new HashMap<>();
         }
diff --git a/src/main/java/org/nrg/xnat/restlet/resources/SecureResource.java b/src/main/java/org/nrg/xnat/restlet/resources/SecureResource.java
index 8d2adf27..eac158f4 100644
--- a/src/main/java/org/nrg/xnat/restlet/resources/SecureResource.java
+++ b/src/main/java/org/nrg/xnat/restlet/resources/SecureResource.java
@@ -1530,7 +1530,7 @@ public abstract class SecureResource extends Resource {
         }
 
         try {
-            List<String> userResourceWhitelist = getSerializer().deserializeJson(config, TYPE_REFERENCE_LIST_STRING);
+            List<String> userResourceWhitelist = getSerializer().deserializeJson(config, SerializerService.TYPE_REF_LIST_STRING);
             if (userResourceWhitelist != null) {
                 return userResourceWhitelist.contains(user.getUsername());
             }
@@ -1542,8 +1542,6 @@ public abstract class SecureResource extends Resource {
         return false;
     }
 
-    protected final static TypeReference<ArrayList<String>> TYPE_REFERENCE_LIST_STRING = new TypeReference<ArrayList<String>>() {};
-
     private static final Map<String, List<FilteredResourceHandlerI>> handlers = Maps.newConcurrentMap();
 
     /**
-- 
GitLab