it.cnr.icar.eric.server.security.authorization.RegistryPolicyFinderModule.java Source code

Java tutorial

Introduction

Here is the source code for it.cnr.icar.eric.server.security.authorization.RegistryPolicyFinderModule.java

Source

/*
 * ====================================================================
 * This file is part of the ebXML Registry by Icar Cnr v3.2 
 * ("eRICv32" in the following disclaimer).
 *
 * "eRICv32" is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * "eRICv32" 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License Version 3
 * along with "eRICv32".  If not, see <http://www.gnu.org/licenses/>.
 *
 * eRICv32 is a forked, derivative work, based on:
 *    - freebXML Registry, a royalty-free, open source implementation of the ebXML Registry standard,
 *      which was published under the "freebxml License, Version 1.1";
 *   - ebXML OMAR v3.2 Edition, published under the GNU GPL v3 by S. Krushe & P. Arwanitis.
 * 
 * All derivative software changes and additions are made under
 *
 * Copyright (C) 2013 Ing. Antonio Messina <messina@pa.icar.cnr.it>
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the freebxml Software Foundation.  For more
 * information on the freebxml Software Foundation, please see
 * "http://www.freebxml.org/".
 *
 * This product includes software developed by the Apache Software
 * Foundation (http://www.apache.org/).
 *
 * ====================================================================
 */
package it.cnr.icar.eric.server.security.authorization;

import it.cnr.icar.eric.common.BindingUtility;
import it.cnr.icar.eric.common.RepositoryItem;
import it.cnr.icar.eric.server.common.RegistryProperties;
import it.cnr.icar.eric.server.common.ServerRequestContext;
import it.cnr.icar.eric.server.persistence.PersistenceManager;
import it.cnr.icar.eric.server.persistence.PersistenceManagerFactory;
import it.cnr.icar.eric.server.repository.RepositoryManager;
import it.cnr.icar.eric.server.repository.RepositoryManagerFactory;
import it.cnr.icar.eric.server.security.authentication.AuthenticationServiceImpl;
import it.cnr.icar.eric.server.util.ServerResourceBundle;

import java.lang.reflect.UndeclaredThrowableException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import javax.activation.DataHandler;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.registry.RegistryException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.oasis.ebxml.registry.bindings.query.ResponseOptionType;
import org.oasis.ebxml.registry.bindings.query.ResponseOptionType.ReturnType;
import org.oasis.ebxml.registry.bindings.rim.IdentifiableType;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import com.sun.xacml.AbstractPolicy;
import com.sun.xacml.EvaluationCtx;
import com.sun.xacml.MatchResult;
import com.sun.xacml.Policy;
import com.sun.xacml.PolicySet;
import com.sun.xacml.PolicyTreeElement;
import com.sun.xacml.attr.AttributeValue;
import com.sun.xacml.finder.PolicyFinder;
import com.sun.xacml.finder.PolicyFinderModule;
import com.sun.xacml.finder.PolicyFinderResult;

/**
 * This module represents a collection of files containing polices,
 * each of which will be searched through when trying to find a
 * policy that is applicable to a specific request.
 *
 * @author Farrukh Najmi
 */
public class RegistryPolicyFinderModule extends PolicyFinderModule {

    private static Log log = LogFactory.getLog(RegistryPolicyFinderModule.class);

    PolicyFinder finder = null;

    // The default Access Control Policy for the registry
    private AbstractPolicy defaultPolicy;
    private String idForDefaultACP = null;

    RepositoryManager rm = RepositoryManagerFactory.getInstance().getRepositoryManager();
    PersistenceManager pm = PersistenceManagerFactory.getInstance().getPersistenceManager();
    BindingUtility bu = BindingUtility.getInstance();
    ServerRequestContext requestContext = null;

    public RegistryPolicyFinderModule() {
        try {
            requestContext = new ServerRequestContext("RegistryPolicyFinderModule:findPolicy", null);
            requestContext.setUser(AuthenticationServiceImpl.getInstance().registryOperator);
        } catch (RegistryException e) {
            log.error(e, e);
            throw new UndeclaredThrowableException(e);
        }
    }

    // Load the default Access Control Policy for the registry
    private AbstractPolicy loadDefaultPolicy(ServerRequestContext requestContext) throws RegistryException {
        AbstractPolicy policy = null;
        try {
            idForDefaultACP = RegistryProperties.getInstance()
                    .getProperty("eric.security.authorization.defaultACP");
            policy = loadPolicy(requestContext, idForDefaultACP);
            if (policy == null) {
                throw new RegistryException(
                        ServerResourceBundle.getInstance().getString("message.defaultAccessControlPolicy"));
            }
        } catch (RegistryException e) {
            throw new RegistryException(
                    ServerResourceBundle.getInstance().getString("message.defaultAccessControlPolicy"), e);
        }
        return policy;
    }

    // Load the Access Control Policy that has the specified id
    private AbstractPolicy loadPolicy(ServerRequestContext requestContext, String id) throws RegistryException {
        if (log.isDebugEnabled()) {
            log.debug("Loading policy for id=" + id);
        }

        AbstractPolicy policy = null;
        RepositoryItem ri = null;

        if (id == null) {
            return policy;
        }

        if ((id.equalsIgnoreCase(idForDefaultACP)) && (defaultPolicy != null)) {
            return defaultPolicy;
        }

        //First check if it is in the repository as a top level Policy
        try {
            ri = rm.getRepositoryItem(id);
        } catch (RegistryException e) {
            log.debug(e, e);
        }

        //Not found as top level repository item
        //See if it is a composed policy within a top level policy in repository 
        if (ri == null) {
            //No policy found. Next find an ACP with specified URI as
            //a value for a Slot named "ComposedPolicies".
            AbstractPolicy rootPolicy = loadRootPolicyFor(requestContext, id);

            try {
                if (rootPolicy != null) {
                    PolicyTreeElement policyElement = getDescendantPolicyTreeElement(rootPolicy, new URI(id));

                    if (policyElement instanceof AbstractPolicy) {
                        policy = (AbstractPolicy) policyElement;
                    }
                }
            } catch (URISyntaxException e) {
                throw new RegistryException(e);
            }
        } else {
            DataHandler dh = ri.getDataHandler();
            policy = loadPolicy(dh, finder);
        }

        return policy;
    }

    /**
     * Gets the Collection of all POlicyElements that are Descendants of specified PolicyTreeElement
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public Collection getDescendantPolicyTreeElements(PolicyTreeElement parent) {
        List<?> children = parent.getChildren();
        List descendants = new ArrayList(parent.getChildren());
        Iterator<?> iter = children.iterator();

        while (iter.hasNext()) {
            PolicyTreeElement child = (PolicyTreeElement) iter.next();

            descendants.addAll(getDescendantPolicyTreeElements(child));
        }

        return descendants;
    }

    /**
     * Get the descendent AbstractPolciy composed within specified PolicyTreeElement object that
     * matches the specified id.
     */
    public PolicyTreeElement getDescendantPolicyTreeElement(PolicyTreeElement parent, URI descendantId) {
        PolicyTreeElement descendent = null;

        //System.err.println("AbstractPolicy.getDescendantPolicyTreeElement: descendantId=" + descendantId);
        Collection<?> descendants = getDescendantPolicyTreeElements(parent);

        Iterator<?> iter = descendants.iterator();

        while (iter.hasNext()) {
            PolicyTreeElement policyElement = (PolicyTreeElement) iter.next();

            //System.err.println("AbstractPolicy.getDescendantPolicyTreeElement: currentId=" + policyElement.getId());
            if (policyElement.getId().toString().equals(descendantId.toString())) {
                descendent = policyElement;

                break;
            }
        }

        return descendent;
    }

    /**
     * Returns the root or top level policy that contains a descendant policy with specified
     * id.
     */
    @SuppressWarnings("rawtypes")
    private AbstractPolicy loadRootPolicyFor(ServerRequestContext context, String composedPolicyId)
            throws RegistryException {
        log.debug("Loading root policy for composed policy with id=" + composedPolicyId);

        AbstractPolicy rootPolicy = null;

        try {
            String id = null;

            ResponseOptionType ebResponseOptionType = BindingUtility.getInstance().queryFac
                    .createResponseOptionType();
            ebResponseOptionType.setReturnType(ReturnType.LEAF_CLASS);
            ebResponseOptionType.setReturnComposedObjects(true);

            String query = "SELECT policy.* from ExtrinsicObject policy, Slot s WHERE " + "((policy.objectType = '"
                    + BindingUtility.CANONICAL_OBJECT_TYPE_LID_Policy + "') OR " + "(policy.objectType = '"
                    + BindingUtility.CANONICAL_OBJECT_TYPE_LID_PolicySet
                    + "')) AND s.name_ = 'ComposedPolicies' AND policy.id = " + "s.parent AND s.value = '"
                    + composedPolicyId + "'";

            List objectRefs = new ArrayList();
            List<IdentifiableType> ebIdentifiableTypeResultList = pm.executeSQLQuery(context, query,
                    ebResponseOptionType, "ExtrinsicObject", objectRefs);
            List<String> ids = bu.getIdsFromRegistryObjectTypes(ebIdentifiableTypeResultList);

            int cnt = ids.size();

            if (cnt > 1) {
                log.warn(ServerResourceBundle.getInstance().getString(
                        "message.MoreThan1AccessControlPolicyFoundContainingComposedPolicy",
                        new Object[] { composedPolicyId }));
            }

            if (cnt >= 1) {
                id = ids.get(0);

                rootPolicy = loadPolicy(context, id);
            }
        } catch (RegistryException e) {
            throw e;
        } catch (Exception e) {
            throw new RegistryException(e);
        }

        return rootPolicy;
    }

    /**
     * Loads a policy from the DataHandler, using the specified
     * <code>PolicyFinder</code> to help with instantiating PolicySets.
     *
     * @param DataHandler the DataHandler to load the policy from
     * @param finder a PolicyFinder used to help in instantiating PolicySets
     * @param handler an error handler used to print warnings and errors
     *                during parsing
     *
     * @return a policy associated with the specified DataHandler
     *
     * @throws RegistryException exception thrown if there is a problem reading the DataHandler's input stream
     */
    private static AbstractPolicy loadPolicy(DataHandler dh, PolicyFinder finder) throws RegistryException {
        AbstractPolicy policy = null;

        try {
            // create the factory
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setIgnoringComments(true);

            DocumentBuilder db = null;

            // set the factory to work the way the system requires
            // we're not doing any validation
            factory.setNamespaceAware(false);
            factory.setValidating(false);

            db = factory.newDocumentBuilder();

            // try to load the policy file
            Document doc = db.parse(dh.getInputStream());

            // handle the policy, if it's a known type
            Element root = doc.getDocumentElement();
            String name = root.getTagName();

            if (name.equals("Policy")) {
                policy = Policy.getInstance(root);
            } else if (name.equals("PolicySet")) {
                policy = PolicySet.getInstance(root, finder);
            } else {
                // this isn't a root type that we know how to handle
                throw new RegistryException(ServerResourceBundle.getInstance()
                        .getString("message.unknownRootDocumentType", new Object[] { name }));
            }
        } catch (Exception e) {
            log.error(ServerResourceBundle.getInstance().getString("message.FailedToLoadPolicy"), e);
            throw new RegistryException(e);
        }

        return policy;
    }

    /**
     * Returns true if the module supports finding policies based on a
     * request (ie, target matching). By default this method returns false.
     *
     * @return true if request retrieval is supported
     */
    public boolean isRequestSupported() {
        return true;
    }

    /**
     * Returns true if the module supports finding policies based on an
     * id reference (in a PolicySet). By default this method returns false.
     *
     * @return true if idReference retrieval is supported
     */
    public boolean isIdReferenceSupported() {
        return true;
    }

    public void init(PolicyFinder finder) {
        this.finder = finder;
    }

    /**
     * Gets the id for the PolicySet object for the objects matching specified resourceId.
     *
     */
    @SuppressWarnings("rawtypes")
    private String getRegistryObjectPolicyId(ServerRequestContext context, String resourceId)
            throws RegistryException {
        String id = null;

        boolean assumeCanonicalObjectsUseDefaultACP = Boolean
                .valueOf(RegistryProperties.getInstance()
                        .getProperty("eric.security.authorization.assumeCanonicalObjectsUseDefaultACP", "true"))
                .booleanValue();
        boolean checkForCustomACP = Boolean
                .valueOf(RegistryProperties.getInstance()
                        .getProperty("eric.security.authorization.customAccessControlPoliciesEnabled", "false"))
                .booleanValue();

        if (!checkForCustomACP || (assumeCanonicalObjectsUseDefaultACP
                && it.cnr.icar.eric.common.Utility.isCanonicalObjectId(resourceId))) {
            getDefaultPolicy(context);
            return idForDefaultACP;
        } else {
            try {
                ResponseOptionType ebResponseOptionType = BindingUtility.getInstance().queryFac
                        .createResponseOptionType();
                ebResponseOptionType.setReturnType(ReturnType.LEAF_CLASS);
                ebResponseOptionType.setReturnComposedObjects(false);

                String query = "SELECT policy.* from ExtrinsicObject policy, Association ass WHERE ass.targetObject = '"
                        + resourceId + "' AND ass.associationType = '"
                        + BindingUtility.CANONICAL_ASSOCIATION_TYPE_ID_AccessControlPolicyFor
                        + "' AND ass.sourceObject = policy.id";

                List objectRefs = new ArrayList();
                List<IdentifiableType> ebIdentifiableTypeResultList = pm.executeSQLQuery(context, query,
                        ebResponseOptionType, "ExtrinsicObject", objectRefs);
                List<String> ids = bu.getIdsFromRegistryObjectTypes(ebIdentifiableTypeResultList);

                int cnt = ids.size();

                if (cnt == 0) {
                    //No policy defined for the RegistryObject
                    //See if a policy is defined for the ObjectType corresponding to this RegistryObject's objectType
                    query = "SELECT policy.* from ExtrinsicObject policy, RegistryObject ro, ClassificationNode ot, Association ass WHERE ass.targetObject = ot.id AND ass.associationType = '"
                            + BindingUtility.CANONICAL_ASSOCIATION_TYPE_ID_AccessControlPolicyFor
                            + "' AND ass.sourceObject = policy.id AND ro.id = '" + resourceId
                            + "' AND ro.objectType = ot.id";

                    objectRefs = new ArrayList();
                    ebIdentifiableTypeResultList = pm.executeSQLQuery(context, query, ebResponseOptionType,
                            "ExtrinsicObject", objectRefs);
                    ids = bu.getIdsFromRegistryObjectTypes(ebIdentifiableTypeResultList);

                    cnt = ids.size();
                }

                if (cnt > 1) {
                    log.warn(ServerResourceBundle.getInstance()
                            .getString("message.MoreThan1AccessControlPolicyWithId", new Object[] { resourceId }));
                }

                if (cnt >= 1) {
                    id = ids.get(0);
                }

            } catch (RegistryException e) {
                context.rollback();
                throw e;
            } catch (Exception e) {
                context.rollback();
                throw new RegistryException(e);
            }

            context.commit();
        }

        log.trace("RegistryAttributeFinderModule.getRegistryObjectPolicyId: resourceId=" + resourceId + " policyId="
                + id);
        return id;
    }

    /**
     * Finds a policy based on a request's context. Returns the custom Policy associated with
     * resource if any, otherwise return the registry's default Policy.
     *
     * This will always do a Target match to make sure that the given policy applies.
     *
     *
     * @param context the representation of the request data
     *
     * @return the result of trying to find an applicable policy
     */
    @SuppressWarnings("unused")
    public PolicyFinderResult findPolicy(EvaluationCtx context) {

        AbstractPolicy roPolicy = null;
        AttributeValue resourceIdAttr = context.getResourceId();
        String resourceId = resourceIdAttr.encode(); //??Suggest marshal instead of encode
        String policyId = null;

        //Need to use an internal ServerRequestContext separate from request with registryOperator
        //to bypass infinite recursion in auth check in qm.getRegistryObject()
        //ServerRequestContext requestContext = AuthorizationServiceImpl.getRequestContext(context);                

        try {
            policyId = getRegistryObjectPolicyId(requestContext, resourceId);
            roPolicy = loadPolicy(requestContext, policyId);
        } catch (RegistryException e) {
            log.error(e, e);
        }

        if (roPolicy != null) {
            MatchResult match = roPolicy.match(context);
            int result = match.getResult();
            if (result == MatchResult.INDETERMINATE) {
                log.warn(ServerResourceBundle.getInstance().getString("message.INDETERMINATEPolicyMatchForObjectId",
                        new Object[] { resourceIdAttr, policyId, match.getStatus().toString() }));
            } else if (result == MatchResult.MATCH) {
                return new PolicyFinderResult(roPolicy);
            }
        }

        // roPolicy had no MATCH so use defaultPolicy
        // see if we match

        AbstractPolicy defaulACP = getDefaultPolicy(requestContext);
        if (defaulACP == null) {
            return new PolicyFinderResult();
        }

        MatchResult match = defaulACP.match(context);
        int result = match.getResult();
        if (result == MatchResult.INDETERMINATE) {
            // if there was an error, we stop right away
            return new PolicyFinderResult(match.getStatus());
        } else if (result == MatchResult.MATCH) {
            // if we found a policy, return it, otherwise we're N/A
            if (defaulACP != null) {
                return new PolicyFinderResult(defaulACP);
            } else {
                return new PolicyFinderResult();
            }
        } else {
            // Return N/A
            return new PolicyFinderResult();
        }
    }

    /**
     * Tries to find one and only one matching policy given the idReference
     * First it tries to find a RegistryObject with specified URI as id.
     * If none is found, it tries to find an ACP with specified URI as
     * a value for a Slot named "ComposedPolicies".
     *
     *
     * @param idReference an identifier specifying some policy
     * @param type type of reference (policy or policySet) as identified by
     *             the fields in <code>PolicyReference</code>
     *
     * @return the result of looking for a matching policy
     */
    public PolicyFinderResult findPolicy(URI idReference, int type) {

        AbstractPolicy policy = null;

        //??XACML: WHy does this call from xacml code not provide EvaluationCtx where we could get ServerRequestContext
        //TODO: Figure out a solution to this 
        ServerRequestContext context = null;
        try {
            context = new ServerRequestContext("RegistryPolicyFinderModule.findPolicy", null);

            String id = idReference.toString();
            policy = loadPolicy(context, id);
        } catch (RegistryException e) {
            log.error(e);
        } finally {
            try {
                context.rollback();
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }

        if (policy == null) {
            log.warn(ServerResourceBundle.getInstance().getString("message.findPolicyFailedToFindPolicyWithId",
                    new Object[] { idReference }));
            return new PolicyFinderResult();
        } else {
            return new PolicyFinderResult(policy);
        }
    }

    public AbstractPolicy getDefaultPolicy(ServerRequestContext requestContext) {
        try {
            if (defaultPolicy == null) {
                defaultPolicy = loadDefaultPolicy(requestContext);
            }
        } catch (RegistryException e) {
            log.error(e);
        }
        return defaultPolicy;
    }

}