Preparation for moving User Storage SPI
- 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
This commit is contained in:
parent
36f76a37ad
commit
703e868a51
37 changed files with 622 additions and 150 deletions
|
@ -56,6 +56,8 @@ import org.keycloak.models.utils.ReadOnlyUserModelDelegate;
|
||||||
import org.keycloak.policy.PasswordPolicyManagerProvider;
|
import org.keycloak.policy.PasswordPolicyManagerProvider;
|
||||||
import org.keycloak.policy.PolicyError;
|
import org.keycloak.policy.PolicyError;
|
||||||
import org.keycloak.models.cache.UserCache;
|
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.ReadOnlyException;
|
||||||
import org.keycloak.storage.StorageId;
|
import org.keycloak.storage.StorageId;
|
||||||
import org.keycloak.storage.UserStorageProvider;
|
import org.keycloak.storage.UserStorageProvider;
|
||||||
|
@ -179,7 +181,8 @@ public class LDAPStorageProvider implements UserStorageProvider,
|
||||||
|
|
||||||
// We need to avoid having CachedUserModel as cache is upper-layer then LDAP. Hence having CachedUserModel here may cause StackOverflowError
|
// We need to avoid having CachedUserModel as cache is upper-layer then LDAP. Hence having CachedUserModel here may cause StackOverflowError
|
||||||
if (local instanceof CachedUserModel) {
|
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());
|
existing = userManager.getManagedProxiedUser(local.getId());
|
||||||
if (existing != null) {
|
if (existing != null) {
|
||||||
|
|
|
@ -27,9 +27,9 @@ import org.keycloak.models.cache.infinispan.entities.*;
|
||||||
import org.keycloak.models.cache.infinispan.events.*;
|
import org.keycloak.models.cache.infinispan.events.*;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.storage.DatastoreProvider;
|
import org.keycloak.storage.DatastoreProvider;
|
||||||
|
import org.keycloak.storage.LegacyStoreManagers;
|
||||||
import org.keycloak.storage.StorageId;
|
import org.keycloak.storage.StorageId;
|
||||||
import org.keycloak.storage.client.ClientStorageProviderModel;
|
import org.keycloak.storage.client.ClientStorageProviderModel;
|
||||||
import org.keycloak.storage.datastore.LegacyDatastoreProvider;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
@ -121,13 +121,13 @@ public class RealmCacheSession implements CacheRealmProvider {
|
||||||
|
|
||||||
protected boolean clearAll;
|
protected boolean clearAll;
|
||||||
protected final long startupRevision;
|
protected final long startupRevision;
|
||||||
private final LegacyDatastoreProvider datastoreProvider;
|
private final LegacyStoreManagers datastoreProvider;
|
||||||
|
|
||||||
public RealmCacheSession(RealmCacheManager cache, KeycloakSession session) {
|
public RealmCacheSession(RealmCacheManager cache, KeycloakSession session) {
|
||||||
this.cache = cache;
|
this.cache = cache;
|
||||||
this.session = session;
|
this.session = session;
|
||||||
this.startupRevision = cache.getCurrentCounter();
|
this.startupRevision = cache.getCurrentCounter();
|
||||||
this.datastoreProvider = (LegacyDatastoreProvider) session.getProvider(DatastoreProvider.class);
|
this.datastoreProvider = (LegacyStoreManagers) session.getProvider(DatastoreProvider.class);
|
||||||
session.getTransactionManager().enlistPrepare(getPrepareTransaction());
|
session.getTransactionManager().enlistPrepare(getPrepareTransaction());
|
||||||
session.getTransactionManager().enlistAfterCompletion(getAfterTransaction());
|
session.getTransactionManager().enlistAfterCompletion(getAfterTransaction());
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,10 @@ import org.keycloak.models.cache.infinispan.stream.InIdentityProviderPredicate;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.models.utils.ReadOnlyUserModelDelegate;
|
import org.keycloak.models.utils.ReadOnlyUserModelDelegate;
|
||||||
import org.keycloak.storage.CacheableStorageProviderModel;
|
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.StorageId;
|
||||||
import org.keycloak.storage.UserStorageProvider;
|
import org.keycloak.storage.UserStorageProvider;
|
||||||
import org.keycloak.storage.UserStorageProviderModel;
|
import org.keycloak.storage.UserStorageProviderModel;
|
||||||
|
@ -71,7 +75,7 @@ import java.util.stream.Stream;
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @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 static final Logger logger = Logger.getLogger(UserCacheSession.class);
|
||||||
protected UserCacheManager cache;
|
protected UserCacheManager cache;
|
||||||
protected KeycloakSession session;
|
protected KeycloakSession session;
|
||||||
|
@ -85,11 +89,13 @@ public class UserCacheSession implements UserCache.Streams {
|
||||||
protected Set<String> realmInvalidations = new HashSet<>();
|
protected Set<String> realmInvalidations = new HashSet<>();
|
||||||
protected Set<InvalidationEvent> invalidationEvents = new HashSet<>(); // Events to be sent across cluster
|
protected Set<InvalidationEvent> invalidationEvents = new HashSet<>(); // Events to be sent across cluster
|
||||||
protected Map<String, UserModel> managedUsers = new HashMap<>();
|
protected Map<String, UserModel> managedUsers = new HashMap<>();
|
||||||
|
private LegacyStoreManagers datastoreProvider;
|
||||||
|
|
||||||
public UserCacheSession(UserCacheManager cache, KeycloakSession session) {
|
public UserCacheSession(UserCacheManager cache, KeycloakSession session) {
|
||||||
this.cache = cache;
|
this.cache = cache;
|
||||||
this.session = session;
|
this.session = session;
|
||||||
this.startupRevision = cache.getCurrentCounter();
|
this.startupRevision = cache.getCurrentCounter();
|
||||||
|
this.datastoreProvider = (LegacyStoreManagers) session.getProvider(DatastoreProvider.class);
|
||||||
session.getTransactionManager().enlistAfterCompletion(getTransaction());
|
session.getTransactionManager().enlistAfterCompletion(getTransaction());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,7 +109,7 @@ public class UserCacheSession implements UserCache.Streams {
|
||||||
public UserProvider getDelegate() {
|
public UserProvider getDelegate() {
|
||||||
if (!transactionActive) throw new IllegalStateException("Cannot access delegate without a transaction");
|
if (!transactionActive) throw new IllegalStateException("Cannot access delegate without a transaction");
|
||||||
if (delegate != null) return delegate;
|
if (delegate != null) return delegate;
|
||||||
delegate = session.userStorageManager();
|
delegate = this.datastoreProvider.userStorageManager();
|
||||||
|
|
||||||
return delegate;
|
return delegate;
|
||||||
}
|
}
|
||||||
|
@ -906,4 +912,17 @@ public class UserCacheSession implements UserCache.Streams {
|
||||||
invalidationEvents.add(UserCacheRealmInvalidationEvent.create(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -17,6 +17,18 @@
|
||||||
|
|
||||||
package org.keycloak.storage;
|
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.jboss.logging.Logger;
|
||||||
import org.keycloak.component.ComponentFactory;
|
import org.keycloak.component.ComponentFactory;
|
||||||
import org.keycloak.component.ComponentModel;
|
import org.keycloak.component.ComponentModel;
|
||||||
|
@ -39,27 +51,15 @@ import org.keycloak.models.cache.OnUserCache;
|
||||||
import org.keycloak.models.cache.UserCache;
|
import org.keycloak.models.cache.UserCache;
|
||||||
import org.keycloak.models.utils.ComponentUtil;
|
import org.keycloak.models.utils.ComponentUtil;
|
||||||
import org.keycloak.models.utils.ReadOnlyUserModelDelegate;
|
import org.keycloak.models.utils.ReadOnlyUserModelDelegate;
|
||||||
import org.keycloak.services.managers.UserStorageSyncManager;
|
|
||||||
import org.keycloak.storage.client.ClientStorageProvider;
|
import org.keycloak.storage.client.ClientStorageProvider;
|
||||||
import org.keycloak.storage.federated.UserFederatedStorageProvider;
|
import org.keycloak.storage.federated.UserFederatedStorageProvider;
|
||||||
|
import org.keycloak.storage.managers.UserStorageSyncManager;
|
||||||
import org.keycloak.storage.user.ImportedUserValidation;
|
import org.keycloak.storage.user.ImportedUserValidation;
|
||||||
import org.keycloak.storage.user.UserBulkUpdateProvider;
|
import org.keycloak.storage.user.UserBulkUpdateProvider;
|
||||||
import org.keycloak.storage.user.UserLookupProvider;
|
import org.keycloak.storage.user.UserLookupProvider;
|
||||||
import org.keycloak.storage.user.UserQueryProvider;
|
import org.keycloak.storage.user.UserQueryProvider;
|
||||||
import org.keycloak.storage.user.UserRegistrationProvider;
|
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>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
|
@ -6,14 +6,18 @@ import org.keycloak.models.GroupProvider;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmProvider;
|
import org.keycloak.models.RealmProvider;
|
||||||
import org.keycloak.models.RoleProvider;
|
import org.keycloak.models.RoleProvider;
|
||||||
|
import org.keycloak.models.UserProvider;
|
||||||
import org.keycloak.models.cache.CacheRealmProvider;
|
import org.keycloak.models.cache.CacheRealmProvider;
|
||||||
|
import org.keycloak.models.cache.UserCache;
|
||||||
import org.keycloak.storage.ClientScopeStorageManager;
|
import org.keycloak.storage.ClientScopeStorageManager;
|
||||||
import org.keycloak.storage.ClientStorageManager;
|
import org.keycloak.storage.ClientStorageManager;
|
||||||
import org.keycloak.storage.DatastoreProvider;
|
import org.keycloak.storage.DatastoreProvider;
|
||||||
import org.keycloak.storage.GroupStorageManager;
|
import org.keycloak.storage.GroupStorageManager;
|
||||||
|
import org.keycloak.storage.LegacyStoreManagers;
|
||||||
import org.keycloak.storage.RoleStorageManager;
|
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 LegacyDatastoreProviderFactory factory;
|
||||||
private final KeycloakSession session;
|
private final KeycloakSession session;
|
||||||
|
@ -23,11 +27,13 @@ public class LegacyDatastoreProvider implements DatastoreProvider {
|
||||||
private GroupProvider groupProvider;
|
private GroupProvider groupProvider;
|
||||||
private RealmProvider realmProvider;
|
private RealmProvider realmProvider;
|
||||||
private RoleProvider roleProvider;
|
private RoleProvider roleProvider;
|
||||||
|
private UserProvider userProvider;
|
||||||
|
|
||||||
private ClientScopeStorageManager clientScopeStorageManager;
|
private ClientScopeStorageManager clientScopeStorageManager;
|
||||||
private RoleStorageManager roleStorageManager;
|
private RoleStorageManager roleStorageManager;
|
||||||
private GroupStorageManager groupStorageManager;
|
private GroupStorageManager groupStorageManager;
|
||||||
private ClientStorageManager clientStorageManager;
|
private ClientStorageManager clientStorageManager;
|
||||||
|
private UserProvider userStorageManager;
|
||||||
|
|
||||||
public LegacyDatastoreProvider(LegacyDatastoreProviderFactory factory, KeycloakSession session) {
|
public LegacyDatastoreProvider(LegacyDatastoreProviderFactory factory, KeycloakSession session) {
|
||||||
this.factory = factory;
|
this.factory = factory;
|
||||||
|
@ -66,6 +72,13 @@ public class LegacyDatastoreProvider implements DatastoreProvider {
|
||||||
return groupStorageManager;
|
return groupStorageManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UserProvider userStorageManager() {
|
||||||
|
if (userStorageManager == null) {
|
||||||
|
userStorageManager = new UserStorageManager(session);
|
||||||
|
}
|
||||||
|
return userStorageManager;
|
||||||
|
}
|
||||||
|
|
||||||
private ClientProvider getClientProvider() {
|
private ClientProvider getClientProvider() {
|
||||||
// TODO: Extract ClientProvider from CacheRealmProvider and use that instead
|
// TODO: Extract ClientProvider from CacheRealmProvider and use that instead
|
||||||
ClientProvider cache = session.getProvider(CacheRealmProvider.class);
|
ClientProvider cache = session.getProvider(CacheRealmProvider.class);
|
||||||
|
@ -115,6 +128,15 @@ public class LegacyDatastoreProvider implements DatastoreProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private UserProvider getUserProvider() {
|
||||||
|
UserCache cache = session.getProvider(UserCache.class);
|
||||||
|
if (cache != null) {
|
||||||
|
return cache;
|
||||||
|
} else {
|
||||||
|
return userStorageManager();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ClientProvider clients() {
|
public ClientProvider clients() {
|
||||||
if (clientProvider == null) {
|
if (clientProvider == null) {
|
||||||
|
@ -154,4 +176,12 @@ public class LegacyDatastoreProvider implements DatastoreProvider {
|
||||||
}
|
}
|
||||||
return roleProvider;
|
return roleProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserProvider users() {
|
||||||
|
if (userProvider == null) {
|
||||||
|
userProvider = getUserProvider();
|
||||||
|
}
|
||||||
|
return userProvider;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
package org.keycloak.storage.datastore;
|
||||||
|
|
||||||
|
import org.keycloak.Config;
|
||||||
|
import org.keycloak.Config.Scope;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
|
import org.keycloak.models.utils.PostMigrationEvent;
|
||||||
|
import org.keycloak.provider.ProviderEvent;
|
||||||
|
import org.keycloak.provider.ProviderEventListener;
|
||||||
|
import org.keycloak.services.managers.UserStorageSyncManager;
|
||||||
|
import org.keycloak.services.scheduled.ClearExpiredClientInitialAccessTokens;
|
||||||
|
import org.keycloak.services.scheduled.ClearExpiredEvents;
|
||||||
|
import org.keycloak.services.scheduled.ClearExpiredUserSessions;
|
||||||
|
import org.keycloak.services.scheduled.ClusterAwareScheduledTaskRunner;
|
||||||
|
import org.keycloak.services.scheduled.ScheduledTaskRunner;
|
||||||
|
import org.keycloak.storage.DatastoreProvider;
|
||||||
|
import org.keycloak.storage.DatastoreProviderFactory;
|
||||||
|
import org.keycloak.storage.LegacyStoreSyncEvent;
|
||||||
|
import org.keycloak.timer.TimerProvider;
|
||||||
|
|
||||||
|
public class LegacyDatastoreProviderFactory implements DatastoreProviderFactory, ProviderEventListener {
|
||||||
|
|
||||||
|
private static final String PROVIDER_ID = "legacy";
|
||||||
|
private long clientStorageProviderTimeout;
|
||||||
|
private long roleStorageProviderTimeout;
|
||||||
|
private Runnable onClose;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DatastoreProvider create(KeycloakSession session) {
|
||||||
|
return new LegacyDatastoreProvider(this, session);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Scope config) {
|
||||||
|
clientStorageProviderTimeout = Config.scope("client").getLong("storageProviderTimeout", 3000L);
|
||||||
|
roleStorageProviderTimeout = Config.scope("role").getLong("storageProviderTimeout", 3000L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postInit(KeycloakSessionFactory factory) {
|
||||||
|
factory.register(this);
|
||||||
|
onClose = () -> factory.unregister(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
onClose.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return PROVIDER_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getClientStorageProviderTimeout() {
|
||||||
|
return clientStorageProviderTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getRoleStorageProviderTimeout() {
|
||||||
|
return roleStorageProviderTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEvent(ProviderEvent event) {
|
||||||
|
if (event instanceof PostMigrationEvent) {
|
||||||
|
setupScheduledTasks(((PostMigrationEvent) event).getFactory());
|
||||||
|
} else if (event instanceof LegacyStoreSyncEvent) {
|
||||||
|
LegacyStoreSyncEvent ev = (LegacyStoreSyncEvent) event;
|
||||||
|
UserStorageSyncManager.notifyToRefreshPeriodicSyncAll(ev.getSession(), ev.getRealm(), ev.getRemoved());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setupScheduledTasks(final KeycloakSessionFactory sessionFactory) {
|
||||||
|
long interval = Config.scope("scheduled").getLong("interval", 900L) * 1000;
|
||||||
|
|
||||||
|
KeycloakSession session = sessionFactory.create();
|
||||||
|
try {
|
||||||
|
TimerProvider timer = session.getProvider(TimerProvider.class);
|
||||||
|
if (timer != null) {
|
||||||
|
timer.schedule(new ClusterAwareScheduledTaskRunner(sessionFactory, new ClearExpiredEvents(), interval), interval, "ClearExpiredEvents");
|
||||||
|
timer.schedule(new ClusterAwareScheduledTaskRunner(sessionFactory, new ClearExpiredClientInitialAccessTokens(), interval), interval, "ClearExpiredClientInitialAccessTokens");
|
||||||
|
timer.schedule(new ScheduledTaskRunner(sessionFactory, new ClearExpiredUserSessions()), interval, ClearExpiredUserSessions.TASK_NAME);
|
||||||
|
UserStorageSyncManager.bootstrapPeriodic(sessionFactory, timer);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
session.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.services.managers;
|
package org.keycloak.storage.managers;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.cluster.ClusterEvent;
|
import org.keycloak.cluster.ClusterEvent;
|
||||||
|
@ -22,12 +22,12 @@ import org.keycloak.cluster.ClusterListener;
|
||||||
import org.keycloak.cluster.ClusterProvider;
|
import org.keycloak.cluster.ClusterProvider;
|
||||||
import org.keycloak.cluster.ExecutionResult;
|
import org.keycloak.cluster.ExecutionResult;
|
||||||
import org.keycloak.common.util.Time;
|
import org.keycloak.common.util.Time;
|
||||||
|
import org.keycloak.component.ComponentModel;
|
||||||
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;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.services.ServicesLogger;
|
|
||||||
import org.keycloak.storage.UserStorageProvider;
|
import org.keycloak.storage.UserStorageProvider;
|
||||||
import org.keycloak.storage.UserStorageProviderFactory;
|
import org.keycloak.storage.UserStorageProviderFactory;
|
||||||
import org.keycloak.storage.UserStorageProviderModel;
|
import org.keycloak.storage.UserStorageProviderModel;
|
||||||
|
@ -54,7 +54,7 @@ public class UserStorageSyncManager {
|
||||||
* @param sessionFactory
|
* @param sessionFactory
|
||||||
* @param timer
|
* @param timer
|
||||||
*/
|
*/
|
||||||
public void bootstrapPeriodic(final KeycloakSessionFactory sessionFactory, final TimerProvider timer) {
|
public static void bootstrapPeriodic(final KeycloakSessionFactory sessionFactory, final TimerProvider timer) {
|
||||||
KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
|
KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -80,7 +80,7 @@ public class UserStorageSyncManager {
|
||||||
ExecutionResult<SynchronizationResult> result;
|
ExecutionResult<SynchronizationResult> result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SynchronizationResult syncAllUsers(final KeycloakSessionFactory sessionFactory, final String realmId, final UserStorageProviderModel provider) {
|
public static SynchronizationResult syncAllUsers(final KeycloakSessionFactory sessionFactory, final String realmId, final UserStorageProviderModel provider) {
|
||||||
UserStorageProviderFactory factory = (UserStorageProviderFactory) sessionFactory.getProviderFactory(UserStorageProvider.class, provider.getProviderId());
|
UserStorageProviderFactory factory = (UserStorageProviderFactory) sessionFactory.getProviderFactory(UserStorageProvider.class, provider.getProviderId());
|
||||||
if (!(factory instanceof ImportSynchronization) || !provider.isImportEnabled() || !provider.isEnabled()) {
|
if (!(factory instanceof ImportSynchronization) || !provider.isImportEnabled() || !provider.isEnabled()) {
|
||||||
return SynchronizationResult.ignored();
|
return SynchronizationResult.ignored();
|
||||||
|
@ -121,7 +121,7 @@ public class UserStorageSyncManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public SynchronizationResult syncChangedUsers(final KeycloakSessionFactory sessionFactory, final String realmId, final UserStorageProviderModel provider) {
|
public static SynchronizationResult syncChangedUsers(final KeycloakSessionFactory sessionFactory, final String realmId, final UserStorageProviderModel provider) {
|
||||||
UserStorageProviderFactory factory = (UserStorageProviderFactory) sessionFactory.getProviderFactory(UserStorageProvider.class, provider.getProviderId());
|
UserStorageProviderFactory factory = (UserStorageProviderFactory) sessionFactory.getProviderFactory(UserStorageProvider.class, provider.getProviderId());
|
||||||
if (!(factory instanceof ImportSynchronization) || !provider.isImportEnabled() || !provider.isEnabled()) {
|
if (!(factory instanceof ImportSynchronization) || !provider.isImportEnabled() || !provider.isEnabled()) {
|
||||||
return SynchronizationResult.ignored();
|
return SynchronizationResult.ignored();
|
||||||
|
@ -164,8 +164,17 @@ public class UserStorageSyncManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void notifyToRefreshPeriodicSyncAll(KeycloakSession session, RealmModel realm, boolean removed) {
|
||||||
|
realm.getUserStorageProvidersStream().forEachOrdered(fedProvider ->
|
||||||
|
notifyToRefreshPeriodicSync(session, realm, fedProvider, removed));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void notifyToRefreshPeriodicSyncSingle(KeycloakSession session, RealmModel realm, ComponentModel component, boolean removed) {
|
||||||
|
notifyToRefreshPeriodicSync(session, realm, new UserStorageProviderModel(component), removed);
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure all cluster nodes are notified
|
// Ensure all cluster nodes are notified
|
||||||
public void notifyToRefreshPeriodicSync(KeycloakSession session, RealmModel realm, UserStorageProviderModel provider, boolean removed) {
|
public static void notifyToRefreshPeriodicSync(KeycloakSession session, RealmModel realm, UserStorageProviderModel provider, boolean removed) {
|
||||||
UserStorageProviderFactory factory = (UserStorageProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(UserStorageProvider.class, provider.getProviderId());
|
UserStorageProviderFactory factory = (UserStorageProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(UserStorageProvider.class, provider.getProviderId());
|
||||||
if (!(factory instanceof ImportSynchronization) || !provider.isImportEnabled()) {
|
if (!(factory instanceof ImportSynchronization) || !provider.isImportEnabled()) {
|
||||||
return;
|
return;
|
||||||
|
@ -180,7 +189,7 @@ public class UserStorageSyncManager {
|
||||||
|
|
||||||
|
|
||||||
// Executed once it receives notification that some UserFederationProvider was created or updated
|
// Executed once it receives notification that some UserFederationProvider was created or updated
|
||||||
protected void refreshPeriodicSyncForProvider(final KeycloakSessionFactory sessionFactory, TimerProvider timer, final UserStorageProviderModel provider, final String realmId) {
|
protected static void refreshPeriodicSyncForProvider(final KeycloakSessionFactory sessionFactory, TimerProvider timer, final UserStorageProviderModel provider, final String realmId) {
|
||||||
logger.debugf("Going to refresh periodic sync for provider '%s' . Full sync period: %d , changed users sync period: %d",
|
logger.debugf("Going to refresh periodic sync for provider '%s' . Full sync period: %d , changed users sync period: %d",
|
||||||
provider.getName(), provider.getFullSyncPeriod(), provider.getChangedSyncPeriod());
|
provider.getName(), provider.getFullSyncPeriod(), provider.getChangedSyncPeriod());
|
||||||
|
|
||||||
|
@ -198,7 +207,7 @@ public class UserStorageSyncManager {
|
||||||
logger.debugf("Ignored periodic full sync with storage provider %s due small time since last sync", provider.getName());
|
logger.debugf("Ignored periodic full sync with storage provider %s due small time since last sync", provider.getName());
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
ServicesLogger.LOGGER.errorDuringFullUserSync(t);
|
logger.error("Error occurred during full sync of users", t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -221,7 +230,7 @@ public class UserStorageSyncManager {
|
||||||
logger.debugf("Ignored periodic changed-users sync with storage provider %s due small time since last sync", provider.getName());
|
logger.debugf("Ignored periodic changed-users sync with storage provider %s due small time since last sync", provider.getName());
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
ServicesLogger.LOGGER.errorDuringChangedUserSync(t);
|
logger.error("Error occurred during sync of changed users", t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,7 +242,7 @@ public class UserStorageSyncManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip syncing if there is short time since last sync time.
|
// Skip syncing if there is short time since last sync time.
|
||||||
private boolean shouldPerformNewPeriodicSync(int lastSyncTime, int period) {
|
private static boolean shouldPerformNewPeriodicSync(int lastSyncTime, int period) {
|
||||||
if (lastSyncTime <= 0) {
|
if (lastSyncTime <= 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -245,14 +254,14 @@ public class UserStorageSyncManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Executed once it receives notification that some UserFederationProvider was removed
|
// Executed once it receives notification that some UserFederationProvider was removed
|
||||||
protected void removePeriodicSyncForProvider(TimerProvider timer, UserStorageProviderModel fedProvider) {
|
protected static void removePeriodicSyncForProvider(TimerProvider timer, UserStorageProviderModel fedProvider) {
|
||||||
logger.debugf("Removing periodic sync for provider %s", fedProvider.getName());
|
logger.debugf("Removing periodic sync for provider %s", fedProvider.getName());
|
||||||
timer.cancelTask(fedProvider.getId() + "-FULL");
|
timer.cancelTask(fedProvider.getId() + "-FULL");
|
||||||
timer.cancelTask(fedProvider.getId() + "-CHANGED");
|
timer.cancelTask(fedProvider.getId() + "-CHANGED");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update interval of last sync for given UserFederationProviderModel. Do it in separate transaction
|
// Update interval of last sync for given UserFederationProviderModel. Do it in separate transaction
|
||||||
private void updateLastSyncInterval(final KeycloakSessionFactory sessionFactory, UserStorageProviderModel provider, final String realmId) {
|
private static void updateLastSyncInterval(final KeycloakSessionFactory sessionFactory, UserStorageProviderModel provider, final String realmId) {
|
||||||
KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
|
KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -274,7 +283,7 @@ public class UserStorageSyncManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private class UserStorageClusterListener implements ClusterListener {
|
private static class UserStorageClusterListener implements ClusterListener {
|
||||||
|
|
||||||
private final KeycloakSessionFactory sessionFactory;
|
private final KeycloakSessionFactory sessionFactory;
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
package org.keycloak.services.resources.admin;
|
||||||
|
|
||||||
|
import org.keycloak.Config.Scope;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.services.resources.admin.ext.AdminRealmResourceProvider;
|
||||||
|
import org.keycloak.services.resources.admin.ext.AdminRealmResourceProviderFactory;
|
||||||
|
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
|
||||||
|
|
||||||
|
public class UserStorageProviderRealmAdminProvider implements AdminRealmResourceProviderFactory, AdminRealmResourceProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AdminRealmResourceProvider create(KeycloakSession session) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Scope config) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postInit(KeycloakSessionFactory factory) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return "user-storage";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getResource(KeycloakSession session, RealmModel realm, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
|
||||||
|
return new UserStorageProviderResource(realm, auth, adminEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
org.keycloak.services.resources.admin.UserStorageProviderRealmAdminProvider
|
|
@ -0,0 +1,20 @@
|
||||||
|
package org.keycloak.storage;
|
||||||
|
|
||||||
|
import org.keycloak.models.ClientProvider;
|
||||||
|
import org.keycloak.models.ClientScopeProvider;
|
||||||
|
import org.keycloak.models.GroupProvider;
|
||||||
|
import org.keycloak.models.RoleProvider;
|
||||||
|
import org.keycloak.models.UserProvider;
|
||||||
|
|
||||||
|
public interface LegacyStoreManagers {
|
||||||
|
|
||||||
|
ClientProvider clientStorageManager();
|
||||||
|
|
||||||
|
ClientScopeProvider clientScopeStorageManager();
|
||||||
|
|
||||||
|
RoleProvider roleStorageManager();
|
||||||
|
|
||||||
|
GroupProvider groupStorageManager();
|
||||||
|
|
||||||
|
UserProvider userStorageManager();
|
||||||
|
}
|
|
@ -1,48 +0,0 @@
|
||||||
package org.keycloak.storage.datastore;
|
|
||||||
|
|
||||||
import org.keycloak.Config;
|
|
||||||
import org.keycloak.Config.Scope;
|
|
||||||
import org.keycloak.models.KeycloakSession;
|
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
|
||||||
import org.keycloak.storage.DatastoreProvider;
|
|
||||||
import org.keycloak.storage.DatastoreProviderFactory;
|
|
||||||
|
|
||||||
public class LegacyDatastoreProviderFactory implements DatastoreProviderFactory {
|
|
||||||
|
|
||||||
private static final String PROVIDER_ID = "legacy";
|
|
||||||
private long clientStorageProviderTimeout;
|
|
||||||
private long roleStorageProviderTimeout;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DatastoreProvider create(KeycloakSession session) {
|
|
||||||
return new LegacyDatastoreProvider(this, session);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init(Scope config) {
|
|
||||||
clientStorageProviderTimeout = Config.scope("client").getLong("storageProviderTimeout", 3000L);
|
|
||||||
roleStorageProviderTimeout = Config.scope("role").getLong("storageProviderTimeout", 3000L);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void postInit(KeycloakSessionFactory factory) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getId() {
|
|
||||||
return PROVIDER_ID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getClientStorageProviderTimeout() {
|
|
||||||
return clientStorageProviderTimeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getRoleStorageProviderTimeout() {
|
|
||||||
return roleStorageProviderTimeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
org.keycloak.storage.UserStorageProviderSpi
|
|
@ -22,13 +22,13 @@ import org.keycloak.component.ComponentFactory;
|
||||||
import org.keycloak.component.ComponentModel;
|
import org.keycloak.component.ComponentModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.UserProvider;
|
||||||
import org.keycloak.provider.Provider;
|
import org.keycloak.provider.Provider;
|
||||||
import org.keycloak.provider.ProviderConfigProperty;
|
import org.keycloak.provider.ProviderConfigProperty;
|
||||||
import org.keycloak.provider.ProviderFactory;
|
import org.keycloak.provider.ProviderFactory;
|
||||||
import org.keycloak.representations.idm.ComponentRepresentation;
|
import org.keycloak.representations.idm.ComponentRepresentation;
|
||||||
import org.keycloak.storage.OnCreateComponent;
|
import org.keycloak.storage.OnCreateComponent;
|
||||||
import org.keycloak.storage.OnUpdateComponent;
|
import org.keycloak.storage.OnUpdateComponent;
|
||||||
import org.keycloak.storage.UserStorageProviderFactory;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -94,15 +94,17 @@ public class ComponentUtil {
|
||||||
public static void notifyCreated(KeycloakSession session, RealmModel realm, ComponentModel model) {
|
public static void notifyCreated(KeycloakSession session, RealmModel realm, ComponentModel model) {
|
||||||
ComponentFactory factory = getComponentFactory(session, model);
|
ComponentFactory factory = getComponentFactory(session, model);
|
||||||
factory.onCreate(session, realm, model);
|
factory.onCreate(session, realm, model);
|
||||||
if (factory instanceof UserStorageProviderFactory) {
|
UserProvider users = session.users();
|
||||||
((OnCreateComponent)session.userStorageManager()).onCreate(session, realm, model);
|
if (users instanceof OnCreateComponent) {
|
||||||
|
((OnCreateComponent) users).onCreate(session, realm, model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public static void notifyUpdated(KeycloakSession session, RealmModel realm, ComponentModel oldModel, ComponentModel newModel) {
|
public static void notifyUpdated(KeycloakSession session, RealmModel realm, ComponentModel oldModel, ComponentModel newModel) {
|
||||||
ComponentFactory factory = getComponentFactory(session, newModel);
|
ComponentFactory factory = getComponentFactory(session, newModel);
|
||||||
factory.onUpdate(session, realm, oldModel, newModel);
|
factory.onUpdate(session, realm, oldModel, newModel);
|
||||||
if (factory instanceof UserStorageProviderFactory) {
|
UserProvider users = session.users();
|
||||||
((OnUpdateComponent)session.userStorageManager()).onUpdate(session, realm, oldModel, newModel);
|
if (users instanceof OnUpdateComponent) {
|
||||||
|
((OnUpdateComponent) users).onUpdate(session, realm, oldModel, newModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public static void notifyPreRemove(KeycloakSession session, RealmModel realm, ComponentModel model) {
|
public static void notifyPreRemove(KeycloakSession session, RealmModel realm, ComponentModel model) {
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
package org.keycloak.models.utils;
|
package org.keycloak.models.utils;
|
||||||
|
|
||||||
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
import org.keycloak.provider.ProviderEvent;
|
import org.keycloak.provider.ProviderEvent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -25,4 +26,14 @@ import org.keycloak.provider.ProviderEvent;
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
*/
|
*/
|
||||||
public class PostMigrationEvent implements ProviderEvent {
|
public class PostMigrationEvent implements ProviderEvent {
|
||||||
|
|
||||||
|
private final KeycloakSessionFactory factory;
|
||||||
|
|
||||||
|
public PostMigrationEvent(KeycloakSessionFactory factory) {
|
||||||
|
this.factory = factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeycloakSessionFactory getFactory() {
|
||||||
|
return this.factory;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,6 @@ import org.jboss.logging.Logger;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.services.ServicesLogger;
|
|
||||||
import org.keycloak.timer.ScheduledTask;
|
import org.keycloak.timer.ScheduledTask;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -54,7 +53,7 @@ public class ScheduledTaskRunner implements Runnable {
|
||||||
}
|
}
|
||||||
runTask(session);
|
runTask(session);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
ServicesLogger.LOGGER.failedToRunScheduledTask(t, task.getClass().getSimpleName());
|
logger.errorf(t, "Failed to run scheduled task %s", task.getClass().getSimpleName());
|
||||||
|
|
||||||
session.getTransactionManager().rollback();
|
session.getTransactionManager().rollback();
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -64,7 +63,7 @@ public class ScheduledTaskRunner implements Runnable {
|
||||||
try {
|
try {
|
||||||
session.close();
|
session.close();
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
ServicesLogger.LOGGER.failedToCloseProviderSession(t);
|
logger.errorf(t, "Failed to close ProviderSession");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,6 +5,7 @@ import org.keycloak.models.ClientScopeProvider;
|
||||||
import org.keycloak.models.GroupProvider;
|
import org.keycloak.models.GroupProvider;
|
||||||
import org.keycloak.models.RealmProvider;
|
import org.keycloak.models.RealmProvider;
|
||||||
import org.keycloak.models.RoleProvider;
|
import org.keycloak.models.RoleProvider;
|
||||||
|
import org.keycloak.models.UserProvider;
|
||||||
import org.keycloak.provider.Provider;
|
import org.keycloak.provider.Provider;
|
||||||
|
|
||||||
public interface DatastoreProvider extends Provider {
|
public interface DatastoreProvider extends Provider {
|
||||||
|
@ -19,4 +20,6 @@ public interface DatastoreProvider extends Provider {
|
||||||
|
|
||||||
public RoleProvider roles();
|
public RoleProvider roles();
|
||||||
|
|
||||||
|
public UserProvider users();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2022 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.storage;
|
||||||
|
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.provider.ProviderEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event for notifying legacy store about the need to reconfigure user providers
|
||||||
|
* sychronization.
|
||||||
|
*/
|
||||||
|
public class LegacyStoreSyncEvent implements ProviderEvent {
|
||||||
|
|
||||||
|
private final KeycloakSession session;
|
||||||
|
private final RealmModel realm;
|
||||||
|
private final boolean removed;
|
||||||
|
|
||||||
|
public LegacyStoreSyncEvent(KeycloakSession session, RealmModel realm, boolean removed) {
|
||||||
|
this.session = session;
|
||||||
|
this.realm = realm;
|
||||||
|
this.removed = removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void fire(KeycloakSession session, RealmModel realm, boolean removed) {
|
||||||
|
session.getKeycloakSessionFactory().publish(new LegacyStoreSyncEvent(session, realm, removed));
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeycloakSession getSession() {
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RealmModel getRealm() {
|
||||||
|
return realm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getRemoved() {
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -17,7 +17,6 @@
|
||||||
|
|
||||||
org.keycloak.component.ComponentFactorySpi
|
org.keycloak.component.ComponentFactorySpi
|
||||||
org.keycloak.provider.ExceptionConverterSpi
|
org.keycloak.provider.ExceptionConverterSpi
|
||||||
org.keycloak.storage.UserStorageProviderSpi
|
|
||||||
org.keycloak.storage.federated.UserFederatedStorageProviderSpi
|
org.keycloak.storage.federated.UserFederatedStorageProviderSpi
|
||||||
org.keycloak.models.ClientSpi
|
org.keycloak.models.ClientSpi
|
||||||
org.keycloak.models.ClientScopeSpi
|
org.keycloak.models.ClientScopeSpi
|
||||||
|
|
|
@ -90,9 +90,9 @@ public class PermissionTicketService {
|
||||||
|
|
||||||
UserModel user = null;
|
UserModel user = null;
|
||||||
if(representation.getRequester() != null)
|
if(representation.getRequester() != null)
|
||||||
user = this.authorization.getKeycloakSession().userStorageManager().getUserById(this.authorization.getRealm(), representation.getRequester());
|
user = this.authorization.getKeycloakSession().users().getUserById(this.authorization.getRealm(), representation.getRequester());
|
||||||
else
|
else
|
||||||
user = this.authorization.getKeycloakSession().userStorageManager().getUserByUsername(this.authorization.getRealm(), representation.getRequesterName());
|
user = this.authorization.getKeycloakSession().users().getUserByUsername(this.authorization.getRealm(), representation.getRequesterName());
|
||||||
|
|
||||||
if (user == null)
|
if (user == null)
|
||||||
throw new ErrorResponseException("invalid_permission", "Requester does not exists in this server as user.", Response.Status.BAD_REQUEST);
|
throw new ErrorResponseException("invalid_permission", "Requester does not exists in this server as user.", Response.Status.BAD_REQUEST);
|
||||||
|
|
|
@ -47,7 +47,6 @@ import org.keycloak.provider.InvalidationHandler.ObjectType;
|
||||||
import org.keycloak.services.clientpolicy.ClientPolicyManager;
|
import org.keycloak.services.clientpolicy.ClientPolicyManager;
|
||||||
import org.keycloak.sessions.AuthenticationSessionProvider;
|
import org.keycloak.sessions.AuthenticationSessionProvider;
|
||||||
import org.keycloak.storage.DatastoreProvider;
|
import org.keycloak.storage.DatastoreProvider;
|
||||||
import org.keycloak.storage.UserStorageManager;
|
|
||||||
import org.keycloak.storage.federated.UserFederatedStorageProvider;
|
import org.keycloak.storage.federated.UserFederatedStorageProvider;
|
||||||
import org.keycloak.vault.DefaultVaultTranscriber;
|
import org.keycloak.vault.DefaultVaultTranscriber;
|
||||||
import org.keycloak.vault.VaultProvider;
|
import org.keycloak.vault.VaultProvider;
|
||||||
|
@ -77,7 +76,6 @@ public class DefaultKeycloakSession implements KeycloakSession {
|
||||||
private final Map<String, Object> attributes = new HashMap<>();
|
private final Map<String, Object> attributes = new HashMap<>();
|
||||||
private final Map<InvalidableObjectType, Set<Object>> invalidationMap = new HashMap<>();
|
private final Map<InvalidableObjectType, Set<Object>> invalidationMap = new HashMap<>();
|
||||||
private DatastoreProvider datastoreProvider;
|
private DatastoreProvider datastoreProvider;
|
||||||
private UserStorageManager userStorageManager;
|
|
||||||
private UserCredentialStoreManager userCredentialStorageManager;
|
private UserCredentialStoreManager userCredentialStorageManager;
|
||||||
private UserSessionProvider sessionProvider;
|
private UserSessionProvider sessionProvider;
|
||||||
private UserLoginFailureProvider userLoginFailureProvider;
|
private UserLoginFailureProvider userLoginFailureProvider;
|
||||||
|
@ -217,21 +215,14 @@ public class DefaultKeycloakSession implements KeycloakSession {
|
||||||
return groups();
|
return groups();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserProvider userStorageManager() {
|
public UserProvider userStorageManager() {
|
||||||
if (userStorageManager == null) userStorageManager = new UserStorageManager(this);
|
return users();
|
||||||
return userStorageManager;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserProvider users() {
|
public UserProvider users() {
|
||||||
UserCache cache = getProvider(UserCache.class);
|
return getDatastoreProvider().users();
|
||||||
if (cache != null) {
|
|
||||||
return cache;
|
|
||||||
} else {
|
|
||||||
return userStorageManager();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -52,6 +52,7 @@ import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.RoleRepresentation;
|
import org.keycloak.representations.idm.RoleRepresentation;
|
||||||
import org.keycloak.sessions.AuthenticationSessionProvider;
|
import org.keycloak.sessions.AuthenticationSessionProvider;
|
||||||
|
import org.keycloak.storage.LegacyStoreSyncEvent;
|
||||||
import org.keycloak.services.clientregistration.policy.DefaultClientRegistrationPolicies;
|
import org.keycloak.services.clientregistration.policy.DefaultClientRegistrationPolicies;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -271,10 +272,7 @@ public class RealmManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh periodic sync tasks for configured storageProviders
|
// Refresh periodic sync tasks for configured storageProviders
|
||||||
UserStorageSyncManager storageSync = new UserStorageSyncManager();
|
LegacyStoreSyncEvent.fire(session, realm, true);
|
||||||
realm.getUserStorageProvidersStream()
|
|
||||||
.forEachOrdered(provider -> storageSync.notifyToRefreshPeriodicSync(session, realm, provider, true));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
return removed;
|
return removed;
|
||||||
}
|
}
|
||||||
|
@ -588,9 +586,7 @@ public class RealmManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh periodic sync tasks for configured storageProviders
|
// Refresh periodic sync tasks for configured storageProviders
|
||||||
UserStorageSyncManager storageSync = new UserStorageSyncManager();
|
LegacyStoreSyncEvent.fire(session, realm, false);
|
||||||
realm.getUserStorageProvidersStream()
|
|
||||||
.forEachOrdered(provider -> storageSync.notifyToRefreshPeriodicSync(session, realm, provider, false));
|
|
||||||
|
|
||||||
setupAuthorizationServices(realm);
|
setupAuthorizationServices(realm);
|
||||||
setupClientRegistrations(realm);
|
setupClientRegistrations(realm);
|
||||||
|
|
|
@ -45,15 +45,8 @@ import org.keycloak.services.error.KcUnrecognizedPropertyExceptionHandler;
|
||||||
import org.keycloak.services.filters.KeycloakSecurityHeadersFilter;
|
import org.keycloak.services.filters.KeycloakSecurityHeadersFilter;
|
||||||
import org.keycloak.services.managers.ApplianceBootstrap;
|
import org.keycloak.services.managers.ApplianceBootstrap;
|
||||||
import org.keycloak.services.managers.RealmManager;
|
import org.keycloak.services.managers.RealmManager;
|
||||||
import org.keycloak.services.managers.UserStorageSyncManager;
|
|
||||||
import org.keycloak.services.resources.admin.AdminRoot;
|
import org.keycloak.services.resources.admin.AdminRoot;
|
||||||
import org.keycloak.services.scheduled.ClearExpiredClientInitialAccessTokens;
|
|
||||||
import org.keycloak.services.scheduled.ClearExpiredEvents;
|
|
||||||
import org.keycloak.services.scheduled.ClearExpiredUserSessions;
|
|
||||||
import org.keycloak.services.scheduled.ClusterAwareScheduledTaskRunner;
|
|
||||||
import org.keycloak.services.scheduled.ScheduledTaskRunner;
|
|
||||||
import org.keycloak.services.util.ObjectMapperResolver;
|
import org.keycloak.services.util.ObjectMapperResolver;
|
||||||
import org.keycloak.timer.TimerProvider;
|
|
||||||
import org.keycloak.transaction.JtaTransactionManagerLookup;
|
import org.keycloak.transaction.JtaTransactionManagerLookup;
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
|
@ -156,9 +149,7 @@ public class KeycloakApplication extends Application {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
sessionFactory.publish(new PostMigrationEvent());
|
sessionFactory.publish(new PostMigrationEvent(sessionFactory));
|
||||||
|
|
||||||
setupScheduledTasks(sessionFactory);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void shutdown() {
|
protected void shutdown() {
|
||||||
|
@ -236,21 +227,6 @@ public class KeycloakApplication extends Application {
|
||||||
return factory;
|
return factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setupScheduledTasks(final KeycloakSessionFactory sessionFactory) {
|
|
||||||
long interval = Config.scope("scheduled").getLong("interval", 900L) * 1000;
|
|
||||||
|
|
||||||
KeycloakSession session = sessionFactory.create();
|
|
||||||
try {
|
|
||||||
TimerProvider timer = session.getProvider(TimerProvider.class);
|
|
||||||
timer.schedule(new ClusterAwareScheduledTaskRunner(sessionFactory, new ClearExpiredEvents(), interval), interval, "ClearExpiredEvents");
|
|
||||||
timer.schedule(new ClusterAwareScheduledTaskRunner(sessionFactory, new ClearExpiredClientInitialAccessTokens(), interval), interval, "ClearExpiredClientInitialAccessTokens");
|
|
||||||
timer.schedule(new ScheduledTaskRunner(sessionFactory, new ClearExpiredUserSessions()), interval, ClearExpiredUserSessions.TASK_NAME);
|
|
||||||
new UserStorageSyncManager().bootstrapPeriodic(sessionFactory, timer);
|
|
||||||
} finally {
|
|
||||||
session.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static KeycloakSessionFactory getSessionFactory() {
|
public static KeycloakSessionFactory getSessionFactory() {
|
||||||
return sessionFactory;
|
return sessionFactory;
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,10 +112,11 @@ import org.keycloak.services.managers.AuthenticationManager;
|
||||||
import org.keycloak.services.managers.LDAPServerCapabilitiesManager;
|
import org.keycloak.services.managers.LDAPServerCapabilitiesManager;
|
||||||
import org.keycloak.services.managers.RealmManager;
|
import org.keycloak.services.managers.RealmManager;
|
||||||
import org.keycloak.services.managers.ResourceAdminManager;
|
import org.keycloak.services.managers.ResourceAdminManager;
|
||||||
import org.keycloak.services.managers.UserStorageSyncManager;
|
import org.keycloak.services.resources.admin.ext.AdminRealmResourceProvider;
|
||||||
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
|
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
|
||||||
import org.keycloak.services.resources.admin.permissions.AdminPermissionManagement;
|
import org.keycloak.services.resources.admin.permissions.AdminPermissionManagement;
|
||||||
import org.keycloak.services.resources.admin.permissions.AdminPermissions;
|
import org.keycloak.services.resources.admin.permissions.AdminPermissions;
|
||||||
|
import org.keycloak.storage.LegacyStoreSyncEvent;
|
||||||
import org.keycloak.utils.ProfileHelper;
|
import org.keycloak.utils.ProfileHelper;
|
||||||
import org.keycloak.utils.ReservedCharValidator;
|
import org.keycloak.utils.ReservedCharValidator;
|
||||||
|
|
||||||
|
@ -436,9 +437,7 @@ public class RealmAdminResource {
|
||||||
RepresentationToModel.updateRealm(rep, realm, session);
|
RepresentationToModel.updateRealm(rep, realm, session);
|
||||||
|
|
||||||
// Refresh periodic sync tasks for configured federationProviders
|
// Refresh periodic sync tasks for configured federationProviders
|
||||||
UserStorageSyncManager usersSyncManager = new UserStorageSyncManager();
|
LegacyStoreSyncEvent.fire(session, realm, false);
|
||||||
realm.getUserStorageProvidersStream().forEachOrdered(fedProvider ->
|
|
||||||
usersSyncManager.notifyToRefreshPeriodicSync(session, realm, fedProvider, false));
|
|
||||||
|
|
||||||
// This populates the map in DefaultKeycloakContext to be used when treating the event
|
// This populates the map in DefaultKeycloakContext to be used when treating the event
|
||||||
session.getContext().getUri();
|
session.getContext().getUri();
|
||||||
|
@ -531,12 +530,18 @@ public class RealmAdminResource {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Path("user-storage")
|
@Path("{extension}")
|
||||||
public UserStorageProviderResource userStorage() {
|
public Object extension(@PathParam("extension") String extension) {
|
||||||
UserStorageProviderResource fed = new UserStorageProviderResource(realm, auth, adminEvent);
|
AdminRealmResourceProvider provider = session.getProvider(AdminRealmResourceProvider.class, extension);
|
||||||
ResteasyProviderFactory.getInstance().injectProperties(fed);
|
if (provider != null) {
|
||||||
//resourceContext.initResource(fed);
|
Object resource = provider.getResource(session, realm, auth, adminEvent);
|
||||||
return fed;
|
if (resource != null) {
|
||||||
|
ResteasyProviderFactory.getInstance().injectProperties(resource);
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Path("authentication")
|
@Path("authentication")
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* JBoss, Home of Professional Open Source.
|
||||||
|
* Copyright 2022 Red Hat, Inc., and individual 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.resources.admin.ext;
|
||||||
|
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.provider.Provider;
|
||||||
|
import org.keycloak.services.resources.admin.AdminEventBuilder;
|
||||||
|
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>A {@link AdminRealmResourceProvider} creates JAX-RS <emphasis>sub-resource</emphasis> instances for paths relative
|
||||||
|
* to Realm's RESTful Admin API that could not be resolved by the server.
|
||||||
|
*/
|
||||||
|
public interface AdminRealmResourceProvider extends Provider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns a JAX-RS resource instance.
|
||||||
|
*
|
||||||
|
* @return a JAX-RS sub-resource instance
|
||||||
|
*/
|
||||||
|
Object getResource(KeycloakSession session, RealmModel realm, AdminPermissionEvaluator auth,
|
||||||
|
AdminEventBuilder adminEvent);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* JBoss, Home of Professional Open Source.
|
||||||
|
* Copyright 2022 Red Hat, Inc., and individual 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.resources.admin.ext;
|
||||||
|
|
||||||
|
import org.keycloak.provider.ProviderFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>A factory that creates {@link AdminRealmResourceProvider} instances.
|
||||||
|
*/
|
||||||
|
public interface AdminRealmResourceProviderFactory extends ProviderFactory<AdminRealmResourceProvider> {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* JBoss, Home of Professional Open Source.
|
||||||
|
* Copyright 2022 Red Hat, Inc., and individual 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.resources.admin.ext;
|
||||||
|
|
||||||
|
import org.keycloak.provider.Provider;
|
||||||
|
import org.keycloak.provider.ProviderFactory;
|
||||||
|
import org.keycloak.provider.Spi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>A {@link Spi} to plug additional sub-resources to Realms' RESTful Admin API.
|
||||||
|
*
|
||||||
|
* <p>Implementors can use this {@link Spi} to provide additional services to the mentioned API and extend Keycloak capabilities by
|
||||||
|
* creating JAX-RS sub-resources for paths not known by the server.
|
||||||
|
*/
|
||||||
|
public class AdminRealmResourceSpi implements Spi {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInternal() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "admin-realm-restapi-extension";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends Provider> getProviderClass() {
|
||||||
|
return AdminRealmResourceProvider.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends ProviderFactory<?>> getProviderFactoryClass() {
|
||||||
|
return AdminRealmResourceProviderFactory.class;
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,3 +27,4 @@ org.keycloak.encoding.ResourceEncodingSpi
|
||||||
org.keycloak.protocol.oidc.grants.ciba.channel.AuthenticationChannelSpi
|
org.keycloak.protocol.oidc.grants.ciba.channel.AuthenticationChannelSpi
|
||||||
org.keycloak.protocol.oidc.grants.ciba.resolvers.CIBALoginUserResolverSpi
|
org.keycloak.protocol.oidc.grants.ciba.resolvers.CIBALoginUserResolverSpi
|
||||||
org.keycloak.protocol.oidc.rar.AuthorizationRequestParserSpi
|
org.keycloak.protocol.oidc.rar.AuthorizationRequestParserSpi
|
||||||
|
org.keycloak.services.resources.admin.ext.AdminRealmResourceSpi
|
||||||
|
|
|
@ -32,7 +32,8 @@ 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.representations.idm.ComponentRepresentation;
|
import org.keycloak.representations.idm.ComponentRepresentation;
|
||||||
import org.keycloak.services.managers.UserStorageSyncManager;
|
import org.keycloak.storage.managers.UserStorageSyncManager;
|
||||||
|
import org.keycloak.storage.UserStoragePrivateUtil;
|
||||||
import org.keycloak.storage.UserStorageProviderModel;
|
import org.keycloak.storage.UserStorageProviderModel;
|
||||||
import org.keycloak.storage.ldap.LDAPStorageProvider;
|
import org.keycloak.storage.ldap.LDAPStorageProvider;
|
||||||
import org.keycloak.storage.ldap.idm.model.LDAPObject;
|
import org.keycloak.storage.ldap.idm.model.LDAPObject;
|
||||||
|
|
|
@ -40,7 +40,9 @@ import org.keycloak.models.cache.UserCache;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.representations.idm.ComponentRepresentation;
|
import org.keycloak.representations.idm.ComponentRepresentation;
|
||||||
import org.keycloak.representations.idm.SynchronizationResultRepresentation;
|
import org.keycloak.representations.idm.SynchronizationResultRepresentation;
|
||||||
import org.keycloak.services.managers.UserStorageSyncManager;
|
import org.keycloak.storage.managers.UserStorageSyncManager;
|
||||||
|
import org.keycloak.storage.UserStoragePrivateUtil;
|
||||||
|
import org.keycloak.storage.UserStorageUtil;
|
||||||
import org.keycloak.storage.ldap.LDAPStorageProvider;
|
import org.keycloak.storage.ldap.LDAPStorageProvider;
|
||||||
import org.keycloak.storage.ldap.LDAPStorageProviderFactory;
|
import org.keycloak.storage.ldap.LDAPStorageProviderFactory;
|
||||||
import org.keycloak.storage.ldap.LDAPUtils;
|
import org.keycloak.storage.ldap.LDAPUtils;
|
||||||
|
|
|
@ -51,6 +51,8 @@ import org.keycloak.provider.ProviderManager;
|
||||||
import org.keycloak.provider.Spi;
|
import org.keycloak.provider.Spi;
|
||||||
import org.keycloak.services.DefaultComponentFactoryProviderFactory;
|
import org.keycloak.services.DefaultComponentFactoryProviderFactory;
|
||||||
import org.keycloak.services.DefaultKeycloakSessionFactory;
|
import org.keycloak.services.DefaultKeycloakSessionFactory;
|
||||||
|
import org.keycloak.storage.DatastoreProviderFactory;
|
||||||
|
import org.keycloak.storage.DatastoreSpi;
|
||||||
import org.keycloak.timer.TimerSpi;
|
import org.keycloak.timer.TimerSpi;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
|
||||||
|
@ -227,6 +229,7 @@ public abstract class KeycloakModelTest {
|
||||||
.add(UserLoginFailureSpi.class)
|
.add(UserLoginFailureSpi.class)
|
||||||
.add(UserSessionSpi.class)
|
.add(UserSessionSpi.class)
|
||||||
.add(UserSpi.class)
|
.add(UserSpi.class)
|
||||||
|
.add(DatastoreSpi.class)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
private static final Set<Class<? extends ProviderFactory>> ALLOWED_FACTORIES = ImmutableSet.<Class<? extends ProviderFactory>>builder()
|
private static final Set<Class<? extends ProviderFactory>> ALLOWED_FACTORIES = ImmutableSet.<Class<? extends ProviderFactory>>builder()
|
||||||
|
@ -234,6 +237,7 @@ public abstract class KeycloakModelTest {
|
||||||
.add(DefaultAuthorizationProviderFactory.class)
|
.add(DefaultAuthorizationProviderFactory.class)
|
||||||
.add(DefaultExecutorsProviderFactory.class)
|
.add(DefaultExecutorsProviderFactory.class)
|
||||||
.add(DeploymentStateProviderFactory.class)
|
.add(DeploymentStateProviderFactory.class)
|
||||||
|
.add(DatastoreProviderFactory.class)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
protected static final List<KeycloakModelParameters> MODEL_PARAMETERS;
|
protected static final List<KeycloakModelParameters> MODEL_PARAMETERS;
|
||||||
|
@ -312,7 +316,7 @@ public abstract class KeycloakModelTest {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
res.init();
|
res.init();
|
||||||
res.publish(new PostMigrationEvent());
|
res.publish(new PostMigrationEvent(res));
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,8 @@ 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.UserProvider;
|
import org.keycloak.models.UserProvider;
|
||||||
import org.keycloak.services.managers.UserStorageSyncManager;
|
import org.keycloak.storage.managers.UserStorageSyncManager;
|
||||||
|
import org.keycloak.storage.UserStoragePrivateUtil;
|
||||||
import org.keycloak.storage.UserStorageProvider;
|
import org.keycloak.storage.UserStorageProvider;
|
||||||
import org.keycloak.storage.UserStorageProviderFactory;
|
import org.keycloak.storage.UserStorageProviderFactory;
|
||||||
import org.keycloak.storage.UserStorageProviderModel;
|
import org.keycloak.storage.UserStorageProviderModel;
|
||||||
|
|
Loading…
Reference in a new issue