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

org.fcrepo.auth.roles.common.AbstractRolesAuthorizationDelegate Maven / Gradle / Ivy

Go to download

This module adds access roles to the repository tree. It creates a REST/JSON API for post and get of access roles on Fedora objects and datastreams. It supports the querying of effective roles by authorization delegates.

There is a newer version: 4.7.5
Show newest version
/*
 * Copyright 2015 DuraSpace, Inc.
 *
 * 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 org.fcrepo.auth.roles.common;

import static java.util.stream.Collectors.toSet;

import java.security.Principal;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.Set;

import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;

import org.fcrepo.auth.common.FedoraAuthorizationDelegate;
import org.fcrepo.http.commons.session.SessionFactory;
import org.fcrepo.kernel.api.exception.RepositoryRuntimeException;
import org.modeshape.jcr.value.Path;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * Policy enforcement point for roles-based authentication
 * @author Gregory Jansen
 */
public abstract class AbstractRolesAuthorizationDelegate implements FedoraAuthorizationDelegate {

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

    protected static final String AUTHZ_DETECTION = "/{" +
            Constants.JcrName.NS_URI + "}";

    private static final String[] REMOVE_ACTIONS = {"remove"};

    @Autowired
    private AccessRolesProvider accessRolesProvider = null;

    @Autowired
    private SessionFactory sessionFactory = null;

    /**
     * Gather effectives roles
     *
     * @param acl access control list
     * @param principals effective principals
     * @return set of effective content roles
     */
    public static Set resolveUserRoles(final Map> acl,
                    final Collection principals) {
        return principals.stream().map(Principal::getName).filter(acl::containsKey)
            .peek(principal -> LOGGER.debug("request principal matched role assignment: {}", principal))
            .map(acl::get)
            .flatMap(Collection::stream)
            .collect(toSet());
    }

    @Override
    public boolean hasPermission(final Session session, final Path absPath, final String[] actions) {
        LOGGER.debug("Does user have permission for actions: {}, on path: {}", actions, absPath);
        final boolean permission = doHasPermission(session, absPath, actions);

        LOGGER.debug("Permission for actions: {}, on: {} = {}", actions, absPath, permission);
        return permission;
    }

    private boolean doHasPermission(final Session session, final Path absPath, final String[] actions) {
        final Set roles;

        final Principal userPrincipal = getUserPrincipal(session);
        if (userPrincipal == null) {
            return false;
        }

        final Set allPrincipals = getPrincipals(session);
        if (allPrincipals == null) {
            return false;
        }

        try {
            final Session internalSession = sessionFactory.getInternalSession();
            final Map> acl =
                    accessRolesProvider.findRolesForPath(absPath,
                            internalSession);
            roles = resolveUserRoles(acl, allPrincipals);
            LOGGER.debug("roles for this request: {}", roles);
        } catch (final RepositoryException e) {
            throw new RepositoryRuntimeException("Cannot look up node information on " + absPath +
                    " for permissions check.", e);
        }

        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("roles: {}, actions: {}, path: {}", roles, actions, absPath);
            if (actions.length > 1) { // have yet to see more than one
                LOGGER.debug("FOUND MULTIPLE ACTIONS: {}", Arrays
                        .toString(actions));
            }
        }

        if (actions.length == 1 && "remove_child_nodes".equals(actions[0])) {
            // in roles-based ACLs, the permission to remove children is
            // conferred by earlier check for "remove_node" on the child node
            // itself.
            return true;
        }

        if (!rolesHavePermission(session, absPath.toString(), actions, roles)) {
            return false;
        }

        if (actions.length == 1 && "remove".equals(actions[0])) {
            // you must be able to delete all the children
            // TODO make recursive/ACL-query-based check configurable
            return canRemoveChildrenRecursive(session, absPath.toString(),
                    allPrincipals, roles);
        }
        return true;
    }

    private static Principal getUserPrincipal(final Session session) {
        final Object value = session.getAttribute(FEDORA_USER_PRINCIPAL);
        if (value instanceof Principal) {
            return (Principal) value;
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    private static Set getPrincipals(final Session session) {
        final Object value = session.getAttribute(FEDORA_ALL_PRINCIPALS);
        if (value instanceof Set) {
            return (Set) value;
        }
        return null;
    }

    /**
     * @param userSession the user session
     * @param parentPath the parent path
     * @param allPrincipals all principals
     * @param parentRoles the roles on the parent
     * @return true if permitted
     */
    private boolean canRemoveChildrenRecursive(final Session userSession,
                                               final String parentPath,
                                               final Set allPrincipals,
                                               final Set parentRoles) {
        try {
            final Session internalSession = sessionFactory.getInternalSession();
            LOGGER.debug("Recursive child remove permission checks for: {}",
                         parentPath);
            final Node parent = internalSession.getNode(parentPath);
            if (!parent.hasNodes()) {
                return true;
            }
            final NodeIterator ni = parent.getNodes();
            while (ni.hasNext()) {
                final Node n = ni.nextNode();
                // are there unique roles?
                final Set roles;
                final Map> acl = accessRolesProvider.getRoles(n, false);

                if (acl != null) {
                    roles = resolveUserRoles(acl, allPrincipals);
                } else {
                    roles = parentRoles;
                }
                if (rolesHavePermission(userSession, n.getPath(),
                        REMOVE_ACTIONS,
                        roles)) {

                    if (!canRemoveChildrenRecursive(userSession, n.getPath(),
                            allPrincipals, roles)) {
                        return false;
                    }
                } else {
                    LOGGER.info("Remove permission denied at {} with roles {}", n.getPath(), roles);
                    return false;
                }
            }
            return true;
        } catch (final RepositoryException e) {
            throw new RepositoryRuntimeException(
                    "Cannot lookup child permission check information for " +
                            parentPath, e);
        }
    }

    /**
     * Subclasses must override this method to determine permissions based on
     * supplied roles.
     * 
     * @param userSession the user session
     * @param absPath path to the object
     * @param actions requested action
     * @param roles effective roles for this request and content
     * @return true if role has permission
     */
    public abstract boolean rolesHavePermission(final Session userSession, final String absPath,
            final String[] actions, final Set roles);

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy