org.sakaiproject.kernel.user.servlet.CreateSakaiGroupServlet.java Source code

Java tutorial

Introduction

Here is the source code for org.sakaiproject.kernel.user.servlet.CreateSakaiGroupServlet.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.user.servlet;

import static org.sakaiproject.kernel.api.user.UserConstants.DEFAULT_HASH_LEVELS;

import org.apache.commons.lang.StringUtils;
import org.apache.jackrabbit.api.security.principal.PrincipalIterator;
import org.apache.jackrabbit.api.security.principal.PrincipalManager;
import org.apache.jackrabbit.api.security.user.Authorizable;
import org.apache.jackrabbit.api.security.user.Group;
import org.apache.jackrabbit.api.security.user.User;
import org.apache.jackrabbit.api.security.user.UserManager;
import org.apache.jackrabbit.core.security.SecurityConstants;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.servlets.HtmlResponse;
import org.apache.sling.jackrabbit.usermanager.impl.helper.RequestProperty;
import org.apache.sling.jackrabbit.usermanager.impl.resource.AuthorizableResourceProvider;
import org.apache.sling.jcr.api.SlingRepository;
import org.apache.sling.jcr.base.util.AccessControlUtil;
import org.apache.sling.servlets.post.Modification;
import org.apache.sling.servlets.post.SlingPostConstants;
import org.osgi.framework.ServiceReference;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;
import org.osgi.service.component.ComponentContext;
import org.sakaiproject.kernel.api.user.UserPostProcessor;
import org.sakaiproject.kernel.util.PathUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.security.Principal;
import java.util.Arrays;
import java.util.Dictionary;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.servlet.http.HttpServletResponse;

/**
 * <p>
 * Sling Post Servlet implementation for creating a group in the jackrabbit UserManager.
 * </p>
 * <h2>Rest Service Description</h2>
 * <p>
 * Creates a new group. Maps on to nodes of resourceType <code>sling/groups</code> like
 * <code>/rep:system/rep:userManager/rep:groups</code> mapped to a resource url
 * <code>/system/userManager/group</code>. This servlet responds at
 * <code>/system/userManager/group.create.html</code>
 * </p>
 * <h4>Methods</h4>
 * <ul>
 * <li>POST</li>
 * </ul>
 * <h4>Post Parameters</h4>
 * <dl>
 * <dt>:name</dt>
 * <dd>The name of the new group (required)</dd>
 * <dt></dt>
 * <dd>Any additional parameters become properties of the group node (optional)</dd>
 * </dl>
 * <h4>Response</h4>
 * <dl>
 * <dt>200</dt>
 * <dd>Success, a redirect is sent to the group resource locator. The redirect comes with
 * HTML describing the status.</dd>
 * <dt>500</dt>
 * <dd>Failure, including group already exists. HTML explains the failure.</dd>
 * </dl>
 * <h4>Example</h4>
 * 
 * <code>
 * curl -F:name=newGroupA  -Fproperty1=value1 http://localhost:8080/system/userManager/group.create.html
 * </code>
 * 
 * <h4>Notes</h4>
 * <p>
 * Groups are stored as JCR node in a 3 way hased tree, for example <code>/rep:system/rep:userManager/rep:groups/ab/3e/4d/newGroupA</code>
 * </p>
 * 
 * @scr.component immediate="true"
 * @scr.service interface="javax.servlet.Servlet"
 * @scr.property name="sling.servlet.resourceTypes" value="sling/groups"
 * @scr.property name="sling.servlet.methods" value="POST"
 * @scr.property name="sling.servlet.selectors" value="create"
 * 
 * @scr.property name="servlet.post.dateFormats"
 *               values.0="EEE MMM dd yyyy HH:mm:ss 'GMT'Z"
 *               values.1="yyyy-MM-dd'T'HH:mm:ss.SSSZ" values.2="yyyy-MM-dd'T'HH:mm:ss"
 *               values.3="yyyy-MM-dd" values.4="dd.MM.yyyy HH:mm:ss"
 *               values.5="dd.MM.yyyy"
 * 
 * @scr.reference name="UserPostProcessor" bind="bindUserPostProcessor"
 *                unbind="unbindUserPostProcessor"
 *                interface="org.sakaiproject.kernel.api.user.UserPostProcessor"
 *                cardinality="0..n" policy="dynamic"
 * 
 */

public class CreateSakaiGroupServlet extends AbstractSakaiGroupPostServlet implements ManagedService {

    /**
     *
     */
    private static final long serialVersionUID = 6587376522316825454L;

    private static final Logger LOGGER = LoggerFactory.getLogger(CreateSakaiGroupServlet.class);

    private UserPostProcessorRegister postProcessorTracker = new UserPostProcessorRegister();

    /**
     * The JCR Repository we access to resolve resources
     * 
     * @scr.reference
     */
    protected SlingRepository repository;

    /**
     * 
     * @scr.property value="authenticated,everyone" type="String"
     *               name="Groups who are allowed to create other groups" description=
     *               "A comma separated list of groups who area allowed to create other groups"
     */
    public static final String GROUP_AUTHORISED_TOCREATE = "groups.authorized.tocreate";

    private String[] authorizedGroups = { "authenticated" };

    /** Returns the JCR repository used by this service. */
    protected SlingRepository getRepository() {
        return repository;
    }

    /**
     * Returns an administrative session to the default workspace.
     */
    private Session getSession() throws RepositoryException {
        return getRepository().loginAdministrative(null);
    }

    /**
     * Return the administrative session and close it.
     */
    private void ungetSession(final Session session) {
        if (session != null) {
            try {
                session.logout();
            } catch (Throwable t) {
                LOGGER.error("Unable to log out of session: " + t.getMessage(), t);
            }
        }
    }

    // ---------- SCR integration ---------------------------------------------

    /**
     * Activates this component.
     * 
     * @param componentContext
     *          The OSGi <code>ComponentContext</code> of this component.
     */
    protected void activate(ComponentContext componentContext) {
        super.activate(componentContext);
        postProcessorTracker.setComponentContext(componentContext);
        String groupList = (String) componentContext.getProperties().get(GROUP_AUTHORISED_TOCREATE);
        if (groupList != null) {
            authorizedGroups = StringUtils.split(groupList, ',');
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.osgi.service.cm.ManagedService#updated(java.util.Dictionary)
     */
    @SuppressWarnings("unchecked")
    public void updated(Dictionary dictionary) throws ConfigurationException {
        String groupList = (String) dictionary.get(GROUP_AUTHORISED_TOCREATE);
        if (groupList != null) {
            authorizedGroups = StringUtils.split(groupList, ',');
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @seeorg.apache.sling.jackrabbit.usermanager.post.AbstractAuthorizablePostServlet#
     * handleOperation(org.apache.sling.api.SlingHttpServletRequest,
     * org.apache.sling.api.servlets.HtmlResponse, java.util.List)
     */
    @Override
    protected void handleOperation(SlingHttpServletRequest request, HtmlResponse response,
            List<Modification> changes) throws RepositoryException {

        // check that the submitted parameter values have valid values.
        final String principalName = request.getParameter(SlingPostConstants.RP_NODE_NAME);
        if (principalName == null) {
            throw new RepositoryException("Group name was not submitted");
        }

        if (!principalName.startsWith("g-")) {
            throw new RepositoryException("Group names must begin with 'g-'");
        }

        // check for allow create Group
        boolean allowCreateGroup = false;
        User currentUser = null;
        try {
            Session currentSession = request.getResourceResolver().adaptTo(Session.class);
            UserManager um = AccessControlUtil.getUserManager(currentSession);
            currentUser = (User) um.getAuthorizable(currentSession.getUserID());
            if (currentUser.isAdmin()) {
                LOGGER.debug("User is an admin ");
                allowCreateGroup = true;
            } else {
                LOGGER.debug("Checking for membership of one of {} ", Arrays.toString(authorizedGroups));
                PrincipalManager principalManager = AccessControlUtil.getPrincipalManager(currentSession);
                PrincipalIterator pi = principalManager
                        .getGroupMembership(principalManager.getPrincipal(currentSession.getUserID()));
                Set<String> groups = new HashSet<String>();
                for (; pi.hasNext();) {
                    groups.add(pi.nextPrincipal().getName());
                }

                for (String groupName : authorizedGroups) {
                    if (groups.contains(groupName)) {
                        allowCreateGroup = true;
                        break;
                    }

                    // TODO: move this nasty hack into the PrincipalManager dynamic groups need to
                    // be in the principal manager for this to work.
                    if ("authenticated".equals(groupName)
                            && !SecurityConstants.ADMIN_ID.equals(currentUser.getID())) {
                        allowCreateGroup = true;
                        break;
                    }

                    // just check via the user manager for dynamic resolution.
                    Group group = (Group) um.getAuthorizable(groupName);
                    LOGGER.debug("Checking for group  {} {} ", groupName, group);
                    if (group != null && group.isMember(currentUser)) {
                        allowCreateGroup = true;
                        LOGGER.debug("User is a member  of {} {} ", groupName, group);
                        break;
                    }
                }
            }
        } catch (Exception ex) {
            LOGGER.warn("Failed to determin if the user is an admin, assuming not. Cause: " + ex.getMessage());
            allowCreateGroup = false;
        }

        if (!allowCreateGroup) {
            LOGGER.debug("User is not allowed to create groups ");
            response.setStatus(HttpServletResponse.SC_FORBIDDEN, "User is not allowed to create groups");
            return;
        }

        Session session = getSession();

        try {
            UserManager userManager = AccessControlUtil.getUserManager(session);
            Authorizable authorizable = userManager.getAuthorizable(principalName);

            if (authorizable != null) {
                // principal already exists!
                throw new RepositoryException(
                        "A principal already exists with the requested name: " + principalName);
            } else {
                Map<String, RequestProperty> reqProperties = collectContent(request, response);

                Group group = userManager.createGroup(new Principal() {
                    public String getName() {
                        return principalName;
                    }
                }, PathUtils.getUserPrefix(principalName, DEFAULT_HASH_LEVELS));

                String groupPath = AuthorizableResourceProvider.SYSTEM_USER_MANAGER_GROUP_PREFIX + group.getID();
                response.setPath(groupPath);
                response.setLocation(externalizePath(request, groupPath));
                response.setParentLocation(
                        externalizePath(request, AuthorizableResourceProvider.SYSTEM_USER_MANAGER_GROUP_PATH));
                changes.add(Modification.onCreated(groupPath));

                // write content from form
                writeContent(session, group, reqProperties, changes);

                // update the group memberships, although this uses session from the request, it
                // only
                // does so for finding authorizables, so its ok that we are using an admin session
                // here.
                updateGroupMembership(session, request, group, changes);
                updateOwnership(session, request, group, new String[] { currentUser.getID() }, changes);

                try {
                    for (UserPostProcessor userPostProcessor : postProcessorTracker.getProcessors()) {
                        userPostProcessor.process(session, request, changes);
                    }
                } catch (Exception e) {
                    LOGGER.warn(e.getMessage(), e);
                    response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
                }
                if (session.hasPendingChanges()) {
                    session.save();
                }

            }
        } catch (RepositoryException re) {
            throw new RepositoryException("Failed to create new group.", re);
        } finally {
            ungetSession(session);
        }
    }

    protected void bindUserPostProcessor(ServiceReference serviceReference) {
        postProcessorTracker.bindUserPostProcessor(serviceReference);

    }

    protected void unbindUserPostProcessor(ServiceReference serviceReference) {
        postProcessorTracker.unbindUserPostProcessor(serviceReference);
    }

}