Walking through the Banner Driver – Part 4

In part 1 of this series I started explaining the driver configuration for the Ellucian Banner driver.

In part 2 of this series I started looking at the Global Configuration Variable values.

In part 3 of this series I finished going through the GCVs and other settings.

In this article I will start working through the Input Transform, since this is a mostly Publisher Channel driver.

The policies in the Input Transform policy set are named:

  • NOVLBNNRMSYS-itp-InitManagedSystemInfo

  • NOVLBNNRATRK-itp-Publish

  • NOVLBNNRATRK-itp-WriteAccounts

  • NOVLBNNRUSER-pub-itp_BannerDataConversion

  • NOVLBNNRUSER-pub-itp_GenerateUsername

  • NOVLPWDSYNC-itp-EmailOnFailedPwdSub

Let's get started.


Now normally, I would say that I will not parse through this policy, since it usually comes from the Data Collection Service packages and is pretty standard in most drivers. One thing I had noted was that one of the per driver settings that needed to differ was a GCV named: msysinfo.drv.ms.type

It is supposed to have a few characters long, started off as 2 or 3 characters long, though there appears to be no rule. I know I once collected all the values, in a table, but cannot find it. It is not actually important, but I was curious. I also used it to report all the broken values (Since a bunch of the drivers just copied from the AD driver, and used the AD value). To be honest, when I see a new driver come up, this is one of the first things I look at, since it is a trivial issue and mostly meaningless, but it demonstrates an attention to detail, and an understanding of how the Managed System Info packages work. I like seeing evidence that people understand stuff. It makes me feel better when I see it. I am a policy snob, I accept that.

All that said, this Banner driver package sets it to EDIR, a simple error. Easy to fix, I will report it.

Now there is a subtle difference here from other drivers, in terms of where the address and port of the authentication server is determined from. You will recall from earlier in this series that the server info is not the usual place, rather it is a configuration setting. Though as I review this, I wonder if that is actually correctly implemented here. It looks like it is using the standard authentication as an attribute of the driver object.

Interesting, but subtle and I think I am going to just let this go since it is fairly unimportant.


These two policies are part of the Account Tracking package and I really need to do a dedicated series just on these policies to put it all together in one place, but not right now. I have discussed these in my series on the Google Apps driver, and if you care, read through that series, starting at: https://www.netiq.com/communities/cool-solutions/series/walking-through-the-idm-4-google-apps-driver/


There is only a single rule in this policy object, named "Strip off Excess data from InstitutionRoles Attribute".

This helps if you understand Banner's use of institutional roles, which the docs describe in section 3.1.6 on the link:

There are three components that come for each Institutional Role from Banner.

  1. Role Name

  • Context

  • Institution Name

Context is apparently always the string "INTCOMP", which is pretty strange, but you go Ellucian!

Here the docs are helpful, where it notes that Banner will send the role data in XDS as:

<add-attr attr-name="InstitutionRoles">

The ITP rule here will change the output to:

<add-attr attr-name=”InstitutionalRoles”>

So lets go look at how the rule does that. We know it is focused on Institution Roles from the name of the rule, and we see the condition is if InstitutionRoles is changing.

It then loops over the XPATH statement:

This means in the current context (period), find any (two forward slashes // ), for any node (asterisk * ) whose XML attribute attr-name ([] for 'WHERE' and @attr-name for XML attribute attr-name) is equal to InstitutionRoles and then find any ( // again) value node, and get the text() it contains.

Now when I first looked at this, I said to myself, just use Reformat Operation Attribute token. (I was about to whip off a scathing email to the author, mocking him mercilessly, since he is a friend and probably won't take out a hit on me). But as I read further I realized there is a method to his madness. I still think he is mad, but that is a different story.

The key thing here is, there is a GCV gcv.NOVLBNNRUSER.institutionRolesFormat we discussed earlier in the series, that basically says how much of the Institutional Roles do you want? 1, 2, or all 3 parts. The GCV has the value 1, 2, or 3 as the value. (It is an Enum type, which means the pretty name of Role Only means 1, Role and Context means 2, and so on)

So they do something fairly clever here. The use the Split token, delimiting on a semi-colon, into a nodeset variable. Then it loops over the XPATH:
$lv_currentNode[position() <= $gcv.NOVLBNNRUSER.institutionRolesFormat]

That means, the nodeset we just built from the three components, ($lv_currentNode) whose position is less than or equal to the value in the GCV that has 1, 2, or 3. Ergo we loop 1, 2, or 3 times over this nodeset of three parts.

The clever part is that this simple approach handles all three cases simply.

Now inside the loop, they use the GCV gcv.NOVLBNNRUSER.institutionRolesSemiColonCharacter which holds a character to separate parts of the roles.

At some levels, if you were cool with all three parts, a simple Replace All changing the semicolon to an underscore would have sufficed. But this way they have the ability to handle 3 different cases.

There is a bit of an odd thing, where the do a replace all, on the lv_returnValue, but they append the underscore character at the end. I think this is to handle the case of one, where you want to return just the name, no trailing underscores. Ran through a bunch of test runs, and they all seem to work.

Then a Strip by XPATH of:
.//value/text()[. = $current-node]

That is, starting at the current context (period), find any child node named value (the // for any), and in the text() part of it, where current context (period, which is now the text() node) is the same as the value in the current-node variable. I.e. Remove just the text in the value node.

That is, find the add-attr or modify-attr node that has a value that we are looping over right now, and remove it. Then Append XML Text with a target of:
.//value[ancestor::*/@attr-name='InstitutionRoles' and not(node())]

And a value of the lv_returnValue local variable.

The target XPATH is saying start at the top, search for a value node, but conditionally that the ancestor (parent) has the XML attribute attr-name for our attribute, and is NOT a node(). I think they are trying to find the empty Text bit but I am not sure why that works with not(node()).

The goal is replace the value we just mucked with and replace it with our newly calculated value.

Finally they reset the variable for the next go round, through the InstitutionRole attribute, since there are probably many many values.

I wonder if this would have been simpler/more efficient to simply handle three Reformat Op Attr cases.

If the GCV value of gcv.NOVLBNNRUSER.institutionRolesFormat is 1, the reformat op attr would simply be an XPATH of substring-before($current-value,";")

If the GCV was 3, then a simple replace all of semicolon with the GCV gcv.NOVLBNNRUSER.institutionRolesSemiColonCharacter in $current-value would suffice.

If the GCV was 2, then a slightly more complex substring-before and substring-after would need to be performed I think. I am too lazy at this moment to work out the specifics.

Which approach is more elegant? I dunno, but I think both are reasonably valid.


There is only a single rule in here, named "Generate CN Attribute" and what it does is follow the GCV's definition of how to set a username, and it pre-calculates the CN value, in the Input Transform.

This is interesting, since it seems like normally we would handle this in the Placement policy. So why do it now? My first thought is that maybe so they have something to match on, in the Match policy? But that uses a GCV to define the name of the matching attribute, so there you would decide if you wanted to use the student number, or some such instead.

Maybe so they could have an initial username and then test for uniqueness in the Placement policy? Nope.

They do have a Veto if operational attribute CN is not available in the Create policy, but that is easily changed.

Honestly, I am not sure if there is a good reason for this.

The major downside is if a modify comes in, and turns into a synthetic add this will not be processed. (Now it may be that you never get a modify event, only add events, as I recall someone telling me that the SPML endpoint always sends the full user document, so I guess it would depend on how the shim maps the events coming as SPML. But that is buried in the shim where cannot see it. I find that frustrating in SOAP shims that hide the XDS-SOAP mapping process. I understand why they do it, but I prefer to have the visibility if nothing else. A good example is the SAP Portal driver, where the way passwords are changed is hidden inside the shim, but is not well documented. I have the outline of an article walking through what I discovered by careful choice of events I sent through, but have not yet had time to write it. One of these days.)

Regardless, here it is. I see one further instance of an issue, that no use of the Unique Name token is used, so while there is a great process for handling some amusing naming patterns in a VERY clever fashion (See Part 3 of this series ) it would be nice to also query to ensure the generated instance is actually unique.

If you recall from the previous article (part 3) the GCV that controls this process allows for very flexible naming patterns.

UDC Identifier
First Initial Last Name
First Name Last Initial
First Name Last Name
First Name . Last Name
First Initial Middle Initial Last Name
Last Name First Initial Middle Initial

This is controlled by a GCV gcv.NOVLBNNRUSER.pubAutoGenerateCN, and the value to use is stored in a GCV named gcv.NOVLBNNRUSER.pubCNFormat.

The value in pubCNFormat is some value like:

That means 1 character of first name, 0 characters of the middle name, and the remaining values of last name.

This naming model allows for a lot of flexibility and is a very clever way to handle this issue. So if the control GCV is true, its an add event, and the class is UDCIdentity, then the first test is, is there an operation attribute named after the value of the pubCNFormat GCV. One of the settings is UDCIdentity, which is the attribute name. Otherwise it looks for f1m1l* which of course is not there.

If there such an operation attribute it uses that. This is a nice and clever way to handle the many possibilities they are trying to cover. Provide an option to provide an attribute name, and by testing it is there, then use it, if not, make sure your string used to define the setting never possibly collides with the attribute names.

As a general approach this is really an elegant idea, and I quite like it. The rest of this policy then spends its time parsing the f1m1l* formatted value following the defined rules. This is a nice example of generic code implementing a configuration that can change, but the code remains the same. Very clever.

I think this is a good point to take a break and next article I will continue and take apart how they implemented this configuration parsing approach.


How To-Best Practice
Comment List