All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.adobe.acs.commons.users.impl.Ace Maven / Gradle / Ivy

There is a newer version: 6.9.4
Show newest version
/*
 * ACS AEM Commons
 *
 * Copyright (C) 2013 - 2023 Adobe
 *
 * Licensed 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 com.adobe.acs.commons.users.impl;

import com.adobe.acs.commons.util.ParameterUtil;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.jackrabbit.api.security.JackrabbitAccessControlEntry;
import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jcr.RepositoryException;
import javax.jcr.Value;
import javax.jcr.security.AccessControlManager;
import javax.jcr.security.Privilege;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * ACE OSGi Config Format
 *
 * type=allow;privileges=jcr:read,rep:write;path=/content/foo;rep:glob=/jcr:content/*
 *
 * REQUIRED
 * - type = allow | deny
 * - privileges = privilegeNames comma separated
 * - path = absolute path (must exist in JCR)
 *
 * OPTIONAL
 * - rep:glob = rep glob expression
 * - rep:ntNames = ntNames expression
 */
public final class Ace {
    private static final Logger log = LoggerFactory.getLogger(Ace.class);

    private static final String PARAM_DELIMITER = ";";
    private static final String KEY_VALUE_SEPARATOR = "=";
    private static final String LIST_SEPARATOR = ",";

    private static final String PROP_TYPE = "type";
    private static final String PROP_PATH = "path";
    private static final String PROP_PRIVILEGES = "privileges";
    private static final String PROP_REP_GLOB = "rep:glob";
    private static final String PROP_REP_NT_NAMES = "rep:ntNames";
    private static final String PROP_REP_ITEM_NAMES = "rep:itemNames";
    private static final String PROP_REP_PREFIXES = "rep:prefixes";


    private String type;
    private String path;
    private String repGlob = null;
    private List repNtNames = new ArrayList();
    private List repItemNames = new ArrayList();
    private List repPrefixes = new ArrayList();
    private final List privilegeNames = new ArrayList();
    private boolean exists = false;

    @SuppressWarnings("squid:S3776")
    public Ace(String raw) throws EnsureAuthorizableException {
        String[] segments = StringUtils.split(raw, PARAM_DELIMITER);

        for (String segment : segments) {
            Map.Entry entry = ParameterUtil.toMapEntry(segment, KEY_VALUE_SEPARATOR);

            if (entry == null) {
                continue;
            }
            if (StringUtils.equals(PROP_TYPE, entry.getKey())) {
                this.type = StringUtils.stripToNull(entry.getValue());
            } else if (StringUtils.equals(PROP_PATH, entry.getKey())) {
                this.path = StringUtils.stripToNull(entry.getValue());
            } else if (StringUtils.equals(PROP_REP_GLOB, entry.getKey())) {
                this.repGlob = StringUtils.stripToEmpty(entry.getValue());
            } else if (StringUtils.equals(PROP_REP_NT_NAMES, entry.getKey())) {
                this.repNtNames.addAll(Arrays.asList(StringUtils.split(StringUtils.stripToEmpty(entry.getValue()), LIST_SEPARATOR)));
            } else if (StringUtils.equals(PROP_REP_ITEM_NAMES, entry.getKey())) {
                this.repItemNames.addAll(Arrays.asList(StringUtils.split(StringUtils.stripToEmpty(entry.getValue()), LIST_SEPARATOR)));
            } else if (StringUtils.equals(PROP_REP_PREFIXES, entry.getKey())) {
                this.repPrefixes.addAll(Arrays.asList(StringUtils.split(StringUtils.stripToEmpty(entry.getValue()), LIST_SEPARATOR)));
            } else if (StringUtils.equals(PROP_PRIVILEGES, entry.getKey())) {
                for (String privilege : StringUtils.split(entry.getValue(), LIST_SEPARATOR)) {
                    privilege = StringUtils.stripToNull(privilege);
                    if (privilege != null) {
                        this.privilegeNames.add(privilege);
                    }
                }
            }
        }

        validate(this.type, this.path, this.privilegeNames);
    }

    protected void validate(String type, String path, List privilegeNames) throws EnsureAuthorizableException {
        if (!ArrayUtils.contains(new String[] { "allow", "deny"}, type)) {
            throw new EnsureAuthorizableException("Ensure Service User requires valid type. [ " + type + " ] type is invalid");
        } else if (!StringUtils.startsWith(path , "/")) {
            throw new EnsureAuthorizableException("Ensure Service User requires an absolute path. [ " + path + " ] path is invalid");
        } else if (privilegeNames.size() < 1) {
            throw new EnsureAuthorizableException("Ensure Service User requires at least 1 privilege to apply.");
        }
    }

    public boolean isAllow() {
        return StringUtils.equals("allow", this.type);
    }

    public String getContentPath() {
        return path;
    }


    public List getPrivilegeNames() {
        return Collections.unmodifiableList(privilegeNames);
    }

    public List getPrivileges(AccessControlManager accessControlManager) {
        final List privileges = new ArrayList();

        for (String privilegeName : getPrivilegeNames()) {
            try {
                privileges.add(accessControlManager.privilegeFromName(privilegeName));
            } catch (RepositoryException e) {
                log.error("Unable to convert provided privilege name [ {} ] to a JCR Privilege. Skipping...", privilegeName);
            }
        }

        return privileges;
    }

    public void setExists(boolean exists) {
        this.exists = exists;
    }

    public boolean isExists() {
        return exists;
    }

    /** rep:glob **/

    public String getRepGlob() {
        return repGlob;
    }

    public boolean hasRepGlob() {
        return getRepGlob() != null;
    }

    /** rep:ntNames **/

    public List getRepNtNames() {
        return Collections.unmodifiableList(repNtNames);
    }

    public boolean hasRepNtNames() {
        return !getRepNtNames().isEmpty();
    }


    /** rep:itemNames **/

    public List getRepItemNames() {
        return Collections.unmodifiableList(repItemNames);
    }

    public boolean hasRepItemNames() {
        return !getRepItemNames().isEmpty();
    }

    /** rep:prefixes **/

    public List getRepPrefixes() {
        return Collections.unmodifiableList(repPrefixes);
    }

    public boolean hasRepPrefixes() {
        return !getRepPrefixes().isEmpty();
    }

    /**
     * Determines if the configured ACE is the same as the actual ACE in the JCR.
     * @param actual the actual ACE in the JCR
     * @return true if both ACEs are logically the same, false if not.
     * @throws RepositoryException
     */
    public boolean isSameAs(JackrabbitAccessControlEntry actual) throws RepositoryException {
        // Allow vs Deny
        if (actual.isAllow() != this.isAllow()) {
            return false;
        }

        // Privileges
        final List actualPrivileges = Arrays.asList(AccessControlUtils.namesFromPrivileges(actual.getPrivileges()));
        if (!CollectionUtils.isEqualCollection(actualPrivileges, getPrivilegeNames())) {
            return false;
        }

        // rep:glob

        // We are converting the single value RepGlob into a List for convenience
        if(!isRestrictionValid(this.hasRepGlob(), actual.getRestrictions(PROP_REP_GLOB), Arrays.asList(new String[]{this.getRepGlob()}))) {
            return false;
        }

        // rep:ntNames
        if(!isRestrictionValid(this.hasRepNtNames(), actual.getRestrictions(PROP_REP_NT_NAMES), this.getRepNtNames())) {
            return false;
        }

        // rep:itemNames
        if(!isRestrictionValid(this.hasRepItemNames(), actual.getRestrictions(PROP_REP_ITEM_NAMES), this.getRepItemNames())) {
            return false;
        }

        // rep:prefixes
        if(!isRestrictionValid(this.hasRepPrefixes(), actual.getRestrictions(PROP_REP_PREFIXES), this.getRepPrefixes())) {
            return false;
        }

        return true;
    }

    @SuppressWarnings("squid:S2589")
    private boolean isRestrictionValid(boolean configExists, Value[] actualValues, List configValues) {
        final ArrayList actualRestrictions = new ArrayList();

        if (configExists && actualValues == null) {
            // configuration has rep:glob, but the actual does not
            return false;
        } else if (!configExists && actualValues != null) {
            // configuration does NOT have rep:XXX, but actual does
            return false;
        } else if (configExists && actualValues != null) {
            // configuration has rep:XXX and actual does too
            for (final Value value : actualValues) {
                actualRestrictions.add(value.toString());
            }

            if (!CollectionUtils.isEqualCollection(actualRestrictions, configValues)) {
                return false;
            }
        } else {
            // neither has this rep:XXX so they match
        }

        return true;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy