A question that comes up often is, "How to do I construct a loop in OGNL?" For example, I might have a collection/array of objects that I need to loop through in order to check or modify the values in it. A very common use case is with group membership (I dealt with this topic in a Technical Roundtable: Managing Groups and OGNL).
As part of sending identity information, you often need to send the groups the user is a member of in an LDAP directory.
The memberOf attribute often contains a DN or distinguishedName in the following format:
CN=Managers,CN=Users,DC=company,DC=com
But let's say that your partner cannot handle the DN format and just wants the group name, which in our example is Managers. How do we remove all the extra bits of the DN to get it down to just the name? There are many ways to accomplish that task, but we will not digress too much. The Java SDK provides a class (javax.naming.ldap.LdapName) that will do the hard work for us, so we will simply use that.
See the complete list of articles in my OGNL series at the end of this article.
The second problem, and what interests us, is that usually you get multiple groups in the memberOf attribute; therefore, you need to loop through each one.
In my previous article, I first talked about curly {} braces and how they can be used to create a collection or array. In addition to creating a collection, the curly braces can also be used to let us step through a collection and even create a new one in the process.
I also talked about the how the org.sourceid.saml20.adapter.attribute.AttributeValue class is used to retrieve attributes available to us in the PingFederate context, either from an Adapter, assertion, or a data source (such as LDAP). If the attribute that PingFederate provides is multi-valued (i.e., a collection) we can use the getValues method to get this collection.
Taking those two bits of knowledge above and some Java know-how, we can create some code that looks like the following:
1. #groupCnOnly = new java.util.ArrayList(),
2. #groups = #this.get("ds.LDAP.memberOf")!=null?#this.get("ds.LDAP.memberOf").getValues() : {},
3. #i= 0,
4. #groups.{
5. #group = new javax.naming.ldap.LdapName(#groups[#i]),
6. #cn = #group.getRdn(#group.size() - 1).getValue().toString(),
7. #groupCnOnly.add(#cn),
8. #i = #i + 1
9. },
10. #this.get("ds.LDAP.memberOf")!=null? new org.sourceid.saml20.adapter.attribute.AttributeValue(#groupCnOnly) : null
I'm taking a slightly different approach of showing you all the code upfront, but now I'll explain it line-by-line (line numbers above should be removed when you copy this code into your own PingFederate environment):
- With the org.sourceid.saml20.adapter.attribute.AttributeValue class, we can create an object that takes an java.util.ArrayList a collection. We want to return the CN for all the groups; therefore we'll create an object to hold those groups.
- We check to make sure that the memberOf attribute retrieved from the LDAP data source actually exists; in other words, does the user belong to at least one group?
- If so, we extract this collection (an ArrayList) from the current context.
- If not, we create an empty collection with the {} braces.
- If so, we extract this collection (an ArrayList) from the current context.
- We initialize a counter to control the loop, because we'll need to go through each of the objects in the collection.
- The groups object either contains the collection of groups or an empty collection. The curly braces will provide an iteration through each entry in the collection -- effectively a for or where loop that will take us through each entry in the collection. Inside the curly braces, we place the code to process each entry in turn.
- Let's extract the nth entry (using the loop counter i). Remember that arrays in OGNL are zero based. We take the value that is a DN and use it to create a javax.naming.ldap.LdapName
- From the group element, we get the last entry (another array we are dealing with here) which is the CN, and get just the value (what is after the equal sign in CN=).
- Add this to the ArrayList that is going to hold our new collection of CNs.
- Increment the loop counter.
- While we still have values in the groups collection, we go back to the open { curly brace and continue.
- Verify that user did belong to at least one group. If so, we return the new collection of just CNs. If not, we simply set the return value to null, what we got at the start.
The code in action
First, let's see what we get from the data source in the memberOf attribute:
The results are:
Now the code:
The results of the code execution:
This code provides one solution to the looping problem. In my next OGNL installment, we'll discuss an alternative approach.
Stay tuned for more about OGNL. In the meantime please leave a comment on this post and let me know what topics you would like to see. Follow me on Twitter: @jdasilvaPI
****************************************
OGNL Blog Series:
- Introduction to OGNL
- A simple OGNL expression
- Declaring variables in OGNL
- Method calls in OGNL
- Arrays in OGNL
- OGNL: What about those curly braces?
- Looping in OGNL
- Looping in OGNL take 2
- So what exactly is #this in OGNL?
- A continuing look at #this variable in OGNL
- Functions in OGNL
- Misc Topics in OGNL
John DaSilva develops training and solutions at Ping Identity.