Skip to content

Commit

Permalink
Preparation for moving User Storage SPI
Browse files Browse the repository at this point in the history
- Introduction of new AdminRealmResource SPI
- Moving handler of /realm/{realm}/user-storage into model/legacy-service
- session.users() and userStorageManager() moved refers legacy module
  IMPORTANT: Broken as UserStorageSyncManager is not yet moved
  • Loading branch information
hmlnarik committed Jun 21, 2022
1 parent 36f76a3 commit 703e868
Show file tree
Hide file tree
Showing 37 changed files with 622 additions and 150 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
import org.keycloak.policy.PasswordPolicyManagerProvider;
import org.keycloak.policy.PolicyError;
import org.keycloak.models.cache.UserCache;
import org.keycloak.storage.DatastoreProvider;
import org.keycloak.storage.LegacyStoreManagers;
import org.keycloak.storage.ReadOnlyException;
import org.keycloak.storage.StorageId;
import org.keycloak.storage.UserStorageProvider;
Expand Down Expand Up @@ -179,7 +181,8 @@ protected UserModel proxy(RealmModel realm, UserModel local, LDAPObject ldapObje

// We need to avoid having CachedUserModel as cache is upper-layer then LDAP. Hence having CachedUserModel here may cause StackOverflowError
if (local instanceof CachedUserModel) {
local = session.userStorageManager().getUserById(realm, local.getId());
LegacyStoreManagers datastoreProvider = (LegacyStoreManagers) session.getProvider(DatastoreProvider.class);
local = datastoreProvider.userStorageManager().getUserById(realm, local.getId());

existing = userManager.getManagedProxiedUser(local.getId());
if (existing != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@
import org.keycloak.models.cache.infinispan.events.*;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.storage.DatastoreProvider;
import org.keycloak.storage.LegacyStoreManagers;
import org.keycloak.storage.StorageId;
import org.keycloak.storage.client.ClientStorageProviderModel;
import org.keycloak.storage.datastore.LegacyDatastoreProvider;

import java.util.*;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -121,13 +121,13 @@ public class RealmCacheSession implements CacheRealmProvider {

protected boolean clearAll;
protected final long startupRevision;
private final LegacyDatastoreProvider datastoreProvider;
private final LegacyStoreManagers datastoreProvider;

public RealmCacheSession(RealmCacheManager cache, KeycloakSession session) {
this.cache = cache;
this.session = session;
this.startupRevision = cache.getCurrentCounter();
this.datastoreProvider = (LegacyDatastoreProvider) session.getProvider(DatastoreProvider.class);
this.datastoreProvider = (LegacyStoreManagers) session.getProvider(DatastoreProvider.class);
session.getTransactionManager().enlistPrepare(getPrepareTransaction());
session.getTransactionManager().enlistAfterCompletion(getAfterTransaction());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.ReadOnlyUserModelDelegate;
import org.keycloak.storage.CacheableStorageProviderModel;
import org.keycloak.storage.DatastoreProvider;
import org.keycloak.storage.LegacyStoreManagers;
import org.keycloak.storage.OnCreateComponent;
import org.keycloak.storage.OnUpdateComponent;
import org.keycloak.storage.StorageId;
import org.keycloak.storage.UserStorageProvider;
import org.keycloak.storage.UserStorageProviderModel;
Expand All @@ -71,7 +75,7 @@
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class UserCacheSession implements UserCache.Streams {
public class UserCacheSession implements UserCache.Streams, OnCreateComponent, OnUpdateComponent {
protected static final Logger logger = Logger.getLogger(UserCacheSession.class);
protected UserCacheManager cache;
protected KeycloakSession session;
Expand All @@ -85,11 +89,13 @@ public class UserCacheSession implements UserCache.Streams {
protected Set<String> realmInvalidations = new HashSet<>();
protected Set<InvalidationEvent> invalidationEvents = new HashSet<>(); // Events to be sent across cluster
protected Map<String, UserModel> managedUsers = new HashMap<>();
private LegacyStoreManagers datastoreProvider;

public UserCacheSession(UserCacheManager cache, KeycloakSession session) {
this.cache = cache;
this.session = session;
this.startupRevision = cache.getCurrentCounter();
this.datastoreProvider = (LegacyStoreManagers) session.getProvider(DatastoreProvider.class);
session.getTransactionManager().enlistAfterCompletion(getTransaction());
}

Expand All @@ -103,7 +109,7 @@ public void clear() {
public UserProvider getDelegate() {
if (!transactionActive) throw new IllegalStateException("Cannot access delegate without a transaction");
if (delegate != null) return delegate;
delegate = session.userStorageManager();
delegate = this.datastoreProvider.userStorageManager();

return delegate;
}
Expand Down Expand Up @@ -906,4 +912,17 @@ private void addRealmInvalidation(String realmId) {
invalidationEvents.add(UserCacheRealmInvalidationEvent.create(realmId));
}

@Override
public void onUpdate(KeycloakSession session, RealmModel realm, ComponentModel oldModel, ComponentModel newModel) {
if (getDelegate() instanceof OnUpdateComponent) {
((OnUpdateComponent) getDelegate()).onUpdate(session, realm, oldModel, newModel);
}
}

@Override
public void onCreate(KeycloakSession session, RealmModel realm, ComponentModel model) {
if (getDelegate() instanceof OnCreateComponent) {
((OnCreateComponent) getDelegate()).onCreate(session, realm, model);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.keycloak.services.scheduled;

import org.keycloak.models.KeycloakSession;
import org.keycloak.timer.ScheduledTask;

/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class ClearExpiredClientInitialAccessTokens implements ScheduledTask {

@Override
public void run(KeycloakSession session) {
session.realms().removeExpiredClientInitialAccess();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.keycloak.services.scheduled;

import org.jboss.logging.Logger;
import org.keycloak.common.util.Time;
import org.keycloak.events.EventStoreProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.timer.ScheduledTask;

/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class ClearExpiredEvents implements ScheduledTask {

protected static final Logger logger = Logger.getLogger(ClearExpiredEvents.class);

@Override
public void run(KeycloakSession session) {
long currentTimeMillis = Time.currentTimeMillis();

EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
if (eventStore != null) {
eventStore.clearExpiredEvents();
}

long took = Time.currentTimeMillis() - currentTimeMillis;
logger.debugf("ClearExpiredEvents finished in %d ms", took);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.keycloak.services.scheduled;

import org.jboss.logging.Logger;
import org.keycloak.common.util.Time;
import org.keycloak.models.KeycloakSession;
import org.keycloak.timer.ScheduledTask;

/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class ClearExpiredUserSessions implements ScheduledTask {

protected static final Logger logger = Logger.getLogger(ClearExpiredUserSessions.class);

public static final String TASK_NAME = "ClearExpiredUserSessions";

@Override
public void run(KeycloakSession session) {
long currentTimeMillis = Time.currentTimeMillis();

session.authenticationSessions().removeAllExpired();
session.sessions().removeAllExpired();

long took = Time.currentTimeMillis() - currentTimeMillis;
logger.debugf("ClearExpiredUserSessions finished in %d ms", took);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@

package org.keycloak.storage;

import static org.keycloak.models.utils.KeycloakModelUtils.runJobInTransaction;
import static org.keycloak.utils.StreamsUtil.distinctByKey;
import static org.keycloak.utils.StreamsUtil.paginatedStream;

import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;

import org.jboss.logging.Logger;
import org.keycloak.component.ComponentFactory;
import org.keycloak.component.ComponentModel;
Expand All @@ -39,27 +51,15 @@
import org.keycloak.models.cache.UserCache;
import org.keycloak.models.utils.ComponentUtil;
import org.keycloak.models.utils.ReadOnlyUserModelDelegate;
import org.keycloak.services.managers.UserStorageSyncManager;
import org.keycloak.storage.client.ClientStorageProvider;
import org.keycloak.storage.federated.UserFederatedStorageProvider;
import org.keycloak.storage.managers.UserStorageSyncManager;
import org.keycloak.storage.user.ImportedUserValidation;
import org.keycloak.storage.user.UserBulkUpdateProvider;
import org.keycloak.storage.user.UserLookupProvider;
import org.keycloak.storage.user.UserQueryProvider;
import org.keycloak.storage.user.UserRegistrationProvider;

import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;

import static org.keycloak.models.utils.KeycloakModelUtils.runJobInTransaction;
import static org.keycloak.utils.StreamsUtil.distinctByKey;
import static org.keycloak.utils.StreamsUtil.paginatedStream;

/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmProvider;
import org.keycloak.models.RoleProvider;
import org.keycloak.models.UserProvider;
import org.keycloak.models.cache.CacheRealmProvider;
import org.keycloak.models.cache.UserCache;
import org.keycloak.storage.ClientScopeStorageManager;
import org.keycloak.storage.ClientStorageManager;
import org.keycloak.storage.DatastoreProvider;
import org.keycloak.storage.GroupStorageManager;
import org.keycloak.storage.LegacyStoreManagers;
import org.keycloak.storage.RoleStorageManager;
import org.keycloak.storage.UserStorageManager;

public class LegacyDatastoreProvider implements DatastoreProvider {
public class LegacyDatastoreProvider implements DatastoreProvider, LegacyStoreManagers {

private final LegacyDatastoreProviderFactory factory;
private final KeycloakSession session;
Expand All @@ -23,11 +27,13 @@ public class LegacyDatastoreProvider implements DatastoreProvider {
private GroupProvider groupProvider;
private RealmProvider realmProvider;
private RoleProvider roleProvider;
private UserProvider userProvider;

private ClientScopeStorageManager clientScopeStorageManager;
private RoleStorageManager roleStorageManager;
private GroupStorageManager groupStorageManager;
private ClientStorageManager clientStorageManager;
private UserProvider userStorageManager;

public LegacyDatastoreProvider(LegacyDatastoreProviderFactory factory, KeycloakSession session) {
this.factory = factory;
Expand Down Expand Up @@ -66,6 +72,13 @@ public GroupProvider groupStorageManager() {
return groupStorageManager;
}

public UserProvider userStorageManager() {
if (userStorageManager == null) {
userStorageManager = new UserStorageManager(session);
}
return userStorageManager;
}

private ClientProvider getClientProvider() {
// TODO: Extract ClientProvider from CacheRealmProvider and use that instead
ClientProvider cache = session.getProvider(CacheRealmProvider.class);
Expand Down Expand Up @@ -115,6 +128,15 @@ private RoleProvider getRoleProvider() {
}
}

private UserProvider getUserProvider() {
UserCache cache = session.getProvider(UserCache.class);
if (cache != null) {
return cache;
} else {
return userStorageManager();
}
}

@Override
public ClientProvider clients() {
if (clientProvider == null) {
Expand Down Expand Up @@ -154,4 +176,12 @@ public RoleProvider roles() {
}
return roleProvider;
}

@Override
public UserProvider users() {
if (userProvider == null) {
userProvider = getUserProvider();
}
return userProvider;
}
}
Loading

0 comments on commit 703e868

Please sign in to comment.