org.sakaiproject.kernel.rest.RestUserProvider.java Source code

Java tutorial

Introduction

Here is the source code for org.sakaiproject.kernel.rest.RestUserProvider.java

Source

/*
 * Licensed to the Sakai Foundation (SF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The SF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */
package org.sakaiproject.kernel.rest;

import com.google.inject.Inject;
import com.google.inject.name.Named;

import org.apache.commons.lang.StringUtils;
import org.sakaiproject.kernel.KernelConstants;
import org.sakaiproject.kernel.api.Registry;
import org.sakaiproject.kernel.api.RegistryService;
import org.sakaiproject.kernel.api.authz.AuthzResolverService;
import org.sakaiproject.kernel.api.jcr.support.JCRNodeFactoryService;
import org.sakaiproject.kernel.api.jcr.support.JCRNodeFactoryServiceException;
import org.sakaiproject.kernel.api.rest.RestProvider;
import org.sakaiproject.kernel.api.serialization.BeanConverter;
import org.sakaiproject.kernel.api.session.Session;
import org.sakaiproject.kernel.api.session.SessionManagerService;
import org.sakaiproject.kernel.api.user.AuthenticationManagerService;
import org.sakaiproject.kernel.api.user.ProfileResolverService;
import org.sakaiproject.kernel.api.user.User;
import org.sakaiproject.kernel.api.user.UserFactoryService;
import org.sakaiproject.kernel.api.user.UserProfile;
import org.sakaiproject.kernel.api.user.UserResolverService;
import org.sakaiproject.kernel.api.userenv.UserEnvironment;
import org.sakaiproject.kernel.api.userenv.UserEnvironmentResolverService;
import org.sakaiproject.kernel.jcr.jackrabbit.sakai.JCRIdPwEvidence;
import org.sakaiproject.kernel.model.UserEnvironmentBean;
import org.sakaiproject.kernel.user.jcr.JcrAuthenticationResolverProvider;
import org.sakaiproject.kernel.util.rest.RestDescription;
import org.sakaiproject.kernel.util.user.AnonUser;
import org.sakaiproject.kernel.webapp.RestServiceFaultException;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;

import javax.jcr.Node;
import javax.jcr.Property;
import javax.jcr.RepositoryException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 *
 */
public class RestUserProvider implements RestProvider {

    private static final RestDescription DESCRIPTION = new RestDescription();
    private static final String FIRST_NAME_PARAM = "firstName";
    private static final String LAST_NAME_PARAM = "lastName";
    private static final String EMAIL_PARAM = "email";
    private static final String EXTERNAL_USERID_PARAM = "eid";
    private static final String PASSWORD_PARAM = "password";
    private static final String USER_TYPE_PARAM = "userType";
    private static final String PASSWORD_OLD_PARAM = "oldPassword";
    private static final String EXISTS = "exists";
    private BeanConverter beanConverter;
    private UserResolverService userResolverService;
    private JCRNodeFactoryService jcrNodeFactoryService;
    private UserFactoryService userFactoryService;
    private UserEnvironmentResolverService userEnvironmentResolverService;
    private SessionManagerService sessionManagerService;
    private boolean anonymousAccounting;
    private AuthenticationManagerService authenticationManagerService;
    private ProfileResolverService profileResolverService;
    private AuthzResolverService authzResolverService;

    /**
     * @param sessionManager
     *
     */
    @Inject
    public RestUserProvider(RegistryService registryService, BeanConverter beanConverter,
            UserResolverService userResolverService, UserEnvironmentResolverService userEnvironmentResolverService,
            JCRNodeFactoryService jcrNodeFactoryService, UserFactoryService userFactoryService,
            SessionManagerService sessionManagerService, AuthenticationManagerService authenticationManagerService,
            ProfileResolverService profileResolverService,
            @Named(KernelConstants.PROP_ANON_ACCOUNTING) boolean anonymousAccounting,
            AuthzResolverService authzResolverService) {
        Registry<String, RestProvider> restRegistry = registryService.getRegistry(RestProvider.REST_REGISTRY);
        restRegistry.add(this);
        this.beanConverter = beanConverter;
        this.userResolverService = userResolverService;
        this.jcrNodeFactoryService = jcrNodeFactoryService;
        this.userFactoryService = userFactoryService;
        this.userEnvironmentResolverService = userEnvironmentResolverService;
        this.sessionManagerService = sessionManagerService;
        this.authenticationManagerService = authenticationManagerService;
        this.profileResolverService = profileResolverService;
        this.anonymousAccounting = anonymousAccounting;
        this.authzResolverService = authzResolverService;

    }

    /**
     * {@inheritDoc}
     *
     * @see org.sakaiproject.kernel.api.rest.RestProvider#dispatch(java.lang.String[],
     *      javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
     */
    public void dispatch(String[] elements, HttpServletRequest request, HttpServletResponse response) {
        try {
            if ("POST".equals(request.getMethod())) {
                // Security is managed by the JCR
                Map<String, Object> map = null;
                if ("new".equals(elements[1])) {
                    map = createUser(request, response);
                } else if ("changepassword".equals(elements[1])) {
                    map = changePassword(request, response, elements.length > 2 ? elements[2] : null);
                }

                if (map != null) {
                    String responseBody = beanConverter.convertToString(map);
                    response.setContentType(RestProvider.CONTENT_TYPE);
                    response.getOutputStream().print(responseBody);
                }

            } else {
                if ("GET".equals(request.getMethod()) && elements.length == 3 && getKey().equals(elements[0])
                        && EXISTS.equals(elements[2]) && elements[1].trim().length() > 0) {
                    handleUserExists(elements[1].trim(), response);
                } else {
                    throw new RestServiceFaultException(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
                }
            }
        } catch (SecurityException ex) {
            throw ex;
        } catch (RestServiceFaultException ex) {
            throw ex;
        } catch (Exception ex) {
            throw new RestServiceFaultException(ex.getMessage(), ex);
        }
    }

    private void handleUserExists(String eid, HttpServletResponse response) throws ServletException, IOException {

        Session session = sessionManagerService.getCurrentSession();
        User user = session.getUser();

        if ((user == null || user.getUuid() == null) && !anonymousAccounting) {
            throw new RestServiceFaultException(HttpServletResponse.SC_UNAUTHORIZED);
        } else {

            UserEnvironment env = userEnvironmentResolverService.resolve(user);

            if (!anonymousAccounting && (null == env || !env.isSuperUser())) {
                throw new RestServiceFaultException(HttpServletResponse.SC_FORBIDDEN);
            } else {
                if (userResolverService.resolve(eid) != null) {
                    response.reset();
                    Map<String, String> body = new HashMap<String, String>();
                    body.put("response", "OK");
                    body.put("eid", eid);
                    body.put("exists", "true");
                    String json = beanConverter.convertToString(body);
                    response.setContentType(RestProvider.CONTENT_TYPE);
                    response.getOutputStream().print(json);
                    response.getOutputStream().flush();
                    response.getOutputStream().close();
                } else {
                    throw new RestServiceFaultException(HttpServletResponse.SC_NOT_FOUND);
                }
            }
        }

    }

    /**
     * @param request
     * @param response
     * @return
     * @throws IOException
     * @throws NoSuchAlgorithmException
     * @throws UnsupportedEncodingException
     * @throws JCRNodeFactoryServiceException
     * @throws RepositoryException
     */
    private Map<String, Object> changePassword(HttpServletRequest request, HttpServletResponse response,
            String externalId) throws UnsupportedEncodingException, NoSuchAlgorithmException, IOException,
            RepositoryException, JCRNodeFactoryServiceException {
        Session session = sessionManagerService.getCurrentSession();
        if (session == null) {
            throw new RestServiceFaultException(HttpServletResponse.SC_UNAUTHORIZED);
        }
        UserEnvironment ue = userEnvironmentResolverService.resolve(session);
        if (ue == null) {
            throw new RestServiceFaultException(HttpServletResponse.SC_UNAUTHORIZED);
        }
        User thisUser = ue.getUser();
        if (thisUser == null || thisUser instanceof AnonUser) {
            throw new RestServiceFaultException(HttpServletResponse.SC_UNAUTHORIZED);
        }

        User user = thisUser;

        boolean superUser = false;
        if (externalId != null) {
            if (ue.isSuperUser()) {
                user = userResolverService.resolve(externalId);
                if (user == null) {
                    throw new SecurityException("Specified user cant be found ");
                }
                superUser = true;
            } else {
                throw new SecurityException("User does not have permission to change others passwords");
            }
        } else {
            externalId = ((UserEnvironmentBean) ue).getEid();
        }
        if (thisUser.getUuid().equals(user.getUuid())) {
            superUser = false;
        }
        String password = request.getParameter(PASSWORD_PARAM);

        String passwordOld = request.getParameter(PASSWORD_OLD_PARAM);

        if (password == null || password.trim().length() < 5) {
            throw new RestServiceFaultException(HttpServletResponse.SC_BAD_REQUEST,
                    "Passwords are too short, minimum 5 characters");
        }
        String userEnvironmentPath = userFactoryService.getUserEnvPath(user.getUuid());
        Node userEnvNode = jcrNodeFactoryService.getNode(userEnvironmentPath);
        if (userEnvNode == null) {
            throw new RestServiceFaultException(HttpServletResponse.SC_NOT_FOUND, "User does not exist");
        }
        if (!superUser) {
            if (passwordOld == null) {
                throw new RestServiceFaultException(HttpServletResponse.SC_FORBIDDEN,
                        "You must specify the old password in order to change the password.");
            }
            // set the password
            Property storedPassword = userEnvNode.getProperty(JcrAuthenticationResolverProvider.JCRPASSWORDHASH);
            if (storedPassword != null) {
                String storedPasswordString = storedPassword.getString();
                String oldPasswordHash = org.sakaiproject.kernel.util.StringUtils.sha1Hash(passwordOld);
                if (storedPasswordString != null) {
                    if (!oldPasswordHash.equals(storedPasswordString)) {
                        throw new RestServiceFaultException(HttpServletResponse.SC_CONFLICT,
                                "Old Password does not match ");
                    }
                }
            }
        }

        JCRIdPwEvidence oldPrincipal = new JCRIdPwEvidence(externalId, passwordOld);
        JCRIdPwEvidence newPrincipal = new JCRIdPwEvidence(externalId, password);

        // Although the authentication manager service does perform the above tests,
        // providers could be outside the kernel, and so they may not be so strict.
        authenticationManagerService.setAuthentication(oldPrincipal, newPrincipal);

        Map<String, Object> r = new HashMap<String, Object>();
        r.put("response", "OK");
        r.put("uuid", user.getUuid());
        return r;

    }

    /**
     * @param request
     * @return
     * @throws JCRNodeFactoryServiceException
     * @throws RepositoryException
     * @throws IOException
     * @throws NoSuchAlgorithmException
     */
    private Map<String, Object> createUser(HttpServletRequest request, HttpServletResponse response)
            throws RepositoryException, JCRNodeFactoryServiceException, IOException, NoSuchAlgorithmException {
        String firstName = request.getParameter(FIRST_NAME_PARAM);
        String lastName = request.getParameter(LAST_NAME_PARAM);
        String email = request.getParameter(EMAIL_PARAM);
        String externalId = request.getParameter(EXTERNAL_USERID_PARAM);
        String password = request.getParameter(PASSWORD_PARAM);
        String userType = request.getParameter(USER_TYPE_PARAM);

        if (StringUtils.isEmpty(firstName)) {
            throw new RestServiceFaultException(HttpServletResponse.SC_BAD_REQUEST, FIRST_NAME_PARAM + " is empty");
        }
        if (StringUtils.isEmpty(lastName)) {
            throw new RestServiceFaultException(HttpServletResponse.SC_BAD_REQUEST, LAST_NAME_PARAM + " is empty");
        }
        if (StringUtils.isEmpty(email)) {
            throw new RestServiceFaultException(HttpServletResponse.SC_BAD_REQUEST, EMAIL_PARAM + " is empty");
        }
        if (StringUtils.isEmpty(externalId)) {
            throw new RestServiceFaultException(HttpServletResponse.SC_BAD_REQUEST,
                    EXTERNAL_USERID_PARAM + " is empty");
        }
        if (StringUtils.isEmpty(password)) {
            throw new RestServiceFaultException(HttpServletResponse.SC_BAD_REQUEST, PASSWORD_PARAM + " is empty");
        }
        if (StringUtils.isEmpty(userType)) {
            throw new RestServiceFaultException(HttpServletResponse.SC_BAD_REQUEST, USER_TYPE_PARAM + " is empty");
        }

        if (anonymousAccounting) {
            authzResolverService.setRequestGrant("Creating User For Anon User");
        } else {
            String loggedInUser = request.getRemoteUser();
            if (loggedInUser == null || "anon".equals(loggedInUser)) {
                throw new RestServiceFaultException(HttpServletResponse.SC_FORBIDDEN,
                        "User Creation is not allowed");
            } else {
                UserEnvironment ue = userEnvironmentResolverService.resolve(loggedInUser);
                if (ue.isSuperUser()) {
                    authzResolverService.setRequestGrant("Creating User For Super User");
                } else {
                    throw new RestServiceFaultException(HttpServletResponse.SC_FORBIDDEN,
                            "User Creation is only allowed by Super Users");
                }
            }
        }

        User u = userResolverService.resolve(externalId);
        if (u != null) {
            throw new RestServiceFaultException(HttpServletResponse.SC_CONFLICT,
                    "Conflict, unable to create new user. Perhaps user already exists?");
        }

        u = userFactoryService.createNewUser(externalId);

        UserEnvironment ue = userEnvironmentResolverService.create(u, externalId, password, userType);
        if (ue == null) {
            throw new RestServiceFaultException(HttpServletResponse.SC_CONFLICT, "Unable to create new user");
        }

        UserProfile userProfile = profileResolverService.create(u.getUuid(), userType);
        Map<String, Object> profileMap = userProfile.getProperties();

        profileMap.put("firstName", firstName);
        profileMap.put("lastName", lastName);
        profileMap.put("email", email);

        userProfile.setProperties(profileMap);
        userProfile.save();

        Map<String, Object> r = new HashMap<String, Object>();
        r.put("response", "OK");
        r.put("uuid", u.getUuid());
        return r;

    }

    static {
        DESCRIPTION.setTitle("User Rest Service");
        DESCRIPTION.setBackUrl("../__describe__");
        DESCRIPTION.setShortDescription("The User service creates users, and sets the users password ");
        DESCRIPTION.addSection(1, "Create User",
                "Create a new user by POST ing to the /rest/user/new url with the " + " following parameters "
                        + FIRST_NAME_PARAM + "," + LAST_NAME_PARAM + "," + EMAIL_PARAM + "," + EXTERNAL_USERID_PARAM
                        + "," + PASSWORD_PARAM + "," + USER_TYPE_PARAM + " as described below");
        DESCRIPTION.addURLTemplate("/user/new",
                "POST to create a new user, firstname, lastname, email, userid and password).");
        DESCRIPTION.addURLTemplate("/user/changepassword/<user eid>",
                "POST to change the users password, and optionally specify which user. If the "
                        + "user is not specified, the action is applied to the current user, if the user "
                        + "is specified on the path as an EID, and the current user has super user "
                        + "privalages the password will be changed. If an attempt is made to change the "
                        + "current users password, super user or not, the old password must also be "
                        + "supplied.).");
        DESCRIPTION.addParameter(FIRST_NAME_PARAM, "The first name of the User");
        DESCRIPTION.addParameter(LAST_NAME_PARAM, "The last name of the User");
        DESCRIPTION.addParameter(EMAIL_PARAM, "The email for the user User");
        DESCRIPTION.addParameter(EXTERNAL_USERID_PARAM, "The external user ID for the user User");
        DESCRIPTION.addParameter(PASSWORD_PARAM, "The initial or replacement password for the User");
        DESCRIPTION.addParameter(PASSWORD_OLD_PARAM, "the old password for the User");
        DESCRIPTION.addParameter(USER_TYPE_PARAM, "The type of the user User");
        DESCRIPTION.addURLTemplate("/user/<user eid>/exists", "GET to test for existence of a user");

        DESCRIPTION.addResponse(String.valueOf(HttpServletResponse.SC_OK),
                "On New { \"response\" : \"OK\", \"uuid\" : \"AAAA\" }, where AAAA is the new user UUID ");
        DESCRIPTION.addResponse(String.valueOf(HttpServletResponse.SC_FORBIDDEN),
                "If the user does not have permission to perform the action");
        DESCRIPTION.addResponse(String.valueOf(HttpServletResponse.SC_METHOD_NOT_ALLOWED), "If the method is used");

    }

    /**
     * {@inheritDoc}
     *
     * @see org.sakaiproject.kernel.api.rest.RestProvider#getDescription()
     */
    public RestDescription getDescription() {
        return DESCRIPTION;
    }

    /**
     * {@inheritDoc}
     *
     * @see org.sakaiproject.kernel.api.Provider#getKey()
     */
    public String getKey() {
        return "user";
    }

    /**
     * {@inheritDoc}
     *
     * @see org.sakaiproject.kernel.api.Provider#getPriority()
     */
    public int getPriority() {
        return 0;
    }

}