/*
* 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-2016 UnboundID Corp.
*/
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;
}
}
|