ddf.security.service.impl.AbstractAuthorizingRealm.java Source code

Java tutorial

Introduction

Here is the source code for ddf.security.service.impl.AbstractAuthorizingRealm.java

Source

/**
 * Copyright (c) Codice Foundation
 *
 * <p>This is free software: you can redistribute it and/or modify it under the terms of the GNU
 * Lesser General Public License as published by the Free Software Foundation, either version 3 of
 * the License, or any later version.
 *
 * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details. A copy of the GNU Lesser General Public
 * License is distributed along with this program and can be found at
 * <http://www.gnu.org/licenses/lgpl.html>.
 */
package ddf.security.service.impl;

import ddf.security.assertion.Attribute;
import ddf.security.assertion.AttributeStatement;
import ddf.security.assertion.SecurityAssertion;
import ddf.security.expansion.Expansion;
import ddf.security.permission.KeyValueCollectionPermission;
import ddf.security.permission.KeyValuePermission;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.Permission;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.CollectionUtils;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Abstraction class used to perform authorization for a realm. This class contains generic methods
 * that can be used to parse out the credentials from an incoming security token. It also handles
 * caching tokens for later use.
 */
public abstract class AbstractAuthorizingRealm extends AuthorizingRealm {

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

    private static final String SAML_ROLE = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role";

    protected Map<ServiceReference, Expansion> userExpansionServices = new ConcurrentHashMap<>();

    protected Map<ServiceReference, Expansion> metacardExpansionServices = new ConcurrentHashMap<>();

    public void addUserExpansion(ServiceReference<Expansion> expansionServiceRef) {
        Bundle bundle = FrameworkUtil.getBundle(AbstractAuthorizingRealm.class);
        if (bundle != null) {
            Expansion expansion = bundle.getBundleContext().getService(expansionServiceRef);
            addUserExpansion(expansionServiceRef, expansion);
        }
    }

    public void addUserExpansion(ServiceReference<Expansion> expansionServiceRef, Expansion expansion) {
        if (expansionServiceRef != null) {
            userExpansionServices.put(expansionServiceRef, expansion);
        }
    }

    public void removeUserExpansion(ServiceReference<Expansion> expansionServiceRef) {
        if (expansionServiceRef != null) {
            userExpansionServices.remove(expansionServiceRef);
        }
    }

    public void addMetacardExpansion(ServiceReference<Expansion> expansionServiceRef) {
        Bundle bundle = FrameworkUtil.getBundle(AbstractAuthorizingRealm.class);
        if (bundle != null) {
            Expansion expansion = bundle.getBundleContext().getService(expansionServiceRef);
            addMetacardExpansion(expansionServiceRef, expansion);
        }
    }

    public void addMetacardExpansion(ServiceReference<Expansion> expansionServiceRef, Expansion expansion) {
        if (expansionServiceRef != null) {
            metacardExpansionServices.put(expansionServiceRef, expansion);
        }
    }

    public void removeMetacardExpansion(ServiceReference<Expansion> expansionServiceRef) {
        if (expansionServiceRef != null) {
            metacardExpansionServices.remove(expansionServiceRef);
        }
    }

    public AbstractAuthorizingRealm() {
        setAuthorizationCachingEnabled(false);
    }

    /**
     * Takes the security attributes about the subject of the incoming security token and builds sets
     * of permissions and roles for use in further checking.
     *
     * @param principalCollection holds the security assertions for the primary principal of this
     *     request
     * @return a new collection of permissions and roles corresponding to the security assertions
     * @throws AuthorizationException if there are no security assertions associated with this
     *     principal collection or if the token cannot be processed successfully.
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        LOGGER.debug("Retrieving authorization info for {}", principalCollection.getPrimaryPrincipal());
        Collection<SecurityAssertion> assertions = principalCollection.byType(SecurityAssertion.class);
        if (assertions.isEmpty()) {
            String msg = "No assertion found, cannot retrieve authorization info.";
            throw new AuthorizationException(msg);
        }
        List<AttributeStatement> attributeStatements = assertions.stream()
                .map(SecurityAssertion::getAttributeStatements).flatMap(List::stream).collect(Collectors.toList());
        Set<Permission> permissions = new HashSet<>();
        Set<String> roles = new HashSet<>();

        Map<String, Set<String>> permissionsMap = new HashMap<>();
        Collection<Expansion> expansionServices = getUserExpansionServices();
        for (AttributeStatement curStatement : attributeStatements) {
            addAttributesToMap(curStatement.getAttributes(), permissionsMap, expansionServices);
        }

        for (Map.Entry<String, Set<String>> entry : permissionsMap.entrySet()) {
            permissions.add(new KeyValuePermission(entry.getKey(), entry.getValue()));
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Adding permission: {} : {}", entry.getKey(), StringUtils.join(entry.getValue(), ","));
            }
        }

        if (permissionsMap.containsKey(SAML_ROLE)) {
            roles.addAll(permissionsMap.get(SAML_ROLE));
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Adding roles to authorization info: {}", StringUtils.join(roles, ","));
            }
        }

        info.setObjectPermissions(permissions);
        info.setRoles(roles);

        return info;
    }

    private void addAttributesToMap(List<Attribute> attributes, Map<String, Set<String>> permissionsMap,
            Collection<Expansion> expansions) {
        Set<String> attributeSet;
        for (Attribute curAttribute : attributes) {
            attributeSet = expandAttributes(curAttribute, expansions);
            if (attributeSet != null) {
                if (permissionsMap.containsKey(curAttribute.getName())) {
                    permissionsMap.get(curAttribute.getName()).addAll(attributeSet);
                } else {
                    permissionsMap.put(curAttribute.getName(), new HashSet<>(attributeSet));
                }
            }
        }
    }

    /**
     * Takes an {@link org.opensaml.saml.saml2.core.Attribute} and utilizes the {@link
     * ddf.security.expansion.Expansion} service to potentially expand it to a different/enhanced set
     * of attributes. This expansion is controlled by the configuration of the expansion service but
     * relies on the name of this attribute as a key. The returned set of Strings represent the
     * possibly expanded set of attributes to be added to the current permissions.
     *
     * @param attribute current attribute whose values are to be potentially expanded
     * @return a set of potentially expanded values
     */
    private Set<String> expandAttributes(Attribute attribute, Collection<Expansion> expansions) {
        Set<String> attributeSet = new HashSet<>();
        String attributeName = attribute.getName();
        attributeSet.addAll(attribute.getValues());
        for (Expansion expansionService : expansions) {
            LOGGER.debug("Expanding attributes for {} - original values: {}", attributeName, attributeSet);
            attributeSet = expansionService.expand(attributeName, attributeSet);
        }
        LOGGER.debug("Expanded attributes for {} - values: {}", attributeName, attributeSet);
        return attributeSet;
    }

    protected List<Permission> expandPermissions(List<Permission> permissions) {
        Collection<Expansion> expansionServices = getMetacardExpansionServices();
        if (CollectionUtils.isEmpty(expansionServices)) {
            return permissions;
        }
        List<Permission> expandedPermissions = new ArrayList<>(permissions.size());
        for (Permission permission : permissions) {
            if (permission instanceof KeyValuePermission) {
                for (Expansion expansionService : expansionServices) {
                    Set<String> expandedSet = expansionService.expand(((KeyValuePermission) permission).getKey(),
                            new HashSet<>(((KeyValuePermission) permission).getValues()));
                    expandedPermissions
                            .add(new KeyValuePermission(((KeyValuePermission) permission).getKey(), expandedSet));
                }
            } else if (permission instanceof KeyValueCollectionPermission) {
                List<Permission> keyValuePermissionList = ((KeyValueCollectionPermission) permission)
                        .getKeyValuePermissionList();
                List<Permission> expandedCollection = expandPermissions(keyValuePermissionList);
                // we know that everything in a key value collection is a key value permission so just do
                // the unchecked cast
                List<KeyValuePermission> castedList = castToKeyValueList(expandedCollection);
                expandedPermissions.add(new KeyValueCollectionPermission(
                        ((KeyValueCollectionPermission) permission).getAction(), castedList));
            } else {
                expandedPermissions.add(permission);
            }
        }

        return expandedPermissions;
    }

    private Collection<Expansion> getUserExpansionServices() {
        return userExpansionServices.values();
    }

    private Collection<Expansion> getMetacardExpansionServices() {
        return metacardExpansionServices.values();
    }

    private <T> List<T> castToKeyValueList(List<Permission> permissionList) {
        return (List<T>) permissionList;
    }
}