diff --git a/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java index a69b412cfb..0fd18a1478 100755 --- a/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java +++ b/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java @@ -477,16 +477,16 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon ClusterProvider cluster = session.getProvider(ClusterProvider.class); cluster.registerListener(REALM_CLEAR_CACHE_EVENTS, (ClusterEvent event) -> { if (event instanceof ClearCacheEvent) { - sessionFactory.invalidate(ObjectType._ALL_); + sessionFactory.invalidate(null, ObjectType._ALL_); } }); cluster.registerListener(REALM_INVALIDATION_EVENTS, (ClusterEvent event) -> { if (event instanceof RealmUpdatedEvent) { RealmUpdatedEvent rr = (RealmUpdatedEvent) event; - sessionFactory.invalidate(ObjectType.REALM, rr.getId()); + sessionFactory.invalidate(null, ObjectType.REALM, rr.getId()); } else if (event instanceof RealmRemovedEvent) { RealmRemovedEvent rr = (RealmRemovedEvent) event; - sessionFactory.invalidate(ObjectType.REALM, rr.getId()); + sessionFactory.invalidate(null, ObjectType.REALM, rr.getId()); } }); } diff --git a/model/map/src/main/java/org/keycloak/models/map/authSession/MapRootAuthenticationSessionProviderFactory.java b/model/map/src/main/java/org/keycloak/models/map/authSession/MapRootAuthenticationSessionProviderFactory.java index 38b174e3df..a80db1a981 100644 --- a/model/map/src/main/java/org/keycloak/models/map/authSession/MapRootAuthenticationSessionProviderFactory.java +++ b/model/map/src/main/java/org/keycloak/models/map/authSession/MapRootAuthenticationSessionProviderFactory.java @@ -18,7 +18,6 @@ package org.keycloak.models.map.authSession; import org.keycloak.models.KeycloakSession; import org.keycloak.models.map.common.AbstractMapProviderFactory; -import org.keycloak.sessions.AuthenticationSessionProvider; import org.keycloak.sessions.AuthenticationSessionProviderFactory; import org.keycloak.sessions.RootAuthenticationSessionModel; @@ -26,15 +25,15 @@ import org.keycloak.sessions.RootAuthenticationSessionModel; /** * @author Martin Kanis */ -public class MapRootAuthenticationSessionProviderFactory extends AbstractMapProviderFactory - implements AuthenticationSessionProviderFactory { +public class MapRootAuthenticationSessionProviderFactory extends AbstractMapProviderFactory + implements AuthenticationSessionProviderFactory { public MapRootAuthenticationSessionProviderFactory() { - super(RootAuthenticationSessionModel.class); + super(RootAuthenticationSessionModel.class, MapRootAuthenticationSessionProvider.class); } @Override - public AuthenticationSessionProvider create(KeycloakSession session) { + public MapRootAuthenticationSessionProvider createNew(KeycloakSession session) { return new MapRootAuthenticationSessionProvider(session, getStorage(session)); } diff --git a/model/map/src/main/java/org/keycloak/models/map/client/MapClientProvider.java b/model/map/src/main/java/org/keycloak/models/map/client/MapClientProvider.java index f5207aac29..d431b627d5 100644 --- a/model/map/src/main/java/org/keycloak/models/map/client/MapClientProvider.java +++ b/model/map/src/main/java/org/keycloak/models/map/client/MapClientProvider.java @@ -17,21 +17,8 @@ package org.keycloak.models.map.client; -import org.jboss.logging.Logger; -import org.keycloak.models.ClientModel; -import org.keycloak.models.ClientModel.ClientUpdatedEvent; -import org.keycloak.models.ClientModel.SearchableFields; -import org.keycloak.models.ClientProvider; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.ModelDuplicateException; -import org.keycloak.models.RealmModel; -import org.keycloak.models.RoleModel; - -import org.keycloak.models.map.common.TimeAdapter; -import org.keycloak.models.map.storage.MapKeycloakTransaction; - import java.util.Collections; -import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Set; @@ -41,18 +28,30 @@ import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; + +import org.jboss.logging.Logger; +import org.keycloak.models.ClientModel; +import org.keycloak.models.ClientModel.ClientUpdatedEvent; +import org.keycloak.models.ClientModel.SearchableFields; +import org.keycloak.models.ClientProvider; +import org.keycloak.models.ClientScopeModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.ModelDuplicateException; +import org.keycloak.models.RealmModel; +import org.keycloak.models.RoleModel; +import org.keycloak.models.map.common.TimeAdapter; +import org.keycloak.models.map.storage.MapKeycloakTransaction; +import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator; import org.keycloak.models.map.storage.MapStorage; +import org.keycloak.models.map.storage.criteria.DefaultModelCriteria; import static org.keycloak.common.util.StackUtil.getShortStackTrace; -import org.keycloak.models.ClientScopeModel; -import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator; -import org.keycloak.models.map.storage.criteria.DefaultModelCriteria; +import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.CLIENT_AFTER_REMOVE; +import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.CLIENT_BEFORE_REMOVE; import static org.keycloak.models.map.storage.QueryParameters.Order.ASCENDING; import static org.keycloak.models.map.storage.QueryParameters.withCriteria; import static org.keycloak.models.map.storage.criteria.DefaultModelCriteria.criteria; -import java.util.HashSet; - public class MapClientProvider implements ClientProvider { private static final Logger LOG = Logger.getLogger(MapClientProvider.class); @@ -196,33 +195,19 @@ public class MapClientProvider implements ClientProvider { @Override public boolean removeClient(RealmModel realm, String id) { - if (id == null) { - return false; - } + if (id == null) return false; LOG.tracef("removeClient(%s, %s)%s", realm, id, getShortStackTrace()); - // TODO: Sending an event (and client role removal) should be extracted to store layer final ClientModel client = getClientById(realm, id); if (client == null) return false; - session.users().preRemove(realm, client); - session.roles().removeRoles(client); - session.getKeycloakSessionFactory().publish(new ClientModel.ClientRemovedEvent() { - @Override - public ClientModel getClient() { - return client; - } - - @Override - public KeycloakSession getKeycloakSession() { - return session; - } - }); - // TODO: ^^^^^^^ Up to here + session.invalidate(CLIENT_BEFORE_REMOVE, realm, client); tx.delete(id); + session.invalidate(CLIENT_AFTER_REMOVE, client); + return true; } @@ -374,6 +359,14 @@ public class MapClientProvider implements ClientProvider { } } + public void preRemove(RealmModel realm) { + LOG.tracef("preRemove(%s)%s", realm, getShortStackTrace()); + DefaultModelCriteria mcb = criteria(); + mcb = mcb.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId()); + + tx.delete(withCriteria(mcb)); + } + @Override public void close() { diff --git a/model/map/src/main/java/org/keycloak/models/map/client/MapClientProviderFactory.java b/model/map/src/main/java/org/keycloak/models/map/client/MapClientProviderFactory.java index af6a64be4b..14ad9193c7 100644 --- a/model/map/src/main/java/org/keycloak/models/map/client/MapClientProviderFactory.java +++ b/model/map/src/main/java/org/keycloak/models/map/client/MapClientProviderFactory.java @@ -16,72 +16,54 @@ */ package org.keycloak.models.map.client; -import org.keycloak.models.ClientModel; -import org.keycloak.models.map.common.AbstractMapProviderFactory; -import org.keycloak.models.ClientProvider; -import org.keycloak.models.ClientProviderFactory; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; -import org.keycloak.models.RealmModel; -import org.keycloak.models.RoleContainerModel; -import org.keycloak.models.RoleContainerModel.RoleRemovedEvent; -import org.keycloak.models.RoleModel; -import org.keycloak.provider.ProviderEvent; -import org.keycloak.provider.ProviderEventListener; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import org.keycloak.models.ClientModel; +import org.keycloak.models.map.common.AbstractMapProviderFactory; +import org.keycloak.models.ClientProviderFactory; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.RoleModel; +import org.keycloak.provider.InvalidationHandler; +import org.keycloak.provider.InvalidationHandler.InvalidableObjectType; + +import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.CLIENT_AFTER_REMOVE; +import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.REALM_BEFORE_REMOVE; +import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.ROLE_BEFORE_REMOVE; /** * * @author hmlnarik */ -public class MapClientProviderFactory extends AbstractMapProviderFactory implements ClientProviderFactory, ProviderEventListener { +public class MapClientProviderFactory extends AbstractMapProviderFactory implements ClientProviderFactory, InvalidationHandler { private final ConcurrentHashMap> REGISTERED_NODES_STORE = new ConcurrentHashMap<>(); - private Runnable onClose; - public MapClientProviderFactory() { - super(ClientModel.class); + super(ClientModel.class, MapClientProvider.class); } @Override - public void postInit(KeycloakSessionFactory factory) { - factory.register(this); - onClose = () -> factory.unregister(this); - } - - @Override - public MapClientProvider create(KeycloakSession session) { + public MapClientProvider createNew(KeycloakSession session) { return new MapClientProvider(session, getStorage(session), REGISTERED_NODES_STORE); } - @Override - public void close() { - super.close(); - onClose.run(); - } - @Override public String getHelpText() { return "Client provider"; } @Override - public void onEvent(ProviderEvent event) { - if (event instanceof RoleContainerModel.RoleRemovedEvent) { - RoleRemovedEvent e = (RoleContainerModel.RoleRemovedEvent) event; - RoleModel role = e.getRole(); - RoleContainerModel container = role.getContainer(); - RealmModel realm; - if (container instanceof RealmModel) { - realm = (RealmModel) container; - } else if (container instanceof ClientModel) { - realm = ((ClientModel) container).getRealm(); - } else { - return; - } - ((MapClientProvider) e.getKeycloakSession().getProvider(ClientProvider.class)).preRemove(realm, role); + public void invalidate(KeycloakSession session, InvalidableObjectType type, Object... params) { + if (type == REALM_BEFORE_REMOVE) { + create(session).preRemove((RealmModel) params[0]); + } else if (type == ROLE_BEFORE_REMOVE) { + create(session).preRemove((RealmModel) params[0], (RoleModel) params[1]); + } else if (type == CLIENT_AFTER_REMOVE) { + session.getKeycloakSessionFactory().publish(new ClientModel.ClientRemovedEvent() { + @Override public ClientModel getClient() { return (ClientModel) params[0]; } + @Override public KeycloakSession getKeycloakSession() { return session; } + }); } } } diff --git a/model/map/src/main/java/org/keycloak/models/map/clientscope/MapClientScopeProvider.java b/model/map/src/main/java/org/keycloak/models/map/clientscope/MapClientScopeProvider.java index 5a47075b5e..c299837639 100644 --- a/model/map/src/main/java/org/keycloak/models/map/clientscope/MapClientScopeProvider.java +++ b/model/map/src/main/java/org/keycloak/models/map/clientscope/MapClientScopeProvider.java @@ -23,7 +23,6 @@ import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; import org.jboss.logging.Logger; -import static org.keycloak.common.util.StackUtil.getShortStackTrace; import org.keycloak.models.ClientScopeModel.SearchableFields; import org.keycloak.models.ClientScopeModel; import org.keycloak.models.ClientScopeProvider; @@ -36,6 +35,9 @@ import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator; import org.keycloak.models.map.storage.criteria.DefaultModelCriteria; import org.keycloak.models.utils.KeycloakModelUtils; +import static org.keycloak.common.util.StackUtil.getShortStackTrace; +import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.CLIENT_SCOPE_AFTER_REMOVE; +import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.CLIENT_SCOPE_BEFORE_REMOVE; import static org.keycloak.models.map.storage.QueryParameters.Order.ASCENDING; import static org.keycloak.models.map.storage.QueryParameters.withCriteria; import static org.keycloak.models.map.storage.criteria.DefaultModelCriteria.criteria; @@ -106,23 +108,12 @@ public class MapClientScopeProvider implements ClientScopeProvider { ClientScopeModel clientScope = getClientScopeById(realm, id); if (clientScope == null) return false; - session.users().preRemove(clientScope); - realm.removeDefaultClientScope(clientScope); - - session.getKeycloakSessionFactory().publish(new ClientScopeModel.ClientScopeRemovedEvent() { - - @Override - public KeycloakSession getKeycloakSession() { - return session; - } - - @Override - public ClientScopeModel getClientScope() { - return clientScope; - } - }); + session.invalidate(CLIENT_SCOPE_BEFORE_REMOVE, realm, clientScope); tx.delete(id); + + session.invalidate(CLIENT_SCOPE_AFTER_REMOVE, clientScope); + return true; } @@ -150,6 +141,14 @@ public class MapClientScopeProvider implements ClientScopeProvider { : entityToAdapterFunc(realm).apply(entity); } + public void preRemove(RealmModel realm) { + LOG.tracef("preRemove(%s)%s", realm, getShortStackTrace()); + DefaultModelCriteria mcb = criteria(); + mcb = mcb.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId()); + + tx.delete(withCriteria(mcb)); + } + @Override public void close() { } diff --git a/model/map/src/main/java/org/keycloak/models/map/clientscope/MapClientScopeProviderFactory.java b/model/map/src/main/java/org/keycloak/models/map/clientscope/MapClientScopeProviderFactory.java index 60d7e6edad..ad35cc55b9 100644 --- a/model/map/src/main/java/org/keycloak/models/map/clientscope/MapClientScopeProviderFactory.java +++ b/model/map/src/main/java/org/keycloak/models/map/clientscope/MapClientScopeProviderFactory.java @@ -17,19 +17,24 @@ package org.keycloak.models.map.clientscope; import org.keycloak.models.ClientScopeModel; -import org.keycloak.models.ClientScopeProvider; import org.keycloak.models.ClientScopeProviderFactory; import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; import org.keycloak.models.map.common.AbstractMapProviderFactory; +import org.keycloak.provider.InvalidationHandler; -public class MapClientScopeProviderFactory extends AbstractMapProviderFactory implements ClientScopeProviderFactory { +import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.CLIENT_SCOPE_AFTER_REMOVE; +import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.CLIENT_SCOPE_BEFORE_REMOVE; +import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.REALM_BEFORE_REMOVE; + +public class MapClientScopeProviderFactory extends AbstractMapProviderFactory implements ClientScopeProviderFactory, InvalidationHandler { public MapClientScopeProviderFactory() { - super(ClientScopeModel.class); + super(ClientScopeModel.class, MapClientScopeProvider.class); } @Override - public ClientScopeProvider create(KeycloakSession session) { + public MapClientScopeProvider createNew(KeycloakSession session) { return new MapClientScopeProvider(session, getStorage(session)); } @@ -37,4 +42,18 @@ public class MapClientScopeProviderFactory extends AbstractMapProviderFactory modelType; + private final Class providerType; private Scope storageConfigScope; - @SuppressWarnings("unchecked") - protected AbstractMapProviderFactory(Class modelType) { + protected AbstractMapProviderFactory(Class modelType, Class providerType) { this.modelType = modelType; + this.providerType = providerType; + } + + public enum MapProviderObjectType implements InvalidationHandler.InvalidableObjectType { + CLIENT_BEFORE_REMOVE, + CLIENT_AFTER_REMOVE, + CLIENT_SCOPE_BEFORE_REMOVE, + CLIENT_SCOPE_AFTER_REMOVE, + GROUP_BEFORE_REMOVE, + GROUP_AFTER_REMOVE, + REALM_BEFORE_REMOVE, + REALM_AFTER_REMOVE, + ROLE_BEFORE_REMOVE, + ROLE_AFTER_REMOVE, + USER_BEFORE_REMOVE, + USER_AFTER_REMOVE + } + + /** + * Creates new instance of a provider. + * + * @param session + * @return See description. + */ + public abstract T createNew(KeycloakSession session); + + /** + * Returns instance of a provider. If the instance is already created within + * the session (it's found in session attributes), it's returned from there, + * otherwise new instance is created (and stored among the session attributes). + * + * @param session + * @return See description. + */ + @Override + public T create(KeycloakSession session) { + T provider = session.getAttribute(uniqueKey, providerType); + if (provider != null) { + return provider; + } + provider = createNew(session); + session.setAttribute(uniqueKey, provider); + return provider; } @Override diff --git a/model/map/src/main/java/org/keycloak/models/map/group/MapGroupProvider.java b/model/map/src/main/java/org/keycloak/models/map/group/MapGroupProvider.java index ed6077df75..23f11a4d8f 100644 --- a/model/map/src/main/java/org/keycloak/models/map/group/MapGroupProvider.java +++ b/model/map/src/main/java/org/keycloak/models/map/group/MapGroupProvider.java @@ -36,10 +36,11 @@ import org.keycloak.models.map.storage.criteria.DefaultModelCriteria; import java.util.Objects; import java.util.function.Function; import java.util.function.UnaryOperator; -import java.util.stream.Collectors; import java.util.stream.Stream; import static org.keycloak.common.util.StackUtil.getShortStackTrace; +import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.GROUP_AFTER_REMOVE; +import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.GROUP_BEFORE_REMOVE; import static org.keycloak.models.map.storage.QueryParameters.Order.ASCENDING; import static org.keycloak.models.map.storage.QueryParameters.withCriteria; import static org.keycloak.models.map.storage.criteria.DefaultModelCriteria.criteria; @@ -214,34 +215,12 @@ public class MapGroupProvider implements GroupProvider { LOG.tracef("removeGroup(%s, %s)%s", realm, group, getShortStackTrace()); if (group == null) return false; - // TODO: Sending an event (, user group removal and realm default groups) should be extracted to store layer - session.getKeycloakSessionFactory().publish(new GroupModel.GroupRemovedEvent() { - - @Override - public RealmModel getRealm() { - return realm; - } - - @Override - public GroupModel getGroup() { - return group; - } - - @Override - public KeycloakSession getKeycloakSession() { - return session; - } - }); - - session.users().preRemove(realm, group); - realm.removeDefaultGroup(group); - - group.getSubGroupsStream().collect(Collectors.toSet()).forEach(subGroup -> session.groups().removeGroup(realm, subGroup)); - - // TODO: ^^^^^^^ Up to here + session.invalidate(GROUP_BEFORE_REMOVE, realm, group); tx.delete(group.getId()); + session.invalidate(GROUP_AFTER_REMOVE, realm, group); + return true; } @@ -306,9 +285,16 @@ public class MapGroupProvider implements GroupProvider { } } + public void preRemove(RealmModel realm) { + LOG.tracef("preRemove(%s)%s", realm, getShortStackTrace()); + DefaultModelCriteria mcb = criteria(); + mcb = mcb.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId()); + + tx.delete(withCriteria(mcb)); + } + @Override public void close() { - } } diff --git a/model/map/src/main/java/org/keycloak/models/map/group/MapGroupProviderFactory.java b/model/map/src/main/java/org/keycloak/models/map/group/MapGroupProviderFactory.java index 22736d888a..a7133304f7 100644 --- a/model/map/src/main/java/org/keycloak/models/map/group/MapGroupProviderFactory.java +++ b/model/map/src/main/java/org/keycloak/models/map/group/MapGroupProviderFactory.java @@ -16,70 +16,58 @@ */ package org.keycloak.models.map.group; -import org.keycloak.models.ClientModel; +import java.util.stream.Collectors; import org.keycloak.models.GroupModel; -import org.keycloak.models.GroupProvider; import org.keycloak.models.GroupProviderFactory; import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.RealmModel; -import org.keycloak.models.RoleContainerModel; -import org.keycloak.models.RoleContainerModel.RoleRemovedEvent; import org.keycloak.models.RoleModel; - -import org.keycloak.provider.ProviderEvent; -import org.keycloak.provider.ProviderEventListener; import org.keycloak.models.map.common.AbstractMapProviderFactory; +import org.keycloak.provider.InvalidationHandler; + +import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.GROUP_AFTER_REMOVE; +import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.GROUP_BEFORE_REMOVE; +import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.REALM_BEFORE_REMOVE; +import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.ROLE_BEFORE_REMOVE; /** * * @author mhajas */ -public class MapGroupProviderFactory extends AbstractMapProviderFactory implements GroupProviderFactory, ProviderEventListener { - - private Runnable onClose; +public class MapGroupProviderFactory extends AbstractMapProviderFactory implements GroupProviderFactory, InvalidationHandler { public MapGroupProviderFactory() { - super(GroupModel.class); + super(GroupModel.class, MapGroupProvider.class); } @Override - public void postInit(KeycloakSessionFactory factory) { - factory.register(this); - onClose = () -> factory.unregister(this); - } - - @Override - public MapGroupProvider create(KeycloakSession session) { + public MapGroupProvider createNew(KeycloakSession session) { return new MapGroupProvider(session, getStorage(session)); } - @Override - public void close() { - super.close(); - onClose.run(); - } - @Override public String getHelpText() { return "Group provider"; } @Override - public void onEvent(ProviderEvent event) { - if (event instanceof RoleContainerModel.RoleRemovedEvent) { - RoleRemovedEvent e = (RoleContainerModel.RoleRemovedEvent) event; - RoleModel role = e.getRole(); - RoleContainerModel container = role.getContainer(); - RealmModel realm; - if (container instanceof RealmModel) { - realm = (RealmModel) container; - } else if (container instanceof ClientModel) { - realm = ((ClientModel) container).getRealm(); - } else { - return; - } - ((MapGroupProvider) e.getKeycloakSession().getProvider(GroupProvider.class)).preRemove(realm, role); + public void invalidate(KeycloakSession session, InvalidableObjectType type, Object... params) { + if (type == REALM_BEFORE_REMOVE) { + create(session).preRemove((RealmModel) params[0]); + } else if (type == ROLE_BEFORE_REMOVE) { + create(session).preRemove((RealmModel) params[0], (RoleModel) params[1]); + } else if (type == GROUP_BEFORE_REMOVE) { + RealmModel realm = (RealmModel) params[0]; + GroupModel group = (GroupModel) params[1]; + + realm.removeDefaultGroup(group); + group.getSubGroupsStream().collect(Collectors.toSet()).forEach(subGroup -> create(session).removeGroup(realm, subGroup)); + } else if (type == GROUP_AFTER_REMOVE) { + session.getKeycloakSessionFactory().publish(new GroupModel.GroupRemovedEvent() { + @Override public RealmModel getRealm() { return (RealmModel) params[0]; } + @Override public GroupModel getGroup() { return (GroupModel) params[1]; } + @Override public KeycloakSession getKeycloakSession() { return session; } + }); } } } diff --git a/model/map/src/main/java/org/keycloak/models/map/loginFailure/MapUserLoginFailureProviderFactory.java b/model/map/src/main/java/org/keycloak/models/map/loginFailure/MapUserLoginFailureProviderFactory.java index e03b6ef9d4..63b4c9a70e 100644 --- a/model/map/src/main/java/org/keycloak/models/map/loginFailure/MapUserLoginFailureProviderFactory.java +++ b/model/map/src/main/java/org/keycloak/models/map/loginFailure/MapUserLoginFailureProviderFactory.java @@ -17,43 +17,28 @@ package org.keycloak.models.map.loginFailure; import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.RealmModel; -import org.keycloak.models.UserLoginFailureProvider; import org.keycloak.models.UserLoginFailureProviderFactory; import org.keycloak.models.UserLoginFailureModel; import org.keycloak.models.UserModel; -import org.keycloak.models.map.common.AbstractEntity; import org.keycloak.models.map.common.AbstractMapProviderFactory; -import org.keycloak.provider.ProviderEvent; -import org.keycloak.provider.ProviderEventListener; +import org.keycloak.provider.InvalidationHandler; + +import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.REALM_BEFORE_REMOVE; +import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.USER_BEFORE_REMOVE; /** * @author Martin Kanis */ -public class MapUserLoginFailureProviderFactory extends AbstractMapProviderFactory - implements UserLoginFailureProviderFactory, ProviderEventListener { - - private Runnable onClose; +public class MapUserLoginFailureProviderFactory extends AbstractMapProviderFactory + implements UserLoginFailureProviderFactory, InvalidationHandler { public MapUserLoginFailureProviderFactory() { - super(UserLoginFailureModel.class); + super(UserLoginFailureModel.class, MapUserLoginFailureProvider.class); } @Override - public void postInit(KeycloakSessionFactory factory) { - factory.register(this); - onClose = () -> factory.unregister(this); - } - - @Override - public void close() { - super.close(); - onClose.run(); - } - - @Override - public MapUserLoginFailureProvider create(KeycloakSession session) { + public MapUserLoginFailureProvider createNew(KeycloakSession session) { return new MapUserLoginFailureProvider(session, getStorage(session)); } @@ -63,17 +48,11 @@ public class MapUserLoginFailureProviderFactory extends AbstractMapProviderFacto } @Override - public void onEvent(ProviderEvent event) { - if (event instanceof UserModel.UserRemovedEvent) { - UserModel.UserRemovedEvent userRemovedEvent = (UserModel.UserRemovedEvent) event; - - MapUserLoginFailureProvider provider = MapUserLoginFailureProviderFactory.this.create(userRemovedEvent.getKeycloakSession()); - provider.removeUserLoginFailure(userRemovedEvent.getRealm(), userRemovedEvent.getUser().getId()); - } else if (event instanceof RealmModel.RealmRemovedEvent) { - RealmModel.RealmRemovedEvent realmRemovedEvent = (RealmModel.RealmRemovedEvent) event; - - MapUserLoginFailureProvider provider = MapUserLoginFailureProviderFactory.this.create(realmRemovedEvent.getKeycloakSession()); - provider.removeAllUserLoginFailures(realmRemovedEvent.getRealm()); + public void invalidate(KeycloakSession session, InvalidableObjectType type, Object... params) { + if (type == REALM_BEFORE_REMOVE) { + create(session).removeAllUserLoginFailures((RealmModel) params[0]); + } else if (type == USER_BEFORE_REMOVE) { + create(session).removeUserLoginFailure((RealmModel) params[0], ((UserModel) params[1]).getId()); } } diff --git a/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmProvider.java b/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmProvider.java index 4699ab0226..669981fd9b 100644 --- a/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmProvider.java +++ b/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmProvider.java @@ -22,7 +22,6 @@ import java.util.Map; import java.util.Set; import java.util.stream.Stream; import org.jboss.logging.Logger; -import static org.keycloak.common.util.StackUtil.getShortStackTrace; import org.keycloak.models.ClientModel; import org.keycloak.models.ClientScopeModel; import org.keycloak.models.GroupModel; @@ -37,6 +36,10 @@ import org.keycloak.models.map.storage.MapStorage; import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator; import org.keycloak.models.map.storage.criteria.DefaultModelCriteria; import org.keycloak.models.utils.KeycloakModelUtils; + +import static org.keycloak.common.util.StackUtil.getShortStackTrace; +import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.REALM_AFTER_REMOVE; +import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.REALM_BEFORE_REMOVE; import static org.keycloak.models.map.storage.QueryParameters.Order.ASCENDING; import static org.keycloak.models.map.storage.QueryParameters.withCriteria; import static org.keycloak.models.map.storage.criteria.DefaultModelCriteria.criteria; @@ -135,27 +138,12 @@ public class MapRealmProvider implements RealmProvider { if (realm == null) return false; - session.users().preRemove(realm); - session.clients().removeClients(realm); - session.clientScopes().removeClientScopes(realm); - session.roles().removeRoles(realm); - realm.getTopLevelGroupsStream().forEach(realm::removeGroup); - - // TODO: Sending an event should be extracted to store layer - session.getKeycloakSessionFactory().publish(new RealmModel.RealmRemovedEvent() { - @Override - public RealmModel getRealm() { - return realm; - } - - @Override - public KeycloakSession getKeycloakSession() { - return session; - } - }); - // TODO: ^^^^^^^ Up to here + session.invalidate(REALM_BEFORE_REMOVE, realm); tx.delete(id); + + session.invalidate(REALM_AFTER_REMOVE, realm); + return true; } diff --git a/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmProviderFactory.java b/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmProviderFactory.java index 6a1431a95d..7c98202eac 100644 --- a/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmProviderFactory.java +++ b/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmProviderFactory.java @@ -19,22 +19,34 @@ package org.keycloak.models.map.realm; import org.keycloak.models.map.common.AbstractMapProviderFactory; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; -import org.keycloak.models.RealmProvider; import org.keycloak.models.RealmProviderFactory; +import org.keycloak.provider.InvalidationHandler; -public class MapRealmProviderFactory extends AbstractMapProviderFactory implements RealmProviderFactory { +import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.REALM_AFTER_REMOVE; + +public class MapRealmProviderFactory extends AbstractMapProviderFactory implements RealmProviderFactory, InvalidationHandler { public MapRealmProviderFactory() { - super(RealmModel.class); + super(RealmModel.class, MapRealmProvider.class); } @Override - public RealmProvider create(KeycloakSession session) { + public MapRealmProvider createNew(KeycloakSession session) { return new MapRealmProvider(session, getStorage(session)); } @Override public String getHelpText() { return "Realm provider"; - } + } + + @Override + public void invalidate(KeycloakSession session, InvalidableObjectType type, Object... params) { + if (type == REALM_AFTER_REMOVE) { + session.getKeycloakSessionFactory().publish(new RealmModel.RealmRemovedEvent() { + @Override public RealmModel getRealm() { return (RealmModel) params[0]; } + @Override public KeycloakSession getKeycloakSession() { return session; } + }); + } + } } diff --git a/model/map/src/main/java/org/keycloak/models/map/role/MapRoleProvider.java b/model/map/src/main/java/org/keycloak/models/map/role/MapRoleProvider.java index 734036abf2..767d700b10 100644 --- a/model/map/src/main/java/org/keycloak/models/map/role/MapRoleProvider.java +++ b/model/map/src/main/java/org/keycloak/models/map/role/MapRoleProvider.java @@ -17,30 +17,29 @@ package org.keycloak.models.map.role; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Stream; import org.jboss.logging.Logger; import org.keycloak.models.ClientModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.RealmModel; - import org.keycloak.models.RoleModel; import org.keycloak.models.map.storage.MapKeycloakTransaction; - -import java.util.Objects; -import java.util.function.Function; -import java.util.stream.Stream; import org.keycloak.models.map.storage.MapStorage; -import static org.keycloak.common.util.StackUtil.getShortStackTrace; -import static org.keycloak.models.map.storage.QueryParameters.Order.ASCENDING; -import static org.keycloak.models.map.storage.QueryParameters.withCriteria; -import static org.keycloak.models.map.storage.criteria.DefaultModelCriteria.criteria; - -import org.keycloak.models.RoleContainerModel; import org.keycloak.models.RoleModel.SearchableFields; import org.keycloak.models.RoleProvider; import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator; import org.keycloak.models.map.storage.criteria.DefaultModelCriteria; +import static org.keycloak.common.util.StackUtil.getShortStackTrace; +import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.ROLE_AFTER_REMOVE; +import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.ROLE_BEFORE_REMOVE; +import static org.keycloak.models.map.storage.QueryParameters.Order.ASCENDING; +import static org.keycloak.models.map.storage.QueryParameters.withCriteria; +import static org.keycloak.models.map.storage.criteria.DefaultModelCriteria.criteria; + public class MapRoleProvider implements RoleProvider { private static final Logger LOG = Logger.getLogger(MapRoleProvider.class); @@ -161,24 +160,12 @@ public class MapRoleProvider implements RoleProvider { RealmModel realm = role.isClientRole() ? ((ClientModel)role.getContainer()).getRealm() : (RealmModel)role.getContainer(); - session.users().preRemove(realm, role); - - // TODO: Sending an event should be extracted to store layer - session.getKeycloakSessionFactory().publish(new RoleContainerModel.RoleRemovedEvent() { - @Override - public RoleModel getRole() { - return role; - } - - @Override - public KeycloakSession getKeycloakSession() { - return session; - } - }); - // TODO: ^^^^^^^ Up to here + session.invalidate(ROLE_BEFORE_REMOVE, realm, role); tx.delete(role.getId()); + session.invalidate(ROLE_AFTER_REMOVE, realm, role); + return true; } @@ -284,6 +271,14 @@ public class MapRoleProvider implements RoleProvider { .map(entityToAdapterFunc(client.getRealm())); } + public void preRemove(RealmModel realm) { + LOG.tracef("preRemove(%s)%s", realm, getShortStackTrace()); + DefaultModelCriteria mcb = criteria(); + mcb = mcb.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId()); + + tx.delete(withCriteria(mcb)); + } + @Override public void close() { } diff --git a/model/map/src/main/java/org/keycloak/models/map/role/MapRoleProviderFactory.java b/model/map/src/main/java/org/keycloak/models/map/role/MapRoleProviderFactory.java index 0bbf3c8e3c..74b7420ee0 100644 --- a/model/map/src/main/java/org/keycloak/models/map/role/MapRoleProviderFactory.java +++ b/model/map/src/main/java/org/keycloak/models/map/role/MapRoleProviderFactory.java @@ -16,20 +16,27 @@ */ package org.keycloak.models.map.role; +import org.keycloak.models.ClientModel; import org.keycloak.models.map.common.AbstractMapProviderFactory; import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.RoleContainerModel; import org.keycloak.models.RoleModel; -import org.keycloak.models.RoleProvider; import org.keycloak.models.RoleProviderFactory; +import org.keycloak.provider.InvalidationHandler; -public class MapRoleProviderFactory extends AbstractMapProviderFactory implements RoleProviderFactory { +import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.CLIENT_BEFORE_REMOVE; +import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.REALM_BEFORE_REMOVE; +import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.ROLE_AFTER_REMOVE; + +public class MapRoleProviderFactory extends AbstractMapProviderFactory implements RoleProviderFactory, InvalidationHandler { public MapRoleProviderFactory() { - super(RoleModel.class); + super(RoleModel.class, MapRoleProvider.class); } @Override - public RoleProvider create(KeycloakSession session) { + public MapRoleProvider createNew(KeycloakSession session) { return new MapRoleProvider(session, getStorage(session)); } @@ -37,4 +44,18 @@ public class MapRoleProviderFactory extends AbstractMapProviderFactory userById = getEntityById(realm, userId); if (userById.isPresent()) { + session.invalidate(USER_BEFORE_REMOVE, realm, user); + tx.delete(userId); + + session.invalidate(USER_AFTER_REMOVE, realm, user); return true; } diff --git a/model/map/src/main/java/org/keycloak/models/map/user/MapUserProviderFactory.java b/model/map/src/main/java/org/keycloak/models/map/user/MapUserProviderFactory.java index 3a4a85f6b2..1fac8b044c 100644 --- a/model/map/src/main/java/org/keycloak/models/map/user/MapUserProviderFactory.java +++ b/model/map/src/main/java/org/keycloak/models/map/user/MapUserProviderFactory.java @@ -17,24 +17,35 @@ package org.keycloak.models.map.user; +import org.keycloak.models.ClientModel; +import org.keycloak.models.ClientScopeModel; +import org.keycloak.models.GroupModel; import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.RoleModel; import org.keycloak.models.UserModel; -import org.keycloak.models.UserProvider; import org.keycloak.models.UserProviderFactory; import org.keycloak.models.map.common.AbstractMapProviderFactory; +import org.keycloak.provider.InvalidationHandler; + +import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.CLIENT_BEFORE_REMOVE; +import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.CLIENT_SCOPE_BEFORE_REMOVE; +import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.GROUP_BEFORE_REMOVE; +import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.REALM_BEFORE_REMOVE; +import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.ROLE_BEFORE_REMOVE; /** * * @author mhajas */ -public class MapUserProviderFactory extends AbstractMapProviderFactory implements UserProviderFactory { +public class MapUserProviderFactory extends AbstractMapProviderFactory implements UserProviderFactory, InvalidationHandler { public MapUserProviderFactory() { - super(UserModel.class); + super(UserModel.class, MapUserProvider.class); } @Override - public UserProvider create(KeycloakSession session) { + public MapUserProvider createNew(KeycloakSession session) { return new MapUserProvider(session, getStorage(session)); } @@ -43,4 +54,18 @@ public class MapUserProviderFactory extends AbstractMapProviderFactoryMartin Kanis */ -public class MapUserSessionProviderFactory implements AmphibianProviderFactory, UserSessionProviderFactory, ProviderEventListener, EnvironmentDependentProviderFactory { +public class MapUserSessionProviderFactory implements AmphibianProviderFactory, UserSessionProviderFactory, EnvironmentDependentProviderFactory, InvalidationHandler { public static final String CONFIG_STORAGE_USER_SESSIONS = "storage-user-sessions"; public static final String CONFIG_STORAGE_CLIENT_SESSIONS = "storage-client-sessions"; public static final String PROVIDER_ID = AbstractMapProviderFactory.PROVIDER_ID; + private final String uniqueKey = getClass().getName() + uniqueCounter.incrementAndGet(); + private Scope storageConfigScopeUserSessions; private Scope storageConfigScopeClientSessions; - private Runnable onClose; - @Override public String getId() { return PROVIDER_ID; @@ -64,14 +66,11 @@ public class MapUserSessionProviderFactory implements AmphibianProviderF @Override public void postInit(KeycloakSessionFactory factory) { - factory.register(this); - onClose = () -> factory.unregister(this); } @Override public void close() { AmphibianProviderFactory.super.close(); - onClose.run(); } @Override @@ -79,7 +78,12 @@ public class MapUserSessionProviderFactory implements AmphibianProviderF } @Override + @SuppressWarnings("unchecked") public MapUserSessionProvider create(KeycloakSession session) { + MapUserSessionProvider provider = session.getAttribute(uniqueKey, MapUserSessionProvider.class); + + if (provider != null) return provider; + MapStorageProviderFactory storageProviderFactoryUs = (MapStorageProviderFactory) getComponentFactory(session.getKeycloakSessionFactory(), MapStorageProvider.class, storageConfigScopeUserSessions, MapStorageSpi.NAME); final MapStorageProvider factoryUs = storageProviderFactoryUs.create(session); @@ -90,7 +94,9 @@ public class MapUserSessionProviderFactory implements AmphibianProviderF final MapStorageProvider factoryCs = storageProviderFactoryCs.create(session); MapStorage clientSessionStore = factoryCs.getStorage(AuthenticatedClientSessionModel.class); - return new MapUserSessionProvider(session, userSessionStore, clientSessionStore); + provider = new MapUserSessionProvider(session, userSessionStore, clientSessionStore); + session.setAttribute(uniqueKey, provider); + return provider; } @Override @@ -99,12 +105,9 @@ public class MapUserSessionProviderFactory implements AmphibianProviderF } @Override - public void onEvent(ProviderEvent event) { - if (event instanceof UserModel.UserRemovedEvent) { - UserModel.UserRemovedEvent userRemovedEvent = (UserModel.UserRemovedEvent) event; - - MapUserSessionProvider provider = MapUserSessionProviderFactory.this.create(userRemovedEvent.getKeycloakSession()); - provider.removeUserSessions(userRemovedEvent.getRealm(), userRemovedEvent.getUser()); + public void invalidate(KeycloakSession session, InvalidableObjectType type, Object... params) { + if (type == USER_BEFORE_REMOVE) { + create(session).removeUserSessions((RealmModel) params[0], (UserModel) params[1]); } } diff --git a/server-spi-private/src/main/java/org/keycloak/models/ClientProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/models/ClientProviderFactory.java index 3117fd080c..c8eefb29a4 100644 --- a/server-spi-private/src/main/java/org/keycloak/models/ClientProviderFactory.java +++ b/server-spi-private/src/main/java/org/keycloak/models/ClientProviderFactory.java @@ -19,5 +19,5 @@ package org.keycloak.models; import org.keycloak.provider.ProviderFactory; -public interface ClientProviderFactory extends ProviderFactory { +public interface ClientProviderFactory extends ProviderFactory { } diff --git a/server-spi-private/src/main/java/org/keycloak/models/ClientScopeProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/models/ClientScopeProviderFactory.java index a1b0fca590..f10ac36395 100644 --- a/server-spi-private/src/main/java/org/keycloak/models/ClientScopeProviderFactory.java +++ b/server-spi-private/src/main/java/org/keycloak/models/ClientScopeProviderFactory.java @@ -19,5 +19,5 @@ package org.keycloak.models; import org.keycloak.provider.ProviderFactory; -public interface ClientScopeProviderFactory extends ProviderFactory { +public interface ClientScopeProviderFactory extends ProviderFactory { } diff --git a/server-spi-private/src/main/java/org/keycloak/models/GroupProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/models/GroupProviderFactory.java index 2d1e783bae..7409a7acf8 100644 --- a/server-spi-private/src/main/java/org/keycloak/models/GroupProviderFactory.java +++ b/server-spi-private/src/main/java/org/keycloak/models/GroupProviderFactory.java @@ -19,5 +19,5 @@ package org.keycloak.models; import org.keycloak.provider.ProviderFactory; -public interface GroupProviderFactory extends ProviderFactory { +public interface GroupProviderFactory extends ProviderFactory { } diff --git a/server-spi-private/src/main/java/org/keycloak/models/RealmProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/models/RealmProviderFactory.java index 95ca89479a..e73d379a02 100755 --- a/server-spi-private/src/main/java/org/keycloak/models/RealmProviderFactory.java +++ b/server-spi-private/src/main/java/org/keycloak/models/RealmProviderFactory.java @@ -23,5 +23,5 @@ import org.keycloak.provider.ProviderFactory; * @author Bill Burke * @version $Revision: 1 $ */ -public interface RealmProviderFactory extends ProviderFactory { +public interface RealmProviderFactory extends ProviderFactory { } diff --git a/server-spi-private/src/main/java/org/keycloak/models/RoleProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/models/RoleProviderFactory.java index 3d49f65b21..7293caed5d 100644 --- a/server-spi-private/src/main/java/org/keycloak/models/RoleProviderFactory.java +++ b/server-spi-private/src/main/java/org/keycloak/models/RoleProviderFactory.java @@ -19,5 +19,5 @@ package org.keycloak.models; import org.keycloak.provider.ProviderFactory; -public interface RoleProviderFactory extends ProviderFactory { +public interface RoleProviderFactory extends ProviderFactory { } diff --git a/server-spi-private/src/main/java/org/keycloak/models/UserLoginFailureProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/models/UserLoginFailureProviderFactory.java index 192d934564..9c27d669ed 100644 --- a/server-spi-private/src/main/java/org/keycloak/models/UserLoginFailureProviderFactory.java +++ b/server-spi-private/src/main/java/org/keycloak/models/UserLoginFailureProviderFactory.java @@ -21,6 +21,6 @@ import org.keycloak.provider.ProviderFactory; /** * @author Martin Kanis */ -public interface UserLoginFailureProviderFactory extends ProviderFactory { +public interface UserLoginFailureProviderFactory extends ProviderFactory { } diff --git a/server-spi-private/src/main/java/org/keycloak/models/UserProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/models/UserProviderFactory.java index 6cd9f136ab..91a587197a 100755 --- a/server-spi-private/src/main/java/org/keycloak/models/UserProviderFactory.java +++ b/server-spi-private/src/main/java/org/keycloak/models/UserProviderFactory.java @@ -23,5 +23,5 @@ import org.keycloak.provider.ProviderFactory; * @author Bill Burke * @version $Revision: 1 $ */ -public interface UserProviderFactory extends ProviderFactory { +public interface UserProviderFactory extends ProviderFactory { } diff --git a/server-spi-private/src/main/java/org/keycloak/sessions/AuthenticationSessionProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/sessions/AuthenticationSessionProviderFactory.java index b182458b5e..442a44415f 100644 --- a/server-spi-private/src/main/java/org/keycloak/sessions/AuthenticationSessionProviderFactory.java +++ b/server-spi-private/src/main/java/org/keycloak/sessions/AuthenticationSessionProviderFactory.java @@ -22,5 +22,5 @@ import org.keycloak.provider.ProviderFactory; /** * @author Marek Posolda */ -public interface AuthenticationSessionProviderFactory extends ProviderFactory { +public interface AuthenticationSessionProviderFactory extends ProviderFactory { } diff --git a/server-spi/src/main/java/org/keycloak/models/KeycloakSession.java b/server-spi/src/main/java/org/keycloak/models/KeycloakSession.java index 6442a2a8d4..bb2a838755 100755 --- a/server-spi/src/main/java/org/keycloak/models/KeycloakSession.java +++ b/server-spi/src/main/java/org/keycloak/models/KeycloakSession.java @@ -19,7 +19,7 @@ package org.keycloak.models; import org.keycloak.component.ComponentModel; import org.keycloak.models.cache.UserCache; -import org.keycloak.provider.InvalidationHandler; +import org.keycloak.provider.InvalidationHandler.InvalidableObjectType; import org.keycloak.provider.Provider; import org.keycloak.services.clientpolicy.ClientPolicyManager; import org.keycloak.sessions.AuthenticationSessionProvider; @@ -33,7 +33,7 @@ import java.util.function.Function; * @author Bill Burke * @version $Revision: 1 $ */ -public interface KeycloakSession extends InvalidationHandler { +public interface KeycloakSession { KeycloakContext getContext(); @@ -128,10 +128,9 @@ public interface KeycloakSession extends InvalidationHandler { /** * Invalidates intermediate states of the given objects, both immediately and at the end of this session. * @param type Type of the objects to invalidate - * @param ids Identifiers of the invalidated objects + * @param params Parameters used for the invalidation */ - @Override - void invalidate(InvalidableObjectType type, Object... ids); + void invalidate(InvalidableObjectType type, Object... params); void enlistForClose(Provider provider); diff --git a/server-spi/src/main/java/org/keycloak/models/RealmModel.java b/server-spi/src/main/java/org/keycloak/models/RealmModel.java index a2999583c9..8717a7a40b 100755 --- a/server-spi/src/main/java/org/keycloak/models/RealmModel.java +++ b/server-spi/src/main/java/org/keycloak/models/RealmModel.java @@ -29,11 +29,9 @@ import org.keycloak.storage.client.ClientStorageProvider; import org.keycloak.storage.client.ClientStorageProviderModel; import org.keycloak.storage.role.RoleStorageProvider; import org.keycloak.storage.role.RoleStorageProviderModel; -import org.keycloak.utils.StringUtil; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; diff --git a/server-spi/src/main/java/org/keycloak/provider/InvalidationHandler.java b/server-spi/src/main/java/org/keycloak/provider/InvalidationHandler.java index 462305a3c7..c23f681954 100644 --- a/server-spi/src/main/java/org/keycloak/provider/InvalidationHandler.java +++ b/server-spi/src/main/java/org/keycloak/provider/InvalidationHandler.java @@ -16,6 +16,8 @@ */ package org.keycloak.provider; +import org.keycloak.models.KeycloakSession; + /** * Handles invalidation requests. This interface is specifically implemented by * providers that implement a cache of objects that might change in the outside. @@ -40,9 +42,10 @@ public interface InvalidationHandler { /** * Invalidates intermediate states of the given objects + * @param session KeycloakSession * @param type Type of the objects to invalidate - * @param ids Identifiers of the invalidated objects + * @param params Parameters used for the invalidation */ - void invalidate(InvalidableObjectType type, Object... ids); + void invalidate(KeycloakSession session, InvalidableObjectType type, Object... params); } diff --git a/services/src/main/java/org/keycloak/services/DefaultComponentFactoryProviderFactory.java b/services/src/main/java/org/keycloak/services/DefaultComponentFactoryProviderFactory.java index e2d34f8e28..6d358c228f 100644 --- a/services/src/main/java/org/keycloak/services/DefaultComponentFactoryProviderFactory.java +++ b/services/src/main/java/org/keycloak/services/DefaultComponentFactoryProviderFactory.java @@ -23,6 +23,7 @@ import org.keycloak.common.util.StackUtil; import org.keycloak.component.ComponentFactoryProviderFactory; import org.keycloak.component.ComponentModel; import org.keycloak.component.ComponentModelScope; +import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.provider.InvalidationHandler; @@ -155,7 +156,7 @@ public class DefaultComponentFactoryProviderFactory implements ComponentFactoryP } @Override - public void invalidate(InvalidableObjectType type, Object... ids) { + public void invalidate(KeycloakSession session, InvalidableObjectType type, Object... ids) { if (LOG.isDebugEnabled()) { LOG.debugf("Invalidating %s: %s", type, Arrays.asList(ids)); } @@ -169,25 +170,25 @@ public class DefaultComponentFactoryProviderFactory implements ComponentFactoryP Stream.of(ids) .map(componentsMap.get()::remove).filter(Objects::nonNull) .forEach(ProviderFactory::close); - propagateInvalidation(componentsMap.get(), type, ids); + propagateInvalidation(session, componentsMap.get(), type, ids); } else if (type == ObjectType.REALM || type == ObjectType.PROVIDER_FACTORY) { Stream.of(ids) .map(dependentInvalidations::get).filter(Objects::nonNull).flatMap(Collection::stream) .map(componentsMap.get()::remove).filter(Objects::nonNull) .forEach(ProviderFactory::close); Stream.of(ids).forEach(dependentInvalidations::remove); - propagateInvalidation(componentsMap.get(), type, ids); + propagateInvalidation(session, componentsMap.get(), type, ids); } else { - propagateInvalidation(componentsMap.get(), type, ids); + propagateInvalidation(session, componentsMap.get(), type, ids); } } - private void propagateInvalidation(ConcurrentMap componentsMap, InvalidableObjectType type, Object[] ids) { + private void propagateInvalidation(KeycloakSession session, ConcurrentMap componentsMap, InvalidableObjectType type, Object[] ids) { componentsMap.values() .stream() .filter(InvalidationHandler.class::isInstance) .map(InvalidationHandler.class::cast) - .forEach(ih -> ih.invalidate(type, ids)); + .forEach(ih -> ih.invalidate(session, type, ids)); } @Override diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java index 69e71e4e9f..cd423c8df1 100644 --- a/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java +++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java @@ -41,10 +41,11 @@ import org.keycloak.models.UserSessionProvider; import org.keycloak.models.cache.CacheRealmProvider; import org.keycloak.models.cache.UserCache; import org.keycloak.models.utils.KeycloakModelUtils; +import org.keycloak.provider.InvalidationHandler.InvalidableObjectType; +import org.keycloak.provider.InvalidationHandler.ObjectType; import org.keycloak.provider.Provider; import org.keycloak.provider.ProviderFactory; import org.keycloak.services.clientpolicy.ClientPolicyManager; -import org.keycloak.services.clientpolicy.DefaultClientPolicyManager; import org.keycloak.sessions.AuthenticationSessionProvider; import org.keycloak.storage.ClientStorageManager; import org.keycloak.storage.ClientScopeStorageManager; @@ -169,8 +170,10 @@ public class DefaultKeycloakSession implements KeycloakSession { @Override public void invalidate(InvalidableObjectType type, Object... ids) { - factory.invalidate(type, ids); - invalidationMap.computeIfAbsent(type, o -> new HashSet<>()).addAll(Arrays.asList(ids)); + factory.invalidate(this, type, ids); + if (type == ObjectType.PROVIDER_FACTORY) { + invalidationMap.computeIfAbsent(type, o -> new HashSet<>()).addAll(Arrays.asList(ids)); + } } @Override @@ -524,7 +527,7 @@ public class DefaultKeycloakSession implements KeycloakSession { providers.values().forEach(safeClose); closable.forEach(safeClose); for (Entry> me : invalidationMap.entrySet()) { - factory.invalidate(me.getKey(), me.getValue().toArray()); + factory.invalidate(this, me.getKey(), me.getValue().toArray()); } } diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java index af506feeca..8ebc320332 100755 --- a/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java +++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java @@ -174,7 +174,7 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr checkProvider(); boolean cfChanged = false; for (ProviderFactory factory : undeployed) { - invalidate(ObjectType.PROVIDER_FACTORY, factory.getClass()); + invalidate(null, ObjectType.PROVIDER_FACTORY, factory.getClass()); factory.close(); cfChanged |= (componentFactoryPF == factory); } @@ -359,13 +359,13 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr } @Override - public void invalidate(InvalidableObjectType type, Object... ids) { + public void invalidate(KeycloakSession session, InvalidableObjectType type, Object... ids) { factoriesMap.values().stream() .map(Map::values) .flatMap(Collection::stream) .filter(InvalidationHandler.class::isInstance) .map(InvalidationHandler.class::cast) - .forEach(ih -> ih.invalidate(type, ids)); + .forEach(ih -> ih.invalidate(session, type, ids)); } @Override diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java index 448aedc25e..1b44f45610 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java @@ -36,6 +36,7 @@ import org.keycloak.admin.client.resource.UsersResource; import org.keycloak.common.Profile; import org.keycloak.common.util.KeycloakUriBuilder; import org.keycloak.common.util.Time; +import org.keycloak.models.RealmProvider; import org.keycloak.models.cache.CacheRealmProvider; import org.keycloak.models.cache.UserCache; import org.keycloak.representations.idm.ClientRepresentation; @@ -720,6 +721,25 @@ public abstract class AbstractKeycloakTest { return isProduct ? Profile.PRODUCT_NAME : Profile.PROJECT_NAME; } + /** + * MapRealmProvider uses session.invalidate() instead of calling e.g. + * session.clients().removeClients(realm); for clients (where clients are being removed one by one) + * + * Therefore it doesn't call session.users().preRemove(realm, client) for each client. + * Due to that JpaUserFederatedStorageProvider.preRemove(realm, client) is not called. + * So there remains objects in the database in user federation related tables after realm removal. + * + * Same for roles etc. + * + * Legacy federated storage is NOT supposed to work with map storage, so this method + * returns true if realm provider is "jpa" to be able to skip particular tests. + */ + protected boolean isJpaRealmProvider() { + String realmProvider = testingClient.server() + .fetchString(s -> s.getKeycloakSessionFactory().getProviderFactory(RealmProvider.class).getId()); + return Objects.equals(realmProvider, "\"jpa\""); + } + protected boolean isRealmCacheEnabled() { String realmCache = testingClient.server() .fetchString(s -> s.getKeycloakSessionFactory().getProviderFactory(CacheRealmProvider.class)); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/FederatedStorageExportImportTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/FederatedStorageExportImportTest.java index 002c06f841..60bfba1933 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/FederatedStorageExportImportTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/FederatedStorageExportImportTest.java @@ -18,6 +18,7 @@ package org.keycloak.testsuite.federation.storage; import org.junit.After; import org.junit.Assert; +import org.junit.Assume; import org.junit.Before; import org.junit.Test; import org.keycloak.admin.client.resource.RealmResource; @@ -42,7 +43,6 @@ import javax.ws.rs.NotFoundException; import java.io.File; import java.util.LinkedList; import java.util.List; -import java.util.Objects; import java.util.stream.Collectors; import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer; @@ -61,6 +61,8 @@ public class FederatedStorageExportImportTest extends AbstractAuthTest { @Before public void setDirs() { + Assume.assumeTrue("RealmProvider is not 'jpa'", isJpaRealmProvider()); + File baseDir = new File(System.getProperty("auth.server.config.dir", "target")); exportFileAbsolutePath = new File (baseDir, "singleFile-full.json").getAbsolutePath(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageFailureTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageFailureTest.java index 8e4de26bf4..631ab3ebfa 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageFailureTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageFailureTest.java @@ -57,7 +57,10 @@ import org.keycloak.testsuite.util.OAuthClient; import java.util.HashMap; import java.util.HashSet; import java.util.Map; +import java.util.Objects; import java.util.stream.Stream; +import org.junit.Assume; +import org.keycloak.models.RealmProvider; import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer; import org.keycloak.testsuite.util.ContainerAssume; @@ -95,6 +98,8 @@ public class UserStorageFailureTest extends AbstractTestRealmKeycloakTest { @Before public void addProvidersBeforeTest() { + Assume.assumeTrue("RealmProvider is not 'jpa'", isJpaRealmProvider()); + ComponentRepresentation memProvider = new ComponentRepresentation(); memProvider.setName("failure"); memProvider.setProviderId(FailableHardcodedStorageProviderFactory.PROVIDER_ID); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageOTPTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageOTPTest.java index e061e3df93..103aef3814 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageOTPTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageOTPTest.java @@ -25,6 +25,7 @@ import java.util.List; import org.jboss.arquillian.graphene.page.Page; +import org.junit.Assume; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -89,6 +90,8 @@ public class UserStorageOTPTest extends AbstractTestRealmKeycloakTest { @Before public void addProvidersBeforeTest() throws URISyntaxException, IOException { + Assume.assumeTrue("RealmProvider is not 'jpa'", isJpaRealmProvider()); + ComponentRepresentation dummyProvider = new ComponentRepresentation(); dummyProvider.setName("dummy"); dummyProvider.setId(componentId); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/sync/SyncFederationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/sync/SyncFederationTest.java index e2499800b0..64ffd69dd2 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/sync/SyncFederationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/sync/SyncFederationTest.java @@ -39,6 +39,8 @@ import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.concurrent.TimeUnit; +import org.junit.Assume; +import org.junit.Before; import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer; @@ -53,6 +55,11 @@ public class SyncFederationTest extends AbstractAuthTest { private static final Logger log = Logger.getLogger(SyncFederationTest.class); + @Before + public void enabled() { + Assume.assumeTrue("RealmProvider is not 'jpa'", isJpaRealmProvider()); + } + /** * Test that period sync is triggered when creating a synchronized User Storage Provider * diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserConsentWithUserStorageModelTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserConsentWithUserStorageModelTest.java index 659f2d5cce..a8a5cb9af9 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserConsentWithUserStorageModelTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserConsentWithUserStorageModelTest.java @@ -19,6 +19,7 @@ package org.keycloak.testsuite.model; import org.junit.After; import org.junit.Assert; +import org.junit.Assume; import org.junit.Before; import org.junit.Test; import org.keycloak.component.ComponentModel; @@ -59,6 +60,7 @@ public class UserConsentWithUserStorageModelTest extends AbstractTestRealmKeyclo @Before public void before() { + Assume.assumeTrue("RealmProvider is not 'jpa'", isJpaRealmProvider()); testingClient.server().run(UserConsentWithUserStorageModelTest::setupEnv); } diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/ConcurrentHashMapStorageTest.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/ConcurrentHashMapStorageTest.java index 1d9f8b150e..e4b0df97d0 100644 --- a/testsuite/model/src/test/java/org/keycloak/testsuite/model/ConcurrentHashMapStorageTest.java +++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/ConcurrentHashMapStorageTest.java @@ -143,11 +143,11 @@ public class ConcurrentHashMapStorageTest extends KeycloakModelTest { assertClientsPersisted(component1Id, component2Id, idMain, id1, id2); // Invalidate one component and check that the storage still contains what it should - getFactory().invalidate(ObjectType.COMPONENT, component1Id); + getFactory().invalidate(null, ObjectType.COMPONENT, component1Id); assertClientsPersisted(component1Id, component2Id, idMain, id1, id2); // Invalidate whole realm and check that the storage still contains what it should - getFactory().invalidate(ObjectType.REALM, realmId); + getFactory().invalidate(null, ObjectType.REALM, realmId); assertClientsPersisted(component1Id, component2Id, idMain, id1, id2); // Refresh factory (akin server restart) and check that the storage still contains what it should