groups initial
This commit is contained in:
parent
0d05d38eb6
commit
d896800ec6
52 changed files with 2406 additions and 2 deletions
81
core/src/main/java/org/keycloak/representations/idm/GroupRepresentation.java
Executable file
81
core/src/main/java/org/keycloak/representations/idm/GroupRepresentation.java
Executable file
|
@ -0,0 +1,81 @@
|
||||||
|
package org.keycloak.representations.idm;
|
||||||
|
|
||||||
|
import org.codehaus.jackson.annotate.JsonIgnore;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class GroupRepresentation {
|
||||||
|
private String id;
|
||||||
|
private String name;
|
||||||
|
protected Map<String, Object> attributes;
|
||||||
|
protected List<String> realmRoles;
|
||||||
|
protected Map<String, List<String>> clientRoles;
|
||||||
|
protected List<GroupRepresentation> subGroups;
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getRealmRoles() {
|
||||||
|
return realmRoles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRealmRoles(List<String> realmRoles) {
|
||||||
|
this.realmRoles = realmRoles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, List<String>> getClientRoles() {
|
||||||
|
return clientRoles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClientRoles(Map<String, List<String>> clientRoles) {
|
||||||
|
this.clientRoles = clientRoles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> getAttributes() {
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method can be removed once we can remove backwards compatibility with Keycloak 1.3 (then getAttributes() can be changed to return Map<String, List<String>> )
|
||||||
|
@JsonIgnore
|
||||||
|
public Map<String, List<String>> getAttributesAsListValues() {
|
||||||
|
return (Map) attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttributes(Map<String, Object> attributes) {
|
||||||
|
this.attributes = attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GroupRepresentation singleAttribute(String name, String value) {
|
||||||
|
if (this.attributes == null) attributes = new HashMap<>();
|
||||||
|
attributes.put(name, Arrays.asList(value));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<GroupRepresentation> getSubGroups() {
|
||||||
|
return subGroups;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSubGroups(List<GroupRepresentation> subGroups) {
|
||||||
|
this.subGroups = subGroups;
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,6 +47,7 @@ public class RealmRepresentation {
|
||||||
protected String certificate;
|
protected String certificate;
|
||||||
protected String codeSecret;
|
protected String codeSecret;
|
||||||
protected RolesRepresentation roles;
|
protected RolesRepresentation roles;
|
||||||
|
protected List<GroupRepresentation> groups;
|
||||||
protected List<String> defaultRoles;
|
protected List<String> defaultRoles;
|
||||||
@Deprecated
|
@Deprecated
|
||||||
protected Set<String> requiredCredentials;
|
protected Set<String> requiredCredentials;
|
||||||
|
@ -775,4 +776,12 @@ public class RealmRepresentation {
|
||||||
public void setClientAuthenticationFlow(String clientAuthenticationFlow) {
|
public void setClientAuthenticationFlow(String clientAuthenticationFlow) {
|
||||||
this.clientAuthenticationFlow = clientAuthenticationFlow;
|
this.clientAuthenticationFlow = clientAuthenticationFlow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<GroupRepresentation> getGroups() {
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGroups(List<GroupRepresentation> groups) {
|
||||||
|
this.groups = groups;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.keycloak.examples.federation.properties;
|
package org.keycloak.examples.federation.properties;
|
||||||
|
|
||||||
import org.keycloak.models.CredentialValidationOutput;
|
import org.keycloak.models.CredentialValidationOutput;
|
||||||
|
import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
|
@ -106,6 +107,12 @@ public abstract class BasePropertiesFederationProvider implements UserFederation
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void preRemove(RealmModel realm, GroupModel group) {
|
||||||
|
// complete we dont'care if a role is removed
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See if the user is still in the properties file
|
* See if the user is still in the properties file
|
||||||
*
|
*
|
||||||
|
|
|
@ -63,4 +63,6 @@ public class ClasspathPropertiesFederationProvider extends BasePropertiesFederat
|
||||||
throw new IllegalStateException("Remove not supported");
|
throw new IllegalStateException("Remove not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
6
federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java
Normal file → Executable file
6
federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java
Normal file → Executable file
|
@ -12,6 +12,7 @@ import org.jboss.logging.Logger;
|
||||||
import org.keycloak.federation.kerberos.impl.KerberosUsernamePasswordAuthenticator;
|
import org.keycloak.federation.kerberos.impl.KerberosUsernamePasswordAuthenticator;
|
||||||
import org.keycloak.federation.kerberos.impl.SPNEGOAuthenticator;
|
import org.keycloak.federation.kerberos.impl.SPNEGOAuthenticator;
|
||||||
import org.keycloak.models.CredentialValidationOutput;
|
import org.keycloak.models.CredentialValidationOutput;
|
||||||
|
import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
|
@ -105,6 +106,11 @@ public class KerberosFederationProvider implements UserFederationProvider {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void preRemove(RealmModel realm, GroupModel group) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isValid(RealmModel realm, UserModel local) {
|
public boolean isValid(RealmModel realm, UserModel local) {
|
||||||
// KerberosUsernamePasswordAuthenticator.isUserAvailable is an overhead, so avoid it for now
|
// KerberosUsernamePasswordAuthenticator.isUserAvailable is an overhead, so avoid it for now
|
||||||
|
|
|
@ -12,6 +12,7 @@ import org.keycloak.federation.ldap.idm.store.ldap.LDAPIdentityStore;
|
||||||
import org.keycloak.federation.ldap.kerberos.LDAPProviderKerberosConfig;
|
import org.keycloak.federation.ldap.kerberos.LDAPProviderKerberosConfig;
|
||||||
import org.keycloak.federation.ldap.mappers.LDAPFederationMapper;
|
import org.keycloak.federation.ldap.mappers.LDAPFederationMapper;
|
||||||
import org.keycloak.models.CredentialValidationOutput;
|
import org.keycloak.models.CredentialValidationOutput;
|
||||||
|
import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.LDAPConstants;
|
import org.keycloak.models.LDAPConstants;
|
||||||
import org.keycloak.models.ModelDuplicateException;
|
import org.keycloak.models.ModelDuplicateException;
|
||||||
|
@ -319,6 +320,11 @@ public class LDAPFederationProvider implements UserFederationProvider {
|
||||||
// TODO: Maybe mappers callback to ensure role deletion propagated to LDAP by RoleLDAPFederationMapper?
|
// TODO: Maybe mappers callback to ensure role deletion propagated to LDAP by RoleLDAPFederationMapper?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void preRemove(RealmModel realm, GroupModel group) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public boolean validPassword(RealmModel realm, UserModel user, String password) {
|
public boolean validPassword(RealmModel realm, UserModel user, String password) {
|
||||||
if (kerberosConfig.isAllowKerberosAuthentication() && kerberosConfig.isUseKerberosForPasswordAuthentication()) {
|
if (kerberosConfig.isAllowKerberosAuthentication() && kerberosConfig.isUseKerberosForPasswordAuthentication()) {
|
||||||
// Use Kerberos JAAS (Krb5LoginModule)
|
// Use Kerberos JAAS (Krb5LoginModule)
|
||||||
|
|
|
@ -57,7 +57,7 @@ public class RefreshableKeycloakSecurityContext extends KeycloakSecurityContext
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isActive() {
|
public boolean isActive() {
|
||||||
return this.token.isActive() && this.token.getIssuedAt() > deployment.getNotBefore();
|
return token != null && this.token.isActive() && this.token.getIssuedAt() > deployment.getNotBefore();
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeycloakDeployment getDeployment() {
|
public KeycloakDeployment getDeployment() {
|
||||||
|
@ -111,6 +111,7 @@ public class RefreshableKeycloakSecurityContext extends KeycloakSecurityContext
|
||||||
log.debug("Token Verification succeeded!");
|
log.debug("Token Verification succeeded!");
|
||||||
} catch (VerificationException e) {
|
} catch (VerificationException e) {
|
||||||
log.error("failed verification of token");
|
log.error("failed verification of token");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
if (response.getNotBeforePolicy() > deployment.getNotBefore()) {
|
if (response.getNotBeforePolicy() > deployment.getNotBefore()) {
|
||||||
deployment.setNotBefore(response.getNotBeforePolicy());
|
deployment.setNotBefore(response.getNotBeforePolicy());
|
||||||
|
|
74
model/api/src/main/java/org/keycloak/models/GroupModel.java
Executable file
74
model/api/src/main/java/org/keycloak/models/GroupModel.java
Executable file
|
@ -0,0 +1,74 @@
|
||||||
|
package org.keycloak.models;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public interface GroupModel {
|
||||||
|
String getId();
|
||||||
|
|
||||||
|
String getName();
|
||||||
|
|
||||||
|
void setName(String name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set single value of specified attribute. Remove all other existing values
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
void setSingleAttribute(String name, String value);
|
||||||
|
|
||||||
|
void setAttribute(String name, List<String> values);
|
||||||
|
|
||||||
|
void removeAttribute(String name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param name
|
||||||
|
* @return null if there is not any value of specified attribute or first value otherwise. Don't throw exception if there are more values of the attribute
|
||||||
|
*/
|
||||||
|
String getFirstAttribute(String name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param name
|
||||||
|
* @return list of all attribute values or empty list if there are not any values. Never return null
|
||||||
|
*/
|
||||||
|
List<String> getAttribute(String name);
|
||||||
|
|
||||||
|
Map<String, List<String>> getAttributes();
|
||||||
|
|
||||||
|
Set<RoleModel> getRealmRoleMappings();
|
||||||
|
Set<RoleModel> getClientRoleMappings(ClientModel app);
|
||||||
|
boolean hasRole(RoleModel role);
|
||||||
|
void grantRole(RoleModel role);
|
||||||
|
Set<RoleModel> getRoleMappings();
|
||||||
|
void deleteRoleMapping(RoleModel role);
|
||||||
|
|
||||||
|
GroupModel getParent();
|
||||||
|
Set<GroupModel> getSubGroups();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* You must also call joinGroup on the parent group.
|
||||||
|
*
|
||||||
|
* @param group
|
||||||
|
*/
|
||||||
|
void setParent(GroupModel group);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Automatically calls setParent() on the subGroup
|
||||||
|
*
|
||||||
|
* @param subGroup
|
||||||
|
*/
|
||||||
|
void addChild(GroupModel subGroup);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Automatically calls setParent() on the subGroup
|
||||||
|
*
|
||||||
|
* @param subGroup
|
||||||
|
*/
|
||||||
|
void removeChild(GroupModel subGroup);
|
||||||
|
}
|
|
@ -328,4 +328,12 @@ public interface RealmModel extends RoleContainerModel {
|
||||||
void setSupportedLocales(Set<String> locales);
|
void setSupportedLocales(Set<String> locales);
|
||||||
String getDefaultLocale();
|
String getDefaultLocale();
|
||||||
void setDefaultLocale(String locale);
|
void setDefaultLocale(String locale);
|
||||||
|
|
||||||
|
GroupModel getGroupById(String id);
|
||||||
|
List<GroupModel> getGroups();
|
||||||
|
List<GroupModel> getTopLevelGroups();
|
||||||
|
boolean removeGroup(GroupModel group);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,8 +20,11 @@ public interface RealmProvider extends Provider {
|
||||||
|
|
||||||
RoleModel getRoleById(String id, RealmModel realm);
|
RoleModel getRoleById(String id, RealmModel realm);
|
||||||
ClientModel getClientById(String id, RealmModel realm);
|
ClientModel getClientById(String id, RealmModel realm);
|
||||||
|
GroupModel getGroupById(String id, RealmModel realm);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
List<RealmModel> getRealms();
|
List<RealmModel> getRealms();
|
||||||
boolean removeRealm(String id);
|
boolean removeRealm(String id);
|
||||||
|
|
||||||
void close();
|
void close();
|
||||||
}
|
}
|
||||||
|
|
|
@ -165,6 +165,16 @@ public class UserFederationManager implements UserProvider {
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<UserModel> getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) {
|
||||||
|
return session.userStorage().getGroupMembers(realm, group, firstResult, maxResults);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<UserModel> getGroupMembers(RealmModel realm, GroupModel group) {
|
||||||
|
return getGroupMembers(realm, group, -1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserModel getUserByUsername(String username, RealmModel realm) {
|
public UserModel getUserByUsername(String username, RealmModel realm) {
|
||||||
UserModel user = session.userStorage().getUserByUsername(username.toLowerCase(), realm);
|
UserModel user = session.userStorage().getUserByUsername(username.toLowerCase(), realm);
|
||||||
|
@ -347,6 +357,16 @@ public class UserFederationManager implements UserProvider {
|
||||||
session.userStorage().preRemove(realm, model);
|
session.userStorage().preRemove(realm, model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void preRemove(RealmModel realm, GroupModel group) {
|
||||||
|
for (UserFederationProviderModel federation : realm.getUserFederationProviders()) {
|
||||||
|
UserFederationProvider fed = getFederationProvider(federation);
|
||||||
|
fed.preRemove(realm, group);
|
||||||
|
}
|
||||||
|
session.userStorage().preRemove(realm, group);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void preRemove(RealmModel realm, RoleModel role) {
|
public void preRemove(RealmModel realm, RoleModel role) {
|
||||||
for (UserFederationProviderModel federation : realm.getUserFederationProviders()) {
|
for (UserFederationProviderModel federation : realm.getUserFederationProviders()) {
|
||||||
|
|
|
@ -119,6 +119,14 @@ public interface UserFederationProvider extends Provider {
|
||||||
*/
|
*/
|
||||||
void preRemove(RealmModel realm, RoleModel role);
|
void preRemove(RealmModel realm, RoleModel role);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* called before a role is removed.
|
||||||
|
*
|
||||||
|
* @param realm
|
||||||
|
* @param group
|
||||||
|
*/
|
||||||
|
void preRemove(RealmModel realm, GroupModel group);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is the Keycloak UserModel still valid and/or existing in federated storage? Keycloak may call this method
|
* Is the Keycloak UserModel still valid and/or existing in federated storage? Keycloak may call this method
|
||||||
* in various user operations. The local storage may be deleted if this method returns false.
|
* in various user operations. The local storage may be deleted if this method returns false.
|
||||||
|
|
|
@ -101,6 +101,11 @@ public interface UserModel {
|
||||||
Set<RoleModel> getRoleMappings();
|
Set<RoleModel> getRoleMappings();
|
||||||
void deleteRoleMapping(RoleModel role);
|
void deleteRoleMapping(RoleModel role);
|
||||||
|
|
||||||
|
Set<GroupModel> getGroups();
|
||||||
|
void joinGroup(GroupModel group);
|
||||||
|
void leaveGroup(GroupModel group);
|
||||||
|
boolean isMemberOf(GroupModel group);
|
||||||
|
|
||||||
String getFederationLink();
|
String getFederationLink();
|
||||||
void setFederationLink(String link);
|
void setFederationLink(String link);
|
||||||
|
|
||||||
|
|
|
@ -25,12 +25,16 @@ public interface UserProvider extends Provider {
|
||||||
UserModel getUserById(String id, RealmModel realm);
|
UserModel getUserById(String id, RealmModel realm);
|
||||||
UserModel getUserByUsername(String username, RealmModel realm);
|
UserModel getUserByUsername(String username, RealmModel realm);
|
||||||
UserModel getUserByEmail(String email, RealmModel realm);
|
UserModel getUserByEmail(String email, RealmModel realm);
|
||||||
|
|
||||||
|
List<UserModel> getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults);
|
||||||
|
|
||||||
UserModel getUserByFederatedIdentity(FederatedIdentityModel socialLink, RealmModel realm);
|
UserModel getUserByFederatedIdentity(FederatedIdentityModel socialLink, RealmModel realm);
|
||||||
UserModel getUserByServiceAccountClient(ClientModel client);
|
UserModel getUserByServiceAccountClient(ClientModel client);
|
||||||
List<UserModel> getUsers(RealmModel realm, boolean includeServiceAccounts);
|
List<UserModel> getUsers(RealmModel realm, boolean includeServiceAccounts);
|
||||||
|
|
||||||
// Service account is included for counts
|
// Service account is included for counts
|
||||||
int getUsersCount(RealmModel realm);
|
int getUsersCount(RealmModel realm);
|
||||||
|
List<UserModel> getGroupMembers(RealmModel realm, GroupModel group);
|
||||||
List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults, boolean includeServiceAccounts);
|
List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults, boolean includeServiceAccounts);
|
||||||
List<UserModel> searchForUser(String search, RealmModel realm);
|
List<UserModel> searchForUser(String search, RealmModel realm);
|
||||||
List<UserModel> searchForUser(String search, RealmModel realm, int firstResult, int maxResults);
|
List<UserModel> searchForUser(String search, RealmModel realm, int firstResult, int maxResults);
|
||||||
|
@ -48,6 +52,7 @@ public interface UserProvider extends Provider {
|
||||||
void preRemove(RealmModel realm, UserFederationProviderModel link);
|
void preRemove(RealmModel realm, UserFederationProviderModel link);
|
||||||
|
|
||||||
void preRemove(RealmModel realm, RoleModel role);
|
void preRemove(RealmModel realm, RoleModel role);
|
||||||
|
void preRemove(RealmModel realm, GroupModel group);
|
||||||
|
|
||||||
void preRemove(RealmModel realm, ClientModel client);
|
void preRemove(RealmModel realm, ClientModel client);
|
||||||
void preRemove(ClientModel realm, ProtocolMapperModel protocolMapper);
|
void preRemove(ClientModel realm, ProtocolMapperModel protocolMapper);
|
||||||
|
|
60
model/api/src/main/java/org/keycloak/models/entities/GroupEntity.java
Executable file
60
model/api/src/main/java/org/keycloak/models/entities/GroupEntity.java
Executable file
|
@ -0,0 +1,60 @@
|
||||||
|
package org.keycloak.models.entities;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bburke@redhat.com">Bill Burke/a>
|
||||||
|
*/
|
||||||
|
public class GroupEntity extends AbstractIdentifiableEntity {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
private String realmId;
|
||||||
|
|
||||||
|
private List<String> roleIds;
|
||||||
|
private String parentId;
|
||||||
|
private Map<String, List<String>> attributes;
|
||||||
|
|
||||||
|
public String getRealmId() {
|
||||||
|
return realmId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRealmId(String realmId) {
|
||||||
|
this.realmId = realmId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getRoleIds() {
|
||||||
|
return roleIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRoleIds(List<String> roleIds) {
|
||||||
|
this.roleIds = roleIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, List<String>> getAttributes() {
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttributes(Map<String, List<String>> attributes) {
|
||||||
|
this.attributes = attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getParentId() {
|
||||||
|
return parentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParentId(String parentId) {
|
||||||
|
this.parentId = parentId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ public class UserEntity extends AbstractIdentifiableEntity {
|
||||||
private String realmId;
|
private String realmId;
|
||||||
|
|
||||||
private List<String> roleIds;
|
private List<String> roleIds;
|
||||||
|
private List<String> groupIds;
|
||||||
|
|
||||||
private Map<String, List<String>> attributes;
|
private Map<String, List<String>> attributes;
|
||||||
private List<String> requiredActions;
|
private List<String> requiredActions;
|
||||||
|
@ -157,5 +158,13 @@ public class UserEntity extends AbstractIdentifiableEntity {
|
||||||
public void setServiceAccountClientLink(String serviceAccountClientLink) {
|
public void setServiceAccountClientLink(String serviceAccountClientLink) {
|
||||||
this.serviceAccountClientLink = serviceAccountClientLink;
|
this.serviceAccountClientLink = serviceAccountClientLink;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<String> getGroupIds() {
|
||||||
|
return groupIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGroupIds(List<String> groupIds) {
|
||||||
|
this.groupIds = groupIds;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package org.keycloak.models.utils;
|
||||||
import org.bouncycastle.openssl.PEMWriter;
|
import org.bouncycastle.openssl.PEMWriter;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.Constants;
|
import org.keycloak.models.Constants;
|
||||||
|
import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
import org.keycloak.models.KeycloakSessionTask;
|
import org.keycloak.models.KeycloakSessionTask;
|
||||||
|
@ -279,6 +280,24 @@ public final class KeycloakModelUtils {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param groups
|
||||||
|
* @param targetGroup
|
||||||
|
* @return true if targetGroup is in groups (directly or indirectly via parent child relationship)
|
||||||
|
*/
|
||||||
|
public static boolean isMember(Set<GroupModel> groups, GroupModel targetGroup) {
|
||||||
|
if (groups.contains(targetGroup)) return true;
|
||||||
|
|
||||||
|
for (GroupModel mapping : groups) {
|
||||||
|
GroupModel child = mapping;
|
||||||
|
while(child.getParent() != null) {
|
||||||
|
if (child.getParent().equals(targetGroup)) return true;
|
||||||
|
child = child.getParent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
// USER FEDERATION RELATED STUFF
|
// USER FEDERATION RELATED STUFF
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.keycloak.models.utils;
|
package org.keycloak.models.utils;
|
||||||
|
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
|
import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.models.UserConsentModel;
|
import org.keycloak.models.UserConsentModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.UserCredentialModel;
|
import org.keycloak.models.UserCredentialModel;
|
||||||
|
@ -255,4 +256,26 @@ public class UserModelDelegate implements UserModel {
|
||||||
public void setCreatedTimestamp(Long timestamp){
|
public void setCreatedTimestamp(Long timestamp){
|
||||||
delegate.setCreatedTimestamp(timestamp);
|
delegate.setCreatedTimestamp(timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<GroupModel> getGroups() {
|
||||||
|
return delegate.getGroups();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void joinGroup(GroupModel group) {
|
||||||
|
delegate.joinGroup(group);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void leaveGroup(GroupModel group) {
|
||||||
|
delegate.leaveGroup(group);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isMemberOf(GroupModel group) {
|
||||||
|
return delegate.isMemberOf(group);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import org.keycloak.connections.file.FileConnectionProvider;
|
||||||
import org.keycloak.connections.file.InMemoryModel;
|
import org.keycloak.connections.file.InMemoryModel;
|
||||||
import org.keycloak.migration.MigrationModel;
|
import org.keycloak.migration.MigrationModel;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
|
import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.ModelDuplicateException;
|
import org.keycloak.models.ModelDuplicateException;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
@ -78,6 +79,11 @@ public class FileRealmProvider implements RealmProvider {
|
||||||
return realm;
|
return realm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GroupModel getGroupById(String id, RealmModel realm) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RealmModel getRealm(String id) {
|
public RealmModel getRealm(String id) {
|
||||||
RealmModel model = inMemoryModel.getRealm(id);
|
RealmModel model = inMemoryModel.getRealm(id);
|
||||||
|
|
|
@ -21,6 +21,7 @@ import org.keycloak.connections.file.InMemoryModel;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.CredentialValidationOutput;
|
import org.keycloak.models.CredentialValidationOutput;
|
||||||
import org.keycloak.models.FederatedIdentityModel;
|
import org.keycloak.models.FederatedIdentityModel;
|
||||||
|
import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.ModelDuplicateException;
|
import org.keycloak.models.ModelDuplicateException;
|
||||||
import org.keycloak.models.ModelException;
|
import org.keycloak.models.ModelException;
|
||||||
|
@ -80,6 +81,21 @@ public class FileUserProvider implements UserProvider {
|
||||||
return inMemoryModel.getUser(realm.getId(), userId);
|
return inMemoryModel.getUser(realm.getId(), userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<UserModel> getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<UserModel> getGroupMembers(RealmModel realm, GroupModel group) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void preRemove(RealmModel realm, GroupModel group) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserModel getUserByUsername(String username, RealmModel realm) {
|
public UserModel getUserByUsername(String username, RealmModel realm) {
|
||||||
for (UserModel user : inMemoryModel.getUsers(realm.getId())) {
|
for (UserModel user : inMemoryModel.getUsers(realm.getId())) {
|
||||||
|
|
208
model/file/src/main/java/org/keycloak/models/file/adapter/GroupAdapter.java
Executable file
208
model/file/src/main/java/org/keycloak/models/file/adapter/GroupAdapter.java
Executable file
|
@ -0,0 +1,208 @@
|
||||||
|
package org.keycloak.models.file.adapter;
|
||||||
|
|
||||||
|
import org.keycloak.models.ClientModel;
|
||||||
|
import org.keycloak.models.GroupModel;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.RoleModel;
|
||||||
|
import org.keycloak.models.entities.GroupEntity;
|
||||||
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
|
*/
|
||||||
|
public class GroupAdapter implements GroupModel {
|
||||||
|
|
||||||
|
private final GroupEntity group;
|
||||||
|
private RealmModel realm;
|
||||||
|
private KeycloakSession session;
|
||||||
|
|
||||||
|
public GroupAdapter(KeycloakSession session, RealmModel realm, GroupEntity group) {
|
||||||
|
this.group = group;
|
||||||
|
this.realm = realm;
|
||||||
|
this.session = session;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return group.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return group.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setName(String name) {
|
||||||
|
group.setName(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || !(o instanceof GroupModel)) return false;
|
||||||
|
|
||||||
|
GroupModel that = (GroupModel) o;
|
||||||
|
return that.getId().equals(getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return getId().hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSingleAttribute(String name, String value) {
|
||||||
|
if (group.getAttributes() == null) {
|
||||||
|
group.setAttributes(new HashMap<String, List<String>>());
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> attrValues = new ArrayList<>();
|
||||||
|
attrValues.add(value);
|
||||||
|
group.getAttributes().put(name, attrValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAttribute(String name, List<String> values) {
|
||||||
|
if (group.getAttributes() == null) {
|
||||||
|
group.setAttributes(new HashMap<String, List<String>>());
|
||||||
|
}
|
||||||
|
|
||||||
|
group.getAttributes().put(name, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAttribute(String name) {
|
||||||
|
if (group.getAttributes() == null) return;
|
||||||
|
|
||||||
|
group.getAttributes().remove(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFirstAttribute(String name) {
|
||||||
|
if (group.getAttributes()==null) return null;
|
||||||
|
|
||||||
|
List<String> attrValues = group.getAttributes().get(name);
|
||||||
|
return (attrValues==null || attrValues.isEmpty()) ? null : attrValues.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getAttribute(String name) {
|
||||||
|
if (group.getAttributes()==null) return Collections.<String>emptyList();
|
||||||
|
List<String> attrValues = group.getAttributes().get(name);
|
||||||
|
return (attrValues == null) ? Collections.<String>emptyList() : Collections.unmodifiableList(attrValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, List<String>> getAttributes() {
|
||||||
|
return group.getAttributes()==null ? Collections.<String, List<String>>emptyMap() : Collections.unmodifiableMap((Map) group.getAttributes());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasRole(RoleModel role) {
|
||||||
|
Set<RoleModel> roles = getRoleMappings();
|
||||||
|
return KeycloakModelUtils.hasRole(roles, role);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void grantRole(RoleModel role) {
|
||||||
|
if (group.getRoleIds() == null) {
|
||||||
|
group.setRoleIds(new LinkedList<String>());
|
||||||
|
}
|
||||||
|
if (group.getRoleIds().contains(role.getId())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
group.getRoleIds().add(role.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<RoleModel> getRoleMappings() {
|
||||||
|
if (group.getRoleIds() == null || group.getRoleIds().isEmpty()) return Collections.EMPTY_SET;
|
||||||
|
Set<RoleModel> roles = new HashSet<>();
|
||||||
|
for (String id : group.getRoleIds()) {
|
||||||
|
roles.add(realm.getRoleById(id));
|
||||||
|
}
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<RoleModel> getRealmRoleMappings() {
|
||||||
|
Set<RoleModel> allRoles = getRoleMappings();
|
||||||
|
|
||||||
|
// Filter to retrieve just realm roles
|
||||||
|
Set<RoleModel> realmRoles = new HashSet<RoleModel>();
|
||||||
|
for (RoleModel role : allRoles) {
|
||||||
|
if (role.getContainer() instanceof RealmModel) {
|
||||||
|
realmRoles.add(role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return realmRoles;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteRoleMapping(RoleModel role) {
|
||||||
|
if (group == null || role == null) return;
|
||||||
|
if (group.getRoleIds() == null) return;
|
||||||
|
group.getRoleIds().remove(role.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<RoleModel> getClientRoleMappings(ClientModel app) {
|
||||||
|
Set<RoleModel> result = new HashSet<RoleModel>();
|
||||||
|
Set<RoleModel> roles = getRoleMappings();
|
||||||
|
|
||||||
|
for (RoleModel role : roles) {
|
||||||
|
if (app.equals(role.getContainer())) {
|
||||||
|
result.add(role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GroupModel getParent() {
|
||||||
|
if (group.getParentId() == null) return null;
|
||||||
|
return realm.getGroupById(group.getParentId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<GroupModel> getSubGroups() {
|
||||||
|
Set<GroupModel> subGroups = new HashSet<>();
|
||||||
|
for (GroupModel groupModel : realm.getGroups()) {
|
||||||
|
if (groupModel.getParent().equals(this)) {
|
||||||
|
subGroups.add(groupModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return subGroups;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setParent(GroupModel group) {
|
||||||
|
this.group.setParentId(group.getId());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addChild(GroupModel subGroup) {
|
||||||
|
subGroup.setParent(this);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeChild(GroupModel subGroup) {
|
||||||
|
subGroup.setParent(null);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,6 +22,7 @@ import org.keycloak.models.AuthenticationExecutionModel;
|
||||||
import org.keycloak.models.AuthenticationFlowModel;
|
import org.keycloak.models.AuthenticationFlowModel;
|
||||||
import org.keycloak.models.AuthenticatorConfigModel;
|
import org.keycloak.models.AuthenticatorConfigModel;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
|
import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.models.IdentityProviderMapperModel;
|
import org.keycloak.models.IdentityProviderMapperModel;
|
||||||
import org.keycloak.models.IdentityProviderModel;
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
@ -88,6 +89,7 @@ public class RealmAdapter implements RealmModel {
|
||||||
private final Map<String, ClientModel> allApps = new HashMap<String, ClientModel>();
|
private final Map<String, ClientModel> allApps = new HashMap<String, ClientModel>();
|
||||||
private ClientModel masterAdminApp = null;
|
private ClientModel masterAdminApp = null;
|
||||||
private final Map<String, RoleAdapter> allRoles = new HashMap<String, RoleAdapter>();
|
private final Map<String, RoleAdapter> allRoles = new HashMap<String, RoleAdapter>();
|
||||||
|
private final Map<String, GroupAdapter> allGroups = new HashMap<String, GroupAdapter>();
|
||||||
private final Map<String, IdentityProviderModel> allIdProviders = new HashMap<String, IdentityProviderModel>();
|
private final Map<String, IdentityProviderModel> allIdProviders = new HashMap<String, IdentityProviderModel>();
|
||||||
|
|
||||||
public RealmAdapter(KeycloakSession session, RealmEntity realm, InMemoryModel inMemoryModel) {
|
public RealmAdapter(KeycloakSession session, RealmEntity realm, InMemoryModel inMemoryModel) {
|
||||||
|
@ -601,6 +603,36 @@ public class RealmAdapter implements RealmModel {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GroupModel getGroupById(String id) {
|
||||||
|
GroupModel found = allGroups.get(id);
|
||||||
|
if (found != null) return found;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GroupModel> getGroups() {
|
||||||
|
List<GroupModel> list = new LinkedList<>();
|
||||||
|
for (GroupAdapter group : allGroups.values()) {
|
||||||
|
list.add(group);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GroupModel> getTopLevelGroups() {
|
||||||
|
List<GroupModel> list = new LinkedList<>();
|
||||||
|
for (GroupAdapter group : allGroups.values()) {
|
||||||
|
if (group.getParent() == null) list.add(group);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeGroup(GroupModel group) {
|
||||||
|
return allGroups.remove(group.getId()) != null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getDefaultRoles() {
|
public List<String> getDefaultRoles() {
|
||||||
return realm.getDefaultRoles();
|
return realm.getDefaultRoles();
|
||||||
|
|
|
@ -21,6 +21,7 @@ import org.keycloak.models.ClientModel;
|
||||||
|
|
||||||
import static org.keycloak.models.utils.Pbkdf2PasswordEncoder.getSalt;
|
import static org.keycloak.models.utils.Pbkdf2PasswordEncoder.getSalt;
|
||||||
|
|
||||||
|
import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.models.ModelDuplicateException;
|
import org.keycloak.models.ModelDuplicateException;
|
||||||
import org.keycloak.models.OTPPolicy;
|
import org.keycloak.models.OTPPolicy;
|
||||||
import org.keycloak.models.UserConsentModel;
|
import org.keycloak.models.UserConsentModel;
|
||||||
|
@ -59,6 +60,7 @@ public class UserAdapter implements UserModel, Comparable {
|
||||||
private final RealmModel realm;
|
private final RealmModel realm;
|
||||||
|
|
||||||
private final Set<RoleModel> allRoles = new HashSet<RoleModel>();
|
private final Set<RoleModel> allRoles = new HashSet<RoleModel>();
|
||||||
|
private final Set<GroupModel> allGroups = new HashSet<GroupModel>();
|
||||||
|
|
||||||
public UserAdapter(RealmModel realm, UserEntity userEntity, InMemoryModel inMemoryModel) {
|
public UserAdapter(RealmModel realm, UserEntity userEntity, InMemoryModel inMemoryModel) {
|
||||||
this.user = userEntity;
|
this.user = userEntity;
|
||||||
|
@ -467,6 +469,29 @@ public class UserAdapter implements UserModel, Comparable {
|
||||||
credentialEntity.setPeriod(credModel.getPeriod());
|
credentialEntity.setPeriod(credModel.getPeriod());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<GroupModel> getGroups() {
|
||||||
|
return Collections.unmodifiableSet(allGroups);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void joinGroup(GroupModel group) {
|
||||||
|
allGroups.add(group);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void leaveGroup(GroupModel group) {
|
||||||
|
if (user == null || group == null) return;
|
||||||
|
allGroups.remove(group);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isMemberOf(GroupModel group) {
|
||||||
|
return KeycloakModelUtils.isMember(getGroups(), group);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasRole(RoleModel role) {
|
public boolean hasRole(RoleModel role) {
|
||||||
Set<RoleModel> roles = getRoleMappings();
|
Set<RoleModel> roles = getRoleMappings();
|
||||||
|
|
36
model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/DefaultCacheRealmProvider.java
vendored
Normal file → Executable file
36
model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/DefaultCacheRealmProvider.java
vendored
Normal file → Executable file
|
@ -21,9 +21,11 @@ public class DefaultCacheRealmProvider implements CacheRealmProvider {
|
||||||
protected Set<String> realmInvalidations = new HashSet<String>();
|
protected Set<String> realmInvalidations = new HashSet<String>();
|
||||||
protected Set<String> appInvalidations = new HashSet<String>();
|
protected Set<String> appInvalidations = new HashSet<String>();
|
||||||
protected Set<String> roleInvalidations = new HashSet<String>();
|
protected Set<String> roleInvalidations = new HashSet<String>();
|
||||||
|
protected Set<String> groupInvalidations = new HashSet<String>();
|
||||||
protected Map<String, RealmModel> managedRealms = new HashMap<String, RealmModel>();
|
protected Map<String, RealmModel> managedRealms = new HashMap<String, RealmModel>();
|
||||||
protected Map<String, ClientModel> managedApplications = new HashMap<String, ClientModel>();
|
protected Map<String, ClientModel> managedApplications = new HashMap<String, ClientModel>();
|
||||||
protected Map<String, RoleModel> managedRoles = new HashMap<String, RoleModel>();
|
protected Map<String, RoleModel> managedRoles = new HashMap<String, RoleModel>();
|
||||||
|
protected Map<String, GroupModel> managedGroups = new HashMap<String, GroupModel>();
|
||||||
|
|
||||||
protected boolean clearAll;
|
protected boolean clearAll;
|
||||||
|
|
||||||
|
@ -73,6 +75,12 @@ public class DefaultCacheRealmProvider implements CacheRealmProvider {
|
||||||
roleInvalidations.add(id);
|
roleInvalidations.add(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerGroupInvalidation(String id) {
|
||||||
|
groupInvalidations.add(id);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
protected void runInvalidations() {
|
protected void runInvalidations() {
|
||||||
for (String id : realmInvalidations) {
|
for (String id : realmInvalidations) {
|
||||||
cache.invalidateCachedRealmById(id);
|
cache.invalidateCachedRealmById(id);
|
||||||
|
@ -80,6 +88,9 @@ public class DefaultCacheRealmProvider implements CacheRealmProvider {
|
||||||
for (String id : roleInvalidations) {
|
for (String id : roleInvalidations) {
|
||||||
cache.invalidateRoleById(id);
|
cache.invalidateRoleById(id);
|
||||||
}
|
}
|
||||||
|
for (String id : groupInvalidations) {
|
||||||
|
cache.invalidateGroupById(id);
|
||||||
|
}
|
||||||
for (String id : appInvalidations) {
|
for (String id : appInvalidations) {
|
||||||
cache.invalidateCachedApplicationById(id);
|
cache.invalidateCachedApplicationById(id);
|
||||||
}
|
}
|
||||||
|
@ -254,6 +265,31 @@ public class DefaultCacheRealmProvider implements CacheRealmProvider {
|
||||||
return adapter;
|
return adapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GroupModel getGroupById(String id, RealmModel realm) {
|
||||||
|
if (!cache.isEnabled()) return getDelegate().getGroupById(id, realm);
|
||||||
|
CachedGroup cached = cache.getGroup(id);
|
||||||
|
if (cached != null && !cached.getRealm().equals(realm.getId())) {
|
||||||
|
cached = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cached == null) {
|
||||||
|
GroupModel model = getDelegate().getGroupById(id, realm);
|
||||||
|
if (model == null) return null;
|
||||||
|
if (groupInvalidations.contains(id)) return model;
|
||||||
|
cached = new CachedGroup(realm, model);
|
||||||
|
cache.addCachedGroup(cached);
|
||||||
|
|
||||||
|
} else if (groupInvalidations.contains(id)) {
|
||||||
|
return getDelegate().getGroupById(id, realm);
|
||||||
|
} else if (managedGroups.containsKey(id)) {
|
||||||
|
return managedGroups.get(id);
|
||||||
|
}
|
||||||
|
GroupAdapter adapter = new GroupAdapter(cached, this, session, realm);
|
||||||
|
managedGroups.put(id, adapter);
|
||||||
|
return adapter;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ClientModel getClientById(String id, RealmModel realm) {
|
public ClientModel getClientById(String id, RealmModel realm) {
|
||||||
if (!cache.isEnabled()) return getDelegate().getClientById(id, realm);
|
if (!cache.isEnabled()) return getDelegate().getClientById(id, realm);
|
||||||
|
|
15
model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/DefaultCacheUserProvider.java
vendored
Normal file → Executable file
15
model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/DefaultCacheUserProvider.java
vendored
Normal file → Executable file
|
@ -197,6 +197,16 @@ public class DefaultCacheUserProvider implements CacheUserProvider {
|
||||||
return getDelegate().getUserByFederatedIdentity(socialLink, realm);
|
return getDelegate().getUserByFederatedIdentity(socialLink, realm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<UserModel> getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) {
|
||||||
|
return getDelegate().getGroupMembers(realm, group, firstResult, maxResults);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<UserModel> getGroupMembers(RealmModel realm, GroupModel group) {
|
||||||
|
return getDelegate().getGroupMembers(realm, group);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserModel getUserByServiceAccountClient(ClientModel client) {
|
public UserModel getUserByServiceAccountClient(ClientModel client) {
|
||||||
return getDelegate().getUserByServiceAccountClient(client);
|
return getDelegate().getUserByServiceAccountClient(client);
|
||||||
|
@ -313,6 +323,11 @@ public class DefaultCacheUserProvider implements CacheUserProvider {
|
||||||
public void preRemove(RealmModel realm, RoleModel role) {
|
public void preRemove(RealmModel realm, RoleModel role) {
|
||||||
getDelegate().preRemove(realm, role);
|
getDelegate().preRemove(realm, role);
|
||||||
}
|
}
|
||||||
|
@Override
|
||||||
|
public void preRemove(RealmModel realm, GroupModel group) {
|
||||||
|
getDelegate().preRemove(realm, group);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void preRemove(RealmModel realm, UserFederationProviderModel link) {
|
public void preRemove(RealmModel realm, UserFederationProviderModel link) {
|
||||||
|
|
236
model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/GroupAdapter.java
vendored
Executable file
236
model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/GroupAdapter.java
vendored
Executable file
|
@ -0,0 +1,236 @@
|
||||||
|
package org.keycloak.models.cache.infinispan;
|
||||||
|
|
||||||
|
import org.keycloak.models.ClientModel;
|
||||||
|
import org.keycloak.models.GroupModel;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.RoleContainerModel;
|
||||||
|
import org.keycloak.models.RoleModel;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.models.cache.CacheRealmProvider;
|
||||||
|
import org.keycloak.models.cache.CacheUserProvider;
|
||||||
|
import org.keycloak.models.cache.entities.CachedGroup;
|
||||||
|
import org.keycloak.models.cache.entities.CachedUser;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class GroupAdapter implements GroupModel {
|
||||||
|
protected GroupModel updated;
|
||||||
|
protected CachedGroup cached;
|
||||||
|
protected CacheRealmProvider cacheSession;
|
||||||
|
protected KeycloakSession keycloakSession;
|
||||||
|
protected RealmModel realm;
|
||||||
|
|
||||||
|
public GroupAdapter(CachedGroup cached, CacheRealmProvider cacheSession, KeycloakSession keycloakSession, RealmModel realm) {
|
||||||
|
this.cached = cached;
|
||||||
|
this.cacheSession = cacheSession;
|
||||||
|
this.keycloakSession = keycloakSession;
|
||||||
|
this.realm = realm;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void getDelegateForUpdate() {
|
||||||
|
if (updated == null) {
|
||||||
|
cacheSession.registerGroupInvalidation(getId());
|
||||||
|
updated = cacheSession.getDelegate().getGroupById(getId(), realm);
|
||||||
|
if (updated == null) throw new IllegalStateException("Not found in database");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || !(o instanceof GroupModel)) return false;
|
||||||
|
|
||||||
|
GroupModel that = (GroupModel) o;
|
||||||
|
|
||||||
|
if (!cached.getId().equals(that.getId())) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return cached.getId().hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
if (updated != null) return updated.getId();
|
||||||
|
return cached.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
if (updated != null) return updated.getName();
|
||||||
|
return cached.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setName(String name) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
updated.setName(name);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSingleAttribute(String name, String value) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
updated.setSingleAttribute(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAttribute(String name, List<String> values) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
updated.setAttribute(name, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAttribute(String name) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
updated.removeAttribute(name);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFirstAttribute(String name) {
|
||||||
|
if (updated != null) return updated.getFirstAttribute(name);
|
||||||
|
return cached.getAttributes().getFirst(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getAttribute(String name) {
|
||||||
|
List<String> values = cached.getAttributes().get(name);
|
||||||
|
if (values == null) return null;
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, List<String>> getAttributes() {
|
||||||
|
return cached.getAttributes();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<RoleModel> getRealmRoleMappings() {
|
||||||
|
if (updated != null) return updated.getRealmRoleMappings();
|
||||||
|
Set<RoleModel> roleMappings = getRoleMappings();
|
||||||
|
Set<RoleModel> realmMappings = new HashSet<RoleModel>();
|
||||||
|
for (RoleModel role : roleMappings) {
|
||||||
|
RoleContainerModel container = role.getContainer();
|
||||||
|
if (container instanceof RealmModel) {
|
||||||
|
if (((RealmModel) container).getId().equals(realm.getId())) {
|
||||||
|
realmMappings.add(role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return realmMappings;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<RoleModel> getClientRoleMappings(ClientModel app) {
|
||||||
|
if (updated != null) return updated.getClientRoleMappings(app);
|
||||||
|
Set<RoleModel> roleMappings = getRoleMappings();
|
||||||
|
Set<RoleModel> appMappings = new HashSet<RoleModel>();
|
||||||
|
for (RoleModel role : roleMappings) {
|
||||||
|
RoleContainerModel container = role.getContainer();
|
||||||
|
if (container instanceof ClientModel) {
|
||||||
|
if (((ClientModel) container).getId().equals(app.getId())) {
|
||||||
|
appMappings.add(role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return appMappings;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasRole(RoleModel role) {
|
||||||
|
if (updated != null) return updated.hasRole(role);
|
||||||
|
if (cached.getRoleMappings().contains(role.getId())) return true;
|
||||||
|
|
||||||
|
Set<RoleModel> mappings = getRoleMappings();
|
||||||
|
for (RoleModel mapping: mappings) {
|
||||||
|
if (mapping.hasRole(role)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void grantRole(RoleModel role) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
updated.grantRole(role);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<RoleModel> getRoleMappings() {
|
||||||
|
if (updated != null) return updated.getRoleMappings();
|
||||||
|
Set<RoleModel> roles = new HashSet<RoleModel>();
|
||||||
|
for (String id : cached.getRoleMappings()) {
|
||||||
|
RoleModel roleById = keycloakSession.realms().getRoleById(id, realm);
|
||||||
|
if (roleById == null) {
|
||||||
|
// chance that role was removed, so just delegate to persistence and get user invalidated
|
||||||
|
getDelegateForUpdate();
|
||||||
|
return updated.getRoleMappings();
|
||||||
|
}
|
||||||
|
roles.add(roleById);
|
||||||
|
|
||||||
|
}
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteRoleMapping(RoleModel role) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
updated.deleteRoleMapping(role);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GroupModel getParent() {
|
||||||
|
if (updated != null) return updated.getParent();
|
||||||
|
if (cached.getParentId() == null) return null;
|
||||||
|
return keycloakSession.realms().getGroupById(cached.getParentId(), realm);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<GroupModel> getSubGroups() {
|
||||||
|
if (updated != null) return updated.getSubGroups();
|
||||||
|
Set<GroupModel> subGroups = new HashSet<>();
|
||||||
|
for (String id : cached.getSubGroups()) {
|
||||||
|
GroupModel subGroup = keycloakSession.realms().getGroupById(id, realm);
|
||||||
|
if (subGroup == null) {
|
||||||
|
// chance that role was removed, so just delegate to persistence and get user invalidated
|
||||||
|
getDelegateForUpdate();
|
||||||
|
return updated.getSubGroups();
|
||||||
|
|
||||||
|
}
|
||||||
|
subGroups.add(subGroup);
|
||||||
|
}
|
||||||
|
return subGroups;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setParent(GroupModel group) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
updated.setParent(group);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addChild(GroupModel subGroup) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
updated.addChild(subGroup);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeChild(GroupModel subGroup) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
updated.removeChild(subGroup);
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ import org.infinispan.Cache;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.models.cache.RealmCache;
|
import org.keycloak.models.cache.RealmCache;
|
||||||
import org.keycloak.models.cache.entities.CachedClient;
|
import org.keycloak.models.cache.entities.CachedClient;
|
||||||
|
import org.keycloak.models.cache.entities.CachedGroup;
|
||||||
import org.keycloak.models.cache.entities.CachedRealm;
|
import org.keycloak.models.cache.entities.CachedRealm;
|
||||||
import org.keycloak.models.cache.entities.CachedRole;
|
import org.keycloak.models.cache.entities.CachedRole;
|
||||||
|
|
||||||
|
@ -101,12 +102,49 @@ public class InfinispanRealmCache implements RealmCache {
|
||||||
cache.remove(id);
|
cache.remove(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CachedGroup getGroup(String id) {
|
||||||
|
if (!enabled) return null;
|
||||||
|
return get(id, CachedGroup.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidateGroup(CachedGroup role) {
|
||||||
|
logger.tracev("Removing group {0}", role.getId());
|
||||||
|
cache.remove(role.getId());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addCachedGroup(CachedGroup role) {
|
||||||
|
if (!enabled) return;
|
||||||
|
logger.tracev("Adding group {0}", role.getId());
|
||||||
|
cache.put(role.getId(), role);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidateCachedGroupById(String id) {
|
||||||
|
logger.tracev("Removing group {0}", id);
|
||||||
|
cache.remove(id);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidateGroupById(String id) {
|
||||||
|
logger.tracev("Removing group {0}", id);
|
||||||
|
cache.remove(id);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CachedRole getRole(String id) {
|
public CachedRole getRole(String id) {
|
||||||
if (!enabled) return null;
|
if (!enabled) return null;
|
||||||
return get(id, CachedRole.class);
|
return get(id, CachedRole.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void invalidateRole(CachedRole role) {
|
public void invalidateRole(CachedRole role) {
|
||||||
logger.tracev("Removing role {0}", role.getId());
|
logger.tracev("Removing role {0}", role.getId());
|
||||||
|
|
|
@ -1262,4 +1262,42 @@ public class RealmAdapter implements RealmModel {
|
||||||
if (updated != null) return updated.getRequiredActionProviderByAlias(alias);
|
if (updated != null) return updated.getRequiredActionProviderByAlias(alias);
|
||||||
return cached.getRequiredActionProvidersByAlias().get(alias);
|
return cached.getRequiredActionProvidersByAlias().get(alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GroupModel getGroupById(String id) {
|
||||||
|
if (updated != null) return updated.getGroupById(id);
|
||||||
|
return cacheSession.getGroupById(id, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GroupModel> getGroups() {
|
||||||
|
if (updated != null) return updated.getGroups();
|
||||||
|
if (cached.getGroups().isEmpty()) return null;
|
||||||
|
List<GroupModel> list = new LinkedList<>();
|
||||||
|
for (String id : cached.getGroups()) {
|
||||||
|
GroupModel group = cacheSession.getGroupById(id, this);
|
||||||
|
if (group == null) continue;
|
||||||
|
list.add(group);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GroupModel> getTopLevelGroups() {
|
||||||
|
List<GroupModel> all = getGroups();
|
||||||
|
Iterator<GroupModel> it = all.iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
GroupModel group = it.next();
|
||||||
|
if (group.getParent() != null) {
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return all;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeGroup(GroupModel group) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
return updated.removeGroup(group);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -317,6 +317,44 @@ public class UserAdapter implements UserModel {
|
||||||
updated.deleteRoleMapping(role);
|
updated.deleteRoleMapping(role);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<GroupModel> getGroups() {
|
||||||
|
if (updated != null) return updated.getGroups();
|
||||||
|
Set<GroupModel> groups = new HashSet<GroupModel>();
|
||||||
|
for (String id : cached.getRoleMappings()) {
|
||||||
|
GroupModel groupModel = keycloakSession.realms().getGroupById(id, realm);
|
||||||
|
if (groupModel == null) {
|
||||||
|
// chance that role was removed, so just delete to persistence and get user invalidated
|
||||||
|
getDelegateForUpdate();
|
||||||
|
return updated.getGroups();
|
||||||
|
}
|
||||||
|
groups.add(groupModel);
|
||||||
|
|
||||||
|
}
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void joinGroup(GroupModel group) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
updated.joinGroup(group);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void leaveGroup(GroupModel group) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
updated.leaveGroup(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isMemberOf(GroupModel group) {
|
||||||
|
if (updated != null) return updated.isMemberOf(group);
|
||||||
|
if (cached.getGroups().contains(group.getId())) return true;
|
||||||
|
Set<GroupModel> roles = getGroups();
|
||||||
|
return KeycloakModelUtils.isMember(roles, group);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addConsent(UserConsentModel consent) {
|
public void addConsent(UserConsentModel consent) {
|
||||||
getDelegateForUpdate();
|
getDelegateForUpdate();
|
||||||
|
@ -348,4 +386,5 @@ public class UserAdapter implements UserModel {
|
||||||
getDelegateForUpdate();
|
getDelegateForUpdate();
|
||||||
return updated.revokeConsentForClient(clientId);
|
return updated.revokeConsentForClient(clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,4 +17,6 @@ public interface CacheRealmProvider extends RealmProvider {
|
||||||
void registerApplicationInvalidation(String id);
|
void registerApplicationInvalidation(String id);
|
||||||
|
|
||||||
void registerRoleInvalidation(String id);
|
void registerRoleInvalidation(String id);
|
||||||
|
|
||||||
|
void registerGroupInvalidation(String id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.keycloak.models.cache;
|
package org.keycloak.models.cache;
|
||||||
|
|
||||||
import org.keycloak.models.cache.entities.CachedClient;
|
import org.keycloak.models.cache.entities.CachedClient;
|
||||||
|
import org.keycloak.models.cache.entities.CachedGroup;
|
||||||
import org.keycloak.models.cache.entities.CachedRealm;
|
import org.keycloak.models.cache.entities.CachedRealm;
|
||||||
import org.keycloak.models.cache.entities.CachedRole;
|
import org.keycloak.models.cache.entities.CachedRole;
|
||||||
|
|
||||||
|
@ -39,6 +40,16 @@ public interface RealmCache {
|
||||||
|
|
||||||
void invalidateRoleById(String id);
|
void invalidateRoleById(String id);
|
||||||
|
|
||||||
|
CachedGroup getGroup(String id);
|
||||||
|
|
||||||
|
void invalidateGroup(CachedGroup role);
|
||||||
|
|
||||||
|
void addCachedGroup(CachedGroup role);
|
||||||
|
|
||||||
|
void invalidateCachedGroupById(String id);
|
||||||
|
|
||||||
|
void invalidateGroupById(String id);
|
||||||
|
|
||||||
boolean isEnabled();
|
boolean isEnabled();
|
||||||
|
|
||||||
void setEnabled(boolean enabled);
|
void setEnabled(boolean enabled);
|
||||||
|
|
74
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedGroup.java
vendored
Executable file
74
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedGroup.java
vendored
Executable file
|
@ -0,0 +1,74 @@
|
||||||
|
package org.keycloak.models.cache.entities;
|
||||||
|
|
||||||
|
import org.keycloak.common.util.MultivaluedHashMap;
|
||||||
|
import org.keycloak.models.GroupModel;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.RoleModel;
|
||||||
|
import org.keycloak.models.UserCredentialValueModel;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class CachedGroup implements Serializable {
|
||||||
|
private String id;
|
||||||
|
private String realm;
|
||||||
|
private String name;
|
||||||
|
private String parentId;
|
||||||
|
private MultivaluedHashMap<String, String> attributes = new MultivaluedHashMap<>();
|
||||||
|
private Set<String> roleMappings = new HashSet<>();
|
||||||
|
private Set<String> subGroups = new HashSet<>();
|
||||||
|
|
||||||
|
public CachedGroup(RealmModel realm, GroupModel group) {
|
||||||
|
this.id = group.getId();
|
||||||
|
this.realm = realm.getId();
|
||||||
|
this.name = group.getName();
|
||||||
|
if (group.getParent() != null) this.parentId = group.getParent().getId();
|
||||||
|
|
||||||
|
this.attributes.putAll(group.getAttributes());
|
||||||
|
for (RoleModel role : group.getRoleMappings()) {
|
||||||
|
roleMappings.add(role.getId());
|
||||||
|
}
|
||||||
|
Set<GroupModel> subGroups1 = group.getSubGroups();
|
||||||
|
if (subGroups1 != null) {
|
||||||
|
for (GroupModel subGroup : subGroups1) {
|
||||||
|
subGroups.add(subGroup.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRealm() {
|
||||||
|
return realm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultivaluedHashMap<String, String> getAttributes() {
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getRoleMappings() {
|
||||||
|
return roleMappings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getParentId() {
|
||||||
|
return parentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getSubGroups() {
|
||||||
|
return subGroups;
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ import org.keycloak.models.AuthenticationExecutionModel;
|
||||||
import org.keycloak.models.AuthenticationFlowModel;
|
import org.keycloak.models.AuthenticationFlowModel;
|
||||||
import org.keycloak.models.AuthenticatorConfigModel;
|
import org.keycloak.models.AuthenticatorConfigModel;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
|
import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.models.IdentityProviderMapperModel;
|
import org.keycloak.models.IdentityProviderMapperModel;
|
||||||
import org.keycloak.models.IdentityProviderModel;
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
import org.keycloak.models.OTPPolicy;
|
import org.keycloak.models.OTPPolicy;
|
||||||
|
@ -106,6 +107,7 @@ public class CachedRealm implements Serializable {
|
||||||
protected Set<String> adminEnabledEventOperations = new HashSet<String>();
|
protected Set<String> adminEnabledEventOperations = new HashSet<String>();
|
||||||
protected boolean adminEventsDetailsEnabled;
|
protected boolean adminEventsDetailsEnabled;
|
||||||
private List<String> defaultRoles = new LinkedList<String>();
|
private List<String> defaultRoles = new LinkedList<String>();
|
||||||
|
private Set<String> groups = new HashSet<String>();
|
||||||
private Map<String, String> realmRoles = new HashMap<String, String>();
|
private Map<String, String> realmRoles = new HashMap<String, String>();
|
||||||
private Map<String, String> clients = new HashMap<String, String>();
|
private Map<String, String> clients = new HashMap<String, String>();
|
||||||
private boolean internationalizationEnabled;
|
private boolean internationalizationEnabled;
|
||||||
|
@ -216,6 +218,9 @@ public class CachedRealm implements Serializable {
|
||||||
executionsById.put(execution.getId(), execution);
|
executionsById.put(execution.getId(), execution);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (GroupModel group : model.getGroups()) {
|
||||||
|
groups.add(group.getId());
|
||||||
|
}
|
||||||
for (AuthenticatorConfigModel authenticator : model.getAuthenticatorConfigs()) {
|
for (AuthenticatorConfigModel authenticator : model.getAuthenticatorConfigs()) {
|
||||||
authenticatorConfigs.put(authenticator.getId(), authenticator);
|
authenticatorConfigs.put(authenticator.getId(), authenticator);
|
||||||
}
|
}
|
||||||
|
@ -507,4 +512,8 @@ public class CachedRealm implements Serializable {
|
||||||
public AuthenticationFlowModel getClientAuthenticationFlow() {
|
public AuthenticationFlowModel getClientAuthenticationFlow() {
|
||||||
return clientAuthenticationFlow;
|
return clientAuthenticationFlow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Set<String> getGroups() {
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.keycloak.models.cache.entities;
|
package org.keycloak.models.cache.entities;
|
||||||
|
|
||||||
|
import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.UserCredentialValueModel;
|
import org.keycloak.models.UserCredentialValueModel;
|
||||||
|
@ -33,6 +34,7 @@ public class CachedUser implements Serializable {
|
||||||
private MultivaluedHashMap<String, String> attributes = new MultivaluedHashMap<>();
|
private MultivaluedHashMap<String, String> attributes = new MultivaluedHashMap<>();
|
||||||
private Set<String> requiredActions = new HashSet<>();
|
private Set<String> requiredActions = new HashSet<>();
|
||||||
private Set<String> roleMappings = new HashSet<>();
|
private Set<String> roleMappings = new HashSet<>();
|
||||||
|
private Set<String> groups = new HashSet<>();
|
||||||
|
|
||||||
public CachedUser(RealmModel realm, UserModel user) {
|
public CachedUser(RealmModel realm, UserModel user) {
|
||||||
this.id = user.getId();
|
this.id = user.getId();
|
||||||
|
@ -53,6 +55,12 @@ public class CachedUser implements Serializable {
|
||||||
for (RoleModel role : user.getRoleMappings()) {
|
for (RoleModel role : user.getRoleMappings()) {
|
||||||
roleMappings.add(role.getId());
|
roleMappings.add(role.getId());
|
||||||
}
|
}
|
||||||
|
Set<GroupModel> groupMappings = user.getGroups();
|
||||||
|
if (groupMappings != null) {
|
||||||
|
for (GroupModel group : groupMappings) {
|
||||||
|
groups.add(group.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
|
@ -118,4 +126,8 @@ public class CachedUser implements Serializable {
|
||||||
public String getServiceAccountClientLink() {
|
public String getServiceAccountClientLink() {
|
||||||
return serviceAccountClientLink;
|
return serviceAccountClientLink;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Set<String> getGroups() {
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
320
model/jpa/src/main/java/org/keycloak/models/jpa/GroupAdapter.java
Executable file
320
model/jpa/src/main/java/org/keycloak/models/jpa/GroupAdapter.java
Executable file
|
@ -0,0 +1,320 @@
|
||||||
|
package org.keycloak.models.jpa;
|
||||||
|
|
||||||
|
import org.keycloak.common.util.MultivaluedHashMap;
|
||||||
|
import org.keycloak.common.util.Time;
|
||||||
|
import org.keycloak.models.ClientModel;
|
||||||
|
import org.keycloak.models.GroupModel;
|
||||||
|
import org.keycloak.models.ModelDuplicateException;
|
||||||
|
import org.keycloak.models.ModelException;
|
||||||
|
import org.keycloak.models.OTPPolicy;
|
||||||
|
import org.keycloak.models.PasswordPolicy;
|
||||||
|
import org.keycloak.models.ProtocolMapperModel;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.RoleContainerModel;
|
||||||
|
import org.keycloak.models.RoleModel;
|
||||||
|
import org.keycloak.models.UserConsentModel;
|
||||||
|
import org.keycloak.models.UserCredentialModel;
|
||||||
|
import org.keycloak.models.UserCredentialValueModel;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.models.jpa.entities.CredentialEntity;
|
||||||
|
import org.keycloak.models.jpa.entities.GroupAttributeEntity;
|
||||||
|
import org.keycloak.models.jpa.entities.GroupEntity;
|
||||||
|
import org.keycloak.models.jpa.entities.GroupRoleMappingEntity;
|
||||||
|
import org.keycloak.models.jpa.entities.RoleEntity;
|
||||||
|
import org.keycloak.models.jpa.entities.UserAttributeEntity;
|
||||||
|
import org.keycloak.models.jpa.entities.UserConsentEntity;
|
||||||
|
import org.keycloak.models.jpa.entities.UserConsentProtocolMapperEntity;
|
||||||
|
import org.keycloak.models.jpa.entities.UserConsentRoleEntity;
|
||||||
|
import org.keycloak.models.jpa.entities.UserEntity;
|
||||||
|
import org.keycloak.models.jpa.entities.UserRequiredActionEntity;
|
||||||
|
import org.keycloak.models.jpa.entities.UserRoleMappingEntity;
|
||||||
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
|
import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.TypedQuery;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.keycloak.models.utils.Pbkdf2PasswordEncoder.getSalt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class GroupAdapter implements GroupModel {
|
||||||
|
|
||||||
|
protected GroupEntity group;
|
||||||
|
protected EntityManager em;
|
||||||
|
protected RealmModel realm;
|
||||||
|
|
||||||
|
public GroupAdapter(RealmModel realm, EntityManager em, GroupEntity group) {
|
||||||
|
this.em = em;
|
||||||
|
this.group = group;
|
||||||
|
this.realm = realm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GroupEntity getGroup() {
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return group.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return group.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setName(String name) {
|
||||||
|
group.setName(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GroupModel getParent() {
|
||||||
|
GroupEntity parent = group.getParent();
|
||||||
|
if (parent == null) return null;
|
||||||
|
return realm.getGroupById(parent.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GroupEntity toEntity(GroupModel model, EntityManager em) {
|
||||||
|
if (model instanceof GroupAdapter) {
|
||||||
|
return ((GroupAdapter)model).getGroup();
|
||||||
|
}
|
||||||
|
return em.getReference(GroupEntity.class, model.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setParent(GroupModel group) {
|
||||||
|
GroupEntity parent = toEntity(group, em);
|
||||||
|
group.setParent(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addChild(GroupModel subGroup) {
|
||||||
|
subGroup.setParent(this);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeChild(GroupModel subGroup) {
|
||||||
|
subGroup.setParent(null);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<GroupModel> getSubGroups() {
|
||||||
|
TypedQuery<String> query = em.createNamedQuery("getGroupIdsByParent", String.class);
|
||||||
|
query.setParameter("parent", group);
|
||||||
|
List<String> ids = query.getResultList();
|
||||||
|
Set<GroupModel> set = new HashSet<>();
|
||||||
|
for (String id : ids) {
|
||||||
|
GroupModel subGroup = realm.getGroupById(id);
|
||||||
|
if (subGroup == null) continue;
|
||||||
|
set.add(subGroup);
|
||||||
|
}
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSingleAttribute(String name, String value) {
|
||||||
|
boolean found = false;
|
||||||
|
List<GroupAttributeEntity> toRemove = new ArrayList<>();
|
||||||
|
for (GroupAttributeEntity attr : group.getAttributes()) {
|
||||||
|
if (attr.getName().equals(name)) {
|
||||||
|
if (!found) {
|
||||||
|
attr.setValue(value);
|
||||||
|
found = true;
|
||||||
|
} else {
|
||||||
|
toRemove.add(attr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (GroupAttributeEntity attr : toRemove) {
|
||||||
|
em.remove(attr);
|
||||||
|
group.getAttributes().remove(attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
persistAttributeValue(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAttribute(String name, List<String> values) {
|
||||||
|
// Remove all existing
|
||||||
|
removeAttribute(name);
|
||||||
|
|
||||||
|
// Put all new
|
||||||
|
for (String value : values) {
|
||||||
|
persistAttributeValue(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void persistAttributeValue(String name, String value) {
|
||||||
|
GroupAttributeEntity attr = new GroupAttributeEntity();
|
||||||
|
attr.setId(KeycloakModelUtils.generateId());
|
||||||
|
attr.setName(name);
|
||||||
|
attr.setValue(value);
|
||||||
|
attr.setGroup(group);
|
||||||
|
em.persist(attr);
|
||||||
|
group.getAttributes().add(attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAttribute(String name) {
|
||||||
|
Iterator<GroupAttributeEntity> it = group.getAttributes().iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
GroupAttributeEntity attr = it.next();
|
||||||
|
if (attr.getName().equals(name)) {
|
||||||
|
it.remove();
|
||||||
|
em.remove(attr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFirstAttribute(String name) {
|
||||||
|
for (GroupAttributeEntity attr : group.getAttributes()) {
|
||||||
|
if (attr.getName().equals(name)) {
|
||||||
|
return attr.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getAttribute(String name) {
|
||||||
|
List<String> result = new ArrayList<>();
|
||||||
|
for (GroupAttributeEntity attr : group.getAttributes()) {
|
||||||
|
if (attr.getName().equals(name)) {
|
||||||
|
result.add(attr.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, List<String>> getAttributes() {
|
||||||
|
MultivaluedHashMap<String, String> result = new MultivaluedHashMap<>();
|
||||||
|
for (GroupAttributeEntity attr : group.getAttributes()) {
|
||||||
|
result.add(attr.getName(), attr.getValue());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasRole(RoleModel role) {
|
||||||
|
Set<RoleModel> roles = getRoleMappings();
|
||||||
|
return KeycloakModelUtils.hasRole(roles, role);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected TypedQuery<GroupRoleMappingEntity> getGroupRoleMappingEntityTypedQuery(RoleModel role) {
|
||||||
|
TypedQuery<GroupRoleMappingEntity> query = em.createNamedQuery("groupHasRole", GroupRoleMappingEntity.class);
|
||||||
|
query.setParameter("group", getGroup());
|
||||||
|
query.setParameter("roleId", role.getId());
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void grantRole(RoleModel role) {
|
||||||
|
if (hasRole(role)) return;
|
||||||
|
GroupRoleMappingEntity entity = new GroupRoleMappingEntity();
|
||||||
|
entity.setGroup(getGroup());
|
||||||
|
entity.setRoleId(role.getId());
|
||||||
|
em.persist(entity);
|
||||||
|
em.flush();
|
||||||
|
em.detach(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<RoleModel> getRealmRoleMappings() {
|
||||||
|
Set<RoleModel> roleMappings = getRoleMappings();
|
||||||
|
|
||||||
|
Set<RoleModel> realmRoles = new HashSet<RoleModel>();
|
||||||
|
for (RoleModel role : roleMappings) {
|
||||||
|
RoleContainerModel container = role.getContainer();
|
||||||
|
if (container instanceof RealmModel) {
|
||||||
|
realmRoles.add(role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return realmRoles;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<RoleModel> getRoleMappings() {
|
||||||
|
// we query ids only as the role might be cached and following the @ManyToOne will result in a load
|
||||||
|
// even if we're getting just the id.
|
||||||
|
TypedQuery<String> query = em.createNamedQuery("groupRoleMappingIds", String.class);
|
||||||
|
query.setParameter("group", getGroup());
|
||||||
|
List<String> ids = query.getResultList();
|
||||||
|
Set<RoleModel> roles = new HashSet<RoleModel>();
|
||||||
|
for (String roleId : ids) {
|
||||||
|
RoleModel roleById = realm.getRoleById(roleId);
|
||||||
|
if (roleById == null) continue;
|
||||||
|
roles.add(roleById);
|
||||||
|
}
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteRoleMapping(RoleModel role) {
|
||||||
|
if (group == null || role == null) return;
|
||||||
|
|
||||||
|
TypedQuery<GroupRoleMappingEntity> query = getGroupRoleMappingEntityTypedQuery(role);
|
||||||
|
List<GroupRoleMappingEntity> results = query.getResultList();
|
||||||
|
if (results.size() == 0) return;
|
||||||
|
for (GroupRoleMappingEntity entity : results) {
|
||||||
|
em.remove(entity);
|
||||||
|
}
|
||||||
|
em.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<RoleModel> getClientRoleMappings(ClientModel app) {
|
||||||
|
Set<RoleModel> roleMappings = getRoleMappings();
|
||||||
|
|
||||||
|
Set<RoleModel> roles = new HashSet<RoleModel>();
|
||||||
|
for (RoleModel role : roleMappings) {
|
||||||
|
RoleContainerModel container = role.getContainer();
|
||||||
|
if (container instanceof ClientModel) {
|
||||||
|
ClientModel appModel = (ClientModel)container;
|
||||||
|
if (appModel.getId().equals(app.getId())) {
|
||||||
|
roles.add(role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || !(o instanceof UserModel)) return false;
|
||||||
|
|
||||||
|
UserModel that = (UserModel) o;
|
||||||
|
return that.getId().equals(getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return getId().hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -2,11 +2,13 @@ package org.keycloak.models.jpa;
|
||||||
|
|
||||||
import org.keycloak.migration.MigrationModel;
|
import org.keycloak.migration.MigrationModel;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
|
import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RealmProvider;
|
import org.keycloak.models.RealmProvider;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.jpa.entities.ClientEntity;
|
import org.keycloak.models.jpa.entities.ClientEntity;
|
||||||
|
import org.keycloak.models.jpa.entities.GroupEntity;
|
||||||
import org.keycloak.models.jpa.entities.RealmEntity;
|
import org.keycloak.models.jpa.entities.RealmEntity;
|
||||||
import org.keycloak.models.jpa.entities.RoleEntity;
|
import org.keycloak.models.jpa.entities.RoleEntity;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
|
@ -117,6 +119,14 @@ public class JpaRealmProvider implements RealmProvider {
|
||||||
return new RoleAdapter(realm, em, entity);
|
return new RoleAdapter(realm, em, entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GroupModel getGroupById(String id, RealmModel realm) {
|
||||||
|
GroupEntity groupEntity = em.find(GroupEntity.class, id);
|
||||||
|
if (groupEntity == null) return null;
|
||||||
|
if (groupEntity.getRealm().getId().equals(realm.getId())) return null;
|
||||||
|
return new GroupAdapter(realm, em, groupEntity);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ClientModel getClientById(String id, RealmModel realm) {
|
public ClientModel getClientById(String id, RealmModel realm) {
|
||||||
ClientEntity app = em.find(ClientEntity.class, id);
|
ClientEntity app = em.find(ClientEntity.class, id);
|
||||||
|
|
|
@ -3,6 +3,7 @@ package org.keycloak.models.jpa;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.CredentialValidationOutput;
|
import org.keycloak.models.CredentialValidationOutput;
|
||||||
import org.keycloak.models.FederatedIdentityModel;
|
import org.keycloak.models.FederatedIdentityModel;
|
||||||
|
import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.ProtocolMapperModel;
|
import org.keycloak.models.ProtocolMapperModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
@ -169,6 +170,8 @@ public class JpaUserProvider implements UserProvider {
|
||||||
.setParameter("realmId", realm.getId()).executeUpdate();
|
.setParameter("realmId", realm.getId()).executeUpdate();
|
||||||
num = em.createNamedQuery("deleteUsersByRealm")
|
num = em.createNamedQuery("deleteUsersByRealm")
|
||||||
.setParameter("realmId", realm.getId()).executeUpdate();
|
.setParameter("realmId", realm.getId()).executeUpdate();
|
||||||
|
num = em.createNamedQuery("deleteUserGroupMembershipByRealm")
|
||||||
|
.setParameter("realmId", realm.getId()).executeUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -219,6 +222,25 @@ public class JpaUserProvider implements UserProvider {
|
||||||
.executeUpdate();
|
.executeUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<UserModel> getGroupMembers(RealmModel realm, GroupModel group) {
|
||||||
|
TypedQuery<UserEntity> query = em.createNamedQuery("groupMembership", UserEntity.class);
|
||||||
|
query.setParameter("groupId", group.getId());
|
||||||
|
List<UserEntity> results = query.getResultList();
|
||||||
|
|
||||||
|
List<UserModel> users = new ArrayList<UserModel>();
|
||||||
|
for (UserEntity user : results) {
|
||||||
|
users.add(new UserAdapter(realm, em, user));
|
||||||
|
}
|
||||||
|
return users;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void preRemove(RealmModel realm, GroupModel group) {
|
||||||
|
em.createNamedQuery("deleteUserGroupMembershipsByGroup").setParameter("groupId", group.getId()).executeUpdate();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserModel getUserById(String id, RealmModel realm) {
|
public UserModel getUserById(String id, RealmModel realm) {
|
||||||
TypedQuery<UserEntity> query = em.createNamedQuery("getRealmUserById", UserEntity.class);
|
TypedQuery<UserEntity> query = em.createNamedQuery("getRealmUserById", UserEntity.class);
|
||||||
|
@ -318,6 +340,25 @@ public class JpaUserProvider implements UserProvider {
|
||||||
return users;
|
return users;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<UserModel> getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) {
|
||||||
|
TypedQuery<UserEntity> query = em.createNamedQuery("groupMembership", UserEntity.class);
|
||||||
|
query.setParameter("groupId", group.getId());
|
||||||
|
if (firstResult != -1) {
|
||||||
|
query.setFirstResult(firstResult);
|
||||||
|
}
|
||||||
|
if (maxResults != -1) {
|
||||||
|
query.setMaxResults(maxResults);
|
||||||
|
}
|
||||||
|
List<UserEntity> results = query.getResultList();
|
||||||
|
|
||||||
|
List<UserModel> users = new ArrayList<UserModel>();
|
||||||
|
for (UserEntity user : results) {
|
||||||
|
users.add(new UserAdapter(realm, em, user));
|
||||||
|
}
|
||||||
|
return users;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<UserModel> searchForUser(String search, RealmModel realm) {
|
public List<UserModel> searchForUser(String search, RealmModel realm) {
|
||||||
return searchForUser(search, realm, -1, -1);
|
return searchForUser(search, realm, -1, -1);
|
||||||
|
|
|
@ -6,6 +6,7 @@ import org.keycloak.models.AuthenticationExecutionModel;
|
||||||
import org.keycloak.models.AuthenticationFlowModel;
|
import org.keycloak.models.AuthenticationFlowModel;
|
||||||
import org.keycloak.models.AuthenticatorConfigModel;
|
import org.keycloak.models.AuthenticatorConfigModel;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
|
import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.models.IdentityProviderMapperModel;
|
import org.keycloak.models.IdentityProviderMapperModel;
|
||||||
import org.keycloak.models.IdentityProviderModel;
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
@ -24,6 +25,7 @@ import org.keycloak.models.jpa.entities.AuthenticationExecutionEntity;
|
||||||
import org.keycloak.models.jpa.entities.AuthenticationFlowEntity;
|
import org.keycloak.models.jpa.entities.AuthenticationFlowEntity;
|
||||||
import org.keycloak.models.jpa.entities.AuthenticatorConfigEntity;
|
import org.keycloak.models.jpa.entities.AuthenticatorConfigEntity;
|
||||||
import org.keycloak.models.jpa.entities.ClientEntity;
|
import org.keycloak.models.jpa.entities.ClientEntity;
|
||||||
|
import org.keycloak.models.jpa.entities.GroupEntity;
|
||||||
import org.keycloak.models.jpa.entities.IdentityProviderEntity;
|
import org.keycloak.models.jpa.entities.IdentityProviderEntity;
|
||||||
import org.keycloak.models.jpa.entities.IdentityProviderMapperEntity;
|
import org.keycloak.models.jpa.entities.IdentityProviderMapperEntity;
|
||||||
import org.keycloak.models.jpa.entities.RealmAttributeEntity;
|
import org.keycloak.models.jpa.entities.RealmAttributeEntity;
|
||||||
|
@ -1944,4 +1946,60 @@ public class RealmAdapter implements RealmModel {
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GroupModel getGroupById(String id) {
|
||||||
|
GroupEntity groupEntity = em.find(GroupEntity.class, id);
|
||||||
|
if (groupEntity == null) return null;
|
||||||
|
if (groupEntity.getRealm().getId().equals(getId())) return null;
|
||||||
|
return new GroupAdapter(this, em, groupEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GroupModel> getGroups() {
|
||||||
|
List<GroupModel> list = new LinkedList<>();
|
||||||
|
Collection<GroupEntity> groups = realm.getGroups();
|
||||||
|
if (groups == null) return list;
|
||||||
|
for (GroupEntity entity : groups) {
|
||||||
|
list.add(new GroupAdapter(this, em, entity));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GroupModel> getTopLevelGroups() {
|
||||||
|
List<GroupModel> all = getGroups();
|
||||||
|
Iterator<GroupModel> it = all.iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
GroupModel group = it.next();
|
||||||
|
if (group.getParent() != null) {
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return all;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeGroup(GroupModel group) {
|
||||||
|
if (group == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
GroupEntity groupEntity = GroupAdapter.toEntity(group, em);
|
||||||
|
if (!groupEntity.getRealm().getId().equals(getId())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (GroupModel subGroup : group.getSubGroups()) {
|
||||||
|
removeGroup(subGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
session.users().preRemove(this, group);
|
||||||
|
realm.getGroups().remove(groupEntity);
|
||||||
|
em.createNamedQuery("deleteGroupAttributesByGroup").setParameter("group", group).executeUpdate();
|
||||||
|
em.createNamedQuery("deleteGroupRoleMappingsByGroup").setParameter("group", group).executeUpdate();
|
||||||
|
em.remove(groupEntity);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package org.keycloak.models.jpa;
|
package org.keycloak.models.jpa;
|
||||||
|
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
|
import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.models.OTPPolicy;
|
import org.keycloak.models.OTPPolicy;
|
||||||
import org.keycloak.models.ProtocolMapperModel;
|
import org.keycloak.models.ProtocolMapperModel;
|
||||||
import org.keycloak.models.UserConsentModel;
|
import org.keycloak.models.UserConsentModel;
|
||||||
|
@ -19,6 +20,7 @@ import org.keycloak.models.jpa.entities.UserConsentProtocolMapperEntity;
|
||||||
import org.keycloak.models.jpa.entities.UserConsentRoleEntity;
|
import org.keycloak.models.jpa.entities.UserConsentRoleEntity;
|
||||||
import org.keycloak.models.jpa.entities.UserAttributeEntity;
|
import org.keycloak.models.jpa.entities.UserAttributeEntity;
|
||||||
import org.keycloak.models.jpa.entities.UserEntity;
|
import org.keycloak.models.jpa.entities.UserEntity;
|
||||||
|
import org.keycloak.models.jpa.entities.UserGroupMembershipEntity;
|
||||||
import org.keycloak.models.jpa.entities.UserRequiredActionEntity;
|
import org.keycloak.models.jpa.entities.UserRequiredActionEntity;
|
||||||
import org.keycloak.models.jpa.entities.UserRoleMappingEntity;
|
import org.keycloak.models.jpa.entities.UserRoleMappingEntity;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
|
@ -485,6 +487,63 @@ public class UserAdapter implements UserModel {
|
||||||
em.flush();
|
em.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<GroupModel> getGroups() {
|
||||||
|
// we query ids only as the group might be cached and following the @ManyToOne will result in a load
|
||||||
|
// even if we're getting just the id.
|
||||||
|
TypedQuery<String> query = em.createNamedQuery("userGroupIds", String.class);
|
||||||
|
query.setParameter("user", getUser());
|
||||||
|
List<String> ids = query.getResultList();
|
||||||
|
Set<GroupModel> groups = new HashSet<>();
|
||||||
|
for (String groupId : ids) {
|
||||||
|
GroupModel group = realm.getGroupById(groupId);
|
||||||
|
if (group == null) continue;
|
||||||
|
groups.add(group);
|
||||||
|
}
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void joinGroup(GroupModel group) {
|
||||||
|
if (isMemberOf(group)) return;
|
||||||
|
UserGroupMembershipEntity entity = new UserGroupMembershipEntity();
|
||||||
|
entity.setUser(getUser());
|
||||||
|
entity.setGroupId(group.getId());
|
||||||
|
em.persist(entity);
|
||||||
|
em.flush();
|
||||||
|
em.detach(entity);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void leaveGroup(GroupModel group) {
|
||||||
|
if (user == null || group == null) return;
|
||||||
|
|
||||||
|
TypedQuery<UserGroupMembershipEntity> query = getUserGroupMappingQuery(group);
|
||||||
|
List<UserGroupMembershipEntity> results = query.getResultList();
|
||||||
|
if (results.size() == 0) return;
|
||||||
|
for (UserGroupMembershipEntity entity : results) {
|
||||||
|
em.remove(entity);
|
||||||
|
}
|
||||||
|
em.flush();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isMemberOf(GroupModel group) {
|
||||||
|
Set<GroupModel> roles = getGroups();
|
||||||
|
return KeycloakModelUtils.isMember(roles, group);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected TypedQuery<UserGroupMembershipEntity> getUserGroupMappingQuery(GroupModel group) {
|
||||||
|
TypedQuery<UserGroupMembershipEntity> query = em.createNamedQuery("userMemberOf", UserGroupMembershipEntity.class);
|
||||||
|
query.setParameter("user", getUser());
|
||||||
|
query.setParameter("groupId", group.getId());
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasRole(RoleModel role) {
|
public boolean hasRole(RoleModel role) {
|
||||||
Set<RoleModel> roles = getRoleMappings();
|
Set<RoleModel> roles = getRoleMappings();
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
package org.keycloak.models.jpa.entities;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.NamedQueries;
|
||||||
|
import javax.persistence.NamedQuery;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
@NamedQueries({
|
||||||
|
@NamedQuery(name="getGroupAttributesByNameAndValue", query="select attr from GroupAttributeEntity attr where attr.name = :name and attr.value = :value"),
|
||||||
|
@NamedQuery(name="deleteGroupAttributesByGroup", query="delete from GroupAttributeEntity attr where attr.group = :group"),
|
||||||
|
@NamedQuery(name="deleteGroupAttributesByRealm", query="delete from GroupAttributeEntity attr where attr.group IN (select u from GroupEntity u where u.realmId=:realmId)")
|
||||||
|
})
|
||||||
|
@Table(name="USER_ATTRIBUTE")
|
||||||
|
@Entity
|
||||||
|
public class GroupAttributeEntity {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column(name="ID", length = 36)
|
||||||
|
protected String id;
|
||||||
|
|
||||||
|
@ManyToOne(fetch= FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "GROUP_ID")
|
||||||
|
protected GroupEntity group;
|
||||||
|
|
||||||
|
@Column(name = "NAME")
|
||||||
|
protected String name;
|
||||||
|
@Column(name = "VALUE")
|
||||||
|
protected String value;
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GroupEntity getGroup() {
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGroup(GroupEntity group) {
|
||||||
|
this.group = group;
|
||||||
|
}
|
||||||
|
}
|
109
model/jpa/src/main/java/org/keycloak/models/jpa/entities/GroupEntity.java
Executable file
109
model/jpa/src/main/java/org/keycloak/models/jpa/entities/GroupEntity.java
Executable file
|
@ -0,0 +1,109 @@
|
||||||
|
package org.keycloak.models.jpa.entities;
|
||||||
|
|
||||||
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
|
|
||||||
|
import javax.persistence.CascadeType;
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.NamedQueries;
|
||||||
|
import javax.persistence.NamedQuery;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import javax.persistence.UniqueConstraint;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
@NamedQueries({
|
||||||
|
@NamedQuery(name="getAllGroupsByRealm", query="select u from GroupEntity u where u.realmId = :realmId order by u.name"),
|
||||||
|
@NamedQuery(name="getGroupById", query="select u from GroupEntity u where u.id = :id and u.realmId = :realmId"),
|
||||||
|
@NamedQuery(name="getGroupIdsByParent", query="select u.id from GroupEntity u where u.parent = :parent"),
|
||||||
|
@NamedQuery(name="getGroupByName", query="select u from GroupEntity u where u.name = :name and u.realmId = :realmId"),
|
||||||
|
@NamedQuery(name="getGroupCount", query="select count(u) from GroupEntity u where u.realmId = :realmId"),
|
||||||
|
@NamedQuery(name="deleteGroupsByRealm", query="delete from GroupEntity u where u.realmId = :realmId")
|
||||||
|
})
|
||||||
|
@Entity
|
||||||
|
@Table(name="GROUP_ENTITY")
|
||||||
|
public class GroupEntity {
|
||||||
|
@Id
|
||||||
|
@Column(name="ID", length = 36)
|
||||||
|
protected String id;
|
||||||
|
|
||||||
|
@Column(name = "NAME")
|
||||||
|
protected String name;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "PARENT_GROUP")
|
||||||
|
private GroupEntity parent;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "REALM")
|
||||||
|
private RealmEntity realm;
|
||||||
|
|
||||||
|
@OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true, mappedBy="group")
|
||||||
|
protected Collection<GroupAttributeEntity> attributes = new ArrayList<GroupAttributeEntity>();
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<GroupAttributeEntity> getAttributes() {
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttributes(Collection<GroupAttributeEntity> attributes) {
|
||||||
|
this.attributes = attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RealmEntity getRealm() {
|
||||||
|
return realm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRealm(RealmEntity realm) {
|
||||||
|
this.realm = realm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GroupEntity getParent() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParent(GroupEntity parent) {
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
|
GroupEntity that = (GroupEntity) o;
|
||||||
|
|
||||||
|
if (!id.equals(that.id)) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return id.hashCode();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
package org.keycloak.models.jpa.entities;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.IdClass;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.NamedQueries;
|
||||||
|
import javax.persistence.NamedQuery;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
@NamedQueries({
|
||||||
|
@NamedQuery(name="groupHasRole", query="select m from GroupRoleMappingEntity m where m.group = :group and m.roleId = :roleId"),
|
||||||
|
@NamedQuery(name="groupRoleMappings", query="select m from GroupRoleMappingEntity m where m.group = :group"),
|
||||||
|
@NamedQuery(name="groupRoleMappingIds", query="select m.roleId from GroupRoleMappingEntity m where m.group = :group"),
|
||||||
|
@NamedQuery(name="deleteGroupRoleMappingsByRealm", query="delete from GroupRoleMappingEntity mapping where mapping.group IN (select u from GroupEntity u where u.realmId=:realmId)"),
|
||||||
|
@NamedQuery(name="deleteGroupRoleMappingsByRole", query="delete from GroupRoleMappingEntity m where m.roleId = :roleId"),
|
||||||
|
@NamedQuery(name="deleteGroupRoleMappingsByGroup", query="delete from GroupRoleMappingEntity m where m.group = :group")
|
||||||
|
|
||||||
|
})
|
||||||
|
@Table(name="GROUP_ROLE_MAPPING")
|
||||||
|
@Entity
|
||||||
|
@IdClass(GroupRoleMappingEntity.Key.class)
|
||||||
|
public class GroupRoleMappingEntity {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@ManyToOne(fetch= FetchType.LAZY)
|
||||||
|
@JoinColumn(name="GROUP_ID")
|
||||||
|
protected GroupEntity group;
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column(name = "ROLE_ID")
|
||||||
|
protected String roleId;
|
||||||
|
|
||||||
|
public GroupEntity getGroup() {
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGroup(GroupEntity group) {
|
||||||
|
this.group = group;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRoleId() {
|
||||||
|
return roleId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRoleId(String roleId) {
|
||||||
|
this.roleId = roleId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class Key implements Serializable {
|
||||||
|
|
||||||
|
protected GroupEntity group;
|
||||||
|
|
||||||
|
protected String roleId;
|
||||||
|
|
||||||
|
public Key() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Key(GroupEntity group, String roleId) {
|
||||||
|
this.group = group;
|
||||||
|
this.roleId = roleId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GroupEntity getGroup() {
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRoleId() {
|
||||||
|
return roleId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
|
Key key = (Key) o;
|
||||||
|
|
||||||
|
if (!roleId.equals(key.roleId)) return false;
|
||||||
|
if (!group.equals(key.group)) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = group.hashCode();
|
||||||
|
result = 31 * result + roleId.hashCode();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -133,6 +133,9 @@ public class RealmEntity {
|
||||||
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
|
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
|
||||||
Collection<RoleEntity> roles = new ArrayList<RoleEntity>();
|
Collection<RoleEntity> roles = new ArrayList<RoleEntity>();
|
||||||
|
|
||||||
|
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
|
||||||
|
Collection<GroupEntity> groups = new ArrayList<GroupEntity>();
|
||||||
|
|
||||||
@ElementCollection
|
@ElementCollection
|
||||||
@MapKeyColumn(name="NAME")
|
@MapKeyColumn(name="NAME")
|
||||||
@Column(name="VALUE")
|
@Column(name="VALUE")
|
||||||
|
@ -718,5 +721,21 @@ public class RealmEntity {
|
||||||
public void setClientAuthenticationFlow(String clientAuthenticationFlow) {
|
public void setClientAuthenticationFlow(String clientAuthenticationFlow) {
|
||||||
this.clientAuthenticationFlow = clientAuthenticationFlow;
|
this.clientAuthenticationFlow = clientAuthenticationFlow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Collection<GroupEntity> getGroups() {
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGroups(Collection<GroupEntity> groups) {
|
||||||
|
this.groups = groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addGroup(GroupEntity group) {
|
||||||
|
if (groups == null) {
|
||||||
|
groups = new ArrayList<GroupEntity>();
|
||||||
|
}
|
||||||
|
groups.add(group);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
package org.keycloak.models.jpa.entities;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.IdClass;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.NamedQueries;
|
||||||
|
import javax.persistence.NamedQuery;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
@NamedQueries({
|
||||||
|
@NamedQuery(name="userMemberOf", query="select m from UserGroupMembershipEntity m where m.user = :user and m.groupId = :groupId"),
|
||||||
|
@NamedQuery(name="userGroupMembership", query="select m from UserGroupMembershipEntity m where m.user = :user"),
|
||||||
|
@NamedQuery(name="groupMembership", query="select g.user from UserGroupMembershipEntity g where g.groupId = :groupId"),
|
||||||
|
@NamedQuery(name="userGroupIds", query="select m.groupId from UserGroupMembershipEntity m where m.user = :user"),
|
||||||
|
@NamedQuery(name="deleteUserGroupMembershipByRealm", query="delete from UserGroupMembershipEntity mapping where mapping.user IN (select u from UserEntity u where u.realmId=:realmId)"),
|
||||||
|
@NamedQuery(name="deleteUserGroupMembershipsByRealmAndLink", query="delete from UserGroupMembershipEntity mapping where mapping.user IN (select u from UserEntity u where u.realmId=:realmId and u.federationLink=:link)"),
|
||||||
|
@NamedQuery(name="deleteUserGroupMembershipsByGroup", query="delete from UserGroupMembershipEntity m where m.groupId = :groupId"),
|
||||||
|
@NamedQuery(name="deleteUserGroupMembershipsByUser", query="delete from UserGroupMembershipEntity m where m.user = :user")
|
||||||
|
|
||||||
|
})
|
||||||
|
@Table(name="USER_GROUP_MEMBERSHIP")
|
||||||
|
@Entity
|
||||||
|
@IdClass(UserGroupMembershipEntity.Key.class)
|
||||||
|
public class UserGroupMembershipEntity {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@ManyToOne(fetch= FetchType.LAZY)
|
||||||
|
@JoinColumn(name="USER_ID")
|
||||||
|
protected UserEntity user;
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column(name = "GROUP_ID")
|
||||||
|
protected String groupId;
|
||||||
|
|
||||||
|
public UserEntity getUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUser(UserEntity user) {
|
||||||
|
this.user = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGroupId() {
|
||||||
|
return groupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGroupId(String groupId) {
|
||||||
|
this.groupId = groupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Key implements Serializable {
|
||||||
|
|
||||||
|
protected UserEntity user;
|
||||||
|
|
||||||
|
protected String groupId;
|
||||||
|
|
||||||
|
public Key() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Key(UserEntity user, String groupId) {
|
||||||
|
this.user = user;
|
||||||
|
this.groupId = groupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserEntity getUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGroupId() {
|
||||||
|
return groupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
|
Key key = (Key) o;
|
||||||
|
|
||||||
|
if (!groupId.equals(key.groupId)) return false;
|
||||||
|
if (!user.equals(key.user)) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = user.hashCode();
|
||||||
|
result = 31 * result + groupId.hashCode();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,225 @@
|
||||||
|
package org.keycloak.models.mongo.keycloak.adapters;
|
||||||
|
|
||||||
|
import com.mongodb.DBObject;
|
||||||
|
import com.mongodb.QueryBuilder;
|
||||||
|
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||||
|
import org.keycloak.models.ClientModel;
|
||||||
|
import org.keycloak.models.GroupModel;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.RoleContainerModel;
|
||||||
|
import org.keycloak.models.RoleModel;
|
||||||
|
import org.keycloak.models.mongo.keycloak.entities.MongoClientEntity;
|
||||||
|
import org.keycloak.models.mongo.keycloak.entities.MongoGroupEntity;
|
||||||
|
import org.keycloak.models.mongo.keycloak.entities.MongoRealmEntity;
|
||||||
|
import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
|
||||||
|
import org.keycloak.models.mongo.utils.MongoModelUtils;
|
||||||
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
|
*/
|
||||||
|
public class GroupAdapter extends AbstractMongoAdapter<MongoGroupEntity> implements GroupModel {
|
||||||
|
|
||||||
|
private final MongoGroupEntity group;
|
||||||
|
private RealmModel realm;
|
||||||
|
private KeycloakSession session;
|
||||||
|
|
||||||
|
public GroupAdapter(KeycloakSession session, RealmModel realm, MongoGroupEntity group, MongoStoreInvocationContext invContext) {
|
||||||
|
super(invContext);
|
||||||
|
this.group = group;
|
||||||
|
this.realm = realm;
|
||||||
|
this.session = session;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return group.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return group.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setName(String name) {
|
||||||
|
group.setName(name);
|
||||||
|
updateGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void updateGroup() {
|
||||||
|
super.updateMongoEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MongoGroupEntity getMongoEntity() {
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || !(o instanceof GroupModel)) return false;
|
||||||
|
|
||||||
|
GroupModel that = (GroupModel) o;
|
||||||
|
return that.getId().equals(getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return getId().hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSingleAttribute(String name, String value) {
|
||||||
|
if (group.getAttributes() == null) {
|
||||||
|
group.setAttributes(new HashMap<String, List<String>>());
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> attrValues = new ArrayList<>();
|
||||||
|
attrValues.add(value);
|
||||||
|
group.getAttributes().put(name, attrValues);
|
||||||
|
updateGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAttribute(String name, List<String> values) {
|
||||||
|
if (group.getAttributes() == null) {
|
||||||
|
group.setAttributes(new HashMap<String, List<String>>());
|
||||||
|
}
|
||||||
|
|
||||||
|
group.getAttributes().put(name, values);
|
||||||
|
updateGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAttribute(String name) {
|
||||||
|
if (group.getAttributes() == null) return;
|
||||||
|
|
||||||
|
group.getAttributes().remove(name);
|
||||||
|
updateGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFirstAttribute(String name) {
|
||||||
|
if (group.getAttributes()==null) return null;
|
||||||
|
|
||||||
|
List<String> attrValues = group.getAttributes().get(name);
|
||||||
|
return (attrValues==null || attrValues.isEmpty()) ? null : attrValues.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getAttribute(String name) {
|
||||||
|
if (group.getAttributes()==null) return Collections.<String>emptyList();
|
||||||
|
List<String> attrValues = group.getAttributes().get(name);
|
||||||
|
return (attrValues == null) ? Collections.<String>emptyList() : Collections.unmodifiableList(attrValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, List<String>> getAttributes() {
|
||||||
|
return group.getAttributes()==null ? Collections.<String, List<String>>emptyMap() : Collections.unmodifiableMap((Map) group.getAttributes());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasRole(RoleModel role) {
|
||||||
|
Set<RoleModel> roles = getRoleMappings();
|
||||||
|
return KeycloakModelUtils.hasRole(roles, role);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void grantRole(RoleModel role) {
|
||||||
|
getMongoStore().pushItemToList(group, "roleIds", role.getId(), true, invocationContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<RoleModel> getRoleMappings() {
|
||||||
|
if (group.getRoleIds() == null || group.getRoleIds().isEmpty()) return Collections.EMPTY_SET;
|
||||||
|
Set<RoleModel> roles = new HashSet<>();
|
||||||
|
for (String id : group.getRoleIds()) {
|
||||||
|
roles.add(realm.getRoleById(id));
|
||||||
|
}
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<RoleModel> getRealmRoleMappings() {
|
||||||
|
Set<RoleModel> allRoles = getRoleMappings();
|
||||||
|
|
||||||
|
// Filter to retrieve just realm roles
|
||||||
|
Set<RoleModel> realmRoles = new HashSet<RoleModel>();
|
||||||
|
for (RoleModel role : allRoles) {
|
||||||
|
if (role.getContainer() instanceof RealmModel) {
|
||||||
|
realmRoles.add(role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return realmRoles;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteRoleMapping(RoleModel role) {
|
||||||
|
if (group == null || role == null) return;
|
||||||
|
|
||||||
|
getMongoStore().pullItemFromList(group, "roleIds", role.getId(), invocationContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<RoleModel> getClientRoleMappings(ClientModel app) {
|
||||||
|
Set<RoleModel> result = new HashSet<RoleModel>();
|
||||||
|
Set<RoleModel> roles = getRoleMappings();
|
||||||
|
|
||||||
|
for (RoleModel role : roles) {
|
||||||
|
if (app.equals(role.getContainer())) {
|
||||||
|
result.add(role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GroupModel getParent() {
|
||||||
|
if (group.getParentId() == null) return null;
|
||||||
|
return realm.getGroupById(group.getParentId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<GroupModel> getSubGroups() {
|
||||||
|
Set<GroupModel> subGroups = new HashSet<>();
|
||||||
|
for (GroupModel groupModel : realm.getGroups()) {
|
||||||
|
if (groupModel.getParent().equals(this)) {
|
||||||
|
subGroups.add(groupModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return subGroups;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setParent(GroupModel group) {
|
||||||
|
this.group.setParentId(group.getId());
|
||||||
|
updateGroup();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addChild(GroupModel subGroup) {
|
||||||
|
subGroup.setParent(this);
|
||||||
|
updateGroup();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeChild(GroupModel subGroup) {
|
||||||
|
subGroup.setParent(null);
|
||||||
|
updateGroup();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,11 +7,13 @@ import org.keycloak.connections.mongo.api.MongoStore;
|
||||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||||
import org.keycloak.migration.MigrationModel;
|
import org.keycloak.migration.MigrationModel;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
|
import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RealmProvider;
|
import org.keycloak.models.RealmProvider;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.mongo.keycloak.entities.MongoClientEntity;
|
import org.keycloak.models.mongo.keycloak.entities.MongoClientEntity;
|
||||||
|
import org.keycloak.models.mongo.keycloak.entities.MongoGroupEntity;
|
||||||
import org.keycloak.models.mongo.keycloak.entities.MongoMigrationModelEntity;
|
import org.keycloak.models.mongo.keycloak.entities.MongoMigrationModelEntity;
|
||||||
import org.keycloak.models.mongo.keycloak.entities.MongoRealmEntity;
|
import org.keycloak.models.mongo.keycloak.entities.MongoRealmEntity;
|
||||||
import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
|
import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
|
||||||
|
@ -121,6 +123,14 @@ public class MongoRealmProvider implements RealmProvider {
|
||||||
return new RoleAdapter(session, realm, role, null, invocationContext);
|
return new RoleAdapter(session, realm, role, null, invocationContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GroupModel getGroupById(String id, RealmModel realm) {
|
||||||
|
MongoGroupEntity group = getMongoStore().loadEntity(MongoGroupEntity.class, id, invocationContext);
|
||||||
|
if (group == null) return null;
|
||||||
|
if (group.getRealmId() != null && !group.getRealmId().equals(realm.getId())) return null;
|
||||||
|
return new GroupAdapter(session, realm, group, invocationContext);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ClientModel getClientById(String id, RealmModel realm) {
|
public ClientModel getClientById(String id, RealmModel realm) {
|
||||||
MongoClientEntity appData = getMongoStore().loadEntity(MongoClientEntity.class, id, invocationContext);
|
MongoClientEntity appData = getMongoStore().loadEntity(MongoClientEntity.class, id, invocationContext);
|
||||||
|
|
|
@ -9,6 +9,7 @@ import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.CredentialValidationOutput;
|
import org.keycloak.models.CredentialValidationOutput;
|
||||||
import org.keycloak.models.FederatedIdentityModel;
|
import org.keycloak.models.FederatedIdentityModel;
|
||||||
|
import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.ProtocolMapperModel;
|
import org.keycloak.models.ProtocolMapperModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
@ -90,10 +91,26 @@ public class MongoUserProvider implements UserProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<UserModel> getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) {
|
||||||
|
QueryBuilder queryBuilder = new QueryBuilder()
|
||||||
|
.and("realmId").is(realm.getId());
|
||||||
|
queryBuilder.and("groupIds").is(group.getId());
|
||||||
|
DBObject sort = new BasicDBObject("username", 1);
|
||||||
|
|
||||||
|
List<MongoUserEntity> users = getMongoStore().loadEntities(MongoUserEntity.class, queryBuilder.get(), sort, firstResult, maxResults, invocationContext);
|
||||||
|
return convertUserEntities(realm, users);
|
||||||
|
}
|
||||||
|
|
||||||
protected MongoStore getMongoStore() {
|
protected MongoStore getMongoStore() {
|
||||||
return invocationContext.getMongoStore();
|
return invocationContext.getMongoStore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<UserModel> getGroupMembers(RealmModel realm, GroupModel group) {
|
||||||
|
return getGroupMembers(realm, group, -1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserModel getUserByFederatedIdentity(FederatedIdentityModel socialLink, RealmModel realm) {
|
public UserModel getUserByFederatedIdentity(FederatedIdentityModel socialLink, RealmModel realm) {
|
||||||
DBObject query = new QueryBuilder()
|
DBObject query = new QueryBuilder()
|
||||||
|
@ -411,6 +428,17 @@ public class MongoUserProvider implements UserProvider {
|
||||||
getMongoStore().updateEntities(MongoUserConsentEntity.class, query, pull, invocationContext);
|
getMongoStore().updateEntities(MongoUserConsentEntity.class, query, pull, invocationContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void preRemove(RealmModel realm, GroupModel group) {
|
||||||
|
// Remove this role from all users, which has it
|
||||||
|
DBObject query = new QueryBuilder()
|
||||||
|
.and("groupIds").is(group.getId())
|
||||||
|
.get();
|
||||||
|
|
||||||
|
DBObject pull = new BasicDBObject("$pull", query);
|
||||||
|
getMongoStore().updateEntities(MongoUserEntity.class, query, pull, invocationContext);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void preRemove(RealmModel realm, RoleModel role) {
|
public void preRemove(RealmModel realm, RoleModel role) {
|
||||||
// Remove this role from all users, which has it
|
// Remove this role from all users, which has it
|
||||||
|
|
|
@ -9,6 +9,7 @@ import org.keycloak.models.AuthenticationExecutionModel;
|
||||||
import org.keycloak.models.AuthenticationFlowModel;
|
import org.keycloak.models.AuthenticationFlowModel;
|
||||||
import org.keycloak.models.AuthenticatorConfigModel;
|
import org.keycloak.models.AuthenticatorConfigModel;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
|
import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.models.IdentityProviderMapperModel;
|
import org.keycloak.models.IdentityProviderMapperModel;
|
||||||
import org.keycloak.models.IdentityProviderModel;
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
@ -34,6 +35,7 @@ import org.keycloak.models.entities.RequiredCredentialEntity;
|
||||||
import org.keycloak.models.entities.UserFederationMapperEntity;
|
import org.keycloak.models.entities.UserFederationMapperEntity;
|
||||||
import org.keycloak.models.entities.UserFederationProviderEntity;
|
import org.keycloak.models.entities.UserFederationProviderEntity;
|
||||||
import org.keycloak.models.mongo.keycloak.entities.MongoClientEntity;
|
import org.keycloak.models.mongo.keycloak.entities.MongoClientEntity;
|
||||||
|
import org.keycloak.models.mongo.keycloak.entities.MongoGroupEntity;
|
||||||
import org.keycloak.models.mongo.keycloak.entities.MongoRealmEntity;
|
import org.keycloak.models.mongo.keycloak.entities.MongoRealmEntity;
|
||||||
import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
|
import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
|
@ -607,6 +609,47 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
||||||
return model.getRoleById(id, this);
|
return model.getRoleById(id, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GroupModel getGroupById(String id) {
|
||||||
|
return model.getGroupById(id, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GroupModel> getGroups() {
|
||||||
|
DBObject query = new QueryBuilder()
|
||||||
|
.and("realmId").is(getId())
|
||||||
|
.get();
|
||||||
|
List<MongoGroupEntity> groups = getMongoStore().loadEntities(MongoGroupEntity.class, query, invocationContext);
|
||||||
|
|
||||||
|
List<GroupModel> result = new LinkedList<>();
|
||||||
|
|
||||||
|
if (groups == null) return result;
|
||||||
|
for (MongoGroupEntity group : groups) {
|
||||||
|
result.add(new GroupAdapter(session, this, group, invocationContext));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GroupModel> getTopLevelGroups() {
|
||||||
|
List<GroupModel> all = getGroups();
|
||||||
|
Iterator<GroupModel> it = all.iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
GroupModel group = it.next();
|
||||||
|
if (group.getParent() != null) {
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return all;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeGroup(GroupModel group) {
|
||||||
|
session.users().preRemove(this, group);
|
||||||
|
return getMongoStore().removeEntity(MongoGroupEntity.class, group.getId(), invocationContext);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getDefaultRoles() {
|
public List<String> getDefaultRoles() {
|
||||||
return realm.getDefaultRoles();
|
return realm.getDefaultRoles();
|
||||||
|
|
|
@ -7,6 +7,7 @@ import com.mongodb.QueryBuilder;
|
||||||
|
|
||||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
|
import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.models.OTPPolicy;
|
import org.keycloak.models.OTPPolicy;
|
||||||
import org.keycloak.models.ProtocolMapperModel;
|
import org.keycloak.models.ProtocolMapperModel;
|
||||||
import org.keycloak.models.UserConsentModel;
|
import org.keycloak.models.UserConsentModel;
|
||||||
|
@ -450,6 +451,37 @@ public class UserAdapter extends AbstractMongoAdapter<MongoUserEntity> implement
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<GroupModel> getGroups() {
|
||||||
|
if (user.getGroupIds() == null && user.getGroupIds().size() == 0) return Collections.EMPTY_SET;
|
||||||
|
Set<GroupModel> groups = new HashSet<>();
|
||||||
|
for (String id : user.getGroupIds()) {
|
||||||
|
groups.add(realm.getGroupById(id));
|
||||||
|
}
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void joinGroup(GroupModel group) {
|
||||||
|
getMongoStore().pushItemToList(getUser(), "groupIds", group.getId(), true, invocationContext);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void leaveGroup(GroupModel group) {
|
||||||
|
if (user == null || group == null) return;
|
||||||
|
|
||||||
|
getMongoStore().pullItemFromList(getUser(), "groupIds", group.getId(), invocationContext);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isMemberOf(GroupModel group) {
|
||||||
|
if (user.getGroupIds().contains(group.getId())) return true;
|
||||||
|
Set<GroupModel> groups = getGroups();
|
||||||
|
return KeycloakModelUtils.isMember(groups, group);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasRole(RoleModel role) {
|
public boolean hasRole(RoleModel role) {
|
||||||
Set<RoleModel> roles = getRoleMappings();
|
Set<RoleModel> roles = getRoleMappings();
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
package org.keycloak.models.mongo.keycloak.entities;
|
||||||
|
|
||||||
|
import com.mongodb.DBObject;
|
||||||
|
import com.mongodb.QueryBuilder;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
import org.keycloak.connections.mongo.api.MongoCollection;
|
||||||
|
import org.keycloak.connections.mongo.api.MongoField;
|
||||||
|
import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
|
||||||
|
import org.keycloak.connections.mongo.api.MongoStore;
|
||||||
|
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||||
|
import org.keycloak.models.entities.GroupEntity;
|
||||||
|
import org.keycloak.models.entities.RoleEntity;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
@MongoCollection(collectionName = "groups")
|
||||||
|
public class MongoGroupEntity extends GroupEntity implements MongoIdentifiableEntity {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(MongoGroupEntity.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterRemove(MongoStoreInvocationContext invContext) {
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,11 +4,13 @@ import com.mongodb.DBObject;
|
||||||
import com.mongodb.QueryBuilder;
|
import com.mongodb.QueryBuilder;
|
||||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
|
import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.entities.ClientEntity;
|
import org.keycloak.models.entities.ClientEntity;
|
||||||
import org.keycloak.models.mongo.keycloak.adapters.ClientAdapter;
|
import org.keycloak.models.mongo.keycloak.adapters.ClientAdapter;
|
||||||
|
import org.keycloak.models.mongo.keycloak.adapters.GroupAdapter;
|
||||||
import org.keycloak.models.mongo.keycloak.adapters.UserAdapter;
|
import org.keycloak.models.mongo.keycloak.adapters.UserAdapter;
|
||||||
import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
|
import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
|
||||||
import org.keycloak.models.mongo.keycloak.entities.MongoUserEntity;
|
import org.keycloak.models.mongo.keycloak.entities.MongoUserEntity;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.keycloak.testsuite;
|
package org.keycloak.testsuite;
|
||||||
|
|
||||||
import org.keycloak.models.CredentialValidationOutput;
|
import org.keycloak.models.CredentialValidationOutput;
|
||||||
|
import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.UserCredentialModel;
|
import org.keycloak.models.UserCredentialModel;
|
||||||
|
@ -67,6 +68,11 @@ public class DummyUserFederationProvider implements UserFederationProvider {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void preRemove(RealmModel realm, GroupModel group) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isValid(RealmModel realm, UserModel local) {
|
public boolean isValid(RealmModel realm, UserModel local) {
|
||||||
return false;
|
return false;
|
||||||
|
|
Loading…
Reference in a new issue