7 Extending Functionality

An extension class can be loaded by the Oracle Entitlements Server runtime environment to enhance core functionality. Extensions are bundled as Java Archive (JAR) files. This chapter contains the following sections on extensions that can be created.

7.1 Working With Attribute Retrievers

The Policy Information Point (PIP) is a system entity that acts as a source for attribute values. During runtime evaluation of a policy, Oracle Entitlements Server relies on an Attribute Retriever plug-in to get attribute values from one or more PIP information stores. These attribute retrievers allow policies to be data-driven in that the value of the attribute can impact the access decision. For example, if access to transfer money from a bank account is based on how much money is currently in the account, an attribute retriever can be used to get a value for the current balance. This infrastructure is highly extensible, allowing users to develop their own PIP plug-ins to retrieve information from many places - for example, from a file, a USB driver, or the internet.

The following sections have more information.

7.1.1 Understanding Attribute Retrievers

Oracle Entitlements Server uses predefined Attribute Retrievers to connect to Lightweight Directory Access Protocol (LDAP) data stores and relational database management systems (RDBMS). Custom attribute retrievers can be developed to get attribute values from other types of PIP data stores. A custom attribute retriever can return values for one or many attributes.

Configuration information for attribute retrievers is defined in the jps-config.xml configuration file. Configuration of the attribute retriever within this file is dependent on whether it is predefined or custom.

  • For predefined attribute retrievers:

    • Configure information needed to connect to the data store as well as credential information.

    • Configure individual attribute values including attribute name, name of Attribute Retriever used, search query to retrieve the value (for example, SQL query if the PIP is a relational database or LDAP query if it's a directory), and any attribute value caching information).

  • For custom attribute retrievers, configure information regarding the name of the class implementing the attribute retriever.

A given attribute retriever can return a single value or multiple values attribute.

7.1.2 Creating Custom Attribute Retrievers

As described in Section 1.3.3, "Adding a Condition," a policy Condition is built using attributes or functions. If a dynamic attribute is used in a Condition, the attribute value can be passed in from the com.bea.security.AppContext interface or retrieved with either a predefined or custom attribute retriever. The following procedure documents the steps to create a custom attribute retriever.

  1. Implement the custom attribute retriever using the com.bea.security.providers.authorization.asi.AttributeRetrieverV2 interface.

    See Section 7.1.3, "Implementing Custom Attribute Retrievers" for more information.

  2. Create a JAR file.

  3. Add the JAR file to the appropriate classpath.

    • If connecting to a Java Security Module, add the JAR file to the application classpath.

    • If connecting to an RMI, Web Services or WebLogic Server Security Module, add the JAR file to the system classpath with the rest of the Security Module JAR files.

    It does not matter where the JAR is physically stored.

  4. Configure the Security Module to use the custom retriever.

    Make sure the configuration specifies the fully-qualified location of the custom attribute retriever. See the Security Module configuration appendix in the Oracle Fusion Middleware Administrator's Guide for Oracle Entitlements Server.

7.1.3 Implementing Custom Attribute Retrievers

A custom attribute retriever must implement the AttributeRetrieverV2 interface. Table 7-1 explains the methods available for this purpose.

Table 7-1 Methods in AttributeRetrieverV2 Interface

Method Description

getAttributeValue()

This method is called every time the value of a particular attribute is required. It returns the value of the named attribute and takes the following parameters:

  • Name defines the name of the attribute being retrieved.

  • RequestHandle is the interface that will retrieve values of other attributes (if required). It also allows the sharing of context – an arbitrary Object - between different attribute retrievers or custom functions.

  • Subject defines the user associated with the request.

  • Roles defines any role membership of the subject, or null if this is a role mapping call.

  • Resource defines the protected resource associated with the request.

  • contextHandler is the context associated with the request; this may be null if non-existent.

getHandled AttributeNames()

This method is called once, usually during loading of the attribute retriever. The method returns the list of attribute names for which the attribute retriever can return values. If the method returns a null or empty value, this attribute retriever object will be called when any dynamic attribute has to be resolved.


In the simplest use case, an attribute retriever does not need additional information to get the value. For example, to get the time of day, getAttributeValue() calls a system function and returns the information. Another use case might find the attribute retriever needs additional information before it can return an attribute value. For example, the attribute retriever would have to know the user's identifier in order to get the location of the user. For this purpose, the attribute retriever is provided a RequestHandle interface to get the values for other attributes. In this example, the attribute retriever can use the RequestHandle interface to get the value of the built-in SYS_USER attribute which resolves to the identity of the current user.

Note:

Names of system attributes must be placed between percentage (%) signs as in %sys_user%.

The following sections contain more information on the attribute retrieval options.

7.1.3.1 Getting Attribute Values Directly

An implementation of AttributeRetrieverV2 can use the getAttributeValue() method to return the value of a named attribute. This method takes as input the name of the attribute whose value will be returned. Example 7-1 illustrates how getAttributeValue() might be used.

Example 7-1 Implementing getAttributeValue() Method

package oracle.security.oes.test;
 
import java.util.Map;
 
import javax.security.auth.Subject;
 
import weblogic.security.service.ContextHandler;
import weblogic.security.spi.Resource;
 
import com.bea.security.providers.authorization.asi.AttributeRetrieverV2;
import com.bea.security.providers.authorization.asi.ARME.evaluator.RequestHandle;
 
public class SimpleAttributeRetriever implements AttributeRetrieverV2 {
 
    public Object getAttributeValue(String name, RequestHandle requestHandle,
         Subject subject, Map roles, Resource resource, 
         ContextHandler contextHandler) {
       if (name == null) return null;
       return "static_value";
    }
 
    public String[] getHandledAttributeNames() {
       return new String[] {"static_attr"};
    }
 
}

MyAttributeRetrieverV2 is the implementation of AttributeRetrieverV2. The getHandledAttributeNames() method returns the names of attributes handled by this implementation. It may return at least one attribute name; an empty or null value indicates that the retriever will be called for any attribute. The values of the getAttributeValue() parameters are defined as:

  • name is the name of the attribute being retrieved.

  • requestHandle is the implementation of the interface that allows you to retrieve values of other attributes (if required). It also allows the sharing of context – an arbitrary Object - between different attribute retrievers or custom functions. It is passed to the function even if it is not used.

  • subject is the principal associated with the request.

  • roles defines the role membership of the associated principal. The object is a map where the key signifies the role name and the value is the role object.

  • resource is the protected resource associated with the request.

  • contextHandler defines the context associated with the request. It may be null if the context is non-existent.

7.1.3.2 Getting Attribute Values Using a Handle

In some cases, the attribute retriever might need to get an attribute for information before retrieving the attribute value it wants. For example, in order to get the location of a user, the attribute retriever would need the identifier of the user. By invoking the getAttribute() method in the RequestHandle interface, the attribute retriever is able to get the identifier and with it access to all of the user's information. The getAttribute() method returns the attribute name and value as a name-value pair in an AttributeElement object.

RequestHandle is the interface that allows you to retrieve values of other attributes if required. It also allows to share context – arbitrary Object - between different invocation of attribute retrievers and/or custom functions

Note:

The getAttribute() method is used to retrieve values for user and resource attributes. It should not be used to get values for dynamic or extension attributes.

Example 7-2 illustrates how getAttribute() might be used.

Example 7-2 Using getAttribute() Method

public Object getAttributeValue
   (String name, RequestHandle requestHandle, Subject subject, 
    Map roles, Resource resource, ContextHandler contextHandler) {

... ...
 
// retrieve sys_user built-in attribute 
   String user = null;
   try {
      AttributeElement element = requestHandle.getAttribute("sys_user", true);
          if (element != null) {
              user = (String)element.getValueAs(String.class);
            }
   } catch (Exception e) {
// ignore it
     }

... ...
}

The values of the getAttribute() parameters are defined as:

  • sys_user is the name of the attribute being retrieved.

  • true enables the attribute type check functionality. The value may be false to disable the type check.

7.1.4 Configuring Oracle Entitlements Server for Custom Attribute Retrievers

This section contains the procedure on how to configure Oracle Entitlements Server to recognize a custom attribute retriever. After implementing com.bea.security.providers.authorization.asi.AttributeRetrieverV2 (as discussed in Section 7.1.3, "Implementing Custom Attribute Retrievers"), compile the Java code, add the compiled class to the class path of the Security Module instance, and make the following changes to the jps-config.xml configuration file.

  1. Declare the PIP service provider in the <serviceProviders> section as illustrated in Example 7-3.

    Example 7-3 serviceProviders Section of jps-config.xml

    <serviceProviders>
       <serviceProvider 
          class="oracle.security.jps.az.internal.runtime.provider.
             PIPServiceProvider" name="pip.service.provider" type="PIP"/>
    </serviceProviders>
    
  2. Declare the PIP service instance in the <serviceInstances> section as illustrated in Example 7-4.

    Example 7-4 serviceProviders Section of jps-config.xml

    <serviceInstances>
       <serviceInstance name="pip.service.MyAttributeRetriever"
        provider="pip.service.provider">
          <property name="type" value="CUSTOM_PIP"/>
          <property name="application" value="testPIPBasedOnCustomPIP"/>
          <property name="description" value="MyAttributeRetriever"/>
          <property name="classnames" value="pips.MyDummyAttributeRetriever"/>
      </serviceInstance>
    </serviceInstances>
    

    Specify the properties as defined in the following table.

    Name Value

    Type

    The value should be CUSTOM_PIP

    Application

    The application in which the PIP instance takes effect

    Classnames

    The fully qualified class name of the custom PIP


  3. Declare the PIP service instance in the <jpsContext> section as illustrated in Example 7-5.

    Example 7-5 jpsContext Section of jps-config.xml

    <jpsContext name="default">
       <serviceInstanceRef ref="pip.service.MyAttributeRetriever"/>
    </jpsContext>
    

7.2 Developing Custom Functions

A function can be used in a policy Condition to perform some advanced operation. The function may have a number of parameters and can return any of the supported data types. Oracle Entitlements Server provides a number of predefined functions and, additionally, allows you to declare your own.

A custom function can be implemented as a method in a class that may contain one or more custom functions. You can choose any method name as long as the name matches the corresponding name referenced in the policy. Custom functions can be passed as arguments consisting of constants or names of other attributes (including dynamic attributes) or names of other functions. Since all evaluation functions share a common namespace, two functions cannot have the same name. The following procedure details the steps to take when implementing a custom function in your policy.

  1. Write the custom code for the function.

  2. Compile the Java code.Add the complied class to the class path of the Security Module.

Example 7-6 illustrates how you might create a custom function.

Example 7-6 Sample Code for a Custom Function

package com.bea.security.examples;
 
import java.util.Map;
import java.util.Properties;
 
import javax.security.auth.Subject;
 
import weblogic.security.service.ContextHandler;
import weblogic.security.spi.Resource;
 
import com.bea.security.providers.authorization.asi.ARME.evaluator.RequestHandle;
import com.wles.util.AttributeElement;
 
public class MyEvaluationFunction {
    
    /**named evaluation function. Additional authorization request data
     is made available to allow for more complex attribute evaluation.
     This method will be registered to ARME, and be invoked while the policy    
     contains a custom evaluation function with name "string_longer_then".
     @param requestHandle the attributes container associated with the request, 
     through which the function can get required attribute value.
     @param args an array of function arguments.  
     Each element is either <code>null</code>, or a String
     @param subject the subject associated with the request
     @param roles the role membership of the subject
     key: role name.
     value: role object 
     <code>null</code> if function is called during role mapping
     @param resource the resource associated with the request
     @param contextHandler the context associated with the request, 
     may be <code>null</code> if non-existant
     @return <code>true</code> or <code>false</code> as the result of the function
     @throws MissingAttributeException for can not get required attribute value.
     */
    public boolean string_longer_then(RequestHandle requestHandle,
            Object[] args,
            Subject subject,
            Map roles,
            Resource resource,
            ContextHandler contextHandler) {
        // Check if we got a correct number of the input paramters
        if(args.length < 2 || args.length > 3 ||
                args[0] == null || args[1] == null) {
            // Incorrect number of arguments.
            // Such policy is invalid and can not be evaluated
            throw new RuntimeException
               ("Incorrect number of arguments in a function");
        }
 
        // Arguments for an evalaution function are attribute names.
        // Unfortunately, if a string literal or a numeric value is used
        // it is passed in as value.  The only way to distinguish
        // values from names is to try to look up the attribute.
 
        // Evaluation function should not set any values.
 
        // Get the integer value of the first argument.
        // This example does not do any type error checking 
        // - but your code should.
        int intCompLength = 0;
        try {
            AttributeElement strLength = requestHandle.getAttribute((String)args[0], true);
            if(strLength != null) {
                if(strLength.isList()) {
                    // string_longer_then: first argument not a single value
                    return false;
                }
                intCompLength = Integer.parseInt((String)strLength.getValueAs(String.class));
            } else {
                // numerical constant will be passed in as is.  
                try {
                    intCompLength = Integer.parseInt((String)args[0]);
                } catch(NumberFormatException ne) {
                    //value format is error, and no attribute in requestHandle.
                    throw new RuntimeException("miss attribute: " + args[0]);
                }
            }
        } catch(Exception e) {
            //caught exception while get attribute
            throw new RuntimeException(
                    "failed while get attribute: " + (String)args[0] + ". Exception: " + e.getMessage());
        }
        
        // Get the string value
        String input = null;
        try {
            AttributeElement strContent = requestHandle.getAttribute((String)args[1], true);
            if(strContent != null) {
                if(strContent.isList()) {
                    // string_longer_then: second argument is not a single value 
                    return false;
                }
                input = (String)strContent.getValueAs(String.class);
            } else {
                input = (String)args[1];
            }
        } catch(Exception e) {
            //caught exception while get attribute
            throw new RuntimeException(
                    "failed while get attribute: " + (String)args[0] + ". Exception: " + e.getMessage());
        }
        
        // return false, if the conditin is not satisfied
        if(input.length() <= intCompLength) {
            return false;
        }
        
        // Condition was satisfied.  Create and attach the return attribute
 
        /* The method appendReturnData(String name, Object data) on RequestHandle object
           does a copy of data.
           Return value will only be appended if the rule
           that called this function actually fired. 
           (other elements in the condition may prevent that)
        */
        requestHandle.appendReturnData("cropped_string", input);
        return true;
    }
    
}

Example 7-7 illustrates how you might create a custom function that returns a DataType argument.

Example 7-7 Sample Code for a Custom Function Returning DataType Argument

import oracle.security.jps.service.policystore.info.DataType;
import oracle.security.jps.service.policystore.info.OpssString;

 /** 
     Another example of evaluation function.
     Additional authorization request data
     is made available to allow for more complex attribute evaluation.
     This method will be registered to ARME, and be invoked while the policy    
     contains a custom evaluation function with name "string_longer_then".
     @param requestHandle the attributes container associated with the request, 
     through which the function can get required attribute value.
     @param args an array of function arguments.  
     Each element is either <code>null</code>, or a String
     @param subject the subject associated with the request
     @param roles the role membership of the subject
     key: role name.
     value: role object 
     <code>null</code> if function is called during role mapping
     @param resource the resource associated with the request
     @param contextHandler the context associated with the request, 
     may be <code>null</code> if non-existant
     @return <code>DataType</code> as the result of the function where
      DataType is any of the out-of-the-box supported data typs such as 
      OpssDate/ OpssTime/ OpssString etc.
     @throws MissingAttributeException for can not get required attribute value.
     */
    public DataType string_to_upper_case(RequestHandle requestHandle,
            Object[] args,
            Subject subject,
            Map roles,
            Resource resource,
            ContextHandler contextHandler)
    throws MissingAttributeException {
        // Check if we got a correct number of the input paramters
        if((args.length != 1) || (args[0] == null)) {
            // Incorrect number of arguments.
            // Such policy is invalid and can not be evaluated
            throw new RuntimeException
               ("Incorrect number of arguments in a function");
        }
 
        // Arguments for an evalaution function are attribute names.
        // Unfortunately, if a string literal or a numeric value is used
        // it is passed in as value.  The only way to distinguish
        // values from names is to try to look up the attribute.
 
        // Evaluation function should not set any values.
 
        // Get the integer value of the first argument.
        // This example does not do any type error checking 
        // - but your code should.
        int intCompLength = 0;
        try {
            AttributeElement str = requestHandle.getAttribute((String)args[0], true);
            String input = null;
            if(str != null) {
                if(str.isList()) {
                    // string_to_upper_case: first argument not a single value
                    return false;
                }
                input = (String)str.getValueAs(String.class);
            } else {
                // string constant will be passed in as is.  
                input = (String)args[0];
            }
        } catch(Exception e) {
            //caught exception while get attribute
            throw new RuntimeException(
                    "failed while get attribute: " + (String)args[0] + ". Exception: " + e.getMessage());
        }
        
      
        String output = input.toUpperCase();
        
        return new OpssString(output);
    }

For more information, see Section 2.3.3.2, "Creating Custom Function Definitions" and Section 2.3.5, "Defining a Condition."