/* * eXist Open Source Native XML Database * Copyright (C) 2001-06 The eXist Project * http://exist-db.org * * This program 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 2 * of the License, or (at your option) any later version. * * 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. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * $Id$ */ package org.exist.security.xacml; import java.net.URI; import java.net.URISyntaxException; import java.util.Collections; import java.util.HashSet; import java.util.Set; import org.exist.dom.QName; import org.exist.security.User; import org.exist.xquery.ExternalModule; import org.exist.xquery.Module; import org.exist.xquery.XQueryContext; import com.sun.xacml.attr.AnyURIAttribute; import com.sun.xacml.attr.AttributeValue; import com.sun.xacml.attr.StringAttribute; import com.sun.xacml.ctx.Attribute; import com.sun.xacml.ctx.RequestCtx; import com.sun.xacml.ctx.Subject; /* * Source.getKey().toString() needs to be unique: * potential collision between FileSource and DBSource ?? */ /** * This class provides methods for creating an XACML request. The main methods * are those that return a <code>RequestCtx</code>. Links are provided to the * relevant constants in <code>XACMLConstants</code> to facilitate policy * writing. * * @see XACMLConstants */ public class RequestHelper { /** * Creates an XACML request for permission to execute an XQuery main module. * The subjects section will contain a subject for the user obtained from the * specified context. The resource section will be created by the * createQueryResource method. The action-id will be * {@link XACMLConstants#EXECUTE_QUERY_ACTION execute query}. The environment * section will be created by createEnvironment, using the access context * of the query context. * * @param context The context for this query * @param source The source of this query * @return A <code>RequestCtx</code> that may be evaluated by the PDP to * determine whether the specified user may execute the query represented by * <code>source</code>. */ public RequestCtx createQueryRequest(XQueryContext context, XACMLSource source) { Set subjects = createQuerySubjects(context.getUser(), null); Set resourceAttributes = createQueryResource(source); Set actionAttributes = createBasicAction(XACMLConstants.EXECUTE_QUERY_ACTION); Set environmentAttributes = createEnvironment(context.getAccessContext()); return new RequestCtx(subjects, resourceAttributes, actionAttributes, environmentAttributes); } /** * Creates a <code>RequestCtx</code> for a request concerning reflective * access to Java code from an XQuery. This handles occurs when a method * is being invoked on the class in question. This method creates a * request with the following content: * <ul> * * <li>Subjects for the contextModule and user are created with the * createQuerySubjects method.</li> * * <li>Resource attributes are created with the * <code>createReflectionResource</code> method.</li> * * <li>Action attributes are created with the * <code>createBasicAction</code> method. The action-id is * {@link XACMLConstants#INVOKE_METHOD_ACTION invoke method}.</li> * * <li>The {@link XACMLConstants#ACCESS_CONTEXT_ATTRIBUTE} access context * attribute is generated for the environment section.</li> * * </ul> * * @param context The <code>XQueryContext</code> for the module making the * request. * @param contextModule The query containing the reflection. * @param className The name of the class that is being accessed or loaded. * @param methodName The name of the method that is being invoked * @return A <code>RequestCtx</code> that represents the access in question. */ public RequestCtx createReflectionRequest(XQueryContext context, Module contextModule, String className, String methodName) { User user = context.getUser(); Set subjects = createQuerySubjects(user, contextModule); Set resourceAttributes = createReflectionResource(className, methodName); Set actionAttributes = createBasicAction(XACMLConstants.INVOKE_METHOD_ACTION); Set environmentAttributes = createEnvironment(context.getAccessContext()); return new RequestCtx(subjects, resourceAttributes, actionAttributes, environmentAttributes); } /** * Creates a <code>RequestCtx</code> for a request concerning access * to a function in an XQuery library module. If the function is * from a main module, this method returns null to indicate that. * The client should interpret this to mean that the request is * granted because access to a main module implies access to its * functions. * * <p> * This method creates a request with the following content: * <ul> * * <li>Subjects for the contextModule and user (obtained from the * XQueryContext) are created with the createQuerySubjects method.</li> * * <li>The specified functionModule parameter is used to generate the * {@link XACMLConstants#SOURCE_KEY_ATTRIBUTE source-key}, * {@link XACMLConstants#SOURCE_TYPE_ATTRIBUTE source-type}, and * {@link XACMLConstants#MODULE_CATEGORY_ATTRIBUTE module category} * attributes. The functionName parameter is the value of the * {@link XACMLConstants#RESOURCE_ID_ATTRIBUTE subject-id} attribute * (the local part) and of the * {@link XACMLConstants#MODULE_NS_ATTRIBUTE module namespace} * attribute (the namespace URI part). The * {@link XACMLConstants#RESOURCE_CATEGORY_ATTRIBUTE resource-category} * attribute is {@link XACMLConstants#FUNCTION_RESOURCE function}. * * <li>Action attributes are created with the * <code>createBasicAction</code> method. The action is * {@link XACMLConstants#CALL_FUNCTION_ACTION call function}. * * <li>The {@link XACMLConstants#ACCESS_CONTEXT_ATTRIBUTE} access context * attribute is generated for the environment section.</li> * * </ul> * * @param context The query context. * @param contextModule The query making the access. * @param functionName The <code>QName</code> of the function being called. * @return A <code>RequestCtx</code> that represents the access in question * or <code>null</code> if the function belongs to a main module and * not a library module. */ public RequestCtx createFunctionRequest(XQueryContext context, Module contextModule, QName functionName) { String namespaceURI = functionName.getNamespaceURI(); Module functionModule = context.getModule(namespaceURI); if(functionModule == null) { //main module, not a library module, so access to function is always allowed return null; } User user = context.getUser(); Set subjects = createQuerySubjects(user, contextModule); Set resourceAttributes = new HashSet(8); addStringAttribute(resourceAttributes, XACMLConstants.MODULE_CATEGORY_ATTRIBUTE, getModuleCategory(functionModule)); XACMLSource moduleSrc = generateModuleSource(functionModule); addSourceAttributes(resourceAttributes, moduleSrc); addValidURIAttribute(resourceAttributes, XACMLConstants.MODULE_NS_ATTRIBUTE, namespaceURI); addStringAttribute(resourceAttributes, XACMLConstants.RESOURCE_CATEGORY_ATTRIBUTE, XACMLConstants.FUNCTION_RESOURCE); addStringAttribute(resourceAttributes, XACMLConstants.RESOURCE_ID_ATTRIBUTE, functionName.getLocalName()); Set actionAttributes = createBasicAction(XACMLConstants.CALL_FUNCTION_ACTION); Set environmentAttributes = createEnvironment(context.getAccessContext()); return new RequestCtx(subjects, resourceAttributes, actionAttributes, environmentAttributes); } /** * Creates a <code>Subject</code> for a <code>User</code>. * The user's name is the value of the * {@link XACMLConstants#SUBJECT_ID_ATTRIBUTE subject-id} attribute. The * subject-category is {@link XACMLConstants#ACCESS_SUBJECT access-subject}. * The {@link XACMLConstants#GROUP_ATTRIBUTE group} attribute is a bag * containing the name of each group of which the user is a member. * * @param user The user making the request * @return A <code>Subject</code> for use in a <code>RequestCtx</code> */ public Subject createUserSubject(User user) { AttributeValue value = new StringAttribute(user.getName()); Attribute attr = new Attribute(XACMLConstants.SUBJECT_ID_ATTRIBUTE, null, null, value); return new Subject(XACMLConstants.ACCESS_SUBJECT, Collections.singleton(attr)); } /** * Creates the basic attributes needed to describe a simple action * in a request. The <code>action</code> parameter is the value of * the {@link XACMLConstants#ACTION_ID_ATTRIBUTE action-id} attribute and the * {@link XACMLConstants#ACTION_NS_ATTRIBUTE namespace} attribute for the * action-id is eXist's XACML * {@link XACMLConstants#ACTION_NS action namespace}. * * @param action The {@link XACMLConstants#ACTION_ID_ATTRIBUTE action-id} * of the action. * @return A <code>Set</code> that contains attributes describing the * action for use in a <code>RequestCtx</code> */ public Set createBasicAction(String action) { if(action == null) return null; Set attributes = new HashSet(4); addStringAttribute(attributes, XACMLConstants.ACTION_ID_ATTRIBUTE, action); addValidURIAttribute(attributes, XACMLConstants.ACTION_NS_ATTRIBUTE, XACMLConstants.ACTION_NS); return attributes; } /** * Creates a <code>Subject</code> for a <code>Module</code>. * If the module is external, its <code>Source</code> is the value of the * {@link XACMLConstants#SUBJECT_ID_ATTRIBUTE subject-id} attribute, otherwise, * the name of the implementing class is used. The subject-category is * {@link XACMLConstants#CODEBASE_SUBJECT codebase}. The value of the * {@link XACMLConstants#SUBJECT_NS_ATTRIBUTE module namespace} attribute * is the namespace URI of the module. The * {@link XACMLConstants#MODULE_CATEGORY_ATTRIBUTE module category} * attribute is the type of module, either * {@link XACMLConstants#INTERNAL_LIBRARY_MODULE internal} or * {@link XACMLConstants#EXTERNAL_LIBRARY_MODULE external}. * * @param module A query module involved in making the request * @return A <code>Subject</code> for use in a <code>RequestCtx</code> */ public Subject createModuleSubject(Module module) { if(module == null) return null; Set attributes = new HashSet(8); addValidURIAttribute(attributes, XACMLConstants.SUBJECT_NS_ATTRIBUTE, module.getNamespaceURI()); addStringAttribute(attributes, XACMLConstants.MODULE_CATEGORY_ATTRIBUTE, getModuleCategory(module)); XACMLSource moduleSrc = generateModuleSource(module); addSourceAttributes(attributes, moduleSrc); addStringAttribute(attributes, XACMLConstants.SUBJECT_ID_ATTRIBUTE, moduleSrc.createId()); return new Subject(XACMLConstants.CODEBASE_SUBJECT, attributes); } /** * Creates a <code>Set</code> of <code>Attribute</code>s for a resource * representing Java reflection in an XQuery. * The {@link XACMLConstants#RESOURCE_CATEGORY_ATTRIBUTE resource-category} * attribute is {@link XACMLConstants#METHOD_RESOURCE method}. * The {@link XACMLConstants#SOURCE_TYPE_ATTRIBUTE source-type} attribute is * {@link XACMLConstants#CLASS_SOURCE_TYPE class} and the * {@link XACMLConstants#SOURCE_KEY_ATTRIBUTE source-key} attribute is the * name of the class. The * {@link XACMLConstants#RESOURCE_ID_ATTRIBUTE resource-id} attribute is the * method name. * * @param className The name of the Java class * @param methodName The name of the method being invoked * @return A <code>Set</code> containing the <code>Attribute</code>s * describing access to Java code by reflection. */ public Set createReflectionResource(String className, String methodName) { if(className == null) throw new NullPointerException("Class name cannot be null"); if(methodName == null) throw new NullPointerException("Method name cannot be null"); Set resourceAttributes = new HashSet(8); addStringAttribute(resourceAttributes, XACMLConstants.RESOURCE_CATEGORY_ATTRIBUTE, XACMLConstants.METHOD_RESOURCE); XACMLSource source = XACMLSource.getInstance(className); addSourceAttributes(resourceAttributes, source); addStringAttribute(resourceAttributes, XACMLConstants.RESOURCE_ID_ATTRIBUTE, methodName); return resourceAttributes; } /** * Creates the Resource section of a request for a main module. * * @param source The source of the query. * @return A <code>Set</code> containing attributes for the specified * query. */ public Set createQueryResource(XACMLSource source) { if(source == null) throw new NullPointerException("Query source cannot be null"); Set resourceAttributes = new HashSet(4); addSourceAttributes(resourceAttributes, source); addStringAttribute(resourceAttributes, XACMLConstants.RESOURCE_ID_ATTRIBUTE, source.createId()); addStringAttribute(resourceAttributes, XACMLConstants.RESOURCE_CATEGORY_ATTRIBUTE, XACMLConstants.MAIN_MODULE_RESOURCE); return resourceAttributes; } /** * Creates <code>Subject</code>s for the specified user and module. This is * equivalent to putting the <code>Subject</code>s created by the * <code>createUserSubject(User user)</code> and * <code>createModuleSubject(Module contextModule)</code> methods. The * context module may be null if there is no context module. * * @param user The user making the access * @param contextModule The module involved in the access, if any. It may * be null to indicate the is not an intermediary XQuery module. * @return A <code>Set</code> containing a <code>Subject</code> for each * the context module if there is one and the user. */ public Set createQuerySubjects(User user, Module contextModule) { if(user == null) throw new NullPointerException("User cannot be null"); Set subjects = new HashSet(4); Subject userSubject = createUserSubject(user); subjects.add(userSubject); if(contextModule != null) { Subject moduleSubject = createModuleSubject(contextModule); subjects.add(moduleSubject); } return subjects; } /** * Creates the environment section of a request for the given * <code>AccessContext</code>. * * @param accessCtx The context * @return A <code>Set</code> containing one attribute, the * {@link XACMLConstants#ACCESS_CONTEXT_ATTRIBUTE access context} * attribute with the value of the specified access context. */ public Set createEnvironment(AccessContext accessCtx) { if(accessCtx == null) throw new NullAccessContextException(); Set environment = new HashSet(4); addStringAttribute(environment, XACMLConstants.ACCESS_CONTEXT_ATTRIBUTE, accessCtx.toString()); return environment; } /** * Generates an <code>XACMLSource</code> for a <code>Module</code> * based on its implementing class name (if it is an * <code>InternalModule</code>) or its <code>Source</code> * (if it is an <code>ExternalModule</code>). * * @param module the module for which the source should be generated * @return an <code>XACMLSource</code> that uniquely defines the source * of the given module */ public static XACMLSource generateModuleSource(Module module) { if(module == null) throw new NullPointerException("Module cannot be null"); if(module.isInternalModule()) return XACMLSource.getInstance(module.getClass()); return XACMLSource.getInstance(((ExternalModule)module).getSource()); } /** * Returns the module type for the given XQuery library module. This * is either * {@link XACMLConstants#INTERNAL_LIBRARY_MODULE internal} or * {@link XACMLConstants#EXTERNAL_LIBRARY_MODULE external} * * @param module The XQuery library module. If it is null, this method * returns null. * @return null if module is null, the module's category (internal or external) * otherwise */ public static String getModuleCategory(Module module) { if(module == null) return null; return module.isInternalModule() ? XACMLConstants.INTERNAL_LIBRARY_MODULE : XACMLConstants.EXTERNAL_LIBRARY_MODULE; } /** * Adds new attributes to the specified <code>Set</code> of attributes * that represent the specified source. The added attributes are the * {@link XACMLConstants#SOURCE_KEY_ATTRIBUTE source's key} and the * {@link XACMLConstants#SOURCE_TYPE_ATTRIBUTE source's type}. * * @param attributes The <code>Set</code> to which attributes will be * added. If null, this method does nothing. * @param source The source for which attributes will be added. It * cannot be null. */ public static void addSourceAttributes(Set attributes, XACMLSource source) { if(source == null) throw new NullPointerException("Source cannot be null"); addStringAttribute(attributes, XACMLConstants.SOURCE_KEY_ATTRIBUTE, source.getKey()); addStringAttribute(attributes, XACMLConstants.SOURCE_TYPE_ATTRIBUTE, source.getType()); } /** * Adds a new attribute of type string to the specified * <code>Set</code> of attributes. The new attribute's value is * constructed from the attrValue parameter and is given the id * of the attrID parameter. * * @param attributes The <code>Set</code> to which the new attribute * should be added. If it is null, this method does nothing. * @param attrID The ID of the new attribute, cannot be null * @param attrValue The value of the new attribute. It cannot be null. */ public static void addStringAttribute(Set attributes, URI attrID, String attrValue) { if(attributes == null) return; if(attrID == null) throw new NullPointerException("Attribute ID cannot be null"); if(attrValue == null) throw new NullPointerException("Attribute value cannot be null"); AttributeValue value = new StringAttribute(attrValue); Attribute attr = new Attribute(attrID, null, null, value); attributes.add(attr); } /** * Adds a new attribute of type anyURI to the specified * <code>Set</code> of attributes. The new attribute's value is * constructed from the uriString parameter and is given the id * of the attrID parameter. * * @param attributes The <code>Set</code> to which the new attribute * should be added. If it is null, this method does nothing. * @param attrID The ID of the new attribute, cannot be null * @param uriString The value of the new attribute. It must parse into a * valid URI and cannot be null. * @throws URISyntaxException if the specified attribute value is not a * valid URI. */ public static void addURIAttribute(Set attributes, URI attrID, String uriString) throws URISyntaxException { if(attributes == null) return; if(attrID == null) throw new NullPointerException("Attribute ID cannot be null"); if(uriString == null) throw new NullPointerException("Attribute value cannot be null"); URI uri = new URI(uriString); AttributeValue value = new AnyURIAttribute(uri); Attribute attr = new Attribute(attrID, null, null, value); attributes.add(attr); } //wrapper for when the URI is known to be valid, such as when obtained from a source //that validates the URI or from a constant private static void addValidURIAttribute(Set attributes, URI attrID, String uriString) { try { addURIAttribute(attributes, attrID, uriString); } catch(URISyntaxException e) { throw new RuntimeException("URI should never be invalid", e); } } RequestHelper() {} }