SAML Single Sign On Further Configuration Current: Transformations with Groovy Transformations with Groovy Starting with version SAML SingleSignOn 4.0.0 and UserSync 2.0, attributes can be transformed using Groovy-scripts.About Groovy: https://groovy-lang.org/documentation.htmlThe Groovy-script contains a variable mapping. This variable is a Map with strings as keys and string-lists as values. In SAML, it contains the attributes from the SAML-assertion sent by the IdP, in UserSync the attributes retrieved by the connector.After the script is run, the values from the key groovyResult is taken for the mapped attribute.Scripts can be configured for any attribute. Theses scripts are independent from each other, each of them gets a fresh copy of the attribute-map.The NameID-attribute from the SAML-assertion is accessible under mapping.ATTR_NAMEID.Each script must finish within 1 second, otherwise its cancelled and a TransformationFailedException is thrown.The SLF4J-logger de.resolution.retransform.impl.transformers.groovy.GroovyTransformerScript is available as logger.ExamplesLogin 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: // Check the group-attribute for the entry "guests" if(mapping.groups.contains("guests")) { // Write the static value "guestuser" to groovyResult. // [] are required because the values are lists mapping.groovyResult = ["guestuser"] } else { // Otherwise use the value from the Name ID // no [] needed because the value in the mapping is already a list mapping.groovyResult = mapping.ATTR_NAMEID } GROOVY Set the last name to uppercaseThis script should be applied to the Application-attribute Full Name and assumes the first name is in first and the last name in last // If the attribute lastName is present transform it to uppercase, // otherwise used an empty string def lastName = mapping.lastName[0] ? mapping.lastName[0].toUpperCase() : "" // Groovy GStrings allow variable substition with $variable or ${expression} // toString() is required here because GStrings must be explicitly turned into Java-Strings. def fullName = "${mapping.firstName[0]} $lastName".toString() // The SLF4J-logger de.resolution.retransform.impl.transformers.groovy.GroovyTransformerScript // is available as logger and can be used to write to the application-log. // warn is enabled by default, so this message should be visible in the log logger.warn("########## This is the new full name: {}",fullName) // Wrapping the value into a list mapping.groovyResult = [fullName] CODE Combine groups from attributes with the value trueIn 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: "attributes": { "grp1": [ "true"], "grp2": [ "true"], "grp3": [ "false"] }, CODE def groups = [] if(mapping.grp1.contains("true")) { groups.add("grp1"); } if(mapping.grp2.contains("true")) { groups.add("grp2"); } if(mapping.grp3.contains("true")) { groups.add("grp3"); } logger.warn("Groups are {}", groups) mapping.groovyResult = groups CODE Handle Groups Not Sent As Multivalue Attribute in SAML Response def trafoMap = ["20368564" : "stash-users", "10096280" : "other-group"] // list of key/ value to replace group names after splitting def splitted = mapping.Groups[0].split(",") // read "Groups" attribute from SAML Response split by comma mapping.groovyResult = splitted .collect{trafoMap[it]} // apply transformation rules from trafoMap (search and replace) .findAll{it} // filter null values a.k.a. drop groups not in the trafoMap GROOVY Transform one group (from the SAML response) to two (or multiple) groups in the Atlassian product // Input your data as per the descriptions below // Replace YourIDPGroupAttribute with your actual IdP Group Attribute def idpGroupAttribute = "YourIDPGroupAttribute" // Replace IdP_groupName with the group name that you need to transform def idpGroupName = "IdP_groupName" // Replace "replacement1" and "replacement2" with the actual group names replacements, and you can add other elements if needed def replacements = ["replacement1", "replacement2"] // No need to change anything in the following section def groups = mapping.get(idpGroupAttribute) if (groups.contains(idpGroupName)) { groups.remove(idpGroupName) groups.addAll(replacements) } mapping.groovyResult = groups CODE SAML Single Sign-On is available for Atlassian Server & Atlassian Data Center products. Our Jira Data Center, Confluence Data Center, Bitbucket Data Center, Jira Server, Confluence Server, Bitbucket Server and other apps are all available on the Atlassian Marketplace.