From 501eb0c714603d2040b7e43b7c845a3afd3425e7 Mon Sep 17 00:00:00 2001 From: Steve Nolen Date: Thu, 17 Mar 2016 11:47:05 -0700 Subject: [PATCH] add user/setup_external api. --- src/org/ohmage/request/RequestBuilder.java | 18 ++ .../user/UserSetupExternalRequest.java | 210 ++++++++++++++++++ .../ohmage/request/user/UserSetupRequest.java | 2 +- src/org/ohmage/service/KeycloakServices.java | 1 - src/org/ohmage/service/UserServices.java | 33 +++ 5 files changed, 262 insertions(+), 2 deletions(-) create mode 100644 src/org/ohmage/request/user/UserSetupExternalRequest.java diff --git a/src/org/ohmage/request/RequestBuilder.java b/src/org/ohmage/request/RequestBuilder.java index fe7da556..cf928d3a 100644 --- a/src/org/ohmage/request/RequestBuilder.java +++ b/src/org/ohmage/request/RequestBuilder.java @@ -21,6 +21,7 @@ import javax.servlet.http.HttpServletRequest; import org.apache.log4j.Logger; +import org.ohmage.cache.KeycloakCache; import org.ohmage.exception.InvalidRequestException; import org.ohmage.exception.ServiceException; import org.ohmage.request.accessrequest.AccessRequestCreationRequest; @@ -95,6 +96,7 @@ import org.ohmage.request.user.UserReadRequest; import org.ohmage.request.user.UserRegistrationRequest; import org.ohmage.request.user.UserSearchRequest; +import org.ohmage.request.user.UserSetupExternalRequest; import org.ohmage.request.user.UserSetupRequest; import org.ohmage.request.user.UserStatsReadRequest; import org.ohmage.request.user.UserUpdateRequest; @@ -225,6 +227,7 @@ public final class RequestBuilder implements ServletContextAware { private String apiUserChangePassword; private String apiUserDelete; private String apiUserSetup; + private String apiUserSetupExternal; // AccessRequest private String apiAccessRequestCreate; @@ -386,6 +389,7 @@ public void setServletContext(final ServletContext servletContext) { apiUserChangePassword = apiRoot + "/user/change_password"; apiUserDelete = apiRoot + "/user/delete"; apiUserSetup = apiRoot + "/user/setup"; + apiUserSetupExternal = apiRoot + "/user/setup_external"; // Registration @@ -694,6 +698,20 @@ else if(apiUserSetup.equals(requestUri)) { return new FailedRequest(); } } + else if(apiUserSetupExternal.equals(requestUri)) { + try { + if (ConfigServices.readServerConfiguration().getUserSetupEnabled() && + KeycloakCache.isEnabled()) + return new UserSetupExternalRequest(httpRequest); + else { + LOGGER.info("Rejecting UserSetupExternal request as API is disabled"); + return new FailedRequest(); + } + } catch (ServiceException e) { + LOGGER.warn("Can't find user setup config. Will disable this API."); + return new FailedRequest(); + } + } // AccessRequest else if(apiAccessRequestCreate.equals(requestUri)) { return new AccessRequestCreationRequest(httpRequest); diff --git a/src/org/ohmage/request/user/UserSetupExternalRequest.java b/src/org/ohmage/request/user/UserSetupExternalRequest.java new file mode 100644 index 00000000..13e66ee8 --- /dev/null +++ b/src/org/ohmage/request/user/UserSetupExternalRequest.java @@ -0,0 +1,210 @@ +package org.ohmage.request.user; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.log4j.Logger; +import org.json.JSONException; +import org.json.JSONObject; +import org.ohmage.annotator.Annotator.ErrorCode; +import org.ohmage.domain.Clazz; +import org.ohmage.domain.KeycloakUser; +import org.ohmage.exception.InvalidRequestException; +import org.ohmage.exception.ServiceException; +import org.ohmage.exception.ValidationException; +import org.ohmage.request.InputKeys; +import org.ohmage.request.UserRequest; +import org.ohmage.service.ClassServices; +import org.ohmage.service.UserServices; +import org.ohmage.util.StringUtils; +import org.ohmage.validator.ClassValidators; +import org.ohmage.validator.UserValidators; + +public class UserSetupExternalRequest extends UserRequest { + /** + * The logger for this class. + */ + private static final Logger LOGGER = + Logger.getLogger(UserCreationRequest.class); + + private final Set classIds; + private final String newUsername; + + + /** + * Creates a user creation request. + * + * @param httpRequest The HttpServletRequest that contains the required and + * optional parameters for creating this request. + * + * @throws InvalidRequestException Thrown if the parameters cannot be + * parsed. + * + * @throws IOException There was an error reading from the request. + */ + public UserSetupExternalRequest( + final HttpServletRequest httpRequest) + throws IOException, InvalidRequestException { + + super(httpRequest, null, TokenLocation.EITHER, null); + + Set tClassIds = null; + String tNewUsername = null; + + if(! isFailed()) { + LOGGER.info("Creating a user setup external request."); + + try { + String[] t; + + // Get and validate the class ID. + t = getParameterValues(InputKeys.CLASS_URN_LIST); + if(t.length > 1) { + throw new ValidationException( + ErrorCode.CLASS_INVALID_ID, + "Multiple class ID lists were given: " + + InputKeys.CLASS_URN_LIST); + } + else if(t.length == 1) { + tClassIds = ClassValidators.validateClassIdList(t[0]); + } + + // get external user account name + t = getParameterValues(InputKeys.NEW_USERNAME); + if(t.length > 1) { + throw new ValidationException( + ErrorCode.USER_INVALID_USERNAME_PROPERTY, + "Multiple new usernames were given: " + + InputKeys.NEW_USERNAME); + } + else if ((t.length == 1) && (! StringUtils.isEmptyOrWhitespaceOnly(t[0]))) { + try { + tNewUsername = UserValidators.validateUsername(t[0].trim()); + } catch (ValidationException e) { + throw new ValidationException( + ErrorCode.USER_INVALID_USERNAME_PROPERTY, + "Invalid new username pattern: " + + InputKeys.NEW_USERNAME, + e); + } + } + + } + catch(ValidationException e) { + e.failRequest(this); + e.logException(LOGGER); + } + } + + classIds = tClassIds; + newUsername = tNewUsername; + } + + /* + * (non-Javadoc) + * @see org.ohmage.request.Request#service() + */ + @Override + public void service() { + LOGGER.info("Servicing the user setup request."); + + if(! authenticate(AllowNewAccount.NEW_ACCOUNT_DISALLOWED)) { + return; + } + + try { + LOGGER.info("Verifying that the requesting user can setup users."); + UserServices + .instance() + .verifyUserCanSetupUsers(getUser().getUsername()); + + if(classIds != null) { + LOGGER.info("Verifying that the classes exist."); + ClassServices.instance().checkClassesExistence(classIds, true); + } + + LOGGER.info("Determining if the user already exists."); + boolean userExistsAndExternal = + UserServices + .instance() + .userExistsAndIsExternal(newUsername); + + // If the user does not exists or external. create them + // This will throw an exception if the user exists but is not external, + // which can be considered intentional. + if(userExistsAndExternal == false) { + LOGGER.info("The user does not exist or is not external."); + LOGGER.info("Trying to create the user."); + UserServices + .instance() + .createUser( + newUsername, + KeycloakUser.KEYCLOAK_USER_PASSWORD, + null, + false, // admin + true, // enable + false, // new account + true, // campaign creation privilege. should read from config + false, // storeInitialPassword + true, // externalAccount + null); + } + + // Add them to the class, if given. + if(classIds != null) { + // need to check that the requester is a privileged + // user in the class + + + // For each class, reset the user's role in that class to + // restricted. + for(String classId : classIds) { + Map usersToAdd = + new HashMap(); + usersToAdd.put(newUsername, Clazz.Role.RESTRICTED); + + LOGGER.info("Reseting the user in the class: " + classId); + ClassServices + .instance() + .updateClass( + classId, + null, + null, + usersToAdd, + null); + } + } + } + catch(ServiceException e) { + e.failRequest(this); + e.logException(LOGGER); + } + } + + /* + * (non-Javadoc) + * @see org.ohmage.request.Request#respond(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) + */ + @Override + public void respond( + final HttpServletRequest httpRequest, + final HttpServletResponse httpResponse) { + + JSONObject response = new JSONObject(); + try { + response.put("username", newUsername); + response.put("external", true); + + super.respond(httpRequest, httpResponse, response); + } + catch(JSONException e) { + super.respond(httpRequest, httpResponse, (JSONObject) null); + } + } + +} \ No newline at end of file diff --git a/src/org/ohmage/request/user/UserSetupRequest.java b/src/org/ohmage/request/user/UserSetupRequest.java index ac845b01..a3670639 100644 --- a/src/org/ohmage/request/user/UserSetupRequest.java +++ b/src/org/ohmage/request/user/UserSetupRequest.java @@ -283,7 +283,7 @@ public void service() { } try { - LOGGER.info("Verifying that the requesting user is an admin."); + LOGGER.info("Verifying that the requesting user can setup users."); UserServices .instance() .verifyUserCanSetupUsers(getUser().getUsername()); diff --git a/src/org/ohmage/service/KeycloakServices.java b/src/org/ohmage/service/KeycloakServices.java index 997891f9..5d12459f 100644 --- a/src/org/ohmage/service/KeycloakServices.java +++ b/src/org/ohmage/service/KeycloakServices.java @@ -53,7 +53,6 @@ public final class KeycloakServices { */ private KeycloakServices() {} - /* * Inspects a bearer token for username and returns a new KeycloakUser object * for user. diff --git a/src/org/ohmage/service/UserServices.java b/src/org/ohmage/service/UserServices.java index ced5e540..01229ab4 100644 --- a/src/org/ohmage/service/UserServices.java +++ b/src/org/ohmage/service/UserServices.java @@ -2280,5 +2280,38 @@ private String generateRandomTemporaryPassword() { } return passwordBuilder.toString(); } + + /** + * Checks for user existence and if user is external. + * + * @param username A username to check. + * + * @return true if user exists AND is external. false otherwise. + */ + public boolean userExistsAndIsExternal(String username) + throws ServiceException { + boolean userExists; + try { + userExists = + userQueries.userExists(username); + } + catch(DataAccessException e) { + throw new ServiceException(e); + } + + if (userExists) { + boolean isExternal = false; + try { + isExternal = userQueries.userIsExternal(username); + } + catch(DataAccessException e) { + throw new ServiceException(e); + } + return isExternal; + } else { + return userExists; + } + + } }