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);
}