Avoiding Hardcoded Values in OpenIAM Groovy Scripts

OPENIAM
Published: January 20, 2023

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.

In this post, I'm going to show you how to make use of a lesser known feature of OpenIAM called the PropertyValueCache. The PropertyValueCache can be used to implement some best practices by avoiding hardcoding values that you need to reuse throughout your groovy scripts.

OpenIAM offers a lot of flexibility when it comes to implementing logic or new functionality. Since 4.2.1.0 it's included a Business Rules engine which offers a UI driven low/no-code way to perform basic tasks. If you need something more complex you'll need to write a groovy script. Groovy scripts allow you to extend OpenIAM's functionality and express complex logic in a Java like language. OpenIAM ships with an extensive number of Groovy scripts, however, these scripts should only be considered basic examples.

While working on my implementation of OpenIAM, I found myself hard coding the same values over and over across lots of groovy scripts. An example, we have different types of users, Staff, Students and External. If we decide that the word External doesn't accurately describe that type of user and we want to rename it, I would need to go through all of our groovy scripts and find where I've hard coded this value and replace it. That is far from ideal and not very smart. I spent some time trying to come up with a better solution than hard coding what are effectively configuration settings.

The first option would be to store the value as a variable, allowing you to store the value once and reuse it. However, with this approach you cannot access this value from other scripts and would still need to go through all your scripts replacing that value if you need to change it.

Next you could store your shared variables within a single file and import or include it in your scripts. Unfortunately, this doesn't really work in groovy, especially if your scripts are across different directories.

After exploring the code base and database I came up with a good solution to this, the PropertyValueCache.

What Is The PropertyValueCache?

The PropertyValueCache or property_file_values table in the database drives the System Configuration of OpenIAM. Adding new entries here make the value available to all of your groovy scripts. Even better, after adding some custom language definitions, these are editable fields within the System Configuration UI. This mechanism can be used as a single source of truth, making values you need to reuse available everywhere.

Before we begin, you will need admin access to an OpenIAM instance, as well as the database.

Let's try it out, we are going to create an entry within the property_file_values table, then we will create a groovy script and attach it to a batch task that outputs the database entry to the logs using the PropertyValueCache. Though this isn't exactly a real world example, it should give you enough to make use of this in your own scripts.

Database

Let's create the shared value that we want to retrieve from the database. OpenIAM supports a number of different databases. Connect to your database using your preferred tool. My instances uses PostgreSQL and PGAdmin4.

property_file_values table in PGAdmin4

The property_file_values table is made up of the following columns:

NameTypeDescription
property_idvarchar(200)The property name or location used to retreive the value. This is set using Reverse Domain Name Notation
property_valuevarchar(400)The value that will be returned
property_typevarchar(30)The type of value that is being stored. This is used by the frontend to know what type of input box to display. Just some of the types this can be: Role, Resource, Group, Integer, OrderableValue, ChallengeResponseGroup, OrganizationType, User, OrganizationTypeHierarchy, Widget, String, Secret, ManagedSystem, RegularExpression, Boolean, Select, Organization and MultiSelect
is_read_onlychar(1)Y \ N - Whether the field should be editable within the UI
is_multilangualchar(1)Y \ N - I've not used this but I guess it allows you to specify whether a different value should be available for different languages
is_empty_value_allowedchar(1)Y \ N - Whether the entry is required or not.
categoryvarchar(100)The name of the category to display the field under within the UI

As you can see, there are a number of options associated with each entry in the property_file_values table. The more interesting field is the property_type, which can make it really easy to integrate your value into the System Settings section of the webconsole. For example, if you need to provide one of your scripts the ID of a group, you can set the type to Group and the UI will provide you with a typeahead box that will allow you to pick a group. Once saved the id from the group will be stored as the value.

For the sake of this demo, we will just be using a string. Let's create a new entry in this table and name it using Reverse Domain Name Notation.

insert into property_file_values 
("property_id", "property_value", "property_type", "is_read_only", "is_multilangual", "is_empty_value_allowed", "category") 
values ('uk.neilherbert.openiam.name', 'Neil Herbert', 'String', 'N', 'N', 'N', 'System');

Running this query will insert a new record with the id uk.neilherbert.openiam.name and the value Neil Herbert. You should be able to see this new value within the System tab under System Configuration. You'll notice that it does not have a label.

OpenIAM System Configuration

Labels are not driven by the database but the language files. To create a label for this, you will need to update your language files such as the messages_en.properties file:

messages_en.properties
uk.neilherbert.openiam.name.name=Label Here
uk.neilherbert.openiam.name.title=Label Here

For each property you add to the PropertyValueCache, you need to add both a .name and .title entry within your language files.


OpenIAM System Configuration

Groovy Script

Now we have a custom entry in the PropertyValueCache, let me show you how to retrieve it.

Log into the WebConsole and go to the Administration menu → Groovy Manager

OpenIAM Administration Menu → Groovy Manager

Save the following code as a new script at /batch/propertValueCacheExample.groovy

/batch/propertValueCacheExample.groovy
import org.apache.commons.logging.Log
import org.apache.commons.logging.LogFactory
import org.openiam.idm.srvc.property.service.PropertyValueCache
import org.springframework.context.ApplicationContext

// Dependencies
Log log = LogFactory.getLog("propertValueExample") // Logging
ApplicationContext appContext = (ApplicationContext) context // IoC Container
PropertyValueCache propertyValueCache = appContext.getBean(PropertyValueCache.class) // Global Settings

log.info("Getting Value from PropertyValueCache")
String value = propertyValueCache.getString("uk.neilherbert.openiam.name") ?: "Not Found"
log.info("${value}")

Before diving into this code, we will be using logging to output the value of the PropertyValueCache. Checkout my post Adding Logging to OpenIAM Groovy Scripts to learn about logging within OpenIAM groovy scripts.

Though the script I've shared is really short, it shows you how easy it is to use the PropertyValueCache.

At the top of the script we import all the classes we need to use to make this script work. At a minimum you need to include the Spring Framework ApplicationContext class and the PropertyValueCache itself. The other imports I've included are related to logging.

import org.openiam.idm.srvc.property.service.PropertyValueCache
import org.springframework.context.ApplicationContext

After the imports we need to get an instance of the PropertyValueCache from the spring framework IOC or Application Context. OpenIAM is written using the Spring Framework so extends much of the frameworks functionality. The Application Context container is an Inversion of Control (IOC) container. It handles dependency injection. It allows dependencies to be instantiated once and it's state shared across the application. This means you no longer have to create new instances of shared classes and provide them with configuration. Imagine having to create a new database class and configure it's connection each time you needed to make a query. IOC allows it to be created in one part of your application, usually some form of startup or boot method, and make that instance of it available everywhere else. A lot of the OIAM functionality is setup using IOC such as the PropertyValueCache. To use it, we simply ask the Application Context to get it for us.

ApplicationContext appContext = (ApplicationContext) context // IoC Container
PropertyValueCache propertyValueCache = appContext.getBean(PropertyValueCache.class) // Global Settings

Once we have an instance of the PropertyValueCache, we can simply ask it to get us the value from a property_id.

String value = propertyValueCache.getString("uk.neilherbert.openiam.name") ?: "Not Found"

The PropertyValueCache class has a number of methods that can be called including getBoolean(string), getInt(string) and getString(string) which will return the value as the appropriate type.

Batch Task

Now that we have both the database entry and groovy script inplace, lets see the PropertyValueCache in action by running our groovy script as a batch task.

Go to the Administration Menu → Batch Task

OpenIAM Administration Menu → Batch Task

On the left-hand side click on New Batch Task

OpenIAM Batch Tasks

Create a new batch task with the following settings

FieldValue
Task NameProperty Value Cache Example
Execution TimeCron Job
Cron Expression0 * * * * *
Execution ScriptGroovy Script
Groovy Script URL/batch/propertValueCacheExample.groovy
OpenIAM Create New Batch Task

Click Save. OpenIAM will create the Batch Task and redirect you to the Edit Task page.

OpenIAM Edit Batch Task

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:

Kubernetes
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
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:

RPM
tail -f /usr/local/openiam/logs/*

Click the Execute button.

OpenIAM Execute Batch Task Confirmation Modal

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).

Output
Getting Value from PropertyValueCache
Neil Herbert

Summary

Though I'm pretty sure the PropertyValueCache wasn't intended for this use or at least by end-users, however, it makes it extremely easy to store values that would need to be hardcoded across lots of files within a centralised location. Even better, these values become configuration items and are editable by your sysadmin from the UI. Simply put a new row into the property_file_values table and use the property_id to retrieve it from any groovy script!

Need help with OpenIAM? Make sure you join the official OpenIAM Community where you can ask for help from other community members.