/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * docs/licenses/cddl.txt * or http://www.opensource.org/licenses/cddl1.php. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * docs/licenses/cddl.txt. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2013-2018 Ping Identity Corporation */ package com.unboundid.directory.sdk.examples; import com.unboundid.directory.sdk.broker.api.PolicyInformationProvider; import com.unboundid.directory.sdk.broker.config. PolicyInformationProviderConfig; import com.unboundid.directory.sdk.broker.types.BrokerContext; import com.unboundid.directory.sdk.broker.types.RequestAttribute; import com.unboundid.directory.sdk.broker.types.RequestContext; import com.unboundid.util.args.ArgumentException; import com.unboundid.util.args.ArgumentParser; import com.unboundid.util.args.FileArgument; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; /** * Example of a third-party Policy Information Provider. This example * implementation retrieves named attributes from a Java properties file, and * can retrieve other JSON content from a additional files. */ public class ExamplePolicyInformationProvider extends PolicyInformationProvider { /** * The name of the argument that will be used to specify the directory * containing 'attributes.txt' and/or JSON content files. */ private static final String ARG_NAME_CONTENT_DIR = "content-directory"; /** * The name of the file that contains named attributes. */ private static final String ATTRIBUTES_FILE_NAME = "attributes.txt"; /** * The directory containing 'attributes.txt' and any JSON content * files. */ private File contentDirectory; private Properties properties; private Set<String> supportedAttributes = new HashSet<String>(); /** * Character used to delimit subject id from attribute id in the * name portion of each java property line. */ public static final Character PROPERTY_NAME_DELIMITER = '$'; /** * Creates a new instance of this policy information provider. All policy * information provider implementations must include a default constructor, * but any initialization should generally be performed in the * {@code initializePolicyInformationProvider} method. */ public ExamplePolicyInformationProvider() { } /** * Retrieves a human-readable name for this extension. * @return extension name */ @Override public String getExtensionName() { return "Example Policy Information Provider"; } /** * Retrieves a human-readable description of this extension. * @return text description string */ @Override public String[] getExtensionDescription() { return new String[] { "This Policy Information Provider serves as an example that may" + " be used to demonstrate the process for creating a third-party" + " policy information provider. This example provider reads" + " attribute data from text files. The extension attribute with" + " name " + ARG_NAME_CONTENT_DIR + " specifies the directory in" + " which these text files are located. The example provider"+ " supports both Attribute Descriptors and Attribute Selectors. " + " For Attribute Descriptors, it reads values from a Java properties"+ " file named 'attributes.txt' from the content directory. For"+ " Attribute Selectors, the XACML Category Id is interpreted as the" + " file name within the content directory from which to retrieve" + " JSON content." }; } /** * Updates the provided argument parser to define any configuration arguments * which may be used by this policy information provider. The argument parser * may also be updated to define relationships between arguments (e.g., to * specify required, exclusive, or dependent argument sets). * * @param parser The argument parser to be updated with the configuration * arguments which may be used by this provider. * * @throws ArgumentException If a problem is encountered while updating the * provided argument parser. */ @Override public void defineConfigArguments(final ArgumentParser parser) throws ArgumentException { // add argument used to specify the path to a properties file Character shortIdentifier = null; String longIdentifier = ARG_NAME_CONTENT_DIR; boolean required = true; int maxOccurrences = 1; String placeholder = "{path}"; String description = "The directory containing the java properties"+ " file 'attributes.txt' and zero or more JSON content files." + " 'attributes.txt' is a file containing name-value pairs. Each"+ " name-value pair must be in the" + " following format: <subjectName>" + PROPERTY_NAME_DELIMITER + "<xacmlAttributeId>=value where subjectName = subject property from" + " the request context and xacmlAttributeId = the XACML attribute Id" + " being requested. (Example: 'applicationX$urn:myorganization" + PROPERTY_NAME_DELIMITER + "myattribute=myvalue'.)"; boolean fileMustExist = true; boolean parentMustExist = true; boolean mustBeFile = false; boolean mustBeDirectory = true; parser.addArgument(new FileArgument(shortIdentifier, longIdentifier, required, maxOccurrences, placeholder, description, fileMustExist, parentMustExist, mustBeFile, mustBeDirectory)); } /** * Initialize this Policy Information Provider. Sets the directory containing * attribute files and loads the named attribute properties file if it exists. * * @param serverContext A handle to the server context for the server in * which this extension is running. * @param config The general configuration for this policy * information provider. * @param parser The argument parser which has been initialized from * the configuration for this policy information * provider. * * @throws IOException if an error occurs reading the properties file */ @Override public void initializePolicyInformationProvider( final BrokerContext serverContext, final PolicyInformationProviderConfig config, final ArgumentParser parser) throws IOException { contentDirectory = ((FileArgument)parser.getNamedArgument( ARG_NAME_CONTENT_DIR)).getValue(); properties = new Properties(); File attributesFile = new File(contentDirectory.getPath() + File.separator + ATTRIBUTES_FILE_NAME); if (attributesFile.exists()) { FileInputStream stream = new FileInputStream(attributesFile); properties.load(stream); // get list of supported attributes for (Object key : properties.keySet()) { String s = (String)key; int pos = s.indexOf(PROPERTY_NAME_DELIMITER); if (pos > 0) { String attributeUrn = s.substring(pos + 1); supportedAttributes.add(attributeUrn); } } } } /** * Retrieve an attribute value from the data store, which in the case of * this example is a java properties file. * @param categoryId XACML category identifier * @param attributeId XACML attribute identifier * @param context request context, can be used to retrieve other * attributes from the request that may be needed * in order to evaluate the requested attribute * @return RequestAttribute object * @throws Exception if an error occurs retrieving the attribute */ @Override public RequestAttribute getAttribute( final String categoryId, final String attributeId, final RequestContext context) throws Exception { // construct key using both subject and attribute Id StringBuilder key = new StringBuilder(context.getSubject()); key.append(PROPERTY_NAME_DELIMITER); key.append(attributeId); String value = properties.getProperty(key.toString()); if (value == null) { throw new Exception(String.format( "attribute %s not found for subject %s", attributeId, context.getSubject())); } return new RequestAttribute("http://www.w3.org/2001/XMLSchema#string", Arrays.asList(value)); } /** * Retrieve JSON-formatted content from the specified attributes category, * which in the case of this example is retrieved from a file whose name * is the same as the category name. * @param categoryId XACML category identifier. This is the category Id * specified by the AttributeSelector element in the * policy that is requesting this attribute. * @param context request context, can be used to retrieve other * attributes from the request that may be needed * in order to retrieve the requested content * @return JSON-encoded string * @throws Exception if an error occurs retrieving the content */ @Override public String getJsonContent( final String categoryId, final RequestContext context) throws Exception { // assume that content files are small Path p = Paths.get(contentDirectory.getPath(), categoryId); return new String(Files.readAllBytes(p), StandardCharsets.UTF_8); } /** * Determine whether this example provider can get the specified * attribute. * * @param categoryId XACML category identifier. This is the category Id * specified by the AttributeDesignator element in the * policy that is requesting this attribute. * @param attributeId XACML attribute identifier as specified in the * AttributeDesignator element in the policy that is * requesting the attribute. * @param context request context * @return True if the attribute is served by this provider. */ @Override public boolean canGetAttribute( final String categoryId, final String attributeId, final RequestContext context) { return supportedAttributes.contains(attributeId); } /** * Determine whether this example provider can get JSON content for the * specified attributes category. * * @param categoryId XACML category identifier. This is the category Id * specified by the AttributeSelector element in the * policy that is requesting this attribute. * @param contextSelectorId optional ContextSelectorId. This is the * context selector string specified in the * AttributeSelector. * @param context request context * @return True if the attributes category / context selector can be served * by this provider. */ @Override public boolean canGetJsonContent( final String categoryId, final String contextSelectorId, final RequestContext context) { Path p = Paths.get(contentDirectory.getPath(), categoryId); return Files.exists(p); } /** * Retrieves a map containing examples of configurations that may be used for * this extension. The map key should be a list of sample arguments, and the * corresponding value should be a description of the behavior that will be * exhibited by the extension when used with that configuration. * * @return A map containing examples of configurations that may be used for * this extension. It may be {@code null} or empty if there should * not be any example argument sets. */ @Override() public Map<List<String>,String> getExamplesArgumentSets() { final LinkedHashMap<List<String>,String> exampleMap = new LinkedHashMap<List<String>,String>(1); exampleMap.put( Arrays.asList(ARG_NAME_CONTENT_DIR + "=/var/xacmlContent"), "Retrieve attribute data from file(s) in the directory" + " /var/xacmlContent."); return exampleMap; } }