Groovy Script Examples

Examples SAML

Login any user as guestuser if the attribute groups contains guests. 

This script should be mapped to the Application-attribute Username and assumes that the usernames comes from the SAML Name-ID:

  1. // Check the group-attribute for the entry "guests"
  2. if(mapping.groups != null && mapping.groups.contains("guests")) {
  3. // Return the static value "guestuser"
  4. return "guestuser"
  5. } else {
  6. // Otherwise use the value from the Name ID
  7. return ATTR_NAMEID
  8. }Handle Groups Not Sent As Multivalue Attribute

Set SAML attribute email to an unique value if the attribute is not present

This script should be mapped to the Application-attribute E-Mail Address.

  1. return mapping.'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress'?.first() ?: "UID@example.com"

Set the last name to uppercase

This script should be applied to the Application-attribute Full Name and assumes the first name is in first and the last name in last

  1. // If the attribute lastName is present transform it to uppercase,
  2. // otherwise use an empty string
  3. def lastName = mapping.lastName?.first() ? mapping.lastName.first().toUpperCase() : ""
  4. // Use an emptry String for the first name if it is not present
  5. def firstName = mapping.firstName?.first() ?: ""
  6. def fullName = "$firstName $lastName"
  7. // The SLF4J-logger de.resolution.retransform.impl.transformers.groovy.GroovyTransformerScript
  8. // is available as logger and can be used to write to the application-log.
  9. // warn is enabled by default, so this message should be visible in the log
  10. if (fullName.trim().isEmpty()) {
  11. logger.warn("Fullname is empty or contains only whitespace")
  12. }
  13. return fullName

Combine groups from attributes with the value true

In this example. the IdP sends a fixed set of group names as keys with the value true if the user is member of that group:

  1. "attributes": {
  2. "grp1": [ "true"],
  3. "grp2": [ "true"],
  4. "grp3": [ "false"]
  5. },


  1. def groups = []
  2. if(mapping.grp1?.contains("true")) {
  3. groups.add("grp1");
  4. }
  5. if(mapping.grp2?.contains("true")) {
  6. groups.add("grp2");
  7. }
  8. if(mapping.grp3?.contains("true")) {
  9. groups.add("grp3");
  10. }
  11. // This logger should not be used in a production-system
  12. logger.warn("Groups are {}", groups)
  13. return groups

Handle Groups Not Sent As Multivalue Attribute in SAML Response, Replace Group Names In The Process

  1. // list of keys/ values to replace group names after splitting
  2. def trafoMap = ["20368564" : "one-group", "10096280" : "other-group"]
  3. // read groups attribute from SAML Response split by semicolon
  4. def splitted = mapping.groups?.first()?.split(";")
  5. // keep this if you want to remove groups from the user, should there be none in the SAML response
  6. if(splitted == null) {
  7. return DROP_ALL // return no values, so existing groups may be removed
  8. }
  9. return splitted
  10. .collect{trafoMap[it]} // apply transformation rules from trafoMap (search and replace)
  11. .findAll{it} // filter null values a.k.a. drop groups not in the trafoMap

Handle Groups Not Sent As Multivalue Attribute in SAML Response And Filter Empty Strings

  1. // read groups attribute from SAML Response split by comma (or any other character that separates them)
  2. def splitted = mapping.groups?.first()?.split(",")
  3. return splitted.findAll{it} // filter null values and empty strings and return


Transform one group from the SAML response to two or more groups

  1. // Input your data as per the descriptions below
  2. // Replace YourIDPGroupAttribute with your actual IdP Group Attribute
  3. def idpGroupAttribute = "YourIDPGroupAttribute"
  4. // Replace IdP_groupName with the group name that you need to transform
  5. def idpGroupName = "IdP_groupName"
  6. // Replace "replacement1" and "replacement2" with the actual group names replacements, and you can add other elements if needed
  7. def replacements = ["replacement1", "replacement2"]
  8. // No need to change anything in the following section
  9. def groups = mapping.get(idpGroupAttribute)
  10. if (groups.contains(idpGroupName)) {
  11. groups.remove(idpGroupName)
  12. groups.addAll(replacements)
  13. }
  14. return groups

Transform one group from the SAML response to two or more groups and also perform more direct transformations

  1. // Input your data as per the descriptions below
  2. // Replace YourIDPGroupAttribute with your actual IdP Group Attribute
  3. // The example below is the Azure AD Default groups claim
  4. def idpGroupAttribute = "http://schemas.microsoft.com/ws/2008/06/identity/claims/groups"
  5. // Replace IdP_groupName_1 with the group name that you want to transform into multiple other groups
  6. def idpGroupName_1 = "my-group-1"
  7. // Add as many groups as you want to be assigned to the user, if idpGroupName_1 is present
  8. def idpGroupName_1_transform_to_groups = ["your-group-1", "your-group-2"]
  9. // No need to change anything in the following block, this takes
  10. def groups = mapping.get(idpGroupAttribute)
  11. if (groups.contains(idpGroupName_1)) {
  12. groups.remove(idpGroupName_1)
  13. groups.addAll(idpGroupName_1_transform_to_groups)
  14. }
  15. // Add more 1:1 replacements here
  16. def idpGroupName_2 = "transform-me-1"
  17. def idpGroupNameReplacement_2 = "your-group-3"
  18. if (groups.contains(idpGroupName_2)) {
  19. groups.remove(idpGroupName_2)
  20. groups.add(idpGroupNameReplacement_2)
  21. }
  22. def idpGroupName_3 = "transform-me-2"
  23. def idpGroupNameReplacement_3 = "your-group-4"
  24. if (groups.contains(idpGroupName_3)) {
  25. groups.remove(idpGroupName_3)
  26. groups.add(idpGroupNameReplacement_3)
  27. }
  28. return groups


Allow user authentication based on the email domain of the user 

  1. // Check the email-attribute and if it is not empty check if it contains the email domain
  2. if (mapping.email[0] != null) {
  3. if (mapping.email[0].contains("@lab.resolution.de")) {
  4. // if it contains the domain return the NameID
  5. return mapping.ATTR_NAMEID
  6. } else {
  7. // Otherwise drop the authentication
  8. return DROP_ALL
  9. }
  10. } else {
  11. // if email-attribute and if it is empty log a warning message and drop the authentication
  12. logger.warn("Dropping User authentication due to missing SAML attribute email")
  13. return DROP_ALL
  14. }


Add a user into a specific group when not calling the JSM portal and the IdP returns a specific group

  1. def groupsToReturn = (groups != null && groups != DROP) ? groups : []
  2. //Check ATTR_SD_CUSTOMER attribute is false and if user is in group g1, when both conditions are met add the user to the group jira-servicedesk-users
  3. if(ATTR_SD_CUSTOMER != ["true"]) {
  4. logger.warn("SD Portal is NOT called")
  5. if(groupsToReturn.contains("g1")) {
  6. logger.warn("User is in g1")
  7. groupsToReturn.add("jira-servicedesk-users")
  8. } else {
  9. //If the user is not in group g1
  10. logger.warn("User is not in g1")
  11. }
  12. //If user has called the JSM portal
  13. } else {
  14. //If user has called the JSM portal
  15. logger.warn("SD Portal is called")
  16. }
  17. return groupsToReturn

Drop the user if the email is already present

Use this to transform the email-attribute. If the email-address is found and don't belong to this user, drop the user.

  1. String email = mapping.email?.first()
  2. String username = mapping.ATTR_NAMEID?.first()
  3. // Check if there is already a user with this email in the system
  4. def existingUser = findUser("ATTR_EMAIL",email)
  5. if(existingUser == null) {
  6. return email
  7. } else {
  8. // If there is such a user check if it's the same username
  9. if(username == existingUser.get("ATTR_NAME").first()) {
  10. return email
  11. } else {
  12. // if not drop the user
  13. return DROP_USER
  14. }
  15. }

Exclude some users from being reactivated during login even if that option is enabled

Use the following Groovy code for the username attribute mapping (assuming that the username attribute is mapped to the default value originally ATTR_NAMEID):

  1. // Enter the list of usernames not to be reactivated below:
  2. def usernameBlacklist = ["username1","username2","username3"]
  3. //
  4. String username = mapping.ATTR_NAMEID
  5. if (username in usernameBlacklist) {
  6. def user = findUser("ATTR_NAME",username)
  7. def active = user?.ATTR_ACTIVE
  8. if (active?.getAt(0) == "true") {
  9. return username
  10. } else {
  11. return DROP_ALL
  12. }
  13. } else {
  14. return username
  15. }


Fail the authentication if a value (among a list) of an attribute matches any value of another list

Use the following Groovy code for the username attribute mapping.

Assumptions:

  • The Jira Username attribute is mapped to ATTR_NAMEID

  • The attribute to be checked is called userDep

  1. // Enter here the list of values that would be checked against:
  2. allowedList = ["value1", "value2", "value3", "value4"]
  3. //
  4. String user = mapping.ATTR_NAMEID?.getAt(0)
  5. def dep = mapping.userDep
  6. if (dep.any { allowedList.contains( it ) }) {
  7. return user
  8. } else {
  9. return DROP_ALL
  10. }


Assign a specific group based on some conditions

In the following example, we are going to assign group 'myGroup' to the users if the following two conditions are both met:

  • The nameId starts with 'M' and followed by some numbers

  • The department attribute has a value of 'IT'

Assuming the following attributes returned by the IdP:

  1. "loginInformation" : {
  2. "nameId" : "M1234",
  3. "attributes" : {
  4. "ATTR_NAMEID" : [ "M1234" ],
  5. "groups" : [ "group1", "group2" ],
  6. "email" : [ "john.doe@example.com" ],
  7. "fullname" : [ "John Doe" ],
  8. "department" : [ "IT" ]
  9. }
  10. }

Use the following Groovy code for the Groups attribute mapping.

  1. username = mapping.ATTR_NAMEID
  2. dept = mapping.department
  3. grp = mapping.groups
  4. if (dept?.getAt(0) == "IT" && username.any {it ==~ /M\d+/} ) {
  5. grp.addAll("myGroup")
  6. }
  7. return grp


Just-In-Time Provisiong add licensed Jira group to any authentication through SAML SSO

In the following example, we are going to add licensed Jira group to the users if the following conditions are met:

  • The user login via SAML

  • If SAML attribute <your-attribute> has a value of 'Internal'

    • The user will be added to the group 'jira-users'

  • Else

    • The user will be added to the group 'jira-external-users'

Use the following Groovy code for the Groups attribute mapping.

  1. def existingGroups = existing?.ATTR_GROUPS ?: []
  2. def groupsToReturn = []
  3. groupsToReturn.addAll(existingGroups)
  4. if(mapping?.<your-attribute>?.first() == "Internal") {
  5. groupsToReturn.add("jira-users")
  6. }
  7. else {
  8. groupsToReturn.add("jira-external-users")
  9. }
  10. return groupsToReturn.toUnique()

Variant

  • If SAML attribute <your-attribute> is null

    • The user will be added to the group 'jira-empty'


  1. def existingGroups = existing?.ATTR_GROUPS ?: []
  2. def groupsToReturn = []
  3. groupsToReturn.addAll(existingGroups)
  4. // Check <your-attribute> and if it is not null check if it contains Internal
  5. // Assign group jira-empty, if it is null.
  6. // Assign group jira-users, if it is not null but does contain Internal
  7. // Assign group jira-external-users, if it is not null but does not contain Internal
  8. if (mapping.<your-attribute> == null)
  9. {
  10. groupsToReturn.add("jira-empty")
  11. }
  12. else if (mapping.<your-attribute>.first() == "Internal")
  13. {
  14. groupsToReturn.add("jira-users")
  15. }
  16. else
  17. {
  18. groupsToReturn.add("jira-external-users")
  19. }
  20. return groupsToReturn.toUnique()


Examples User Sync

Assign a license group based on some conditions and only if user login via SAML

In the following example, we are going to assign the Jira license group 'jira-users' or jira-external-users' to the users if the following conditions are met:

  • The user login via SAML

  • The WorkerType attribute has a value of 'Internal'

    • The user will be added to the group 'jira-users'

  • The WorkerType attribute starts with 'E[ABCD]'

    • The user will be added to the group 'jira-external-users'

  • If the user is already part of the other group (e.g. jira-users) the group will be removed. So, the user can only be part of one group.

Since we are using Group Management, we moved the Group Management configuration (UI) to the groovy script, too. Please remove the UI Group Management configuration and modify the script to your needs.

Assuming the attribute WorkerType is returned by the IdP:

  1. "loginInformation" : {
  2. "nameId" : "M1234",
  3. "attributes" : {
  4. "ATTR_NAMEID" : [ "M1234" ],
  5. "email" : [ "john.doe@example.com" ],
  6. "fullname" : [ "John Doe" ],
  7. "WorkerType" : [ "Internal" ]
  8. }
  9. }


This script should be mapped to the Application-attribute Groups:

  1. // Groups to never remove (can be overriden below)
  2. def groupsToKeep = [ /jira-.*/ ]
  3. // Groups in this list are filtered out
  4. def groupBlacklist = [ /\[TBD.*/ ]
  5. // Groups not in this list are filtered out
  6. def groupWhitelist = [ /\[TBD\].*/, "jira-users", "jira-external-users" ]
  7. def existingGroups = existing?.ATTR_GROUPS?.asStringList() ?: []
  8. def connectorGroups = con?.GROUPS?.asStringList() ?: []
  9. def groupsToReturn = []
  10. def existingGroupsToKeep = existingGroups.findAll {
  11. egroup -> groupsToKeep.any {keep -> egroup ==~ keep } }
  12. groupsToReturn.addAll(existingGroupsToKeep)
  13. // Every group-pattern in the blacklist must NOT match a group's name
  14. def groupsAfterBlacklist = connectorGroups.findAll {
  15. cgroup -> groupBlacklist.every {bl -> !(cgroup ==~ bl) } }
  16. // Any matching group-pattern in the whitelist is sufficient
  17. // be aware that the blacklisted groups are filtered out already,
  18. // so the blacklist has higher precedence than the whitelist.
  19. def groupsAfterWhitelist = groupsAfterBlacklist.findAll {
  20. cgroup -> groupWhitelist.any {wl -> cgroup ==~ wl } }
  21. groupsToReturn.addAll(groupsAfterWhitelist)
  22. String workerType = saml.'WorkerType'?.first()?.asString()
  23. // When saml is not empty, this
  24. // is a single user update during saml login
  25. boolean isLogin = !saml?.isEmpty()
  26. if(isLogin) {
  27. if(workerType ==~ /^Internal.*/ ) {
  28. groupsToReturn.add("jira-users")
  29. groupsToReturn.remove("jira-external-users") }
  30. else if(workerType ==~ /^E[ABC].*/) {
  31. groupsToReturn.add("jira-external-users")
  32. groupsToReturn.remove("jira-users")
  33. }
  34. }
  35. return groupsToReturn.toUnique()

Remove any user from the group confluence-users if the user is disabled on the IdP side.

The script could be helpful since Confluence is sometimes displaying an incorrect number of active users. License will display the correct number of users only if the users are not part of any group with CAN-USE permission (in our case 'confluence-users').

This script should be mapped to the Application-attribute Groups:

  1. def groups = existing?.ATTR_GROUPS ?: []
  2. groups.addAll(con?.GROUPS ?: [])
  3. // Check if the account is disabled and the user is part of the group confluence-users
  4. if (con.accountEnabled == "false" && groups.contains("confluence-users")) {
  5. // If the criterias match remove the user from the group confluence-users
  6. groups.remove("confluence-users")
  7. }
  8. return groups.toUnique()



Transform group names using regular expressions

Replacing attributes using regular expressions can be done in a Groovy-transformation instead of adding Regexes to the configuration. Especially when a large number of regular expressions need to be applied it can be helpful to have all of them in a piece of code

  1. // collect{} applies the given closure to all
  2. // elements in a list and returns a list containing
  3. // all the transformed elements.
  4. // Using safe-dereference ?. to avoid a NullPointerException if GROUPS is not present
  5. return con.GROUPS?.collect{
  6. grp -> grp.toString() // toString() is required because the elements are StringStructuredData
  7. .replaceAll(/Pink Floyd/,'def') // using Slashy Strings for the regex avoids some escaping
  8. .replaceAll(/Blind(.*)/,'xyz$1') // using '' instead of "" for the replacements allows using $ without escaping
  9. .replaceAll(/\./,'%') // replacing . requires escaping (\.), in a normal String this would be \\., but not required in Slashy Strings
  10. // add more as needed
  11. } ?: [] // return an empty list of the GROUP-attribute is not present

Assuming the user data looks like this

  1. {
  2. "con": {
  3. "USERID": "damaris.rau",
  4. "Unique_ID": "b9e13bcc-3df9-4eaf-ab8c-c8e77485d484",
  5. "EMAIL": "damaris.rau@example.org",
  6. "FULLNAME": "Damaris Rau",
  7. "Manager": "zane.maggio",
  8. "GROUPS": [
  9. "Pink Floyd",
  10. "Blind Faith",
  11. "R.E.M."
  12. ]
  13. },
  14. "saml": {},
  15. "existing": {}
  16. }

This transformation returns

  1. ["def","xyz Faith","R%E%M%"]


Apply the Cleanup Behaviour (e.g. Disable) when a user is a member of a certain group

Use the following Groovy code for the Groups attribute mapping in the connector:

  1. // Replace the <> placeholder with the group name below
  2. def exclusionGroup = "<enter the group name here>"
  3. if (con.GROUPS.contains(exclusionGroup)) {
  4. return DROP_ALL
  5. }
  6. return GROUPS


Assign default groups only to a certain domain in the email address

Use the following Groovy code for the Groups attribute mapping in the connector:

  1. // default groups to assign under specific criteria
  2. // change the group names as per your requirement
  3. def defaultGroups = ["jira-users", "confluence-users" ]
  4. // mapping the IdP groups
  5. def connectorGroups = con?.GROUPS?.asStringList() ?: []
  6. // mapping the email address
  7. // change userPrincipalName below to your email attribute
  8. def email = con?.userPrincipalName?.first() ?: []
  9. // check if the email address has a specific domain
  10. // change example below to your actual domain
  11. if (email ==~ /.*@example\.com/) {
  12. // if true, add the default groups to the list of groups returned by the IdP
  13. connectorGroups.addAll(defaultGroups)
  14. }
  15. return connectorGroups

Assign a default group only upon a successful login via SAML

User should be assigned to a specific group, called groupA, only upon a successful SAML login (i.e. not via the regular sync of User Sync)

Use the following Groovy code for the Groups attribute mapping in the connector:

  1. // mapping the IdP groups
  2. def connectorGroups = con?.GROUPS?.asStringList() ?: []
  3. // When saml is not empty, this
  4. // is a single user update during SAML SSO login
  5. boolean isLogin = !saml?.isEmpty()
  6. if (isLogin) {
  7. connectorGroups.addAll("groupA")
  8. }
  9. return connectorGroups

Assign a default group only to the members of certain groups

Use the following Groovy code for the Groups attribute mapping in the connector:

  1. // default group to assign under specific criteria
  2. def defaultGroup = "jira-software-users"
  3. // required groups for the condition
  4. def requiredGroups = ["requiredGroup1", "requiredGroup2", "requiredGroup3"]
  5. // mapping the IdP groups
  6. def connectorGroups = con?.GROUPS?.asStringList() ?: []
  7. // if the user is a member of any of the requiredGroups, add the defaultGroup
  8. if (connectorGroups.any { requiredGroups.contains( it ) }) {
  9. connectorGroups.addAll(defaultGroup)
  10. }
  11. return connectorGroups