Add roles based on attribute value (instead of role name) #86

Open
Elektordi wants to merge 2 commits from Elektordi/main into main
3 changed files with 75 additions and 2 deletions

View file

@ -74,6 +74,20 @@ public class RegexRealmAndClientRoleClaimMapper extends AbstractClaimMapper {
realmRolesRegularExpressionConfigProperty.setHelpText("regular expression to apply to the OIDC claim to extract realm roles; must specify one named-capturing groups: role");
realmRolesRegularExpressionConfigProperty.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(realmRolesRegularExpressionConfigProperty);
ProviderConfigProperty searchRolesAttributeNameAttributeNameConfigProperty = new ProviderConfigProperty();
searchRolesAttributeNameAttributeNameConfigProperty.setName(RegexRealmAndClientRoleMapperUtil.SEARCH_ROLES_ATTRIBUTE_NAME);
searchRolesAttributeNameAttributeNameConfigProperty.setLabel("search roles attribute name");
searchRolesAttributeNameAttributeNameConfigProperty.setHelpText("only evaluate realm or client roles having an attribute with this name");
searchRolesAttributeNameAttributeNameConfigProperty.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(searchRolesAttributeNameAttributeNameConfigProperty);
ProviderConfigProperty searchRolesAttributeNameRegularExpressionConfigProperty = new ProviderConfigProperty();
searchRolesAttributeNameRegularExpressionConfigProperty.setName(RegexRealmAndClientRoleMapperUtil.SEARCH_ROLES_REGULAR_EXPRESSION);
searchRolesAttributeNameRegularExpressionConfigProperty.setLabel("search roles regular expression");
searchRolesAttributeNameRegularExpressionConfigProperty.setHelpText("regular expression to apply to the OIDC claim to search for roles having this attribute value; must specify one named-capturing groups: value");
searchRolesAttributeNameRegularExpressionConfigProperty.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(searchRolesAttributeNameRegularExpressionConfigProperty);
}
@Override

View file

@ -74,6 +74,20 @@ public class RegexRealmAndClientRoleAttributeMapper extends AbstractIdentityProv
realmRolesRegularExpressionConfigProperty.setHelpText("regular expression to apply to the SAML attribute to extract realm roles; must specify one named-capturing group: role");
realmRolesRegularExpressionConfigProperty.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(realmRolesRegularExpressionConfigProperty);
ProviderConfigProperty searchRolesAttributeNameAttributeNameConfigProperty = new ProviderConfigProperty();
searchRolesAttributeNameAttributeNameConfigProperty.setName(RegexRealmAndClientRoleMapperUtil.SEARCH_ROLES_ATTRIBUTE_NAME);
searchRolesAttributeNameAttributeNameConfigProperty.setLabel("search roles attribute name");
searchRolesAttributeNameAttributeNameConfigProperty.setHelpText("only evaluate realm or client roles having an attribute with this name");
searchRolesAttributeNameAttributeNameConfigProperty.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(searchRolesAttributeNameAttributeNameConfigProperty);
ProviderConfigProperty searchRolesAttributeNameRegularExpressionConfigProperty = new ProviderConfigProperty();
searchRolesAttributeNameRegularExpressionConfigProperty.setName(RegexRealmAndClientRoleMapperUtil.SEARCH_ROLES_REGULAR_EXPRESSION);
searchRolesAttributeNameRegularExpressionConfigProperty.setLabel("search roles regular expression");
searchRolesAttributeNameRegularExpressionConfigProperty.setHelpText("regular expression to apply to the SAML claim to search for roles having this attribute value; must specify one named-capturing groups: value");
searchRolesAttributeNameRegularExpressionConfigProperty.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(searchRolesAttributeNameRegularExpressionConfigProperty);
}
@Override

View file

@ -6,6 +6,7 @@ import com.google.common.collect.Sets;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import java.util.stream.Collectors;
import org.jboss.logging.Logger;
import org.keycloak.models.ClientModel;
@ -27,6 +28,10 @@ public final class RegexRealmAndClientRoleMapperUtil {
public static final String REALM_ROLES_REGULAR_EXPRESSION = "realm-roles-regular-expression";
public static final String SEARCH_ROLES_ATTRIBUTE_NAME = "search-roles-attribute-name";
public static final String SEARCH_ROLES_REGULAR_EXPRESSION = "search-roles-regular-expression";
private static final Logger LOG = Logger.getLogger(RegexRealmAndClientRoleMapperUtil.class);
private RegexRealmAndClientRoleMapperUtil() {
@ -39,12 +44,20 @@ public final class RegexRealmAndClientRoleMapperUtil {
// adjust the user's client role assignments
String clientRolesRegularExpression = mapper.getConfig().getOrDefault(CLIENT_ROLES_REGULAR_EXPRESSION, "");
String clientRolesAttributeName = mapper.getConfig().getOrDefault(CLIENT_ROLES_ATTRIBUTE_NAME, "");
RegexRealmAndClientRoleMapperUtil.adjustUserClientRoleAssignments(realm, user, assertedValues, clientRolesRegularExpression, clientRolesAttributeName);
if(clientRolesRegularExpression != "" && clientRolesAttributeName != "")
RegexRealmAndClientRoleMapperUtil.adjustUserClientRoleAssignments(realm, user, assertedValues, clientRolesRegularExpression, clientRolesAttributeName);
// adjust the user's realm role assignments
String realmRolesRegularExpression = mapper.getConfig().getOrDefault(REALM_ROLES_REGULAR_EXPRESSION, "");
String realmRolesAttributeName = mapper.getConfig().getOrDefault(REALM_ROLES_ATTRIBUTE_NAME, "");
RegexRealmAndClientRoleMapperUtil.adjustUserRealmRoleAssignments(realm, user, assertedValues, realmRolesRegularExpression, realmRolesAttributeName);
if(realmRolesRegularExpression != "" && realmRolesRegularExpression != "")
RegexRealmAndClientRoleMapperUtil.adjustUserRealmRoleAssignments(realm, user, assertedValues, realmRolesRegularExpression, realmRolesAttributeName);
// adjust the user's attribute-based (search) role assignments
String searchRolesRegularExpression = mapper.getConfig().getOrDefault(SEARCH_ROLES_REGULAR_EXPRESSION, "");
String searchRolesAttributeName = mapper.getConfig().getOrDefault(SEARCH_ROLES_ATTRIBUTE_NAME, "");
if(searchRolesRegularExpression != "" && searchRolesAttributeName != "")
RegexRealmAndClientRoleMapperUtil.adjustUserSearchRoleAssignments(realm, user, assertedValues, searchRolesRegularExpression, searchRolesAttributeName);
}
private static void adjustUserClientRoleAssignments(RealmModel realm, UserModel user, Set<String> assertedValues, String regularExpression, String attributeName) {
@ -110,4 +123,36 @@ public final class RegexRealmAndClientRoleMapperUtil {
// un-assign the realm roles that the user has but shouldn't
Sets.difference(haveRoles, wantRoles).forEach(user::deleteRoleMapping);
}
private static void adjustUserSearchRoleAssignments(RealmModel realm, UserModel user, Set<String> assertedValues, String regularExpression, String attributeName) {
LOG.trace("adjust user attribute-based role assignments");
Pattern pattern = Pattern.compile(regularExpression);
// determine the roles that the user should have
Set<RoleModel> wantRoles = assertedValues.stream()
.map(pattern::matcher)
.filter(Matcher::matches)
.filter(matcher -> matcher.groupCount() == 1)
.filter(matcher -> matcher.group("value") != null)
.flatMap(matcher ->
realm.getRolesStream()
.filter(realmRole ->
realmRole.getAttributeStream(attributeName)
.flatMap(s -> Stream.of(s.split(",")))
.anyMatch(s -> matcher.group("value").equals(s))))
.collect(Collectors.toSet());
// determine the roles that the user does have
Set<RoleModel> haveRoles = user.getRoleMappingsStream()
.filter(role -> role.getAttributes().containsKey(attributeName))
.collect(Collectors.toSet());
// assign the roles that the user should have but doesn't
Sets.difference(wantRoles, haveRoles).forEach(user::grantRole);
// un-assign the roles that the user has but shouldn't
Sets.difference(haveRoles, wantRoles).forEach(user::deleteRoleMapping);
}
}