AccessControlActionFactory.java 13.3 KB
/*
 * Decompiled with CFR 0_118.
 * 
 * Could not load the following classes:
 *  com.day.cq.wcm.api.WCMException
 *  com.day.cq.wcm.msm.api.LiveAction
 *  com.day.cq.wcm.msm.api.LiveRelationship
 *  com.day.cq.wcm.msm.api.LiveStatus
 *  com.day.cq.wcm.msm.commons.BaseAction
 *  com.day.cq.wcm.msm.commons.BaseActionFactory
 *  javax.jcr.Node
 *  javax.jcr.NodeIterator
 *  javax.jcr.RepositoryException
 *  javax.jcr.Session
 *  javax.jcr.security.AccessControlException
 *  javax.jcr.security.AccessControlManager
 *  javax.jcr.security.Privilege
 *  org.apache.commons.lang.StringUtils
 *  org.apache.felix.scr.annotations.Activate
 *  org.apache.felix.scr.annotations.Component
 *  org.apache.felix.scr.annotations.Modified
 *  org.apache.felix.scr.annotations.Property
 *  org.apache.felix.scr.annotations.Service
 *  org.apache.jackrabbit.api.JackrabbitSession
 *  org.apache.jackrabbit.api.security.JackrabbitAccessControlManager
 *  org.apache.jackrabbit.api.security.principal.PrincipalManager
 *  org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils
 *  org.apache.sling.api.resource.Resource
 *  org.apache.sling.api.resource.ValueMap
 *  org.apache.sling.jcr.resource.JcrPropertyMap
 *  org.osgi.service.component.ComponentContext
 *  org.slf4j.Logger
 *  org.slf4j.LoggerFactory
 */
package com.day.cq.wcm.msm.impl.actions;

import com.day.cq.wcm.api.WCMException;
import com.day.cq.wcm.msm.api.LiveAction;
import com.day.cq.wcm.msm.api.LiveRelationship;
import com.day.cq.wcm.msm.api.LiveStatus;
import com.day.cq.wcm.msm.commons.BaseAction;
import com.day.cq.wcm.msm.commons.BaseActionFactory;
import com.day.cq.wcm.msm.impl.Utils;
import com.day.cq.wcm.msm.impl.actions.util.ACEUtils;
import com.day.cq.wcm.msm.impl.actions.util.AccessControlEntry;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.security.AccessControlException;
import javax.jcr.security.AccessControlManager;
import javax.jcr.security.Privilege;
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.Property;
import org.apache.felix.scr.annotations.Service;
import org.apache.jackrabbit.api.JackrabbitSession;
import org.apache.jackrabbit.api.security.JackrabbitAccessControlManager;
import org.apache.jackrabbit.api.security.principal.PrincipalManager;
import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.jcr.resource.JcrPropertyMap;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component(metatype=0)
@Service
public class AccessControlActionFactory
extends BaseActionFactory<BaseAction> {
    private static final Logger log = LoggerFactory.getLogger(AccessControlActionFactory.class);
    @Property(name="liveActionName")
    private static final String[] LIVE_ACTION_NAME = new String[]{AccessControlListAction.class.getSimpleName(), "MandatoryActionFactory", "mandatory", "MandatoryContentAction", "mandatoryContent", "MandatoryStructureAction", "mandatoryStructure"};

    public String createsAction() {
        return LIVE_ACTION_NAME[0];
    }

    protected BaseAction newActionInstance(ValueMap config) throws WCMException {
        log.info("AccessControlActions must be created using createAction");
        return null;
    }

    public BaseAction createAction(Resource resource) throws WCMException {
        try {
            Node node;
            if (resource == null || (node = (Node)resource.adaptTo(Node.class)) == null || !(node.getSession() instanceof JackrabbitSession)) {
                log.warn("Resource does not meet requirement to build an LiveAction: Resource must be a Node from JackrabbitRepository");
                return null;
            }
            return new AccessControlListAction((ValueMap)resource.adaptTo(ValueMap.class), node, this);
        }
        catch (RepositoryException e) {
            log.debug("Failure Accessing Repository on building AccessControlListAction: {}", (Throwable)e);
            throw new WCMException((Throwable)e);
        }
    }

    @Activate
    @Modified
    protected void activate(ComponentContext context) {
    }

    static final class AccessControlListAction
    extends BaseAction {
        static final String MANDATORY_ACTION_PARAM_TARGET = "target";
        static final String PN_PRINCIPAL = "principalName";
        static final String PN_PRIVILEGES = "privilegeNames";
        static final String PN_DENY = "deny";
        private static final String[] modificationPrivileges = new String[]{"{http://www.jcp.org/jcr/1.0}modifyProperties", "{http://www.jcp.org/jcr/1.0}lockManagement", "{http://www.jcp.org/jcr/1.0}versionManagement", "{http://www.jcp.org/jcr/1.0}addChildNodes", "{http://www.jcp.org/jcr/1.0}nodeTypeManagement", "{http://www.jcp.org/jcr/1.0}removeChildNodes", "{http://www.jcp.org/jcr/1.0}removeNode"};
        private static final String[] nodeManagementPrivileges = new String[]{"{http://www.jcp.org/jcr/1.0}addChildNodes", "{http://www.jcp.org/jcr/1.0}nodeTypeManagement", "{http://www.jcp.org/jcr/1.0}removeChildNodes", "{http://www.jcp.org/jcr/1.0}removeNode"};
        private final List<AccessControlEntry> aces = new ArrayList<AccessControlEntry>();
        private static final String PATH_CONTENT = "/jcr:content";

        private AccessControlListAction(ValueMap config, Node configNode, BaseActionFactory factory) throws RepositoryException {
            super(config, factory);
            this.aces.addAll(this.loadACE(configNode));
            NodeIterator children = configNode.getNodes();
            while (children.hasNext()) {
                Node child = children.nextNode();
                this.aces.addAll(this.loadACE(child));
            }
        }

        private List<AccessControlEntry> loadACE(Node configNode) throws RepositoryException {
            ArrayList<AccessControlEntry> nodeAceList = new ArrayList<AccessControlEntry>();
            JcrPropertyMap nodeProps = new JcrPropertyMap(configNode);
            String nodeName = configNode.getName();
            String principalName = null;
            AccessControlEntry[] aceTemplates = null;
            if (configNode.hasProperty("principalName") && configNode.hasProperty("privilegeNames")) {
                principalName = (String)nodeProps.get("principalName", String.class);
                String[] privileges = (String[])nodeProps.get("privilegeNames", String[].class);
                boolean isAllow = (Boolean)nodeProps.get("deny", (Object)Boolean.FALSE) == false;
                aceTemplates = new AccessControlEntry[]{new AccessControlEntry(null, isAllow, null, privileges)};
            } else if (configNode.hasProperty("target")) {
                principalName = (String)nodeProps.get("target", String.class);
                aceTemplates = ACLTemplates.INSTANCE.getMandatoryACE(nodeName);
            }
            if (aceTemplates != null && StringUtils.isNotBlank((String)principalName)) {
                for (AccessControlEntry template : aceTemplates) {
                    AccessControlEntry newAce = new AccessControlEntry(principalName, template);
                    nodeAceList.add(newAce);
                    log.debug("Found ACE {} on config node {}", (Object)newAce, (Object)configNode.getPath());
                }
            }
            return nodeAceList;
        }

        protected void doExecute(Resource source, Resource target, LiveRelationship relation, boolean resetRollout) throws RepositoryException, WCMException {
            Node targetNode = Utils.getHierarchyNode((Node)target.adaptTo(Node.class));
            Session session = targetNode.getSession();
            String targetPath = targetNode.getPath();
            JackrabbitSession jrSession = (JackrabbitSession)session;
            JackrabbitAccessControlManager accessControlManager = (JackrabbitAccessControlManager)jrSession.getAccessControlManager();
            PrincipalManager principalManager = jrSession.getPrincipalManager();
            for (AccessControlEntry ace : this.aces) {
                boolean isRoot = "/jcr:content".equals(relation.getSyncPath()) || StringUtils.isEmpty((String)relation.getSyncPath());
                boolean applyPrivileges = isRoot || ACEUtils.getPrincipal(ace, principalManager) != null && !this.aceIsEffective(targetPath, ace, accessControlManager, ACEUtils.getPrincipal(ace, principalManager));
                if (!applyPrivileges) continue;
                this.applyPrivileges(session, targetPath, ace, (AccessControlManager)accessControlManager, principalManager);
            }
        }

        protected boolean handles(Resource source, Resource target, LiveRelationship relation, boolean resetRollout) throws WCMException, RepositoryException {
            Node node;
            return relation.getStatus().isPage() && target != null && (node = (Node)target.adaptTo(Node.class)) != null && node.getSession() instanceof JackrabbitSession && node.getSession().getAccessControlManager() != null;
        }

        private boolean aceIsEffective(String target, AccessControlEntry ace, JackrabbitAccessControlManager accManager, Principal principal) throws RepositoryException {
            Set<Principal> principals = Collections.singleton(principal);
            if (ace.isAllow()) {
                Privilege[] privileges = AccessControlUtils.privilegesFromNames((AccessControlManager)accManager, (String[])ace.getPrivilegeNames());
                return accManager.hasPrivileges(target, principals, privileges);
            }
            for (String privilege : ace.getPrivilegeNames()) {
                Privilege[] privileges = AccessControlUtils.privilegesFromNames((AccessControlManager)accManager, (String[])new String[]{privilege});
                if (!accManager.hasPrivileges(target, principals, privileges)) continue;
                return false;
            }
            return true;
        }

        private void applyPrivileges(Session session, String path, AccessControlEntry accessControlEntry, AccessControlManager accessControlManager, PrincipalManager prManager) throws RepositoryException {
            try {
                String[] privNames = accessControlEntry.getPrivilegeNames();
                Privilege[] privileges = AccessControlUtils.privilegesFromNames((AccessControlManager)accessControlManager, (String[])privNames);
                Principal principal = ACEUtils.getPrincipal(accessControlEntry, prManager);
                if (StringUtils.isNotBlank((String)path) && principal != null) {
                    boolean addedPrivilegeEntry = ACEUtils.addAccessControlEntry(session, path, principal, privileges, accessControlEntry);
                    if (addedPrivilegeEntry) {
                        log.debug("Add new ACE to be for {} to ACL at {}", (Object)accessControlEntry.getPrincipalName(), (Object)path);
                    } else {
                        log.debug("ACE for {} contained in ACL at {}: no change", (Object)accessControlEntry.getPrincipalName(), (Object)path);
                    }
                } else {
                    log.debug("No change in ACL, path is empty or principal is null!");
                }
            }
            catch (AccessControlException e) {
                log.debug("No change in ACL: Entry contains invalid privilege.");
            }
        }

        static enum ACLTemplates {
            INSTANCE;
            
            private HashMap<String, AccessControlEntry[]> mandatoryActionACETemplates = new HashMap();

            private ACLTemplates() {
                AccessControlEntry denyPageModificationsACE = new AccessControlEntry(null, false, null, modificationPrivileges);
                AccessControlEntry denyPageContentModificationsACE = new AccessControlEntry(null, false, "/jcr:content*", modificationPrivileges);
                AccessControlEntry denyNodeManagementACE = new AccessControlEntry(null, false, null, nodeManagementPrivileges);
                AccessControlEntry allowPageContentACE = new AccessControlEntry(null, true, "/jcr:content*", modificationPrivileges);
                this.mandatoryActionACETemplates.put("mandatory", new AccessControlEntry[]{denyPageModificationsACE});
                this.mandatoryActionACETemplates.put("MandatoryActionFactory", new AccessControlEntry[]{denyPageModificationsACE});
                this.mandatoryActionACETemplates.put("mandatoryContent", new AccessControlEntry[]{denyPageContentModificationsACE});
                this.mandatoryActionACETemplates.put("MandatoryContentAction", new AccessControlEntry[]{denyPageContentModificationsACE});
                this.mandatoryActionACETemplates.put("mandatoryStructure", new AccessControlEntry[]{denyNodeManagementACE, allowPageContentACE});
                this.mandatoryActionACETemplates.put("MandatoryStructureAction", new AccessControlEntry[]{denyNodeManagementACE, allowPageContentACE});
            }

            public AccessControlEntry[] getMandatoryACE(String actionName) {
                return this.mandatoryActionACETemplates.get(actionName);
            }
        }

    }

}