Merge pull request #3315 from patriot1burke/master

import and sync spi
This commit is contained in:
Bill Burke authored and GitHub committed 2016-10-14 10:12:42 -04:00
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);
@@ -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;
}
}
@@ -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);
}