Package com.sun.xacml.support.finder

Source Code of com.sun.xacml.support.finder.PolicyCollection$VersionComparator

/*
* @(#)PolicyCollection.java
*
* Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*   1. Redistribution of source code must retain the above copyright notice,
*      this list of conditions and the following disclaimer.
*
*   2. Redistribution in binary form must reproduce the above copyright
*      notice, this list of conditions and the following disclaimer in the
*      documentation and/or other materials provided with the distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
* ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN")
* AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
* AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
* REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
* INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
* OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
* EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed or intended for use in
* the design, construction, operation or maintenance of any nuclear facility.
*/

package com.sun.xacml.support.finder;

import java.net.URI;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.StringTokenizer;
import java.util.TreeSet;

import com.sun.xacml.AbstractPolicy;
import com.sun.xacml.EvaluationCtx;
import com.sun.xacml.MatchResult;
import com.sun.xacml.Policy;
import com.sun.xacml.PolicyMetaData;
import com.sun.xacml.PolicyReference;
import com.sun.xacml.PolicySet;
import com.sun.xacml.Target;
import com.sun.xacml.TargetMatch;
import com.sun.xacml.TargetSection;
import com.sun.xacml.VersionConstraints;
import com.sun.xacml.combine.PolicyCombiningAlgorithm;
import com.sun.xacml.ctx.Status;

/**
* This class handles collections of <code>AbstractPolicy</code> instances, and provides some
* commonly useful operations. Specifically, it lets you retrieve matching policies (based on
* reference or context), it optionally handles wrapping multiple matches under a single PolicySet,
* and it manages different versions of policies correctly. This class is intended for use as a
* backing store to <code>PolicyFinderModule</code>s, but in practice may have many uses.
* <p>
* Note that this class will accept multiple versions of the same policy. This means that when you
* retieve a policy by reference, you will get the correct version. It also means that when you
* retrieve a policy based on context, there may be multiple revisions of the same policy, any
* number of which may apply. Generally speaking, the correct behavior here is not to return all of
* these policies, since they are (virtually speaking) the same policy, but may have conflicting
* rules. So, as a simplification, and to handle the most common cases, only the most recent version
* of a policy is returned in these cases. If you need a more complex solution, you will need to
* implement it yourself. Because the support modules use this class as their backing store, this is
* true also of those modules.
* <p>
* Note that this is not a heavily optimized class. It is intended more as an example, support code
* for the finder modules, and a starting utility for other programmers than as an
* enterprise-quality implementation. That said, it is fully functional, and should be useful for
* many applications.
*
* @since 2.0
* @author Seth Proctor
*
*         Adding generic type support by Christian Mueller (geotools)
*/
public class PolicyCollection {

    // the actual collection of policies
    private HashMap<String, TreeSet<AbstractPolicy>> policies;

    // the single instance of the comparator we'll use for managing versions
    private VersionComparator versionComparator = new VersionComparator();

    // the optional combining algorithm used when wrapping multiple policies
    private PolicyCombiningAlgorithm combiningAlg;

    // the optional policy id used when wrapping multiple policies
    private URI parentId;

    // default target that matches anything, used in wrapping policies
    private static final Target target;

    /**
     * This static initializer just sets up the default target, which is used by all wrapping policy
     * sets.
     */
    static {
        target = new Target(new TargetSection(null, TargetMatch.SUBJECT,
                PolicyMetaData.XACML_VERSION_2_0), new TargetSection(null, TargetMatch.RESOURCE,
                PolicyMetaData.XACML_VERSION_2_0), new TargetSection(null, TargetMatch.ACTION,
                PolicyMetaData.XACML_VERSION_2_0), new TargetSection(null, TargetMatch.ENVIRONMENT,
                PolicyMetaData.XACML_VERSION_2_0));
    };

    /**
     * Creates a new <code>PolicyCollection</code> that will return errors when multiple policies
     * match for a given request.
     */
    public PolicyCollection() {
        policies = new HashMap<String, TreeSet<AbstractPolicy>>();
        combiningAlg = null;
    }

    /**
     * Creates a new <code>PolicyCollection</code> that will create a new top-level PolicySet when
     * multiple policies match for a given request.
     *
     * @param combiningAlg
     *            the algorithm to use in a new PolicySet when more than one policy applies
     * @param parentPolicyId
     *            the identifier to use for the new PolicySet
     */
    public PolicyCollection(PolicyCombiningAlgorithm combiningAlg, URI parentPolicyId) {
        policies = new HashMap<String, TreeSet<AbstractPolicy>>();

        this.combiningAlg = combiningAlg;
        this.parentId = parentPolicyId;
    }

    /**
     * Adds a new policy to the collection, and uses the policy's identifier as the reference
     * identifier. If this identifier already exists in the collection, and this policy does not
     * represent a new version of the policy, then the policy is not added.
     *
     * @param policy
     *            the policy to add
     *
     * @return true if the policy was added, false otherwise
     */
    public boolean addPolicy(AbstractPolicy policy) {
        return addPolicy(policy, policy.getId().toString());
    }

    /**
     * Adds a new policy to the collection using the given identifier as the reference identifier.
     * If this identifier already exists in the collection, and this policy does not represent a new
     * version of the policy, then the policy is not added.
     *
     * @param policy
     *            the policy to add
     * @param identifier
     *            the identifier to use when referencing this policy
     *
     * @return true if the policy was added, false otherwise
     */
    public boolean addPolicy(AbstractPolicy policy, String identifier) {
        if (policies.containsKey(identifier)) {
            // this identifier is already is use, so see if this version is
            // already in the set
            TreeSet<AbstractPolicy> set = policies.get(identifier);
            return set.add(policy);
        } else {
            // this identifier isn't already being used, so create a new
            // set in the map for it, and add the policy
            TreeSet<AbstractPolicy> set = new TreeSet<AbstractPolicy>(versionComparator);
            policies.put(identifier, set);
            return set.add(policy);
        }
    }

    /**
     * Attempts to retrieve a policy based on the given context. If multiple policies match then
     * this will either throw an exception or wrap the policies under a new PolicySet (depending on
     * how this instance was constructed). If no policies match, then this will return null. See the
     * comment in the class header about how this behaves when multiple versions of the same policy
     * exist.
     *
     * @param context
     *            representation of a request
     *
     * @return a matching policy, or null if no policy matches
     *
     * @throws TopLevelPolicyException
     *             if multiple policies match but this instance wasn't setup to wrap policies
     */
    public AbstractPolicy getPolicy(EvaluationCtx context) throws TopLevelPolicyException {
        // setup a list of matching policies
        ArrayList<AbstractPolicy> list = new ArrayList<AbstractPolicy>();
        // get an iterator over all the identifiers
        Iterator<TreeSet<AbstractPolicy>> it = policies.values().iterator();

        while (it.hasNext()) {
            // for each identifier, get only the most recent policy
            AbstractPolicy policy = it.next().first();

            // see if we match
            MatchResult match = policy.match(context);
            int result = match.getResult();

            // if there was an error, we stop right away
            if (result == MatchResult.INDETERMINATE)
                throw new TopLevelPolicyException(match.getStatus());

            // if we matched, we keep track of the matching policy...
            if (result == MatchResult.MATCH) {
                // ...first checking if this is the first match and if
                // we automaticlly nest policies
                if ((combiningAlg == null) && (list.size() > 0)) {
                    ArrayList<String> code = new ArrayList<String>();
                    code.add(Status.STATUS_PROCESSING_ERROR);
                    Status status = new Status(code, "too many applicable" + " top-level policies");
                    throw new TopLevelPolicyException(status);
                }

                list.add(policy);
            }
        }

        // no errors happened during the search, so now take the right
        // action based on how many policies we found
        switch (list.size()) {
        case 0:
            return null;
        case 1:
            return ((AbstractPolicy) (list.get(0)));
        default:
            return new PolicySet(parentId, combiningAlg, target, list);
        }
    }

    /**
     * Attempts to retrieve a policy based on the given identifier and other constraints. If there
     * are multiple versions of the identified policy that meet the version constraints, then the
     * most recent version is returned.
     *
     * @param identifier
     *            an identifier specifying some policy
     * @param type
     *            type of reference (policy or policySet) as identified by the fields in
     *            <code>PolicyReference</code>
     * @param constraints
     *            any optional constraints on the version of the referenced policy (this will never
     *            be null, but it may impose no constraints, and in fact will never impose
     *            constraints when used from a pre-2.0 XACML policy)
     */
    public AbstractPolicy getPolicy(String identifier, int type, VersionConstraints constraints) {
        TreeSet<AbstractPolicy> set = policies.get(identifier);

        // if we don't know about this identifier then there's nothing to do
        if (set == null)
            return null;

        // walk through the set starting with the most recent version, looking
        // for a match until we exhaust all known versions
        for (AbstractPolicy policy : set) {
            if (constraints.meetsConstraint(policy.getVersion())) {
                // we found a valid version, so see if it's the right kind,
                // and if it is then we return it
                if (type == PolicyReference.POLICY_REFERENCE) {
                    if (policy instanceof Policy)
                        return policy;
                } else {
                    if (policy instanceof PolicySet)
                        return policy;
                }
            }
        }

        // we didn't find a match
        return null;
    }

    /**
     * A <code>Comparator</code> that is used within this class to maintain ordering amongst
     * different versions of the same policy. Note that it actually maintains reverse-ordering,
     * since we want to traverse the sets in decreasing, not increasing order.
     * <p>
     * Note that this comparator is only used when there are multiple versions of the same policy,
     * which in practice will probably happen far less (from this class' point of view) than
     * additions or fetches.
     */
    class VersionComparator implements Comparator<AbstractPolicy> {
        public int compare(AbstractPolicy o1, AbstractPolicy o2) {
            // we swap the parameters so that sorting goes largest to smallest
            String v1 = o2.getVersion();
            String v2 = o1.getVersion();

            // do a quick check to see if the strings are equal (note that
            // even if the strings aren't equal, the versions can still
            // be equal)
            if (v1.equals(v2))
                return 0;

            // setup tokenizers, and walk through both strings one set of
            // numeric values at a time
            StringTokenizer tok1 = new StringTokenizer(v1, ".");
            StringTokenizer tok2 = new StringTokenizer(v2, ".");

            while (tok1.hasMoreTokens()) {
                // if there's nothing left in tok2, then v1 is bigger
                if (!tok2.hasMoreTokens())
                    return 1;

                // get the next elements in the version, convert to numbers,
                // and compare them (continuing with the loop only if the
                // two values were equal)
                int num1 = Integer.parseInt(tok1.nextToken());
                int num2 = Integer.parseInt(tok2.nextToken());

                if (num1 > num2)
                    return 1;

                if (num1 < num2)
                    return -1;
            }

            // if there's still something left in tok2, then it's bigger
            if (tok2.hasMoreTokens())
                return -1;

            // if we got here it means both versions had the same number of
            // elements and all the elements were equal, so the versions
            // are in fact equal
            return 0;
        }
    }

}
TOP

Related Classes of com.sun.xacml.support.finder.PolicyCollection$VersionComparator

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.