Introducing the Rule Development Kit

You have been asking for a way to write and test cloud rules locally before handing them off to Professional Services for validation and installation. We are incredibly excited to announce a new tool available to the SailPoint Developer Community: The Rule Development Kit.

Utilizing the Rule Development Kit will save you both time and money when it comes to submitting rules for review, by ensuring that your rule logic is sound—and so much more!

We will be doing a live stream today, at 1PM Central Time, to give an introduction and overview of the Rule Development Kit and show you how to get started (and don’t worry, the recording will be available right at this same link!):

The Rule Development Kit

Overview

The SailPoint Rule Development Kit (RDK) is a project you can use to develop rules quickly and easily. The RDK provides you with the classes and methods available to you when you’re developing a rule. You can mock out these classes and run your rule locally to test your logic before submitting it for review. The RDK ships with a few example rules to demonstrate its functionality and to help you get started.

Refer to the following guide to get started with the RDK:

Requirements

:bangbang: The Rule Development Kit is only for IdentityNow Cloud Executed Rules.

Java and Maven are needed for this tool to run, for more information and installation steps refer to requirements.

You can use any IDE that supports Maven and Java, but our team will always recommend VS Code as this is the IDEs we are most familiar with.

Next Steps

As always, we’d love to hear your feedback! If you notice that this tool could be expanded to better fit your needs or notice that something is incorrect please let us know right here in the announcement, create an issue in Github, or you can send us a message directly.

10 Likes

The Rule Development Kit is only for IdentityNow Cloud Executed Rules.

It’ll work for most IIQ rules too! I plan to do so.

3 Likes

Hi, I am seeing this error when attempting to clone the kit repo:

git clone [email protected]:sailpoint-oss/rule-development-kit.git
Cloning into ‘rule-development-kit’…
Connection reset by 140.82.113.3 port 22
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

A post was split to a new topic: Rule Development Kit Installation issue when running mvn clean install

This is a great tool! Thank you!

1 Like

Excited to hear what you think!

We had tried it for Connector Executed Rules, it also worked, however need to some changes to make it.

1 Like

What changes did you have in mind, Ping? @tyler_mairose would love to hear them!

I was watching this presentation ( Getting started with the SailPoint Rule Development Kit ) and thought I would share some changes that I’ve made to the RDK to make my life easier.

Two things bothered me that I could solve:

My solution was to use a plugin to generate the XML code from the Java classes at compile time.
Using the plugin com.igormaznitsa:jcp in pom.xml:

    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>3.2.5</version>
                </plugin>
            </plugins>
        </pluginManagement>
        <plugins>
            <plugin>
                <groupId>com.igormaznitsa</groupId>
                <artifactId>jcp</artifactId>
                <version>7.1.1</version>
                <executions>
                    <execution>
                        <id>preprocessSources</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>preprocess</goal>
                        </goals>
                        <configuration>
                            <sources>
                                <source>${basedir}/src/main/java/rules</source>
                            </sources>
                            <target>${project.basedir}/src/main/resources/rules</target>
                            <!-- <keepComments>REMOVE_JCP_ONLY</keepComments> -->
                            <keepComments>false</keepComments>
                            <keepLines>false</keepLines>
                            <dontOverwriteSameContent>true</dontOverwriteSameContent>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

and creating the Java class like this:

//#outname "Rule - AttributeGenerator - UserPrincipalNameGenerator.xml"
//$$<?xml version='1.0' encoding='UTF-8'?>
//$$<!DOCTYPE Rule PUBLIC "sailpoint.dtd" "sailpoint.dtd">
//$$<Rule name="UserPrincipalNameGenerator" type="AttributeGenerator">
//$$    <Description>Generate a unique UserPrincipalName for Active Directory.</Description>
//$$    <Source><![CDATA[
//#//
package rules;

import java.text.Normalizer;
import java.text.Normalizer.Form;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.WordUtils;
//#//
import org.apache.log4j.LogManager;
//#//
import org.apache.log4j.Logger;
import sailpoint.object.Application;
import sailpoint.object.Identity;
import sailpoint.server.IdnRuleUtil;

//#if false

/**
 * ...
 */
@SuppressWarnings({"rawtypes", "FieldMayBeFinal"})   // BeanShell doesn't support generics
public class UserPrincipalNameGenerator {

    Logger log = LogManager.getLogger(rules.UserPrincipalNameGenerator.class);
    Identity identity = new Identity();
    Application application = new Application();
    IdnRuleUtil idn;
//#endif

    private static String RULE_CONFIG_ATTRIBUTE = "userPrincipalNameGeneratorConfig";
    private static int MAX_USERNAME_LENGTH = 64;


    public void loadConfiguration() {
        ...
    }

    public String generateUserPrincipalName(String firstName, String lastName, String domain) {
        ...
    }

    String generateUserPrincipalName(String firstName, String lastName, String domain, int maxUsernameLength) {
        ...
    }

    String getCanonicalFirstName(String firstName) {
        ...
    }

    String getCanonicalLastName(String lastName) {
        ...
    }

    private static String capitalize(String lastName) {
        ...
    }

    private static String replaceUnsupportedCharsWithSpace(String firstName) {
        ...
    }

    private Map ASCII_REPLACEMENTS = Map.of(
            ...
    );

    private String normalizeDiacritics(String text) {
        ...
    }

    public boolean isUpnUnique(String upn) {
        ...
    }

    String getDomain(Identity identity) {
        ...
    }


    //#//
    String beanshellEntry() {
        loadConfiguration();
        String domain = getDomain(identity);
        if (domain == null) {
            return null;
        }
        return generateUserPrincipalName(identity.getFirstname(), identity.getLastname(), domain);
        //#//
    }
//#//
}
//$$  ]]></Source>
//$$</Rule>

This allows to exclude some lines from XML (e.g. importing of org.apache.log4j.* classes, the beanshell entry function declaration), or blocks of code (documentation, class declaration and objects already present in context - log, identity, idn, application).
This code can be than broken in individual functions which can be easily tested independently, without having to set up identities or the BeanShell environment.


For my personal taste I would also try to reduce the duplicate code in the tests, I find such tests easier to read than rule-development-kit/src/test/java/sailpoint/UsernameGeneratorTest.java at a4fdf22221b1d5a0cd8f4f4f9db414c511c4b84a · sailpoint-oss/rule-development-kit · GitHub

    @Test
    public void testRule_uniqueLogic() {
        mockIdentity("John", "Doe");

        mockAccountExists("[email protected]");
        mockAccountExists("[email protected]");

        assertEquals("[email protected]", evalRule());
    }

Next step would be to use AssertJ as testing library instead of the plain JUnit.

1 Like