Package org.fcrepo.server.security

Source Code of org.fcrepo.server.security.PolicyFinderModule

/* The contents of this file are subject to the license and copyright terms
* detailed in the license directory at the root of the source tree (also
* available online at http://fedora-commons.org/license/).
*/
package org.fcrepo.server.security;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.fcrepo.common.Constants;
import org.fcrepo.common.FaultException;
import org.fcrepo.server.ReadOnlyContext;
import org.fcrepo.server.Server;
import org.fcrepo.server.config.ModuleConfiguration;
import org.fcrepo.server.errors.GeneralException;
import org.fcrepo.server.errors.ObjectNotInLowlevelStorageException;
import org.fcrepo.server.errors.ServerException;
import org.fcrepo.server.errors.ValidationException;
import org.fcrepo.server.storage.DOReader;
import org.fcrepo.server.storage.RepositoryReader;
import org.fcrepo.server.storage.types.Datastream;
import org.fcrepo.server.validation.ValidationUtility;
import org.fcrepo.utilities.FileUtils;
import org.fcrepo.utilities.XmlTransformUtility;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sun.xacml.AbstractPolicy;
import com.sun.xacml.EvaluationCtx;
import com.sun.xacml.PolicySet;
import com.sun.xacml.attr.AttributeValue;
import com.sun.xacml.attr.BagAttribute;
import com.sun.xacml.attr.StringAttribute;
import com.sun.xacml.combine.PolicyCombiningAlgorithm;
import com.sun.xacml.cond.EvaluationResult;
import com.sun.xacml.ctx.Status;
import com.sun.xacml.finder.PolicyFinder;
import com.sun.xacml.finder.PolicyFinderResult;

/**
* XACML PolicyFinder for Fedora.
* <p>
* This provides repository-wide policies and object-specific policies,
* when available.
*/
public class PolicyFinderModule
        extends com.sun.xacml.finder.PolicyFinderModule {

    private static final Logger logger =
            LoggerFactory.getLogger(PolicyFinderModule.class);

    private static final List<String> ERROR_CODE_LIST = new ArrayList<String>(1);

    static {
        ERROR_CODE_LIST.add(Status.STATUS_PROCESSING_ERROR);
    }

    private static final String DEFAULT = "default";

    private static final String DEFAULT_XACML_COMBINING_ALGORITHM = "com.sun.xacml.combine.OrderedDenyOverridesPolicyAlg";

    private static final String XACML_DIST_BASE = "fedora-internal-use";

    private static final String DEFAULT_REPOSITORY_POLICIES_DIRECTORY =
            XACML_DIST_BASE
            + "/fedora-internal-use-repository-policies-approximating-2.0";

    private static final String BACKEND_POLICIES_ACTIVE_DIRECTORY =
            XACML_DIST_BASE + "/fedora-internal-use-backend-service-policies";

    private static final String BE_SECURITY_XML_LOCATION =
            "config/beSecurity.xml";

    private static final String BACKEND_POLICIES_XSL_LOCATION =
            XACML_DIST_BASE + "/build-backend-policy.xsl";

    private static final String COMBINING_ALGORITHM_KEY = "XACML-COMBINING-ALGORITHM";

    private static final String REPOSITORY_POLICIES_DIRECTORY_KEY =
            "REPOSITORY-POLICIES-DIRECTORY";

    private static final String POLICY_SCHEMA_PATH_KEY = "POLICY-SCHEMA-PATH";

    private static final String VALIDATE_REPOSITORY_POLICIES_KEY =
            "VALIDATE-REPOSITORY-POLICIES";

    private static final String VALIDATE_OBJECT_POLICIES_FROM_DATASTREAM_KEY =
            "VALIDATE-OBJECT-POLICIES-FROM-DATASTREAM";

    private final String m_combiningAlgorithm;

    private final String m_serverHome;

    private final String m_repositoryPolicyDirectoryPath;

    private final String m_repositoryBackendPolicyDirectoryPath;

    private final boolean m_validateRepositoryPolicies;

    private final boolean m_validateObjectPoliciesFromDatastream;

    private final PolicyParser m_policyParser;
   
    private final PolicyLoader m_policyLoader;

    private final List<AbstractPolicy> m_repositoryPolicies;

    public PolicyFinderModule(Server server,
                              PolicyLoader policyLoader,
                              ModuleConfiguration authorizationConfig)
            throws GeneralException {
        m_serverHome = server.getHomeDir().getAbsolutePath();

        m_policyLoader = policyLoader;
       
        Map<String,String> moduleParameters = authorizationConfig.getParameters();

        m_repositoryBackendPolicyDirectoryPath = m_serverHome + File.separator
                + BACKEND_POLICIES_ACTIVE_DIRECTORY;

        if (moduleParameters.containsKey(REPOSITORY_POLICIES_DIRECTORY_KEY)) {
            m_repositoryPolicyDirectoryPath =
                    authorizationConfig.getParameter(REPOSITORY_POLICIES_DIRECTORY_KEY, true);
        } else {
            m_repositoryPolicyDirectoryPath = "";
        }

        if (moduleParameters.containsKey(COMBINING_ALGORITHM_KEY)) {
            m_combiningAlgorithm =
                    moduleParameters.get(COMBINING_ALGORITHM_KEY);
        } else {
            m_combiningAlgorithm = DEFAULT_XACML_COMBINING_ALGORITHM;
        }

        if (moduleParameters.containsKey(VALIDATE_REPOSITORY_POLICIES_KEY)) {
            m_validateRepositoryPolicies =
                    (new Boolean(moduleParameters
                            .get(VALIDATE_REPOSITORY_POLICIES_KEY)))
                            .booleanValue();
        } else {
            m_validateRepositoryPolicies = false;
        }
        if (moduleParameters
                .containsKey(VALIDATE_OBJECT_POLICIES_FROM_DATASTREAM_KEY)) {
            try {
                m_validateObjectPoliciesFromDatastream =
                        Boolean.parseBoolean(moduleParameters
                                .get(VALIDATE_OBJECT_POLICIES_FROM_DATASTREAM_KEY));
            } catch (Exception e) {
                throw new GeneralException("bad init parm boolean value for "
                                                        + VALIDATE_OBJECT_POLICIES_FROM_DATASTREAM_KEY,
                                                        e);
            }
        } else {
            m_validateObjectPoliciesFromDatastream = false;
        }

        // Initialize the policy parser given the POLICY_SCHEMA_PATH_KEY
        if (moduleParameters.containsKey(POLICY_SCHEMA_PATH_KEY)) {
            String schemaPath = moduleParameters.get(POLICY_SCHEMA_PATH_KEY);
            File schema;
            if (schemaPath.startsWith(File.separator)){ // absolute
                schema = new File(schemaPath);
            } else {
                schema = new File(new File(m_serverHome), schemaPath);
            }
            try {
                FileInputStream in = new FileInputStream(schema);
                m_policyParser = new PolicyParser(in);
                ValidationUtility.setPolicyParser(m_policyParser);
            } catch (Exception e) {
                throw new GeneralException("Error loading policy"
                                                        + " schema: " + schema.getAbsolutePath(), e);
            }
        } else {
            throw new GeneralException("Policy schema path not"
                                                    + " specified.  Must be given as " + POLICY_SCHEMA_PATH_KEY);
        }

        m_repositoryPolicies = new ArrayList<AbstractPolicy>();
    }

    /**
     * Does nothing at init time.
     */
    @Override
    public void init(PolicyFinder finder) {
        try {
            logger.info("Loading repository policies...");
            setupActivePolicyDirectories();
            m_repositoryPolicies.clear();
            Map<String,AbstractPolicy> repositoryPolicies =
                    m_policyLoader.loadPolicies(m_policyParser,
                    m_validateRepositoryPolicies,
                    new File(m_repositoryBackendPolicyDirectoryPath));
            repositoryPolicies.putAll(
                    m_policyLoader.loadPolicies(m_policyParser,
                                 m_validateRepositoryPolicies,
                                 new File(m_repositoryPolicyDirectoryPath)));
            m_repositoryPolicies.addAll(repositoryPolicies.values());
        } catch (Throwable t) {
            logger.error("Error loading repository policies: " + t.toString(), t);
        }
    }

    private final void generateBackendPolicies() throws Exception {
        logger.info("Generating backend policies...");
        FileUtils.deleteContents(new File(m_repositoryBackendPolicyDirectoryPath));
        BackendPolicies backendPolicies =
                new BackendPolicies(m_serverHome + File.separator
                                    + BE_SECURITY_XML_LOCATION);
        Hashtable tempfiles = backendPolicies.generateBackendPolicies();
        TransformerFactory tfactory = XmlTransformUtility.getTransformerFactory();
        try {
            Iterator iterator = tempfiles.keySet().iterator();
            while (iterator.hasNext()) {
                File f =
                        new File(m_serverHome + File.separator
                                 + BACKEND_POLICIES_XSL_LOCATION); // <<stylesheet
                // location
                StreamSource ss = new StreamSource(f);
                Transformer transformer = tfactory.newTransformer(ss); // xformPath
                String key = (String) iterator.next();
                File infile = new File((String) tempfiles.get(key));
                FileInputStream fis = new FileInputStream(infile);
                FileOutputStream fos =
                        new FileOutputStream(m_repositoryBackendPolicyDirectoryPath
                                             + File.separator + key);
                transformer.transform(new StreamSource(fis),
                                      new StreamResult(fos));
            }
        } finally {
            // we're done with temp files now, so delete them
            Iterator iter = tempfiles.keySet().iterator();
            while (iter.hasNext()) {
                File tempFile = new File((String) tempfiles.get(iter.next()));
                tempFile.delete();
            }
        }
    }

    private void setupActivePolicyDirectories() throws Exception {
        File repoPolicyDir = new File(m_repositoryPolicyDirectoryPath + File.separator + DEFAULT);
        if (!repoPolicyDir.exists()){
            repoPolicyDir.mkdirs();
            File source = new File(m_serverHome + File.separator + DEFAULT_REPOSITORY_POLICIES_DIRECTORY);
            FileUtils.copy(source, repoPolicyDir);
        }
        generateBackendPolicies();
    }


    /**
     * Always returns true, indicating that this impl supports finding policies
     * based on a request.
     */
    @Override
    public boolean isRequestSupported() {
        return true;
    }

    /**
     * Gets a deny-biased policy set that includes all repository-wide and
     * object-specific policies.
     */
    @Override
    public PolicyFinderResult findPolicy(EvaluationCtx context) {
        PolicyFinderResult policyFinderResult = null;
        try {
            List<AbstractPolicy> policies = new ArrayList<AbstractPolicy>(m_repositoryPolicies);
            String pid = getPid(context);
            if (pid != null && !"".equals(pid)) {
                AbstractPolicy objectPolicyFromObject =
                        m_policyLoader.loadObjectPolicy(m_policyParser.copy(),
                                                         pid,
                                                         m_validateObjectPoliciesFromDatastream);
                if (objectPolicyFromObject != null) {
                    policies.add(objectPolicyFromObject);
                }
            }
            PolicyCombiningAlgorithm policyCombiningAlgorithm =
                    (PolicyCombiningAlgorithm) Class
                            .forName(m_combiningAlgorithm).newInstance();
            PolicySet policySet =
                    new PolicySet(new URI(""),
                                  policyCombiningAlgorithm,
                                  null /*
                                   * no general target beyond those of
                                   * multiplexed individual policies
                                   */,
                                  policies);
            policyFinderResult = new PolicyFinderResult(policySet);
        } catch (Exception e) {
            logger.warn("PolicyFinderModule seriously failed to evaluate a policy ", e);
            policyFinderResult =
                    new PolicyFinderResult(new Status(ERROR_CODE_LIST, e
                            .getMessage()));
        }
        return policyFinderResult;
    }

    // get the pid from the context, or null if unable
    public static String getPid(EvaluationCtx context) {
        URI resourceIdType = null;
        URI resourceIdId = null;
        try {
            resourceIdType = new URI(StringAttribute.identifier);
            resourceIdId = new URI(Constants.OBJECT.PID.uri);
        } catch (URISyntaxException e) {
            throw new FaultException("Bad URI syntax", e);
        }
        EvaluationResult attribute
                = context.getResourceAttribute(resourceIdType,
                                               resourceIdId,
                                               null);
        Object element = getAttributeFromEvaluationResult(attribute);
        if (element == null) {
            logger.debug("PolicyFinderModule:getPid exit on "
                    + "can't get contextId on request callback");
            return null;
        }

        if (!(element instanceof StringAttribute)) {
            logger.debug("PolicyFinderModule:getPid exit on "
                    + "couldn't get contextId from xacml request "
                    + "non-string returned");
            return null;
        }

        return ((StringAttribute) element).getValue();
    }

    // copy of code in AttributeFinderModule; consider refactoring
    private static final Object getAttributeFromEvaluationResult(EvaluationResult attribute) {
        if (attribute.indeterminate()) {
            return null;
        }

        if (attribute.getStatus() != null
                && !Status.STATUS_OK.equals(attribute.getStatus())) {
            return null;
        }

        AttributeValue attributeValue = attribute.getAttributeValue();
        if (!(attributeValue instanceof BagAttribute)) {
            return null;
        }

        BagAttribute bag = (BagAttribute) attributeValue;
        if (1 != bag.size()) {
            return null;
        } else {
            return bag.iterator().next();
        }
    }

    // load and parse all policies (*.xml) from a given directory, recursively
    private static List<AbstractPolicy> loadPolicies(PolicyParser parser,
                                                     boolean validate,
                                                     File dir)
            throws IOException, ValidationException {
        List<AbstractPolicy> policies = new ArrayList<AbstractPolicy>();
        for (File file: dir.listFiles()) {
            if (file.isDirectory()) {
                policies.addAll(loadPolicies(parser, validate, file));
            } else {
                if (file.getName().endsWith(".xml")) {
                    logger.info("Loading policy: " + file.getPath());
                    InputStream policyStream = new FileInputStream(file);
                    policies.add(parser.parse(policyStream, validate));
                }
            }
        }
        return policies;
    }

}
TOP

Related Classes of org.fcrepo.server.security.PolicyFinderModule

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.