Merge pull request #3859 from thomasdarimont/issue/KEYCLOAK-4205-role-mapper-value-as-array

KEYCLOAK-4205 Allow to return json arrays in Client and Realm Role Mappers
This commit is contained in:
Marek Posolda 2017-03-09 08:47:29 +01:00 committed by GitHub
commit 16d5ca3378
5 changed files with 154 additions and 52 deletions

View file

@ -23,8 +23,10 @@ import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.utils.RoleUtils;
import org.keycloak.protocol.ProtocolMapperUtils;
import org.keycloak.representations.IDToken;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@ -100,10 +102,17 @@ abstract class AbstractUserRoleMappingMapper extends AbstractOIDCProtocolMapper
clientUserRoles = clientUserRoles.filter(clientRoles::contains);
}
Set<String> realmRoleNames = clientUserRoles
List<String> realmRoleNames = clientUserRoles
.map(m -> rolePrefix + m.getName())
.collect(Collectors.toSet());
.collect(Collectors.toList());
OIDCAttributeMapperHelper.mapClaim(token, mappingModel, realmRoleNames);
Object claimValue = realmRoleNames;
boolean multiValued = "true".equals(mappingModel.getConfig().get(ProtocolMapperUtils.MULTIVALUED));
if (!multiValued) {
claimValue = realmRoleNames.toString();
}
OIDCAttributeMapperHelper.mapClaim(token, mappingModel, claimValue);
}
}

View file

@ -60,6 +60,14 @@ public class UserClientRoleMappingMapper extends AbstractUserRoleMappingMapper {
clientRolePrefix.setType(ProviderConfigProperty.STRING_TYPE);
CONFIG_PROPERTIES.add(clientRolePrefix);
ProviderConfigProperty multiValued = new ProviderConfigProperty();
multiValued.setName(ProtocolMapperUtils.MULTIVALUED);
multiValued.setLabel(ProtocolMapperUtils.MULTIVALUED_LABEL);
multiValued.setHelpText(ProtocolMapperUtils.MULTIVALUED_HELP_TEXT);
multiValued.setType(ProviderConfigProperty.BOOLEAN_TYPE);
multiValued.setDefaultValue(false);
CONFIG_PROPERTIES.add(multiValued);
OIDCAttributeMapperHelper.addAttributeConfig(CONFIG_PROPERTIES, UserClientRoleMappingMapper.class);
}
@ -138,15 +146,23 @@ public class UserClientRoleMappingMapper extends AbstractUserRoleMappingMapper {
String name,
String tokenClaimName,
boolean accessToken, boolean idToken) {
ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper(name, "foo",
tokenClaimName, "String",
true, name,
accessToken, idToken,
PROVIDER_ID);
return create(clientId, clientRolePrefix, name, tokenClaimName, accessToken, idToken, false);
}
public static ProtocolMapperModel create(String clientId, String clientRolePrefix,
String name,
String tokenClaimName,
boolean accessToken, boolean idToken, boolean multiValued) {
ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper(name, "foo",
tokenClaimName, "String",
true, name,
accessToken, idToken,
PROVIDER_ID);
mapper.getConfig().put(ProtocolMapperUtils.MULTIVALUED, String.valueOf(multiValued));
mapper.getConfig().put(ProtocolMapperUtils.USER_MODEL_CLIENT_ROLE_MAPPING_CLIENT_ID, clientId);
mapper.getConfig().put(ProtocolMapperUtils.USER_MODEL_CLIENT_ROLE_MAPPING_ROLE_PREFIX, clientRolePrefix);
return mapper;
}
}

View file

@ -46,6 +46,14 @@ public class UserRealmRoleMappingMapper extends AbstractUserRoleMappingMapper {
realmRolePrefix.setType(ProviderConfigProperty.STRING_TYPE);
CONFIG_PROPERTIES.add(realmRolePrefix);
ProviderConfigProperty multiValued = new ProviderConfigProperty();
multiValued.setName(ProtocolMapperUtils.MULTIVALUED);
multiValued.setLabel(ProtocolMapperUtils.MULTIVALUED_LABEL);
multiValued.setHelpText(ProtocolMapperUtils.MULTIVALUED_HELP_TEXT);
multiValued.setType(ProviderConfigProperty.BOOLEAN_TYPE);
multiValued.setDefaultValue(false);
CONFIG_PROPERTIES.add(multiValued);
OIDCAttributeMapperHelper.addAttributeConfig(CONFIG_PROPERTIES, UserRealmRoleMappingMapper.class);
}
@ -83,14 +91,21 @@ public class UserRealmRoleMappingMapper extends AbstractUserRoleMappingMapper {
public static ProtocolMapperModel create(String realmRolePrefix,
String name,
String tokenClaimName, boolean accessToken, boolean idToken) {
ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper(name, "foo",
tokenClaimName, "String",
true, name,
accessToken, idToken,
PROVIDER_ID);
return create(realmRolePrefix, name, tokenClaimName, accessToken, idToken, false);
}
public static ProtocolMapperModel create(String realmRolePrefix,
String name,
String tokenClaimName, boolean accessToken, boolean idToken, boolean multiValued) {
ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper(name, "foo",
tokenClaimName, "String",
true, name,
accessToken, idToken,
PROVIDER_ID);
mapper.getConfig().put(ProtocolMapperUtils.MULTIVALUED, String.valueOf(multiValued));
mapper.getConfig().put(ProtocolMapperUtils.USER_MODEL_REALM_ROLE_MAPPING_ROLE_PREFIX, realmRolePrefix);
return mapper;
}
}

View file

@ -17,19 +17,13 @@
package org.keycloak.testsuite.oauth;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.junit.After;
import org.hamcrest.CoreMatchers;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.ProtocolMappersResource;
import org.keycloak.admin.client.resource.UserResource;
import org.keycloak.testsuite.util.ProtocolMapperUtil;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.mappers.AddressMapper;
import org.keycloak.representations.AccessToken;
@ -47,8 +41,21 @@ import org.keycloak.testsuite.util.ClientManager;
import org.keycloak.testsuite.util.OAuthClient;
import org.keycloak.testsuite.util.ProtocolMapperUtil;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.arrayContainingInAnyOrder;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
import static org.keycloak.testsuite.admin.ApiUtil.findClientByClientId;
import static org.keycloak.testsuite.admin.ApiUtil.findClientResourceByClientId;
@ -249,9 +256,46 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
Assert.assertThat(roleMappings.keySet(), containsInAnyOrder("realm", "test-app"));
String realmRoleMappings = (String) roleMappings.get("realm");
String testAppMappings = (String) roleMappings.get("test-app");
assertRolesString(realmRoleMappings,
"pref.user", // from direct assignment in user definition
"pref.offline_access" // from direct assignment in user definition
);
assertRolesString(testAppMappings,
"customer-user" // from direct assignment in user definition
);
// Revert
deleteMappers(protocolMappers);
}
/**
* KEYCLOAK-4205
* @throws Exception
*/
@Test
public void testUserRoleToAttributeMappersWithMultiValuedRoles() throws Exception {
// Add mapper for realm roles
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true, true);
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper("test-app", null, "Client roles mapper", "roles-custom.test-app", true, true, true);
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper));
// Login user
OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("password", "test-user@localhost", "password");
IDToken idToken = oauth.verifyIDToken(response.getIdToken());
// Verify attribute is filled
Map<String, Object> roleMappings = (Map<String, Object>)idToken.getOtherClaims().get("roles-custom");
Assert.assertThat(roleMappings.keySet(), containsInAnyOrder("realm", "test-app"));
Assert.assertThat(roleMappings.get("realm"), CoreMatchers.instanceOf(List.class));
Assert.assertThat(roleMappings.get("test-app"), CoreMatchers.instanceOf(List.class));
List<String> realmRoleMappings = (List<String>) roleMappings.get("realm");
List<String> testAppMappings = (List<String>) roleMappings.get("test-app");
assertRoles(realmRoleMappings,
"pref.user", // from direct assignment in user definition
"pref.offline_access" // from direct assignment in user definition
"pref.user", // from direct assignment in user definition
"pref.offline_access" // from direct assignment in user definition
);
assertRoles(testAppMappings,
"customer-user" // from direct assignment in user definition
@ -261,7 +305,6 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
deleteMappers(protocolMappers);
}
@Test
public void testUserGroupRoleToAttributeMappers() throws Exception {
// Add mapper for realm roles
@ -281,14 +324,14 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
Assert.assertThat(roleMappings.keySet(), containsInAnyOrder("realm", clientId));
String realmRoleMappings = (String) roleMappings.get("realm");
String testAppMappings = (String) roleMappings.get(clientId);
assertRoles(realmRoleMappings,
"pref.admin", // from direct assignment to /roleRichGroup/level2group
"pref.user", // from parent group of /roleRichGroup/level2group, i.e. from /roleRichGroup
"pref.customer-user-premium", // from client role customer-admin-composite-role - realm role for test-app
"pref.realm-composite-role", // from parent group of /roleRichGroup/level2group, i.e. from /roleRichGroup
"pref.sample-realm-role" // from realm role realm-composite-role
assertRolesString(realmRoleMappings,
"pref.admin", // from direct assignment to /roleRichGroup/level2group
"pref.user", // from parent group of /roleRichGroup/level2group, i.e. from /roleRichGroup
"pref.customer-user-premium", // from client role customer-admin-composite-role - realm role for test-app
"pref.realm-composite-role", // from parent group of /roleRichGroup/level2group, i.e. from /roleRichGroup
"pref.sample-realm-role" // from realm role realm-composite-role
);
assertRoles(testAppMappings,
assertRolesString(testAppMappings,
"ta.customer-user", // from direct assignment to /roleRichGroup/level2group
"ta.customer-admin-composite-role", // from direct assignment to /roleRichGroup/level2group
"ta.customer-admin", // from client role customer-admin-composite-role - client role for test-app
@ -319,14 +362,14 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
Assert.assertThat(roleMappings.keySet(), containsInAnyOrder("realm", clientId));
String realmRoleMappings = (String) roleMappings.get("realm");
String testAppAuthzMappings = (String) roleMappings.get(clientId);
assertRoles(realmRoleMappings,
"pref.admin", // from direct assignment to /roleRichGroup/level2group
"pref.user", // from parent group of /roleRichGroup/level2group, i.e. from /roleRichGroup
"pref.customer-user-premium", // from client role customer-admin-composite-role - realm role for test-app
"pref.realm-composite-role", // from parent group of /roleRichGroup/level2group, i.e. from /roleRichGroup
"pref.sample-realm-role" // from realm role realm-composite-role
assertRolesString(realmRoleMappings,
"pref.admin", // from direct assignment to /roleRichGroup/level2group
"pref.user", // from parent group of /roleRichGroup/level2group, i.e. from /roleRichGroup
"pref.customer-user-premium", // from client role customer-admin-composite-role - realm role for test-app
"pref.realm-composite-role", // from parent group of /roleRichGroup/level2group, i.e. from /roleRichGroup
"pref.sample-realm-role" // from realm role realm-composite-role
);
assertRoles(testAppAuthzMappings); // There is no client role defined for test-app-authz
assertRolesString(testAppAuthzMappings); // There is no client role defined for test-app-authz
// Revert
deleteMappers(protocolMappers);
@ -352,11 +395,11 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
Assert.assertThat(roleMappings.keySet(), containsInAnyOrder("realm", clientId));
String realmRoleMappings = (String) roleMappings.get("realm");
String testAppScopeMappings = (String) roleMappings.get(clientId);
assertRoles(realmRoleMappings,
"pref.admin", // from direct assignment to /roleRichGroup/level2group
"pref.user" // from parent group of /roleRichGroup/level2group, i.e. from /roleRichGroup
assertRolesString(realmRoleMappings,
"pref.admin", // from direct assignment to /roleRichGroup/level2group
"pref.user" // from parent group of /roleRichGroup/level2group, i.e. from /roleRichGroup
);
assertRoles(testAppScopeMappings,
assertRolesString(testAppScopeMappings,
"test-app-allowed-by-scope" // from direct assignment to roleRichUser, present as scope allows it
);
@ -384,11 +427,11 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
Assert.assertThat(roleMappings.keySet(), containsInAnyOrder("realm", clientId));
String realmRoleMappings = (String) roleMappings.get("realm");
String testAppScopeMappings = (String) roleMappings.get(clientId);
assertRoles(realmRoleMappings,
assertRolesString(realmRoleMappings,
"pref.admin", // from direct assignment to /roleRichGroup/level2group
"pref.user" // from parent group of /roleRichGroup/level2group, i.e. from /roleRichGroup
);
assertRoles(testAppScopeMappings,
assertRolesString(testAppScopeMappings,
"test-app-allowed-by-scope", // from direct assignment to roleRichUser, present as scope allows it
"customer-admin-composite-role" // from direct assignment to /roleRichGroup/level2group, present as scope allows it
);
@ -397,10 +440,14 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
deleteMappers(protocolMappers);
}
private void assertRoles(String actualRoleString, String...expectedRoles) {
String[] roles;
private void assertRoles(List<String> actualRoleList, String ...expectedRoles){
Assert.assertNames(actualRoleList, expectedRoles);
}
private void assertRolesString(String actualRoleString, String...expectedRoles) {
Assert.assertThat(actualRoleString.matches("^\\[.*\\]$"), is(true));
roles = actualRoleString.substring(1, actualRoleString.length() - 1).split(",\\s*");
String[] roles = actualRoleString.substring(1, actualRoleString.length() - 1).split(",\\s*");
if (expectedRoles == null || expectedRoles.length == 0) {
Assert.assertThat(roles, arrayContainingInAnyOrder(""));

View file

@ -114,16 +114,31 @@ public class ProtocolMapperUtil {
String tokenClaimName,
boolean accessToken, boolean idToken) {
return ModelToRepresentation.toRepresentation(UserRealmRoleMappingMapper.create(realmRolePrefix, name, tokenClaimName, accessToken, idToken));
return createUserRealmRoleMappingMapper(realmRolePrefix, name, tokenClaimName, accessToken, idToken, false);
}
public static ProtocolMapperRepresentation createUserRealmRoleMappingMapper(String realmRolePrefix,
String name,
String tokenClaimName,
boolean accessToken, boolean idToken, boolean multiValued) {
return ModelToRepresentation.toRepresentation(UserRealmRoleMappingMapper.create(realmRolePrefix, name, tokenClaimName, accessToken, idToken, multiValued));
}
public static ProtocolMapperRepresentation createUserClientRoleMappingMapper(String clientId, String clientRolePrefix,
String name,
String tokenClaimName,
boolean accessToken, boolean idToken) {
return ModelToRepresentation.toRepresentation(UserClientRoleMappingMapper.create(clientId, clientRolePrefix, name, tokenClaimName, accessToken, idToken));
return createUserClientRoleMappingMapper(clientId, clientRolePrefix, name, tokenClaimName, accessToken, idToken, false);
}
public static ProtocolMapperRepresentation createUserClientRoleMappingMapper(String clientId, String clientRolePrefix,
String name,
String tokenClaimName,
boolean accessToken, boolean idToken, boolean multiValued) {
return ModelToRepresentation.toRepresentation(UserClientRoleMappingMapper.create(clientId, clientRolePrefix, name, tokenClaimName, accessToken, idToken, multiValued));
}
public static ProtocolMapperRepresentation getMapperByNameAndProtocol(ProtocolMappersResource protocolMappers, String protocol, String name) {