001/*
002 * Licensed to DuraSpace under one or more contributor license agreements.
003 * See the NOTICE file distributed with this work for additional information
004 * regarding copyright ownership.
005 *
006 * DuraSpace licenses this file to you under the Apache License,
007 * Version 2.0 (the "License"); you may not use this file except in
008 * compliance with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.fcrepo.auth.xacml;
019
020import java.io.InputStream;
021import java.net.URI;
022import java.util.HashSet;
023import java.util.Set;
024
025import javax.jcr.Node;
026import javax.jcr.PathNotFoundException;
027import javax.jcr.RepositoryException;
028import javax.jcr.Session;
029import javax.xml.parsers.DocumentBuilder;
030import javax.xml.parsers.DocumentBuilderFactory;
031
032import org.jboss.security.xacml.sunxacml.EvaluationCtx;
033import org.jboss.security.xacml.sunxacml.attr.AttributeValue;
034import org.jboss.security.xacml.sunxacml.cond.EvaluationResult;
035import org.slf4j.Logger;
036import org.slf4j.LoggerFactory;
037import org.w3c.dom.Document;
038import org.w3c.dom.Element;
039
040/**
041 * @author Gregory Jansen
042 *
043 */
044public class PolicyUtil {
045
046    private static final Logger LOGGER = LoggerFactory.getLogger(PolicyUtil.class);
047
048    private PolicyUtil() {
049        //not called
050    }
051
052    /**
053     * Extract a policy set or policy ID for the document.
054     *
055     * @param policyStream the policy input
056     * @return an identifier
057     */
058    public static String getID(final InputStream policyStream) {
059        try {
060            // create the factory
061            final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
062            factory.setIgnoringComments(true);
063
064            factory.setNamespaceAware(true);
065            factory.setValidating(false);
066            final DocumentBuilder db = factory.newDocumentBuilder();
067
068            // Parse the policy content
069            final Document doc = db.parse(policyStream);
070
071            final String result = getID(doc);
072            if (result == null) {
073                throw new Error("Cannot find policy ID");
074            }
075            return result;
076        } catch (final Exception e) {
077            throw new Error("Unable to parse policy", e);
078        }
079    }
080
081    /**
082     * Get the ID of the XACML policy document.
083     *
084     * @param doc the DOM
085     * @return the ID
086     */
087    public static String getID(final Document doc) {
088        // handle the policy, if it's a known type
089        final Element root = doc.getDocumentElement();
090        final String name = root.getTagName();
091        if (name.equals("Policy")) {
092            return root.getAttribute("PolicyId");
093        } else if (name.equals("PolicySet")) {
094            return root.getAttribute("PolicySetId");
095        } else {
096            return null;
097        }
098    }
099
100    /**
101     * Gets the repository path for a policy ID.
102     *
103     * @param id the policy id
104     * @return the repository path
105     */
106    public static String getPathForId(final String id) {
107        return id.substring(URIConstants.POLICY_URI_PREFIX.length());
108    }
109
110    /**
111     * Find the nearest real Modeshape node for a given Modeshape path.
112     *
113     * @param modepath the path in ModeShape
114     * @param session a session
115     * @return a Node in session
116     */
117    public static Node getFirstRealNode(final String modepath, final Session session) {
118        LOGGER.debug("Finding firstRealNode for path: {}", modepath);
119
120        Node node = null;
121        for (String path = modepath; path.contains("/{"); path = path.substring(0, path.lastIndexOf("/{"))) {
122            try {
123                node = session.getNode(path);
124                break;
125            } catch (final PathNotFoundException expected) {
126            } catch (final RepositoryException e) {
127                throw new Error("Cannot reach repository", e);
128            }
129        }
130        if (node == null) {
131            try {
132                node = session.getRootNode();
133            } catch (final RepositoryException e) {
134                throw new Error("Cannot reach repository", e);
135            }
136        }
137
138        LOGGER.debug("Found firstRealNode for path: {}", modepath);
139        return node;
140    }
141
142    /**
143     * Get the action ids.
144     *
145     * @param context the evaluation context
146     * @return a set of actions
147     */
148    public static Set<String> getActions(final EvaluationCtx context) {
149        final Set<String> result = new HashSet<>();
150        final EvaluationResult eval =
151                context.getActionAttribute(URI.create("http://www.w3.org/2001/XMLSchema#string"),
152                        URIConstants.ATTRIBUTEID_ACTION_ID, null);
153        if (eval == null) {
154            return null;
155        }
156        if (eval.getStatus() == null) {
157            final AttributeValue val = eval.getAttributeValue();
158            if (val != null && val.getValue() != null) {
159                result.add(val.getValue().toString());
160            }
161        }
162        return result;
163    }
164}