Sun OpenSSO Enterprise 8.0 Developer's Guide

Chapter 2 Using the Policy Service API

OpenSSO Enterprise enables organizations to control the usage of, and access to, their resources. This chapter provides information about how the Policy Service allows you to define, manage, and enforce policies towards that end. It contains the following sections:

OpenSSO Enterprise also provides C APIs for external applications to connect to the Policy Service framework. For information on using the C API, see Sun OpenSSO Enterprise 8.0 C API Reference for Application and Web Policy Agent Developers. For a comprehensive listing of all Java API and their usage, see the Sun OpenSSO Enterprise 8.0 Java API Reference.

About the Policy Service Interfaces

The Policy Service provides the functionality to control access to web services and applications by providing authorization decisions based on defined and applicable policies or rules that define who or what is authorized to access a resource. In a single sign-on (SSO) environment, the Policy Service acts as authorization authority, providing authorization decisions that are enforced by a policy agent. The Policy Service acts as a Policy Administration Point (PAP) and a Policy Decision Point (PDP). As a PAP, it allows privileged users to create, modify, and delete access control policies. As a PDP, it provides access control decisions (after evaluating applicable policies) to a Policy Enforcement Point (PEP) which, in a OpenSSO Enterprise environment, is a policy agent.


Note –

For information on how the Policy Service works within a user session, see Chapter 6, Models of the User Session and Single Sign-On Processes, in Sun OpenSSO Enterprise 8.0 Technical Overview. Additional information is in Chapter 8, Authorization and the Policy Service, in Sun OpenSSO Enterprise 8.0 Technical Overview. More information on policy agents can be found in Sun OpenSSO Enterprise Policy Agent 3.0 User’s Guide for J2EE Agents.


The Policy Service provides an application programming interface (API) to manage policies and provide authorization decisions. It also provides a service provider interface (SPI) to extend the Policy Service functionality. These interfaces include the following packages:

com.sun.identity.policy

The com.sun.identity.policy package contains the following classes for policy management and policy evaluation:

Policy Management Classes

Policy management classes are used by privileged system administrators to programmatically add, look up, modify, replace and delete policies, and update the policy data store, if appropriate. Attempts by non-privileged users to manage policies will result in an exception and be logged. A valid session token is required to invoke any method provided by these classes. The key policy management classes are:

PolicyManager

com.sun.identity.policy.PolicyManager is the top-level administrator class for policy management in a specific realm. This class provides methods that enable the administrator to add, look up, modify, replace and delete policies. Only a privileged user with access to the policy data store and a valid session token can create a PolicyManager object. Some of the more widely used methods include:

getPolicyNames()

Retrieves all named policies created in the realm for which the PolicyManager object was instantiated. This method can also take a pattern (filter) as an argument.

getPolicy()

Retrieves a policy when given the policy name.

addPolicy()

Adds a policy to the realm for which the PolicyManager object was instantiated. If a policy with the same name already exists, it will be overwritten.

removePolicy()

Removes a policy from the realm for which the PolicyManager object was instantiated.

replacePolicy()

Overwrites a policy already defined in the realm for which the PolicyManager object was instantiated.

Policy

com.sun.identity.policy.Policy represents a policy definition with all its intended parts, including Rule(s), Subject(s), Condition(s), Referral(s) and Response Provider(s). The Policy object can be saved in the policy data store if the addPolicy() or replacePolicy() methods from the PolicyManager class are invoked. This class contains methods for adding, removing, replacing or retrieving any of the parts of a policy definition.

Policy Evaluation Classes

Policy Decision APIs are used to evaluate policy decision when a principal attempts an action on a resource. This section covers some key classes that provide Policy Evaluation APIs. Some classes are also provided to be used only by privileged users to test policy decisions applicable to other users.

Policy evaluation classes are used to evaluate the applicable policy when a principal attempts an action on a resource and send a determination on whether the principal will be allowed or denied access. The key policy evaluation classes are:


Caution – Caution –

Policy evaluation classes from this package require a direct connection to the policy data store. These classes should be used with caution, and only when classes from com.sun.identity.policy.client cannot handle your use case. See com.sun.identity.policy.client.


PolicyEvaluator

com.sun.identity.policy.PolicyEvaluator evaluates policy privileges and provides policy decisions. It provides methods to evaluate access to one resource or a hierarchy of resources, and supports both boolean and non-boolean type policies. A valid session token of the principal attempting access is required to invoke any method of this class. A PolicyEvaluator class is created by calling the constructor with a service name. Key public methods of this class include:

isAllowed()

Evaluates a policy associated with the given resource and returns a boolean-type value indicating an allow or deny decision.

getPolicyDecision()

Evaluates policies and returns a decision as to whether the associated principal can perform the specified actions on the specified resource.

getResourceResults()

A ResourceResult contains policy decisions regarding a particular protected resource and its sub resources. getResourceResults() obtains these policy decisions. Possible values for the scope of objects retrieved are ResourceResult.SELF_SCOPE (returns an object that contains the policy decision for the specified resource only), ResourceResult.SUBTREE_SCOPE (includes policy decisions for the specified resource and its sub-resources), and ResourceResult.STRICT_SUBTREE_SCOPE (returns an object that contains one policy decision regarding the resourceName only). For example, the PolicyEvaluator class can be used to display links for a list of resources to which an authenticated user has access. The getResourceResults() method can be used to retrieve a list of resources to which the user has access from a defined resourceName parameter — a URL in the form http://host.domain:port. The resources are returned as a PolicyDecision object based on the user’s policies. If the user is allowed to access resources on different servers, this method needs to be called for each server.

Not all resources that have policy decisions are accessible to the user. Access depends on the ActionDecision() value contained in policy decisions.

ProxyPolicyEvaluator

com.sun.identity.policy.ProxyPolicyEvaluator allows a privileged user (top-level administrator, organization administrator, policy administrator, or organization policy administrator) to get policy privileges and evaluate policy decisions for any user in their scope of administration. com.sun.identity.policy.ProxyPolicyEvaluatorFactory is the singleton class used to get ProxyPolicyEvaluator instances. This is supported only within the OpenSSO Enterprise server process.

PolicyEvent

com.sun.identity.policy.PolicyEvent represents a policy event that could potentially change the current access status. A policy event is created and passed to registered policy listeners whenever there is a change in a policy rule. This class works with the PolicyListener class in the com.sun.identity.policy.interface package.

com.sun.identity.policy.client

The com.sun.identity.policy.client package contains classes that can be used by remote Java applications to evaluate policies and communicate with the Policy Service to get policy decisions. This package does not communicate with the policy data store therefore, use it when, for example, there is an intervening firewall. The package also maintains a local cache of policy decisions kept current either by a configurable time to live and/or notifications from the Policy Service.

com.sun.identity.policy.interfaces

The com.sun.identity.policy.interfaces package contains SPI for writing custom plug-ins to extend the Policy Service. The classes are used by service developers and policy administrators who need to provide additional policy features as well as support for legacy policies.

Condition

Provides methods used to constrain a policy to, for example, time-of-day or IP address. This interface allows the pluggable implementation of the conditions.

PolicyListener

Defines an interface for registering policy events when a policy is added, removed or changed. PolicyListener is used by the Policy Service to send notifications and by listeners to review policy change events.

Referral

Provides methods used to delegate the policy definition or evaluation of a selected resource (and its sub-resources) to another realm or policy server.

ResourceName

Provides methods to determine the hierarchy of the resource names for a determined service type. For example, these methods can check to see if two resources names are the same or if one is a sub-resource of the other.

ResponseProvider

Defines an interface to allow pluggable response providers into the OpenSSO Enterprise framework. Response providers are used to provide policy response attributes which typically provide attribute values from the user profile.

Subject

Provides methods to determine if an authenticated user is a member of the given subject.

Policy Service Provider Interfaces and Plug-Ins

OpenSSO Enterprise includes SPIs that work with the Policy Service framework to create and manage policies. You can develop customized plug-ins for creating custom policy subjects, referrals, conditions, and response providers. For information on creating custom policy plug-ins, see Sample Code for Custom Subjects, Conditions, Referrals, and Response Providers. The following table summarizes the Policy Service SPI, and lists the specialized Policy Service plug-ins that come bundled with OpenSSO Enterprise.

Table 2–1 Policy Service Service Provider Interfaces

Interface 

Description 

Subject 

Defines a set of authenticated users for whom the policy applies. The following Subject plug-ins come bundled with OpenSSO Enterprise: Access Manager Identity Subject, Access Manager Roles, Authenticated Users, LDAP Groups, LDAP Roles, LDAP Users, Organization Web, and Services Clients. 

Referral 

Delegates management of policy definitions to another access control realm.  

Condition 

Specifies applicability of policy based on conditions such as IP address, time of day, authentication level. The following Condition plug-ins come bundled with OpenSSO Enterprise: Authentication Level, Authentication Scheme, IP Address, LE Authentication Level, Session, SessionProperty, and Time. 

Resource Name 

Allows a pluggable resource. 

Response Provider 

Gets attributes that are sent along with policy decision to the policy agent, and used by the policy agent to customize the client applications. Custom implementations of this interface are now supported in OpenSSO Enterprise. 

com.sun.identity.policy.jaas

The com.sun.identity.policy.jaas package provides classes for performing policy evaluation against OpenSSO Enterprise using the Java Authentication and Authorization Service (JAAS) framework. JAAS is a set of APIs that enable services to authenticate and enforce access controls upon users. This package provides support for authorization only, making it possible to use JAAS interfaces to access the Policy Service. It contains the following implementations of JAAS classes:

For more information see Enabling Authorization Using the Java Authentication and Authorization Service (JAAS).

ISPermission

com.sun.identity.policy.jaas.ISPermission extends java.security.Permission, an abstract class for representing access to a resource. It represents the control of a sensitive operation, such as opening of a socket or accessing a file for a read or write operation. It does not grant permission for that operation, leaving that responsibility to the JAAS AccessController class which evaluates OpenSSO Enterprise policy against the Policy Service.

ISPermission covers the case when additional policy services are defined and imported provided they only have boolean action values as a JAAS permission only has a boolean result.

ISPolicy

com.sun.identity.policy.jaas.ISPolicy is an implementation of the JAAS abstract class java.security.Policy which represents the system policy for a Java application environment. It performs policy evaluation against the Policy Service instead of against the default file-based PolicyFile.

Enabling Authorization Using the Java Authentication and Authorization Service (JAAS)

The Java Authentication and Authorization Service (JAAS) is a set of API that can determine the identity of a user or computer attempting to run Java code, and ensure that the entity has the right to execute the requested functions. After an identity has been determined using authentication, a Subject object, representing a grouping of information about the entity, is created. Whenever the Subject attempts a restricted operation or access, the Java runtime uses the JAAS AccessController class to determine which, if any, Principal (representing one piece of information established during authentication) would authorize the request. If the Subject in question contains the appropriate Principal, the request is allowed. If the appropriate Principal is not present, an exception is thrown.

In OpenSSO Enterprise the custom implementation of the JAAS java.security.Policy, com.sun.identity.policy.jaas.ISPolicy, relies on the policy framework to provide policy evaluation for all Policy Service policies. Policy related to resources not under OpenSSO Enterprise control (for example, system level resources) are evaluated using JAAS.

OpenSSO Enterprise policy does not control access to com.sun.security.auth.PolicyFile, the default JAAS policy store.


Note –

For more information see the JAAS Java API Reference.


To enable authorization using JAAS in OpenSSO Enterprise use the JAAS java.security.Policy API to reset policy during run time. In the sample code, the client application resets the policy to communicate with OpenSSO Enterprise using ISPolicy. OpenSSO Enterprise provides the support needed to define policy through ISPermission.


Example 2–1 Sample JAAS Authorization Code


public static void main(String[] args) {
   try {
       // Create an SSOToken

      AuthContext ac = new AuthContext("dc=iplanet,dc=com");
       ac.login();
       Callback[] callbacks = null;
       if (ac.hasMoreRequirements()) {
           callbacks = ac.getRequirements();

           if (callbacks != null) {
               try {
                   addLoginCallbackMessage(callbacks); 
					// this method sets appropriate responses 
					// in the callbacks.
                   ac.submitRequirements(callbacks);
               } catch (Exception e) { }
           }
       }
       if (ac.getStatus() == AuthContext.Status.SUCCESS) {
             Subject subject = ac.getSubject();
							// get the authenticated subject

               Policy.setPolicy(new ISPolicy());
               // change the policy to  our own Policy

               ISPermission perm = new ("iPlanetAMWebAgentService",

                   "http://www.sun.com:80", "GET");
             Subject.doAs(subject, new PrivilegedExceptionAction() {
                 /* above statement means execute  run() method of the
							 /* Class PrivilegedExceptionAction()
                     as the specified subject */
                 public Object run() throws Exception {
                     AccessController.checkPermission(perm);
                       // the above will return quietly if the Permission
								  //  has been granted
                       // else will throw access denied
                       // Exception, so if the above highlighed ISPermission
								  // had not been granted, this return null;
                 }
            });
        }
   }

Using the Policy Evaluation API

The OpenSSO Enterprise policy framework defines Subject, Condition, Referral and Response Provider interfaces to enable you to create your own plug- ins to extend the functionality.

ProcedureTo Develop a Custom Policy Plug-In

This information is also included in the OpenSSO Enterprise /samples directory. See the following file:

http://openSSO-host:3080/opensso/samples/policy/policy-plugins.html

  1. Write Java source files implementing Subject, Condition, Referral or ResponseProvider interface.

    See Sample Code for Custom Subjects, Conditions, Referrals, and Response Providers.

  2. Compile the source files to create class files.

    Include opensso.jar and opesnsso-sharedlib.jar in the classpath at compilation time.

  3. Package the compiled classes into a JAR file.

    In this example, the file is named policy-plugins.jar.

  4. Explode the opensso.war file.

  5. Add the policy-plugins.jar file to WEB-INF/lib directory.

    Alternatively, you can copy the custom plug-in classes to the WEB-INF/classes directory. Be sure to maintain the directory structure corresponding to the Java package of the plug-in classes.

  6. Update WEB-INF/classes/amPolicy.properties.

    Add the globalization (L10N) values for the new internationalization (I18N) keys used by iPlanetAMPolicyService.

  7. Update WEB-INF/classes/amPolicyConfig.properties.

    Add L10N values for the new I18N keys used by iPlanetAMPolicyConfigService.

  8. Recreate the WAR file.

  9. Redeploy the WAR file.

    Steps 1 through 9 have been already taken care of for the sample plug-ins included in OpenSSO distribution.

  10. Use the ssoadm command to register the new plug-ins with the iPlanetAMPolicyService.

    In the following example, the password.txt file contains the password of amadmin:


    ssoadm create-svc -X amPolicy_mod.xml -u amadmin -f password.txt
    

    See the sample amPolicy_mod.xml. The new i18keys are referred in the XML file. Add Corresponding L10N values in amPolicy.properties.

  11. Register the new plug-ins in one of the following ways:

    • Use the ssoadm command to register the new plug-ins as choice values in the iPlanetAMPolicyConfigService.


      # ssoadm set-attr-choicevals -s iPlanetAMPolicyConfigService 
      -t Organization -a iplanet-am-policy-selected-subjects 
      -k a160=SampleSubject -u amadmin -f password.txt
       # ssoadm set-attr-choicevals -s iPlanetAMPolicyConfigService 
      -t Organization -a iplanet-am-policy-selected-conditions 
      -k a161=SampleCondition -u amadmin -f password.txt 
      # ssoadm set-attr-choicevals -s iPlanetAMPolicyConfigService 
      -t Organization -a iplanet-am-policy-selected-referrals 
      -k a162=SampleReferral -u amadmin -f password.txt 
      #ssoadm set-attr-choicevals -s iPlanetAMPolicyConfigService 
      -t Organization -a sun-am-policy-selected-responseproviders 
      -k a163=SampleResponseProvider -u amadmin -f password.txt
    • Use the ssoadm command to register the new plug-ins as enabled for a selected realm.


      # ssoadm add-attr-defs -s iPlanetAMPolicyConfigService -t Organization 
      -a iplanet-am-policy-selected-subjects=SampleSubject -u amadmin -f password.txt
      # ssoadm add-attr-defs -s iPlanetAMPolicyConfigService -t Organization 
      -a iplanet-am-policy-selected-conditions=SampleCondition -u amadmin -f password.txt
      # ssoadm add-attr-defs -s iPlanetAMPolicyConfigService -t Organization 
      -a iplanet-am-policy-selected-referrals=SampleReferral -u amadmin -f password.txt
      # ssoadm add-attr-defs -s iPlanetAMPolicyConfigService -t Organization 
      -a sun-am-policy-selected-responseproviders=SampleResponseProvider 
      -u amadmin -f password.txt   
    • Use the administration console to register the new plug-ins for existing realms.

      1. Log in to the administration console as amadmin or administrator.

      2. Navigate to the Realm > Services > Policy Configuration.

      3. In the Policy Configuration page, enable or disable the selected plug-in.

  12. Restart the web application or the container.

  13. Use either the administration console or the ssoadm command to add the instances of the new plug-ins while defining policies.

    The new plug-ins are available as choices in appropriate policy management pages of the administration console.

  14. To disable the custom plug-ins from being added to newly-created policies:

    1. In the administration console, navigate to Access Control > Realm > Services | Policy Configuration.

    2. Deselect the appropriate custom plug-ins.

    3. Save the Policy Configuration properties page for exisiting realms.

    If you navigate to Configuration > Global > Policy Configuration and do this, the custom plug-ins would be deselected for the realms that would be created subsequently.

  15. Copy your custom plug-in classes to <TOOLS_HOME>/classes.

    Be sure to maintain the directory structure corresponding to the Java package of the plug-in classes. You can copy the classes of bundled, custom sample plug-ins from the exploded opensso.war directory WEB-INF/classes/com/sun/identity/samples/policy. This is required if you plan to use ssoadm to export or add policies.

Sample Code for Custom Subjects, Conditions, Referrals, and Response Providers

OpenSSO Enterprise provides subject, condition, referral, and response provider interfaces that enable you to develop your own custom subjects, conditions, referrals, and response providers. The following samples illustrate how to implement these custom objects:

SampleSubject.java

Implements the Subject interface. This subject applies to all the authenticated users who have valid SSOTokens.


Example 2–2 SampleSubject.java

package com.sun.identity.samples.policy;

import java.util.*;
//import java.security.Principal;

import com.iplanet.sso.*;
import com.sun.identity.policy.*;
import com.sun.identity.policy.interfaces.Subject;

/**
 * The class <code>Subject</code> defines a collection
 * of users (or subject) to whom the specified policy is applied.
 * A complete implementation of this interface can have complex
 * boolean operations to determine if the given user identified
 * by the <code>SSOToken</code> belongs to this collection.
 * <p>
 * The interfaces are seperated into administrative
 * interfaces and evaluation interfaces. The administrative interfaces
 * will be used by GUI/CLI component to create a <code>Subject</code>
 * object and the evaluation interfaces will be used by the policy evaluator.
 * 
 * This sample inplementation defines the collection of all users who have 
 * been authenticated (a user with a valid SSOToken.).
 */
public class SampleSubject implements Subject {

    /**
     * Constructor with no parameter
     */
    public SampleSubject() {
	// do nothing
    }

    /**
     * Initialize (or configure) the <code>Subject</code>
     * object. Usually it will be initialized with the environment
     * paramaters set by the system administrator via SMS.
     * For example in a Role implementation, the configuration
     * parameters could specify the directory server name, port, etc.
     *
     * @param configParams configuration parameters as a map.
     * The values in the map is <code>java.util.Set</code>,
     * which contains one or more configuration paramaters.
     *
     * @exception PolicyException if an error occured during
     * initialization of <code>Subject</code> instance
     */

    public void initialize(Map configParams) 
        throws PolicyException {
	// do nothing
    }

    /**
     * Returns the syntax of the values the
     * <code>Subject</code> implementation can have.
     * @see com.sun.identity.policy.Syntax
     *
     * @param token the <code>SSOToken</code> that will be used
     * to determine the syntax
     *
     * @return set of of valid names for the user collection.
     *
     * @exception SSOException if SSO token is not valid
     * @exception PolicyException if unable to get the list of valid
     * names.
     *
     * @return syntax of the values for the <code>Subject</code>
     */

    public Syntax getValueSyntax(SSOToken token) {
	return (Syntax.CONSTANT);
    }


    /**
     * Returns the syntax of the values the
     * <code>Subject</code> implementation can have.
     * @see com.sun.identity.policy.Syntax
     *
     * @param token the <code>SSOToken</code> that will be used
     * to determine the syntax
     *
     * @return set of of valid names for the user collection.
     *
     * @exception SSOException if SSO token is not valid
     * @exception PolicyException if unable to get the list of valid
     * names.
     *
     * @return syntax of the values for the <code>Subject</code>
     */

    public ValidValues getValidValues(SSOToken token) {
	return (new ValidValues(ValidValues.SUCCESS, 
					Collections.EMPTY_SET));
    }


    /**
     * Returns a list of possible values for the <code>Subject
     * </code>. The implementation must use the <code>SSOToken
     * </code> <i>token</i> provided to determine the possible
     * values. For example, in a Role implementation
     * this method will return all the roles defined
     * in the organization.
     *
     * @param token the <code>SSOToken</code> that will be used
     * to determine the possible values
     *
     * @return <code>ValidValues</code> object
     *
     * @exception SSOException if SSO token is not valid
     * @exception PolicyException if unable to get the list of valid
     * names.
     */

    public ValidValues getValidValues(SSOToken token, String pattern) {
	return (new ValidValues(ValidValues.SUCCESS, 
					Collections.EMPTY_SET));
    }


    /**
     * Returns the display name for the value for the given locale.
     * For all the valid values obtained through the methods
     * <code>getValidValues</code> this method must be called
     * by GUI and CLI to get the corresponding display name.
     * The <code>locale</code> variable could be used by the
     * plugin to customize
     * the display name for the given locale.
     * The <code>locale</code> variable
     * could be <code>null</code>, in which case the plugin must
     * use the default locale (most probabily en_US).
     * This method returns only the display name and should not
     * be used for the method <code>setValues</code>.
     * Alternatively, if the plugin does not have to localize
     * the value, it can just return the <code>value</code> as is.
     *
     * @param value one of the valid value for the plugin
     * @param locale locale for which the display name must be customized
     *
     * @exception NameNotFoundException if the given <code>value</code>
     * is not one of the valid values for the plugin
     */

    public String getDisplayNameForValue(String value, Locale locale)
	throws NameNotFoundException {
        return value;
    }


    /**
     * Returns the values that was set using the
     * method <code>setValues</code>.
     *
     * @return values that have been set for the user collection
     */

    public Set getValues() {
	return (Collections.EMPTY_SET);
    }


    /**
     * Sets the names for the instance of the <code>Subject</code>
     * object. The names are obtained from the policy object,
     * usually configured when a policy is created. For example
     * in a Role implementation, this would be name of the role.
     *
     * @param names names selected for the instance of
     * the user collection object.
     *
     * @exception InvalidNameException if the given names are not valid
     */

    public void setValues(Set names) throws InvalidNameException {
    }


    /**
     * Determines if the user belongs to this instance
     * of the <code>Subject</code> object.
     * For example, a Role implemenation
     * would return <code>true</code> if the user belongs
     * the specified role; <code>false</code> otherwise.
     *
     * @param token single-sign-on token of the user
     *
     * @return <code>true</code> if the user is memeber of the
     * given subject; <code>false</code> otherwise.
     *
     * @exception SSOException if SSO token is not valid
     * @exception PolicyException if an error occured while
     * checking if the user is a member of this subject
     */

    public boolean isMember(SSOToken token) 
	throws SSOException {
	return (SSOTokenManager.getInstance().isValidToken(token));
    }

    /**
     * Indicates whether some other object is "equal to" this one.
     *
     * @param o another object that will be compared with this one
     *
     * @return <code>true</code> if eqaul; <code>false</code>
     * otherwise
     */

    public boolean equals(Object o) {
	if (o instanceof SampleSubject) {
	    return (true);
	}
	return (false);
    }


    /**
     * Creates and returns a copy of this object.
     *
     * @return a copy of this object
     */

    public Object clone() {
	return (new SampleSubject());
    }
}

SampleCondition.java

Implements the Condition interface. This condition makes the policy applicable to those users whose user name length is greater than or equal to the length specified in the condition.


Example 2–3 SampleCondition.java

package com.sun.identity.samples.policy;

import java.util.*;

import com.sun.identity.policy.interfaces.Condition;
import com.sun.identity.policy.ConditionDecision;
import com.sun.identity.policy.PolicyException;
import com.sun.identity.policy.PolicyManager;
import com.sun.identity.policy.Syntax;
import com.iplanet.sso.SSOException;
import com.iplanet.sso.SSOToken;
import com.iplanet.sso.SSOTokenManager;


/**
 * The class <code>SampleCondition</code> is a plugin 
 * implementation of <code>Condition</code> interface.
 * This condition object provides the policy framework with the 
 * condition decision based on the length of the user's name.
 */

public class SampleCondition implements Condition {

    /** Key that is used to define the minimum of the user name length  
     *  for which the policy would apply.  The value should be
     *  a Set with only one element. The element should be a 
     *  String, parsable as an integer.
     */

    public static final String USER_NAME_LENGTH = "userNameLength";

    private List propertyNames;
    private Map properties;
    private int nameLength;

    /** No argument constructor 
     */
    public SampleCondition() {
         propertyNames = new ArrayList();
         propertyNames.add(USER_NAME_LENGTH);
    }

     /**
      * Returns a set of property names for the condition.
      *
      * @return set of property names
      */

     public List getPropertyNames()
     {
         return propertyNames;
     }
 
     /**
      * Returns the syntax for a property name
      * @see com.sun.identity.policy.Syntax
      *
      * @param String property name
      *
      * @return <code>Syntax<code> for the property name
      */
     public Syntax getPropertySyntax(String property)
     {
         return (Syntax.ANY);
     }
      
     /**
      * Gets the display name for the property name.
      * The <code>locale</code> variable could be used by the
      * plugin to customize the display name for the given locale.
      * The <code>locale</code> variable could be <code>null</code>, in which 
      * case the plugin must use the default locale.
      *
      * @param String property name
      * @param Locale locale for which the property name must be customized
      * @return display name for the property name
      */
     public String getDisplayName(String property, Locale locale) 
       throws PolicyException
     {
         return property;
     }
 
     /**
      * Returns a set of valid values given the property name. This method
      * is called if the property Syntax is either the SINGLE_CHOICE or 
      * MULTIPLE_CHOICE.
      *
      * @param String property name
      * @return Set of valid values for the property.
      * @exception PolicyException if unable to get the Syntax.
      */
     public Set getValidValues(String property) throws PolicyException
     {
         return (Collections.EMPTY_SET);
     }


    /** Sets the properties of the condition.
     *  Evaluation of ConditionDecision is influenced by these properties.
     *  @param properties the properties of the condition that governs
     *         whether a policy applies. The properties should
     *         define value for the key USER_NAME_LENGTH. The value should
     *         be a Set with only one element. The element should be
     *         a String, parsable as an integer. Please note that
     *         properties is not cloned by the method.
     *
     *  @throws PolicyException if properties is null or does not contain
     *          value for the key USER_NAME_LENGTH or the value of the key is
     *          not a Set with one String element that is parsable as
     *          an integer.
     */

    public void setProperties(Map properties) throws PolicyException {
        this.properties = (Map)((HashMap) properties);
        if ( (properties == null) || ( properties.keySet() == null) ) {
            throw new PolicyException("properties can not be null or empty");
        }

        //Check if the key is valid
        Set keySet = properties.keySet();
        Iterator keys = keySet.iterator();
        String key = (String) keys.next();
        if ( !USER_NAME_LENGTH.equals(key) ) {
            throw new PolicyException(
                "property " + USER_NAME_LENGTH + " is not defined");
        }

        // check if the value is valid
        Set nameLengthSet = (Set) properties.get(USER_NAME_LENGTH);
        if (( nameLengthSet == null ) || nameLengthSet.isEmpty() 
            || ( nameLengthSet.size() > 1 )) {
            throw new PolicyException(
                "property value is not defined or invalid");
        }

        Iterator nameLengths = nameLengthSet.iterator();
        String nameLengthString = null;
        nameLengthString = (String) nameLengths.next();
        try {
            nameLength = Integer.parseInt(nameLengthString);
        } catch (Exception e) {
            throw new PolicyException("name length value is not an integer");
        }
    }


    /** Get properties of this condition.
     */
    public Map getProperties() {
        return properties;
    } 


    /**
     * Gets the decision computed by this condition object.
     *
     * @param token single sign on token of the user
     *
     * @param env request specific environment map of key/value pairs.
     *        SampleCondition doesn't use this parameter.
     *
     * @return the condition decision. The condition decision 
     *         encapsulates whether a policy applies for the request. 
     *
     * Policy framework continues evaluating a policy only if it 
     * applies to the request as indicated by the CondtionDecision. 
     * Otherwise, further evaluation of the policy is skipped. 
     *
     * @throws SSOException if the token is invalid
     */

    public ConditionDecision getConditionDecision(SSOToken token, Map env) 
            throws PolicyException, SSOException {
	boolean allowed = false;

        String userDN = token.getPrincipal().getName();
        // user DN is in the format like "uid=username,ou=people,dc=example,dc=com"
        int beginIndex = userDN.indexOf("=");
        int endIndex = userDN.indexOf(",");
        if (beginIndex >= endIndex) {
            throw (new PolicyException("invalid user DN"));
        }

        String userName = userDN.substring(beginIndex+1, endIndex);
        if (userName.length() >= nameLength) {
            allowed = true;
        }

	return new ConditionDecision(allowed);
    }


    public Object clone() {
	Object theClone = null;
	try {
	    theClone = super.clone();
	} catch (CloneNotSupportedException e) {
            throw new InternalError();
	}
	return theClone;
    }

}

SampleReferral.java

Implements the Referral interface. SampleReferral.java gets the referral policy decision from a text file SampleReferral.properties located in the /samples directory.


Example 2–4 SampleReferral.java

package com.sun.identity.samples.policy;

import java.io.*;
import java.util.*;

import com.sun.identity.policy.*;
import com.sun.identity.policy.interfaces.Referral;
import com.iplanet.sso.SSOToken;
import com.iplanet.sso.SSOException;
import com.iplanet.am.util.SystemProperties;

public class SampleReferral implements Referral {

    static final String SEPARATOR = ":";
    static String PROPERTIES = "samples/policy/SampleReferral.properties";
    static String INSTALL_DIR = SystemProperties.get("com.iplanet.am.installdir");
    static Properties properties = new Properties();
    private String _name;
    private Set _values;

    /** No argument constructor */
    public SampleReferral() {
    }

    /**Initializes the referral with a map of Configuration parameters
     * @param configurationMap a map containing configuration 
     *        information. Each key of the map is a configuration
     *        parameter. Each value of the key would be a set of values
     *        for the parameter. The map is cloned and a reference to the 
     *        clone is stored in the referral
     */
    public void initialize(Map configurationMap) {
    }

    /**Sets the name of this referral 
     * @param name name of this referral
     */
    private void setName(String name) {
        _name = name;
    }

    /**Gets the name of this referral 
     * @return the name of this referral
     */
    private String getName() {
        return _name;
    }            

    /**Sets the values of this referral.
     * @param values a set of values for this referral
     *        Each element of the set has to be a String
     * @throws InvalidNameException if any value passed in the 
     * values is invalid
     */
    public void setValues(Set values) throws InvalidNameException {
        _values = values;
    }

    /**Gets the values of this referral 
     * @return the values of this referral
     *                Each element of the set would be a String
     */
    public Set getValues() {
        return _values;
    }

    /**
     * Returns the display name for the value for the given locale.
     * For all the valid values obtained through the methods
     * <code>getValidValues</code> this method must be called
     * by GUI and CLI to get the corresponding display name.
     * The <code>locale</code> variable could be used by the
     * plugin to customize
     * the display name for the given locale.
     * The <code>locale</code> variable
     * could be <code>null</code>, in which case the plugin must
     * use the default locale (most probabily en_US).
     * This method returns only the display name and should not
     * be used for the method <code>setValues</code>.
     * Alternatively, if the plugin does not have to localize
     * the value, it can just return the <code>value</code> as is.
     *
     * @param value one of the valid value for the plugin
     * @param locale locale for which the display name must be customized
     *
     * @exception NameNotFoundException if the given <code>value</code>
     * is not one of the valid values for the plugin
     */
    public String getDisplayNameForValue(String value, Locale locale)
	throws NameNotFoundException {
	return value;
    }

    /**Gets the valid values for this referral 
     * @param token SSOToken
     * @return <code>ValidValues</code> object
     * @throws SSOException, PolicyException
     */
    public ValidValues getValidValues(SSOToken token) 
            throws SSOException, PolicyException {
        return getValidValues(token, "*");
    }

    /**Gets the valid values for this referral 
     * matching a pattern
     * @param token SSOToken
     * @param pattern a pattern to match against the value
     * @return </code>ValidValues</code> object
     * @throws SSOException, PolicyException
     */
    public ValidValues getValidValues(SSOToken token, String pattern)
            throws SSOException, PolicyException {
        Set values = new HashSet();
        values.add(PROPERTIES);
        return (new ValidValues(ValidValues.SUCCESS,
                                values));
    }

    /**Gets the syntax for the value 
     * @param token SSOToken
     * @see com.sun.identity.policy.Syntax
     */
    public Syntax getValueSyntax(SSOToken token)
            throws SSOException, PolicyException {
        return (Syntax.SINGLE_CHOICE);
    }

    /**Gets the name of the ReferralType 
     * @return name of the ReferralType representing this referral
     */
    public String getReferralTypeName() 
    {
        return "SampleReferral";
    }

    /**Gets policy results 
     * @param token SSOToken
     * @param resourceType resource type
     * @param resourceName name of the resource 
     * @param actionNames a set of action names
     * @param envParameters a map of enivronment parameters.
     *        Each key is an environment parameter name.
     *        Each value is a set of values for the parameter.
     * @return policy decision
     * @throws SSOException
         * @throws PolicyException
     */
    public PolicyDecision getPolicyDecision(SSOToken token, String resourceType, 
	    String resourceName, Set actionNames, Map envParameters) 
            throws SSOException, PolicyException {

        PolicyDecision pd = new PolicyDecision();
        Iterator elements = _values.iterator();
        if (!elements.hasNext()) {
            return pd;
        }

        String fileName = (String)elements.next(); 
        fileName = INSTALL_DIR + "/" + fileName;
        try {
            InputStream is = new FileInputStream(fileName);
            if (is == null) {
                return pd;
            }
            properties.load(is);
        } catch (Exception e) {
            return pd;
        }

        String serviceName = getProperty("servicename");
        if (!serviceName.equals(resourceType)) {
            return pd;
        }

        String resName = getProperty("resourcename");
        if (!resName.equals(resourceName)) {
            return pd;
        }
       
        List actionNameList = getPropertyValues("actionnames");
        List actionValueList = getPropertyValues("actionvalues");

        int numOfActions = actionNameList.size();
        int numOfValues = actionValueList.size();

        if ((numOfActions == 0 || (numOfValues == 0) 
                               || numOfActions != numOfValues)) {
            return pd;
        } 

        Iterator namesIter = actionNameList.iterator();
        Iterator valuesIter = actionValueList.iterator();

        for (int i = 0; i < numOfActions; i++) {
            String actionName = (String)namesIter.next();
            String actionValue = (String)valuesIter.next();
            if (actionNames.contains(actionName)) {
                Set values = new HashSet();
                values.add(actionValue);
                ActionDecision ad = new ActionDecision(
                    actionName, values, null, Long.MAX_VALUE);
                pd.addActionDecision(ad);            
            }
        }
        return pd;
    }


    private String getProperty(String key)
    {
        return properties.getProperty(key);
    }


    private List getPropertyValues(String name) {
        List values = new ArrayList();
        String value = getProperty(name);
        if ( value != null ) {
            StringTokenizer st = new StringTokenizer(value, SEPARATOR);
            while ( st.hasMoreTokens() ) {
               values.add(st.nextToken());
            }
        }
        return values;
    }

    /** Gets resource names rooted at the given resource name for the given
     *  serviceType that could be governed by this referral 
     * @param token ssoToken sso token
     * @param serviceTypeName service type name
     * @param rsourceName resource name
     * @return names of sub resources for the given resourceName.
     *         The return value also includes the resourceName.
     *
     * @throws PolicyException
     * @throws SSOException
     */
    public Set getResourceNames(SSOToken token, String serviceTypeName, 
            String resourceName) throws PolicyException, SSOException {
        return null;
    }

}

SampleResponseProvider.java

Implements the ResponseProvider interface. SampleResponseProvider.java takes as input the attribute for which values are retrieved from OpenSSO Enterprise and sent back in the Policy Decision. If the attribute does not exist in the user profile, no value is sent back in the response. SampleResponseProvider.java relies on the underlying Identity Repository service to retrieve the attribute values for the Subject(s) defined in the policy.


Example 2–5 SampleResponseProvider.java

package com.sun.identity.samples.policy;

import com.sun.identity.policy.PolicyException;
import com.sun.identity.policy.PolicyUtils;
import com.sun.identity.policy.PolicyConfig;
import com.sun.identity.policy.PolicyManager;
import com.sun.identity.policy.interfaces.ResponseProvider;
import com.sun.identity.policy.Syntax;

import com.iplanet.sso.SSOToken;
import com.iplanet.sso.SSOException;

import com.sun.identity.idm.AMIdentity;
import com.sun.identity.idm.IdUtils;
import com.sun.identity.idm.IdRepoException;

import java.util.List;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.Locale;
import java.util.Map;
import java.util.HashSet;
import java.util.HashMap;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.Collections;

/**
 * This class is an implementation of <code>ResponseProvider</code> interface. 
 * It takes as input the attribute for which values are to be fetched from
 * the access manager and sent back in the Policy Decision.
 * if the attribute does not exist in the use profile no value is sent
 * back in the response.
 * It relies on underlying Identity repository service to 
 * fetch the attribute values for the Subject(s) defined in the policy.
 * It computes a <code>Map</code> of response attributes
 * based on the <code>SSOToken</code>, resource name and  env map passed 
 * in the method call <code>getResponseDecision()</code>.
 *
 * Policy framework would make a call to the ResponseProvider in a 
 * policy only if the policy is applicable to a request as determined by 
 * <code>SSOToken</code>, resource name, <code>Subjects</code> and <code>Conditions
 * </code>.
 *
 */
public class SampleResponseProvider implements ResponseProvider {

    public static final String ATTRIBUTE_NAME = "AttributeName";

    private Map properties;
    private static List propertyNames = new ArrayList(1);

    private boolean initialized=false;
    private String orgName = null;

    static {
        propertyNames.add(ATTRIBUTE_NAME);
    }

    /**
     * No argument constructor.
     */
    public SampleResponseProvider () {

    }


    /** 
     * Initialize the SampleResponseProvider object by using the configuration
     * information passed by the Policy Framework.
     * @param configParams the configuration information
     * @exception PolicyException if an error occured during 
     * initialization of the instance
     */

    public void initialize(Map configParams) throws PolicyException {
        // get the organization name
        Set orgNameSet = (Set) configParams.get(
                                     PolicyManager.ORGANIZATION_NAME);
        if ((orgNameSet != null) && (orgNameSet.size() != 0)) {
            Iterator items = orgNameSet.iterator();
            orgName = (String) items.next();
        }
	/** 
         * Organization name is not used in this sample, but this is code
    	 * to illustrate how any other custom response provider can get data
         * out from the policy configuration service and use it in 
         * getResponseDecision() as necessary.
	 */
	initialized = true;
    }


    /**
     * Returns a list of property names for the responseprovider.
     *
     * @return <code>List</code> of property names
     */
    public List getPropertyNames()  {
         return propertyNames;
    }

    /**
     * Returns the syntax for a property name
     * @see com.sun.identity.policy.Syntax
     *
     * @param property property name
     *
     * @return <code>Syntax<code> for the property name
     */
    public Syntax getPropertySyntax(String property) {
        return (Syntax.LIST);
    }
	
    /**
     * Gets the display name for the property name.
     * The <code>locale</code> variable could be used by the plugin to
     * customize the display name for the given locale.
     * The <code>locale</code> variable could be <code>null</code>, in which
     * case the plugin must use the default locale.
     *
     * @param property property name
     * @param locale locale for which the property name must be customized
     * @return display name for the property name.
     * @throws PolicyException
     */
    public String getDisplayName(String property, Locale locale)
            throws PolicyException {
	return property;
    }

    /**
     * Returns a set of valid values given the property name. 
     *
     * @param property property name
     * from the PolicyConfig Service configured for the specified realm.
     * @return Set of valid values for the property.
     * @exception PolicyException if unable to get the Syntax.
     */
    public Set getValidValues(String property) throws PolicyException {
	if (!initialized) {
	    throw (new PolicyException("idrepo response provider not yet "
		+"initialized"));
	}
        return Collections.EMPTY_SET;
    }

    /** Sets the properties of the responseProvider plugin.
     *  This influences the response attribute-value Map that would be
     *  computed by a call to method <code>getResponseDecision(Map)</code>
     *  These attribute-value pairs are encapsulated in 
     *  <code>ResponseAttribute</code> element tag which is a child of the 
     *  <code>PolicyDecision</code> element in the PolicyResponse xml
     *  if the policy is applicable to the user for the resource, subject and
     *  conditions defined.
     *  @param properties the properties of the responseProvider
     *         Keys of the properties have to be String.
     *         Value corresponding to each key have to be a Set of String
     *         elements. Each implementation of ResponseProvider could add 
     *         further restrictions on the keys and values of this map.
     *  @throws PolicyException for any abnormal condition
     */
    public void setProperties(Map properties) throws PolicyException {
        if ( (properties == null) || ( properties.isEmpty()) ) {
            throw new PolicyException("Properties cannot be null or empty");
        }
        this.properties = properties;

        //Check if the keys needed for this provider are present namely
 	// ATTRIBUTE_NAME
	if (!properties.containsKey(ATTRIBUTE_NAME)) {
            throw new PolicyException("Missing required property");
	}
	/** 
         * Addtional validation on property name and values can be done
         * as per the individual use case
         */
    }

    /** Gets the properties of the responseprovider
     *  @return properties of the responseprovider
     *  @see #setProperties
     */
    public Map getProperties() {
	return (properties == null) 
		? null : Collections.unmodifiableMap(properties);
    }

    /**
     * Gets the response attributes computed by this ResponseProvider object,
     * based on the sso token and map of environment parameters
     *
     * @param token single-sign-on token of the user
     *
     * @param env specific environment map of key/value pairs
     * @return  a Map of response attributes.
     *          Keys of the Map are attribute names ATTRIBUTE_NAME or
     *          Value is a Set of Strings representing response attribute 
     *          values.
     *
     * @throws PolicyException if the decision could not be computed
     * @throws SSOException if SSO token is not valid
     *
     */
    public Map getResponseDecision(SSOToken token, 
            Map env) throws PolicyException, SSOException { 

	Map respMap = new HashMap();
	Set attrs = (Set)properties.get(ATTRIBUTE_NAME);
	Set values = null;
	if ((attrs != null) && !(attrs.isEmpty())) {
            try {
                if (token.getPrincipal() != null) {
                    AMIdentity id = IdUtils.getIdentity(token);
                    Map idRepoMap = id.getAttributes(attrs);
                    if (idRepoMap != null) {
                        for (Iterator iter = attrs.iterator(); iter.hasNext(); )
			{
                            String attrName = (String)iter.next();
                            values = new HashSet();
                            Set subValues = (Set)idRepoMap.get(attrName);
                            if (subValues != null) {
                                values.addAll(subValues);
                            }
			    respMap.put(attrName, values);
                        }
                    }
                } else {
                    throw (new PolicyException("SSOToken principal is null"));
                }
            } catch (IdRepoException ide) {
                throw new PolicyException(ide);
  	    }	
        }
	return respMap;
    }


    /**
     * Returns a copy of this object.
     *
     * @return a copy of this object
     */
    public Object clone() {
        SampleResponseProvider theClone = null;
        try {
            theClone = (SampleResponseProvider)super.clone();
        } catch (CloneNotSupportedException e) {
            // this should never happen
            throw new InternalError();
        }

        if (properties != null) {
            theClone.properties = new HashMap();
            Iterator iter = properties.keySet().iterator();
            while (iter.hasNext()) {
                Object obj = iter.next();
                Set values = new HashSet();
                values.addAll((Set) properties.get(obj));
                theClone.properties.put(obj, values);
            }
        }
        return theClone;
    }
}