JIT Custom User Provisioning in OAM and SP Continued
This article shows how to build a custom User Provisioning module in OAM/SP. This is based on the OAM Developer’s Guide, chapter 16, which describes how to develop such a module.
In this article, focus on how to:
-
Implement the plugin
-
Compile it
-
Package it
-
Upload the plugin to OAM
-
Configure OAM to use the newly uploaded plugin
For this example, use the sample code listed in the OAM Developer’s Guide.
Custom User Provisioning Module
The User Provisioning framework allows the implementation of a custom plugin that implements the interfaces defined by the framework.
A custom User Provisioning plugin is made of the following:
-
One or more Java class implementing the custom User Provisioning plugin. One of the class extends the
oracle.security.fed.plugins.fed.provisioning.OIFUserProvisioningPlugin
class. -
A
MANIFEST.MF
file describing the Java classes. -
An XML file describing the plugin.
These three elements is bundled in a JAR file which is then uploaded to the OAM server via the OAM Administration Console. Once uploaded and activated, it can be used at runtime by OAM/SP.
Java Class
The class implementing the custom User Provisioning module must adhere to the following:
-
Extend the
oracle.security.fed.plugins.fed.provisioning.OIFUserProvisioningPlugin
class. -
Implement the following methods: public
ExecutionStatus
process(UserContext
context) throwsUserProvisioningException
. -
This method is invoked when a user record needs to be created.
-
Must return a status (failure or success).
-
In our example, this method checks that the user data contained in the
UserContext
has the required information. -
Open a connection to the LDAP server.
-
Create a user record.
-
public String
getPluginName()
returns the name of the custom User Provisioning module. -
In our example it returns
CustomProvisioningPlugin
. -
public String
getDescription()
returns a description of the custom User Provisioning module. -
In our example it returns
Custom Provisioning Plugin
. -
public Map
<String, MonitoringData> getMonitoringData()
is not used in a User Provisioning flow. -
In our example it returns null.
-
public boolean
getMonitoringStatus()
is not used in a User Provisioning flow. -
In our example it returns false.
-
public int
getRevision()
must be the same value than the version specified in the manifest file. -
In our example it returns 10.
-
public void
setMonitoringStatus(boolean status)
is not used in a User Provisioning flow. -
In our example this method is empty.
-
The class must implement the
org.osgi.framework.BundleActivator
interface and the following methods:-
public void start(BundleContext arg0) throws Exception.
-
In our example this method is empty.
-
-
public void stop(BundleContext arg0) throws Exception.
-
In our example this method is empty.
-
Optionally, if the plugin needs to read configuration data from the Plugin settings, the
initialize()
method can be implemented. -
public ExecutionStatus initialize (PluginConfig config)
-
In our example, this method reads the user base DN from a setting entry in the plugin config:
USER_BASE_DN
The following code is an example of the custom plugin called CustomerUserProvisioning
.
package userprov;
import java.util.Collection;
import java.util.Hashtable;
import java.util.Map;
import javax.naming.Context;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.InitialDirContext;
import oracle.security.am.plugin.ExecutionStatus;
import oracle.security.am.plugin.MonitoringData;
import oracle.security.am.plugin.PluginConfig;
import oracle.security.fed.plugins.fed.provisioning.OIFUserProvisioningPlugin;
import oracle.security.fed.plugins.fed.provisioning.UserContext;
import oracle.security.fed.plugins.fed.provisioning.UserProvisioningException;
public class CustomUserProvisioning extends OIFUserProvisioningPlugin implements
org.osgi.framework.BundleActivator
{
private String userBaseDN = null;
public ExecutionStatus initialize(PluginConfig config)
{
ExecutionStatus status = super.initialize(config);
if (status.getStatus() == ExecutionStatus.SUCCESS.getStatus())
userBaseDN = (String)config.getParameter("USER_BASE_DN");
return status;
}
public ExecutionStatus process(UserContext context) throws UserProvisioningException
{
try
{
// get user data
// after OIF/SP processing of the attributes sent by the IdP, we expect
// givenname, sn, mail as well as fed.nameidvalue which contains
// the userid
Map assertionAbributes = context.getAttributes();
Collection collection = (Collection)assertionAbributes.get("givenname");
String firstname = (String)(collection.size() > 0 ? collection.iterator().next() : null);
collection = (Collection)assertionAbributes.get("sn");
String lastname = (String)(collection.size() > 0 ? collection.iterator().next() : null);
collection = (Collection)assertionAbributes.get("mail");
String email = (String)(collection.size() > 0 ? collection.iterator().next() : null);
collection = (Collection)assertionAbributes.get("fed.nameidvalue"); String userid = (String)(collection.size() > 0 ?
collection.iterator().next() : null);
// check that the required attributes are present. If not, return an error
if (firstname == null || firstname.length() == 0 || lastname == null || lastname.length() == 0 || email == null || email.length() == 0 ||
userid == null || userid.length() == 0)
{
// failure
return ExecutionStatus.FAILURE;
}
String ldap = "ldap://adc00pcc.us.oracle.com:11389";
String username = "cn=orcladmin"; String password = "welcome1";
// connects to the LDAP directory
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, ldap);
if (ldap.toLowerCase().startsWith("ldaps"))
env.put(Context.SECURITY_PROTOCOL, "ssl");
env.put(Context.SECURITY_AUTHENTICATION,"simple");
env.put(Context.REFERRAL, "follow"); env.put(Context.SECURITY_PRINCIPAL, username);
env.put(Context.SECURITY_CREDENTIALS, password);
InitialDirContext ldapContext = new
InitialDirContext(env);
// create the user entry
BasicAttributes attributes = new
BasicAttributes();
// object classes
BasicAttribute objClassAbr = new
BasicAttribute("objectClass");
objClassAbr.add("person");
objClassAbr.add("organizationalPerson");
objClassAbr.add("inetOrgPerson");
objClassAbr.add("top");
attributes.put(objClassAbr);
// uid abr
attributes.put(new BasicAttribute("uid", userid));
// first name
attributes.put(new BasicAttribute("givenname", firstname));
// last name
attributes.put(new BasicAttribute("sn", lastname));
// email
attributes.put(new BasicAttribute("mail", email));
// DN: cn will be set to userID
String dn = "cn=" + userid + (userBaseDN != null && userBaseDN.length() \> 0 ? "," + userBaseDN : "");
// create the user record
ldapContext.createSubcontext(dn, attributes);
// return success
return ExecutionStatus.SUCCESS;
}
catch (Exception ex)
{
ex.printStackTrace();
return ExecutionStatus.FAILURE;
}
}
public String getPluginName()
{
return "CustomProvisioningPlugin";
}
public String getDescription()
{
return "Custom Provisioning Plugin";
}
public Map<String, MonitoringData> getMonitoringData()
{
return null;
}
public boolean getMonitoringStatus()
{
return false;
}
public int getRevision()
{
return 10;
}
public void setMonitoringStatus(boolean status)
{
}
public void start(BundleContext arg0) throws Exception
{
}
public void stop(BundleContext arg0) throws Exception
{
}
}
Plugin Registration File
The custom User Provisioning plugin must be defined in a plugin XML file such as:
<Plugin type="User Provisioning">
<author>uid=admin</author>
<email>admin@example</email>
<creationDate>08:00:00,2014-01-15</creationDate>
<description>Custom Provisioning Plugin</description>
<configuration>
<AbributeValuePair>
<Attribute type="string" length="100">USER_BASE_DN</Abribute> <mandatory>false</mandatory>
<instanceOverride>false</instanceOverride>
<globalUIOverride>false</globalUIOverride>
<value> </value>
</AbributeValuePair>
</configuration>
</Plugin>
Important Note: The XML file must have the same name as the class implementing the plugin, in this case
CustomUserProvisioning.xml
See the OAM Developer’s Guide for more information.
Manifest File
Before packaging the custom User Provisioning plugin in a JAR file, a MANIFEST.MF must be defined such as:
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: CustomUserProvisioning
Bundle-SymbolicName: CustomUserProvisioning
Bundle-Version: 10
Bundle-Activator: userprov.CustomUserProvisioning Import-Package: org.osgi.framework;version="1.3.0",oracle.security.fed
.plugins.fed.provisioning,javax.naming,javax.naming.directory,oracle. security.am.plugin
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
See the OAM Developer’s Guide for more information.
Note: The manifest file must include the ImportPackage property which lists all the packages that are used in the plugin.
Building the Plugin
Compiling
The following JAR files from the OAM deployment need to be used for compilation:
-
felix.jar
-
oam-plugin.jar
-
fed.jar
These files are found in the following locations:
-
felix.jar:
$IAM_HOME/oam/server/lib/plugin/felix.jar
-
oam-plugin.jar:
$IAM_HOME/oam/server/lib/plugin/oam-plugin.jar
-
fed.jar: in the
$DOMAIN_HOME/servers/MANAGED_INSTANCE_NAME/tmp/_WL_user/oam_server/RANDOM_STRING/APP-INF/lib
, withRANDOM_STRING
being a directory name with random characters, for example 88g74i in our test installation. In our example, we place the CustomerUserProvisioning.java file in asrc/userprov
folder: bash-4.1$ ls -lsrc/userprov/
total 8-rw-r--r-- 1 root root 4717 Mar 1 11:42 CustomUserProvisioning.java
To compile, execute the following command:
$JDK_HOME/bin/javac -cp $IAM_HOME/oam/server/lib/plugin/felix.jar:$IAM_HOME/oam/server/lib/plugin/oam-plugin.jar:/tmp/oam-server/APPINF/lib/fed.jar src/userprov/*.java
Packaging the Custom Plugin
We created the MANIFEST.MF in the current directory based on the content listed in the previous section, and the CustomUserProvisioning.xmlin
the src directory, which contains the plugin definition listed in the previous section.
find
.
./MANIFEST.MF
./src
./src/userprov
./src/userprov/CustomUserProvisioning.class
./src/userprov/CustomUserProvisioning.java ./src/CustomUserProvisioning.xml
To create the CustomUserProvisioning.jar
JAR file that contains the plugin and the required files, execute the following command:
jar cfvm CustomUserProvisioning.jar MANIFEST.MF -C src/ .
added manifest adding: userprov/(in = 0) (out= 0)(stored 0%) adding: userprov/CustomUserProvisioning.class(in =3991) (out= 1954)(de_ated 51%)
adding: userprov/CustomUserProvisioning.java(in =4717) (out= 1401)(de_ated 70%)
adding: CustomUserProvisioning.xml(in = 486) (out= 266)(de_ated 45%)
This creates the CustomUserProvisioning.jar
. To view the contents of the file:
unzip -l CustomUserProvisioning.jar
Archive: CustomUserProvisioning.jar
Length | Date | Time | Name |
---|---|---|---|
0 | 03-01-2014 | 14:32 | META-INF/ |
404 | 03-01-2014 | 14:32 | META-INF/MANIFEST.MF |
0 | 03-01-2014 | 12:10 | userprov/ |
3991 | 03-01-2014 | 12:10 | userprov/CustomUserProvisioning.class |
4717 | 03-01-2014 | 11:42 | userprov/CustomUserProvisioning.java |
486 | 03-01-2014 | 14:04 | CustomUserProvisioning.xml |
9598 | 6 files |
Important Note: The JAR file must have the same name as the class implementing the plugin, in this case CustomUserProvisioning.jar
Deploying the Custom Provisioning Module
Perform the following steps to deploy the custom User Provisioning plugin in OAM:
-
Go to the OAM Administration Console:
http(s)://oam-admin-host:oam-adminport/oamconsole
. -
Navigate to Access Manager , Plugins.
-
Click Import Plug-In.
-
Select the plugin JAR file (
CustomUserProvisioning.jar
in this example).
Description of the illustration Import_Plugin.jpg
The plugin will be in an uploaded state:
Description of the illustration Plugin_Screen.jpg
You need to distribute the plugin to the runtime OAM servers and activate it:
-
Select the plugin.
-
Click Distribute Selected.
-
The Activation Status tab of the plugin shows the state of the plugin.
Description of the illustration Plugin_Status.jpg
You needs to activate the plugin:
-
Select the plugin.
-
Click Activate Selected.
-
The Activation Status tab of the plugin shows the state of the plugin.
Description of the illustration Activation_Status.jpg
Finally, the plugin must be configured for user base DN. Perform the following steps:
-
Select the plugin.
-
Click on the Configuration Parameters tab of the plugin.
-
Enter the User Base DN (for example for the directory we are using: ou=users,dc=us,dc=oracle,dc=com).
-
Click Save.
Description of the illustration Configure_Plugin.jpg
Using the Custom User Provisioning Module
Enabling the User Provisioning in OAM
To enable/disable User Provisioning in OAM/SP, perform the following steps:
-
Enter the WLST environment by executing:
$IAM_ORACLE_HOME/common/bin/wlst.sh
. -
Connect to the WLS Admin server:
connect()
. -
Navigate to the Domain Runtime branch:
domainRuntime()
. -
Update the
userprovisioningenabled
property to: -
Enable User Provisioning in OAM/SP:
putBooleanProperty("/fedserverconfig /userprovisioningenabled", "true")
-
Disable User Provisioning in OAM/SP:
putBooleanProperty("/fedserverconfig /userprovisioningenabled", "false")
-
Exit the WLST environment:
exit()
.
Configuring OAM to use the Custom Plugin
To configure OAM/SP to use the custom plugin, perform the following steps:
-
Enter the WLST environment by executing:
$IAM_ORACLE_HOME/common/bin/wlst.sh
. -
Connect to the WLS Admin server:
connect()
. -
Navigate to the Domain Runtime branch:
domainRuntime()
. -
Update the
userprovisioningplugin
property to: Configure OAM to use the custom plugin, set the property to the plugin name (in our exampleCustomUserProvisioning
):putStringProperty("/fedserverconfig/userprovisioningplugin","CustomUserProvisioning")
-
Configure OAM to use the built-in User Provisioning module:
putStringProperty("/fedserverconfig /userprovisioningplugin","FedUserProvisioningPlugin")
-
Exit the WLST environment:
exit()
.
Testing Setup
Use the same SAML 2.0 Federation setup that was configured earlier, where:
-
OAM acts as a Service Provider
-
The IdP (
AcmeIdP
) sends a SAML Assertion with-
NameID
set touserID
-
Attributes sent
-
email set to user’s email address
-
fname set to user’s first name
-
-
surname set to user’s last name
-
title set to user’s last job title
-
OAM/SP configured with an IdP Attribute Profile to
-
Map fname to givenname
-
Map surname to sn
-
Map email to mail
-
User alice is used at the IdP, while no user account for alice exists at OAM/SP:
-
userID: alice
-
email: alice@oracle.com
-
first name: Alice
-
last name: Appleton
-
title: manager
-
During a SAML 2.0 Federation SSO with the remote IdP partner, the XML SAML Response with the Assertion sent back by the IdP is:
<samlp:Response ..>
<saml:Issuer ...>http://acme.com/idp</saml:Issuer>
<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
</samlp:Status>
<saml:Assertion ...>
<saml:Issuer ...>http://acme.com/idp</saml:Issuer>
<dsig:Signature ...>
...
</dsig:Signature>
<saml:Subject>
<saml:NameID ...>alice</saml:NameID>
...
</saml:Subject>
<saml:Conditions ...>
...
</saml:Conditions>
<saml:AuthnStatement ...>
...
</saml:AuthnStatement>
<saml:AbributeStatement ...>
<saml:Attribute Name="email" ...>
<saml:AbributeValue ...>alice@oracle.com</saml:AbributeValue>
</saml:Abribute>
<saml:Attribute Name="title" ...>
<saml:AbributeValue ...>manager</saml:AbributeValue>
</saml:Abribute>
<saml:Attribute Name="surname" ...>
<saml:AbributeValue ...>Appleton</saml:AbributeValue>
</saml:Abribute>
<saml:Attribute Name="fname" ...>
<saml:AbributeValue ...>Alice</saml:AbributeValue>
</saml:Abribute>
</saml:AbributeStatement>
</saml:Assertion>
</samlp:Response>
The result of the processing of the SAML 2.0 Assertion by OAM/SP shows the transformed attributes as well as the NameID
Description of the illustration Operation_Result.jpg
Test
After Federation SSO, the user record for alice was created:
- dn: uid=alice,ou=users,dc=us,dc=oracle,dc=com
- mail: alice@oracle.com
- givenName: Alice
- objectClass: person
- objectClass: inetOrgPerson
- objectClass: organizationalPerson
- objectClass: top
- uid: alice
- cn: alice
- n: alice
- sn: Appleton
More Learning Resources
Explore other labs on docs.oracle.com/learn or access more free learning content on the Oracle Learning YouTube channel. Additionally, visit education.oracle.com/learning-explorer to become an Oracle Learning Explorer.
For product documentation, visit Oracle Help Center.
JIT Custom User Provisioning in OAM and SP Continued
F60939-01
September 2022
Copyright © 2022, Oracle and/or its affiliates.