Wednesday, August 6, 2008

OID Indexes

Here are a few simple notes for handling OID indexes. If you want to search on an attribute in OAM where the data is stored in OID it must be indexed. Sometimes you might want to remove and then possible re-add an index. Index adding and removal can be handles with LDIF, however, if you need to recreate an index on existing data then you need to use a command line tool called catalog.

Index an attribute

dn: cn=catalogs
changetype: modify
add: orclindexedattribute
orclindexedattribute: attributename
-

Remove an index

dn: cn=catalogs
changetype: modify
delete: orclindexedattribute
orclindexedattribute: attributename
-

Re-index an attribute
that was previously removed. If you remove an index form an attribute and the data remains and you need to re-index the data in place then you need to use the catalog command line tool.

$ORACLE_HOME/ldap/bin/catalog connect=oiddev add=&quotTRUE" attribute="attributename"

Unwilling to perform
If after the attribute has been re-indexed the directory server will still not allow it to be searched and returns an unwilling to perform error, try restarting the OID gateway.

$ORACLE_HOME/opmn/bin/opmnctl restartproc ias-component=OID

Current Indexed AttirbutesUse ldapsearch to get the current indexed attributes

ldapsearch -h localhost -p 389 -x -s base -b "cn=catalogs" "objectclass=*"

Saturday, July 26, 2008

Deleting a User with IDXML

Certain actions (such as creating or removing an LDAP entry) are only available via OAM's 'workflow' engine. A freshly installed OAM system has no workflows configured, thus, no immediate mechanism to affect such actions.

To the newly initiated, discovering the create workflow mechanisms are relatively straightforward. But the delete, however, tends to throw people for a loop at first.

The trick is to create a 'Deactivate User Workflow'. Exactly what this workflow does is up the user building the workflow. You'll find, following the definition of the initial step, three similar action choices:
  • deactivate
  • disable
  • delete

If your goal is truly to whack the account, choose delete. Otherwise, a choice of disable will set the user account ObUserAccountControl flag to DEACTIVATED (with no human interaction required). By default, the Identity System ignores DEACTIVATED accounts in the user searchbase. The deactivate action accomplishes the same thing but it requires a human participant to actually push the button to confirm the action.

Lastly, if you want to access this 'Delete User Workflow' from IDXML you just need to keep in mind that it is a workflow you are calling. Pay close attention to:
  • function="workflowDeactivateUserSave"
  • and the fact that you do provide the workflow DN in the call
Here is a complete request for calling a Deactivate User Workflow:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas-xmlsoap.org/soap/envelope/" xmlns:oblix="http://www.oblix.com">
<SOAP-ENV:Body>
<oblix:authentication type="basic">
<oblix:login>admin</oblix:login>
<oblix:password>test1234</oblix:password>
</oblix:authentication>
<oblix:request application="userservcenter" function="workflowDeactivateUserSave" version="NPWSDL1.0">
<oblix:params>
<oblix:ObWorkflowName>obworkflowid=c60491a5ca0a45668fff08da2f1072d2,obcontainerId=workflowDefinitions,OU=Oblix,OU=apps,DC=company,DC=com</oblix:ObWorkflowName>
<oblix:uid>UID=372af3c1-0c7e-428d-a80a-fae632211489,OU=people,DC=company,DC=com</oblix:uid>
<oblix:noOfFields>2</oblix:noOfFields>
<AttributeParams xmlns="http://www.oblix.com/">
<GenericAttribute>
<AttrName>cn</AttrName>
<AttrNewValue>test</AttrNewValue>
<AttrOperation>REPLACE_ALL</AttrOperation>
</GenericAttribute>
<GenericAttribute>
<AttrName>userStatus</AttrName>
<AttrNewValue>delete</AttrNewValue>
<AttrOperation>REPLACE_ALL</AttrOperation>
</GenericAttribute>
</AttributeParams>
</oblix:params>
</oblix:request>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Tuesday, July 22, 2008

OAM Identity XML (IDXML) via XMLHttpRequest

It makes sense that the ideal HTTP Client for IDXML processing is the authenticated user's browser. After all, it already has the ObSSOCookie.

JQuery is the Javascript library of choice for all my client work lately. You can see why in the following example of processing an IDXML request via Javascript straight from the client. The use cases for this capability are endless.

This is the proverbial 'tip of the iceberg' in utilizing OAM Identity in a modern web development context. The end result: Perfectable user experiences based on data and services made available and secured through OAM's web based configuration tools. It's a powerful combination.

Lets take a simple create user workflow request and turn out a simple Javascript templating function to build the string for us:
getSoap = function(data){
  var dat = [];
  dat[dat.length] = '<?xml version="1.0" encoding="UTF-8"?>';
  dat[dat.length] = '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas-xmlsoap.org/soap/envelope/" xmlns:oblix="http://www.oblix.com">';
  dat[dat.length] = '<SOAP-ENV:Body>';
  dat[dat.length] = '<oblix:request function="workflowSaveCreateProfile" version="NPWSDL1.0">';
  dat[dat.length] = '<oblix:params>';
  dat[dat.length] = '<oblix:ObWorkflowName>obworkflowid=672fcf2e9c5946a8b5b225b349acd46b,obcontainerId=workflowDefinitions,OU=Oblix,OU=apps,DC=company,DC=com</oblix:ObWorkflowName>';
  dat[dat.length] = '<oblix:ObDomainName>OU=people,DC=company,DC=com</oblix:ObDomainName>';
  dat[dat.length] = '<oblix:noOfFields>5</oblix:noOfFields>';
  dat[dat.length] = '<AttributeParams xmlns="http://www.oblix.com/">';
  dat[dat.length] = '<GenericAttribute>';
  dat[dat.length] = '<AttrName>uid</AttrName>';
  dat[dat.length] = '<AttrNewValue>'+data.uid+'</AttrNewValue>';
  dat[dat.length] = '<AttrOperation>ADD</AttrOperation>';
  dat[dat.length] = '</GenericAttribute>';
  dat[dat.length] = '<GenericAttribute>';
  dat[dat.length] = '<AttrName>cn</AttrName>';
  dat[dat.length] = '<AttrNewValue>'+data.cn+'</AttrNewValue>';
  dat[dat.length] = '<AttrOperation>ADD</AttrOperation>';
  dat[dat.length] = '</GenericAttribute>';
  dat[dat.length] = '<GenericAttribute>';
  dat[dat.length] = '<AttrName>mail</AttrName>';
  dat[dat.length] = '<AttrNewValue>'+data.mail+'</AttrNewValue>';
  dat[dat.length] = '<AttrOperation>ADD</AttrOperation>';
  dat[dat.length] = '</GenericAttribute>';
  dat[dat.length] = '<GenericAttribute>';
  dat[dat.length] = '<AttrName>givenName</AttrName>';
  dat[dat.length] = '<AttrNewValue>'+data.givenName+'</AttrNewValue>';
  dat[dat.length] = '<AttrOperation>ADD</AttrOperation>';
  dat[dat.length] = '</GenericAttribute>';
  dat[dat.length] = '<GenericAttribute>';
  dat[dat.length] = '<AttrName>sn</AttrName>';
  dat[dat.length] = '<AttrNewValue>'+data.sn+'</AttrNewValue>';
  dat[dat.length] = '<AttrOperation>ADD</AttrOperation>';
  dat[dat.length] = '</GenericAttribute>';
  dat[dat.length] = '</AttributeParams>';
  dat[dat.length] = '<oblix:obactorcomment>IDXML from browser via Javascrip</oblix:obactorcomment>';
  dat[dat.length] = '</oblix:params>';
  dat[dat.length] = '</oblix:request>';
  dat[dat.length] = '</SOAP-ENV:Body>';
  dat[dat.length] = '</SOAP-ENV:Envelope>';

  return dat.join("");
};


Then, if we prep a little data object with values (presumably pulled from the user interface):

var userdata = {
  uid:"marmil",
  cn:"Mark Miller",
  mail:"mark[at]nulli.com",
  givenName:"Mark",
  sn:"Miller"
};


I can call my template and consider my soap envelope ready to go:

var createUserSoapRequest = getSoap(userdata);


All over but the sending (and response handling):

// process the request
$.ajax({
  type: "POST",
  dataType:'xml',
  url: "/identity/oblix/apps/userservcenter/bin/userservcenter.cgi",
  data: createUserSoapRequest,
  contentType:"text/xml",
  processData:false,
  success: function(idxmlResponse){
    // crude
    alert(idxmlResponse);

    // better
    $("ObConfirmation",idxmlResponse).find("ObValue").each(function(i,o){
      alert($(o).text());
    });

    // in the real world, employ dom trickery to keep the user oriented...
  }
});


Cool, no?

OAM Search Results Virtual Attribute

What if you want to include a virtual attribute in your search results that is derived form another attribute? Just create the attribute in an outbound mapper flow and add it to the search results and it should show up as expected - right? Well, maybe it will and maybe it won't; if the attribute(s) the derived value is based on is/are in the search results too then it will show up as anticipated. However, if they are not there then it won't show up as expected.

The solution is to use the OVD addReturnAttribute delivered mapper function on the reciprocal inbound request with the required source attributes. This will allow the outbound mapper to have the required data to complete the derived attribute and enable the OAM search result configuration to exist without the source attributes.

One Bad Mapper Can Spoil the Whole Bunch (if you're not careful)

Creating a mapping file for OVD to use on inbound and/or outbound LDAP transactions can sometimes be tricky to get absolutely correct the first time. Invariably, the message
Could not complete mapping
is bound to show up at least once when you are trying something new.

When a mapping file is broken it can cause a variety of problems, but most notably if your adapters for authentication are using other unrelated mappers it can cause them to fail, resulting in failed authentication. So for instance if you are working on a mapper for one object you could prevent other users from authenticating. This pitfall can be easily avoided, however, using the OVD Filters to Exclude and Filters to Include fields on the routing tab of the adapters tab in OVD Manager. For instance by exluding (objectclass=inetorgperson) from an adapter that presents site data you can prevent an authentication search request from even attempting the adapter, thus insulating you and other development users from such a problem.

However, what if you cannot authenticate to the OVD Manager console to correct a broken mapper or add a routing filter to an adapter? I had one case where I could not authenticate even as cn=admin do the OVD Manager becuase of a broken adapter. The solution to this is simple; just remove the compiled mapper jar file from the OVD Server directory. You can do so by following these steps:
  1. stop ovd
  2. remove bad mapper ($VDE_DIR/mappings/jars/offendingMappingFile.jar)
  3. start ovd
  4. log into the OVD Manager and fix the source of the problem

Thursday, July 3, 2008

Error: Look up of symbol - ObInitEventAPI failed

If you ever receive an error like this when trying to configure an identity system .NET based PPP event, check the action; chances are it is set to lib instead of managedlib.

"Event API call for the event returned STATUS_PPP_ABORT" Error^base\obport.cpp:845: Error: Look up of symbol - ObInitEventAPI failed - The specified procedure could not be found.%0d%0a.

Thursday, May 22, 2008

WebGate - Oracle Client Conflict

When installing the 10.1.4 WebGate to protect an application with web services that relied on the Oracle Client for database connectivity, the application failed to run after the install. The following message is what was received back from the application:
The provider is not compatible with the version of Oracle client
The WebGate installation went seamlessly. It seemed apparent from the message that there must be an Oracle Client dll conflict. After looking at the IIS process through Process Explorer it became apparent that the application was relying on the Oracle 10g R2 client, but that WebGate was loading the Oracle 10g R1 client prior to the Web Service. Then when the web service was invoked, it would get the wrong client and thus fail.

My first instinct was to change the Oracle Client the web appl was using to 10.1 from 10.2. This worked for a while but it turned out there was a bug in the the 10.1 client that caused one of the components of the web app to fail. I had to re-install the 10.2 client.

My next instinct was to just proxy the web application with a proxy server and take the WebGate out of the mix on the web server where the app was hosted. Then I read a MetaLink KB article that suggested using a WebGate 7.0.4 build that DOES NOT include the oracle client in it. This seemed like a good opportunity so I parked the proxy idea.

I installed the WebGate but could not configure it with the 10.1.4 Access Server. In the Access Server oblog.log there was the following message.
Client and Server's NAP versions do not match
It turns out that the the 10.1.4 Access Server IS backwards compatible but not by default. In the ..\access\oblix\apps\common\bin\globalparams.xml file

there is a parameter - IsBackwardCompatible - that needs to be set to true. Once that is set, voila, the WebGate configuration completes.