This is part of a series of posts about OpenIAM. OpenIAM is an Identity Management platform that helps organisations manage the digital identities of its workforce and customers.
Check out the OpenIAM category for other posts.
Groovy scripts allow you to carry out complex logic, as well as add new functionality to OpenIAM. Though there are a lot of out of the box examples, these scripts should only really be considered as examples. Ideally you should write your own logic, but unfortunately this has a rather steep learning curve. In this post I'm going to show you the fundamentals of finding users within a groovy script in OpenIAM.
Before we get into any code, we need to understand a little bit of OpenIAM's internals.
OpenIAM consists of a number of microservices that are interconnected via a message bus (RabbitMQ). Each microservice use two queues, one to receive requests, and one to send responses. When we need to request something from another part of the stack, a message is placed into the receive queue for the microservice we need to interact with. The microservice watches the receive queue for messages and works through them First-in-First-out (FIFO). Once the microservice has generated a response, it posts the message onto the response queue. The requesting service can then pick up the response and carry on, as long as the timeout for the request has not yet passed.
Now we know how OpenIAM's microservice architecture works, we know how to request data within our groovy scripts. Let's dive a little deeper to understand exactly how we call other microservices from a groovy script. OpenIAM has been written on top of the Spring Framework which has an Inversion of Control (IoC) container. The IoC container is used for dependency injection. We can call upon the IoC container to get classes and services that have already been instantiated, which OpenIAM does at boot. Imagine you want to perform a database query, without IoC you would need to create a new instance of the database class and provide it with configuration such as hostname, username, password and database name each time you wanted to talk to the database. With IoC, the database class is created at boot or the first time it's needed and put into the IoC container. Instead of creating a new class each time, you get it from the IoC container where it has already been created and configured (instantiated). OpenIAM instantiates a large number of classes and services when it starts, this includes a number of RabbitMQ services such as the UserRabbitMQService
which we will be using to send and receive messages to the appropriate microservice. These RabbitMQServices are wrappers around the RabbitMQSender
class, each one helps setup the underlying communication and adds boilerplate such as the name of the microservice you are sending your request to as well as what types of responses you are expecting.
One final note; I'm using OpenIAM 4.2.1.3 EE, however, there is no reason why the scripts here will not work in other versions.
One of the most basic things you need to be able to do within a groovy script with OpenIAM is to be able to search for users. Using our knowledge above we can use the UserRabbitMQService
to search for a user.
There are a number of reasons why you might need to find a user, but most operations that center around a user within OpenIAM start by finding or getting the full details of a user. Within your managed system policy maps you only get a diff object, so it can sometimes be useful to fetch the full user.
One thing that is hard with OpenIAM is testing, to run the scripts within this article, we will be running the scripts we make from a batch task and outputting our results into the logs.
Let us start by searching for a single user. Though you can search by lots of different criteria, we will search by name in this example. We will search for the out-of-the-box user Scott Nelson
.
Log into the WebConsole and go to the Administration menu → Groovy Manager
Save the following code as a new script at /batch/findUserTest.groovy
import java.util.List
import org.apache.commons.logging.Log
import org.apache.commons.logging.LogFactory
import org.openiam.base.ws.MatchType
import org.openiam.base.ws.SearchParam
import org.openiam.common.beans.mq.UserRabbitMQService
import org.openiam.idm.searchbeans.UserSearchBean
import org.openiam.idm.srvc.user.dto.User
import org.openiam.idm.srvc.user.dto.UserCollection
import org.openiam.idm.srvc.user.dto.UserStatusEnum
import org.springframework.context.ApplicationContext
// Dependencies
Log log = LogFactory.getLog("findUserTest") // Logging
ApplicationContext appContext = (ApplicationContext) context // IoC Container
UserRabbitMQService userRabbitMQService = appContext.getBean(UserRabbitMQService.class) // Docs - https://download.openiam.com/release/enterprise/4.2.1.2/javadoc/org/openiam/common/beans/mq/UserRabbitMQService.html
log.info("Finding Scott Nelson")
UserSearchBean usb = new UserSearchBean() // Docs - https://download.openiam.com/release/enterprise/4.2.1.2/javadoc/org/openiam/idm/searchbeans/UserSearchBean.html
usb.addFirstNameMatchToken(new SearchParam("Scott", MatchType.EXACT)) // Add Scott as the first name to look for
usb.addLastNameMatchToken(new SearchParam("Nelson", MatchType.EXACT)) // Add Nelson as the last name to look for
log.info("Performing search")
// Build a list of attributes you want returned for the user - Docs - https://download.openiam.com/release/enterprise/4.2.1.2/javadoc/org/openiam/idm/srvc/user/dto/UserCollection.html
List<UserCollection> userCollection = [UserCollection.PRINCIPALS, UserCollection.ATTRIBUTES, UserCollection.ORGANIZATIONS, UserCollection.EMAILS, UserCollection.SUPERVISORS]
List<User> users = userRabbitMQService.findBeans(usb, userCollection as UserCollection[], 0, 1) // Search Bean, UserCollection[], from, size
log.info("Found: ${users?.size() ?: 0}")
if (users.size() > 0) {
User user = users.first() // Docs - https://download.openiam.com/release/enterprise/4.2.1.2/javadoc/org/openiam/idm/srvc/user/dto/User.html
log.info("${user.getId()} - First Name: ${user.getFirstName()}")
log.info("${user.getId()} - Last Name: ${user.getLastName()}")
log.info("${user}")
} else {
log.info("Could not find user")
}
The script above starts by getting some dependencies, it gets the UserRabbitMQService
from the Spring Framework IoC container.
UserRabbitMQService userRabbitMQService = appContext.getBean(UserRabbitMQService.class) // Docs - https://download.openiam.com/release/enterprise/4.2.1.2/javadoc/org/openiam/common/beans/mq/UserRabbitMQService.html
It then creates a UserSearchBean
, this allows us to specify the search criteria. Within the UserSearchBean
we have provided it with a first and last name to look for. As noted in the comments in the code, you can find the currently published docs for the UserSearchBean
class here.
UserSearchBean usb = new UserSearchBean() // Docs - https://download.openiam.com/release/enterprise/4.2.1.2/javadoc/org/openiam/idm/searchbeans/UserSearchBean.html
usb.addFirstNameMatchToken(new SearchParam("Scott", MatchType.EXACT)) // Add Scott as the first name to look for
usb.addLastNameMatchToken(new SearchParam("Nelson", MatchType.EXACT)) // Add Nelson as the last name to look for
Next we create a list containing one to more UserCollection
enums. This specifies what attributes we want returned with the user. The fewer UserCollection
enums provided, the quicker the results come back. The UserCollection
class docs can be found here.
List<UserCollection> userCollection = [UserCollection.PRINCIPALS, UserCollection.ATTRIBUTES, UserCollection.ORGANIZATIONS, UserCollection.EMAILS, UserCollection.SUPERVISORS]
Finally, we make a request to the UserRabbitMQService
and ask it to only return one result by setting the size to 1
. The UserRabbitMQService
class docs can be found here.
List<User> users = userRabbitMQService.findBeans(usb, userCollection as UserCollection[], 0, 1) // Search Bean, UserCollection[], from, size
Throughout the script you will find various log.info()
methods, these give us a simple way to output what is going on and can be very useful when debugging your code.
Let's test this script out using a batch task.
Now go to the Administration Menu → Batch Task
On the left-hand side click on New Batch Task
Create a new batch task with the following settings
Field | Value |
---|---|
Task Name | Find User Test |
Execution Time | Cron Job |
Cron Expression | 0 * * * * * |
Execution Script | Groovy Script |
Groovy Script URL | /batch/findUserTest.groovy |
Click Save
. OpenIAM will create the Batch Task and redirect you to the Edit Task page.
Underneath the Batch Task fields you will find some buttons including Execute
, you can click on this to run the script. Before clicking Execute, we need to make sure we are looking at the logs.
Depending on how you've deployed OpenIAM will depend on where you will find your logs. It's probably worth pointing out that these logs are different to what you'll find in the Log Viewer within the WebConsole UI. The logs that we are going to generate go into the operating systems logs.
For Kubernetes you will need to run the following kubectl logs
command:
kubectl logs -l release=test --all-containers --max-log-requests 34 -f
Replace test
with the same value set as APP_NAME
within your env.sh
file. If you are running lots of replicas, you might need to increase the --max-log-requests
if you get an error.
For Docker you can run the following command:
docker logs --tail=0 --follow
For RPM installs, you'll find your logs in the /usr/local/openiam/logs/
folder and can tail them with:
tail -f /usr/local/openiam/logs/*
Click the Execute
button.
Click Yes
to execute the batch task.
After a few seconds you should see the following in your logs (I've stripped the timestamp and a few bits from the output to make it easier to read).
Finding Scott Nelson
Performing search
Found: 1
3006 - First Name: Scott
3006 - Last Name: Nelson
User(super=AbstractMetadataTypeDTO(super=KeyNameDTO [name=null, id=3006], mdTypeId=DEFAULT_USER, metadataTypeName=null, mdGrouping=null), operation=NO_CHANGE, birthdate=null, companyOwnerId=null, createDate=Mon Dec 05 16:36:23 GMT 2022, createdBy=null, employeeId=null, employeeTypeId=null, firstName=Scott, jobCodeId=null, lastName=Nelson, lastUpdate=Mon Dec 05 16:38:29 GMT 2022, lastUpdatedBy=null, locationCd=null, locationName=null, classification=null, middleInit=null, prefix=null, sex=null, status=ACTIVE, secondaryStatus=null, positionStatus=null, previousPosition=null, suffix=null, title=null, userTypeInd=null, mailCode=null, costCenter=null, startDate=null, lastDate=null, claimDate=null, nickname=null, maidenName=null, passwordTheme=null, principalList=[Login(super=AbstractLogin(super=KeyDTO(super=BaseObject(objectState=NEW, requestorSessionID=null, requestorUserId=null, requestorLogin=null, requestClientIP=null, parentAuditLogId=null, testRequest=false), id=7), managedSysId=0, operation=NO_CHANGE, active=true, provStatus=null), login=snelson, lowerCaseLogin=snelson, userId=3006, password=60d151ee0b0760ad204df60cc533d40d00d3ceb324b3bdef573deca79be6c5c0, pwdChanged=null, pwdExp=null, firstTimeLogin=true, resetPasswordEnum=DEFAULT, resetPassword=false, locked=false, gracePeriod=null, createDate=Mon Dec 05 16:36:23 GMT 2022, createdBy=null, currentLoginHost=null, authFailCount=0, lastAuthAttempt=null, lastLogin=null, passwordChangeCount=0, lastLoginIP=null, prevLogin=null, prevLoginIP=null, pswdResetToken=null, pswdResetTokenExp=null, managedSysName=null, lastUpdate=Mon Dec 05 16:38:39 GMT 2022, smsCodeExpiration=null, otpActive=false, totpActive=false)], alternateContactId=null, alternativeStartDate=null, alternativeEndDate=null, certificationDelegateId=null, certificationDelegateStartDate=null, certificationDelegateEndDate=null, userOwnerId=null, datePasswordChanged=null, dateChallengeRespChanged=null, dateITPolicyApproved=null, roles=[], affiliations=[UserToOrganizationMembershipXref(super=MembershipXref(super=KeyNameDTO [name=null, id=null], startDate=null, endDate=null, entityId=100, memberEntityId=3006, rights=[AccessRight(super=KeyNameDTO [name=null, id=null], managedSysId=null, managedSysName=null)], operation=NO_CHANGE, description=null), organizationTypeId=null, organizationName=OpenIAM)], groups=[], resources=[], notifyUserViaEmail=true, relatedAccounts=null, primaryAccount=null, fromActivitiCreation=false, userSubTypeId=null, partnerName=null, prefixPartnerName=null, prefixLastName=null, displayNameFormat=null, accessRightIds=null, oauthCodes=null, accessRightStartDate=null, accessRightEndDate=null, visible=true, applicationIds=null, applicationNames=null)
The groovy script found the user Scott Nelson
and output details of the user to the logs including a print of the entire User
object.
Be careful with what you send to your logs. Using logging to output user information isn't a good idea as it may commit personal, identifiable or sensitive information such as passwords into storage. If you need to identify the user affected by an issue, use the getId()
method to output their OpenIAM ID only.
If you already know the internal ID of the user, you can fetch the user without having to search. This can be useful when working with a diff-object, such as those used in managed system policy maps. You can use the ID to get the full user. From the previous code, we know that Scott Nelson
has an ID of 3006
, we can use this to fetch the user.
import java.util.List
import org.apache.commons.logging.Log
import org.apache.commons.logging.LogFactory
import org.openiam.common.beans.mq.UserRabbitMQService
import org.openiam.idm.srvc.user.dto.User
import org.openiam.idm.srvc.user.dto.UserCollection
import org.springframework.context.ApplicationContext
// Dependencies
Log log = LogFactory.getLog("findUserTest") // Logging
ApplicationContext appContext = (ApplicationContext) context // IoC Container
UserRabbitMQService userRabbitMQService = appContext.getBean(UserRabbitMQService.class) // Docs - https://download.openiam.com/release/enterprise/4.2.1.2/javadoc/org/openiam/common/beans/mq/UserRabbitMQService.html
log.info("Finding Scott Nelson by ID")
log.info("Performing search")
// Build a list of attributes you want returned for the user - Docs - https://download.openiam.com/release/enterprise/4.2.1.2/javadoc/org/openiam/idm/srvc/user/dto/UserCollection.html
List<UserCollection> userCollection = [UserCollection.PRINCIPALS, UserCollection.ATTRIBUTES, UserCollection.ORGANIZATIONS, UserCollection.EMAILS, UserCollection.SUPERVISORS]
User user = userRabbitMQService.getUser("3006", userCollection as UserCollection[])
if (user) {
log.info("${user.getId()} - First Name: ${user.getFirstName()}")
log.info("${user.getId()} - Last Name: ${user.getLastName()}")
log.info("${user}")
} else {
log.info("Could not find user")
}
output = 0
As we already know the ID of the OpenIAM user we can significantly slim down the code required to get the user. The UserSearchBean
can be removed and we use a different method from UserRabbitMQService
.
User user = userRabbitMQService.getUser("3006", userCollection as UserCollection[])
This gets the user and outputs the following.
Finding Scott Nelson by ID
Performing search
3006 - First Name: Scott
3006 - Last Name: Nelson
User(super=AbstractMetadataTypeDTO(super=KeyNameDTO [name=null, id=3006], mdTypeId=DEFAULT_USER, metadataTypeName=null, mdGrouping=null), operation=NO_CHANGE, birthdate=null, companyOwnerId=null, createDate=Mon Dec 05 16:36:23 GMT 2022, createdBy=null, employeeId=null, employeeTypeId=null, firstName=Scott, jobCodeId=null, lastName=Nelson, lastUpdate=Mon Dec 05 16:38:29 GMT 2022, lastUpdatedBy=null, locationCd=null, locationName=null, classification=null, middleInit=null, prefix=null, sex=null, status=ACTIVE, secondaryStatus=null, positionStatus=null, previousPosition=null, suffix=null, title=null, userTypeInd=null, mailCode=null, costCenter=null, startDate=null, lastDate=null, claimDate=null, nickname=null, maidenName=null, passwordTheme=null, principalList=[Login(super=AbstractLogin(super=KeyDTO(super=BaseObject(objectState=NEW, requestorSessionID=null, requestorUserId=null, requestorLogin=null, requestClientIP=null, parentAuditLogId=null, testRequest=false), id=7), managedSysId=0, operation=NO_CHANGE, active=true, provStatus=null), login=snelson, lowerCaseLogin=snelson, userId=3006, password=60d151ee0b0760ad204df60cc533d40d00d3ceb324b3bdef573deca79be6c5c0, pwdChanged=null, pwdExp=null, firstTimeLogin=true, resetPasswordEnum=DEFAULT, resetPassword=false, locked=false, gracePeriod=null, createDate=Mon Dec 05 16:36:23 GMT 2022, createdBy=null, currentLoginHost=null, authFailCount=0, lastAuthAttempt=null, lastLogin=null, passwordChangeCount=0, lastLoginIP=null, prevLogin=null, prevLoginIP=null, pswdResetToken=null, pswdResetTokenExp=null, managedSysName=null, lastUpdate=Mon Dec 05 16:38:39 GMT 2022, smsCodeExpiration=null, otpActive=false, totpActive=false)], alternateContactId=null, alternativeStartDate=null, alternativeEndDate=null, certificationDelegateId=null, certificationDelegateStartDate=null, certificationDelegateEndDate=null, userOwnerId=null, datePasswordChanged=null, dateChallengeRespChanged=null, dateITPolicyApproved=null, roles=[], affiliations=[UserToOrganizationMembershipXref(super=MembershipXref(super=KeyNameDTO [name=null, id=null], startDate=null, endDate=null, entityId=100, memberEntityId=3006, rights=[AccessRight(super=KeyNameDTO [name=null, id=null], managedSysId=null, managedSysName=null)], operation=NO_CHANGE, description=null), organizationTypeId=null, organizationName=OpenIAM)], groups=[], resources=[], notifyUserViaEmail=true, relatedAccounts=null, primaryAccount=null, fromActivitiCreation=false, userSubTypeId=null, partnerName=null, prefixPartnerName=null, prefixLastName=null, displayNameFormat=null, accessRightIds=null, oauthCodes=null, accessRightStartDate=null, accessRightEndDate=null, visible=true, applicationIds=null, applicationNames=null)
In the previous example, we assumed that there was only a single user with the name Scott Nelson
, but what do we do if there are several people with that name? We can add an iterator, or loop to go over multiple users. For this example I've added a new user to OpenIAM also called Scott Nelson
. I've set both job title and primary email on both users so we can clearly see which one is which.
I'm going to assume you've been following along so won't cover how to run the batch task or view the output, both of which are covered above.
Go back to the Groovy Manager and update the /batch/findUserTest.groovy
script with the following.
import java.util.List
import org.apache.commons.logging.Log
import org.apache.commons.logging.LogFactory
import org.openiam.base.ws.MatchType
import org.openiam.base.ws.SearchParam
import org.openiam.common.beans.mq.UserRabbitMQService
import org.openiam.idm.searchbeans.UserSearchBean
import org.openiam.idm.srvc.user.dto.User
import org.openiam.idm.srvc.user.dto.UserCollection
import org.openiam.idm.srvc.user.dto.UserStatusEnum
import org.springframework.context.ApplicationContext
// Dependencies
Log log = LogFactory.getLog("findUserTest") // Logging
ApplicationContext appContext = (ApplicationContext) context // IoC Container
UserRabbitMQService userRabbitMQService = appContext.getBean(UserRabbitMQService.class) // Docs - https://download.openiam.com/release/enterprise/4.2.1.2/javadoc/org/openiam/common/beans/mq/UserRabbitMQService.html
log.info("Finding Scott Nelson")
UserSearchBean usb = new UserSearchBean() // Docs - https://download.openiam.com/release/enterprise/4.2.1.2/javadoc/org/openiam/idm/searchbeans/UserSearchBean.html
usb.addFirstNameMatchToken(new SearchParam("Scott", MatchType.EXACT)) // Add Scott as the first name to look for
usb.addLastNameMatchToken(new SearchParam("Nelson", MatchType.EXACT)) // Add Nelson as the last name to look for
log.info("Performing search")
// Build a list of attributes you want returned for the user - Docs - https://download.openiam.com/release/enterprise/4.2.1.2/javadoc/org/openiam/idm/srvc/user/dto/UserCollection.html
List<UserCollection> userCollection = [UserCollection.PRINCIPALS, UserCollection.ATTRIBUTES, UserCollection.ORGANIZATIONS, UserCollection.EMAILS, UserCollection.SUPERVISORS]
List<User> users = userRabbitMQService.findBeans(usb, userCollection as UserCollection[], 0, 50) // Search Bean, UserCollection[], from, size
log.info("Found: ${users?.size() ?: 0}")
if (users.size() > 0) {
for (User user : users) {
log.info("${user.getId()} - First Name: ${user.getFirstName()}")
log.info("${user.getId()} - Last Name: ${user.getLastName()}")
log.info("${user.getId()} - Job Title: ${user.getTitle()}")
log.info("${user.getId()} - Email Address: ${user.emailAddresses?.find({ a -> a.mdTypeId = 'PRIMARY_EMAIL' }).emailAddress ?: 'Not found'}")
log.info("${user}")
}
} else {
log.info("Could not find user")
}
There are a couple of differences between this and the previous version of the script. I've changed the size parameter for the userRabbitMQService.findBeans()
method from 1
to 50
as this sets the maximum number of results you want back. If you need to get lots of users you should look at paginating rather than setting the size too high as you may run into resource issues or issues with RabbitMQ.
List<User> users = userRabbitMQService.findBeans(usb, userCollection as UserCollection[], 0, 50) // Search Bean, UserCollection[], from, size
I've also added an iterator, as well as some additional log lines to output the user's job title and primary email address.
for (User user : users) {
log.info("${user.getId()} - First Name: ${user.getFirstName()}")
log.info("${user.getId()} - Last Name: ${user.getLastName()}")
log.info("${user.getId()} - Job Title: ${user.getTitle()}")
log.info("${user.getId()} - Email Address: ${user.emailAddresses?.find({ a -> a.mdTypeId = 'PRIMARY_EMAIL' }).emailAddress ?: 'Not found'}")
log.info("${user}")
}
Run the batch task again, this time the output shows:
Finding Scott Nelson
Performing search
Found: 2
8a67dccb851566a40185158774c401e2 - First Name: Scott
8a67dccb851566a40185158774c401e2 - Last Name: Nelson
8a67dccb851566a40185158774c401e2 - Job Title: Superhero
8a67dccb851566a40185158774c401e2 - Email Address: scott-the-superhero@example.com
User(super=AbstractMetadataTypeDTO(super=KeyNameDTO [name=null, id=8a67dccb851566a40185158774c401e2], mdTypeId=DEFAULT_USER, metadataTypeName=null, mdGrouping=null), operation=NO_CHANGE, birthdate=null, companyOwnerId=null, createDate=Thu Dec 15 11:24:36 GMT 2022, createdBy=3000, employeeId=null, employeeTypeId=null, firstName=Scott, jobCodeId=null, lastName=Nelson, lastUpdate=Thu Dec 15 11:26:16 GMT 2022, lastUpdatedBy=null, locationCd=null, locationName=null, classification=null, middleInit=null, prefix=null, sex=null, status=ACTIVE, secondaryStatus=null, positionStatus=null, previousPosition=null, suffix=null, title=Superhero, userTypeInd=null, mailCode=null, costCenter=null, startDate=null, lastDate=null, claimDate=null, nickname=null, maidenName=null, passwordTheme=null, principalList=[Login(super=AbstractLogin(super=KeyDTO(super=BaseObject(objectState=NEW, requestorSessionID=null, requestorUserId=null, requestorLogin=null, requestClientIP=null, parentAuditLogId=null, testRequest=false), id=8a67dccb851566a40185158774c501e3), managedSysId=0, operation=NO_CHANGE, active=true, provStatus=SAVED), login=Scott.Nelson, lowerCaseLogin=scott.nelson, userId=8a67dccb851566a40185158774c401e2, password=13d9758b9b18ebe2f10aa8b2f3487c6a6483c1bd3c914690e4fe4f1322a1b6df, pwdChanged=null, pwdExp=Wed Mar 15 11:24:36 GMT 2023, firstTimeLogin=false, resetPasswordEnum=DEFAULT, resetPassword=false, locked=false, gracePeriod=Fri Dec 16 11:24:36 GMT 2022, createDate=Thu Dec 15 11:24:36 GMT 2022, createdBy=null, currentLoginHost=null, authFailCount=0, lastAuthAttempt=null, lastLogin=null, passwordChangeCount=0, lastLoginIP=null, prevLogin=null, prevLoginIP=null, pswdResetToken=null, pswdResetTokenExp=null, managedSysName=null, lastUpdate=Thu Dec 15 11:24:36 GMT 2022, smsCodeExpiration=null, otpActive=false, totpActive=false)], alternateContactId=null, alternativeStartDate=null, alternativeEndDate=null, certificationDelegateId=null, certificationDelegateStartDate=null, certificationDelegateEndDate=null, userOwnerId=null, datePasswordChanged=null, dateChallengeRespChanged=null, dateITPolicyApproved=null, roles=[], affiliations=[], groups=[], resources=[], notifyUserViaEmail=true, relatedAccounts=null, primaryAccount=null, fromActivitiCreation=false, userSubTypeId=null, partnerName=null, prefixPartnerName=null, prefixLastName=null, displayNameFormat=null, accessRightIds=null, oauthCodes=null, accessRightStartDate=null, accessRightEndDate=null, visible=true, applicationIds=null, applicationNames=null)
3006 - First Name: Scott
3006 - Last Name: Nelson
3006 - Job Title: Office Worker
3006 - Email Address: scott-the-office-worker@example.com
User(super=AbstractMetadataTypeDTO(super=KeyNameDTO [name=null, id=3006], mdTypeId=DEFAULT_USER, metadataTypeName=null, mdGrouping=null), operation=NO_CHANGE, birthdate=null, companyOwnerId=null, createDate=Thu Dec 15 10:45:57 GMT 2022, createdBy=NA, employeeId=null, employeeTypeId=null, firstName=Scott, jobCodeId=null, lastName=Nelson, lastUpdate=Thu Dec 15 11:27:21 GMT 2022, lastUpdatedBy=null, locationCd=null, locationName=null, classification=null, middleInit=null, prefix=null, sex=null, status=ACTIVE, secondaryStatus=null, positionStatus=null, previousPosition=null, suffix=null, title=Office Worker, userTypeInd=null, mailCode=null, costCenter=null, startDate=null, lastDate=null, claimDate=null, nickname=null, maidenName=null, passwordTheme=null, principalList=[Login(super=AbstractLogin(super=KeyDTO(super=BaseObject(objectState=NEW, requestorSessionID=null, requestorUserId=null, requestorLogin=null, requestClientIP=null, parentAuditLogId=null, testRequest=false), id=7), managedSysId=0, operation=NO_CHANGE, active=true, provStatus=null), login=snelson, lowerCaseLogin=snelson, userId=3006, password=9a129b07b74b694be62ecb74df148e2f7d6085e1ff6271247dbdc39ee03429a9, pwdChanged=null, pwdExp=null, firstTimeLogin=true, resetPasswordEnum=DEFAULT, resetPassword=false, locked=false, gracePeriod=null, createDate=Thu Dec 15 10:45:57 GMT 2022, createdBy=null, currentLoginHost=null, authFailCount=0, lastAuthAttempt=null, lastLogin=null, passwordChangeCount=0, lastLoginIP=null, prevLogin=null, prevLoginIP=null, pswdResetToken=null, pswdResetTokenExp=null, managedSysName=null, lastUpdate=Thu Dec 15 10:49:15 GMT 2022, smsCodeExpiration=null, otpActive=false, totpActive=false)], alternateContactId=null, alternativeStartDate=null, alternativeEndDate=null, certificationDelegateId=null, certificationDelegateStartDate=null, certificationDelegateEndDate=null, userOwnerId=null, datePasswordChanged=null, dateChallengeRespChanged=null, dateITPolicyApproved=null, roles=[], affiliations=[UserToOrganizationMembershipXref(super=MembershipXref(super=KeyNameDTO [name=null, id=null], startDate=null, endDate=null, entityId=100, memberEntityId=3006, rights=[AccessRight(super=KeyNameDTO [name=null, id=null], managedSysId=null, managedSysName=null)], operation=NO_CHANGE, description=null), organizationTypeId=null, organizationName=OpenIAM)], groups=[], resources=[], notifyUserViaEmail=true, relatedAccounts=null, primaryAccount=null, fromActivitiCreation=false, userSubTypeId=null, partnerName=null, prefixPartnerName=null, prefixLastName=null, displayNameFormat=null, accessRightIds=null, oauthCodes=null, accessRightStartDate=null, accessRightEndDate=null, visible=true, applicationIds=null, applicationNames=null)
From the previous examples, we have only searched by First
and Last
names. This isn't the most useful of search criteria but is a good example of how to find people. The code we've used for these search criteria performs an exact match but this can be changed to a number of different MatchType
such as BETWEEN
, CONTAINS
, STARTS_WITH
, ENDS_WITH
, EXACT
, the full list can be found within the docs here.
The UserSearchBean
class has a number of different methods which configure the search criteria, here are some examples which can all be combined.
Search for a user by first name
usb.addFirstNameMatchToken(new SearchParam("Scott", MatchType.EXACT))
usb.addFirstNameMatchToken(new SearchParam("Sc", MatchType.STARTS_WITH))
Search for a user by last name
usb.addLastNameMatchToken(new SearchParam("Nelson", MatchType.EXACT))
usb.addLastNameMatchToken(new SearchParam("Nel", MatchType.STARTS_WITH))
Search for a user by maiden name
usb.addMaidenNameMatchToken(new SearchParam("Nelson", MatchType.EXACT))
usb.addMaidenNameMatchToken(new SearchParam("Nel", MatchType.STARTS_WITH))
Search for a user by their employee ID.
usb.setEmployeeIdMatchTokens(new SearchParam("ID_HERE", MatchType.EXACT))
usb.setEmployeeIdMatchTokens(new SearchParam("ID_HE", MatchType.STARTS_WITH))
Search for a user by their employee type
usb.setEmployeeType("Student")
Search for a user by their Job Title.
usb.setTitle("Exact Job Title Here")
When importing or creating users, set the userTypeInd
field to the name of the metadatatype you set the user as. This is easier to search than metadatatype as it is stored as a string.
usb.setUserType("Student")
Search for a user based on their account status. Statuses are defined within UserStatusEnum
such as ACTIVE
, DISABLED
, INACTIVE
, LOCKED
, PENDING_INITIAL_LOGIN
, PENDING_START_DATE
, TERMINATED
, a full list can be found on the docs here.
Searching for users based on User Status could result in a large number of results. Consider using pagination to get the results.
usb.setUserStatus(UserStatusEnum.ACTIVE.getValue())
usb.setUserStatus(UserStatusEnum.DISABLED.getValue())
usb.setUserStatus(UserStatusEnum.INACTIVE.getValue())
usb.setUserStatus(UserStatusEnum.PENDING_INITIAL_LOGIN.getValue())
usb.setUserStatus(UserStatusEnum.PENDING_START_DATE.getValue())
usb.setUserStatus(UserStatusEnum.TERMINATED.getValue())
Search for a user based on their Start Date.
The setStartDateToken()
method within UserSearchBean
currently does not work. We will need to substitute the UserRabbitMQService
with another service.
import org.openiam.srvc.user.UserDataWebService
...
UserDataWebService userDataWebService = appContext.getBean("userWS") as UserDataWebService
...
// Date to Search from (Today)
Calendar from = Calendar.getInstance()
from.set(Calendar.HOUR_OF_DAY, 0)
from.set(Calendar.MINUTE, 0)
from.set(Calendar.SECOND, 0)
from.set(Calendar.MILLISECOND, 000)
// Date to search until (3 Days from now)
Calendar to = Calendar.getInstance()
to.add(Calendar.DAY_OF_MONTH, 3) // Add 3 days to today's date
to.set(Calendar.HOUR_OF_DAY, 23)
to.set(Calendar.MINUTE, 59)
to.set(Calendar.SECOND, 59)
to.set(Calendar.MILLISECOND, 999)
...
List<User> users = userDataWebService.getUserBetweenStartDate(from.getTime(), to.getTime())
This method of retrieving users cannot be combined with other search criteria and cannot be paginated. You also cannot specify which properties/attributes you want to return. If further criteria is required, you will need to filter the results with code.
Search for a user based on their Last (End) Date.
The setLastDateToken()
method within UserSearchBean
currently does not work. We will need to substitute the UserRabbitMQService
with another service.
import org.openiam.srvc.user.UserDataWebService
...
UserDataWebService userDataWebService = appContext.getBean("userWS") as UserDataWebService
...
// Date to Search from (Today)
Calendar from = Calendar.getInstance()
from.set(Calendar.HOUR_OF_DAY, 0)
from.set(Calendar.MINUTE, 0)
from.set(Calendar.SECOND, 0)
from.set(Calendar.MILLISECOND, 000)
// Date to search until (3 Days from now)
Calendar to = Calendar.getInstance()
to.add(Calendar.DAY_OF_MONTH, 3) // Add 3 days to today's date
to.set(Calendar.HOUR_OF_DAY, 23)
to.set(Calendar.MINUTE, 59)
to.set(Calendar.SECOND, 59)
to.set(Calendar.MILLISECOND, 999)
...
List<User> users = userDataWebService.getUserBetweenLastDate(from.getTime(), to.getTime())
This method of retrieving users cannot be combined with other search criteria and cannot be paginated. You also cannot specify which properties/attributes you want to return. If further criteria is required, you will need to filter the results with code.
In the examples above, we've only been expecting a small number of results. If you have the potential of getting large numbers of results, you should paginate your requests to UserRabbitMQService
. Asking for too many results in one go can cause resource issues and potentially issues with RabbitMQ due to the size of data being stored such as hitting the storage limits of a single message or exceeding the response timeout limit.
Go to Groovy Manager and replace the script from above with the following
import java.util.List
import org.apache.commons.logging.Log
import org.apache.commons.logging.LogFactory
import org.openiam.base.request.BaseSearchServiceRequest
import org.openiam.base.request.BaseServiceRequest
import org.openiam.base.response.data.IntResponse
import org.openiam.base.response.data.BaseDataResponse
import org.openiam.base.ws.MatchType
import org.openiam.base.ws.SearchParam
import org.openiam.common.beans.mq.RabbitMQSender
import org.openiam.common.beans.mq.UserRabbitMQService
import org.openiam.idm.searchbeans.UserSearchBean
import org.openiam.idm.srvc.user.dto.User
import org.openiam.idm.srvc.user.dto.UserCollection
import org.openiam.idm.srvc.user.dto.UserStatusEnum
import org.openiam.mq.constants.api.OpenIAMAPI
import org.openiam.mq.constants.api.user.UserServiceAPI
import org.openiam.mq.constants.queue.user.UserServiceQueue
import org.springframework.context.ApplicationContext
// Dependencies
Log log = LogFactory.getLog("findUserTest") // Logging
ApplicationContext appContext = (ApplicationContext) context // IoC Container
UserRabbitMQService userRabbitMQService = appContext.getBean(UserRabbitMQService.class) // Docs - https://download.openiam.com/release/enterprise/4.2.1.2/javadoc/org/openiam/common/beans/mq/UserRabbitMQService.html
RabbitMQSender rabbitMQSender = appContext.getBean(RabbitMQSender.class) // Underlying RabbitMQSender
UserServiceQueue queue = appContext.getBean(UserServiceQueue.class) // Queue to use
log.info("Finding All Users")
UserSearchBean usb = new UserSearchBean() // Docs - https://download.openiam.com/release/enterprise/4.2.1.2/javadoc/org/openiam/idm/searchbeans/UserSearchBean.html
// Build a list of attributes you want returned for the user - Docs - https://download.openiam.com/release/enterprise/4.2.1.2/javadoc/org/openiam/idm/srvc/user/dto/UserCollection.html
List<UserCollection> userCollection = [UserCollection.PRINCIPALS, UserCollection.ATTRIBUTES, UserCollection.ORGANIZATIONS, UserCollection.EMAILS, UserCollection.SUPERVISORS]
log.info("Getting total results from RabbitMQSender")
Integer totalCount = 0
BaseDataResponse response = (BaseDataResponse) rabbitMQSender.sendAndReceive((UserServiceQueue) queue, (OpenIAMAPI) UserServiceAPI.Count, new BaseSearchServiceRequest(usb), IntResponse.class)
if (response.isFailure()) {
log.info(String.format("Can't getValue: code %s, errorText %s", response.getErrorCode().name(), response.getErrorText()));
} else {
totalCount = (Integer) response.getValue()
}
log.info("There are ${totalCount} results")
int total, from = 0
int size = 5 // Number of results to get each time
List<User> users = new ArrayList<>() // Empty list to hold users
while (total < totalCount) {
List<User> results = userRabbitMQService.findBeans(usb, userCollection as UserCollection[], from, size) // Get page
if(results.size() > 0) {
total += results.size()
from += size
users.addAll(results) // Add results to users array
log.info("${total}/${totalCount} users retrieved")
} else {
log.info("Done")
break
}
}
log.info("There are ${users.size()} users")
for (User user : users) {
log.info(user.getId())
}
output = 0
There are a number of new imports required for this code, each of these new imports are required to get the total number of results based on your search criteria.
import org.openiam.base.request.BaseServiceRequest
import org.openiam.base.response.data.BaseDataResponse
import org.openiam.base.response.data.IntResponse
import org.openiam.common.beans.mq.RabbitMQSender
import org.openiam.mq.constants.api.OpenIAMAPI
import org.openiam.mq.constants.queue.user.UserServiceQueue
We also need to get a couple of new classes from the IoC container. RabbitMQSender
is the underlying class that the UserRabbitMQService
class uses to make requests. UserServiceQueue
is where to put the requests in RabbitMQ.
RabbitMQSender rabbitMQSender = appContext.getBean(RabbitMQSender.class) // Underlying RabbitMQSender
UserServiceQueue queue = appContext.getBean(UserServiceQueue.class) // Queue to use
As there are only a small number of users out of the box, I've removed the search criteria so this script gets all users.
The first step to paginate the results is to get a total count based on your search criteria.
log.info("Getting total results from RabbitMQSender")
Integer totalCount = 0
BaseDataResponse response = (BaseDataResponse) rabbitMQSender.sendAndReceive((UserServiceQueue) queue, (OpenIAMAPI) UserServiceAPI.Count, new BaseSearchServiceRequest(usb), IntResponse.class)
if (response.isFailure()) {
log.info(String.format("Can't getValue: code %s, errorText %s", response.getErrorCode().name(), response.getErrorText()));
} else {
totalCount = (Integer) response.getValue()
}
log.info("There are ${totalCount} results")
Now we have a total number of results to fetch, we perform a while loop to get batches of users until there are no more results. I've set the size
variable to 5
as this test system only has a small number of users. I would set this to a maximum of around 500
.
int total, from = 0
int size = 5 // Number of results to get each time
List<User> users = new ArrayList<>() // Empty list to hold users
while (total < totalCount) {
List<User> results = userRabbitMQService.findBeans(usb, userCollection as UserCollection[], from, size) // Get page
if(results.size() > 0) {
total += results.size()
from += size
users.addAll(results) // Add results to users array
log.info("${total}/${totalCount} users retrieved")
} else {
log.info("Done")
break
}
}
log.info("There are ${users.size()} users")
The last part of the code loops through the users list and puts their ID into the logs resulting in the following output.
Finding All Users
Getting total results from RabbitMQSender
There are 9 results
5/9 users retrieved
9/9 users retrieved
There are 9 users
3009
3008
3007
0001
3001
3010
3000
8a67dccb851566a40185158774c401e2
3006
Though the code I've shared isn't exhaustive, it should give you a good starting point when you need to find users matching certain criteria within your groovy scripts. In a future post I'll share how to make changes to users which is another fundamental task of an IDM system.
Need help with OpenIAM? Make sure you join the official OpenIAM Community where you can ask for help from other community members.