As you develop your extension, the extension needs to perform the following tasks:
For information about the Extension API interfaces and class, see the Javadoc API Reference.
Each extension type has two interfaces:
A factory interface that contains the method for initializing an extension object with data from the engine that the extension can use to retrieve data from an external source or to evaluate a condition or an action.
An extension interface that contains the methods that need to be implemented for the specific type of extension. For example, the NxpeCondition interface contains the method for evaluating the condition and returning True, False, or Error.
All extensions need to implement both interfaces for the extension type and use the NxpeResult class for return codes and the NxpeException class for exceptions.
The NxpeResult class allows an extension to return the following values:
Return Code |
Extension Type |
Description |
---|---|---|
Cancel |
Reserved |
|
ConditionFalse |
Condition |
The compared values do not match, so the condition evaluation resolved to False. |
ConditionTrue |
Condition |
The compared values match, so the condition evaluation resolved to True. |
ConditionUnknown |
Condition |
The values could not be compared, so the results are unknown. This is comparable to the Result on Condition Error option when creating a policy. |
Deny |
Action |
A deny action was applied. |
ErrorBadData |
Context Data |
The data cannot be parsed. This result can be returned with the NxpeException class. |
ErrorCodeComponent |
Reserved. |
|
ErrorConfigInitialization |
All |
The initialize method for the extension encountered an error. This result can be returned with the NxpeException class. |
ErrorDataUnavailable |
Context Data |
The requested data is not available. This result can be returned with the NxpeException class. |
ErrorIllegalArgument |
All |
The informationContext object contains an unknown parameter. This result can be returned with the NxpeException class. |
ErrorIllegalState |
Reserved |
|
ErrorInterfaceUnavailable |
All |
The extension has not implemented one of the required methods in the interface. This result can be returned with the NxpeException class. |
ErrorNoMemory |
Reserved |
|
GeneralFailure |
All |
Unknown error. This result can be returned with the NxpeException class. |
NoAction |
|
Reserved for use by the policy engine. |
Obligation |
Action |
An obligation action was performed. |
Pending |
Reserved. |
|
Permit |
Action |
A permit action was performed. |
Success |
|
Reserved for use by the policy engine. |
You can use a constructor that throws exceptions with the following information:
No information
With a string message
With a string message and a cause
With a result from the NxpeResult class. See Return Codes in the NxpeResult Class.
With a cause and a result from the NxpeResult class
With a string message and a result from the NxpeResult class
With a string message, a cause, and a result from the NxpeResult class
All extension types need to implement the factory interface for the extension type and initialize an object specific to its type. The policy engine uses this object to send the parameter information about the user making the request to the extension. The extension uses this object to return its results to the policy engine.
The following code sample illustrates how to implement the factory interface. It uses the NxpeContextDataElementFactory to create an LDAPGroupDataElement object.
1 package ContextDataElement; 2 3 import com.novell.nxpe.NxpeContextDataElement; 4 import com.novell.nxpe.NxpeContextDataElementFactory; 5 import com.novell.nxpe.NxpeException; 6 7 public final class LDAPGroupDataElementFactory implements NxpeContextDataElementFactory 8 { 9 public LDAPGroupDataElementFactory() 10 { 11 } 11 public NxpeContextDataElement getInstance( 12 String strName, 13 int iEnumerativeValue, 14 String strParameter) 15 throws NxpeException 16 { 17 return (new LDAPGroupDataElement(strName, iEnumerativeValue, strParameter) ); 18 } 19 } /* LDAPGroupDataElementFactory */
The package line needs to be replaced with the package line for your extension.
All extensions need the three import lines for the factory interface. The first two import lines vary with the type of extension you are creating, but you need to import the factory interface and the extension interface.
Lines 7 through 19 implement the factory interface that creates an LDAPGroupDataElement object.
The other factory interfaces are very similar and are as easy to implement.
All extensions need to access an external data store and retrieve information from it. You must know the type of data that your extension will retrieve, and then design how you are going to retrieve it.
If the extension needs to establish a connection to the external data store and log in to retrieve information, consider using one of the following methods:
The extension can use the credentials that authenticated the user to Identity Server to log in as a user in the external data store. This method assumes that the user has the same credentials in Identity Server user store and the external data store.
You can create an LDAP attribute in Identity Server user store and store an X.509 certificate that you can use to access the external data store.
You can create configuration parameters that allow the administrator of Administration Console to enter a username and password for accessing the external data store. The password is entered in clear text in Administration Console, so this is not a secure method. To minimize the security risk, you can create a special user in the external data store whose rights are restricted to retrieving only the information required by the extension. If the retrieved information is not sensitive, this simple solution might not present a security risk.
When you create configuration parameters, you need to provide documentation for the administrator who installs the extension. Each configuration parameter requires a name, an ID, and a mapping to a data item. You need to document these for the administrator.
The name and ID you create to fit your programing requirements. These must be mapped to a data item available for the extension type.
NOTE:The data items are returned as strings or as string arrays if they are multivalued.
Your external data store and the methods available for accessing its data determine whether any of the data items are useful in making the connection to the external data store.
For the data items specific to an extension type, see the following:
All extensions need to perform the following tasks.
All extensions need a package line and the following import lines. The package line for the sample needs to be replaced with the package line for your extension. The first import line needs to be modified to import the interface for the extension type you are creating. The other import lines are standard for all extensions.
package ContextDataElement; import com.novell.nxpe.NxpeContextDataElement; import com.novell.nxpe.NxpeException; import com.novell.nxpe.NxpeInformationContext; import com.novell.nxpe.NxpeParameter; import com.novell.nxpe.NxpeParameterList; import com.novell.nxpe.NxpeResponseContext; import com.novell.nxpe.NxpeResult;
The NxpeExpection class contains the defined constructors for throwing exceptions. For more information, see Constructors in the NxpeException Class.
The NxpeInformationContext class contains methods that allow you to gather information about extension evaluation.
The NxpeParameter class contains methods that allow you to retrieve information about a specific configuration parameter.
The NxpeParamaterList class contains methods that allow you to retrieve information about the configuration parameters you have defined for the extension.
The NxpeResponseContext class contains methods that allow you to configure the information that is sent with the results, such as logging or trace entry.
The NxpeResult class contains the methods and constants to set the return value for the extension. For more information, see Return Codes in the NxpeResult Class.
If your extension requires configuration parameters, you need to define them. The following code snippet contains the parameters for the LDAP group extension. These are the name and ID values that are configured on the Extension Details page (Policies > Extensions > [Extension Name]).
private static final String USER_STORE_NAME = "User Store"; private static final int EV_USER_STORE = 11; private static final String AUTHENTICATION_NAME = "Authentication"; private static final int EV_AUTHENTICATION = 211; private static final String DEFAULT_AUTHENTICATION = "simple"; private static final String DIRECTORY_TYPE_NAME = "Directory Type"; private static final int EV_DIRECTORY_TYPE = 222; private static final String DEFAULT_DIRECTORY_TYPE = "unknown"; private static final String PROVIDER_URL_NAME = "User Store Replica"; private static final int EV_PROVIDER_URL = 31; private static final String DEFAULT_PROVIDER_URL = "ldap://localhost:389"; private static final String LDAP_USER_DN_NAME = "LDAP User DN"; private static final int EV_LDAP_USER_DN = 41; private static final String SECURITY_PRINCIPAL_NAME = "Security Principal"; private static final int EV_SECURITY_PRINCIPAL = 51; private static final String SECURITY_CREDENTIALS_NAME = "Security Credentials"; private static final int EV_SECURITY_CREDENTIALS = 52; private static final String SEARCH_CONTEXT_NAME = "Search Context"; private static final int EV_SEARCH_CONTEXT = 61; private static final String DEBUG_NAME = "Debug"; private static final int EV_DEBUG = 91;
Not all of the parameters need to be defined in Administration Console. If you want the administrator to decide the value that is mapped to the parameter, then you need to document the parameter and let the administrator select the mapping.
This is also a good place to define any other static constants your extension needs.
If your extension needs to be aware of some parameter values before it is called during policy evaluation, you can retrieve the values during the initialize method. Each extension interface (NxpeAction, NxpeCondition, NxpeContextDataElement) has an initialize method that contains a configurationValues object. The following code snippet illustrates the LDAP group extension defined for this method. The setDebug line shows how to obtain the current value for the debug parameter.
public void initialize( NxpeParameterList configurationValues) throws NxpeException { this.configurationValues = configurationValues; setDebug(configurationValues); strProviderURL = DEFAULT_PROVIDER_URL; strAuthentication = DEFAULT_AUTHENTICATION; strDirectoryType = DEFAULT_DIRECTORY_TYPE; StringBuffer sbLdapFilter = new StringBuffer(128); // setup filter sbLdapFilter.append("(|(objectClass="); sbLdapFilter.append(CLS_GROUP); sbLdapFilter.append(")(objectClass="); sbLdapFilter.append(CLS_GROUPOFNAMES); sbLdapFilter.append(")(objectClass="); sbLdapFilter.append(CLS_GROUPOFUNIQUENAMES); sbLdapFilter.append("))"); strLdapFilter = new String(sbLdapFilter); // setup search controls searchControls = new SearchControls(); searchControls.setTimeLimit(0); searchControls.setReturningObjFlag(true); searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE); searchControls.setReturningAttributes(new String[] { ATTR_CN }); }
Besides having an initialize method, each extension interface has a few other methods that need to be implemented. The NxpeContextDataElement interface has the get methods. The following code snippet illustrates how the LDAP Group extension implements three of these methods.
public int getEnumerativeValue() { return (iEnumerativeValue); } public String getName() { return (strName); } public String getParameter() { return (strParameter);
NxpeContextDataElement introduces a new element with additional methods. Using these methods, you can set the duration for which the data returned from the extension interface is cached by Access Manager.
public int getValidForSeconds() { return -1; } public int getValidForSeconds () { return 0; } public int getValidForSeconds () { return n; }
getValidForSeconds informs the policy engine about how often data needs to be queried. Specify 0 as the return value to query data for each request. Specify -1 as the return value to cache the data. Substitute n with the number of seconds to indicate validity of the data.The fourth method (the getValue method) is described in the next section. See Task 5: Retrieving Configuration Parameters at Policy Evaluation.
All extension interfaces have a method for retrieving configuration parameters at policy evaluation. The NxpeCondition interface has an evaluate method with an informationContext object. The NxpeAction interface has a doAction method with a informationCxt object. The NxpeContextDataElement interface has a getValue method with an informationContext object. The informationContext object contains information about the user and the user’s request that you need. You populate this object with the parameters that you need to evaluate the policy, and the policy engine supplies the values.
The following code snippet illustrates how the LDAP Group extension retrieves parameter values:
public synchronized Object getValue( NxpeInformationContext informationContext, NxpeResponseContext responseContext) throws NxpeException { LdapContext ldapContext = null; String strUserStore = getUserStore(informationContext); String strProviderURL = getProviderURL(informationContext); String strAuthentication = getAuthentication(informationContext); String strDirectoryType = getDirectoryType(informationContext); String strLDAPUserDN = getLDAPUserDN(informationContext); String strDN = getSecurityPrincipal(informationContext); if (strLDAPUserDN == null) { strLDAPUserDN = strDN; } String strPassword = getSecurityCredentials(informationContext); String strSearchContext = getSearchContext(informationContext);
Notice that this code snippet does not have an ending parenthesis. All the main work of the extension is done in this method. The next two tasks (Task 6: Connecting with the External Data Source and Task 7: Returning from an Extension) are performed within the getValue method.
How you connect to the external data source in your extension is specific to the type of data source you are using. The following code snippet from the LDAP Group extension file illustrates how to connect to an LDAP user store:
try { HashSet<String> groupDNs = new HashSet<String>(); ldapContext = newInitialLdapContext(strDN, strPassword); NamingEnumeration neGroups = ldapContext.search(strSearchContext, strLdapMemberFilter, searchControls);
This piece of code is very specific to LDAP.
The following code snippet from the LDAP Group extension illustrates the tasks you need to complete as you return the results of your extension action/evaluation to the policy engine:
while (neGroups.hasMore()) { Attribute cn; SearchResult srGroup = (SearchResult) neGroups.next(); String strGroupDN = srGroup.getNameInNamespace(); groupDNs.add(strGroupDN); if (debug) { System.out.println("LDAPGroupDataElement: \"" + strGroupDN + "\""); } } String[] strGroupDNs = new String[groupDNs.size()]; groupDNs.toArray(strGroupDNs); return (strGroupDNs);
This code searches through the LDAP search results, retrieves the DN of any group found, adds it to the array, then returns the array.
This task is specific to the purpose of the extension. If the purpose of the extension is to evaluate a condition and determine whether the user matches the condition, the code for this task should show the extension obtaining the user’s value for the condition, comparing that value to the expected value, then return True for a match, False for a mismatch, and Error if extension cannot perform the evaluation.
Each extension must handle potential error conditions. The following snippet illustrate how the LDAP Group extension handles potential errors:
catch (NamingException e) { if (debug) { e.printStackTrace(); } throw (new NxpeException(NxpeResult.ErrorDataUnavailable, e)); } finally { if (ldapContext != null) { try { ldapContext.close(); } catch (NamingException e) { if (debug) { System.out.println(e.getMessage()); }
After your extension has implemented all the required interface methods, the rest of the code implements what the extension requires to perform its purpose. Everything that follows the
********************** LDAPGroupDataElement/private ********************
comment in the LDAPGroupDataElement.java file shows how the LDAP Group extension performs its required tasks. For example, you can see how the extension retrieves parameter information from the policy engine, such as the user’s DN, security credentials, and user store information. With this information the extension interacts with the LDAP user store and retrieves the groups the user belongs to.