commit
1c0abbd722
20 files changed
+418
-37
No files matched your search
@@ -122,34 +122,36 @@ public class UserCredentialStoreManager implements UserCredentialManager, OnUser
|
||||
UserFederationProvider link = session.users().getFederationLink(realm, user);
|
||||
if (link != null) {
|
||||
session.users().validateUser(realm, user);
|
||||
Iterator<CredentialInput> it = toValidate.iterator();
|
||||
while (it.hasNext()) {
|
||||
CredentialInput input = it.next();
|
||||
if (link.supportsCredentialType(input.getType())
|
||||
&& link.isValid(realm, user, input)) {
|
||||
it.remove();
|
||||
}
|
||||
validate(realm, user, toValidate, link);
|
||||
} // </deprecate>
|
||||
else if (user.getFederationLink() != null) {
|
||||
UserStorageProvider provider = UserStorageManager.getStorageProvider(session, realm, user.getFederationLink());
|
||||
if (provider != null && provider instanceof CredentialInputValidator) {
|
||||
validate(realm, user, toValidate, ((CredentialInputValidator)provider));
|
||||
}
|
||||
}
|
||||
// </deprecate>
|
||||
}
|
||||
|
||||
if (toValidate.isEmpty()) return true;
|
||||
|
||||
List<CredentialInputValidator> credentialProviders = getCredentialProviders(realm, CredentialInputValidator.class);
|
||||
for (CredentialInputValidator validator : credentialProviders) {
|
||||
Iterator<CredentialInput> it = toValidate.iterator();
|
||||
while (it.hasNext()) {
|
||||
CredentialInput input = it.next();
|
||||
if (validator.supportsCredentialType(input.getType()) && validator.isValid(realm, user, input)) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
validate(realm, user, toValidate, validator);
|
||||
|
||||
}
|
||||
return toValidate.isEmpty();
|
||||
}
|
||||
|
||||
private void validate(RealmModel realm, UserModel user, List<CredentialInput> toValidate, CredentialInputValidator validator) {
|
||||
Iterator<CredentialInput> it = toValidate.iterator();
|
||||
while (it.hasNext()) {
|
||||
CredentialInput input = it.next();
|
||||
if (validator.supportsCredentialType(input.getType()) && validator.isValid(realm, user, input)) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected <T> List<T> getCredentialProviders(RealmModel realm, Class<T> type) {
|
||||
List<T> list = new LinkedList<T>();
|
||||
for (ProviderFactory f : session.getKeycloakSessionFactory().getProviderFactories(CredentialProvider.class)) {
|
||||
@@ -178,6 +180,12 @@ public class UserCredentialStoreManager implements UserCredentialManager, OnUser
|
||||
if (link.updateCredential(realm, user, input)) return;
|
||||
}
|
||||
// </deprecated>
|
||||
else if (user.getFederationLink() != null) {
|
||||
UserStorageProvider provider = UserStorageManager.getStorageProvider(session, realm, user.getFederationLink());
|
||||
if (provider != null && provider instanceof CredentialInputUpdater) {
|
||||
if (((CredentialInputUpdater)provider).updateCredential(realm, user, input)) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<CredentialInputUpdater> credentialProviders = getCredentialProviders(realm, CredentialInputUpdater.class);
|
||||
@@ -203,6 +211,12 @@ public class UserCredentialStoreManager implements UserCredentialManager, OnUser
|
||||
if (link != null && link.getSupportedCredentialTypes().contains(credentialType)) {
|
||||
link.disableCredentialType(realm, user, credentialType);
|
||||
}
|
||||
else if (user.getFederationLink() != null) {
|
||||
UserStorageProvider provider = UserStorageManager.getStorageProvider(session, realm, user.getFederationLink());
|
||||
if (provider != null && provider instanceof CredentialInputUpdater) {
|
||||
((CredentialInputUpdater)provider).disableCredentialType(realm, user, credentialType);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -233,6 +247,12 @@ public class UserCredentialStoreManager implements UserCredentialManager, OnUser
|
||||
if (link.isConfiguredFor(realm, user, type)) return true;
|
||||
}
|
||||
// </deprecate>
|
||||
else if (user.getFederationLink() != null) {
|
||||
UserStorageProvider provider = UserStorageManager.getStorageProvider(session, realm, user.getFederationLink());
|
||||
if (provider != null && provider instanceof CredentialInputValidator) {
|
||||
if (((CredentialInputValidator)provider).isConfiguredFor(realm, user, type)) return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -299,6 +299,14 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr
|
||||
return spis;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spi getSpi(Class<? extends Provider> providerClass) {
|
||||
for (Spi spi : spis) {
|
||||
if (spi.getProviderClass().equals(providerClass)) return spi;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Provider> ProviderFactory<T> getProviderFactory(Class<T> clazz) {
|
||||
return getProviderFactory(clazz, provider.get(clazz));
|
||||
|
||||
@@ -49,6 +49,7 @@ import org.keycloak.representations.idm.OAuthClientRepresentation;
|
||||
import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
@@ -491,6 +492,13 @@ public class RealmManager implements RealmImporter {
|
||||
usersSyncManager.notifyToRefreshPeriodicSync(session, realm, fedProvider, false);
|
||||
}
|
||||
|
||||
// Refresh periodic sync tasks for configured storageProviders
|
||||
List<UserStorageProviderModel> storageProviders = realm.getUserStorageProviders();
|
||||
UserStorageSyncManager storageSync = new UserStorageSyncManager();
|
||||
for (UserStorageProviderModel provider : storageProviders) {
|
||||
storageSync.notifyToRefreshPeriodicSync(session, realm, provider, false);
|
||||
}
|
||||
|
||||
setupAuthorizationServices(realm);
|
||||
fireRealmPostCreate(realm);
|
||||
|
||||
|
||||
@@ -165,6 +165,11 @@ public class UserStorageSyncManager {
|
||||
|
||||
// Ensure all cluster nodes are notified
|
||||
public void notifyToRefreshPeriodicSync(KeycloakSession session, RealmModel realm, UserStorageProviderModel provider, boolean removed) {
|
||||
UserStorageProviderFactory factory = (UserStorageProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(UserStorageProvider.class, provider.getProviderId());
|
||||
if (!(factory instanceof ImportSynchronization) || !provider.isImportEnabled()) {
|
||||
return;
|
||||
|
||||
}
|
||||
UserStorageProviderClusterEvent event = UserStorageProviderClusterEvent.createEvent(removed, realm.getId(), provider);
|
||||
session.getProvider(ClusterProvider.class).notify(USER_STORAGE_TASK_KEY, event);
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ import org.keycloak.services.ServicesLogger;
|
||||
import org.keycloak.services.filters.KeycloakTransactionCommitter;
|
||||
import org.keycloak.services.managers.ApplianceBootstrap;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.services.managers.UserStorageSyncManager;
|
||||
import org.keycloak.services.managers.UsersSyncManager;
|
||||
import org.keycloak.services.resources.admin.AdminRoot;
|
||||
import org.keycloak.services.scheduled.ClearExpiredEvents;
|
||||
@@ -319,6 +320,7 @@ public class KeycloakApplication extends Application {
|
||||
timer.schedule(new ClusterAwareScheduledTaskRunner(sessionFactory, new ClearExpiredEvents(), interval), interval, "ClearExpiredEvents");
|
||||
timer.schedule(new ClusterAwareScheduledTaskRunner(sessionFactory, new ClearExpiredUserSessions(), interval), interval, "ClearExpiredUserSessions");
|
||||
new UsersSyncManager().bootstrapPeriodic(sessionFactory, timer);
|
||||
new UserStorageSyncManager().bootstrapPeriodic(sessionFactory, timer);
|
||||
} finally {
|
||||
session.close();
|
||||
}
|
||||
|
||||
@@ -361,6 +361,14 @@ public class RealmAdminResource {
|
||||
return fed;
|
||||
}
|
||||
|
||||
@Path("user-storage")
|
||||
public UserStorageProviderResource userStorage() {
|
||||
UserStorageProviderResource fed = new UserStorageProviderResource(realm, auth, adminEvent);
|
||||
ResteasyProviderFactory.getInstance().injectProperties(fed);
|
||||
//resourceContext.initResource(fed);
|
||||
return fed;
|
||||
}
|
||||
|
||||
@Path("authentication")
|
||||
public AuthenticationManagementResource flows() {
|
||||
AuthenticationManagementResource resource = new AuthenticationManagementResource(realm, session, auth, adminEvent);
|
||||
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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.resources.admin;
|
||||
|
||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import org.jboss.resteasy.spi.NotFoundException;
|
||||
import org.keycloak.common.ClientConnection;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.services.ServicesLogger;
|
||||
import org.keycloak.services.managers.UserStorageSyncManager;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
import org.keycloak.storage.user.SynchronizationResult;
|
||||
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class UserStorageProviderResource {
|
||||
protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER;
|
||||
|
||||
protected RealmModel realm;
|
||||
|
||||
protected RealmAuth auth;
|
||||
|
||||
protected AdminEventBuilder adminEvent;
|
||||
|
||||
@Context
|
||||
protected ClientConnection clientConnection;
|
||||
|
||||
@Context
|
||||
protected UriInfo uriInfo;
|
||||
|
||||
@Context
|
||||
protected KeycloakSession session;
|
||||
|
||||
@Context
|
||||
protected HttpHeaders headers;
|
||||
|
||||
public UserStorageProviderResource(RealmModel realm, RealmAuth auth, AdminEventBuilder adminEvent) {
|
||||
this.auth = auth;
|
||||
this.realm = realm;
|
||||
this.adminEvent = adminEvent;
|
||||
|
||||
auth.init(RealmAuth.Resource.USER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger sync of users
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@POST
|
||||
@Path("{id}/sync")
|
||||
@NoCache
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public SynchronizationResult syncUsers(@PathParam("id") String id,
|
||||
@QueryParam("action") String action) {
|
||||
auth.requireManage();
|
||||
|
||||
ComponentModel model = realm.getComponent(id);
|
||||
if (model == null) {
|
||||
throw new NotFoundException("Could not find component");
|
||||
}
|
||||
if (!model.getProviderType().equals(UserStorageProvider.class.getName())) {
|
||||
throw new NotFoundException("found, but not a UserStorageProvider");
|
||||
}
|
||||
|
||||
UserStorageProviderModel providerModel = new UserStorageProviderModel(model);
|
||||
|
||||
|
||||
|
||||
logger.debug("Syncing users");
|
||||
|
||||
UserStorageSyncManager syncManager = new UserStorageSyncManager();
|
||||
SynchronizationResult syncResult;
|
||||
if ("triggerFullSync".equals(action)) {
|
||||
syncResult = syncManager.syncAllUsers(session.getKeycloakSessionFactory(), realm.getId(), providerModel);
|
||||
} else if ("triggerChangedUsersSync".equals(action)) {
|
||||
syncResult = syncManager.syncChangedUsers(session.getKeycloakSessionFactory(), realm.getId(), providerModel);
|
||||
} else {
|
||||
throw new NotFoundException("Unknown action: " + action);
|
||||
}
|
||||
|
||||
Map<String, Object> eventRep = new HashMap<>();
|
||||
eventRep.put("action", action);
|
||||
eventRep.put("result", syncResult);
|
||||
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).representation(eventRep).success();
|
||||
|
||||
return syncResult;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
+4
@@ -50,6 +50,7 @@ import org.keycloak.representations.info.ServerInfoRepresentation;
|
||||
import org.keycloak.representations.info.SpiInfoRepresentation;
|
||||
import org.keycloak.representations.info.SystemInfoRepresentation;
|
||||
import org.keycloak.representations.info.ThemeInfoRepresentation;
|
||||
import org.keycloak.storage.user.ImportSynchronization;
|
||||
import org.keycloak.theme.Theme;
|
||||
import org.keycloak.theme.ThemeProvider;
|
||||
|
||||
@@ -135,6 +136,9 @@ public class ServerInfoAdminResource {
|
||||
List<ProviderConfigProperty> configProperties = configured.getConfigProperties();
|
||||
if (configProperties == null) configProperties = Collections.EMPTY_LIST;
|
||||
rep.setProperties(ModelToRepresentation.toRepresentation(configProperties));
|
||||
if (pi instanceof ImportSynchronization) {
|
||||
rep.getMetadata().put("synchronizable", true);
|
||||
}
|
||||
List<ComponentTypeRepresentation> reps = info.getComponentTypes().get(spi.getProviderClass().getName());
|
||||
if (reps == null) {
|
||||
reps = new LinkedList<>();
|
||||
|
||||
@@ -38,6 +38,8 @@ import org.keycloak.models.cache.CachedUserModel;
|
||||
import org.keycloak.models.cache.OnUserCache;
|
||||
import org.keycloak.storage.federated.UserFederatedStorageProvider;
|
||||
import org.keycloak.credential.CredentialAuthentication;
|
||||
import org.keycloak.storage.user.ImportSynchronization;
|
||||
import org.keycloak.storage.user.ImportedUserValidation;
|
||||
import org.keycloak.storage.user.UserLookupProvider;
|
||||
import org.keycloak.storage.user.UserQueryProvider;
|
||||
import org.keycloak.storage.user.UserRegistrationProvider;
|
||||
@@ -225,11 +227,38 @@ public class UserStorageManager implements UserProvider, OnUserCache {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows a UserStorageProvider to proxy and/or synchronize an imported user.
|
||||
*
|
||||
* @param realm
|
||||
* @param user
|
||||
* @return
|
||||
*/
|
||||
protected UserModel importValidation(RealmModel realm, UserModel user) {
|
||||
if (user == null || user.getFederationLink() == null) return user;
|
||||
UserStorageProvider provider = getStorageProvider(session, realm, user.getFederationLink());
|
||||
if (provider != null && provider instanceof ImportedUserValidation) {
|
||||
return ((ImportedUserValidation)provider).validate(realm, user);
|
||||
} else {
|
||||
return user;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected List<UserModel> importValidation(RealmModel realm, List<UserModel> users) {
|
||||
List<UserModel> tmp = new LinkedList<>();
|
||||
for (UserModel user : users) {
|
||||
tmp.add(importValidation(realm, user));
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserById(String id, RealmModel realm) {
|
||||
StorageId storageId = new StorageId(id);
|
||||
if (storageId.getProviderId() == null) {
|
||||
return localStorage().getUserById(id, realm);
|
||||
UserModel user = localStorage().getUserById(id, realm);
|
||||
return importValidation(realm, user);
|
||||
}
|
||||
UserLookupProvider provider = (UserLookupProvider)getStorageProvider(session, realm, storageId.getProviderId());
|
||||
return provider.getUserById(id, realm);
|
||||
@@ -243,7 +272,9 @@ public class UserStorageManager implements UserProvider, OnUserCache {
|
||||
@Override
|
||||
public UserModel getUserByUsername(String username, RealmModel realm) {
|
||||
UserModel user = localStorage().getUserByUsername(username, realm);
|
||||
if (user != null) return user;
|
||||
if (user != null) {
|
||||
return importValidation(realm, user);
|
||||
}
|
||||
for (UserLookupProvider provider : getStorageProviders(session, realm, UserLookupProvider.class)) {
|
||||
user = provider.getUserByUsername(username, realm);
|
||||
if (user != null) return user;
|
||||
@@ -257,7 +288,9 @@ public class UserStorageManager implements UserProvider, OnUserCache {
|
||||
if (user != null) return user;
|
||||
for (UserLookupProvider provider : getStorageProviders(session, realm, UserLookupProvider.class)) {
|
||||
user = provider.getUserByEmail(email, realm);
|
||||
if (user != null) return user;
|
||||
if (user != null) {
|
||||
return importValidation(realm, user);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -266,7 +299,7 @@ public class UserStorageManager implements UserProvider, OnUserCache {
|
||||
public UserModel getUserByFederatedIdentity(FederatedIdentityModel socialLink, RealmModel realm) {
|
||||
UserModel user = localStorage().getUserByFederatedIdentity(socialLink, realm);
|
||||
if (user != null) {
|
||||
return user;
|
||||
return importValidation(realm, user);
|
||||
}
|
||||
if (getFederatedStorage() == null) return null;
|
||||
String id = getFederatedStorage().getUserByFederatedIdentity(socialLink, realm);
|
||||
@@ -354,7 +387,7 @@ public class UserStorageManager implements UserProvider, OnUserCache {
|
||||
|
||||
@Override
|
||||
public List<UserModel> getUsers(final RealmModel realm, int firstResult, int maxResults, final boolean includeServiceAccounts) {
|
||||
return query((provider, first, max) -> {
|
||||
List<UserModel> results = query((provider, first, max) -> {
|
||||
if (provider instanceof UserProvider) { // it is local storage
|
||||
return ((UserProvider) provider).getUsers(realm, first, max, includeServiceAccounts);
|
||||
} else if (provider instanceof UserQueryProvider) {
|
||||
@@ -364,6 +397,7 @@ public class UserStorageManager implements UserProvider, OnUserCache {
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
, realm, firstResult, maxResults);
|
||||
return importValidation(realm, results);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -373,23 +407,26 @@ public class UserStorageManager implements UserProvider, OnUserCache {
|
||||
|
||||
@Override
|
||||
public List<UserModel> searchForUser(String search, RealmModel realm, int firstResult, int maxResults) {
|
||||
return query((provider, first, max) -> {
|
||||
List<UserModel> results = query((provider, first, max) -> {
|
||||
if (provider instanceof UserQueryProvider) {
|
||||
return ((UserQueryProvider)provider).searchForUser(search, realm, first, max);
|
||||
|
||||
}
|
||||
return Collections.EMPTY_LIST;
|
||||
}, realm, firstResult, maxResults);
|
||||
return importValidation(realm, results);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> searchForUser(Map<String, String> attributes, RealmModel realm) {
|
||||
return searchForUser(attributes, realm, 0, Integer.MAX_VALUE - 1);
|
||||
List<UserModel> results = searchForUser(attributes, realm, 0, Integer.MAX_VALUE - 1);
|
||||
return importValidation(realm, results);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> searchForUser(Map<String, String> attributes, RealmModel realm, int firstResult, int maxResults) {
|
||||
return query((provider, first, max) -> {
|
||||
List<UserModel> results = query((provider, first, max) -> {
|
||||
if (provider instanceof UserQueryProvider) {
|
||||
return ((UserQueryProvider)provider).searchForUser(attributes, realm, first, max);
|
||||
|
||||
@@ -397,6 +434,8 @@ public class UserStorageManager implements UserProvider, OnUserCache {
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
, realm, firstResult, maxResults);
|
||||
return importValidation(realm, results);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -417,7 +456,7 @@ public class UserStorageManager implements UserProvider, OnUserCache {
|
||||
}
|
||||
return Collections.EMPTY_LIST;
|
||||
}, realm,0, Integer.MAX_VALUE - 1);
|
||||
return results;
|
||||
return importValidation(realm, results);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -472,7 +511,7 @@ public class UserStorageManager implements UserProvider, OnUserCache {
|
||||
}
|
||||
return Collections.EMPTY_LIST;
|
||||
}, realm, firstResult, maxResults);
|
||||
return results;
|
||||
return importValidation(realm, results);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user