diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/group-members.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/group-members.html index 6c20930275..50bc11b008 100755 --- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/group-members.html +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/group-members.html @@ -14,7 +14,7 @@ Last Name First Name Email - Actions + diff --git a/model/api/src/main/java/org/keycloak/models/UserFederationManager.java b/model/api/src/main/java/org/keycloak/models/UserFederationManager.java index d75ed95136..1a1709b8dd 100755 --- a/model/api/src/main/java/org/keycloak/models/UserFederationManager.java +++ b/model/api/src/main/java/org/keycloak/models/UserFederationManager.java @@ -462,6 +462,9 @@ public class UserFederationManager implements UserProvider { } + + + @Override public boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input) { return validCredentials(realm, user, Arrays.asList(input)); diff --git a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java index c2fd73e7d9..ba91ee6b27 100755 --- a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java +++ b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java @@ -30,6 +30,7 @@ import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.cert.X509Certificate; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -386,4 +387,51 @@ public final class KeycloakModelUtils { realm.addDefaultRole(Constants.OFFLINE_ACCESS_ROLE); } } + + public static String resolveFirstAttribute(GroupModel group, String name) { + String value = group.getFirstAttribute(name); + if (value != null) return value; + if (group.getParentId() == null) return null; + return resolveFirstAttribute(group.getParent(), name); + + } + + /** + * + * + * @param user + * @param name + * @return + */ + public static String resolveFirstAttribute(UserModel user, String name) { + String value = user.getFirstAttribute(name); + if (value != null) return value; + for (GroupModel group : user.getGroups()) { + value = resolveFirstAttribute(group, name); + if (value != null) return value; + } + return null; + + } + + public static List resolveAttribute(GroupModel group, String name) { + List values = group.getAttribute(name); + if (!values.isEmpty()) return values; + if (group.getParentId() == null) return null; + return resolveAttribute(group.getParent(), name); + + } + + + public static List resolveAttribute(UserModel user, String name) { + List values = user.getAttribute(name); + if (!values.isEmpty()) return values; + for (GroupModel group : user.getGroups()) { + values = resolveAttribute(group, name); + if (values != null) return values; + } + return Collections.emptyList(); + } + + } diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/GroupAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/GroupAdapter.java index 209d2ae694..0fb9c2f22e 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/GroupAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/GroupAdapter.java @@ -312,9 +312,9 @@ public class GroupAdapter implements GroupModel { @Override public boolean equals(Object o) { if (this == o) return true; - if (o == null || !(o instanceof UserModel)) return false; + if (o == null || !(o instanceof GroupModel)) return false; - UserModel that = (UserModel) o; + GroupModel that = (GroupModel) o; return that.getId().equals(getId()); } diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/UserAttributeStatementMapper.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/UserAttributeStatementMapper.java index dc80760747..8f5e3b80d5 100755 --- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/UserAttributeStatementMapper.java +++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/UserAttributeStatementMapper.java @@ -5,6 +5,7 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionModel; +import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.protocol.ProtocolMapperUtils; import org.keycloak.dom.saml.v2.assertion.AttributeStatementType; import org.keycloak.provider.ProviderConfigProperty; @@ -62,7 +63,7 @@ public class UserAttributeStatementMapper extends AbstractSAMLProtocolMapper imp public void transformAttributeStatement(AttributeStatementType attributeStatement, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) { UserModel user = userSession.getUser(); String attributeName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_ATTRIBUTE); - String attributeValue = user.getFirstAttribute(attributeName); + String attributeValue = KeycloakModelUtils.resolveFirstAttribute(user, attributeName); if (attributeValue == null) return; AttributeStatementHelper.addAttribute(attributeStatement, mappingModel, attributeValue); diff --git a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java index 17d5b230f4..32a9a6df24 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java @@ -12,6 +12,7 @@ import org.keycloak.jose.jws.JWSInput; import org.keycloak.jose.jws.crypto.RSAProvider; import org.keycloak.models.ClientModel; import org.keycloak.models.ClientSessionModel; +import org.keycloak.models.GroupModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.ProtocolMapperModel; @@ -289,10 +290,23 @@ public class TokenManager { } } + public static void addGroupRoles(GroupModel group, Set roleMappings) { + roleMappings.addAll(group.getRoleMappings()); + if (group.getParentId() == null) return; + addGroupRoles(group.getParent(), roleMappings); + } + public static Set getAccess(String scopeParam, boolean applyScopeParam, ClientModel client, UserModel user) { Set requestedRoles = new HashSet(); - Set roleMappings = user.getRoleMappings(); + Set mappings = user.getRoleMappings(); + Set roleMappings = new HashSet<>(); + roleMappings.addAll(mappings); + for (GroupModel group : user.getGroups()) { + addGroupRoles(group, roleMappings); + } + + if (client.isFullScopeAllowed()) { requestedRoles = roleMappings; diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserAttributeMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserAttributeMapper.java index 246b82ee75..3e96692f59 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserAttributeMapper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserAttributeMapper.java @@ -6,6 +6,7 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionModel; +import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.protocol.ProtocolMapperUtils; import org.keycloak.provider.ProviderConfigProperty; import org.keycloak.representations.AccessToken; @@ -84,7 +85,7 @@ public class UserAttributeMapper extends AbstractOIDCProtocolMapper implements O protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession) { UserModel user = userSession.getUser(); String attributeName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_ATTRIBUTE); - List attributeValue = user.getAttribute(attributeName); + List attributeValue = KeycloakModelUtils.resolveAttribute(user, attributeName); if (attributeValue == null) return; OIDCAttributeMapperHelper.mapClaim(token, mappingModel, attributeValue); }