KEYCLOAK-3469 Make role mappers account for user groups
This commit is contained in:
parent
6c64494620
commit
750e942267
9 changed files with 407 additions and 63 deletions
|
@ -24,8 +24,17 @@ import java.util.Set;
|
|||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface RoleMapperModel {
|
||||
/**
|
||||
* Returns set of realm roles that are directly set to this object.
|
||||
* @return see description
|
||||
*/
|
||||
Set<RoleModel> getRealmRoleMappings();
|
||||
|
||||
/**
|
||||
* Returns set of client roles that are directly set to this object for the given client.
|
||||
* @param app Client to get the roles for
|
||||
* @return see description
|
||||
*/
|
||||
Set<RoleModel> getClientRoleMappings(ClientModel app);
|
||||
|
||||
/**
|
||||
|
@ -48,7 +57,15 @@ public interface RoleMapperModel {
|
|||
*/
|
||||
void grantRole(RoleModel role);
|
||||
|
||||
/**
|
||||
* Returns set of all role (both realm all client) that are directly set to this object.
|
||||
* @return
|
||||
*/
|
||||
Set<RoleModel> getRoleMappings();
|
||||
|
||||
/**
|
||||
* Removes the given role mapping from this object.
|
||||
* @param role Role to remove
|
||||
*/
|
||||
void deleteRoleMapping(RoleModel role);
|
||||
}
|
||||
|
|
|
@ -17,18 +17,19 @@
|
|||
|
||||
package org.keycloak.protocol.oidc.mappers;
|
||||
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.IDToken;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Base class for mapping of user role mappings to an ID and Access Token claim.
|
||||
|
@ -38,39 +39,95 @@ import java.util.Set;
|
|||
abstract class AbstractUserRoleMappingMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper {
|
||||
|
||||
/**
|
||||
* Returns the role names extracted from the given {@code roleModels} while recursively traversing "Composite Roles".
|
||||
* <p>
|
||||
* Optionally prefixes each role name with the given {@code prefix}.
|
||||
* </p>
|
||||
*
|
||||
* @param roleModels
|
||||
* @param prefix the prefix to apply, may be {@literal null}
|
||||
* Returns a stream with roles that come from:
|
||||
* <ul>
|
||||
* <li>Direct assignment of the role to the user</li>
|
||||
* <li>Direct assignment of the role to any group of the user or any of its parent group</li>
|
||||
* <li>Composite roles are expanded recursively, the composite role itself is also contained in the returned stream</li>
|
||||
* </ul>
|
||||
* @param user User to enumerate the roles for
|
||||
* @return
|
||||
*/
|
||||
protected Set<String> flattenRoleModelToRoleNames(Set<RoleModel> roleModels, String prefix) {
|
||||
public static Stream<RoleModel> getAllUserRolesStream(UserModel user) {
|
||||
return Stream.concat(
|
||||
user.getRoleMappings().stream(),
|
||||
user.getGroups().stream()
|
||||
.flatMap(g -> groupAndItsParentsStream(g))
|
||||
.flatMap(g -> g.getRoleMappings().stream()))
|
||||
.flatMap(role -> expandCompositeRolesStream(role));
|
||||
}
|
||||
|
||||
Set<String> roleNames = new LinkedHashSet<>();
|
||||
/**
|
||||
* Returns stream of the given group and its parents (recursively).
|
||||
* @param group
|
||||
* @return
|
||||
*/
|
||||
private static Stream<GroupModel> groupAndItsParentsStream(GroupModel group) {
|
||||
Stream.Builder<GroupModel> sb = Stream.builder();
|
||||
while (group != null) {
|
||||
sb.add(group);
|
||||
group = group.getParent();
|
||||
}
|
||||
return sb.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively expands composite roles into their composite.
|
||||
* @param role
|
||||
* @return Stream of containing all of the composite roles and their components.
|
||||
*/
|
||||
private static Stream<RoleModel> expandCompositeRolesStream(RoleModel role) {
|
||||
Stream.Builder<RoleModel> sb = Stream.builder();
|
||||
|
||||
Deque<RoleModel> stack = new ArrayDeque<>();
|
||||
stack.add(role);
|
||||
|
||||
Deque<RoleModel> stack = new ArrayDeque<>(roleModels);
|
||||
while (! stack.isEmpty()) {
|
||||
|
||||
RoleModel current = stack.pop();
|
||||
sb.add(current);
|
||||
|
||||
if (current.isComposite()) {
|
||||
for (RoleModel compositeRoleModel : current.getComposites()) {
|
||||
stack.push(compositeRoleModel);
|
||||
stack.addAll(current.getComposites());
|
||||
}
|
||||
}
|
||||
|
||||
String roleName = current.getName();
|
||||
|
||||
if (prefix != null && !prefix.trim().isEmpty()) {
|
||||
roleName = prefix.trim() + roleName;
|
||||
return sb.build();
|
||||
}
|
||||
|
||||
roleNames.add(roleName);
|
||||
/**
|
||||
* Retrieves all roles of the current user based on direct roles set to the user, its groups and their parent groups.
|
||||
* Then it recursively expands all composite roles, and restricts according to the given predicate {@code restriction}.
|
||||
* If the current client sessions is restricted (i.e. no client found in active user session has full scope allowed),
|
||||
* the final list of roles is also restricted by the client scope. Finally, the list is mapped to the token into
|
||||
* a claim.
|
||||
*
|
||||
* @param token
|
||||
* @param mappingModel
|
||||
* @param userSession
|
||||
* @param restriction
|
||||
* @param prefix
|
||||
*/
|
||||
protected static void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession,
|
||||
Predicate<RoleModel> restriction, String prefix) {
|
||||
String rolePrefix = prefix == null ? "" : prefix;
|
||||
UserModel user = userSession.getUser();
|
||||
|
||||
// get a set of all realm roles assigned to the user or its group
|
||||
Stream<RoleModel> clientUserRoles = getAllUserRolesStream(user).filter(restriction);
|
||||
|
||||
boolean dontLimitScope = userSession.getClientSessions().stream().anyMatch(cs -> cs.getClient().isFullScopeAllowed());
|
||||
if (! dontLimitScope) {
|
||||
Set<RoleModel> clientRoles = userSession.getClientSessions().stream()
|
||||
.flatMap(cs -> cs.getClient().getScopeMappings().stream())
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
clientUserRoles = clientUserRoles.filter(clientRoles::contains);
|
||||
}
|
||||
|
||||
return roleNames;
|
||||
Set<String> realmRoleNames = clientUserRoles
|
||||
.map(m -> rolePrefix + m.getName())
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
OIDCAttributeMapperHelper.mapClaim(token, mappingModel, realmRoleNames);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,6 +100,9 @@ public class OIDCAttributeMapperHelper {
|
|||
if (attributeValue == null) return;
|
||||
|
||||
String protocolClaim = mappingModel.getConfig().get(TOKEN_CLAIM_NAME);
|
||||
if (protocolClaim == null) {
|
||||
return;
|
||||
}
|
||||
String[] split = protocolClaim.split("\\.");
|
||||
Map<String, Object> jsonObject = token.getOtherClaims();
|
||||
for (int i = 0; i < split.length; i++) {
|
||||
|
|
|
@ -18,17 +18,20 @@
|
|||
package org.keycloak.protocol.oidc.mappers;
|
||||
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientTemplateModel;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.protocol.ProtocolMapperUtils;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.representations.IDToken;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Allows mapping of user client role mappings to an ID and Access Token claim.
|
||||
|
@ -39,7 +42,7 @@ public class UserClientRoleMappingMapper extends AbstractUserRoleMappingMapper {
|
|||
|
||||
public static final String PROVIDER_ID = "oidc-usermodel-client-role-mapper";
|
||||
|
||||
private static final List<ProviderConfigProperty> CONFIG_PROPERTIES = new ArrayList<ProviderConfigProperty>();
|
||||
private static final List<ProviderConfigProperty> CONFIG_PROPERTIES = new ArrayList<>();
|
||||
|
||||
static {
|
||||
|
||||
|
@ -60,6 +63,7 @@ public class UserClientRoleMappingMapper extends AbstractUserRoleMappingMapper {
|
|||
OIDCAttributeMapperHelper.addAttributeConfig(CONFIG_PROPERTIES, UserClientRoleMappingMapper.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return CONFIG_PROPERTIES;
|
||||
}
|
||||
|
@ -84,23 +88,51 @@ public class UserClientRoleMappingMapper extends AbstractUserRoleMappingMapper {
|
|||
return "Map a user client role to a token claim.";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession) {
|
||||
|
||||
UserModel user = userSession.getUser();
|
||||
|
||||
String clientId = mappingModel.getConfig().get(ProtocolMapperUtils.USER_MODEL_CLIENT_ROLE_MAPPING_CLIENT_ID);
|
||||
if (clientId != null) {
|
||||
|
||||
ClientModel clientModel = userSession.getRealm().getClientByClientId(clientId.trim());
|
||||
Set<RoleModel> clientRoleMappings = user.getClientRoleMappings(clientModel);
|
||||
|
||||
String rolePrefix = mappingModel.getConfig().get(ProtocolMapperUtils.USER_MODEL_CLIENT_ROLE_MAPPING_ROLE_PREFIX);
|
||||
Set<String> clientRoleNames = flattenRoleModelToRoleNames(clientRoleMappings, rolePrefix);
|
||||
|
||||
OIDCAttributeMapperHelper.mapClaim(token, mappingModel, clientRoleNames);
|
||||
setClaim(token, mappingModel, userSession, getClientRoleFilter(clientId, userSession), rolePrefix);
|
||||
}
|
||||
|
||||
private static Predicate<RoleModel> getClientRoleFilter(String clientId, UserSessionModel userSession) {
|
||||
if (clientId == null) {
|
||||
return RoleModel::isClientRole;
|
||||
}
|
||||
|
||||
RealmModel clientRealm = userSession.getRealm();
|
||||
ClientModel client = clientRealm.getClientByClientId(clientId.trim());
|
||||
|
||||
if (client == null) {
|
||||
return RoleModel::isClientRole;
|
||||
}
|
||||
|
||||
ClientTemplateModel template = client.getClientTemplate();
|
||||
boolean useTemplateScope = template != null && client.useTemplateScope();
|
||||
boolean fullScopeAllowed = (useTemplateScope && template.isFullScopeAllowed()) || client.isFullScopeAllowed();
|
||||
|
||||
Set<RoleModel> clientRoleMappings = client.getRoles();
|
||||
if (fullScopeAllowed) {
|
||||
return clientRoleMappings::contains;
|
||||
}
|
||||
|
||||
Set<RoleModel> scopeMappings = new HashSet<>();
|
||||
|
||||
if (useTemplateScope) {
|
||||
Set<RoleModel> templateScopeMappings = template.getScopeMappings();
|
||||
if (templateScopeMappings != null) {
|
||||
scopeMappings.addAll(templateScopeMappings);
|
||||
}
|
||||
}
|
||||
|
||||
Set<RoleModel> clientScopeMappings = client.getScopeMappings();
|
||||
if (clientScopeMappings != null) {
|
||||
scopeMappings.addAll(clientScopeMappings);
|
||||
}
|
||||
|
||||
return role -> clientRoleMappings.contains(role) && scopeMappings.contains(role);
|
||||
}
|
||||
|
||||
public static ProtocolMapperModel create(String clientId, String clientRolePrefix,
|
||||
String name,
|
||||
|
|
|
@ -18,18 +18,13 @@
|
|||
package org.keycloak.protocol.oidc.mappers;
|
||||
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.protocol.ProtocolMapperUtils;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.representations.IDToken;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Allows mapping of user realm role mappings to an ID and Access Token claim.
|
||||
|
@ -40,7 +35,7 @@ public class UserRealmRoleMappingMapper extends AbstractUserRoleMappingMapper {
|
|||
|
||||
public static final String PROVIDER_ID = "oidc-usermodel-realm-role-mapper";
|
||||
|
||||
private static final List<ProviderConfigProperty> CONFIG_PROPERTIES = new ArrayList<ProviderConfigProperty>();
|
||||
private static final List<ProviderConfigProperty> CONFIG_PROPERTIES = new ArrayList<>();
|
||||
|
||||
static {
|
||||
|
||||
|
@ -54,6 +49,7 @@ public class UserRealmRoleMappingMapper extends AbstractUserRoleMappingMapper {
|
|||
OIDCAttributeMapperHelper.addAttributeConfig(CONFIG_PROPERTIES, UserRealmRoleMappingMapper.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return CONFIG_PROPERTIES;
|
||||
}
|
||||
|
@ -78,17 +74,12 @@ public class UserRealmRoleMappingMapper extends AbstractUserRoleMappingMapper {
|
|||
return "Map a user realm role to a token claim.";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession) {
|
||||
|
||||
UserModel user = userSession.getUser();
|
||||
|
||||
String rolePrefix = mappingModel.getConfig().get(ProtocolMapperUtils.USER_MODEL_REALM_ROLE_MAPPING_ROLE_PREFIX);
|
||||
Set<String> realmRoleNames = flattenRoleModelToRoleNames(user.getRealmRoleMappings(), rolePrefix);
|
||||
|
||||
OIDCAttributeMapperHelper.mapClaim(token, mappingModel, realmRoleNames);
|
||||
AbstractUserRoleMappingMapper.setClaim(token, mappingModel, userSession, role -> ! role.isClientRole(), rolePrefix);
|
||||
}
|
||||
|
||||
|
||||
public static ProtocolMapperModel create(String realmRolePrefix,
|
||||
String name,
|
||||
String tokenClaimName, boolean accessToken, boolean idToken) {
|
||||
|
|
|
@ -817,7 +817,7 @@ public class UserTest extends AbstractAdminTest {
|
|||
|
||||
// List realm roles
|
||||
assertNames(roles.realmLevel().listAll(), "realm-role", "realm-composite", "user", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION);
|
||||
assertNames(roles.realmLevel().listAvailable(), "admin", "customer-user-premium");
|
||||
assertNames(roles.realmLevel().listAvailable(), "admin", "customer-user-premium", "realm-composite-role", "sample-realm-role");
|
||||
assertNames(roles.realmLevel().listEffective(), "realm-role", "realm-composite", "realm-child", "user", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION);
|
||||
|
||||
// List client roles
|
||||
|
|
|
@ -438,7 +438,7 @@ public class GroupTest extends AbstractGroupTest {
|
|||
|
||||
// List realm roles
|
||||
assertNames(roles.realmLevel().listAll(), "realm-role", "realm-composite");
|
||||
assertNames(roles.realmLevel().listAvailable(), "admin", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION, "user", "customer-user-premium");
|
||||
assertNames(roles.realmLevel().listAvailable(), "admin", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION, "user", "customer-user-premium", "realm-composite-role", "sample-realm-role");
|
||||
assertNames(roles.realmLevel().listEffective(), "realm-role", "realm-composite", "realm-child");
|
||||
|
||||
// List client roles
|
||||
|
|
|
@ -43,10 +43,8 @@ import org.keycloak.testsuite.util.ClientManager;
|
|||
import org.keycloak.testsuite.util.OAuthClient;
|
||||
import org.keycloak.testsuite.util.ProtocolMapperUtil;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
|
||||
import static org.keycloak.testsuite.admin.ApiUtil.findClientByClientId;
|
||||
import static org.keycloak.testsuite.admin.ApiUtil.findClientResourceByClientId;
|
||||
|
@ -222,11 +220,152 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
|
|||
|
||||
// Verify attribute is filled
|
||||
Map<String, Object> roleMappings = (Map<String, Object>)idToken.getOtherClaims().get("roles-custom");
|
||||
Assert.assertEquals(2, roleMappings.size());
|
||||
Assert.assertThat(roleMappings.keySet(), containsInAnyOrder("realm", "test-app"));
|
||||
String realmRoleMappings = (String) roleMappings.get("realm");
|
||||
String testAppMappings = (String) roleMappings.get("test-app");
|
||||
Assert.assertTrue(realmRoleMappings.contains("pref.user"));
|
||||
Assert.assertEquals("[customer-user]", testAppMappings);
|
||||
assertRoles(realmRoleMappings,
|
||||
"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
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testUserGroupRoleToAttributeMappers() throws Exception {
|
||||
// Add mapper for realm roles
|
||||
String clientId = "test-app";
|
||||
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true);
|
||||
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(clientId, "ta.", "Client roles mapper", "roles-custom.test-app", true, true);
|
||||
|
||||
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), clientId).getProtocolMappers();
|
||||
protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper));
|
||||
|
||||
// Login user
|
||||
OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("password", "rich.roles@redhat.com", "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", 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
|
||||
);
|
||||
assertRoles(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
|
||||
"ta.sample-client-role" // from realm role realm-composite-role - client role for test-app
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUserGroupRoleToAttributeMappersNotScopedOtherApp() throws Exception {
|
||||
String clientId = "test-app-authz";
|
||||
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true);
|
||||
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(clientId, null, "Client roles mapper", "roles-custom." + clientId, true, true);
|
||||
|
||||
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), clientId).getProtocolMappers();
|
||||
protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper));
|
||||
|
||||
// Login user
|
||||
ClientManager.realm(adminClient.realm("test")).clientId(clientId).directAccessGrant(true);
|
||||
oauth.clientId(clientId);
|
||||
OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("secret", "rich.roles@redhat.com", "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", 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
|
||||
);
|
||||
assertRoles(testAppAuthzMappings); // There is no client role defined for test-app-authz
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUserGroupRoleToAttributeMappersScoped() throws Exception {
|
||||
String clientId = "test-app-scope";
|
||||
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true);
|
||||
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(clientId, null, "Client roles mapper", "roles-custom.test-app-scope", true, true);
|
||||
|
||||
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), clientId).getProtocolMappers();
|
||||
protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper));
|
||||
|
||||
// Login user
|
||||
ClientManager.realm(adminClient.realm("test")).clientId(clientId).directAccessGrant(true);
|
||||
oauth.clientId(clientId);
|
||||
OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("password", "rich.roles@redhat.com", "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", 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
|
||||
);
|
||||
assertRoles(testAppScopeMappings,
|
||||
"test-app-allowed-by-scope" // from direct assignment to roleRichUser, present as scope allows it
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUserGroupRoleToAttributeMappersScopedClientNotSet() throws Exception {
|
||||
String clientId = "test-app-scope";
|
||||
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true);
|
||||
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(null, null, "Client roles mapper", "roles-custom.test-app-scope", true, true);
|
||||
|
||||
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), clientId).getProtocolMappers();
|
||||
protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper));
|
||||
|
||||
// Login user
|
||||
ClientManager.realm(adminClient.realm("test")).clientId(clientId).directAccessGrant(true);
|
||||
oauth.clientId(clientId);
|
||||
OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("password", "rich.roles@redhat.com", "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", 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
|
||||
);
|
||||
assertRoles(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
|
||||
);
|
||||
}
|
||||
|
||||
private void assertRoles(String actualRoleString, String...expectedRoles) {
|
||||
String[] roles;
|
||||
Assert.assertThat(actualRoleString.matches("^\\[.*\\]$"), is(true));
|
||||
roles = actualRoleString.substring(1, actualRoleString.length() - 1).split(",\\s*");
|
||||
|
||||
if (expectedRoles == null || expectedRoles.length == 0) {
|
||||
Assert.assertThat(roles, arrayContainingInAnyOrder(""));
|
||||
} else {
|
||||
Assert.assertThat(roles, arrayContainingInAnyOrder(expectedRoles));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -85,6 +85,21 @@
|
|||
"groups": [
|
||||
"/topGroup/level2group"
|
||||
]
|
||||
},
|
||||
{
|
||||
"username" : "roleRichUser",
|
||||
"enabled": true,
|
||||
"email" : "rich.roles@redhat.com",
|
||||
"credentials" : [
|
||||
{ "type" : "password",
|
||||
"value" : "password" }
|
||||
],
|
||||
"groups": [
|
||||
"/roleRichGroup/level2group"
|
||||
],
|
||||
"clientRoles": {
|
||||
"test-app-scope": [ "test-app-allowed-by-scope", "test-app-disallowed-by-scope" ]
|
||||
}
|
||||
}
|
||||
],
|
||||
"scopeMappings": [
|
||||
|
@ -95,6 +110,10 @@
|
|||
{
|
||||
"client": "test-app",
|
||||
"roles": ["user"]
|
||||
},
|
||||
{
|
||||
"client": "test-app-scope",
|
||||
"roles": ["user", "admin"]
|
||||
}
|
||||
],
|
||||
"clients": [
|
||||
|
@ -108,6 +127,16 @@
|
|||
"adminUrl": "http://localhost:8180/auth/realms/master/app/admin",
|
||||
"secret": "password"
|
||||
},
|
||||
{
|
||||
"clientId" : "test-app-scope",
|
||||
"enabled": true,
|
||||
|
||||
"redirectUris": [
|
||||
"http://localhost:8180/auth/realms/master/app/*"
|
||||
],
|
||||
"secret": "password",
|
||||
"fullScopeAllowed": "false"
|
||||
},
|
||||
{
|
||||
"clientId" : "third-party",
|
||||
"enabled": true,
|
||||
|
@ -290,6 +319,22 @@
|
|||
{
|
||||
"name": "customer-user-premium",
|
||||
"description": "Have User Premium privileges"
|
||||
},
|
||||
{
|
||||
"name": "sample-realm-role",
|
||||
"description": "Sample realm role"
|
||||
},
|
||||
{
|
||||
"name": "realm-composite-role",
|
||||
"description": "Realm composite role containing client role",
|
||||
"composite" : true,
|
||||
"composites" : {
|
||||
"realm" : [ "sample-realm-role" ],
|
||||
"client" : {
|
||||
"test-app" : [ "sample-client-role" ],
|
||||
"account" : [ "view-profile" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"client" : {
|
||||
|
@ -301,6 +346,31 @@
|
|||
{
|
||||
"name": "customer-admin",
|
||||
"description": "Have Customer Admin privileges"
|
||||
},
|
||||
{
|
||||
"name": "sample-client-role",
|
||||
"description": "Sample client role"
|
||||
},
|
||||
{
|
||||
"name": "customer-admin-composite-role",
|
||||
"description": "Have Customer Admin privileges via composite role",
|
||||
"composite" : true,
|
||||
"composites" : {
|
||||
"realm" : [ "customer-user-premium" ],
|
||||
"client" : {
|
||||
"test-app" : [ "customer-admin" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"test-app-scope" : [
|
||||
{
|
||||
"name": "test-app-allowed-by-scope",
|
||||
"description": "Role allowed by scope in test-app-scope"
|
||||
},
|
||||
{
|
||||
"name": "test-app-disallowed-by-scope",
|
||||
"description": "Role disallowed by scope in test-app-scope"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -325,6 +395,31 @@
|
|||
"attributes": {
|
||||
"level2Attribute": ["true"]
|
||||
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "roleRichGroup",
|
||||
"attributes": {
|
||||
"topAttribute": ["true"]
|
||||
|
||||
},
|
||||
"realmRoles": ["user", "realm-composite-role"],
|
||||
"clientRoles": {
|
||||
"account": ["manage-account"]
|
||||
},
|
||||
|
||||
"subGroups": [
|
||||
{
|
||||
"name": "level2group",
|
||||
"realmRoles": ["admin"],
|
||||
"clientRoles": {
|
||||
"test-app": ["customer-user", "customer-admin-composite-role"]
|
||||
},
|
||||
"attributes": {
|
||||
"level2Attribute": ["true"]
|
||||
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -337,6 +432,16 @@
|
|||
{
|
||||
"client": "third-party",
|
||||
"roles": ["customer-user"]
|
||||
},
|
||||
{
|
||||
"client": "test-app-scope",
|
||||
"roles": ["customer-admin-composite-role"]
|
||||
}
|
||||
],
|
||||
"test-app-scope": [
|
||||
{
|
||||
"client": "test-app-scope",
|
||||
"roles": ["test-app-allowed-by-scope"]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue