Wikis - Page

Making Fields in a PRD Reflect Trustee Rights (ACLs) – Part 2

0 Likes
Making your administrative trustee rights reflect in the behavior of a PRD form is possible, but there are quite a few moving parts to make it happen. The story of this implementation will include several important steps including:

  • Understanding how Trustee Rights (ACLs) work in eDirectory

  • Reviewing the client’s use case

  • Getting the rights to see the rights

  • Reading and interpreting the rights

  • Copying the rights in to the form

  • Understanding Name Spaces in eDirectory, LDAP and IDM



  • Implementing the form behavior


In part 1 of this article, we focused on the back-end retrieval of ACL information for use by a Provisioning Request Definition (PRD) in order to enable the form to reflect an administrator’s rights. In part 2, we will discuss how to use that information within the Form API.


Forming it Up



The first thing we need is a form global variable to hold the ACL information. This can be instantiated in the global form onload event:



The specific code here is:

editable = new Array();
viewable = new Array();
supervisory = new Array();
editableOOB = new Array();
viewableOOB = new Array();


What’s notable here is that these variables are declared without the var keyword. This makes them global (as opposed to local) variables.

Now that we have a place to land our ACLs, we need to get them from the value sent by the Start activity and into the field. The following code is from the onload event of the “editable” field:

  • editable = form.getAllValues(field.getName())[0];


What’s in a Name (Space)?



One of the unique abilities, and challenges, of NetIQ IDM’s Directory Abstraction Layer is that the names of attributes that you might be familiar with as a directory administrator are not identical within the IDM Identity Applications. I understand the reason this was chosen; in the early daze of directory services, clients would use whatever was in the base schema for purposes that they were not intended for. How many clients had employee ID in the Generational Qualifier1 field?

Additionally, the same class of object might mean different things in different containers. OU might be a department in one part of the tree and a location somewhere else.

And the other reason was that Lightweight Directory Access Protocol (LDAP) was co-developed between Microsoft and Novell at a time when they didn’t like each other so much, so Microsoft didn’t want to adopt the schema of Novell Directory Access Protocol (NDAP) verbatim. So while Novell adopted LDAP and mapped it to NDAP, some attributes ended up with different names.

An unfortunate decision was to have the DAL introduce a third new name space, with friendlier names by default, but different from either one. I suppose the intent was to demystify directory services, but the DAL just added one more unneeded complexity.

All this is water under the bridge, the IDVault object uses DAL keys, and calls classes “entities”. We got used to it. Part of how I have worked around that over the years is to name fields for their DAL keys. That’s allowed me to build a collection of functions which are pretty generic.

But since we are now reading the ACLs using a DAL call, which uses VDX to make an LDAP call and return the value of the ACL which is a structured attribute containing a DN (returned in LDAP form) and the attribute (also returned using LDAP semantics) all this background explanation actually leads to a point: You may need to translate the ACLs read to match the DAL (or a field name if you don’t subscribe to my methodology) in order to apply them to the fields in your form.

To do this I employ a simple List object in the DAL:



I suppose I could have tried to read the DAL from my Start activity to do this and perhaps that’s a subject of some future article…

To use this, I create a variable to store the list (so that I don’t have to keep reading it):

  • xlate = IDVault.globalList(null, "DAL2LDAP");


Just One More Thing



For anyone out there who is a consultant, you know the term “scope creep”. This is when you get the scope of a project and then the client says, “Oh, we didn’t think of this, but can you add one more thing?”

The one more thing on this project was this: that a large part of their user population is synchronized from any of their HR applications (Workday, PeopleSoft, Lawson, etc.) to the Identity Vault, that the attributes are set to “reset”, and so, for half of the users, if you make a change using the PRD, the driver will just set them back. This is all by design.

So what was desired was to disable fields which are managed by the HR application even if the admin has rights to modify them. Now if I had started with that requirement I might have done things differently, but getting it late in the game, we took a simple approach.

  • We created another container with none of the users in it, and applied an alternative set of trustee rights there

  • We created an IDM driver which would set a Boolean attribute if the user has any HR driver associations

  • We use the alternate set of rights if the user is “HR Managed”


What’s unfortunate is that if the user population was into different containers based on HR Managed or not, this would have been a whole lot simpler.

Applying the Rights to the Form



As I indicated before, I purposefully name fields to match the attributes they are intended to represent. I do this to be able to create simple, reusable functions. In this case, there are several related bits of my methodology which enable this field.

In the onchange event of the recipient field, I have the following code:

  • publishEvent(field,event)


From the form scripts, here is that function:

function publishEvent(field,event)
{
/* republishes an event so other fields can use
it in the format eventname-fieldname */

if (!undef(field.getValue()))
{
field.fireEvent(event.getEventName() "-" field.getName(),field.getValue())
}
}


Every field that should be filled in when the target user is selected in the recipient field will then receive an onchange-recipient event. These event handlers do the work of filling in the field:

Here is an example of the onchange-recipient event from the FirstName field

  • fill(IDVault,event,field)


From the form scripts, here is that function:

function fill(IDVault,event,field,applyAllAttributeRights) 
{
// applyAllAttributeRights defaults to true if not supplied in the call
if (undef(applyAllAttributeRights)) applyAllAttributeRights=true;

// get the value from the vault for this field
var value= IDVault.get(null, event.getCustomData().toString(), "user", field.getName());

// should this field be made invisible or disabled?
visinabled(field, applyAllAttributeRights);

// if no value is returned from the vault, clear the field.
if (!undef(value))
{
field.setValues(value.toString())
}
else
{
field.setValues([""])
}

}


The implementation of visibility and enablement is implemented in a call to a function, visenabled(). The actual lookup within this function of rights happens in other related functions, ok2Enable() and ok2Show().

function visinabled(field, applyAllAttributeRights)
{
// applyAllAttributeRights defaults to true if not supplied in the call
if (undef(applyAllAttributeRights)) applyAllAttributeRights=true;

var isEditable = ok2Enable(field.getName(), applyAllAttributeRights);
var isVisible = ok2Show(field.getName(), applyAllAttributeRights);

visible=" isVisible.toString());

// for sensitive fields where [All Attribute Rights] should not apply make invisible
// if the field is not editable.

visible(field, isVisible);

if (!isEditable && !applyAllAttributeRights)
{
// dbg("Not editable and not applying all attribute rights forcing disabled");
enabled(field,false);
return false;
}
else
{
enabled(field, isEditable);
// dbg("enabled()");
return true;
}
}
function ok2Enable(fieldName, applyAllAttributeRights)
{



//default respect all attribute rights to yes
if (undef(applyAllAttributeRights)) applyAllAttributeRights=true;


if (applyAllAttributeRights && thisEditable().indexOf("[All Attributes Rights]") != -1)
{
return true;
}
else
{
var dex = xlate[0].indexOf(fieldName);
if (dex != -1) fieldName = xlate[1][dex];
return (thisEditable().indexOf(fieldName) != -1);
}
}

function ok2Show(fieldName, applyAllAttributeRights)
{
//default respect all attribute rights to yes
if (undef(applyAllAttributeRights)) applyAllAttributeRights=true;

if (applyAllAttributeRights && thisViewable().indexOf("[All Attributes Rights]") != -1)
{
return true;
}
else
{
//translate attributename to field name
var dex = xlate[0].indexOf(fieldName);
if (dex != -1) fieldName = xlate[1][dex];

return (thisViewable().indexOf(fieldName) != -1);
}
}

Remember how the target user might affect whether we want to enable or disable the user (whether or not they are HR Managed)? This is all handled here inside the call to the ok2xxxx() functions. In order to provide a simple mechanism to allow these two configurations to be bolted into my original design, these functions present the proper list depending on the status of the HRManaged flag
function OOB()
{
return !toBoolean(Form.getValue("XXHRmanaged"));
}

function toBoolean(strng)
{
if (undef(strng))
return false
else
return (strng.toString().toLowerCase() == "true");
}


function thisEditable()
{
if (OOB())
{
return editableOOB;
}
else
{
return editable;
}

}


function thisViewable()
{
if (OOB())
{
return viewableOOB;
}
else
{
return viewable;
}

}


Summary





The forms capability within the IDM User Application was originally designed to provide a self-service capability for administration requests. But the architecture is such that the opportunity presents itself to create highly customized and specialized administration tools. Making the form’s behavior mirror the user’s assigned rights makes the tool that much more intuitive.


1 Generational Qualifier is intended to be the generation specified after a name, Jr., Sr., III, IV, etc.99





Labels:

How To-Best Practice
Comment List
Related
Recommended