org.sakaiproject.nakamura.personal.PersonalAuthorizablePostProcessor.java Source code

Java tutorial

Introduction

Here is the source code for org.sakaiproject.nakamura.personal.PersonalAuthorizablePostProcessor.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.nakamura.personal;

import static javax.jcr.security.Privilege.JCR_ALL;
import static javax.jcr.security.Privilege.JCR_READ;
import static javax.jcr.security.Privilege.JCR_WRITE;
import static org.apache.sling.jcr.resource.JcrResourceConstants.SLING_RESOURCE_TYPE_PROPERTY;
import static org.sakaiproject.nakamura.api.personal.PersonalConstants.VISIBILITY_LOGGED_IN;
import static org.sakaiproject.nakamura.api.personal.PersonalConstants.VISIBILITY_PRIVATE;
import static org.sakaiproject.nakamura.api.personal.PersonalConstants.VISIBILITY_PUBLIC;
import static org.sakaiproject.nakamura.api.user.UserConstants.GROUP_HOME_RESOURCE_TYPE;
import static org.sakaiproject.nakamura.api.user.UserConstants.PROP_AUTHORIZABLE_PATH;
import static org.sakaiproject.nakamura.api.user.UserConstants.USER_HOME_RESOURCE_TYPE;

import org.apache.commons.lang.StringUtils;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Modified;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.PropertyOption;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.ReferencePolicy;
import org.apache.felix.scr.annotations.Service;
import org.apache.jackrabbit.JcrConstants;
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.sling.commons.osgi.OsgiUtil;
import org.apache.sling.jcr.base.util.AccessControlUtil;
import org.apache.sling.jcr.contentloader.ContentImporter;
import org.apache.sling.jcr.resource.JcrResourceConstants;
import org.apache.sling.servlets.post.Modification;
import org.apache.sling.servlets.post.ModificationType;
import org.osgi.service.event.EventAdmin;
import org.sakaiproject.nakamura.api.jcr.JCRConstants;
import org.sakaiproject.nakamura.api.profile.ProfileService;
import org.sakaiproject.nakamura.api.user.AuthorizableEvent.Operation;
import org.sakaiproject.nakamura.api.user.AuthorizableEventUtil;
import org.sakaiproject.nakamura.api.user.AuthorizablePostProcessor;
import org.sakaiproject.nakamura.api.user.UserConstants;
import org.sakaiproject.nakamura.util.JcrUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.security.Principal;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import javax.jcr.Node;
import javax.jcr.PathNotFoundException;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Value;
import javax.jcr.lock.LockException;
import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.security.AccessControlManager;
import javax.jcr.security.Privilege;
import javax.jcr.version.VersionException;

/**
 * This PostProcessor listens to post operations on User objects and processes the
 * changes.
 *
 */
@Component(immediate = true, metatype = true)
@Service(value = AuthorizablePostProcessor.class)
@Properties(value = {
        @org.apache.felix.scr.annotations.Property(name = "service.vendor", value = "The Sakai Foundation"),
        @org.apache.felix.scr.annotations.Property(name = "service.description", value = "Post Processes User and Group operations"),
        @org.apache.felix.scr.annotations.Property(name = "service.ranking", intValue = 0) })
public class PersonalAuthorizablePostProcessor implements AuthorizablePostProcessor {

    @org.apache.felix.scr.annotations.Property(description = "The default access settings for the home of a new user or group.", value = VISIBILITY_PUBLIC, options = {
            @PropertyOption(name = VISIBILITY_PRIVATE, value = "The home is private."),
            @PropertyOption(name = VISIBILITY_LOGGED_IN, value = "The home is blocked to anonymous users; all logged-in users can see it."),
            @PropertyOption(name = VISIBILITY_PUBLIC, value = "The home is completely public.") })
    static final String VISIBILITY_PREFERENCE = "org.sakaiproject.nakamura.personal.visibility.preference";
    static final String VISIBILITY_PREFERENCE_DEFAULT = VISIBILITY_PUBLIC;

    public static final String PROFILE_IMPORT_TEMPLATE_DEFAULT = "{'basic':{'elements':{'firstName':{'value':'@@firstName@@'},'lastName':{'value':'@@lastName@@'},'email':{'value':'@@email@@'}},'access':'everybody'}}";
    @org.apache.felix.scr.annotations.Property
    static final String PROFILE_IMPORT_TEMPLATE = "sakai.user.profile.template.default";
    private String defaultProfileTemplate;

    private ArrayList<String> profileParams = new ArrayList<String>();

    @Reference
    private ProfileService profileService;

    @Reference
    private EventAdmin eventAdmin;

    @Reference(policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.OPTIONAL_UNARY)
    protected ContentImporter contentImporter;

    private String visibilityPreference;

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

    @Activate
    @Modified
    protected void modified(Map<?, ?> props) {
        visibilityPreference = OsgiUtil.toString(props.get(VISIBILITY_PREFERENCE), VISIBILITY_PREFERENCE_DEFAULT);
        defaultProfileTemplate = OsgiUtil.toString(props.get(PROFILE_IMPORT_TEMPLATE),
                PROFILE_IMPORT_TEMPLATE_DEFAULT);

        int startPos = defaultProfileTemplate.indexOf("@@");
        while (startPos > -1) {
            int endPos = defaultProfileTemplate.indexOf("@@", startPos + 2);
            if (endPos > -1) {
                String param = defaultProfileTemplate.substring(startPos + 2, endPos);
                profileParams.add(param);

                endPos = defaultProfileTemplate.indexOf("@@", endPos + 2);
            }
            startPos = endPos;
        }
    }

    /**
     * {@inheritDoc}
     * @see org.sakaiproject.nakamura.api.user.AuthorizablePostProcessor#process(org.apache.jackrabbit.api.security.user.Authorizable, javax.jcr.Session, org.apache.sling.servlets.post.Modification, java.util.Map)
     */
    public void process(Authorizable authorizable, Session session, Modification change,
            Map<String, Object[]> parameters) throws Exception {
        if (!ModificationType.DELETE.equals(change.getType())) {
            LOGGER.debug("Processing  {} ", authorizable.getID());
            try {
                if (ModificationType.CREATE.equals(change.getType())) {
                    createHomeFolder(session, authorizable, change, parameters);
                } else {
                    updateHomeFolder(session, authorizable, change, parameters);
                }
                fireEvent(session, authorizable.getID(), change);
                LOGGER.debug("DoneProcessing  {} ", authorizable.getID());
            } catch (Exception ex) {
                LOGGER.error("Post Processing failed " + ex.getMessage(), ex);
            }
        }
    }

    /**
     * @param authorizable
     * @param changes
     * @throws RepositoryException
     * @throws ConstraintViolationException
     * @throws LockException
     * @throws VersionException
     * @throws PathNotFoundException
     */
    private void updateProfileProperties(Session session, Node profileNode, Authorizable authorizable,
            Modification change, Map<String, Object[]> parameters) throws RepositoryException {
        if (profileNode == null) {
            return;
        }

        // The current session does not necessarily have write access to
        // the Profile.
        if (isAbleToModify(session, profileNode.getPath())) {
            // If the client sent a parameter specifying new Profile content,
            // apply it now.
            String defaultProfile = processProfileParameters(defaultProfileTemplate, authorizable, parameters);
            ProfileImporter.importFromParameters(profileNode, parameters, contentImporter, session, defaultProfile);

            // build a blacklist set of properties that should be kept private
            Set<String> privateProperties = new HashSet<String>();
            if (profileNode.hasProperty(UserConstants.PRIVATE_PROPERTIES)) {
                Value[] pp = profileNode.getProperty(UserConstants.PRIVATE_PROPERTIES).getValues();
                for (Value v : pp) {
                    privateProperties.add(v.getString());
                }
            }
            // copy the non blacklist set of properties into the users profile.
            if (authorizable != null) {
                // explicitly add protected properties form the user authorizable
                if (!authorizable.isGroup() && !profileNode.hasProperty("rep:userId")) {
                    profileNode.setProperty("rep:userId", authorizable.getID());
                }
                Iterator<?> inames = authorizable.getPropertyNames();
                while (inames.hasNext()) {
                    String propertyName = (String) inames.next();
                    // No need to copy in jcr:* properties, otherwise we would copy over the uuid
                    // which could lead to a lot of confusion.
                    if (!propertyName.startsWith("jcr:") && !propertyName.startsWith("rep:")) {
                        if (!privateProperties.contains(propertyName)) {
                            Value[] v = authorizable.getProperty(propertyName);
                            if (!(profileNode.hasProperty(propertyName)
                                    && profileNode.getProperty(propertyName).getDefinition().isProtected())) {
                                if (v.length == 1) {
                                    profileNode.setProperty(propertyName, v[0]);
                                } else {
                                    profileNode.setProperty(propertyName, v);
                                }
                            }
                        }
                    } else {
                        LOGGER.debug("Not Updating {}", propertyName);
                    }
                }
            }
        }
    }

    private String processProfileParameters(final String defaultProfileTemplate, final Authorizable auth,
            final Map<String, Object[]> parameters) throws RepositoryException {
        String retval = defaultProfileTemplate;
        for (String param : profileParams) {
            String val = "unknown";
            if (parameters.containsKey(param)) {
                val = (String) parameters.get(param)[0];
            } else if (auth.hasProperty(param)) {
                val = auth.getProperty(param)[0].getString();
            }
            retval = StringUtils.replace(retval, "@@" + param + "@@", val);
        }
        return retval;
    }

    /**
     * Creates the home folder for a {@link User user} or a {@link Group group}. It will
     * also create all the subfolders such as private, public, ..
     *
     * @param session
     * @param authorizable
     * @param isGroup
     * @param change
     * @throws RepositoryException
     */
    private void createHomeFolder(Session session, Authorizable authorizable, Modification change,
            Map<String, Object[]> parameters) throws RepositoryException {
        String homeFolderPath = profileService.getHomePath(authorizable);

        Node homeNode = JcrUtils.deepGetOrCreateNode(session, homeFolderPath);
        if (homeNode.isNew()) {
            LOGGER.debug("Created Home Node for {} at   {} user was {} ",
                    new Object[] { authorizable.getID(), homeNode, session.getUserID() });
        } else {
            LOGGER.debug("Existing Home Node for {} at   {} user was {} ",
                    new Object[] { authorizable.getID(), homeNode, session.getUserID() });
        }

        if (!UserConstants.ANON_USERID.equals(authorizable.getID())) {
            initializeAccess(homeNode, session, authorizable);
        }

        refreshOwnership(session, authorizable, homeFolderPath);

        // add things to home
        decorateHome(homeNode, authorizable);

        // Create the public, private, authprofile
        createPrivate(session, authorizable);
        createPublic(session, authorizable);
        Node profileNode = createProfile(session, authorizable);

        // Update the values on the profile node.
        updateProfileProperties(session, profileNode, authorizable, change, parameters);

        if (authorizable.isGroup()) {
            // setup a joinrequests node for the group
            Value[] path = authorizable.getProperty(PROP_AUTHORIZABLE_PATH);
            if (path != null && path.length > 0) {
                String pathString = "/_group" + path[0].getString() + "/joinrequests";
                Node messageStore = JcrUtils.deepGetOrCreateNode(session, pathString);
                messageStore.setProperty(JcrResourceConstants.SLING_RESOURCE_TYPE_PROPERTY, "sakai/joinrequests");
            }
        }
    }

    private void decorateHome(Node homeNode, Authorizable authorizable) throws RepositoryException {
        // set the home node resource type
        if (authorizable.isGroup()) {
            homeNode.setProperty(SLING_RESOURCE_TYPE_PROPERTY, GROUP_HOME_RESOURCE_TYPE);
        } else {
            homeNode.setProperty(SLING_RESOURCE_TYPE_PROPERTY, USER_HOME_RESOURCE_TYPE);
        }

        // set whether the home node should be excluded from searches
        if (authorizable.hasProperty(JCRConstants.SEARCH_EXCLUDE_TREE)) {
            homeNode.setProperty(JCRConstants.SEARCH_EXCLUDE_TREE,
                    authorizable.getProperty(JCRConstants.SEARCH_EXCLUDE_TREE)[0]);
        }
    }

    /**
     * @param principalManager
     * @param managerSettings
     * @return
     * @throws RepositoryException
     */
    private Principal[] valuesToPrincipal(Value[] values, Principal[] defaultValue,
            PrincipalManager principalManager) throws RepositoryException {
        // An explicitly empty list of group viewers or managers does not mean the
        // same thing as having no group viewers or managers property, and so
        // a zero-length array should still override the defaults.
        if (values != null) {
            Principal[] valueAsStrings = new Principal[values.length];
            for (int i = 0; i < values.length; i++) {
                valueAsStrings[i] = principalManager.getPrincipal(values[i].getString());
                if (valueAsStrings[i] == null) {
                    LOGGER.warn("Principal {} cant be resolved, will be ignored ", values[i].getString());
                }
            }
            return valueAsStrings;
        } else {
            return defaultValue;
        }
    }

    /**
     * @param request
     * @param authorizable
     * @return
     * @throws RepositoryException
     */
    private Node createProfile(Session session, Authorizable authorizable) throws RepositoryException {
        String path = profileService.getProfilePath(authorizable);
        Node profileNode = null;
        if (!isPostProcessingDone(session, authorizable)) {
            String type = nodeTypeForAuthorizable(authorizable.isGroup());
            LOGGER.debug("Creating or resetting Profile Node {} for authorizable {} ", path, authorizable.getID());
            profileNode = JcrUtils.deepGetOrCreateNode(session, path);
            profileNode.setProperty(SLING_RESOURCE_TYPE_PROPERTY, type);
            // Make sure we can place references to this profile node in the future.
            // This will make it easier to search on it later on.
            if (profileNode.canAddMixin(JcrConstants.MIX_REFERENCEABLE)) {
                profileNode.addMixin(JcrConstants.MIX_REFERENCEABLE);
            }
        } else {
            profileNode = session.getNode(path);
        }
        return profileNode;
    }

    /**
     * Creates the private folder in the user his home space.
     * TODO As of 2010-09-28 the "private" node is no longer used by any Nakamura
     * component. Can this code be eliminated?
     *
     * @param session
     *          The session to create the node
     * @param athorizable
     *          The Authorizable to create it for
     * @return The {@link Node node} that represents the private folder.
     * @throws RepositoryException
     */
    private Node createPrivate(Session session, Authorizable authorizable) throws RepositoryException {
        String privatePath = profileService.getPrivatePath(authorizable);
        if (session.itemExists(privatePath)) {
            return session.getNode(privatePath);
        }
        LOGGER.debug("creating or replacing ACLs for private at {} ", privatePath);
        Node privateNode = JcrUtils.deepGetOrCreateNode(session, privatePath);

        LOGGER.debug("Done creating private at {} ", privatePath);
        return privateNode;
    }

    /**
     * Set access controls on the new User or Group node according to the profile
     * preference configuration property.
     *
     * @param node
     * @param session
     * @param authorizable
     * @throws RepositoryException
     */
    private void initializeAccess(Node node, Session session, Authorizable authorizable)
            throws RepositoryException {
        String nodePath = node.getPath();
        PrincipalManager principalManager = AccessControlUtil.getPrincipalManager(session);
        Principal everyone = principalManager.getEveryone();
        Principal anon = new Principal() {
            public String getName() {
                return UserConstants.ANON_USERID;
            }
        };
        // KERN-886 : Depending on the profile preference we set some ACL's on the profile.
        if (UserConstants.ANON_USERID.equals(authorizable.getID())) {
            AccessControlUtil.replaceAccessControlEntry(session, nodePath, anon, new String[] { JCR_READ }, null,
                    null, null);
            AccessControlUtil.replaceAccessControlEntry(session, nodePath, everyone, new String[] { JCR_READ },
                    null, null, null);
        } else if (VISIBILITY_PUBLIC.equals(visibilityPreference)) {
            AccessControlUtil.replaceAccessControlEntry(session, nodePath, anon, new String[] { JCR_READ }, null,
                    null, null);
            AccessControlUtil.replaceAccessControlEntry(session, nodePath, everyone, new String[] { JCR_READ },
                    null, null, null);
        } else if (VISIBILITY_LOGGED_IN.equals(visibilityPreference)) {
            AccessControlUtil.replaceAccessControlEntry(session, nodePath, anon, null, new String[] { JCR_READ },
                    null, null);
            AccessControlUtil.replaceAccessControlEntry(session, nodePath, everyone, new String[] { JCR_READ },
                    null, null, null);
        } else if (VISIBILITY_PRIVATE.equals(visibilityPreference)) {
            AccessControlUtil.replaceAccessControlEntry(session, nodePath, anon, null, new String[] { JCR_READ },
                    null, null);
            AccessControlUtil.replaceAccessControlEntry(session, nodePath, everyone, null,
                    new String[] { JCR_READ }, null, null);
        }
    }

    /**
     * Creates the public folder in the user his home space.
     *
     * @param session
     *          The session to create the node
     * @param athorizable
     *          The Authorizable to create it for
     * @return The {@link Node node} that represents the public folder.
     * @throws RepositoryException
     */
    private Node createPublic(Session session, Authorizable athorizable) throws RepositoryException {
        String publicPath = profileService.getPublicPath(athorizable);
        if (session.nodeExists(publicPath)) {
            // No more work needed at present.
            return session.getNode(publicPath);
        }
        LOGGER.debug("Creating Public  for {} at   {} ", athorizable.getID(), publicPath);
        Node publicNode = JcrUtils.deepGetOrCreateNode(session, publicPath);
        return publicNode;
    }

    private String nodeTypeForAuthorizable(boolean isGroup) {
        if (isGroup) {
            return UserConstants.GROUP_PROFILE_RESOURCE_TYPE;
        } else {
            return UserConstants.USER_PROFILE_RESOURCE_TYPE;
        }
    }

    // event processing
    // -----------------------------------------------------------------------------

    /**
     * Fire events, into OSGi, one synchronous one asynchronous.
     *
     * @param operation
     *          the operation being performed.
     * @param session
     *          the session performing operation.
     * @param request
     *          the request that triggered the operation.
     * @param authorizable
     *          the authorizable that is the target of the operation.
     * @param changes
     *          a list of {@link Modification} caused by the operation.
     */
    private void fireEvent(Session session, String principalName, Modification change) {
        try {
            String user = session.getUserID();
            String path = change.getDestination();
            if (path == null) {
                path = change.getSource();
            }
            if (AuthorizableEventUtil.isAuthorizableModification(change)) {
                LOGGER.debug("Got Authorizable modification: " + change);
                switch (change.getType()) {
                case COPY:
                case CREATE:
                case DELETE:
                case MOVE:
                    LOGGER.debug("Ignoring unknown modification type: " + change.getType());
                    break;
                case MODIFY:
                    eventAdmin.postEvent(AuthorizableEventUtil.newGroupEvent(user, change));
                    break;
                }
            } else if (path.endsWith(principalName)) {
                switch (change.getType()) {
                case COPY:
                    eventAdmin.postEvent(AuthorizableEventUtil.newAuthorizableEvent(Operation.update, user,
                            principalName, change));
                    break;
                case CREATE:
                    eventAdmin.postEvent(AuthorizableEventUtil.newAuthorizableEvent(Operation.create, user,
                            principalName, change));
                    break;
                case DELETE:
                    eventAdmin.postEvent(AuthorizableEventUtil.newAuthorizableEvent(Operation.delete, user,
                            principalName, change));
                    break;
                case MODIFY:
                    eventAdmin.postEvent(AuthorizableEventUtil.newAuthorizableEvent(Operation.update, user,
                            principalName, change));
                    break;
                case MOVE:
                    eventAdmin.postEvent(AuthorizableEventUtil.newAuthorizableEvent(Operation.update, user,
                            principalName, change));
                    break;
                }
            }
        } catch (Throwable t) {
            LOGGER.warn("Failed to fire event", t);
        }
    }

    /**
     * @param eventAdmin
     *          the new EventAdmin service to bind to this service.
     */
    protected void bindEventAdmin(EventAdmin eventAdmin) {
        this.eventAdmin = eventAdmin;
    }

    /**
     * @param eventAdmin
     *          the EventAdminService to be unbound from this service.
     */
    protected void unbindEventAdmin(EventAdmin eventAdmin) {
        this.eventAdmin = null;
    }

    /**
     * Decide whether post-processing this user or group would be redundant because it has
     * already been done. The current logic uses the existence of a profile node of the
     * correct type as a marker.
     *
     * @param session
     * @param authorizable
     * @return true if there is evidence that post-processing has already occurred for this
     *         user or group
     * @throws RepositoryException
     */
    private boolean isPostProcessingDone(Session session, Authorizable authorizable) throws RepositoryException {
        boolean isProfileCreated = false;
        Node node = getProfileNode(session, authorizable);
        if (node != null) {
            String type = nodeTypeForAuthorizable(authorizable.isGroup());
            if (node.hasProperty(SLING_RESOURCE_TYPE_PROPERTY)) {
                if (node.getProperty(SLING_RESOURCE_TYPE_PROPERTY).getString().equals(type)) {
                    isProfileCreated = true;
                }
            }
        }
        return isProfileCreated;
    }

    private Node getProfileNode(Session session, Authorizable authorizable) throws RepositoryException {
        Node profileNode;
        String path = profileService.getProfilePath(authorizable);
        if (session.nodeExists(path)) {
            profileNode = session.getNode(path);
        } else {
            profileNode = null;
        }
        return profileNode;
    }

    private void updateHomeFolder(Session session, Authorizable authorizable, Modification change,
            Map<String, Object[]> parameters) throws RepositoryException {
        Node profileNode = getProfileNode(session, authorizable);
        if (profileNode != null) {
            // Mirror the current state of the Authorizable's visibility and
            // management controls, if the current session has the right to do
            // so.
            // TODO Replace these implicit side-effects with something more controllable
            // by the client.
            refreshOwnership(session, authorizable, profileService.getHomePath(authorizable));
            if (!parameters.containsKey(":sakai:update-profile")
                    || !"false".equals(parameters.get(":sakai:update-profile")[0])) {
                updateProfileProperties(session, getProfileNode(session, authorizable), authorizable, change,
                        parameters);
            }
        }
    }

    /**
     * If the current session has sufficient rights, synchronize home folder
     * access to match the current accessibility of the Jackrabbit User or
     * Group. Currently this is done for every update, overwriting any ACLs
     * which might have been explicitly set on the home node.
     *
     * @param session
     * @param authorizable
     * @param homeFolderPath
     * @throws RepositoryException
     */
    private void refreshOwnership(Session session, Authorizable authorizable, String homeFolderPath)
            throws RepositoryException {
        if (isAbleToControlAccess(session, homeFolderPath)) {
            PrincipalManager principalManager = AccessControlUtil.getPrincipalManager(session);

            Value[] managerSettings = authorizable.getProperty(UserConstants.PROP_GROUP_MANAGERS);
            Value[] viewerSettings = authorizable.getProperty(UserConstants.PROP_GROUP_VIEWERS);

            // If the Authorizable has a managers list, everyone on that list gets write access.
            // Otherwise, the Authorizable itself is the owner.
            Principal[] managers = valuesToPrincipal(managerSettings,
                    new Principal[] { authorizable.getPrincipal() }, principalManager);

            // Do not automatically give read-access to anonymous and everyone, since that
            // forces User Home folders to be public and overwrites configuration settings.
            Principal[] viewers = valuesToPrincipal(viewerSettings, new Principal[] {}, principalManager);

            for (Principal manager : managers) {
                if (manager != null && !UserConstants.ANON_USERID.equals(manager.getName())) {
                    LOGGER.debug("User {} is attempting to make {} a manager ", session.getUserID(),
                            manager.getName());
                    AccessControlUtil.replaceAccessControlEntry(session, homeFolderPath, manager,
                            new String[] { JCR_ALL }, null, null, null);
                }
            }
            for (Principal viewer : viewers) {
                if (viewer != null && !UserConstants.ANON_USERID.equals(viewer.getName())) {
                    LOGGER.debug("User {} is attempting to make {} a viewer ", session.getUserID(),
                            viewer.getName());
                    AccessControlUtil.replaceAccessControlEntry(session, homeFolderPath, viewer,
                            new String[] { JCR_READ }, new String[] { JCR_WRITE }, null, null);
                }
            }
            LOGGER.debug("Set ACL on Node for {} at   {} ", authorizable.getID(), homeFolderPath);
        }
    }

    private boolean isAbleToControlAccess(Session session, String homeFolderPath) throws RepositoryException {
        AccessControlManager accessControlManager = AccessControlUtil.getAccessControlManager(session);
        Privilege[] modifyAclPrivileges = {
                accessControlManager.privilegeFromName(Privilege.JCR_MODIFY_ACCESS_CONTROL) };
        return accessControlManager.hasPrivileges(homeFolderPath, modifyAclPrivileges);
    }

    private boolean isAbleToModify(Session session, String path) throws RepositoryException {
        AccessControlManager accessControlManager = AccessControlUtil.getAccessControlManager(session);
        Privilege[] modifyPrivileges = { accessControlManager.privilegeFromName(Privilege.JCR_MODIFY_PROPERTIES),
                accessControlManager.privilegeFromName(Privilege.JCR_ADD_CHILD_NODES),
                accessControlManager.privilegeFromName(Privilege.JCR_REMOVE_CHILD_NODES) };
        return accessControlManager.hasPrivileges(path, modifyPrivileges);
    }
}