Merge pull request #3545 from patriot1burke/master
remove UserFederation SPI
This commit is contained in:
commit
57da86c598
117 changed files with 825 additions and 5819 deletions
|
@ -78,7 +78,7 @@ public class SecretQuestionCredentialProvider implements CredentialProvider, Cre
|
|||
creds.get(0).setValue(credInput.getValue());
|
||||
session.userCredentialManager().updateCredential(realm, user, creds.get(0));
|
||||
}
|
||||
session.getUserCache().evict(realm, user);
|
||||
session.userCache().evict(realm, user);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -86,7 +86,7 @@ public class SecretQuestionCredentialProvider implements CredentialProvider, Cre
|
|||
public void disableCredentialType(RealmModel realm, UserModel user, String credentialType) {
|
||||
if (!SECRET_QUESTION.equals(credentialType)) return;
|
||||
session.userCredentialManager().disableCredentialType(realm, user, credentialType);
|
||||
session.getUserCache().evict(realm, user);
|
||||
session.userCache().evict(realm, user);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
Example User Federation Provider
|
||||
===================================================
|
||||
|
||||
This is an example of user federation backed by a simple properties file. This properties file only contains username/password
|
||||
key pairs. To deploy, build this directory then take the jar and copy it to providers directory. Alternatively you can deploy as a module by running:
|
||||
|
||||
KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.userprops --resources=target/federation-properties-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi,org.keycloak.keycloak-server-spi-private"
|
||||
|
||||
Then registering the provider by editing `standalone/configuration/standalone.xml` and adding the module to the providers element:
|
||||
|
||||
<providers>
|
||||
...
|
||||
<provider>module:org.keycloak.examples.userprops</provider>
|
||||
</providers>
|
||||
|
||||
You will then have to restart the authentication server.
|
||||
|
||||
The ClasspathPropertiesFederationProvider is an example of a readonly provider. If you go to the Users/Federation
|
||||
page of the admin console you will see this provider listed under "classpath-properties. To configure this provider you
|
||||
specify a classpath to a properties file in the "path" field of the admin page for this plugin. This example includes
|
||||
a "test-users.properties" within the JAR that you can use as the variable.
|
||||
|
||||
The FilePropertiesFederationProvider is an example of a writable provider. It synchronizes changes made to
|
||||
username and password with the properties file. If you go to the Users/Federation page of the admin console you will
|
||||
see this provider listed under "file-properties". To configure this provider you specify a fully qualified file path to
|
||||
a properties file in the "path" field of the admin page for this plugin.
|
|
@ -1,152 +0,0 @@
|
|||
/*
|
||||
* 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.examples.federation.properties;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.KeycloakSessionTask;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserFederationProvider;
|
||||
import org.keycloak.models.UserFederationProviderFactory;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserFederationSyncResult;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserProvider;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public abstract class BasePropertiesFederationFactory implements UserFederationProviderFactory {
|
||||
static final Set<String> configOptions = new HashSet<String>();
|
||||
protected ConcurrentHashMap<String, Properties> files = new ConcurrentHashMap<String, Properties>();
|
||||
|
||||
static {
|
||||
configOptions.add("path");
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserFederationProvider getInstance(KeycloakSession session, UserFederationProviderModel model) {
|
||||
// first get the path to our properties file from the stored configuration of this provider instance.
|
||||
String path = model.getConfig().get("path");
|
||||
if (path == null) {
|
||||
throw new IllegalStateException("Path attribute not configured for provider");
|
||||
}
|
||||
// see if we already loaded the config file
|
||||
Properties props = files.get(path);
|
||||
if (props != null) return createProvider(session, model, props);
|
||||
|
||||
|
||||
props = new Properties();
|
||||
InputStream is = getPropertiesFileStream(path);
|
||||
try {
|
||||
props.load(is);
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
// remember the properties file for next time
|
||||
files.put(path, props);
|
||||
return createProvider(session, model, props);
|
||||
}
|
||||
|
||||
protected abstract InputStream getPropertiesFileStream(String path);
|
||||
|
||||
protected abstract BasePropertiesFederationProvider createProvider(KeycloakSession session, UserFederationProviderModel model, Properties props);
|
||||
|
||||
/**
|
||||
* List the configuration options to render and display in the admin console's generic management page for this
|
||||
* plugin
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Set<String> getConfigurationOptions() {
|
||||
return configOptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserFederationProvider create(KeycloakSession session) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* You can import additional plugin configuration from standalone.xml here.
|
||||
*
|
||||
* @param config
|
||||
*/
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserFederationSyncResult syncAllUsers(KeycloakSessionFactory sessionFactory, final String realmId, final UserFederationProviderModel model) {
|
||||
final UserFederationSyncResult syncResult = new UserFederationSyncResult();
|
||||
|
||||
KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
|
||||
|
||||
@Override
|
||||
public void run(KeycloakSession session) {
|
||||
RealmModel realm = session.realms().getRealm(realmId);
|
||||
BasePropertiesFederationProvider federationProvider = (BasePropertiesFederationProvider)getInstance(session, model);
|
||||
Set<String> allUsernames = federationProvider.getProperties().stringPropertyNames();
|
||||
UserProvider localProvider = session.userStorage();
|
||||
for (String username : allUsernames) {
|
||||
UserModel localUser = localProvider.getUserByUsername(username, realm);
|
||||
|
||||
if (localUser == null) {
|
||||
// New user, let's import him
|
||||
UserModel imported = federationProvider.getUserByUsername(realm, username);
|
||||
if (imported != null) {
|
||||
syncResult.increaseAdded();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return syncResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserFederationSyncResult syncChangedUsers(KeycloakSessionFactory sessionFactory, final String realmId, final UserFederationProviderModel model, Date lastSync) {
|
||||
return syncAllUsers(sessionFactory, realmId, model);
|
||||
}
|
||||
}
|
|
@ -1,184 +0,0 @@
|
|||
/*
|
||||
* 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.examples.federation.properties;
|
||||
|
||||
import org.keycloak.credential.CredentialInput;
|
||||
import org.keycloak.models.CredentialValidationOutput;
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserFederationProvider;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public abstract class BasePropertiesFederationProvider implements UserFederationProvider {
|
||||
protected static final Set<String> supportedCredentialTypes = new HashSet<String>();
|
||||
protected KeycloakSession session;
|
||||
protected Properties properties;
|
||||
protected UserFederationProviderModel model;
|
||||
|
||||
public BasePropertiesFederationProvider(KeycloakSession session, UserFederationProviderModel model, Properties properties) {
|
||||
this.session = session;
|
||||
this.model = model;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
static
|
||||
{
|
||||
supportedCredentialTypes.add(UserCredentialModel.PASSWORD);
|
||||
}
|
||||
|
||||
|
||||
public KeycloakSession getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
public Properties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public UserFederationProviderModel getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserByUsername(RealmModel realm, String username) {
|
||||
String password = properties.getProperty(username);
|
||||
if (password != null) {
|
||||
UserModel userModel = session.userStorage().addUser(realm, username);
|
||||
userModel.setEnabled(true);
|
||||
userModel.setFederationLink(model.getId());
|
||||
return userModel;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserByEmail(RealmModel realm, String email) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* We only search for Usernames as that is all that is stored in the properties file. Not that if the user
|
||||
* does exist in the properties file, we only import it if the user hasn't been imported already.
|
||||
*
|
||||
* @param attributes
|
||||
* @param realm
|
||||
* @param maxResults
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public List<UserModel> searchByAttributes(Map<String, String> attributes, RealmModel realm, int maxResults) {
|
||||
String username = attributes.get(USERNAME);
|
||||
if (username != null) {
|
||||
// make sure user isn't already in storage
|
||||
if (session.userStorage().getUserByUsername(username, realm) == null) {
|
||||
// user is not already imported, so let's import it until local storage.
|
||||
UserModel user = getUserByUsername(realm, username);
|
||||
if (user != null) {
|
||||
List<UserModel> list = new ArrayList<UserModel>(1);
|
||||
list.add(user);
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm) {
|
||||
// complete We don't care about the realm being removed
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, RoleModel role) {
|
||||
// complete we dont'care if a role is removed
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, GroupModel group) {
|
||||
// complete we dont'care if a role is removed
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* See if the user is still in the properties file
|
||||
*
|
||||
* @param local
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean isValid(RealmModel realm, UserModel local) {
|
||||
return properties.containsKey(local.getUsername());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getSupportedCredentialTypes() {
|
||||
return supportedCredentialTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(RealmModel realm, UserModel user, CredentialInput input) {
|
||||
if (!supportsCredentialType(input.getType()) || !(input instanceof UserCredentialModel)) return false;
|
||||
|
||||
UserCredentialModel cred = (UserCredentialModel)input;
|
||||
String password = properties.getProperty(user.getUsername());
|
||||
if (password == null) return false;
|
||||
return password.equals(cred.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConfiguredFor(RealmModel realm, UserModel user, String credentialType) {
|
||||
return getSupportedCredentialTypes().contains(credentialType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCredentialType(String credentialType) {
|
||||
return getSupportedCredentialTypes().contains(credentialType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CredentialValidationOutput validCredentials(RealmModel realm, UserCredentialModel credential) {
|
||||
return CredentialValidationOutput.failed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
/*
|
||||
* 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.examples.federation.properties;
|
||||
|
||||
import org.keycloak.credential.CredentialInput;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class FilePropertiesFederationProvider extends BasePropertiesFederationProvider {
|
||||
|
||||
public FilePropertiesFederationProvider(KeycloakSession session, Properties properties, UserFederationProviderModel model) {
|
||||
super(session, model, properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Keycloak will call this method if it finds an imported UserModel. Here we proxy the UserModel with
|
||||
* a Writable proxy which will synchronize updates to username and password back to the properties file
|
||||
*
|
||||
* @param local
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public UserModel validateAndProxy(RealmModel realm, UserModel local) {
|
||||
if (isValid(realm, local)) {
|
||||
return new WritableUserModelProxy(local, this);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adding new users is supported
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean synchronizeRegistrations() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void save() {
|
||||
String path = getModel().getConfig().get("path");
|
||||
try {
|
||||
FileOutputStream fos = new FileOutputStream(path);
|
||||
properties.store(fos, "");
|
||||
fos.close();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the properties file with the new user.
|
||||
*
|
||||
* @param realm
|
||||
* @param user
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public UserModel register(RealmModel realm, UserModel user) {
|
||||
synchronized (properties) {
|
||||
properties.setProperty(user.getUsername(), "");
|
||||
save();
|
||||
}
|
||||
return validateAndProxy(realm, user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeUser(RealmModel realm, UserModel user) {
|
||||
synchronized (properties) {
|
||||
if (properties.remove(user.getUsername()) == null) return false;
|
||||
save();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateCredential(RealmModel realm, UserModel user, CredentialInput input) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableCredentialType(RealmModel realm, UserModel user, String credentialType) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user) {
|
||||
return Collections.EMPTY_SET;
|
||||
}
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
/*
|
||||
* 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.examples.federation.properties;
|
||||
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.utils.UserModelDelegate;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Proxy that will synchronize password updates to the properties file.
|
||||
*
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class WritableUserModelProxy extends UserModelDelegate {
|
||||
protected FilePropertiesFederationProvider provider;
|
||||
|
||||
public WritableUserModelProxy(UserModel delegate, FilePropertiesFederationProvider provider) {
|
||||
super(delegate);
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates the properties file if the username changes. If you have a more complex user storage, you can
|
||||
* override other methods on UserModel to synchronize updates back to your external storage.
|
||||
*
|
||||
* @param username
|
||||
*/
|
||||
@Override
|
||||
public void setUsername(String username) {
|
||||
if (delegate.getUsername().equals(username)) return;
|
||||
delegate.setUsername(username);
|
||||
Properties properties = provider.getProperties();
|
||||
synchronized (properties) {
|
||||
if (properties.containsKey(username)) {
|
||||
throw new IllegalStateException("Can't change username to existing user");
|
||||
}
|
||||
String password = (String) properties.remove(username);
|
||||
if (password == null) {
|
||||
throw new IllegalStateException("User doesn't exist");
|
||||
}
|
||||
properties.setProperty(username, password);
|
||||
provider.save();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
#
|
||||
# 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.examples.federation.properties.ClasspathPropertiesFederationFactory
|
||||
org.keycloak.examples.federation.properties.FilePropertiesFederationFactory
|
|
@ -33,10 +33,10 @@
|
|||
<modules>
|
||||
<module>event-listener-sysout</module>
|
||||
<module>event-store-mem</module>
|
||||
<module>federation-provider</module>
|
||||
<module>authenticator</module>
|
||||
<module>rest</module>
|
||||
<module>domain-extension</module>
|
||||
<module>user-storage-simple</module>
|
||||
<module>user-storage-jpa</module>
|
||||
</modules>
|
||||
</project>
|
||||
|
|
19
examples/providers/user-storage-simple/README.md
Executable file
19
examples/providers/user-storage-simple/README.md
Executable file
|
@ -0,0 +1,19 @@
|
|||
Example User Federation Provider
|
||||
===================================================
|
||||
|
||||
This is an example of user storage backed by a simple properties file. This properties file only contains username/password
|
||||
key pairs. To deploy this provider you must have Keycloak running in standalone or standalone-ha mode. Then type the follow maven command:
|
||||
|
||||
mvn clean install wildfly:deploy
|
||||
|
||||
|
||||
|
||||
The ClasspathPropertiesStorageProvider is an example of a readonly provider. If you go to the Users/Federation
|
||||
page of the admin console you will see this provider listed under "classpath-properties. To configure this provider you
|
||||
specify a classpath to a properties file in the "path" field of the admin page for this plugin. This example includes
|
||||
a "test-users.properties" within the JAR that you can use as the variable.
|
||||
|
||||
The FilePropertiesStorageProvider is an example of a writable provider. It synchronizes changes made to
|
||||
username and password with the properties file. If you go to the Users/Federation page of the admin console you will
|
||||
see this provider listed under "file-properties". To configure this provider you specify a fully qualified file path to
|
||||
a properties file in the "path" field of the admin page for this plugin.
|
|
@ -23,11 +23,11 @@
|
|||
<version>2.4.1.Final-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<name>Properties Authentication Provider Example</name>
|
||||
<name>UserStorageProvider Simple Example</name>
|
||||
<description/>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>federation-properties-example</artifactId>
|
||||
<artifactId>user-storage-properties-example</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
|
@ -41,11 +41,6 @@
|
|||
<artifactId>keycloak-server-spi</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-server-spi-private</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
|
@ -54,6 +49,23 @@
|
|||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>federation-properties-example</finalName>
|
||||
<finalName>user-storage-properties-example</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.wildfly.plugins</groupId>
|
||||
<artifactId>wildfly-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>false</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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.examples.federation.properties;
|
||||
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.storage.UserStorageProviderFactory;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public abstract class BasePropertiesStorageFactory<T extends BasePropertiesStorageProvider> implements UserStorageProviderFactory<T> {
|
||||
protected ConcurrentHashMap<String, Properties> files = new ConcurrentHashMap<String, Properties>();
|
||||
|
||||
|
||||
@Override
|
||||
public T create(KeycloakSession session, ComponentModel model) {
|
||||
// first get the path to our properties file from the stored configuration of this provider instance.
|
||||
String path = model.getConfig().getFirst("path");
|
||||
if (path == null) {
|
||||
throw new IllegalStateException("Path attribute not configured for provider");
|
||||
}
|
||||
// see if we already loaded the config file
|
||||
Properties props = files.get(path);
|
||||
if (props != null) return (T)createProvider(session, new UserStorageProviderModel(model), props);
|
||||
|
||||
|
||||
props = new Properties();
|
||||
InputStream is = getPropertiesFileStream(path);
|
||||
if (is != null) {
|
||||
try {
|
||||
props.load(is);
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
// remember the properties file for next time
|
||||
files.put(path, props);
|
||||
return (T)createProvider(session, new UserStorageProviderModel(model), props);
|
||||
}
|
||||
|
||||
protected abstract InputStream getPropertiesFileStream(String path);
|
||||
|
||||
protected abstract BasePropertiesStorageProvider createProvider(KeycloakSession session, UserStorageProviderModel model, Properties props);
|
||||
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* 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.examples.federation.properties;
|
||||
|
||||
import org.keycloak.credential.CredentialInput;
|
||||
import org.keycloak.credential.CredentialInputValidator;
|
||||
import org.keycloak.credential.CredentialModel;
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.storage.StorageId;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
import org.keycloak.storage.user.UserLookupProvider;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public abstract class BasePropertiesStorageProvider implements
|
||||
UserStorageProvider,
|
||||
UserLookupProvider,
|
||||
CredentialInputValidator
|
||||
{
|
||||
public static final String UNSET_PASSWORD="#$!-UNSET-PASSWORD";
|
||||
|
||||
protected KeycloakSession session;
|
||||
protected Properties properties;
|
||||
protected UserStorageProviderModel model;
|
||||
// map of loaded users in this transaction
|
||||
protected Map<String, UserModel> loadedUsers = new HashMap<>();
|
||||
|
||||
public BasePropertiesStorageProvider(KeycloakSession session, UserStorageProviderModel model, Properties properties) {
|
||||
this.session = session;
|
||||
this.model = model;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
public KeycloakSession getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
public Properties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserByUsername(String username, RealmModel realm) {
|
||||
UserModel adapter = loadedUsers.get(username);
|
||||
if (adapter == null) {
|
||||
String password = properties.getProperty(username);
|
||||
if (password != null) {
|
||||
adapter = createAdapter(realm, username);
|
||||
loadedUsers.put(username, adapter);
|
||||
}
|
||||
}
|
||||
return adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserById(String id, RealmModel realm) {
|
||||
StorageId storageId = new StorageId(id);
|
||||
String username = storageId.getExternalId();
|
||||
return getUserByUsername(username, realm);
|
||||
}
|
||||
|
||||
protected abstract UserModel createAdapter(RealmModel realm, String username);
|
||||
|
||||
@Override
|
||||
public UserModel getUserByEmail(String email, RealmModel realm) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm) {
|
||||
// complete We don't care about the realm being removed
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, RoleModel role) {
|
||||
// complete we dont'care if a role is removed
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, GroupModel group) {
|
||||
// complete we dont'care if a role is removed
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(RealmModel realm, UserModel user, CredentialInput input) {
|
||||
if (!supportsCredentialType(input.getType()) || !(input instanceof UserCredentialModel)) return false;
|
||||
|
||||
UserCredentialModel cred = (UserCredentialModel)input;
|
||||
String password = properties.getProperty(user.getUsername());
|
||||
if (password == null || password.equals(UNSET_PASSWORD)) return false;
|
||||
return password.equals(cred.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConfiguredFor(RealmModel realm, UserModel user, String credentialType) {
|
||||
String password = properties.getProperty(user.getUsername());
|
||||
return credentialType.equals(CredentialModel.PASSWORD) && password != null && !password.equals(UNSET_PASSWORD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCredentialType(String credentialType) {
|
||||
return credentialType.equals(CredentialModel.PASSWORD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
}
|
|
@ -18,22 +18,40 @@
|
|||
package org.keycloak.examples.federation.properties;
|
||||
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.provider.ProviderConfigurationBuilder;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class ClasspathPropertiesFederationFactory extends BasePropertiesFederationFactory {
|
||||
public class ClasspathPropertiesStorageFactory extends BasePropertiesStorageFactory<ClasspathPropertiesStorageProvider> {
|
||||
|
||||
public static final String PROVIDER_NAME = "classpath-properties";
|
||||
protected static final List<ProviderConfigProperty> configProperties;
|
||||
|
||||
static {
|
||||
configProperties = ProviderConfigurationBuilder.create()
|
||||
.property().name("path")
|
||||
.type(ProviderConfigProperty.STRING_TYPE)
|
||||
.label("Classpath")
|
||||
.helpText("Classpath of properties file")
|
||||
.add().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BasePropertiesFederationProvider createProvider(KeycloakSession session, UserFederationProviderModel model, Properties props) {
|
||||
return new ClasspathPropertiesFederationProvider(session, model, props);
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return configProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BasePropertiesStorageProvider createProvider(KeycloakSession session, UserStorageProviderModel model, Properties props) {
|
||||
return new ClasspathPropertiesStorageProvider(session, model, props);
|
||||
}
|
||||
|
||||
protected InputStream getPropertiesFileStream(String path) {
|
|
@ -18,73 +18,45 @@
|
|||
package org.keycloak.examples.federation.properties;
|
||||
|
||||
import org.keycloak.credential.CredentialInput;
|
||||
import org.keycloak.credential.CredentialInputUpdater;
|
||||
import org.keycloak.credential.CredentialModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.storage.ReadOnlyException;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
import org.keycloak.storage.adapter.AbstractUserAdapter;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Creates a read-only user. Implements CredentialInputUpdater so that passwords cannot be updated
|
||||
*
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class ClasspathPropertiesFederationProvider extends BasePropertiesFederationProvider {
|
||||
public class ClasspathPropertiesStorageProvider extends BasePropertiesStorageProvider implements CredentialInputUpdater {
|
||||
|
||||
public ClasspathPropertiesFederationProvider(KeycloakSession session, UserFederationProviderModel model, Properties properties) {
|
||||
public ClasspathPropertiesStorageProvider(KeycloakSession session, UserStorageProviderModel model, Properties properties) {
|
||||
super(session, model, properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Keycloak will call this method if it finds an imported UserModel. Here we proxy the UserModel with
|
||||
* a Readonly proxy which will barf if password is updated.
|
||||
*
|
||||
* @param local
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public UserModel validateAndProxy(RealmModel realm, UserModel local) {
|
||||
if (isValid(realm, local)) {
|
||||
return new ReadonlyUserModelProxy(local);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The properties file is readonly so don't suppport registration.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean synchronizeRegistrations() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The properties file is readonly so don't suppport registration.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public UserModel register(RealmModel realm, UserModel user) {
|
||||
throw new IllegalStateException("Registration not supported");
|
||||
}
|
||||
|
||||
/**
|
||||
* The properties file is readonly so don't removing a user
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean removeUser(RealmModel realm, UserModel user) {
|
||||
throw new IllegalStateException("Remove not supported");
|
||||
protected UserModel createAdapter(RealmModel realm, String username) {
|
||||
return new AbstractUserAdapter(session, realm, model) {
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateCredential(RealmModel realm, UserModel user, CredentialInput input) {
|
||||
if (input.getType().equals(CredentialModel.PASSWORD)) throw new ReadOnlyException("user is read only for this update");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -18,30 +18,48 @@
|
|||
package org.keycloak.examples.federation.properties;
|
||||
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.provider.ProviderConfigurationBuilder;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class FilePropertiesFederationFactory extends BasePropertiesFederationFactory {
|
||||
public class FilePropertiesStorageFactory extends BasePropertiesStorageFactory<FilePropertiesStorageProvider> {
|
||||
|
||||
public static final String PROVIDER_NAME = "file-properties";
|
||||
protected static final List<ProviderConfigProperty> configProperties;
|
||||
|
||||
static {
|
||||
configProperties = ProviderConfigurationBuilder.create()
|
||||
.property().name("path")
|
||||
.type(ProviderConfigProperty.STRING_TYPE)
|
||||
.label("Path")
|
||||
.helpText("File path to properties file")
|
||||
.add().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BasePropertiesFederationProvider createProvider(KeycloakSession session, UserFederationProviderModel model, Properties props) {
|
||||
return new FilePropertiesFederationProvider(session, props, model);
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return configProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BasePropertiesStorageProvider createProvider(KeycloakSession session, UserStorageProviderModel model, Properties props) {
|
||||
return new FilePropertiesStorageProvider(session, props, model);
|
||||
}
|
||||
protected InputStream getPropertiesFileStream(String path) {
|
||||
try {
|
||||
return new FileInputStream(path);
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* 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.examples.federation.properties;
|
||||
|
||||
import org.keycloak.credential.CredentialInput;
|
||||
import org.keycloak.credential.CredentialInputUpdater;
|
||||
import org.keycloak.credential.CredentialModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.storage.ReadOnlyException;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
import org.keycloak.storage.adapter.AbstractUserAdapterFederatedStorage;
|
||||
import org.keycloak.storage.user.UserRegistrationProvider;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class FilePropertiesStorageProvider extends BasePropertiesStorageProvider implements
|
||||
UserRegistrationProvider,
|
||||
CredentialInputUpdater
|
||||
{
|
||||
|
||||
public FilePropertiesStorageProvider(KeycloakSession session, Properties properties, UserStorageProviderModel model) {
|
||||
super(session, model, properties);
|
||||
}
|
||||
|
||||
public void save() {
|
||||
String path = model.getConfig().getFirst("path");
|
||||
try {
|
||||
FileOutputStream fos = new FileOutputStream(path);
|
||||
properties.store(fos, "");
|
||||
fos.close();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeUser(RealmModel realm, UserModel user) {
|
||||
synchronized (properties) {
|
||||
if (properties.remove(user.getUsername()) == null) return false;
|
||||
save();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UserModel createAdapter(RealmModel realm, String username) {
|
||||
return new AbstractUserAdapterFederatedStorage(session, realm, model) {
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUsername(String username) {
|
||||
throw new ReadOnlyException("Cannot edit username");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel addUser(RealmModel realm, String username) {
|
||||
synchronized (properties) {
|
||||
properties.setProperty(username, UNSET_PASSWORD);
|
||||
save();
|
||||
}
|
||||
return createAdapter(realm, username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void grantToAllUsers(RealmModel realm, RoleModel role) {
|
||||
// unsupported
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateCredential(RealmModel realm, UserModel user, CredentialInput input) {
|
||||
if (!(input instanceof UserCredentialModel)) return false;
|
||||
UserCredentialModel cred = (UserCredentialModel)input;
|
||||
if (!cred.getType().equals(CredentialModel.PASSWORD)) return false;
|
||||
synchronized (properties) {
|
||||
properties.setProperty(user.getUsername(), cred.getValue());
|
||||
save();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableCredentialType(RealmModel realm, UserModel user, String credentialType) {
|
||||
if (!credentialType.equals(CredentialModel.PASSWORD)) return;
|
||||
synchronized (properties) {
|
||||
properties.setProperty(user.getUsername(), UNSET_PASSWORD);
|
||||
save();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final Set<String> disableableTypes = new HashSet<>();
|
||||
|
||||
static {
|
||||
disableableTypes.add(CredentialModel.PASSWORD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user) {
|
||||
|
||||
return disableableTypes;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
org.keycloak.examples.federation.properties.ClasspathPropertiesStorageFactory
|
||||
org.keycloak.examples.federation.properties.FilePropertiesStorageFactory
|
|
@ -20,9 +20,6 @@ package org.keycloak.federation.kerberos;
|
|||
import org.keycloak.common.constants.KerberosConstants;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.models.LDAPConstants;
|
||||
import org.keycloak.models.UserFederationProvider;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.storage.UserStorageProvider.EditMode;
|
||||
|
||||
/**
|
||||
|
|
|
@ -33,8 +33,6 @@ import org.keycloak.models.ModelReadOnlyException;
|
|||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserFederationProvider;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserManager;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
|
@ -44,8 +42,6 @@ import org.keycloak.storage.user.UserLookupProvider;
|
|||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
|
|
@ -21,28 +21,19 @@ import org.jboss.logging.Logger;
|
|||
import org.keycloak.Config;
|
||||
import org.keycloak.common.constants.KerberosConstants;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.component.ComponentValidationException;
|
||||
import org.keycloak.federation.kerberos.impl.KerberosServerSubjectAuthenticator;
|
||||
import org.keycloak.federation.kerberos.impl.KerberosUsernamePasswordAuthenticator;
|
||||
import org.keycloak.federation.kerberos.impl.SPNEGOAuthenticator;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.LDAPConstants;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserFederationProvider;
|
||||
import org.keycloak.models.UserFederationProviderFactory;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserFederationSyncResult;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.provider.ProviderConfigurationBuilder;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.storage.UserStorageProviderFactory;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Factory for standalone Kerberos federation provider. Standalone means that it's not backed by LDAP. For Kerberos backed by LDAP (like MS AD or ApacheDS environment)
|
||||
|
|
|
@ -596,7 +596,7 @@ public class LDAPStorageProvider implements UserStorageProvider,
|
|||
logger.warnf("User with username [%s] aready exists and is linked to provider [%s] but is not valid. Stale LDAP_ID on local user is: %s",
|
||||
username, model.getName(), user.getFirstAttribute(LDAPConstants.LDAP_ID));
|
||||
logger.warn("Will re-create user");
|
||||
session.getUserCache().evict(realm, user);
|
||||
session.userCache().evict(realm, user);
|
||||
new UserManager(session).removeUser(realm, user, session.userLocalStorage());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -495,7 +495,7 @@ public class LDAPStorageProviderFactory implements UserStorageProviderFactory<LD
|
|||
if (username != null) {
|
||||
UserModel existing = session.userLocalStorage().getUserByUsername(username, currentRealm);
|
||||
if (existing != null) {
|
||||
session.getUserCache().evict(currentRealm, existing);
|
||||
session.userCache().evict(currentRealm, existing);
|
||||
session.userLocalStorage().removeUser(currentRealm, existing);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ package org.keycloak.storage.ldap;
|
|||
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.component.ComponentValidationException;
|
||||
import org.keycloak.mappers.FederationConfigValidationException;
|
||||
import org.keycloak.models.LDAPConstants;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
@ -268,7 +267,7 @@ public class LDAPUtils {
|
|||
* Validate configured customFilter matches the requested format
|
||||
*
|
||||
* @param customFilter
|
||||
* @throws FederationConfigValidationException
|
||||
* @throws ComponentValidationException
|
||||
*/
|
||||
public static void validateCustomLdapFilter(String customFilter) throws ComponentValidationException {
|
||||
if (customFilter != null) {
|
||||
|
|
|
@ -24,15 +24,11 @@ import org.keycloak.credential.CredentialInputValidator;
|
|||
import org.keycloak.credential.CredentialModel;
|
||||
import org.keycloak.federation.sssd.api.Sssd;
|
||||
import org.keycloak.federation.sssd.impl.PAMAuthenticator;
|
||||
import org.keycloak.models.CredentialValidationOutput;
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ModelReadOnlyException;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserFederationProvider;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.models.UserManager;
|
||||
|
@ -40,9 +36,7 @@ import org.keycloak.storage.UserStorageProvider;
|
|||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
import org.keycloak.storage.user.UserLookupProvider;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
|
|
@ -24,18 +24,10 @@ import org.keycloak.federation.sssd.api.Sssd;
|
|||
import org.keycloak.federation.sssd.impl.PAMAuthenticator;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.UserFederationProvider;
|
||||
import org.keycloak.models.UserFederationProviderFactory;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserFederationSyncResult;
|
||||
import org.keycloak.provider.EnvironmentDependentProviderFactory;
|
||||
import org.keycloak.storage.UserStorageProviderFactory;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bruno@abstractj.org">Bruno Oliveira</a>
|
||||
* @version $Revision: 1 $
|
||||
|
|
|
@ -172,9 +172,6 @@ public interface RealmResource {
|
|||
@Path("attack-detection")
|
||||
AttackDetectionResource attackDetection();
|
||||
|
||||
@Path("user-federation")
|
||||
UserFederationProvidersResource userFederation();
|
||||
|
||||
@Path("testLDAPConnection")
|
||||
@GET
|
||||
@NoCache
|
||||
|
|
|
@ -35,8 +35,6 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.RequiredActionProviderModel;
|
||||
import org.keycloak.models.RequiredCredentialModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserFederationMapperModel;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.cache.CachedRealmModel;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedRealm;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
|
@ -634,38 +632,6 @@ public class RealmAdapter implements CachedRealmModel {
|
|||
updated.removeIdentityProviderByAlias(alias);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserFederationProviderModel> getUserFederationProviders() {
|
||||
if (isUpdated()) return updated.getUserFederationProviders();
|
||||
return cached.getUserFederationProviders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUserFederationProviders(List<UserFederationProviderModel> providers) {
|
||||
getDelegateForUpdate();
|
||||
updated.setUserFederationProviders(providers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserFederationProviderModel addUserFederationProvider(String providerName, Map<String, String> config, int priority, String displayName, int fullSyncPeriod, int changedSyncPeriod, int lastSync) {
|
||||
getDelegateForUpdate();
|
||||
return updated.addUserFederationProvider(providerName, config, priority, displayName, fullSyncPeriod, changedSyncPeriod, lastSync);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUserFederationProvider(UserFederationProviderModel provider) {
|
||||
getDelegateForUpdate();
|
||||
updated.removeUserFederationProvider(provider);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUserFederationProvider(UserFederationProviderModel provider) {
|
||||
getDelegateForUpdate();
|
||||
updated.updateUserFederationProvider(provider);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLoginTheme() {
|
||||
if (isUpdated()) return updated.getLoginTheme();
|
||||
|
@ -952,63 +918,6 @@ public class RealmAdapter implements CachedRealmModel {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<UserFederationMapperModel> getUserFederationMappers() {
|
||||
if (isUpdated()) return updated.getUserFederationMappers();
|
||||
return cached.getUserFederationMapperSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<UserFederationMapperModel> getUserFederationMappersByFederationProvider(String federationProviderId) {
|
||||
if (isUpdated()) return updated.getUserFederationMappersByFederationProvider(federationProviderId);
|
||||
Set<UserFederationMapperModel> mappers = new HashSet<>();
|
||||
List<UserFederationMapperModel> list = cached.getUserFederationMappers().getList(federationProviderId);
|
||||
for (UserFederationMapperModel entity : list) {
|
||||
mappers.add(entity);
|
||||
}
|
||||
return Collections.unmodifiableSet(mappers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserFederationMapperModel addUserFederationMapper(UserFederationMapperModel mapper) {
|
||||
getDelegateForUpdate();
|
||||
return updated.addUserFederationMapper(mapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUserFederationMapper(UserFederationMapperModel mapper) {
|
||||
getDelegateForUpdate();
|
||||
updated.removeUserFederationMapper(mapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUserFederationMapper(UserFederationMapperModel mapper) {
|
||||
getDelegateForUpdate();
|
||||
updated.updateUserFederationMapper(mapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserFederationMapperModel getUserFederationMapperById(String id) {
|
||||
if (isUpdated()) return updated.getUserFederationMapperById(id);
|
||||
for (List<UserFederationMapperModel> models : cached.getUserFederationMappers().values()) {
|
||||
for (UserFederationMapperModel model : models) {
|
||||
if (model.getId().equals(id)) return model;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserFederationMapperModel getUserFederationMapperByName(String federationProviderId, String name) {
|
||||
if (isUpdated()) return updated.getUserFederationMapperByName(federationProviderId, name);
|
||||
List<UserFederationMapperModel> models = cached.getUserFederationMappers().getList(federationProviderId);
|
||||
if (models == null) return null;
|
||||
for (UserFederationMapperModel model : models) {
|
||||
if (model.getName().equals(name)) return model;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthenticationFlowModel getBrowserFlow() {
|
||||
if (isUpdated()) return updated.getBrowserFlow();
|
||||
|
@ -1333,7 +1242,7 @@ public class RealmAdapter implements CachedRealmModel {
|
|||
if (parentId != null && !parentId.equals(getId())) {
|
||||
ComponentModel parent = getComponent(parentId);
|
||||
if (parent != null && UserStorageProvider.class.getName().equals(parent.getProviderType())) {
|
||||
session.getUserCache().evict(this);
|
||||
session.userCache().evict(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,6 @@ import org.keycloak.models.ProtocolMapperModel;
|
|||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserConsentModel;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserProvider;
|
||||
import org.keycloak.models.cache.CachedUserModel;
|
||||
|
@ -53,9 +52,7 @@ import org.keycloak.storage.StorageId;
|
|||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
|
@ -300,7 +297,7 @@ public class UserCacheSession implements UserCache {
|
|||
// its also hard to test stuff
|
||||
boolean invalidate = false;
|
||||
if (policy != null) {
|
||||
String currentTime = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(new Date(Time.currentTimeMillis()));
|
||||
//String currentTime = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(new Date(Time.currentTimeMillis()));
|
||||
if (policy == UserStorageProviderModel.CachePolicy.NO_CACHE) {
|
||||
invalidate = true;
|
||||
} else if (cached.getCacheTimestamp() < model.getCacheInvalidBefore()) {
|
||||
|
@ -317,8 +314,8 @@ public class UserCacheSession implements UserCache {
|
|||
int oneWeek = 7 * 24 * 60 * 60 * 1000;
|
||||
long weeklyTimeout = weeklyTimeout(model.getEvictionDay(), model.getEvictionHour(), model.getEvictionMinute());
|
||||
long lastTimeout = weeklyTimeout - oneWeek;
|
||||
String timeout = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(new Date(weeklyTimeout));
|
||||
String stamp = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(new Date(cached.getCacheTimestamp()));
|
||||
//String timeout = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(new Date(weeklyTimeout));
|
||||
//String stamp = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(new Date(cached.getCacheTimestamp()));
|
||||
if (cached.getCacheTimestamp() <= lastTimeout) {
|
||||
invalidate = true;
|
||||
}
|
||||
|
@ -852,12 +849,6 @@ public class UserCacheSession implements UserCache {
|
|||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, UserFederationProviderModel link) {
|
||||
addRealmInvalidation(realm.getId()); // easier to just invalidate whole realm
|
||||
getDelegate().preRemove(realm, link);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, ClientModel client) {
|
||||
addRealmInvalidation(realm.getId()); // easier to just invalidate whole realm
|
||||
|
|
|
@ -33,8 +33,6 @@ import org.keycloak.models.PasswordPolicy;
|
|||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RequiredActionProviderModel;
|
||||
import org.keycloak.models.RequiredCredentialModel;
|
||||
import org.keycloak.models.UserFederationMapperModel;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
|
@ -96,12 +94,9 @@ public class CachedRealm extends AbstractExtendableRevisioned {
|
|||
protected String masterAdminClient;
|
||||
|
||||
protected List<RequiredCredentialModel> requiredCredentials;
|
||||
protected List<UserFederationProviderModel> userFederationProviders;
|
||||
protected MultivaluedHashMap<String, ComponentModel> componentsByParent = new MultivaluedHashMap<>();
|
||||
protected MultivaluedHashMap<String, ComponentModel> componentsByParentAndType = new MultivaluedHashMap<>();
|
||||
protected Map<String, ComponentModel> components = new HashMap<>();
|
||||
protected MultivaluedHashMap<String, UserFederationMapperModel> userFederationMappers = new MultivaluedHashMap<String, UserFederationMapperModel>();
|
||||
protected Set<UserFederationMapperModel> userFederationMapperSet;
|
||||
protected List<IdentityProviderModel> identityProviders;
|
||||
|
||||
protected Map<String, String> browserSecurityHeaders;
|
||||
|
@ -187,11 +182,6 @@ public class CachedRealm extends AbstractExtendableRevisioned {
|
|||
emailTheme = model.getEmailTheme();
|
||||
|
||||
requiredCredentials = model.getRequiredCredentials();
|
||||
userFederationProviders = model.getUserFederationProviders();
|
||||
userFederationMapperSet = model.getUserFederationMappers();
|
||||
for (UserFederationMapperModel mapper : userFederationMapperSet) {
|
||||
this.userFederationMappers.add(mapper.getFederationProviderId(), mapper);
|
||||
}
|
||||
|
||||
this.identityProviders = new ArrayList<>();
|
||||
|
||||
|
@ -462,14 +452,6 @@ public class CachedRealm extends AbstractExtendableRevisioned {
|
|||
return adminEventsDetailsEnabled;
|
||||
}
|
||||
|
||||
public List<UserFederationProviderModel> getUserFederationProviders() {
|
||||
return userFederationProviders;
|
||||
}
|
||||
|
||||
public MultivaluedHashMap<String, UserFederationMapperModel> getUserFederationMappers() {
|
||||
return userFederationMappers;
|
||||
}
|
||||
|
||||
public List<IdentityProviderModel> getIdentityProviders() {
|
||||
return identityProviders;
|
||||
}
|
||||
|
@ -546,10 +528,6 @@ public class CachedRealm extends AbstractExtendableRevisioned {
|
|||
return clientTemplates;
|
||||
}
|
||||
|
||||
public Set<UserFederationMapperModel> getUserFederationMapperSet() {
|
||||
return userFederationMapperSet;
|
||||
}
|
||||
|
||||
public List<AuthenticationFlowModel> getAuthenticationFlowList() {
|
||||
return authenticationFlowList;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ import org.keycloak.component.ComponentModel;
|
|||
import org.keycloak.credential.CredentialModel;
|
||||
import org.keycloak.credential.UserCredentialStore;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.CredentialValidationOutput;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
@ -34,8 +33,6 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.RequiredActionProviderModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserConsentModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserProvider;
|
||||
import org.keycloak.models.jpa.entities.CredentialAttributeEntity;
|
||||
|
@ -383,12 +380,6 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
|
|||
.setParameter("realmId", realm.getId()).executeUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, UserFederationProviderModel link) {
|
||||
String linkId = link.getId();
|
||||
removeUserDataByLink(realm, linkId);
|
||||
}
|
||||
|
||||
public void removeUserDataByLink(RealmModel realm, String linkId) {
|
||||
int num = em.createNamedQuery("deleteUserRoleMappingsByRealmAndLink")
|
||||
.setParameter("realmId", realm.getId())
|
||||
|
|
|
@ -31,7 +31,6 @@ import org.keycloak.models.GroupModel;
|
|||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ModelDuplicateException;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.OTPPolicy;
|
||||
import org.keycloak.models.PasswordPolicy;
|
||||
|
@ -39,9 +38,6 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.RequiredActionProviderModel;
|
||||
import org.keycloak.models.RequiredCredentialModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserFederationMapperModel;
|
||||
import org.keycloak.models.UserFederationProviderCreationEventImpl;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.jpa.entities.AuthenticationExecutionEntity;
|
||||
import org.keycloak.models.jpa.entities.AuthenticationFlowEntity;
|
||||
import org.keycloak.models.jpa.entities.AuthenticatorConfigEntity;
|
||||
|
@ -58,8 +54,6 @@ import org.keycloak.models.jpa.entities.RealmEntity;
|
|||
import org.keycloak.models.jpa.entities.RequiredActionProviderEntity;
|
||||
import org.keycloak.models.jpa.entities.RequiredCredentialEntity;
|
||||
import org.keycloak.models.jpa.entities.RoleEntity;
|
||||
import org.keycloak.models.jpa.entities.UserFederationMapperEntity;
|
||||
import org.keycloak.models.jpa.entities.UserFederationProviderEntity;
|
||||
import org.keycloak.models.utils.ComponentUtil;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
|
||||
|
@ -68,7 +62,6 @@ import javax.persistence.TypedQuery;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
|
@ -734,194 +727,6 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
|
|||
}
|
||||
|
||||
|
||||
private void removeFederationMappersForProvider(String federationProviderId) {
|
||||
Set<UserFederationMapperEntity> mappers = getUserFederationMapperEntitiesByFederationProvider(federationProviderId);
|
||||
for (UserFederationMapperEntity mapper : mappers) {
|
||||
realm.getUserFederationMappers().remove(mapper);
|
||||
em.remove(mapper);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserFederationProviderModel> getUserFederationProviders() {
|
||||
List<UserFederationProviderEntity> entities = realm.getUserFederationProviders();
|
||||
if (entities.isEmpty()) return Collections.EMPTY_LIST;
|
||||
List<UserFederationProviderEntity> copy = new ArrayList<UserFederationProviderEntity>();
|
||||
for (UserFederationProviderEntity entity : entities) {
|
||||
copy.add(entity);
|
||||
|
||||
}
|
||||
Collections.sort(copy, new Comparator<UserFederationProviderEntity>() {
|
||||
|
||||
@Override
|
||||
public int compare(UserFederationProviderEntity o1, UserFederationProviderEntity o2) {
|
||||
return o1.getPriority() - o2.getPriority();
|
||||
}
|
||||
|
||||
});
|
||||
List<UserFederationProviderModel> result = new ArrayList<UserFederationProviderModel>();
|
||||
for (UserFederationProviderEntity entity : copy) {
|
||||
result.add(new UserFederationProviderModel(entity.getId(), entity.getProviderName(), entity.getConfig(), entity.getPriority(), entity.getDisplayName(),
|
||||
entity.getFullSyncPeriod(), entity.getChangedSyncPeriod(), entity.getLastSync()));
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserFederationProviderModel addUserFederationProvider(String providerName, Map<String, String> config, int priority, String displayName, int fullSyncPeriod, int changedSyncPeriod, int lastSync) {
|
||||
KeycloakModelUtils.ensureUniqueDisplayName(displayName, null, getUserFederationProviders());
|
||||
|
||||
String id = KeycloakModelUtils.generateId();
|
||||
UserFederationProviderEntity entity = new UserFederationProviderEntity();
|
||||
entity.setId(id);
|
||||
entity.setRealm(realm);
|
||||
entity.setProviderName(providerName);
|
||||
entity.setConfig(config);
|
||||
entity.setPriority(priority);
|
||||
if (displayName == null) {
|
||||
displayName = id;
|
||||
}
|
||||
entity.setDisplayName(displayName);
|
||||
entity.setFullSyncPeriod(fullSyncPeriod);
|
||||
entity.setChangedSyncPeriod(changedSyncPeriod);
|
||||
entity.setLastSync(lastSync);
|
||||
em.persist(entity);
|
||||
realm.getUserFederationProviders().add(entity);
|
||||
em.flush();
|
||||
UserFederationProviderModel providerModel = new UserFederationProviderModel(entity.getId(), providerName, config, priority, displayName, fullSyncPeriod, changedSyncPeriod, lastSync);
|
||||
|
||||
session.getKeycloakSessionFactory().publish(new UserFederationProviderCreationEventImpl(this, providerModel));
|
||||
|
||||
return providerModel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUserFederationProvider(UserFederationProviderModel provider) {
|
||||
Iterator<UserFederationProviderEntity> it = realm.getUserFederationProviders().iterator();
|
||||
while (it.hasNext()) {
|
||||
UserFederationProviderEntity entity = it.next();
|
||||
if (entity.getId().equals(provider.getId())) {
|
||||
|
||||
session.users().preRemove(this, provider);
|
||||
removeFederationMappersForProvider(provider.getId());
|
||||
|
||||
it.remove();
|
||||
em.remove(entity);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void updateUserFederationProvider(UserFederationProviderModel model) {
|
||||
KeycloakModelUtils.ensureUniqueDisplayName(model.getDisplayName(), model, getUserFederationProviders());
|
||||
|
||||
Iterator<UserFederationProviderEntity> it = realm.getUserFederationProviders().iterator();
|
||||
while (it.hasNext()) {
|
||||
UserFederationProviderEntity entity = it.next();
|
||||
if (entity.getId().equals(model.getId())) {
|
||||
String displayName = model.getDisplayName();
|
||||
if (displayName != null) {
|
||||
entity.setDisplayName(model.getDisplayName());
|
||||
}
|
||||
entity.setConfig(model.getConfig());
|
||||
entity.setPriority(model.getPriority());
|
||||
entity.setProviderName(model.getProviderName());
|
||||
entity.setPriority(model.getPriority());
|
||||
entity.setFullSyncPeriod(model.getFullSyncPeriod());
|
||||
entity.setChangedSyncPeriod(model.getChangedSyncPeriod());
|
||||
entity.setLastSync(model.getLastSync());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUserFederationProviders(List<UserFederationProviderModel> providers) {
|
||||
for (UserFederationProviderModel currentProvider : providers) {
|
||||
KeycloakModelUtils.ensureUniqueDisplayName(currentProvider.getDisplayName(), currentProvider, providers);
|
||||
}
|
||||
|
||||
Iterator<UserFederationProviderEntity> it = realm.getUserFederationProviders().iterator();
|
||||
while (it.hasNext()) {
|
||||
UserFederationProviderEntity entity = it.next();
|
||||
boolean found = false;
|
||||
for (UserFederationProviderModel model : providers) {
|
||||
if (entity.getId().equals(model.getId())) {
|
||||
entity.setConfig(model.getConfig());
|
||||
entity.setPriority(model.getPriority());
|
||||
entity.setProviderName(model.getProviderName());
|
||||
String displayName = model.getDisplayName();
|
||||
if (displayName != null) {
|
||||
entity.setDisplayName(displayName);
|
||||
}
|
||||
entity.setFullSyncPeriod(model.getFullSyncPeriod());
|
||||
entity.setChangedSyncPeriod(model.getChangedSyncPeriod());
|
||||
entity.setLastSync(model.getLastSync());
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
if (found) continue;
|
||||
session.users().preRemove(this, new UserFederationProviderModel(entity.getId(), entity.getProviderName(), entity.getConfig(), entity.getPriority(), entity.getDisplayName(),
|
||||
entity.getFullSyncPeriod(), entity.getChangedSyncPeriod(), entity.getLastSync()));
|
||||
removeFederationMappersForProvider(entity.getId());
|
||||
|
||||
it.remove();
|
||||
em.remove(entity);
|
||||
}
|
||||
|
||||
List<UserFederationProviderModel> add = new LinkedList<>();
|
||||
for (UserFederationProviderModel model : providers) {
|
||||
boolean found = false;
|
||||
for (UserFederationProviderEntity entity : realm.getUserFederationProviders()) {
|
||||
if (entity.getId().equals(model.getId())) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) add.add(model);
|
||||
}
|
||||
|
||||
for (UserFederationProviderModel model : add) {
|
||||
UserFederationProviderEntity entity = new UserFederationProviderEntity();
|
||||
if (model.getId() != null) {
|
||||
entity.setId(model.getId());
|
||||
} else {
|
||||
String id = KeycloakModelUtils.generateId();
|
||||
entity.setId(id);
|
||||
model.setId(id);
|
||||
}
|
||||
entity.setConfig(model.getConfig());
|
||||
entity.setPriority(model.getPriority());
|
||||
entity.setProviderName(model.getProviderName());
|
||||
entity.setPriority(model.getPriority());
|
||||
String displayName = model.getDisplayName();
|
||||
if (displayName == null) {
|
||||
displayName = entity.getId();
|
||||
}
|
||||
entity.setDisplayName(displayName);
|
||||
entity.setFullSyncPeriod(model.getFullSyncPeriod());
|
||||
entity.setChangedSyncPeriod(model.getChangedSyncPeriod());
|
||||
entity.setLastSync(model.getLastSync());
|
||||
entity.setRealm(realm);
|
||||
em.persist(entity);
|
||||
realm.getUserFederationProviders().add(entity);
|
||||
|
||||
session.getKeycloakSessionFactory().publish(new UserFederationProviderCreationEventImpl(this, model));
|
||||
}
|
||||
}
|
||||
|
||||
protected UserFederationProviderEntity getUserFederationProviderEntityById(String federationProviderId) {
|
||||
for (UserFederationProviderEntity entity : realm.getUserFederationProviders()) {
|
||||
if (entity.getId().equals(federationProviderId)) {
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel getRole(String name) {
|
||||
return session.realms().getRealmRole(this, name);
|
||||
|
@ -1402,130 +1207,6 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
|
|||
return mapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<UserFederationMapperModel> getUserFederationMappers() {
|
||||
Collection<UserFederationMapperEntity> entities = this.realm.getUserFederationMappers();
|
||||
if (entities.isEmpty()) return Collections.EMPTY_SET;
|
||||
Set<UserFederationMapperModel> mappers = new HashSet<>();
|
||||
for (UserFederationMapperEntity entity : entities) {
|
||||
UserFederationMapperModel mapper = entityToModel(entity);
|
||||
mappers.add(mapper);
|
||||
}
|
||||
return Collections.unmodifiableSet(mappers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<UserFederationMapperModel> getUserFederationMappersByFederationProvider(String federationProviderId) {
|
||||
Set<UserFederationMapperEntity> mapperEntities = getUserFederationMapperEntitiesByFederationProvider(federationProviderId);
|
||||
if (mapperEntities.isEmpty()) return Collections.EMPTY_SET;
|
||||
Set<UserFederationMapperModel> mappers = new HashSet<UserFederationMapperModel>();
|
||||
for (UserFederationMapperEntity entity : mapperEntities) {
|
||||
UserFederationMapperModel mapper = entityToModel(entity);
|
||||
mappers.add(mapper);
|
||||
}
|
||||
return Collections.unmodifiableSet(mappers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserFederationMapperModel addUserFederationMapper(UserFederationMapperModel model) {
|
||||
if (getUserFederationMapperByName(model.getFederationProviderId(), model.getName()) != null) {
|
||||
throw new ModelDuplicateException("User federation mapper must be unique per federation provider. There is already: " + model.getName());
|
||||
}
|
||||
String id = KeycloakModelUtils.generateId();
|
||||
UserFederationMapperEntity entity = new UserFederationMapperEntity();
|
||||
entity.setId(id);
|
||||
entity.setName(model.getName());
|
||||
entity.setFederationProvider(getUserFederationProviderEntityById(model.getFederationProviderId()));
|
||||
entity.setFederationMapperType(model.getFederationMapperType());
|
||||
entity.setRealm(this.realm);
|
||||
entity.setConfig(model.getConfig());
|
||||
|
||||
em.persist(entity);
|
||||
this.realm.getUserFederationMappers().add(entity);
|
||||
UserFederationMapperModel mapperModel = entityToModel(entity);
|
||||
|
||||
return mapperModel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUserFederationMapper(UserFederationMapperModel mapper) {
|
||||
UserFederationMapperEntity toDelete = getUserFederationMapperEntity(mapper.getId());
|
||||
if (toDelete != null) {
|
||||
this.realm.getUserFederationMappers().remove(toDelete);
|
||||
em.remove(toDelete);
|
||||
}
|
||||
}
|
||||
|
||||
protected UserFederationMapperEntity getUserFederationMapperEntity(String id) {
|
||||
for (UserFederationMapperEntity entity : this.realm.getUserFederationMappers()) {
|
||||
if (entity.getId().equals(id)) {
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
protected UserFederationMapperEntity getUserFederationMapperEntityByName(String federationProviderId, String name) {
|
||||
for (UserFederationMapperEntity entity : this.realm.getUserFederationMappers()) {
|
||||
if (federationProviderId.equals(entity.getFederationProvider().getId()) && entity.getName().equals(name)) {
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
protected Set<UserFederationMapperEntity> getUserFederationMapperEntitiesByFederationProvider(String federationProviderId) {
|
||||
Set<UserFederationMapperEntity> mappers = new HashSet<UserFederationMapperEntity>();
|
||||
for (UserFederationMapperEntity entity : this.realm.getUserFederationMappers()) {
|
||||
if (federationProviderId.equals(entity.getFederationProvider().getId())) {
|
||||
mappers.add(entity);
|
||||
}
|
||||
}
|
||||
return mappers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUserFederationMapper(UserFederationMapperModel mapper) {
|
||||
UserFederationMapperEntity entity = getUserFederationMapperEntity(mapper.getId());
|
||||
entity.setFederationProvider(getUserFederationProviderEntityById(mapper.getFederationProviderId()));
|
||||
entity.setFederationMapperType(mapper.getFederationMapperType());
|
||||
if (entity.getConfig() == null) {
|
||||
entity.setConfig(mapper.getConfig());
|
||||
} else {
|
||||
entity.getConfig().clear();
|
||||
entity.getConfig().putAll(mapper.getConfig());
|
||||
}
|
||||
em.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserFederationMapperModel getUserFederationMapperById(String id) {
|
||||
UserFederationMapperEntity entity = getUserFederationMapperEntity(id);
|
||||
if (entity == null) return null;
|
||||
return entityToModel(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserFederationMapperModel getUserFederationMapperByName(String federationProviderId, String name) {
|
||||
UserFederationMapperEntity entity = getUserFederationMapperEntityByName(federationProviderId, name);
|
||||
if (entity == null) return null;
|
||||
return entityToModel(entity);
|
||||
}
|
||||
|
||||
protected UserFederationMapperModel entityToModel(UserFederationMapperEntity entity) {
|
||||
UserFederationMapperModel mapper = new UserFederationMapperModel();
|
||||
mapper.setId(entity.getId());
|
||||
mapper.setName(entity.getName());
|
||||
mapper.setFederationProviderId(entity.getFederationProvider().getId());
|
||||
mapper.setFederationMapperType(entity.getFederationMapperType());
|
||||
Map<String, String> config = new HashMap<String, String>();
|
||||
if (entity.getConfig() != null) config.putAll(entity.getConfig());
|
||||
mapper.setConfig(config);
|
||||
return mapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthenticationFlowModel getBrowserFlow() {
|
||||
String flowId = realm.getBrowserFlow();
|
||||
|
|
|
@ -30,18 +30,11 @@ import org.keycloak.models.ProtocolMapperModel;
|
|||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserConsentModel;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.storage.StorageId;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.storage.federated.UserAttributeFederatedStorage;
|
||||
import org.keycloak.storage.federated.UserBrokerLinkFederatedStorage;
|
||||
import org.keycloak.storage.federated.UserConsentFederatedStorage;
|
||||
import org.keycloak.storage.federated.UserFederatedStorageProvider;
|
||||
import org.keycloak.storage.federated.UserGroupMembershipFederatedStorage;
|
||||
import org.keycloak.storage.federated.UserRequiredActionsFederatedStorage;
|
||||
import org.keycloak.storage.federated.UserRoleMappingsFederatedStorage;
|
||||
import org.keycloak.storage.jpa.entity.BrokerLinkEntity;
|
||||
import org.keycloak.storage.jpa.entity.FederatedUser;
|
||||
import org.keycloak.storage.jpa.entity.FederatedUserAttributeEntity;
|
||||
|
@ -794,38 +787,6 @@ public class JpaUserFederatedStorageProvider implements
|
|||
.setParameter("realmId", realm.getId()).executeUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, UserFederationProviderModel link) {
|
||||
int num = em.createNamedQuery("deleteFederatedUserRoleMappingsByRealmAndLink")
|
||||
.setParameter("realmId", realm.getId())
|
||||
.setParameter("link", link.getId())
|
||||
.executeUpdate();
|
||||
num = em.createNamedQuery("deleteFederatedUserRequiredActionsByRealmAndLink")
|
||||
.setParameter("realmId", realm.getId())
|
||||
.setParameter("link", link.getId())
|
||||
.executeUpdate();
|
||||
num = em.createNamedQuery("deleteBrokerLinkByRealmAndLink")
|
||||
.setParameter("realmId", realm.getId())
|
||||
.setParameter("link", link.getId())
|
||||
.executeUpdate();
|
||||
num = em.createNamedQuery("deleteFederatedCredentialAttributeByRealmAndLink")
|
||||
.setParameter("realmId", realm.getId())
|
||||
.setParameter("link", link.getId())
|
||||
.executeUpdate();
|
||||
num = em.createNamedQuery("deleteFederatedUserCredentialsByRealmAndLink")
|
||||
.setParameter("realmId", realm.getId())
|
||||
.setParameter("link", link.getId())
|
||||
.executeUpdate();
|
||||
num = em.createNamedQuery("deleteUserFederatedAttributesByRealmAndLink")
|
||||
.setParameter("realmId", realm.getId())
|
||||
.setParameter("link", link.getId())
|
||||
.executeUpdate();
|
||||
num = em.createNamedQuery("deleteFederatedUsersByRealmAndLink")
|
||||
.setParameter("realmId", realm.getId())
|
||||
.setParameter("link", link.getId())
|
||||
.executeUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, RoleModel role) {
|
||||
em.createNamedQuery("deleteFederatedUserRoleMappingsByRole").setParameter("roleId", role.getId()).executeUpdate();
|
||||
|
|
|
@ -28,7 +28,6 @@ import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
|||
import org.keycloak.credential.CredentialModel;
|
||||
import org.keycloak.credential.UserCredentialStore;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.CredentialValidationOutput;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
@ -39,8 +38,6 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.RequiredActionProviderModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserConsentModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserManager;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserProvider;
|
||||
|
@ -457,17 +454,6 @@ public class MongoUserProvider implements UserProvider, UserCredentialStore {
|
|||
getMongoStore().removeEntities(MongoUserEntity.class, query, true, invocationContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, UserFederationProviderModel link) {
|
||||
// Remove all users linked with federationProvider and their consents
|
||||
DBObject query = new QueryBuilder()
|
||||
.and("realmId").is(realm.getId())
|
||||
.and("federationLink").is(link.getId())
|
||||
.get();
|
||||
getMongoStore().removeEntities(MongoUserEntity.class, query, true, invocationContext);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, ClientModel client) {
|
||||
// Remove all role mappings and consents mapped to all roles of this client
|
||||
|
|
|
@ -32,7 +32,6 @@ import org.keycloak.models.GroupModel;
|
|||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ModelDuplicateException;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.OTPPolicy;
|
||||
import org.keycloak.models.PasswordPolicy;
|
||||
|
@ -41,9 +40,6 @@ import org.keycloak.models.RealmProvider;
|
|||
import org.keycloak.models.RequiredActionProviderModel;
|
||||
import org.keycloak.models.RequiredCredentialModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserFederationMapperModel;
|
||||
import org.keycloak.models.UserFederationProviderCreationEventImpl;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.mongo.keycloak.entities.AuthenticationExecutionEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.AuthenticationFlowEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.AuthenticatorConfigEntity;
|
||||
|
@ -56,15 +52,12 @@ import org.keycloak.models.mongo.keycloak.entities.MongoRealmEntity;
|
|||
import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.RequiredActionProviderEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.RequiredCredentialEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.UserFederationMapperEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.UserFederationProviderEntity;
|
||||
import org.keycloak.models.utils.ComponentUtil;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
|
@ -869,183 +862,6 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
|||
updateRealm();
|
||||
}
|
||||
|
||||
|
||||
private void removeFederationMappersForProvider(String federationProviderId) {
|
||||
Set<UserFederationMapperEntity> mappers = getUserFederationMapperEntitiesByFederationProvider(federationProviderId);
|
||||
for (UserFederationMapperEntity mapper : mappers) {
|
||||
getMongoEntity().getUserFederationMappers().remove(mapper);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserFederationProviderModel addUserFederationProvider(String providerName, Map<String, String> config, int priority, String displayName, int fullSyncPeriod, int changedSyncPeriod, int lastSync) {
|
||||
KeycloakModelUtils.ensureUniqueDisplayName(displayName, null, getUserFederationProviders());
|
||||
|
||||
UserFederationProviderEntity entity = new UserFederationProviderEntity();
|
||||
entity.setId(KeycloakModelUtils.generateId());
|
||||
entity.setPriority(priority);
|
||||
entity.setProviderName(providerName);
|
||||
entity.setConfig(config);
|
||||
if (displayName == null) {
|
||||
displayName = entity.getId();
|
||||
}
|
||||
entity.setDisplayName(displayName);
|
||||
entity.setFullSyncPeriod(fullSyncPeriod);
|
||||
entity.setChangedSyncPeriod(changedSyncPeriod);
|
||||
entity.setLastSync(lastSync);
|
||||
realm.getUserFederationProviders().add(entity);
|
||||
updateRealm();
|
||||
|
||||
UserFederationProviderModel providerModel = new UserFederationProviderModel(entity.getId(), providerName, config, priority, displayName, fullSyncPeriod, changedSyncPeriod, lastSync);
|
||||
|
||||
session.getKeycloakSessionFactory().publish(new UserFederationProviderCreationEventImpl(this, providerModel));
|
||||
|
||||
return providerModel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUserFederationProvider(UserFederationProviderModel provider) {
|
||||
Iterator<UserFederationProviderEntity> it = realm.getUserFederationProviders().iterator();
|
||||
while (it.hasNext()) {
|
||||
UserFederationProviderEntity entity = it.next();
|
||||
if (entity.getId().equals(provider.getId())) {
|
||||
session.users().preRemove(this, new UserFederationProviderModel(entity.getId(), entity.getProviderName(), entity.getConfig(), entity.getPriority(), entity.getDisplayName(),
|
||||
entity.getFullSyncPeriod(), entity.getChangedSyncPeriod(), entity.getLastSync()));
|
||||
removeFederationMappersForProvider(provider.getId());
|
||||
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
updateRealm();
|
||||
}
|
||||
@Override
|
||||
public void updateUserFederationProvider(UserFederationProviderModel model) {
|
||||
KeycloakModelUtils.ensureUniqueDisplayName(model.getDisplayName(), model, getUserFederationProviders());
|
||||
|
||||
Iterator<UserFederationProviderEntity> it = realm.getUserFederationProviders().iterator();
|
||||
while (it.hasNext()) {
|
||||
UserFederationProviderEntity entity = it.next();
|
||||
if (entity.getId().equals(model.getId())) {
|
||||
entity.setProviderName(model.getProviderName());
|
||||
entity.setConfig(model.getConfig());
|
||||
entity.setPriority(model.getPriority());
|
||||
String displayName = model.getDisplayName();
|
||||
if (displayName != null) {
|
||||
entity.setDisplayName(model.getDisplayName());
|
||||
}
|
||||
entity.setFullSyncPeriod(model.getFullSyncPeriod());
|
||||
entity.setChangedSyncPeriod(model.getChangedSyncPeriod());
|
||||
entity.setLastSync(model.getLastSync());
|
||||
}
|
||||
}
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserFederationProviderModel> getUserFederationProviders() {
|
||||
List<UserFederationProviderEntity> entities = realm.getUserFederationProviders();
|
||||
if (entities.isEmpty()) return Collections.EMPTY_LIST;
|
||||
List<UserFederationProviderEntity> copy = new LinkedList<UserFederationProviderEntity>();
|
||||
for (UserFederationProviderEntity entity : entities) {
|
||||
copy.add(entity);
|
||||
|
||||
}
|
||||
Collections.sort(copy, new Comparator<UserFederationProviderEntity>() {
|
||||
|
||||
@Override
|
||||
public int compare(UserFederationProviderEntity o1, UserFederationProviderEntity o2) {
|
||||
return o1.getPriority() - o2.getPriority();
|
||||
}
|
||||
|
||||
});
|
||||
List<UserFederationProviderModel> result = new LinkedList<UserFederationProviderModel>();
|
||||
for (UserFederationProviderEntity entity : copy) {
|
||||
result.add(new UserFederationProviderModel(entity.getId(), entity.getProviderName(), entity.getConfig(), entity.getPriority(), entity.getDisplayName(),
|
||||
entity.getFullSyncPeriod(), entity.getChangedSyncPeriod(), entity.getLastSync()));
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUserFederationProviders(List<UserFederationProviderModel> providers) {
|
||||
for (UserFederationProviderModel currentProvider : providers) {
|
||||
KeycloakModelUtils.ensureUniqueDisplayName(currentProvider.getDisplayName(), currentProvider, providers);
|
||||
}
|
||||
|
||||
List<UserFederationProviderEntity> existingProviders = realm.getUserFederationProviders();
|
||||
List<UserFederationProviderEntity> toRemove = new LinkedList<>();
|
||||
for (UserFederationProviderEntity entity : existingProviders) {
|
||||
boolean found = false;
|
||||
for (UserFederationProviderModel model : providers) {
|
||||
if (entity.getId().equals(model.getId())) {
|
||||
entity.setConfig(model.getConfig());
|
||||
entity.setPriority(model.getPriority());
|
||||
entity.setProviderName(model.getProviderName());
|
||||
String displayName = model.getDisplayName();
|
||||
if (displayName != null) {
|
||||
entity.setDisplayName(displayName);
|
||||
}
|
||||
entity.setFullSyncPeriod(model.getFullSyncPeriod());
|
||||
entity.setChangedSyncPeriod(model.getChangedSyncPeriod());
|
||||
entity.setLastSync(model.getLastSync());
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
if (found) continue;
|
||||
session.users().preRemove(this, new UserFederationProviderModel(entity.getId(), entity.getProviderName(), entity.getConfig(), entity.getPriority(), entity.getDisplayName(),
|
||||
entity.getFullSyncPeriod(), entity.getChangedSyncPeriod(), entity.getLastSync()));
|
||||
removeFederationMappersForProvider(entity.getId());
|
||||
|
||||
toRemove.add(entity);
|
||||
}
|
||||
|
||||
for (UserFederationProviderEntity entity : toRemove) {
|
||||
realm.getUserFederationProviders().remove(entity);
|
||||
}
|
||||
|
||||
List<UserFederationProviderModel> add = new LinkedList<UserFederationProviderModel>();
|
||||
for (UserFederationProviderModel model : providers) {
|
||||
boolean found = false;
|
||||
for (UserFederationProviderEntity entity : realm.getUserFederationProviders()) {
|
||||
if (entity.getId().equals(model.getId())) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) add.add(model);
|
||||
}
|
||||
|
||||
for (UserFederationProviderModel model : add) {
|
||||
UserFederationProviderEntity entity = new UserFederationProviderEntity();
|
||||
if (model.getId() != null) {
|
||||
entity.setId(model.getId());
|
||||
} else {
|
||||
String id = KeycloakModelUtils.generateId();
|
||||
entity.setId(id);
|
||||
model.setId(id);
|
||||
}
|
||||
entity.setProviderName(model.getProviderName());
|
||||
entity.setConfig(model.getConfig());
|
||||
entity.setPriority(model.getPriority());
|
||||
String displayName = model.getDisplayName();
|
||||
if (displayName == null) {
|
||||
displayName = entity.getId();
|
||||
}
|
||||
entity.setDisplayName(displayName);
|
||||
entity.setFullSyncPeriod(model.getFullSyncPeriod());
|
||||
entity.setChangedSyncPeriod(model.getChangedSyncPeriod());
|
||||
entity.setLastSync(model.getLastSync());
|
||||
realm.getUserFederationProviders().add(entity);
|
||||
|
||||
session.getKeycloakSessionFactory().publish(new UserFederationProviderCreationEventImpl(this, model));
|
||||
}
|
||||
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEventsEnabled() {
|
||||
return realm.isEventsEnabled();
|
||||
|
@ -1760,131 +1576,6 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Set<UserFederationMapperModel> getUserFederationMappers() {
|
||||
List<UserFederationMapperEntity> entities = getMongoEntity().getUserFederationMappers();
|
||||
if (entities.isEmpty()) return Collections.EMPTY_SET;
|
||||
Set<UserFederationMapperModel> mappers = new HashSet<UserFederationMapperModel>();
|
||||
for (UserFederationMapperEntity entity : entities) {
|
||||
UserFederationMapperModel mapper = entityToModel(entity);
|
||||
mappers.add(mapper);
|
||||
}
|
||||
return Collections.unmodifiableSet(mappers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<UserFederationMapperModel> getUserFederationMappersByFederationProvider(String federationProviderId) {
|
||||
Set<UserFederationMapperModel> mappers = new HashSet<UserFederationMapperModel>();
|
||||
Set<UserFederationMapperEntity> mapperEntities = getUserFederationMapperEntitiesByFederationProvider(federationProviderId);
|
||||
for (UserFederationMapperEntity entity : mapperEntities) {
|
||||
mappers.add(entityToModel(entity));
|
||||
}
|
||||
return mappers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserFederationMapperModel addUserFederationMapper(UserFederationMapperModel model) {
|
||||
if (getUserFederationMapperByName(model.getFederationProviderId(), model.getName()) != null) {
|
||||
throw new ModelDuplicateException("User federation mapper must be unique per federation provider. There is already: " + model.getName());
|
||||
}
|
||||
String id = KeycloakModelUtils.generateId();
|
||||
UserFederationMapperEntity entity = new UserFederationMapperEntity();
|
||||
entity.setId(id);
|
||||
entity.setName(model.getName());
|
||||
entity.setFederationProviderId(model.getFederationProviderId());
|
||||
entity.setFederationMapperType(model.getFederationMapperType());
|
||||
entity.setConfig(model.getConfig());
|
||||
|
||||
getMongoEntity().getUserFederationMappers().add(entity);
|
||||
updateMongoEntity();
|
||||
UserFederationMapperModel mapperModel = entityToModel(entity);
|
||||
|
||||
return mapperModel;
|
||||
}
|
||||
|
||||
protected UserFederationMapperEntity getUserFederationMapperEntity(String id) {
|
||||
for (UserFederationMapperEntity entity : getMongoEntity().getUserFederationMappers()) {
|
||||
if (entity.getId().equals(id)) {
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
protected UserFederationMapperEntity getUserFederationMapperEntityByName(String federationProviderId, String name) {
|
||||
for (UserFederationMapperEntity entity : getMongoEntity().getUserFederationMappers()) {
|
||||
if (entity.getFederationProviderId().equals(federationProviderId) && entity.getName().equals(name)) {
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
protected Set<UserFederationMapperEntity> getUserFederationMapperEntitiesByFederationProvider(String federationProviderId) {
|
||||
Set<UserFederationMapperEntity> mappers = new HashSet<UserFederationMapperEntity>();
|
||||
for (UserFederationMapperEntity entity : getMongoEntity().getUserFederationMappers()) {
|
||||
if (federationProviderId.equals(entity.getFederationProviderId())) {
|
||||
mappers.add(entity);
|
||||
}
|
||||
}
|
||||
return mappers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUserFederationMapper(UserFederationMapperModel mapper) {
|
||||
UserFederationMapperEntity toDelete = getUserFederationMapperEntity(mapper.getId());
|
||||
if (toDelete != null) {
|
||||
this.realm.getUserFederationMappers().remove(toDelete);
|
||||
updateMongoEntity();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUserFederationMapper(UserFederationMapperModel mapper) {
|
||||
UserFederationMapperEntity entity = getUserFederationMapperEntity(mapper.getId());
|
||||
entity.setFederationProviderId(mapper.getFederationProviderId());
|
||||
entity.setFederationMapperType(mapper.getFederationMapperType());
|
||||
if (entity.getConfig() == null) {
|
||||
entity.setConfig(mapper.getConfig());
|
||||
} else {
|
||||
entity.getConfig().clear();
|
||||
entity.getConfig().putAll(mapper.getConfig());
|
||||
}
|
||||
updateMongoEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserFederationMapperModel getUserFederationMapperById(String id) {
|
||||
UserFederationMapperEntity entity = getUserFederationMapperEntity(id);
|
||||
if (entity == null) return null;
|
||||
return entityToModel(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserFederationMapperModel getUserFederationMapperByName(String federationProviderId, String name) {
|
||||
UserFederationMapperEntity entity = getUserFederationMapperEntityByName(federationProviderId, name);
|
||||
if (entity == null) return null;
|
||||
return entityToModel(entity);
|
||||
}
|
||||
|
||||
protected UserFederationMapperModel entityToModel(UserFederationMapperEntity entity) {
|
||||
UserFederationMapperModel mapper = new UserFederationMapperModel();
|
||||
mapper.setId(entity.getId());
|
||||
mapper.setName(entity.getName());
|
||||
mapper.setFederationProviderId(entity.getFederationProviderId());
|
||||
mapper.setFederationMapperType(entity.getFederationMapperType());
|
||||
Map<String, String> config = new HashMap<String, String>();
|
||||
if (entity.getConfig() != null) config.putAll(entity.getConfig());
|
||||
mapper.setConfig(config);
|
||||
return mapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ClientTemplateModel> getClientTemplates() {
|
||||
DBObject query = new QueryBuilder()
|
||||
|
|
|
@ -33,27 +33,18 @@ import org.keycloak.models.ProtocolMapperModel;
|
|||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserConsentModel;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.mongo.keycloak.entities.CredentialEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.FederatedIdentityEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoUserEntity;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.storage.StorageId;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.storage.federated.UserAttributeFederatedStorage;
|
||||
import org.keycloak.storage.federated.UserBrokerLinkFederatedStorage;
|
||||
import org.keycloak.storage.federated.UserConsentFederatedStorage;
|
||||
import org.keycloak.storage.federated.UserFederatedStorageProvider;
|
||||
import org.keycloak.storage.federated.UserGroupMembershipFederatedStorage;
|
||||
import org.keycloak.storage.federated.UserRequiredActionsFederatedStorage;
|
||||
import org.keycloak.storage.federated.UserRoleMappingsFederatedStorage;
|
||||
import org.keycloak.storage.mongo.entity.FederatedUser;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
@ -220,11 +211,6 @@ public class MongoUserFederatedStorageProvider implements
|
|||
getMongoStore().removeEntities(FederatedUser.class, query, true, invocationContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, UserFederationProviderModel link) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, GroupModel group) {
|
||||
DBObject query = new QueryBuilder()
|
||||
|
|
2
pom.xml
2
pom.xml
|
@ -1296,7 +1296,7 @@
|
|||
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>federation-properties-example</artifactId>
|
||||
<artifactId>user-storage-properties-example</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* 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.mappers;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class FederationConfigValidationException extends Exception {
|
||||
|
||||
private Object[] parameters;
|
||||
|
||||
public FederationConfigValidationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public FederationConfigValidationException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public FederationConfigValidationException(String message, Object ... parameters) {
|
||||
super(message);
|
||||
this.parameters = parameters;
|
||||
}
|
||||
|
||||
public Object[] getParameters() {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
public void setParameters(Object[] parameters) {
|
||||
this.parameters = parameters;
|
||||
}
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
/*
|
||||
* 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.mappers;
|
||||
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserFederationMapperModel;
|
||||
import org.keycloak.models.UserFederationProvider;
|
||||
import org.keycloak.models.UserFederationSyncResult;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.provider.Provider;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public interface UserFederationMapper extends Provider {
|
||||
|
||||
/**
|
||||
* Sync data from federated storage to Keycloak. It's useful just if mapper needs some data preloaded from federated storage (For example
|
||||
* load roles from federated provider and sync them to Keycloak database)
|
||||
*
|
||||
* Applicable just if sync is supported (see UserFederationMapperFactory.getSyncConfig() )
|
||||
*
|
||||
* @see UserFederationMapperFactory#getSyncConfig()
|
||||
* @param mapperModel
|
||||
* @param federationProvider
|
||||
* @param session
|
||||
* @param realm
|
||||
*/
|
||||
UserFederationSyncResult syncDataFromFederationProviderToKeycloak(UserFederationMapperModel mapperModel, UserFederationProvider federationProvider, KeycloakSession session, RealmModel realm);
|
||||
|
||||
/**
|
||||
* Sync data from Keycloak back to federated storage
|
||||
*
|
||||
* @see UserFederationMapperFactory#getSyncConfig()
|
||||
* @param mapperModel
|
||||
* @param federationProvider
|
||||
* @param session
|
||||
* @param realm
|
||||
*/
|
||||
UserFederationSyncResult syncDataFromKeycloakToFederationProvider(UserFederationMapperModel mapperModel, UserFederationProvider federationProvider, KeycloakSession session, RealmModel realm);
|
||||
|
||||
/**
|
||||
* Return empty list if doesn't support storing of groups
|
||||
*/
|
||||
List<UserModel> getGroupMembers(UserFederationMapperModel mapperModel, UserFederationProvider federationProvider, RealmModel realm, GroupModel group, int firstResult, int maxResults);
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
* 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.mappers;
|
||||
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserFederationMapperModel;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.provider.ConfiguredProvider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.representations.idm.UserFederationMapperSyncConfigRepresentation;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public interface UserFederationMapperFactory extends ProviderFactory<UserFederationMapper>, ConfiguredProvider {
|
||||
|
||||
/**
|
||||
* Refers to providerName (type) of the federated provider, which this mapper can be used for. For example "ldap" or "kerberos"
|
||||
*
|
||||
* @return providerName
|
||||
*/
|
||||
String getFederationProviderType();
|
||||
|
||||
String getDisplayCategory();
|
||||
String getDisplayType();
|
||||
|
||||
/**
|
||||
* Specifies if mapper supports sync data from federated storage to keycloak and viceversa.
|
||||
* Also specifies messages to be displayed in admin console UI (For example "Sync roles from LDAP" etc)
|
||||
*
|
||||
* @return syncConfig representation
|
||||
*/
|
||||
UserFederationMapperSyncConfigRepresentation getSyncConfig();
|
||||
|
||||
/**
|
||||
* Called when instance of mapperModel is created for this factory through admin endpoint
|
||||
*
|
||||
* @param realm
|
||||
* @param fedProviderModel
|
||||
* @param mapperModel
|
||||
* @throws FederationConfigValidationException if configuration provided in mapperModel is not valid
|
||||
*/
|
||||
void validateConfig(RealmModel realm, UserFederationProviderModel fedProviderModel, UserFederationMapperModel mapperModel) throws FederationConfigValidationException;
|
||||
|
||||
/**
|
||||
* Used to detect what are default values for ProviderConfigProperties specified during mapper creation
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Map<String, String> getDefaultConfig(UserFederationProviderModel providerModel);
|
||||
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
/*
|
||||
* 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.mappers;
|
||||
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.provider.Spi;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class UserFederationMapperSpi implements Spi {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "userFederationMapper";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Provider> getProviderClass() {
|
||||
return UserFederationMapper.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends ProviderFactory> getProviderFactoryClass() {
|
||||
return UserFederationMapperFactory.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInternal() {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -42,14 +42,14 @@ public class MigrationModelManager {
|
|||
private static Logger logger = Logger.getLogger(MigrationModelManager.class);
|
||||
|
||||
private static final Migration[] migrations = {
|
||||
new MigrateTo1_2_0(),
|
||||
new MigrateTo1_3_0(),
|
||||
new MigrateTo1_4_0(),
|
||||
new MigrateTo1_5_0(),
|
||||
new MigrateTo1_6_0(),
|
||||
new MigrateTo1_7_0(),
|
||||
new MigrateTo1_8_0(),
|
||||
new MigrateTo1_9_0(),
|
||||
new MigrateTo1_2_0(),
|
||||
new MigrateTo1_3_0(),
|
||||
new MigrateTo1_4_0(),
|
||||
new MigrateTo1_5_0(),
|
||||
new MigrateTo1_6_0(),
|
||||
new MigrateTo1_7_0(),
|
||||
new MigrateTo1_8_0(),
|
||||
new MigrateTo1_9_0(),
|
||||
new MigrateTo1_9_2(),
|
||||
new MigrateTo2_0_0(),
|
||||
new MigrateTo2_1_0(),
|
||||
|
|
|
@ -17,15 +17,16 @@
|
|||
|
||||
package org.keycloak.migration.migrators;
|
||||
|
||||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
import org.keycloak.component.ComponentFactory;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.migration.ModelVersion;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.LDAPConstants;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserFederationEventAwareProviderFactory;
|
||||
import org.keycloak.models.UserFederationMapperModel;
|
||||
import org.keycloak.models.UserFederationProvider;
|
||||
import org.keycloak.models.UserFederationProviderFactory;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
|
||||
import javax.naming.directory.SearchControls;
|
||||
import java.util.List;
|
||||
|
@ -37,7 +38,6 @@ import java.util.Set;
|
|||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class MigrateTo1_3_0 implements Migration {
|
||||
|
||||
public static final ModelVersion VERSION = new ModelVersion("1.3.0");
|
||||
|
||||
public ModelVersion getVersion() {
|
||||
|
@ -53,44 +53,45 @@ public class MigrateTo1_3_0 implements Migration {
|
|||
}
|
||||
|
||||
private void migrateLDAPProviders(KeycloakSession session, RealmModel realm) {
|
||||
List<UserFederationProviderModel> federationProviders = realm.getUserFederationProviders();
|
||||
for (UserFederationProviderModel fedProvider : federationProviders) {
|
||||
List<UserStorageProviderModel> federationProviders = realm.getUserStorageProviders();
|
||||
for (UserStorageProviderModel fedProvider : federationProviders) {
|
||||
|
||||
if (fedProvider.getProviderName().equals(LDAPConstants.LDAP_PROVIDER)) {
|
||||
Map<String, String> config = fedProvider.getConfig();
|
||||
if (fedProvider.getProviderId().equals(LDAPConstants.LDAP_PROVIDER)) {
|
||||
fedProvider = new UserStorageProviderModel(fedProvider); // copy don't want to muck with cache
|
||||
MultivaluedHashMap<String, String> config = fedProvider.getConfig();
|
||||
|
||||
// Update config properties for LDAP federated provider
|
||||
// Update config properties for LDAP federation provider
|
||||
if (config.get(LDAPConstants.SEARCH_SCOPE) == null) {
|
||||
config.put(LDAPConstants.SEARCH_SCOPE, String.valueOf(SearchControls.SUBTREE_SCOPE));
|
||||
config.putSingle(LDAPConstants.SEARCH_SCOPE, String.valueOf(SearchControls.SUBTREE_SCOPE));
|
||||
}
|
||||
|
||||
String usersDn = config.remove("userDnSuffix");
|
||||
if (usersDn != null && config.get(LDAPConstants.USERS_DN) == null) {
|
||||
List<String> usersDn = config.remove("userDnSuffix");
|
||||
if (usersDn != null && !usersDn.isEmpty() && config.getFirst(LDAPConstants.USERS_DN) == null) {
|
||||
config.put(LDAPConstants.USERS_DN, usersDn);
|
||||
}
|
||||
|
||||
String usernameLdapAttribute = config.get(LDAPConstants.USERNAME_LDAP_ATTRIBUTE);
|
||||
if (usernameLdapAttribute != null && config.get(LDAPConstants.RDN_LDAP_ATTRIBUTE) == null) {
|
||||
String usernameLdapAttribute = config.getFirst(LDAPConstants.USERNAME_LDAP_ATTRIBUTE);
|
||||
if (usernameLdapAttribute != null && config.getFirst(LDAPConstants.RDN_LDAP_ATTRIBUTE) == null) {
|
||||
if (usernameLdapAttribute.equalsIgnoreCase(LDAPConstants.SAM_ACCOUNT_NAME)) {
|
||||
config.put(LDAPConstants.RDN_LDAP_ATTRIBUTE, LDAPConstants.CN);
|
||||
config.putSingle(LDAPConstants.RDN_LDAP_ATTRIBUTE, LDAPConstants.CN);
|
||||
} else {
|
||||
config.put(LDAPConstants.RDN_LDAP_ATTRIBUTE, usernameLdapAttribute);
|
||||
config.putSingle(LDAPConstants.RDN_LDAP_ATTRIBUTE, usernameLdapAttribute);
|
||||
}
|
||||
}
|
||||
|
||||
if (config.get(LDAPConstants.UUID_LDAP_ATTRIBUTE) == null) {
|
||||
String uuidAttrName = LDAPConstants.getUuidAttributeName(config.get(LDAPConstants.VENDOR));
|
||||
config.put(LDAPConstants.UUID_LDAP_ATTRIBUTE, uuidAttrName);
|
||||
if (config.getFirst(LDAPConstants.UUID_LDAP_ATTRIBUTE) == null) {
|
||||
String uuidAttrName = LDAPConstants.getUuidAttributeName(config.getFirst(LDAPConstants.VENDOR));
|
||||
config.putSingle(LDAPConstants.UUID_LDAP_ATTRIBUTE, uuidAttrName);
|
||||
}
|
||||
|
||||
realm.updateUserFederationProvider(fedProvider);
|
||||
realm.updateComponent(fedProvider);
|
||||
|
||||
// Create default mappers for LDAP
|
||||
Set<UserFederationMapperModel> mappers = realm.getUserFederationMappersByFederationProvider(fedProvider.getId());
|
||||
List<ComponentModel> mappers = realm.getComponents(fedProvider.getId());
|
||||
if (mappers.isEmpty()) {
|
||||
UserFederationProviderFactory ldapFactory = (UserFederationProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(UserFederationProvider.class, LDAPConstants.LDAP_PROVIDER);
|
||||
ProviderFactory ldapFactory = session.getKeycloakSessionFactory().getProviderFactory(UserStorageProvider.class, LDAPConstants.LDAP_PROVIDER);
|
||||
if (ldapFactory != null) {
|
||||
((UserFederationEventAwareProviderFactory) ldapFactory).onProviderModelCreated(realm, fedProvider);
|
||||
((ComponentFactory) ldapFactory).onCreate(session, realm, fedProvider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,15 +17,17 @@
|
|||
|
||||
package org.keycloak.migration.migrators;
|
||||
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.migration.ModelVersion;
|
||||
import org.keycloak.models.ImpersonationConstants;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.LDAPConstants;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserFederationMapperModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.utils.DefaultAuthenticationFlows;
|
||||
import org.keycloak.models.utils.DefaultRequiredActions;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
@ -36,7 +38,6 @@ import java.util.List;
|
|||
*/
|
||||
public class MigrateTo1_4_0 implements Migration {
|
||||
public static final ModelVersion VERSION = new ModelVersion("1.4.0");
|
||||
|
||||
public ModelVersion getVersion() {
|
||||
return VERSION;
|
||||
}
|
||||
|
@ -58,21 +59,29 @@ public class MigrateTo1_4_0 implements Migration {
|
|||
|
||||
private void migrateLDAPMappers(KeycloakSession session, RealmModel realm) {
|
||||
List<String> mandatoryInLdap = Arrays.asList("username", "username-cn", "first name", "last name");
|
||||
for (UserFederationMapperModel ldapMapper : realm.getUserFederationMappers()) {
|
||||
if (mandatoryInLdap.contains(ldapMapper.getName())) {
|
||||
ldapMapper.getConfig().put("is.mandatory.in.ldap", "true");
|
||||
realm.updateUserFederationMapper(ldapMapper);
|
||||
for (UserStorageProviderModel providerModel : realm.getUserStorageProviders()) {
|
||||
if (providerModel.getProviderId().equals(LDAPConstants.LDAP_PROVIDER)) {
|
||||
List<ComponentModel> mappers = realm.getComponents(providerModel.getId());
|
||||
for (ComponentModel mapper : mappers) {
|
||||
if (mandatoryInLdap.contains(mapper.getName())) {
|
||||
mapper = new ComponentModel(mapper); // don't want to modify cache
|
||||
mapper.getConfig().putSingle("is.mandatory.in.ldap", "true");
|
||||
realm.updateComponent(mapper);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void migrateUsers(KeycloakSession session, RealmModel realm) {
|
||||
List<UserModel> users = session.userStorage().getUsers(realm, false);
|
||||
List<UserModel> users = session.userLocalStorage().getUsers(realm, false);
|
||||
for (UserModel user : users) {
|
||||
String email = user.getEmail();
|
||||
email = KeycloakModelUtils.toLowerCaseSafe(email);
|
||||
if (email != null && !email.equals(user.getEmail())) {
|
||||
user.setEmail(email);
|
||||
session.userCache().evict(realm, user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,13 +17,14 @@
|
|||
|
||||
package org.keycloak.migration.migrators;
|
||||
|
||||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.migration.ModelVersion;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.LDAPConstants;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserFederationMapperModel;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -39,22 +40,21 @@ public class MigrateTo1_8_0 implements Migration {
|
|||
return VERSION;
|
||||
}
|
||||
|
||||
|
||||
public void migrate(KeycloakSession session) {
|
||||
List<RealmModel> realms = session.realms().getRealms();
|
||||
for (RealmModel realm : realms) {
|
||||
|
||||
List<UserFederationProviderModel> federationProviders = realm.getUserFederationProviders();
|
||||
for (UserFederationProviderModel fedProvider : federationProviders) {
|
||||
List<UserStorageProviderModel> federationProviders = realm.getUserStorageProviders();
|
||||
for (UserStorageProviderModel fedProvider : federationProviders) {
|
||||
|
||||
if (fedProvider.getProviderName().equals(LDAPConstants.LDAP_PROVIDER)) {
|
||||
Map<String, String> config = fedProvider.getConfig();
|
||||
|
||||
if (isActiveDirectory(config)) {
|
||||
if (fedProvider.getProviderId().equals(LDAPConstants.LDAP_PROVIDER)) {
|
||||
|
||||
if (isActiveDirectory(fedProvider)) {
|
||||
// Create mapper for MSAD account controls
|
||||
if (realm.getUserFederationMapperByName(fedProvider.getId(), "MSAD account controls") == null) {
|
||||
UserFederationMapperModel mapperModel = KeycloakModelUtils.createUserFederationMapperModel("MSAD account controls", fedProvider.getId(), LDAPConstants.MSAD_USER_ACCOUNT_CONTROL_MAPPER);
|
||||
realm.addUserFederationMapper(mapperModel);
|
||||
if (getMapperByName(realm, fedProvider, "MSAD account controls") == null) {
|
||||
ComponentModel mapperModel = KeycloakModelUtils.createComponentModel("MSAD account controls", fedProvider.getId(), LDAPConstants.MSAD_USER_ACCOUNT_CONTROL_MAPPER, "org.keycloak.storage.ldap.mappers.LDAPStorageMapper");
|
||||
realm.addComponentModel(mapperModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -63,8 +63,19 @@ public class MigrateTo1_8_0 implements Migration {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean isActiveDirectory(Map<String, String> ldapConfig) {
|
||||
String vendor = ldapConfig.get(LDAPConstants.VENDOR);
|
||||
public static ComponentModel getMapperByName(RealmModel realm, ComponentModel providerModel, String name) {
|
||||
List<ComponentModel> components = realm.getComponents(providerModel.getId(), "org.keycloak.storage.ldap.mappers.LDAPStorageMapper");
|
||||
for (ComponentModel component : components) {
|
||||
if (component.getName().equals(name)) {
|
||||
return component;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private boolean isActiveDirectory(UserStorageProviderModel provider) {
|
||||
String vendor = provider.getConfig().getFirst(LDAPConstants.VENDOR);
|
||||
return vendor != null && vendor.equals(LDAPConstants.VENDOR_ACTIVE_DIRECTORY);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* 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.models;
|
||||
|
||||
import org.keycloak.provider.ProviderEvent;
|
||||
import org.keycloak.provider.ProviderEventListener;
|
||||
|
||||
/**
|
||||
* Provides "onProviderModelCreated" callback invoked when UserFederationProviderModel for this factory implementation is created in realm
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public abstract class UserFederationEventAwareProviderFactory implements UserFederationProviderFactory {
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
factory.register(new ProviderEventListener() {
|
||||
|
||||
@Override
|
||||
public void onEvent(ProviderEvent event) {
|
||||
if (event instanceof RealmModel.UserFederationProviderCreationEvent) {
|
||||
RealmModel.UserFederationProviderCreationEvent fedCreationEvent = (RealmModel.UserFederationProviderCreationEvent)event;
|
||||
UserFederationProviderModel providerModel = fedCreationEvent.getCreatedFederationProvider();
|
||||
|
||||
if (providerModel.getProviderName().equals(getId())) {
|
||||
onProviderModelCreated(fedCreationEvent.getRealm(), providerModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public abstract void onProviderModelCreated(RealmModel realm, UserFederationProviderModel createdProviderModel);
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* 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.models;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class UserFederationProviderCreationEventImpl implements RealmModel.UserFederationProviderCreationEvent {
|
||||
|
||||
private final UserFederationProviderModel createdFederationProvider;
|
||||
private final RealmModel realm;
|
||||
|
||||
public UserFederationProviderCreationEventImpl(RealmModel realm, UserFederationProviderModel createdFederationProvider) {
|
||||
this.realm = realm;
|
||||
this.createdFederationProvider = createdFederationProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserFederationProviderModel getCreatedFederationProvider() {
|
||||
return createdFederationProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel getRealm() {
|
||||
return realm;
|
||||
}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* 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.models;
|
||||
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.provider.Spi;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class UserFederationSpi implements Spi {
|
||||
|
||||
@Override
|
||||
public boolean isInternal() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "userFederation";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Provider> getProviderClass() {
|
||||
return UserFederationProvider.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends ProviderFactory> getProviderFactoryClass() {
|
||||
return UserFederationProviderFactory.class;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* 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.models;
|
||||
|
||||
import org.keycloak.mappers.FederationConfigValidationException;
|
||||
|
||||
/**
|
||||
* TODO: Merge with UserFederationProviderFactory and add "default" method validateConfig with empty body once we move to source level 1.8
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public interface UserFederationValidatingProviderFactory extends UserFederationProviderFactory {
|
||||
|
||||
/**
|
||||
* Called when instance of mapperModel is created for this factory through admin endpoint
|
||||
*
|
||||
* @param realm
|
||||
* @param providerModel
|
||||
* @throws FederationConfigValidationException if configuration provided in mapperModel is not valid
|
||||
*/
|
||||
void validateConfig(RealmModel realm, UserFederationProviderModel providerModel) throws FederationConfigValidationException;
|
||||
}
|
|
@ -22,11 +22,8 @@ import org.keycloak.broker.social.SocialIdentityProviderFactory;
|
|||
import org.keycloak.common.util.Base64Url;
|
||||
import org.keycloak.common.util.CertificateUtils;
|
||||
import org.keycloak.common.util.KeyUtils;
|
||||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
import org.keycloak.common.util.PemUtils;
|
||||
import org.keycloak.component.ComponentFactory;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.keys.KeyProvider;
|
||||
import org.keycloak.models.AuthenticationExecutionModel;
|
||||
import org.keycloak.models.AuthenticationFlowModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
|
@ -38,19 +35,12 @@ import org.keycloak.models.KeycloakSession;
|
|||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.KeycloakSessionTask;
|
||||
import org.keycloak.models.KeycloakTransaction;
|
||||
import org.keycloak.models.ModelDuplicateException;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleContainerModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.ScopeContainerModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserFederationMapperModel;
|
||||
import org.keycloak.models.UserFederationProvider;
|
||||
import org.keycloak.models.UserFederationProviderFactory;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.representations.idm.CertificateRepresentation;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
import org.keycloak.transaction.JtaTransactionManagerLookup;
|
||||
|
@ -61,20 +51,15 @@ import javax.transaction.SystemException;
|
|||
import javax.transaction.Transaction;
|
||||
import java.security.Key;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
/**
|
||||
* Set of helper methods, which are useful in various model implementations.
|
||||
|
@ -259,51 +244,6 @@ public final class KeycloakModelUtils {
|
|||
|
||||
// USER FEDERATION RELATED STUFF
|
||||
|
||||
/**
|
||||
* Ensure that displayName of myProvider (if not null) is unique and there is no other provider with same displayName in the list.
|
||||
*
|
||||
* @param displayName to check for duplications
|
||||
* @param myProvider provider, which is excluded from the list (if present)
|
||||
* @param federationProviders
|
||||
* @throws ModelDuplicateException if there is other provider with same displayName
|
||||
*/
|
||||
public static void ensureUniqueDisplayName(String displayName, UserFederationProviderModel myProvider, List<UserFederationProviderModel> federationProviders) throws ModelDuplicateException {
|
||||
if (displayName != null) {
|
||||
|
||||
for (UserFederationProviderModel federationProvider : federationProviders) {
|
||||
if (myProvider != null && (myProvider.equals(federationProvider) || (myProvider.getId() != null && myProvider.getId().equals(federationProvider.getId())))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (displayName.equals(federationProvider.getDisplayName())) {
|
||||
throw new ModelDuplicateException("There is already existing federation provider with display name: " + displayName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static UserFederationProviderModel findUserFederationProviderByDisplayName(String displayName, RealmModel realm) {
|
||||
if (displayName == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (UserFederationProviderModel fedProvider : realm.getUserFederationProviders()) {
|
||||
if (displayName.equals(fedProvider.getDisplayName())) {
|
||||
return fedProvider;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static UserFederationProviderModel findUserFederationProviderById(String fedProviderId, RealmModel realm) {
|
||||
for (UserFederationProviderModel fedProvider : realm.getUserFederationProviders()) {
|
||||
if (fedProviderId.equals(fedProvider.getId())) {
|
||||
return fedProvider;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static UserStorageProviderModel findUserStorageProviderByName(String displayName, RealmModel realm) {
|
||||
if (displayName == null) {
|
||||
|
@ -351,41 +291,6 @@ public final class KeycloakModelUtils {
|
|||
}
|
||||
|
||||
|
||||
|
||||
public static UserFederationMapperModel createUserFederationMapperModel(String name, String federationProviderId, String mapperType, String... config) {
|
||||
UserFederationMapperModel mapperModel = new UserFederationMapperModel();
|
||||
mapperModel.setName(name);
|
||||
mapperModel.setFederationProviderId(federationProviderId);
|
||||
mapperModel.setFederationMapperType(mapperType);
|
||||
|
||||
Map<String, String> configMap = new HashMap<>();
|
||||
String key = null;
|
||||
for (String configEntry : config) {
|
||||
if (key == null) {
|
||||
key = configEntry;
|
||||
} else {
|
||||
configMap.put(key, configEntry);
|
||||
key = null;
|
||||
}
|
||||
}
|
||||
if (key != null) {
|
||||
throw new IllegalStateException("Invalid count of arguments for config. Maybe mistake?");
|
||||
}
|
||||
mapperModel.setConfig(configMap);
|
||||
|
||||
return mapperModel;
|
||||
}
|
||||
|
||||
public static UserFederationProviderFactory getFederationProviderFactory(KeycloakSession session, UserFederationProviderModel model) {
|
||||
return (UserFederationProviderFactory)session.getKeycloakSessionFactory().getProviderFactory(UserFederationProvider.class, model.getProviderName());
|
||||
}
|
||||
|
||||
public static UserFederationProvider getFederationProviderInstance(KeycloakSession session, UserFederationProviderModel model) {
|
||||
UserFederationProviderFactory factory = getFederationProviderFactory(session, model);
|
||||
return factory.getInstance(session, model);
|
||||
|
||||
}
|
||||
|
||||
// END USER FEDERATION RELATED STUFF
|
||||
|
||||
public static String toLowerCaseSafe(String str) {
|
||||
|
|
|
@ -52,8 +52,6 @@ import org.keycloak.models.RequiredCredentialModel;
|
|||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserConsentModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserFederationMapperModel;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
|
@ -78,8 +76,6 @@ import org.keycloak.representations.idm.RealmRepresentation;
|
|||
import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
import org.keycloak.representations.idm.UserConsentRepresentation;
|
||||
import org.keycloak.representations.idm.UserFederationMapperRepresentation;
|
||||
import org.keycloak.representations.idm.UserFederationProviderRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.representations.idm.UserSessionRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
|
@ -352,8 +348,6 @@ public class ModelToRepresentation {
|
|||
}
|
||||
}
|
||||
|
||||
exportUserFederationProvidersAndMappers(realm, rep);
|
||||
|
||||
for (IdentityProviderModel provider : realm.getIdentityProviders()) {
|
||||
rep.addIdentityProvider(toRepresentation(realm, provider));
|
||||
}
|
||||
|
@ -384,23 +378,7 @@ public class ModelToRepresentation {
|
|||
return rep;
|
||||
}
|
||||
|
||||
public static void exportUserFederationProvidersAndMappers(RealmModel realm, RealmRepresentation rep) {
|
||||
List<UserFederationProviderModel> fedProviderModels = realm.getUserFederationProviders();
|
||||
if (fedProviderModels.size() > 0) {
|
||||
List<UserFederationProviderRepresentation> fedProviderReps = new ArrayList<UserFederationProviderRepresentation>();
|
||||
for (UserFederationProviderModel model : fedProviderModels) {
|
||||
UserFederationProviderRepresentation fedProvRep = toRepresentation(model);
|
||||
fedProviderReps.add(fedProvRep);
|
||||
}
|
||||
rep.setUserFederationProviders(fedProviderReps);
|
||||
}
|
||||
|
||||
for (UserFederationMapperModel mapper : realm.getUserFederationMappers()) {
|
||||
rep.addUserFederationMapper(toRepresentation(realm, mapper));
|
||||
}
|
||||
}
|
||||
|
||||
public static void exportGroups(RealmModel realm, RealmRepresentation rep) {
|
||||
public static void exportGroups(RealmModel realm, RealmRepresentation rep) {
|
||||
List<GroupRepresentation> groups = toGroupHierarchy(realm, true);
|
||||
rep.setGroups(groups);
|
||||
}
|
||||
|
@ -592,37 +570,6 @@ public class ModelToRepresentation {
|
|||
return rep;
|
||||
}
|
||||
|
||||
public static UserFederationProviderRepresentation toRepresentation(UserFederationProviderModel model) {
|
||||
UserFederationProviderRepresentation rep = new UserFederationProviderRepresentation();
|
||||
rep.setId(model.getId());
|
||||
rep.setConfig(model.getConfig());
|
||||
rep.setProviderName(model.getProviderName());
|
||||
rep.setPriority(model.getPriority());
|
||||
rep.setDisplayName(model.getDisplayName());
|
||||
rep.setFullSyncPeriod(model.getFullSyncPeriod());
|
||||
rep.setChangedSyncPeriod(model.getChangedSyncPeriod());
|
||||
rep.setLastSync(model.getLastSync());
|
||||
return rep;
|
||||
}
|
||||
|
||||
public static UserFederationMapperRepresentation toRepresentation(RealmModel realm, UserFederationMapperModel model) {
|
||||
UserFederationMapperRepresentation rep = new UserFederationMapperRepresentation();
|
||||
rep.setId(model.getId());
|
||||
rep.setName(model.getName());
|
||||
rep.setFederationMapperType(model.getFederationMapperType());
|
||||
Map<String, String> config = new HashMap<String, String>();
|
||||
config.putAll(model.getConfig());
|
||||
rep.setConfig(config);
|
||||
|
||||
UserFederationProviderModel fedProvider = KeycloakModelUtils.findUserFederationProviderById(model.getFederationProviderId(), realm);
|
||||
if (fedProvider == null) {
|
||||
throw new ModelException("Couldn't find federation provider with ID " + model.getId());
|
||||
}
|
||||
rep.setFederationProviderDisplayName(fedProvider.getDisplayName());
|
||||
|
||||
return rep;
|
||||
}
|
||||
|
||||
public static IdentityProviderRepresentation toRepresentation(RealmModel realm, IdentityProviderModel identityProviderModel) {
|
||||
IdentityProviderRepresentation providerRep = new IdentityProviderRepresentation();
|
||||
|
||||
|
|
|
@ -62,8 +62,6 @@ import org.keycloak.models.RoleModel;
|
|||
import org.keycloak.models.ScopeContainerModel;
|
||||
import org.keycloak.models.UserConsentModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserFederationMapperModel;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.representations.idm.ApplicationRepresentation;
|
||||
|
@ -113,7 +111,6 @@ import java.util.List;
|
|||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -367,46 +364,21 @@ public class RepresentationToModel {
|
|||
mapperConvertSet.put(LDAPConstants.LDAP_PROVIDER, "org.keycloak.storage.ldap.mappers.LDAPStorageMapper");
|
||||
|
||||
|
||||
List<UserFederationProviderModel> providerModels = null;
|
||||
Map<String, ComponentModel> userStorageModels = new HashMap<>();
|
||||
|
||||
if (rep.getUserFederationProviders() != null) {
|
||||
providerModels = new LinkedList<>();
|
||||
for (UserFederationProviderRepresentation fedRep : rep.getUserFederationProviders()) {
|
||||
if (convertSet.contains(fedRep.getProviderName())) {
|
||||
ComponentModel component = convertFedProviderToComponent(newRealm.getId(), fedRep);
|
||||
userStorageModels.put(fedRep.getDisplayName(), newRealm.importComponentModel(component));
|
||||
} else {
|
||||
providerModels.add(convertFederationProvider(fedRep));
|
||||
}
|
||||
|
||||
}
|
||||
newRealm.setUserFederationProviders(providerModels);
|
||||
}
|
||||
|
||||
// This is for case, when you have hand-written JSON file with LDAP userFederationProvider, but WITHOUT any userFederationMappers configured. Default LDAP mappers need to be created in that case.
|
||||
Set<String> storageProvidersWhichShouldImportDefaultMappers = new HashSet<>(userStorageModels.keySet());
|
||||
|
||||
if (rep.getUserFederationMappers() != null) {
|
||||
|
||||
// Remove builtin mappers for federation providers, which have some mappers already provided in JSON (likely due to previous export)
|
||||
if (rep.getUserFederationProviders() != null) {
|
||||
Set<String> providerNames = new TreeSet<String>();
|
||||
for (UserFederationMapperRepresentation representation : rep.getUserFederationMappers()) {
|
||||
providerNames.add(representation.getFederationProviderDisplayName());
|
||||
}
|
||||
for (String providerName : providerNames) {
|
||||
for (UserFederationProviderModel providerModel : providerModels) {
|
||||
if (providerName.equals(providerModel.getDisplayName())) {
|
||||
Set<UserFederationMapperModel> toDelete = newRealm.getUserFederationMappersByFederationProvider(providerModel.getId());
|
||||
for (UserFederationMapperModel mapperModel : toDelete) {
|
||||
newRealm.removeUserFederationMapper(mapperModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (UserFederationMapperRepresentation representation : rep.getUserFederationMappers()) {
|
||||
if (userStorageModels.containsKey(representation.getFederationProviderDisplayName())) {
|
||||
ComponentModel parent = userStorageModels.get(representation.getFederationProviderDisplayName());
|
||||
|
@ -417,8 +389,6 @@ public class RepresentationToModel {
|
|||
|
||||
storageProvidersWhichShouldImportDefaultMappers.remove(representation.getFederationProviderDisplayName());
|
||||
|
||||
} else {
|
||||
newRealm.addUserFederationMapper(toModel(newRealm, representation));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -865,11 +835,6 @@ public class RepresentationToModel {
|
|||
realm.setBrowserSecurityHeaders(rep.getBrowserSecurityHeaders());
|
||||
}
|
||||
|
||||
if (rep.getUserFederationProviders() != null) {
|
||||
List<UserFederationProviderModel> providerModels = convertFederationProviders(rep.getUserFederationProviders());
|
||||
realm.setUserFederationProviders(providerModels);
|
||||
}
|
||||
|
||||
if(rep.isInternationalizationEnabled() != null){
|
||||
realm.setInternationalizationEnabled(rep.isInternationalizationEnabled());
|
||||
}
|
||||
|
@ -899,22 +864,6 @@ public class RepresentationToModel {
|
|||
// Basic realm stuff
|
||||
|
||||
|
||||
private static List<UserFederationProviderModel> convertFederationProviders(List<UserFederationProviderRepresentation> providers) {
|
||||
List<UserFederationProviderModel> result = new ArrayList<UserFederationProviderModel>();
|
||||
|
||||
for (UserFederationProviderRepresentation representation : providers) {
|
||||
UserFederationProviderModel model = convertFederationProvider(representation);
|
||||
result.add(model);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static UserFederationProviderModel convertFederationProvider(UserFederationProviderRepresentation representation) {
|
||||
return new UserFederationProviderModel(representation.getId(), representation.getProviderName(),
|
||||
representation.getConfig(), representation.getPriority(), representation.getDisplayName(),
|
||||
representation.getFullSyncPeriod(), representation.getChangedSyncPeriod(), representation.getLastSync());
|
||||
}
|
||||
|
||||
public static ComponentModel convertFedProviderToComponent(String realmId, UserFederationProviderRepresentation fedModel) {
|
||||
UserStorageProviderModel model = new UserStorageProviderModel();
|
||||
model.setId(fedModel.getId());
|
||||
|
@ -950,23 +899,6 @@ public class RepresentationToModel {
|
|||
}
|
||||
|
||||
|
||||
public static UserFederationMapperModel toModel(RealmModel realm, UserFederationMapperRepresentation rep) {
|
||||
UserFederationMapperModel model = new UserFederationMapperModel();
|
||||
model.setId(rep.getId());
|
||||
model.setName(rep.getName());
|
||||
model.setFederationMapperType(rep.getFederationMapperType());
|
||||
model.setConfig(rep.getConfig());
|
||||
|
||||
UserFederationProviderModel fedProvider = KeycloakModelUtils.findUserFederationProviderByDisplayName(rep.getFederationProviderDisplayName(), realm);
|
||||
if (fedProvider == null) {
|
||||
throw new ModelException("Couldn't find federation provider with display name [" + rep.getFederationProviderDisplayName() + "] referenced from mapper ["
|
||||
+ rep.getName());
|
||||
}
|
||||
model.setFederationProviderId(fedProvider.getId());
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
// Roles
|
||||
|
||||
public static void createRole(RealmModel newRealm, RoleRepresentation roleRep) {
|
||||
|
@ -1415,7 +1347,7 @@ public class RepresentationToModel {
|
|||
convertDeprecatedSocialProviders(userRep);
|
||||
|
||||
// Import users just to user storage. Don't federate
|
||||
UserModel user = session.userStorage().addUser(newRealm, userRep.getId(), userRep.getUsername(), false, false);
|
||||
UserModel user = session.userLocalStorage().addUser(newRealm, userRep.getId(), userRep.getUsername(), false, false);
|
||||
user.setEnabled(userRep.isEnabled() != null && userRep.isEnabled());
|
||||
user.setCreatedTimestamp(userRep.getCreatedTimestamp());
|
||||
user.setEmail(userRep.getEmail());
|
||||
|
@ -1447,7 +1379,7 @@ public class RepresentationToModel {
|
|||
if (userRep.getClientConsents() != null) {
|
||||
for (UserConsentRepresentation consentRep : userRep.getClientConsents()) {
|
||||
UserConsentModel consentModel = toModel(newRealm, consentRep);
|
||||
session.userStorage().addConsent(newRealm, user.getId(), consentModel);
|
||||
session.users().addConsent(newRealm, user.getId(), consentModel);
|
||||
}
|
||||
}
|
||||
if (userRep.getServiceAccountClientId() != null) {
|
||||
|
|
|
@ -15,10 +15,8 @@
|
|||
# limitations under the License.
|
||||
#
|
||||
|
||||
org.keycloak.models.UserFederationSpi
|
||||
org.keycloak.storage.UserStorageProviderSpi
|
||||
org.keycloak.storage.federated.UserFederatedStorageProviderSpi
|
||||
org.keycloak.mappers.UserFederationMapperSpi
|
||||
org.keycloak.models.RealmSpi
|
||||
org.keycloak.models.UserSessionSpi
|
||||
org.keycloak.models.UserSpi
|
||||
|
|
|
@ -110,29 +110,29 @@ public interface KeycloakSession {
|
|||
*
|
||||
* @return may be null if cache is disabled
|
||||
*/
|
||||
UserCache getUserCache();
|
||||
UserCache userCache();
|
||||
|
||||
/**
|
||||
* A cached view of all users in system including deprecated UserFederationProvider SPI
|
||||
* A cached view of all users in system including users loaded by UserStorageProviders
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
UserFederationManager users();
|
||||
UserProvider users();
|
||||
|
||||
|
||||
/**
|
||||
* Un-cached view of all users in system that does NOT include users available from the deprecated UserFederationProvider SPI.
|
||||
* Un-cached view of all users in system including users loaded by UserStorageProviders
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
UserProvider userStorageManager();
|
||||
|
||||
UserCredentialManager userCredentialManager();
|
||||
|
||||
/**
|
||||
* A cached view of all users in system that does NOT include users available from the deprecated UserFederationProvider SPI.
|
||||
* Service that allows you to valid and update credentials for a user
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
UserProvider userStorage();
|
||||
UserCredentialManager userCredentialManager();
|
||||
|
||||
/**
|
||||
* Keycloak specific local storage for users. No cache in front, this api talks directly to database configured for Keycloak
|
||||
|
|
|
@ -67,11 +67,6 @@ public interface RealmModel extends RoleContainerModel {
|
|||
KeycloakSession getKeycloakSession();
|
||||
}
|
||||
|
||||
interface UserFederationProviderCreationEvent extends ProviderEvent {
|
||||
UserFederationProviderModel getCreatedFederationProvider();
|
||||
RealmModel getRealm();
|
||||
}
|
||||
|
||||
String getId();
|
||||
|
||||
String getName();
|
||||
|
@ -310,21 +305,6 @@ public interface RealmModel extends RoleContainerModel {
|
|||
return list;
|
||||
}
|
||||
|
||||
// Should return list sorted by UserFederationProviderModel.priority
|
||||
List<UserFederationProviderModel> getUserFederationProviders();
|
||||
UserFederationProviderModel addUserFederationProvider(String providerName, Map<String, String> config, int priority, String displayName, int fullSyncPeriod, int changedSyncPeriod, int lastSync);
|
||||
void updateUserFederationProvider(UserFederationProviderModel provider);
|
||||
void removeUserFederationProvider(UserFederationProviderModel provider);
|
||||
void setUserFederationProviders(List<UserFederationProviderModel> providers);
|
||||
|
||||
Set<UserFederationMapperModel> getUserFederationMappers();
|
||||
Set<UserFederationMapperModel> getUserFederationMappersByFederationProvider(String federationProviderId);
|
||||
UserFederationMapperModel addUserFederationMapper(UserFederationMapperModel mapper);
|
||||
void removeUserFederationMapper(UserFederationMapperModel mapper);
|
||||
void updateUserFederationMapper(UserFederationMapperModel mapper);
|
||||
UserFederationMapperModel getUserFederationMapperById(String id);
|
||||
UserFederationMapperModel getUserFederationMapperByName(String federationProviderId, String name);
|
||||
|
||||
String getLoginTheme();
|
||||
|
||||
void setLoginTheme(String name);
|
||||
|
|
|
@ -1,513 +0,0 @@
|
|||
/*
|
||||
* 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.models;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class UserFederationManager implements UserProvider {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(UserFederationManager.class);
|
||||
|
||||
protected KeycloakSession session;
|
||||
|
||||
// Set of already validated/proxied federation users during this session. Key is user ID
|
||||
private Map<String, UserModel> managedUsers = new HashMap<>();
|
||||
|
||||
public UserFederationManager(KeycloakSession session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel addUser(RealmModel realm, String id, String username, boolean addDefaultRoles, boolean addDefaultRequiredActions) {
|
||||
UserModel user = session.userStorage().addUser(realm, id, username.toLowerCase(), addDefaultRoles, addDefaultRequiredActions);
|
||||
return registerWithFederation(realm, user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel addUser(RealmModel realm, String username) {
|
||||
UserModel user = session.userStorage().addUser(realm, username.toLowerCase());
|
||||
return registerWithFederation(realm, user);
|
||||
}
|
||||
|
||||
protected UserModel registerWithFederation(RealmModel realm, UserModel user) {
|
||||
for (UserFederationProviderModel federation : realm.getUserFederationProviders()) {
|
||||
UserFederationProvider fed = getFederationProvider(federation);
|
||||
if (fed.synchronizeRegistrations()) {
|
||||
user.setFederationLink(federation.getId());
|
||||
UserModel registered = fed.register(realm, user);
|
||||
managedUsers.put(registered.getId(), registered);
|
||||
return registered;
|
||||
}
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
public UserFederationProvider getFederationProvider(UserFederationProviderModel model) {
|
||||
UserFederationProviderFactory factory = (UserFederationProviderFactory)session.getKeycloakSessionFactory().getProviderFactory(UserFederationProvider.class, model.getProviderName());
|
||||
return factory.getInstance(session, model);
|
||||
}
|
||||
|
||||
public UserFederationProvider getFederationLink(RealmModel realm, UserModel user) {
|
||||
if (user.getFederationLink() == null) return null;
|
||||
for (UserFederationProviderModel fed : realm.getUserFederationProviders()) {
|
||||
if (fed.getId().equals(user.getFederationLink())) {
|
||||
return getFederationProvider(fed);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeUser(RealmModel realm, UserModel user) {
|
||||
UserFederationProvider link = getFederationLink(realm, user);
|
||||
if (link != null) {
|
||||
boolean fedRemoved = link.removeUser(realm, user);
|
||||
if (fedRemoved) {
|
||||
boolean localRemoved = session.userStorage().removeUser(realm, user);
|
||||
managedUsers.remove(user.getId());
|
||||
if (!localRemoved) {
|
||||
logger.warn("User possibly removed from federation provider, but failed to remove him from keycloak model");
|
||||
}
|
||||
return localRemoved;
|
||||
} else {
|
||||
logger.warn("Failed to remove user from federation provider");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return session.userStorage().removeUser(realm, user);
|
||||
|
||||
}
|
||||
|
||||
public void validateUser(RealmModel realm, UserModel user) {
|
||||
if (managedUsers.containsKey(user.getId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
UserFederationProvider link = getFederationLink(realm, user);
|
||||
if (link != null && !link.isValid(realm, user)) {
|
||||
deleteInvalidUser(realm, user);
|
||||
throw new IllegalStateException("Federated user no longer valid");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void deleteInvalidUser(final RealmModel realm, final UserModel user) {
|
||||
runJobInTransaction(session.getKeycloakSessionFactory(), new KeycloakSessionTask() {
|
||||
|
||||
@Override
|
||||
public void run(KeycloakSession session) {
|
||||
RealmModel realmModel = session.realms().getRealm(realm.getId());
|
||||
if (realmModel == null) return;
|
||||
UserModel deletedUser = session.userStorage().getUserById(user.getId(), realmModel);
|
||||
new UserManager(session).removeUser(realmModel, deletedUser, session.userStorage());
|
||||
logger.debugf("Removed invalid user '%s'", user.getUsername());
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private static void runJobInTransaction(KeycloakSessionFactory factory, KeycloakSessionTask task) {
|
||||
KeycloakSession session = factory.create();
|
||||
KeycloakTransaction tx = session.getTransactionManager();
|
||||
try {
|
||||
tx.begin();
|
||||
task.run(session);
|
||||
|
||||
if (tx.isActive()) {
|
||||
if (tx.getRollbackOnly()) {
|
||||
tx.rollback();
|
||||
} else {
|
||||
tx.commit();
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException re) {
|
||||
if (tx.isActive()) {
|
||||
tx.rollback();
|
||||
}
|
||||
throw re;
|
||||
} finally {
|
||||
session.close();
|
||||
}
|
||||
}
|
||||
|
||||
protected UserModel validateAndProxyUser(RealmModel realm, UserModel user) {
|
||||
UserModel managed = managedUsers.get(user.getId());
|
||||
if (managed != null) {
|
||||
return managed;
|
||||
}
|
||||
|
||||
UserFederationProvider link = getFederationLink(realm, user);
|
||||
if (link != null) {
|
||||
UserModel validatedProxyUser = link.validateAndProxy(realm, user);
|
||||
if (validatedProxyUser != null) {
|
||||
managedUsers.put(user.getId(), validatedProxyUser);
|
||||
return validatedProxyUser;
|
||||
} else {
|
||||
deleteInvalidUser(realm, user);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addFederatedIdentity(RealmModel realm, UserModel user, FederatedIdentityModel socialLink) {
|
||||
validateUser(realm, user);
|
||||
session.userStorage().addFederatedIdentity(realm, user, socialLink);
|
||||
}
|
||||
|
||||
public void updateFederatedIdentity(RealmModel realm, UserModel federatedUser, FederatedIdentityModel federatedIdentityModel) {
|
||||
session.userStorage().updateFederatedIdentity(realm, federatedUser, federatedIdentityModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeFederatedIdentity(RealmModel realm, UserModel user, String socialProvider) {
|
||||
validateUser(realm, user);
|
||||
if (user == null) throw new IllegalStateException("Federated user no longer valid");
|
||||
return session.userStorage().removeFederatedIdentity(realm, user, socialProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addConsent(RealmModel realm, String userId, UserConsentModel consent) {
|
||||
session.userStorage().addConsent(realm, userId, consent);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserConsentModel getConsentByClient(RealmModel realm, String userId, String clientInternalId) {
|
||||
return session.userStorage().getConsentByClient(realm, userId, clientInternalId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserConsentModel> getConsents(RealmModel realm, String userId) {
|
||||
return session.userStorage().getConsents(realm, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateConsent(RealmModel realm, String userId, UserConsentModel consent) {
|
||||
session.userStorage().updateConsent(realm, userId, consent);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean revokeConsentForClient(RealmModel realm, String userId, String clientInternalId) {
|
||||
return session.userStorage().revokeConsentForClient(realm, userId, clientInternalId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserById(String id, RealmModel realm) {
|
||||
UserModel user = session.userStorage().getUserById(id, realm);
|
||||
if (user != null) {
|
||||
user = validateAndProxyUser(realm, user);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> getGroupMembers(RealmModel realm, final GroupModel group, int firstResult, int maxResults) {
|
||||
// Not very effective. For the page X, it is loading also all previous pages 0..X-1 . Improve if needed...
|
||||
int maxTotal = firstResult + maxResults;
|
||||
List<UserModel> localMembers = query(new PaginatedQuery() {
|
||||
|
||||
@Override
|
||||
public List<UserModel> query(RealmModel realm, int first, int max) {
|
||||
return session.userStorage().getGroupMembers(realm, group, first, max);
|
||||
}
|
||||
|
||||
}, realm, 0, maxTotal);
|
||||
|
||||
Set<UserModel> result = new LinkedHashSet<>(localMembers);
|
||||
|
||||
for (UserFederationProviderModel federation : realm.getUserFederationProviders()) {
|
||||
if (result.size() >= maxTotal) {
|
||||
break;
|
||||
}
|
||||
|
||||
int max = maxTotal - result.size();
|
||||
|
||||
UserFederationProvider fed = getFederationProvider(federation);
|
||||
List<UserModel> current = fed.getGroupMembers(realm, group, 0, max);
|
||||
if (current != null) {
|
||||
result.addAll(current);
|
||||
}
|
||||
}
|
||||
|
||||
if (result.size() <= firstResult) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
int max = Math.min(maxTotal, result.size());
|
||||
return new ArrayList<>(result).subList(firstResult, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> getGroupMembers(RealmModel realm, GroupModel group) {
|
||||
return getGroupMembers(realm, group, -1, -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserByUsername(String username, RealmModel realm) {
|
||||
UserModel user = session.userStorage().getUserByUsername(username.toLowerCase(), realm);
|
||||
if (user != null) {
|
||||
user = validateAndProxyUser(realm, user);
|
||||
if (user != null) return user;
|
||||
}
|
||||
for (UserFederationProviderModel federation : realm.getUserFederationProviders()) {
|
||||
UserFederationProvider fed = getFederationProvider(federation);
|
||||
user = fed.getUserByUsername(realm, username);
|
||||
if (user != null) return user;
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserByEmail(String email, RealmModel realm) {
|
||||
UserModel user = session.userStorage().getUserByEmail(email.toLowerCase(), realm);
|
||||
if (user != null) {
|
||||
user = validateAndProxyUser(realm, user);
|
||||
if (user != null) return user;
|
||||
}
|
||||
for (UserFederationProviderModel federation : realm.getUserFederationProviders()) {
|
||||
UserFederationProvider fed = getFederationProvider(federation);
|
||||
user = fed.getUserByEmail(realm, email);
|
||||
if (user != null) return user;
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserByFederatedIdentity(FederatedIdentityModel socialLink, RealmModel realm) {
|
||||
UserModel user = session.userStorage().getUserByFederatedIdentity(socialLink, realm);
|
||||
if (user != null) {
|
||||
user = validateAndProxyUser(realm, user);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getServiceAccount(ClientModel client) {
|
||||
UserModel user = session.userStorage().getServiceAccount(client);
|
||||
if (user != null) {
|
||||
user = validateAndProxyUser(client.getRealm(), user);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> getUsers(RealmModel realm, boolean includeServiceAccounts) {
|
||||
return getUsers(realm, 0, Integer.MAX_VALUE - 1, includeServiceAccounts);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> getUsers(RealmModel realm) {
|
||||
return getUsers(realm, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults) {
|
||||
return getUsers(realm, firstResult, maxResults, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUsersCount(RealmModel realm) {
|
||||
return session.userStorage().getUsersCount(realm);
|
||||
}
|
||||
|
||||
interface PaginatedQuery {
|
||||
List<UserModel> query(RealmModel realm, int first, int max);
|
||||
}
|
||||
|
||||
protected List<UserModel> query(PaginatedQuery pagedQuery, RealmModel realm, int firstResult, int maxResults) {
|
||||
List<UserModel> results = new LinkedList<UserModel>();
|
||||
if (maxResults == 0) return results;
|
||||
int first = firstResult;
|
||||
int max = maxResults;
|
||||
do {
|
||||
List<UserModel> query = pagedQuery.query(realm, first, max);
|
||||
if (query == null || query.size() == 0) return results;
|
||||
int added = 0;
|
||||
for (UserModel user : query) {
|
||||
user = validateAndProxyUser(realm, user);
|
||||
if (user == null) continue;
|
||||
results.add(user);
|
||||
added++;
|
||||
}
|
||||
if (results.size() == maxResults) return results;
|
||||
if (query.size() < max) return results;
|
||||
first = query.size();
|
||||
max -= added;
|
||||
if (max <= 0) return results;
|
||||
} while (true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults, final boolean includeServiceAccounts) {
|
||||
return query(new PaginatedQuery() {
|
||||
@Override
|
||||
public List<UserModel> query(RealmModel realm, int first, int max) {
|
||||
return session.userStorage().getUsers(realm, first, max, includeServiceAccounts);
|
||||
}
|
||||
}, realm, firstResult, maxResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> searchForUser(String search, RealmModel realm) {
|
||||
return searchForUser(search, realm, 0, Integer.MAX_VALUE - 1);
|
||||
}
|
||||
|
||||
void federationLoad(RealmModel realm, Map<String, String> attributes) {
|
||||
for (UserFederationProviderModel federation : realm.getUserFederationProviders()) {
|
||||
UserFederationProvider fed = getFederationProvider(federation);
|
||||
fed.searchByAttributes(attributes, realm, 30);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> searchForUser(final String search, RealmModel realm, int firstResult, int maxResults) {
|
||||
Map<String, String> attributes = new HashMap<String, String>();
|
||||
int spaceIndex = search.lastIndexOf(' ');
|
||||
if (spaceIndex > -1) {
|
||||
String firstName = search.substring(0, spaceIndex).trim();
|
||||
String lastName = search.substring(spaceIndex).trim();
|
||||
attributes.put(UserModel.FIRST_NAME, firstName);
|
||||
attributes.put(UserModel.LAST_NAME, lastName);
|
||||
} else if (search.indexOf('@') > -1) {
|
||||
attributes.put(UserModel.USERNAME, search.trim().toLowerCase());
|
||||
attributes.put(UserModel.EMAIL, search.trim().toLowerCase());
|
||||
} else {
|
||||
attributes.put(UserModel.LAST_NAME, search.trim());
|
||||
attributes.put(UserModel.USERNAME, search.trim().toLowerCase());
|
||||
}
|
||||
federationLoad(realm, attributes);
|
||||
return query(new PaginatedQuery() {
|
||||
@Override
|
||||
public List<UserModel> query(RealmModel realm, int first, int max) {
|
||||
return session.userStorage().searchForUser(search, realm, first, max);
|
||||
}
|
||||
}, realm, firstResult, maxResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> searchForUser(Map<String, String> attributes, RealmModel realm) {
|
||||
return searchForUser(attributes, realm, 0, Integer.MAX_VALUE - 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> searchForUser(final Map<String, String> attributes, RealmModel realm, int firstResult, int maxResults) {
|
||||
federationLoad(realm, attributes);
|
||||
return query(new PaginatedQuery() {
|
||||
@Override
|
||||
public List<UserModel> query(RealmModel realm, int first, int max) {
|
||||
return session.userStorage().searchForUser(attributes, realm, first, max);
|
||||
}
|
||||
}, realm, firstResult, maxResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> searchForUserByUserAttribute(String attrName, String attrValue, RealmModel realm) {
|
||||
return session.userStorage().searchForUserByUserAttribute(attrName, attrValue, realm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<FederatedIdentityModel> getFederatedIdentities(UserModel user, RealmModel realm) {
|
||||
validateUser(realm, user);
|
||||
if (user == null) throw new IllegalStateException("Federated user no longer valid");
|
||||
return session.userStorage().getFederatedIdentities(user, realm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FederatedIdentityModel getFederatedIdentity(UserModel user, String socialProvider, RealmModel realm) {
|
||||
validateUser(realm, user);
|
||||
if (user == null) throw new IllegalStateException("Federated user no longer valid");
|
||||
return session.userStorage().getFederatedIdentity(user, socialProvider, realm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void grantToAllUsers(RealmModel realm, RoleModel role) {
|
||||
// not federation-aware for now
|
||||
session.userStorage().grantToAllUsers(realm, role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm) {
|
||||
for (UserFederationProviderModel federation : realm.getUserFederationProviders()) {
|
||||
UserFederationProvider fed = getFederationProvider(federation);
|
||||
fed.preRemove(realm);
|
||||
}
|
||||
session.userStorage().preRemove(realm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, UserFederationProviderModel model) {
|
||||
session.userStorage().preRemove(realm, model);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, GroupModel group) {
|
||||
for (UserFederationProviderModel federation : realm.getUserFederationProviders()) {
|
||||
UserFederationProvider fed = getFederationProvider(federation);
|
||||
fed.preRemove(realm, group);
|
||||
}
|
||||
session.userStorage().preRemove(realm, group);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, RoleModel role) {
|
||||
for (UserFederationProviderModel federation : realm.getUserFederationProviders()) {
|
||||
UserFederationProvider fed = getFederationProvider(federation);
|
||||
fed.preRemove(realm, role);
|
||||
}
|
||||
session.userStorage().preRemove(realm, role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, ClientModel client) {
|
||||
session.userStorage().preRemove(realm, client);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(ProtocolMapperModel protocolMapper) {
|
||||
session.userStorage().preRemove(protocolMapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, ComponentModel component) {
|
||||
session.userStorage().preRemove(realm, component);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
/*
|
||||
* 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.models;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class UserFederationMapperModel implements Serializable {
|
||||
|
||||
protected String id;
|
||||
protected String name;
|
||||
|
||||
// Refers to DB ID of federation provider
|
||||
protected String federationProviderId;
|
||||
|
||||
// Refers to ID of UserFederationMapper implementation ( UserFederationMapperFactory.getId )
|
||||
protected String federationMapperType;
|
||||
|
||||
protected Map<String, String> config;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getFederationProviderId() {
|
||||
return federationProviderId;
|
||||
}
|
||||
|
||||
public void setFederationProviderId(String federationProviderId) {
|
||||
this.federationProviderId = federationProviderId;
|
||||
}
|
||||
|
||||
public String getFederationMapperType() {
|
||||
return federationMapperType;
|
||||
}
|
||||
|
||||
public void setFederationMapperType(String federationMapperType) {
|
||||
this.federationMapperType = federationMapperType;
|
||||
}
|
||||
|
||||
public Map<String, String> getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public void setConfig(Map<String, String> config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
UserFederationMapperModel that = (UserFederationMapperModel) o;
|
||||
|
||||
if (!id.equals(that.id)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringBuilder(" { name=" + name)
|
||||
.append(", federationMapperType=" + federationMapperType)
|
||||
.append(", config=" + config)
|
||||
.append(" } ").toString();
|
||||
}
|
||||
}
|
|
@ -1,196 +0,0 @@
|
|||
/*
|
||||
* 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.models;
|
||||
|
||||
import org.keycloak.credential.CredentialInputUpdater;
|
||||
import org.keycloak.credential.CredentialInputValidator;
|
||||
import org.keycloak.provider.Provider;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* SPI for plugging in federation storage. This class is instantiated once per session/request and is closed after
|
||||
* the session/request is finished.
|
||||
*
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
@Deprecated
|
||||
public interface UserFederationProvider extends Provider, CredentialInputValidator, CredentialInputUpdater {
|
||||
|
||||
public static final String USERNAME = UserModel.USERNAME;
|
||||
public static final String EMAIL = UserModel.EMAIL;
|
||||
public static final String FIRST_NAME = UserModel.FIRST_NAME;
|
||||
public static final String LAST_NAME = UserModel.LAST_NAME;
|
||||
|
||||
/**
|
||||
* Optional type that can be by implementations to describe edit mode of federation storage
|
||||
*
|
||||
*/
|
||||
enum EditMode {
|
||||
/**
|
||||
* federation storage is read-only
|
||||
*/
|
||||
READ_ONLY,
|
||||
/**
|
||||
* federation storage is writable
|
||||
*
|
||||
*/
|
||||
WRITABLE,
|
||||
/**
|
||||
* updates to user are stored locally and not synced with federation storage.
|
||||
*
|
||||
*/
|
||||
UNSYNCED
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gives the provider an option to validate if user still exists in federation backend and then proxy UserModel loaded from local storage.
|
||||
* This method is called whenever a UserModel is pulled from Keycloak local storage.
|
||||
* For example, the LDAP provider proxies the UserModel and does on-demand synchronization with
|
||||
* LDAP whenever UserModel update methods are invoked. It also overrides UserModel.updateCredential for the
|
||||
* credential types it supports
|
||||
*
|
||||
* @param realm
|
||||
* @param local
|
||||
* @return null if user is no longer valid or proxy object otherwise
|
||||
*/
|
||||
UserModel validateAndProxy(RealmModel realm, UserModel local);
|
||||
|
||||
/**
|
||||
* Should user registrations be synchronized with this provider?
|
||||
* FYI, only one provider will be chosen (by priority) to have this synchronization
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean synchronizeRegistrations();
|
||||
|
||||
/**
|
||||
* Called if this federation provider has priority and supports synchronized registrations.
|
||||
*
|
||||
* @param realm
|
||||
* @param user
|
||||
* @return
|
||||
*/
|
||||
UserModel register(RealmModel realm, UserModel user);
|
||||
boolean removeUser(RealmModel realm, UserModel user);
|
||||
|
||||
/**
|
||||
* Keycloak will search for user in local storage first. If it can't find the UserModel is local storage,
|
||||
* it will call this method. You are required to import the returned UserModel into local storage.
|
||||
*
|
||||
* @param realm
|
||||
* @param username
|
||||
* @return
|
||||
*/
|
||||
UserModel getUserByUsername(RealmModel realm, String username);
|
||||
|
||||
/**
|
||||
* Keycloak will search for user in local storage first. If it can't find the UserModel is local storage,
|
||||
* it will call this method. You are required to import the returned UserModel into local storage.
|
||||
*
|
||||
* @param realm
|
||||
* @param email
|
||||
* @return
|
||||
*/
|
||||
UserModel getUserByEmail(RealmModel realm, String email);
|
||||
|
||||
/**
|
||||
* Keycloak does not search in local storage first before calling this method. The implementation must check
|
||||
* to see if user is already in local storage (KeycloakSession.userStorage()) before doing an import.
|
||||
* Currently only attributes USERNAME, EMAIL, FIRST_NAME and LAST_NAME will be used.
|
||||
*
|
||||
* @param attributes
|
||||
* @param realm
|
||||
* @return
|
||||
*/
|
||||
List<UserModel> searchByAttributes(Map<String, String> attributes, RealmModel realm, int maxResults);
|
||||
|
||||
/**
|
||||
* Return group members from federation storage. Useful if info about group memberships is stored in the federation storage.
|
||||
* Return empty list if your federation provider doesn't support storing user-group memberships
|
||||
*
|
||||
* @param realm
|
||||
* @param group
|
||||
* @param firstResult
|
||||
* @param maxResults
|
||||
* @return
|
||||
*/
|
||||
List<UserModel> getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults);
|
||||
|
||||
/**
|
||||
* called whenever a Realm is removed
|
||||
*
|
||||
* @param realm
|
||||
*/
|
||||
void preRemove(RealmModel realm);
|
||||
|
||||
/**
|
||||
* called before a role is removed.
|
||||
*
|
||||
* @param realm
|
||||
* @param role
|
||||
*/
|
||||
void preRemove(RealmModel realm, RoleModel role);
|
||||
|
||||
/**
|
||||
* called before a role is removed.
|
||||
*
|
||||
* @param realm
|
||||
* @param group
|
||||
*/
|
||||
void preRemove(RealmModel realm, GroupModel group);
|
||||
|
||||
/**
|
||||
* Is the Keycloak UserModel still valid and/or existing in federated storage? Keycloak may call this method
|
||||
* in various user operations. The local storage may be deleted if this method returns false.
|
||||
*
|
||||
* @param realm
|
||||
* @param local
|
||||
* @return
|
||||
*/
|
||||
boolean isValid(RealmModel realm, UserModel local);
|
||||
|
||||
/**
|
||||
* What UserCredentialModel types should be handled by this provider? This is called in scenarios when we don't know user,
|
||||
* who is going to authenticate (For example Kerberos authentication).
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Set<String> getSupportedCredentialTypes();
|
||||
|
||||
/**
|
||||
* Validate credentials of unknown user. The authenticated user is recognized based on provided credentials and returned back in CredentialValidationOutput
|
||||
* @param realm
|
||||
* @param credential
|
||||
* @return
|
||||
*/
|
||||
CredentialValidationOutput validCredentials(RealmModel realm, UserCredentialModel credential);
|
||||
|
||||
/**
|
||||
* This method is called at the end of requests.
|
||||
*
|
||||
*/
|
||||
void close();
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
/*
|
||||
* 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.models;
|
||||
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface UserFederationProviderFactory extends ProviderFactory<UserFederationProvider> {
|
||||
/**
|
||||
* called per Keycloak transaction.
|
||||
*
|
||||
* @param session
|
||||
* @param model
|
||||
* @return
|
||||
*/
|
||||
UserFederationProvider getInstance(KeycloakSession session, UserFederationProviderModel model);
|
||||
|
||||
/**
|
||||
* Config options to display in generic admin console page for federation
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Set<String> getConfigurationOptions();
|
||||
|
||||
/**
|
||||
* This is the name of the provider and will be showed in the admin console as an option.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
String getId();
|
||||
|
||||
/**
|
||||
* Sync all users from the provider storage to Keycloak storage. Alternatively can update existing users or remove keycloak users, which are no longer
|
||||
* available in federation storage (depends on the implementation)
|
||||
*
|
||||
* @param sessionFactory
|
||||
* @param realmId
|
||||
* @param model
|
||||
* @return result with count of added/updated/removed users
|
||||
*/
|
||||
UserFederationSyncResult syncAllUsers(KeycloakSessionFactory sessionFactory, String realmId, UserFederationProviderModel model);
|
||||
|
||||
/**
|
||||
* Sync just changed (added / updated / removed) users from the provider storage to Keycloak storage. This is useful in case
|
||||
* that your storage supports "changelogs" (Tracking what users changed since specified date). It's implementation specific to
|
||||
* decide what exactly will be changed
|
||||
*
|
||||
* @param sessionFactory
|
||||
* @param realmId
|
||||
* @param model
|
||||
* @param lastSync
|
||||
*/
|
||||
UserFederationSyncResult syncChangedUsers(KeycloakSessionFactory sessionFactory, String realmId, UserFederationProviderModel model, Date lastSync);
|
||||
|
||||
/**
|
||||
* This method is never called and is only an artifact of ProviderFactory. Returning null with no implementation is recommended.
|
||||
* @param session
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
UserFederationProvider create(KeycloakSession session);
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
/*
|
||||
* 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.models;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Stored configuration of a User Federation provider instance.
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
* @author <a href="mailto:bburke@redhat.com">Bill Burke</a>
|
||||
*/
|
||||
public class UserFederationProviderModel implements Serializable {
|
||||
|
||||
private String id;
|
||||
private String providerName;
|
||||
private Map<String, String> config = new HashMap<String, String>();
|
||||
private int priority;
|
||||
private String displayName;
|
||||
private int fullSyncPeriod = -1; // In seconds. -1 means that periodic full sync is disabled
|
||||
private int changedSyncPeriod = -1; // In seconds. -1 means that periodic changed sync is disabled
|
||||
private int lastSync; // Date when last sync was done for this provider
|
||||
|
||||
public UserFederationProviderModel() {}
|
||||
|
||||
public UserFederationProviderModel(String id, String providerName, Map<String, String> config, int priority, String displayName, int fullSyncPeriod, int changedSyncPeriod, int lastSync) {
|
||||
this.id = id;
|
||||
this.providerName = providerName;
|
||||
if (config != null) {
|
||||
this.config.putAll(config);
|
||||
}
|
||||
this.priority = priority;
|
||||
this.displayName = displayName;
|
||||
this.fullSyncPeriod = fullSyncPeriod;
|
||||
this.changedSyncPeriod = changedSyncPeriod;
|
||||
this.lastSync = lastSync;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getProviderName() {
|
||||
return providerName;
|
||||
}
|
||||
|
||||
public void setProviderName(String providerName) {
|
||||
this.providerName = providerName;
|
||||
}
|
||||
|
||||
public Map<String, String> getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public void setConfig(Map<String, String> config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public int getPriority() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
public void setPriority(int priority) {
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public void setDisplayName(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public int getFullSyncPeriod() {
|
||||
return fullSyncPeriod;
|
||||
}
|
||||
|
||||
public void setFullSyncPeriod(int fullSyncPeriod) {
|
||||
this.fullSyncPeriod = fullSyncPeriod;
|
||||
}
|
||||
|
||||
public int getChangedSyncPeriod() {
|
||||
return changedSyncPeriod;
|
||||
}
|
||||
|
||||
public void setChangedSyncPeriod(int changedSyncPeriod) {
|
||||
this.changedSyncPeriod = changedSyncPeriod;
|
||||
}
|
||||
|
||||
public int getLastSync() {
|
||||
return lastSync;
|
||||
}
|
||||
|
||||
public void setLastSync(int lastSync) {
|
||||
this.lastSync = lastSync;
|
||||
}
|
||||
}
|
|
@ -1,124 +0,0 @@
|
|||
/*
|
||||
* 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.models;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class UserFederationSyncResult {
|
||||
|
||||
private boolean ignored;
|
||||
|
||||
private int added;
|
||||
private int updated;
|
||||
private int removed;
|
||||
private int failed;
|
||||
|
||||
public boolean isIgnored() {
|
||||
return ignored;
|
||||
}
|
||||
|
||||
public void setIgnored(boolean ignored) {
|
||||
this.ignored = ignored;
|
||||
}
|
||||
|
||||
public int getAdded() {
|
||||
return added;
|
||||
}
|
||||
|
||||
public void setAdded(int added) {
|
||||
this.added = added;
|
||||
}
|
||||
|
||||
public int getUpdated() {
|
||||
return updated;
|
||||
}
|
||||
|
||||
public void setUpdated(int updated) {
|
||||
this.updated = updated;
|
||||
}
|
||||
|
||||
public int getRemoved() {
|
||||
return removed;
|
||||
}
|
||||
|
||||
public void setRemoved(int removed) {
|
||||
this.removed = removed;
|
||||
}
|
||||
|
||||
public int getFailed() {
|
||||
return failed;
|
||||
}
|
||||
|
||||
public void setFailed(int failed) {
|
||||
this.failed = failed;
|
||||
}
|
||||
|
||||
public void increaseAdded() {
|
||||
added++;
|
||||
}
|
||||
|
||||
public void increaseUpdated() {
|
||||
updated++;
|
||||
}
|
||||
|
||||
public void increaseRemoved() {
|
||||
removed++;
|
||||
}
|
||||
|
||||
public void increaseFailed() {
|
||||
failed++;
|
||||
}
|
||||
|
||||
public void add(UserFederationSyncResult other) {
|
||||
added += other.added;
|
||||
updated += other.updated;
|
||||
removed += other.removed;
|
||||
failed += other.failed;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
if (ignored) {
|
||||
return "Synchronization ignored as it's already in progress";
|
||||
} else {
|
||||
String status = String.format("%d imported users, %d updated users", added, updated);
|
||||
if (removed > 0) {
|
||||
status += String.format(", %d removed users", removed);
|
||||
}
|
||||
if (failed != 0) {
|
||||
status += String.format(", %d users failed sync! See server log for more details", failed);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("UserFederationSyncResult [ %s ]", getStatus());
|
||||
}
|
||||
|
||||
public static UserFederationSyncResult empty() {
|
||||
return new UserFederationSyncResult();
|
||||
}
|
||||
|
||||
public static UserFederationSyncResult ignored() {
|
||||
UserFederationSyncResult result = new UserFederationSyncResult();
|
||||
result.setIgnored(true);
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -67,8 +67,6 @@ public interface UserProvider extends Provider,
|
|||
UserModel addUser(RealmModel realm, String id, String username, boolean addDefaultRoles, boolean addDefaultRequiredActions);
|
||||
void preRemove(RealmModel realm);
|
||||
|
||||
void preRemove(RealmModel realm, UserFederationProviderModel link);
|
||||
|
||||
void preRemove(RealmModel realm, RoleModel role);
|
||||
void preRemove(RealmModel realm, GroupModel group);
|
||||
|
||||
|
|
|
@ -14,27 +14,16 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.examples.federation.properties;
|
||||
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.utils.UserModelDelegate;
|
||||
package org.keycloak.storage;
|
||||
|
||||
/**
|
||||
* Readonly proxy for a UserModel that prevents passwords from being updated.
|
||||
* Thrown when UserStorageProvider UserModel adapter is read-only
|
||||
*
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class ReadonlyUserModelProxy extends UserModelDelegate {
|
||||
|
||||
public ReadonlyUserModelProxy(UserModel delegate) {
|
||||
super(delegate);
|
||||
public class ReadOnlyException extends RuntimeException {
|
||||
public ReadOnlyException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUsername(String username) {
|
||||
throw new IllegalStateException("Username is readonly");
|
||||
}
|
||||
|
||||
}
|
|
@ -27,6 +27,7 @@ import org.keycloak.models.RoleModel;
|
|||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.utils.DefaultRoles;
|
||||
import org.keycloak.models.utils.RoleUtils;
|
||||
import org.keycloak.storage.ReadOnlyException;
|
||||
import org.keycloak.storage.StorageId;
|
||||
|
||||
import java.util.Collections;
|
||||
|
@ -49,11 +50,6 @@ import java.util.Set;
|
|||
* @version $Revision: 1 $
|
||||
*/
|
||||
public abstract class AbstractUserAdapter implements UserModel {
|
||||
public static class ReadOnlyException extends RuntimeException {
|
||||
public ReadOnlyException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
protected KeycloakSession session;
|
||||
protected RealmModel realm;
|
||||
protected ComponentModel storageProviderModel;
|
||||
|
|
|
@ -23,7 +23,6 @@ import org.keycloak.models.GroupModel;
|
|||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.provider.Provider;
|
||||
|
||||
|
@ -47,8 +46,6 @@ public interface UserFederatedStorageProvider extends Provider,
|
|||
|
||||
void preRemove(RealmModel realm);
|
||||
|
||||
void preRemove(RealmModel realm, UserFederationProviderModel link);
|
||||
|
||||
void preRemove(RealmModel realm, GroupModel group);
|
||||
|
||||
void preRemove(RealmModel realm, RoleModel role);
|
||||
|
|
|
@ -20,9 +20,19 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.UserModel;
|
||||
|
||||
/**
|
||||
* If your UserStorageProvider is importing users into local storage, you can validate that import whenever the
|
||||
* user is queried from local storage.
|
||||
*
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface ImportedUserValidation {
|
||||
UserModel validate(RealmModel realmm, UserModel user);
|
||||
/**
|
||||
* If this method returns null, then the user storage in local storage will be removed
|
||||
*
|
||||
* @param realm
|
||||
* @param user
|
||||
* @return null if user no longer valid
|
||||
*/
|
||||
UserModel validate(RealmModel realm, UserModel user);
|
||||
}
|
||||
|
|
|
@ -30,8 +30,8 @@ import org.keycloak.models.ClientModel;
|
|||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserFederationManager;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserProvider;
|
||||
import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.services.ErrorResponse;
|
||||
|
@ -93,7 +93,7 @@ public class ResourceSetService {
|
|||
if (!resourceServer.getClientId().equals(ownerId)) {
|
||||
RealmModel realm = authorization.getRealm();
|
||||
KeycloakSession keycloakSession = authorization.getKeycloakSession();
|
||||
UserFederationManager users = keycloakSession.users();
|
||||
UserProvider users = keycloakSession.users();
|
||||
UserModel ownerModel = users.getUserById(ownerId, realm);
|
||||
|
||||
if (ownerModel == null) {
|
||||
|
|
|
@ -102,7 +102,7 @@ public class OTPCredentialProvider implements CredentialProvider, CredentialInpu
|
|||
} else {
|
||||
getCredentialStore().updateCredential(realm, user, model);
|
||||
}
|
||||
session.getUserCache().evict(realm, user);
|
||||
session.userCache().evict(realm, user);
|
||||
return true;
|
||||
|
||||
|
||||
|
@ -138,7 +138,7 @@ public class OTPCredentialProvider implements CredentialProvider, CredentialInpu
|
|||
|
||||
}
|
||||
if (disableTOTP || disableHOTP) {
|
||||
session.getUserCache().evict(realm, user);
|
||||
session.userCache().evict(realm, user);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@ import org.keycloak.models.UserCredentialModel;
|
|||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.cache.CachedUserModel;
|
||||
import org.keycloak.models.cache.OnUserCache;
|
||||
import org.keycloak.policy.HashAlgorithmPasswordPolicyProviderFactory;
|
||||
import org.keycloak.policy.PasswordPolicyManagerProvider;
|
||||
import org.keycloak.policy.PolicyError;
|
||||
|
||||
|
@ -99,7 +98,7 @@ public class PasswordCredentialProvider implements CredentialProvider, Credentia
|
|||
newPassword.setCreatedDate(createdDate);
|
||||
hash.encode(cred.getValue(), policy, newPassword);
|
||||
getCredentialStore().createCredential(realm, user, newPassword);
|
||||
session.getUserCache().evict(realm, user);
|
||||
session.userCache().evict(realm, user);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -213,7 +212,7 @@ public class PasswordCredentialProvider implements CredentialProvider, Credentia
|
|||
|
||||
hash.encode(cred.getValue(), policy, password);
|
||||
getCredentialStore().updateCredential(realm, user, password);
|
||||
session.getUserCache().evict(realm, user);
|
||||
session.userCache().evict(realm, user);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -21,19 +21,14 @@ import org.keycloak.models.CredentialValidationOutput;
|
|||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserCredentialManager;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserFederationProvider;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.cache.CachedUserModel;
|
||||
import org.keycloak.models.cache.OnUserCache;
|
||||
import org.keycloak.models.utils.CredentialValidation;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.storage.StorageId;
|
||||
import org.keycloak.storage.UserStorageManager;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
|
@ -120,13 +115,7 @@ public class UserCredentialStoreManager implements UserCredentialManager, OnUser
|
|||
}
|
||||
}
|
||||
} else {
|
||||
// <deprecate>
|
||||
UserFederationProvider link = session.users().getFederationLink(realm, user);
|
||||
if (link != null) {
|
||||
session.users().validateUser(realm, user);
|
||||
validate(realm, user, toValidate, link);
|
||||
} // </deprecate>
|
||||
else if (user.getFederationLink() != null) {
|
||||
if (user.getFederationLink() != null) {
|
||||
UserStorageProvider provider = UserStorageManager.getStorageProvider(session, realm, user.getFederationLink());
|
||||
if (provider != null && provider instanceof CredentialInputValidator) {
|
||||
validate(realm, user, toValidate, ((CredentialInputValidator)provider));
|
||||
|
@ -176,13 +165,7 @@ public class UserCredentialStoreManager implements UserCredentialManager, OnUser
|
|||
}
|
||||
}
|
||||
} else {
|
||||
// <deprecated>
|
||||
UserFederationProvider link = session.users().getFederationLink(realm, user);
|
||||
if (link != null) {
|
||||
if (link.updateCredential(realm, user, input)) return;
|
||||
}
|
||||
// </deprecated>
|
||||
else if (user.getFederationLink() != null) {
|
||||
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;
|
||||
|
@ -209,11 +192,7 @@ public class UserCredentialStoreManager implements UserCredentialManager, OnUser
|
|||
}
|
||||
}
|
||||
} else {
|
||||
UserFederationProvider link = session.users().getFederationLink(realm, user);
|
||||
if (link != null && link.getSupportedCredentialTypes().contains(credentialType)) {
|
||||
link.disableCredentialType(realm, user, credentialType);
|
||||
}
|
||||
else if (user.getFederationLink() != null) {
|
||||
if (user.getFederationLink() != null) {
|
||||
UserStorageProvider provider = UserStorageManager.getStorageProvider(session, realm, user.getFederationLink());
|
||||
if (provider != null && provider instanceof CredentialInputUpdater) {
|
||||
((CredentialInputUpdater)provider).disableCredentialType(realm, user, credentialType);
|
||||
|
@ -243,11 +222,7 @@ public class UserCredentialStoreManager implements UserCredentialManager, OnUser
|
|||
types.addAll(updater.getDisableableCredentialTypes(realm, user));
|
||||
}
|
||||
} else {
|
||||
UserFederationProvider link = session.users().getFederationLink(realm, user);
|
||||
if (link != null) {
|
||||
types.addAll(link.getDisableableCredentialTypes(realm, user));
|
||||
}
|
||||
else if (user.getFederationLink() != null) {
|
||||
if (user.getFederationLink() != null) {
|
||||
UserStorageProvider provider = UserStorageManager.getStorageProvider(session, realm, user.getFederationLink());
|
||||
if (provider != null && provider instanceof CredentialInputUpdater) {
|
||||
types.addAll(((CredentialInputUpdater)provider).getDisableableCredentialTypes(realm, user));
|
||||
|
@ -275,13 +250,7 @@ public class UserCredentialStoreManager implements UserCredentialManager, OnUser
|
|||
}
|
||||
}
|
||||
} else {
|
||||
// <deprecate>
|
||||
UserFederationProvider link = session.users().getFederationLink(realm, user);
|
||||
if (link != null) {
|
||||
if (link.isConfiguredFor(realm, user, type)) return true;
|
||||
}
|
||||
// </deprecate>
|
||||
else if (user.getFederationLink() != null) {
|
||||
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;
|
||||
|
@ -307,16 +276,6 @@ public class UserCredentialStoreManager implements UserCredentialManager, OnUser
|
|||
|
||||
@Override
|
||||
public CredentialValidationOutput authenticate(KeycloakSession session, RealmModel realm, CredentialInput input) {
|
||||
List<UserFederationProviderModel> fedProviderModels = realm.getUserFederationProviders();
|
||||
List<UserFederationProvider> fedProviders = new ArrayList<UserFederationProvider>();
|
||||
for (UserFederationProviderModel fedProviderModel : fedProviderModels) {
|
||||
UserFederationProvider provider = session.users().getFederationProvider(fedProviderModel);
|
||||
if (input instanceof UserCredentialModel && provider != null && provider.supportsCredentialType(input.getType())) {
|
||||
CredentialValidationOutput output = provider.validCredentials(realm, (UserCredentialModel)input);
|
||||
if (output != null) return output;
|
||||
}
|
||||
}
|
||||
|
||||
List<CredentialAuthentication> list = UserStorageManager.getStorageProviders(session, realm, CredentialAuthentication.class);
|
||||
for (CredentialAuthentication auth : list) {
|
||||
if (auth.supportsCredentialAuthenticationFor(input.getType())) {
|
||||
|
|
|
@ -44,8 +44,8 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.RoleContainerModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserConsentModel;
|
||||
import org.keycloak.models.UserFederationManager;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserProvider;
|
||||
import org.keycloak.models.utils.ModelToRepresentation;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.ClientTemplateRepresentation;
|
||||
|
@ -358,7 +358,7 @@ public class ExportUtils {
|
|||
String users = config.get("users");
|
||||
|
||||
if (users != null && !users.isEmpty()) {
|
||||
UserFederationManager userManager = session.users();
|
||||
UserProvider userManager = session.users();
|
||||
List<String> userIds = JsonSerialization.readValue(users, List.class);
|
||||
config.put("users", JsonSerialization.writeValueAsString(userIds.stream().map(userId -> userManager.getUserById(userId, realm).getUsername()).collect(Collectors.toList())));
|
||||
}
|
||||
|
|
|
@ -27,14 +27,12 @@ import org.keycloak.models.KeycloakTransactionManager;
|
|||
import org.keycloak.models.KeyManager;
|
||||
import org.keycloak.models.RealmProvider;
|
||||
import org.keycloak.models.UserCredentialManager;
|
||||
import org.keycloak.models.UserFederationManager;
|
||||
import org.keycloak.models.UserProvider;
|
||||
import org.keycloak.models.UserSessionProvider;
|
||||
import org.keycloak.models.cache.CacheRealmProvider;
|
||||
import org.keycloak.models.cache.UserCache;
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.scripting.ScriptingProvider;
|
||||
import org.keycloak.storage.UserStorageManager;
|
||||
import org.keycloak.storage.federated.UserFederatedStorageProvider;
|
||||
|
||||
|
@ -60,7 +58,6 @@ public class DefaultKeycloakSession implements KeycloakSession {
|
|||
private UserStorageManager userStorageManager;
|
||||
private UserCredentialStoreManager userCredentialStorageManager;
|
||||
private UserSessionProvider sessionProvider;
|
||||
private UserFederationManager federationManager;
|
||||
private UserFederatedStorageProvider userFederatedStorageProvider;
|
||||
private KeycloakContext context;
|
||||
private KeyManager keyManager;
|
||||
|
@ -68,7 +65,6 @@ public class DefaultKeycloakSession implements KeycloakSession {
|
|||
public DefaultKeycloakSession(DefaultKeycloakSessionFactory factory) {
|
||||
this.factory = factory;
|
||||
this.transactionManager = new DefaultKeycloakTransactionManager(this);
|
||||
federationManager = new UserFederationManager(this);
|
||||
context = new DefaultKeycloakContext(this);
|
||||
}
|
||||
|
||||
|
@ -86,17 +82,8 @@ public class DefaultKeycloakSession implements KeycloakSession {
|
|||
}
|
||||
}
|
||||
|
||||
private UserProvider getUserProvider() {
|
||||
UserCache cache = getProvider(UserCache.class);
|
||||
if (cache != null) {
|
||||
return cache;
|
||||
} else {
|
||||
return getProvider(UserProvider.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserCache getUserCache() {
|
||||
public UserCache userCache() {
|
||||
return getProvider(UserCache.class);
|
||||
|
||||
}
|
||||
|
@ -151,17 +138,19 @@ public class DefaultKeycloakSession implements KeycloakSession {
|
|||
}
|
||||
|
||||
@Override
|
||||
public UserCredentialManager userCredentialManager() {
|
||||
if (userCredentialStorageManager == null) userCredentialStorageManager = new UserCredentialStoreManager(this);
|
||||
return userCredentialStorageManager;
|
||||
public UserProvider users() {
|
||||
UserCache cache = getProvider(UserCache.class);
|
||||
if (cache != null) {
|
||||
return cache;
|
||||
} else {
|
||||
return userStorageManager();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserProvider userStorage() {
|
||||
if (userModel == null) {
|
||||
userModel = getUserProvider();
|
||||
}
|
||||
return userModel;
|
||||
public UserCredentialManager userCredentialManager() {
|
||||
if (userCredentialStorageManager == null) userCredentialStorageManager = new UserCredentialStoreManager(this);
|
||||
return userCredentialStorageManager;
|
||||
}
|
||||
|
||||
public <T extends Provider> T getProvider(Class<T> clazz) {
|
||||
|
@ -239,11 +228,6 @@ public class DefaultKeycloakSession implements KeycloakSession {
|
|||
return model;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserFederationManager users() {
|
||||
return federationManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserSessionProvider sessions() {
|
||||
if (sessionProvider == null) {
|
||||
|
|
|
@ -156,7 +156,7 @@ public class ClientManager {
|
|||
logger.debugf("Creating service account user '%s'", username);
|
||||
|
||||
// Don't use federation for service account user
|
||||
UserModel user = realmManager.getSession().userStorage().addUser(client.getRealm(), username);
|
||||
UserModel user = realmManager.getSession().userLocalStorage().addUser(client.getRealm(), username);
|
||||
user.setEnabled(true);
|
||||
user.setEmail(username + "@placeholder.org");
|
||||
user.setServiceAccountClientLink(client.getId());
|
||||
|
|
|
@ -31,7 +31,6 @@ import org.keycloak.models.ProtocolMapperModel;
|
|||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RealmProvider;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionProvider;
|
||||
import org.keycloak.models.session.UserSessionPersisterProvider;
|
||||
|
@ -228,7 +227,6 @@ public class RealmManager implements RealmImporter {
|
|||
}
|
||||
|
||||
public boolean removeRealm(RealmModel realm) {
|
||||
List<UserFederationProviderModel> federationProviders = realm.getUserFederationProviders();
|
||||
|
||||
ClientModel masterAdminClient = realm.getMasterAdminClient();
|
||||
boolean removed = model.removeRealm(realm.getId());
|
||||
|
@ -247,11 +245,13 @@ public class RealmManager implements RealmImporter {
|
|||
sessionsPersister.onRealmRemoved(realm);
|
||||
}
|
||||
|
||||
// Remove all periodic syncs for configured federation providers
|
||||
UsersSyncManager usersSyncManager = new UsersSyncManager();
|
||||
for (final UserFederationProviderModel fedProvider : federationProviders) {
|
||||
usersSyncManager.notifyToRefreshPeriodicSync(session, realm, fedProvider, true);
|
||||
// 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, true);
|
||||
}
|
||||
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
@ -487,13 +487,6 @@ public class RealmManager implements RealmImporter {
|
|||
setupAuthenticationFlows(realm);
|
||||
setupRequiredActions(realm);
|
||||
|
||||
// Refresh periodic sync tasks for configured federationProviders
|
||||
List<UserFederationProviderModel> federationProviders = realm.getUserFederationProviders();
|
||||
UsersSyncManager usersSyncManager = new UsersSyncManager();
|
||||
for (final UserFederationProviderModel fedProvider : federationProviders) {
|
||||
usersSyncManager.notifyToRefreshPeriodicSync(session, realm, fedProvider, false);
|
||||
}
|
||||
|
||||
// Refresh periodic sync tasks for configured storageProviders
|
||||
List<UserStorageProviderModel> storageProviders = realm.getUserStorageProviders();
|
||||
UserStorageSyncManager storageSync = new UserStorageSyncManager();
|
||||
|
|
|
@ -1,327 +0,0 @@
|
|||
/*
|
||||
* 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.managers;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.cluster.ClusterEvent;
|
||||
import org.keycloak.cluster.ClusterListener;
|
||||
import org.keycloak.cluster.ClusterProvider;
|
||||
import org.keycloak.cluster.ExecutionResult;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.KeycloakSessionTask;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserFederationProvider;
|
||||
import org.keycloak.models.UserFederationProviderFactory;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserFederationSyncResult;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.services.ServicesLogger;
|
||||
import org.keycloak.timer.TimerProvider;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class UsersSyncManager {
|
||||
|
||||
private static final String FEDERATION_TASK_KEY = "federation";
|
||||
|
||||
private static final Logger logger = Logger.getLogger(UsersSyncManager.class);
|
||||
|
||||
/**
|
||||
* Check federationProviderModel of all realms and possibly start periodic sync for them
|
||||
*
|
||||
* @param sessionFactory
|
||||
* @param timer
|
||||
*/
|
||||
public void bootstrapPeriodic(final KeycloakSessionFactory sessionFactory, final TimerProvider timer) {
|
||||
KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
|
||||
|
||||
@Override
|
||||
public void run(KeycloakSession session) {
|
||||
List<RealmModel> realms = session.realms().getRealms();
|
||||
for (final RealmModel realm : realms) {
|
||||
List<UserFederationProviderModel> federationProviders = realm.getUserFederationProviders();
|
||||
for (final UserFederationProviderModel fedProvider : federationProviders) {
|
||||
refreshPeriodicSyncForProvider(sessionFactory, timer, fedProvider, realm.getId());
|
||||
}
|
||||
}
|
||||
|
||||
ClusterProvider clusterProvider = session.getProvider(ClusterProvider.class);
|
||||
clusterProvider.registerListener(FEDERATION_TASK_KEY, new UserFederationClusterListener(sessionFactory));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private class Holder {
|
||||
ExecutionResult<UserFederationSyncResult> result;
|
||||
}
|
||||
|
||||
public UserFederationSyncResult syncAllUsers(final KeycloakSessionFactory sessionFactory, final String realmId, final UserFederationProviderModel fedProvider) {
|
||||
final Holder holder = new Holder();
|
||||
|
||||
// Ensure not executed concurrently on this or any other cluster node
|
||||
KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
|
||||
|
||||
@Override
|
||||
public void run(KeycloakSession session) {
|
||||
ClusterProvider clusterProvider = session.getProvider(ClusterProvider.class);
|
||||
// shared key for "full" and "changed" . Improve if needed
|
||||
String taskKey = fedProvider.getId() + "::sync";
|
||||
|
||||
// 30 seconds minimal timeout for now
|
||||
int timeout = Math.max(30, fedProvider.getFullSyncPeriod());
|
||||
holder.result = clusterProvider.executeIfNotExecuted(taskKey, timeout, new Callable<UserFederationSyncResult>() {
|
||||
|
||||
@Override
|
||||
public UserFederationSyncResult call() throws Exception {
|
||||
final UserFederationProviderFactory fedProviderFactory = (UserFederationProviderFactory) sessionFactory.getProviderFactory(UserFederationProvider.class, fedProvider.getProviderName());
|
||||
updateLastSyncInterval(sessionFactory, fedProvider, realmId);
|
||||
return fedProviderFactory.syncAllUsers(sessionFactory, realmId, fedProvider);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
if (holder.result == null || !holder.result.isExecuted()) {
|
||||
logger.debugf("syncAllUsers for federation provider %s was ignored as it's already in progress", fedProvider.getDisplayName());
|
||||
return UserFederationSyncResult.ignored();
|
||||
} else {
|
||||
return holder.result.getResult();
|
||||
}
|
||||
}
|
||||
|
||||
public UserFederationSyncResult syncChangedUsers(final KeycloakSessionFactory sessionFactory, final String realmId, final UserFederationProviderModel fedProvider) {
|
||||
final Holder holder = new Holder();
|
||||
|
||||
// Ensure not executed concurrently on this or any other cluster node
|
||||
KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
|
||||
|
||||
@Override
|
||||
public void run(KeycloakSession session) {
|
||||
ClusterProvider clusterProvider = session.getProvider(ClusterProvider.class);
|
||||
// shared key for "full" and "changed" . Improve if needed
|
||||
String taskKey = fedProvider.getId() + "::sync";
|
||||
|
||||
// 30 seconds minimal timeout for now
|
||||
int timeout = Math.max(30, fedProvider.getChangedSyncPeriod());
|
||||
holder.result = clusterProvider.executeIfNotExecuted(taskKey, timeout, new Callable<UserFederationSyncResult>() {
|
||||
|
||||
@Override
|
||||
public UserFederationSyncResult call() throws Exception {
|
||||
final UserFederationProviderFactory fedProviderFactory = (UserFederationProviderFactory) sessionFactory.getProviderFactory(UserFederationProvider.class, fedProvider.getProviderName());
|
||||
|
||||
// See when we did last sync.
|
||||
int oldLastSync = fedProvider.getLastSync();
|
||||
updateLastSyncInterval(sessionFactory, fedProvider, realmId);
|
||||
return fedProviderFactory.syncChangedUsers(sessionFactory, realmId, fedProvider, Time.toDate(oldLastSync));
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
if (holder.result == null || !holder.result.isExecuted()) {
|
||||
logger.debugf("syncChangedUsers for federation provider %s was ignored as it's already in progress", fedProvider.getDisplayName());
|
||||
return UserFederationSyncResult.ignored();
|
||||
} else {
|
||||
return holder.result.getResult();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Ensure all cluster nodes are notified
|
||||
public void notifyToRefreshPeriodicSync(KeycloakSession session, RealmModel realm, UserFederationProviderModel federationProvider, boolean removed) {
|
||||
FederationProviderClusterEvent event = FederationProviderClusterEvent.createEvent(removed, realm.getId(), federationProvider);
|
||||
session.getProvider(ClusterProvider.class).notify(FEDERATION_TASK_KEY, event, false);
|
||||
}
|
||||
|
||||
|
||||
// Executed once it receives notification that some UserFederationProvider was created or updated
|
||||
protected void refreshPeriodicSyncForProvider(final KeycloakSessionFactory sessionFactory, TimerProvider timer, final UserFederationProviderModel fedProvider, final String realmId) {
|
||||
logger.debugf("Going to refresh periodic sync for provider '%s' . Full sync period: %d , changed users sync period: %d",
|
||||
fedProvider.getDisplayName(), fedProvider.getFullSyncPeriod(), fedProvider.getChangedSyncPeriod());
|
||||
|
||||
if (fedProvider.getFullSyncPeriod() > 0) {
|
||||
// We want periodic full sync for this provider
|
||||
timer.schedule(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
boolean shouldPerformSync = shouldPerformNewPeriodicSync(fedProvider.getLastSync(), fedProvider.getChangedSyncPeriod());
|
||||
if (shouldPerformSync) {
|
||||
syncAllUsers(sessionFactory, realmId, fedProvider);
|
||||
} else {
|
||||
logger.debugf("Ignored periodic full sync with federation provider %s due small time since last sync", fedProvider.getDisplayName());
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
ServicesLogger.LOGGER.errorDuringFullUserSync(t);
|
||||
}
|
||||
}
|
||||
|
||||
}, fedProvider.getFullSyncPeriod() * 1000, fedProvider.getId() + "-FULL");
|
||||
} else {
|
||||
timer.cancelTask(fedProvider.getId() + "-FULL");
|
||||
}
|
||||
|
||||
if (fedProvider.getChangedSyncPeriod() > 0) {
|
||||
// We want periodic sync of just changed users for this provider
|
||||
timer.schedule(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
boolean shouldPerformSync = shouldPerformNewPeriodicSync(fedProvider.getLastSync(), fedProvider.getChangedSyncPeriod());
|
||||
if (shouldPerformSync) {
|
||||
syncChangedUsers(sessionFactory, realmId, fedProvider);
|
||||
} else {
|
||||
logger.debugf("Ignored periodic changed-users sync with federation provider %s due small time since last sync", fedProvider.getDisplayName());
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
ServicesLogger.LOGGER.errorDuringChangedUserSync(t);
|
||||
}
|
||||
}
|
||||
|
||||
}, fedProvider.getChangedSyncPeriod() * 1000, fedProvider.getId() + "-CHANGED");
|
||||
|
||||
} else {
|
||||
timer.cancelTask(fedProvider.getId() + "-CHANGED");
|
||||
}
|
||||
}
|
||||
|
||||
// Skip syncing if there is short time since last sync time.
|
||||
private boolean shouldPerformNewPeriodicSync(int lastSyncTime, int period) {
|
||||
if (lastSyncTime <= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int currentTime = Time.currentTime();
|
||||
int timeSinceLastSync = currentTime - lastSyncTime;
|
||||
|
||||
return (timeSinceLastSync * 2 > period);
|
||||
}
|
||||
|
||||
// Executed once it receives notification that some UserFederationProvider was removed
|
||||
protected void removePeriodicSyncForProvider(TimerProvider timer, UserFederationProviderModel fedProvider) {
|
||||
logger.debugf("Removing periodic sync for provider %s", fedProvider.getDisplayName());
|
||||
timer.cancelTask(fedProvider.getId() + "-FULL");
|
||||
timer.cancelTask(fedProvider.getId() + "-CHANGED");
|
||||
}
|
||||
|
||||
// Update interval of last sync for given UserFederationProviderModel. Do it in separate transaction
|
||||
private void updateLastSyncInterval(final KeycloakSessionFactory sessionFactory, final UserFederationProviderModel fedProvider, final String realmId) {
|
||||
KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
|
||||
|
||||
@Override
|
||||
public void run(KeycloakSession session) {
|
||||
RealmModel persistentRealm = session.realms().getRealm(realmId);
|
||||
List<UserFederationProviderModel> persistentFedProviders = persistentRealm.getUserFederationProviders();
|
||||
for (UserFederationProviderModel persistentFedProvider : persistentFedProviders) {
|
||||
if (fedProvider.getId().equals(persistentFedProvider.getId())) {
|
||||
// Update persistent provider in DB
|
||||
int lastSync = Time.currentTime();
|
||||
persistentFedProvider.setLastSync(lastSync);
|
||||
persistentRealm.updateUserFederationProvider(persistentFedProvider);
|
||||
|
||||
// Update "cached" reference
|
||||
fedProvider.setLastSync(lastSync);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private class UserFederationClusterListener implements ClusterListener {
|
||||
|
||||
private final KeycloakSessionFactory sessionFactory;
|
||||
|
||||
public UserFederationClusterListener(KeycloakSessionFactory sessionFactory) {
|
||||
this.sessionFactory = sessionFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eventReceived(ClusterEvent event) {
|
||||
final FederationProviderClusterEvent fedEvent = (FederationProviderClusterEvent) event;
|
||||
KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
|
||||
|
||||
@Override
|
||||
public void run(KeycloakSession session) {
|
||||
TimerProvider timer = session.getProvider(TimerProvider.class);
|
||||
if (fedEvent.isRemoved()) {
|
||||
removePeriodicSyncForProvider(timer, fedEvent.getFederationProvider());
|
||||
} else {
|
||||
refreshPeriodicSyncForProvider(sessionFactory, timer, fedEvent.getFederationProvider(), fedEvent.getRealmId());
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Send to cluster during each update or remove of federationProvider, so all nodes can update sync periods
|
||||
public static class FederationProviderClusterEvent implements ClusterEvent {
|
||||
|
||||
private boolean removed;
|
||||
private String realmId;
|
||||
private UserFederationProviderModel federationProvider;
|
||||
|
||||
public boolean isRemoved() {
|
||||
return removed;
|
||||
}
|
||||
|
||||
public void setRemoved(boolean removed) {
|
||||
this.removed = removed;
|
||||
}
|
||||
|
||||
public String getRealmId() {
|
||||
return realmId;
|
||||
}
|
||||
|
||||
public void setRealmId(String realmId) {
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
public UserFederationProviderModel getFederationProvider() {
|
||||
return federationProvider;
|
||||
}
|
||||
|
||||
public void setFederationProvider(UserFederationProviderModel federationProvider) {
|
||||
this.federationProvider = federationProvider;
|
||||
}
|
||||
|
||||
public static FederationProviderClusterEvent createEvent(boolean removed, String realmId, UserFederationProviderModel fedProvider) {
|
||||
FederationProviderClusterEvent notification = new FederationProviderClusterEvent();
|
||||
notification.setRemoved(removed);
|
||||
notification.setRealmId(realmId);
|
||||
notification.setFederationProvider(fedProvider);
|
||||
return notification;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -46,7 +46,6 @@ 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;
|
||||
import org.keycloak.services.scheduled.ClearExpiredUserSessions;
|
||||
|
@ -323,7 +322,6 @@ public class KeycloakApplication extends Application {
|
|||
TimerProvider timer = session.getProvider(TimerProvider.class);
|
||||
timer.schedule(new ClusterAwareScheduledTaskRunner(sessionFactory, new ClearExpiredEvents(), interval), interval, "ClearExpiredEvents");
|
||||
timer.schedule(new ScheduledTaskRunner(sessionFactory, new ClearExpiredUserSessions()), interval, "ClearExpiredUserSessions");
|
||||
new UsersSyncManager().bootstrapPeriodic(sessionFactory, timer);
|
||||
new UserStorageSyncManager().bootstrapPeriodic(sessionFactory, timer);
|
||||
} finally {
|
||||
session.close();
|
||||
|
|
|
@ -42,7 +42,6 @@ import org.keycloak.models.GroupModel;
|
|||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ModelDuplicateException;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.models.cache.CacheRealmProvider;
|
||||
import org.keycloak.models.cache.UserCache;
|
||||
|
@ -66,8 +65,9 @@ import org.keycloak.services.managers.AuthenticationManager;
|
|||
import org.keycloak.services.managers.LDAPConnectionTestManager;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.services.managers.ResourceAdminManager;
|
||||
import org.keycloak.services.managers.UsersSyncManager;
|
||||
import org.keycloak.services.managers.UserStorageSyncManager;
|
||||
import org.keycloak.services.resources.admin.RealmAuth.Resource;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
|
@ -304,9 +304,9 @@ public class RealmAdminResource {
|
|||
RepresentationToModel.updateRealm(rep, realm, session);
|
||||
|
||||
// Refresh periodic sync tasks for configured federationProviders
|
||||
List<UserFederationProviderModel> federationProviders = realm.getUserFederationProviders();
|
||||
UsersSyncManager usersSyncManager = new UsersSyncManager();
|
||||
for (final UserFederationProviderModel fedProvider : federationProviders) {
|
||||
List<UserStorageProviderModel> federationProviders = realm.getUserStorageProviders();
|
||||
UserStorageSyncManager usersSyncManager = new UserStorageSyncManager();
|
||||
for (final UserStorageProviderModel fedProvider : federationProviders) {
|
||||
usersSyncManager.notifyToRefreshPeriodicSync(session, realm, fedProvider, false);
|
||||
}
|
||||
|
||||
|
@ -348,14 +348,6 @@ public class RealmAdminResource {
|
|||
return users;
|
||||
}
|
||||
|
||||
@Path("user-federation")
|
||||
public UserFederationProvidersResource userFederation() {
|
||||
UserFederationProvidersResource fed = new UserFederationProvidersResource(realm, auth, adminEvent);
|
||||
ResteasyProviderFactory.getInstance().injectProperties(fed);
|
||||
//resourceContext.initResource(fed);
|
||||
return fed;
|
||||
}
|
||||
|
||||
@Path("user-storage")
|
||||
public UserStorageProviderResource userStorage() {
|
||||
UserStorageProviderResource fed = new UserStorageProviderResource(realm, auth, adminEvent);
|
||||
|
|
|
@ -1,459 +0,0 @@
|
|||
/*
|
||||
* 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.logging.Logger;
|
||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import org.jboss.resteasy.spi.NotFoundException;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.events.admin.ResourceType;
|
||||
import org.keycloak.mappers.FederationConfigValidationException;
|
||||
import org.keycloak.mappers.UserFederationMapper;
|
||||
import org.keycloak.mappers.UserFederationMapperFactory;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.ModelDuplicateException;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserFederationMapperModel;
|
||||
import org.keycloak.models.UserFederationProvider;
|
||||
import org.keycloak.models.UserFederationProviderFactory;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserFederationSyncResult;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.models.utils.ModelToRepresentation;
|
||||
import org.keycloak.models.utils.RepresentationToModel;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.representations.idm.ConfigPropertyRepresentation;
|
||||
import org.keycloak.representations.idm.UserFederationMapperRepresentation;
|
||||
import org.keycloak.representations.idm.UserFederationMapperTypeRepresentation;
|
||||
import org.keycloak.representations.idm.UserFederationProviderRepresentation;
|
||||
import org.keycloak.services.ErrorResponse;
|
||||
import org.keycloak.services.ErrorResponseException;
|
||||
import org.keycloak.services.ServicesLogger;
|
||||
import org.keycloak.services.managers.UsersSyncManager;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.PUT;
|
||||
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.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class UserFederationProviderResource {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(UserFederationProviderResource.class);
|
||||
|
||||
private final KeycloakSession session;
|
||||
private final RealmModel realm;
|
||||
private final RealmAuth auth;
|
||||
private final UserFederationProviderModel federationProviderModel;
|
||||
private final AdminEventBuilder adminEvent;
|
||||
|
||||
@Context
|
||||
private UriInfo uriInfo;
|
||||
|
||||
public UserFederationProviderResource(KeycloakSession session, RealmModel realm, RealmAuth auth, UserFederationProviderModel federationProviderModel, AdminEventBuilder adminEvent) {
|
||||
this.session = session;
|
||||
this.realm = realm;
|
||||
this.auth = auth;
|
||||
this.federationProviderModel = federationProviderModel;
|
||||
this.adminEvent = adminEvent.resource(ResourceType.USER_FEDERATION_PROVIDER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a provider
|
||||
*
|
||||
* @param rep
|
||||
*/
|
||||
@PUT
|
||||
@NoCache
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public Response updateProviderInstance(UserFederationProviderRepresentation rep) {
|
||||
auth.requireManage();
|
||||
|
||||
if (federationProviderModel == null) {
|
||||
throw new NotFoundException("Could not find federation provider");
|
||||
}
|
||||
|
||||
try {
|
||||
String displayName = rep.getDisplayName();
|
||||
if (displayName != null && displayName.trim().equals("")) {
|
||||
displayName = null;
|
||||
}
|
||||
UserFederationProviderModel model = new UserFederationProviderModel(rep.getId(), rep.getProviderName(), rep.getConfig(), rep.getPriority(), displayName,
|
||||
rep.getFullSyncPeriod(), rep.getChangedSyncPeriod(), rep.getLastSync());
|
||||
|
||||
UserFederationProvidersResource.validateFederationProviderConfig(session, auth, realm, model);
|
||||
|
||||
realm.updateUserFederationProvider(model);
|
||||
new UsersSyncManager().notifyToRefreshPeriodicSync(session, realm, model, false);
|
||||
boolean kerberosCredsAdded = UserFederationProvidersResource.checkKerberosCredential(session, realm, model);
|
||||
if (kerberosCredsAdded) {
|
||||
ServicesLogger.LOGGER.addedKerberosToRealmCredentials();
|
||||
}
|
||||
|
||||
adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo).representation(rep).success();
|
||||
return Response.noContent().build();
|
||||
} catch (ModelDuplicateException e) {
|
||||
if (session.getTransactionManager().isActive()) {
|
||||
session.getTransactionManager().setRollbackOnly();
|
||||
}
|
||||
return ErrorResponse.exists("Federation provider exists with same name.");
|
||||
} catch (ModelException me) {
|
||||
if (session.getTransactionManager().isActive()) {
|
||||
session.getTransactionManager().setRollbackOnly();
|
||||
}
|
||||
return ErrorResponse.error("Unable to update federation provider.", Response.Status.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a provider
|
||||
*
|
||||
*/
|
||||
@GET
|
||||
@NoCache
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public UserFederationProviderRepresentation getProviderInstance() {
|
||||
auth.requireView();
|
||||
|
||||
if (federationProviderModel == null) {
|
||||
throw new NotFoundException("Could not find federation provider");
|
||||
}
|
||||
|
||||
return ModelToRepresentation.toRepresentation(this.federationProviderModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a provider
|
||||
*
|
||||
*/
|
||||
@DELETE
|
||||
@NoCache
|
||||
public void deleteProviderInstance() {
|
||||
auth.requireManage();
|
||||
|
||||
if (federationProviderModel == null) {
|
||||
throw new NotFoundException("Could not find federation provider");
|
||||
}
|
||||
|
||||
realm.removeUserFederationProvider(this.federationProviderModel);
|
||||
new UsersSyncManager().notifyToRefreshPeriodicSync(session, realm, this.federationProviderModel, true);
|
||||
|
||||
adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo).success();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger sync of users
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@POST
|
||||
@Path("sync")
|
||||
@NoCache
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public UserFederationSyncResult syncUsers(@QueryParam("action") String action) {
|
||||
auth.requireManage();
|
||||
|
||||
if (federationProviderModel == null) {
|
||||
throw new NotFoundException("Could not find federation provider");
|
||||
}
|
||||
|
||||
logger.debug("Syncing users");
|
||||
|
||||
UsersSyncManager syncManager = new UsersSyncManager();
|
||||
UserFederationSyncResult syncResult;
|
||||
if ("triggerFullSync".equals(action)) {
|
||||
syncResult = syncManager.syncAllUsers(session.getKeycloakSessionFactory(), realm.getId(), this.federationProviderModel);
|
||||
} else if ("triggerChangedUsersSync".equals(action)) {
|
||||
syncResult = syncManager.syncChangedUsers(session.getKeycloakSessionFactory(), realm.getId(), this.federationProviderModel);
|
||||
} 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available user federation mapper types
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@GET
|
||||
@Path("mapper-types")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@NoCache
|
||||
public Map<String, UserFederationMapperTypeRepresentation> getMapperTypes() {
|
||||
auth.requireView();
|
||||
|
||||
if (federationProviderModel == null) {
|
||||
throw new NotFoundException("Could not find federation provider");
|
||||
}
|
||||
|
||||
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
|
||||
Map<String, UserFederationMapperTypeRepresentation> types = new HashMap<>();
|
||||
List<ProviderFactory> factories = sessionFactory.getProviderFactories(UserFederationMapper.class);
|
||||
|
||||
for (ProviderFactory factory : factories) {
|
||||
UserFederationMapperFactory mapperFactory = (UserFederationMapperFactory)factory;
|
||||
if (mapperFactory.getFederationProviderType().equals(this.federationProviderModel.getProviderName())) {
|
||||
|
||||
UserFederationMapperTypeRepresentation rep = new UserFederationMapperTypeRepresentation();
|
||||
rep.setId(mapperFactory.getId());
|
||||
rep.setCategory(mapperFactory.getDisplayCategory());
|
||||
rep.setName(mapperFactory.getDisplayType());
|
||||
rep.setHelpText(mapperFactory.getHelpText());
|
||||
rep.setSyncConfig(mapperFactory.getSyncConfig());
|
||||
List<ProviderConfigProperty> configProperties = mapperFactory.getConfigProperties();
|
||||
for (ProviderConfigProperty prop : configProperties) {
|
||||
ConfigPropertyRepresentation propRep = ModelToRepresentation.toRepresentation(prop);
|
||||
rep.getProperties().add(propRep);
|
||||
}
|
||||
rep.setDefaultConfig(mapperFactory.getDefaultConfig(this.federationProviderModel));
|
||||
|
||||
types.put(rep.getId(), rep);
|
||||
}
|
||||
}
|
||||
return types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get mappers configured for this provider
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@GET
|
||||
@Path("mappers")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@NoCache
|
||||
public List<UserFederationMapperRepresentation> getMappers() {
|
||||
auth.requireView();
|
||||
|
||||
if (federationProviderModel == null) {
|
||||
throw new NotFoundException("Could not find federation provider");
|
||||
}
|
||||
|
||||
List<UserFederationMapperRepresentation> mappers = new LinkedList<>();
|
||||
for (UserFederationMapperModel model : realm.getUserFederationMappersByFederationProvider(this.federationProviderModel.getId())) {
|
||||
mappers.add(ModelToRepresentation.toRepresentation(realm, model));
|
||||
}
|
||||
|
||||
// Sort mappers by category,type,name
|
||||
Collections.sort(mappers, new Comparator<UserFederationMapperRepresentation>() {
|
||||
|
||||
@Override
|
||||
public int compare(UserFederationMapperRepresentation o1, UserFederationMapperRepresentation o2) {
|
||||
UserFederationMapperFactory factory1 = (UserFederationMapperFactory) session.getKeycloakSessionFactory().getProviderFactory(UserFederationMapper.class, o1.getFederationMapperType());
|
||||
UserFederationMapperFactory factory2 = (UserFederationMapperFactory) session.getKeycloakSessionFactory().getProviderFactory(UserFederationMapper.class, o2.getFederationMapperType());
|
||||
|
||||
int compare = factory1.getDisplayCategory().compareTo(factory2.getDisplayCategory());
|
||||
if (compare != 0) return compare;
|
||||
|
||||
compare = factory1.getDisplayType().compareTo(factory2.getDisplayType());
|
||||
if (compare != 0) return compare;
|
||||
|
||||
compare = o1.getName().compareTo(o2.getName());
|
||||
return compare;
|
||||
}
|
||||
});
|
||||
|
||||
return mappers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a mapper
|
||||
*
|
||||
* @param mapper
|
||||
* @return
|
||||
*/
|
||||
@POST
|
||||
@Path("mappers")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public Response addMapper(UserFederationMapperRepresentation mapper) {
|
||||
auth.requireManage();
|
||||
|
||||
if (federationProviderModel == null) {
|
||||
throw new NotFoundException("Could not find federation provider");
|
||||
}
|
||||
|
||||
UserFederationMapperModel model = RepresentationToModel.toModel(realm, mapper);
|
||||
|
||||
validateModel(model);
|
||||
|
||||
model = realm.addUserFederationMapper(model);
|
||||
|
||||
adminEvent.operation(OperationType.CREATE).resource(ResourceType.USER_FEDERATION_MAPPER).resourcePath(uriInfo, model.getId())
|
||||
.representation(mapper).success();
|
||||
|
||||
return Response.created(uriInfo.getAbsolutePathBuilder().path(model.getId()).build()).build();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a mapper
|
||||
*
|
||||
* @param id Mapper id
|
||||
* @return
|
||||
*/
|
||||
@GET
|
||||
@NoCache
|
||||
@Path("mappers/{id}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public UserFederationMapperRepresentation getMapperById(@PathParam("id") String id) {
|
||||
auth.requireView();
|
||||
|
||||
if (federationProviderModel == null) {
|
||||
throw new NotFoundException("Could not find federation provider");
|
||||
}
|
||||
|
||||
UserFederationMapperModel model = realm.getUserFederationMapperById(id);
|
||||
if (model == null) throw new NotFoundException("Model not found");
|
||||
return ModelToRepresentation.toRepresentation(realm, model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a mapper
|
||||
*
|
||||
* @param id Mapper id
|
||||
* @param rep
|
||||
*/
|
||||
@PUT
|
||||
@NoCache
|
||||
@Path("mappers/{id}")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public void update(@PathParam("id") String id, UserFederationMapperRepresentation rep) {
|
||||
auth.requireManage();
|
||||
|
||||
if (federationProviderModel == null) {
|
||||
throw new NotFoundException("Could not find federation provider");
|
||||
}
|
||||
|
||||
UserFederationMapperModel model = realm.getUserFederationMapperById(id);
|
||||
if (model == null) throw new NotFoundException("Model not found");
|
||||
model = RepresentationToModel.toModel(realm, rep);
|
||||
|
||||
validateModel(model);
|
||||
|
||||
realm.updateUserFederationMapper(model);
|
||||
adminEvent.operation(OperationType.UPDATE).resource(ResourceType.USER_FEDERATION_MAPPER).resourcePath(uriInfo).representation(rep).success();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a mapper with a given id
|
||||
*
|
||||
* @param id Mapper id
|
||||
*/
|
||||
@DELETE
|
||||
@NoCache
|
||||
@Path("mappers/{id}")
|
||||
public void delete(@PathParam("id") String id) {
|
||||
auth.requireManage();
|
||||
|
||||
if (federationProviderModel == null) {
|
||||
throw new NotFoundException("Could not find federation provider");
|
||||
}
|
||||
|
||||
UserFederationMapperModel model = realm.getUserFederationMapperById(id);
|
||||
if (model == null) throw new NotFoundException("Model not found");
|
||||
realm.removeUserFederationMapper(model);
|
||||
adminEvent.operation(OperationType.DELETE).resource(ResourceType.USER_FEDERATION_MAPPER).resourcePath(uriInfo).success();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger sync of mapper data related to federationMapper (roles, groups, ...)
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@POST
|
||||
@Path("mappers/{id}/sync")
|
||||
@NoCache
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public UserFederationSyncResult syncMapperData(@PathParam("id") String mapperId, @QueryParam("direction") String direction) {
|
||||
auth.requireManage();
|
||||
|
||||
if (federationProviderModel == null) {
|
||||
throw new NotFoundException("Could not find federation provider");
|
||||
}
|
||||
|
||||
UserFederationMapperModel mapperModel = realm.getUserFederationMapperById(mapperId);
|
||||
if (mapperModel == null) throw new NotFoundException("Mapper model not found");
|
||||
UserFederationMapper mapper = session.getProvider(UserFederationMapper.class, mapperModel.getFederationMapperType());
|
||||
|
||||
UserFederationProviderModel providerModel = KeycloakModelUtils.findUserFederationProviderById(mapperModel.getFederationProviderId(), realm);
|
||||
if (providerModel == null) throw new NotFoundException("Provider model not found");
|
||||
UserFederationProviderFactory providerFactory = (UserFederationProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(UserFederationProvider.class, providerModel.getProviderName());
|
||||
UserFederationProvider federationProvider = providerFactory.getInstance(session, providerModel);
|
||||
|
||||
ServicesLogger.LOGGER.syncingDataForMapper(mapperModel.getName(), mapperModel.getFederationMapperType(), direction);
|
||||
|
||||
UserFederationSyncResult syncResult;
|
||||
if ("fedToKeycloak".equals(direction)) {
|
||||
syncResult = mapper.syncDataFromFederationProviderToKeycloak(mapperModel, federationProvider, session, realm);
|
||||
} else if ("keycloakToFed".equals(direction)) {
|
||||
syncResult = mapper.syncDataFromKeycloakToFederationProvider(mapperModel, federationProvider, session, realm);
|
||||
} else {
|
||||
throw new NotFoundException("Unknown direction: " + direction);
|
||||
}
|
||||
|
||||
Map<String, Object> eventRep = new HashMap<>();
|
||||
eventRep.put("action", direction);
|
||||
eventRep.put("result", syncResult);
|
||||
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).representation(eventRep).success();
|
||||
return syncResult;
|
||||
}
|
||||
|
||||
private void validateModel(UserFederationMapperModel model) {
|
||||
try {
|
||||
UserFederationMapperFactory mapperFactory = (UserFederationMapperFactory) session.getKeycloakSessionFactory().getProviderFactory(UserFederationMapper.class, model.getFederationMapperType());
|
||||
mapperFactory.validateConfig(realm, federationProviderModel, model);
|
||||
} catch (FederationConfigValidationException ex) {
|
||||
logger.error(ex.getMessage());
|
||||
Properties messages = AdminRoot.getMessages(session, realm, auth.getAuth().getToken().getLocale());
|
||||
throw new ErrorResponseException(ex.getMessage(), MessageFormat.format(messages.getProperty(ex.getMessage(), ex.getMessage()), ex.getParameters()),
|
||||
Response.Status.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,344 +0,0 @@
|
|||
/*
|
||||
* 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.logging.Logger;
|
||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import org.jboss.resteasy.spi.NotFoundException;
|
||||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||
import org.keycloak.common.constants.KerberosConstants;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.events.admin.ResourceType;
|
||||
import org.keycloak.mappers.FederationConfigValidationException;
|
||||
import org.keycloak.models.AuthenticationExecutionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ModelDuplicateException;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserFederationProvider;
|
||||
import org.keycloak.models.UserFederationProviderFactory;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserFederationValidatingProviderFactory;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.models.utils.ModelToRepresentation;
|
||||
import org.keycloak.provider.ConfiguredProvider;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.representations.idm.ConfigPropertyRepresentation;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.representations.idm.UserFederationProviderFactoryRepresentation;
|
||||
import org.keycloak.representations.idm.UserFederationProviderRepresentation;
|
||||
import org.keycloak.services.ErrorResponse;
|
||||
import org.keycloak.services.ErrorResponseException;
|
||||
import org.keycloak.services.ServicesLogger;
|
||||
import org.keycloak.services.managers.UsersSyncManager;
|
||||
import org.keycloak.utils.CredentialHelper;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Base resource for managing users
|
||||
*
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class UserFederationProvidersResource {
|
||||
private static final Logger logger = Logger.getLogger(UserFederationProvidersResource.class);
|
||||
|
||||
protected RealmModel realm;
|
||||
|
||||
protected RealmAuth auth;
|
||||
|
||||
protected AdminEventBuilder adminEvent;
|
||||
|
||||
@Context
|
||||
protected UriInfo uriInfo;
|
||||
|
||||
@Context
|
||||
protected KeycloakSession session;
|
||||
|
||||
public UserFederationProvidersResource(RealmModel realm, RealmAuth auth, AdminEventBuilder adminEvent) {
|
||||
this.auth = auth;
|
||||
this.realm = realm;
|
||||
this.adminEvent = adminEvent.resource(ResourceType.USER_FEDERATION_PROVIDER);
|
||||
|
||||
auth.init(RealmAuth.Resource.REALM);
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatically add "kerberos" to required realm credentials if it's supported by saved provider
|
||||
*
|
||||
* @param realm
|
||||
* @param model
|
||||
* @return true if kerberos credentials were added
|
||||
*/
|
||||
public static boolean checkKerberosCredential(KeycloakSession session, RealmModel realm, UserFederationProviderModel model) {
|
||||
String allowKerberosCfg = model.getConfig().get(KerberosConstants.ALLOW_KERBEROS_AUTHENTICATION);
|
||||
if (Boolean.valueOf(allowKerberosCfg)) {
|
||||
CredentialHelper.setOrReplaceAuthenticationRequirement(session, realm, CredentialRepresentation.KERBEROS,
|
||||
AuthenticationExecutionModel.Requirement.ALTERNATIVE, AuthenticationExecutionModel.Requirement.DISABLED);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void validateFederationProviderConfig(KeycloakSession session, RealmAuth auth, RealmModel realm, UserFederationProviderModel model) {
|
||||
UserFederationProviderFactory providerFactory = KeycloakModelUtils.getFederationProviderFactory(session, model);
|
||||
if (providerFactory instanceof UserFederationValidatingProviderFactory) {
|
||||
try {
|
||||
((UserFederationValidatingProviderFactory) providerFactory).validateConfig(realm, model);
|
||||
} catch (FederationConfigValidationException fcve) {
|
||||
logger.error(fcve.getMessage());
|
||||
Properties messages = AdminRoot.getMessages(session, realm, auth.getAuth().getToken().getLocale());
|
||||
throw new ErrorResponseException(fcve.getMessage(), MessageFormat.format(messages.getProperty(fcve.getMessage(), fcve.getMessage()), fcve.getParameters()),
|
||||
Response.Status.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available provider factories
|
||||
*
|
||||
* Returns a list of available provider factories.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@GET
|
||||
@NoCache
|
||||
@Path("providers")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public List<UserFederationProviderFactoryRepresentation> getProviders() {
|
||||
auth.requireView();
|
||||
|
||||
List<UserFederationProviderFactoryRepresentation> providers = new LinkedList<UserFederationProviderFactoryRepresentation>();
|
||||
for (ProviderFactory factory : session.getKeycloakSessionFactory().getProviderFactories(UserFederationProvider.class)) {
|
||||
UserFederationProviderFactoryRepresentation rep = new UserFederationProviderFactoryRepresentation();
|
||||
rep.setId(factory.getId());
|
||||
rep.setOptions(((UserFederationProviderFactory)factory).getConfigurationOptions());
|
||||
providers.add(rep);
|
||||
}
|
||||
return providers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get factory with given id
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@GET
|
||||
@NoCache
|
||||
@Path("providers/{id}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public UserFederationProviderFactoryRepresentation getProvider(@PathParam("id") String id) {
|
||||
auth.requireView();
|
||||
|
||||
for (ProviderFactory factory : session.getKeycloakSessionFactory().getProviderFactories(UserFederationProvider.class)) {
|
||||
if (!factory.getId().equals(id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (factory instanceof ConfiguredProvider) {
|
||||
|
||||
UserFederationProviderFactoryDescription rep = new UserFederationProviderFactoryDescription();
|
||||
rep.setId(factory.getId());
|
||||
|
||||
ConfiguredProvider cp = (ConfiguredProvider) factory;
|
||||
rep.setHelpText(cp.getHelpText());
|
||||
rep.setProperties(toConfigPropertyRepresentationList(cp.getConfigProperties()));
|
||||
|
||||
return rep;
|
||||
}
|
||||
|
||||
UserFederationProviderFactoryRepresentation rep = new UserFederationProviderFactoryRepresentation();
|
||||
rep.setId(factory.getId());
|
||||
rep.setOptions(((UserFederationProviderFactory) factory).getConfigurationOptions());
|
||||
|
||||
return rep;
|
||||
}
|
||||
throw new NotFoundException("Could not find provider");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a provider
|
||||
*
|
||||
* @param rep
|
||||
* @return
|
||||
*/
|
||||
@POST
|
||||
@Path("instances")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public Response createProviderInstance(UserFederationProviderRepresentation rep) {
|
||||
auth.requireManage();
|
||||
|
||||
try {
|
||||
String displayName = rep.getDisplayName();
|
||||
if (displayName != null && displayName.trim().equals("")) {
|
||||
displayName = null;
|
||||
}
|
||||
|
||||
UserFederationProviderModel tempModel = new UserFederationProviderModel(null, rep.getProviderName(), rep.getConfig(), rep.getPriority(), displayName, rep.getFullSyncPeriod(), rep.getChangedSyncPeriod(), rep.getLastSync());
|
||||
validateFederationProviderConfig(session, auth, realm, tempModel);
|
||||
|
||||
UserFederationProviderModel model = realm.addUserFederationProvider(rep.getProviderName(), rep.getConfig(), rep.getPriority(), displayName,
|
||||
rep.getFullSyncPeriod(), rep.getChangedSyncPeriod(), rep.getLastSync());
|
||||
new UsersSyncManager().notifyToRefreshPeriodicSync(session, realm, model, false);
|
||||
boolean kerberosCredsAdded = checkKerberosCredential(session, realm, model);
|
||||
if (kerberosCredsAdded) {
|
||||
ServicesLogger.LOGGER.addedKerberosToRealmCredentials();
|
||||
}
|
||||
|
||||
rep.setId(model.getId());
|
||||
adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo, model.getId()).representation(rep).success();
|
||||
|
||||
return Response.created(uriInfo.getAbsolutePathBuilder().path(model.getId()).build()).build();
|
||||
} catch (ModelDuplicateException e) {
|
||||
if (session.getTransactionManager().isActive()) {
|
||||
session.getTransactionManager().setRollbackOnly();
|
||||
}
|
||||
return ErrorResponse.exists("Federation provider exists with same name.");
|
||||
} catch (ModelException me){
|
||||
if (session.getTransactionManager().isActive()) {
|
||||
session.getTransactionManager().setRollbackOnly();
|
||||
}
|
||||
return ErrorResponse.error("Could not create federation provider.", Response.Status.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configured providers
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@GET
|
||||
@Path("instances")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@NoCache
|
||||
public List<UserFederationProviderRepresentation> getUserFederationInstances() {
|
||||
auth.requireView();
|
||||
|
||||
List<UserFederationProviderRepresentation> reps = new LinkedList<UserFederationProviderRepresentation>();
|
||||
for (UserFederationProviderModel model : realm.getUserFederationProviders()) {
|
||||
UserFederationProviderRepresentation rep = ModelToRepresentation.toRepresentation(model);
|
||||
reps.add(rep);
|
||||
}
|
||||
return reps;
|
||||
}
|
||||
|
||||
@Path("instances/{id}")
|
||||
public UserFederationProviderResource getUserFederationInstance(@PathParam("id") String id) {
|
||||
this.auth.requireView();
|
||||
|
||||
UserFederationProviderModel model = KeycloakModelUtils.findUserFederationProviderById(id, realm);
|
||||
UserFederationProviderResource instanceResource = new UserFederationProviderResource(session, realm, this.auth, model, adminEvent);
|
||||
ResteasyProviderFactory.getInstance().injectProperties(instanceResource);
|
||||
return instanceResource;
|
||||
}
|
||||
|
||||
// TODO: This endpoint exists, so that admin console can lookup userFederation provider OR userStorage provider by federationLink.
|
||||
// TODO: Endpoint should be removed once UserFederation SPI is removed as fallback is not needed anymore than
|
||||
@GET
|
||||
@Path("instances-with-fallback/{id}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@NoCache
|
||||
public Map<String, String> getUserFederationInstanceWithFallback(@PathParam("id") String id) {
|
||||
this.auth.requireView();
|
||||
|
||||
Map<String, String> result = new HashMap<>();
|
||||
UserFederationProviderModel model = KeycloakModelUtils.findUserFederationProviderById(id, realm);
|
||||
if (model != null) {
|
||||
result.put("federationLinkName", model.getDisplayName());
|
||||
result.put("federationLink", "#/realms/" + realm.getName() + "/user-federation/providers/" + model.getProviderName() + "/" + model.getId());
|
||||
return result;
|
||||
} else {
|
||||
ComponentModel userStorage = KeycloakModelUtils.findUserStorageProviderById(id, realm);
|
||||
if (userStorage != null) {
|
||||
result.put("federationLinkName", userStorage.getName());
|
||||
result.put("federationLink", "#/realms/" + realm.getName() + "/user-storage/providers/" + userStorage.getProviderId() + "/" + userStorage.getId());
|
||||
return result;
|
||||
} else {
|
||||
throw new NotFoundException("Could not find federation provider or userStorage provider");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private ConfigPropertyRepresentation toConfigPropertyRepresentation(ProviderConfigProperty prop) {
|
||||
return ModelToRepresentation.toRepresentation(prop);
|
||||
}
|
||||
|
||||
private List<ConfigPropertyRepresentation> toConfigPropertyRepresentationList(List<ProviderConfigProperty> props) {
|
||||
|
||||
List<ConfigPropertyRepresentation> reps = new ArrayList<>(props.size());
|
||||
for(ProviderConfigProperty prop : props){
|
||||
reps.add(toConfigPropertyRepresentation(prop));
|
||||
}
|
||||
|
||||
return reps;
|
||||
}
|
||||
|
||||
|
||||
public static class UserFederationProviderFactoryDescription extends UserFederationProviderFactoryRepresentation {
|
||||
|
||||
protected String name;
|
||||
|
||||
protected String helpText;
|
||||
|
||||
protected List<ConfigPropertyRepresentation> properties;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getHelpText() {
|
||||
return helpText;
|
||||
}
|
||||
|
||||
public void setHelpText(String helpText) {
|
||||
this.helpText = helpText;
|
||||
}
|
||||
|
||||
public List<ConfigPropertyRepresentation> getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public void setProperties(List<ConfigPropertyRepresentation> properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,24 +21,21 @@ import org.jboss.logging.Logger;
|
|||
import org.keycloak.common.util.reflections.Types;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.CredentialValidationOutput;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionTask;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserConsentModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserManager;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserProvider;
|
||||
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;
|
||||
|
@ -52,6 +49,8 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.keycloak.models.utils.KeycloakModelUtils.runJobInTransaction;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
|
@ -244,13 +243,44 @@ public class UserStorageManager implements UserProvider, OnUserCache {
|
|||
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);
|
||||
UserModel validated = ((ImportedUserValidation) provider).validate(realm, user);
|
||||
if (validated == null) {
|
||||
deleteInvalidUser(realm, user);
|
||||
return null;
|
||||
} else {
|
||||
return validated;
|
||||
}
|
||||
|
||||
} else if (provider == null) {
|
||||
// remove linked user with unknown storage provider.
|
||||
logger.debugf("Removed user with federation link of unknown storage provider '%s'", user.getUsername());
|
||||
deleteInvalidUser(realm, user);
|
||||
return null;
|
||||
} else {
|
||||
return user;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void deleteInvalidUser(final RealmModel realm, final UserModel user) {
|
||||
String userId = user.getId();
|
||||
String userName = user.getUsername();
|
||||
session.userCache().evict(realm, user);
|
||||
runJobInTransaction(session.getKeycloakSessionFactory(), new KeycloakSessionTask() {
|
||||
|
||||
@Override
|
||||
public void run(KeycloakSession session) {
|
||||
RealmModel realmModel = session.realms().getRealm(realm.getId());
|
||||
if (realmModel == null) return;
|
||||
UserModel deletedUser = session.userLocalStorage().getUserById(userId, realmModel);
|
||||
new UserManager(session).removeUser(realmModel, deletedUser, session.userLocalStorage());
|
||||
logger.debugf("Removed invalid user '%s'", userName);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
protected List<UserModel> importValidation(RealmModel realm, List<UserModel> users) {
|
||||
List<UserModel> tmp = new LinkedList<>();
|
||||
for (UserModel user : users) {
|
||||
|
@ -291,11 +321,13 @@ public class UserStorageManager implements UserProvider, OnUserCache {
|
|||
@Override
|
||||
public UserModel getUserByEmail(String email, RealmModel realm) {
|
||||
UserModel user = localStorage().getUserByEmail(email, realm);
|
||||
if (user != null) return user;
|
||||
if (user != null) {
|
||||
return importValidation(realm, user);
|
||||
}
|
||||
for (UserLookupProvider provider : getStorageProviders(session, realm, UserLookupProvider.class)) {
|
||||
user = provider.getUserByEmail(email, realm);
|
||||
if (user != null) {
|
||||
return importValidation(realm, user);
|
||||
return user;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
@ -532,12 +564,6 @@ public class UserStorageManager implements UserProvider, OnUserCache {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, UserFederationProviderModel model) {
|
||||
if (getFederatedStorage() != null) getFederatedStorage().preRemove(realm, model);
|
||||
localStorage().preRemove(realm, model);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, GroupModel group) {
|
||||
localStorage().preRemove(realm, group);
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* 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.testsuite.federation;
|
||||
|
||||
import org.keycloak.provider.ConfiguredProvider;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class DummyConfigurableUserFederationProviderFactory extends DummyUserFederationProviderFactory implements ConfiguredProvider {
|
||||
|
||||
public static final String PROVIDER_NAME = "dummy-configurable";
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "Dummy User Federation Provider Help Text";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
|
||||
ProviderConfigProperty prop1 = new ProviderConfigProperty();
|
||||
prop1.setName("prop1");
|
||||
prop1.setLabel("Prop1");
|
||||
prop1.setDefaultValue("prop1Default");
|
||||
prop1.setHelpText("Prop1 HelpText");
|
||||
prop1.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
|
||||
ProviderConfigProperty prop2 = new ProviderConfigProperty();
|
||||
prop2.setName("prop2");
|
||||
prop2.setLabel("Prop2");
|
||||
prop2.setDefaultValue("true");
|
||||
prop2.setHelpText("Prop2 HelpText");
|
||||
prop2.setType(ProviderConfigProperty.BOOLEAN_TYPE);
|
||||
|
||||
return Arrays.asList(prop1, prop2);
|
||||
}
|
||||
}
|
|
@ -1,140 +0,0 @@
|
|||
/*
|
||||
* 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.testsuite.federation;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.mappers.FederationConfigValidationException;
|
||||
import org.keycloak.mappers.UserFederationMapper;
|
||||
import org.keycloak.mappers.UserFederationMapperFactory;
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserFederationMapperModel;
|
||||
import org.keycloak.models.UserFederationProvider;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserFederationSyncResult;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.representations.idm.UserFederationMapperSyncConfigRepresentation;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class DummyUserFederationMapper implements UserFederationMapperFactory, UserFederationMapper {
|
||||
|
||||
public static final String PROVIDER_NAME = "dummy-mapper";
|
||||
|
||||
@Override
|
||||
public String getFederationProviderType() {
|
||||
return DummyUserFederationProviderFactory.PROVIDER_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayCategory() {
|
||||
return "Dummy";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayType() {
|
||||
return "Dummy";
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserFederationMapperSyncConfigRepresentation getSyncConfig() {
|
||||
return new UserFederationMapperSyncConfigRepresentation(true, "dummyFedToKeycloak", true, "dummyKeycloakToFed");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateConfig(RealmModel realm, UserFederationProviderModel fedProviderModel, UserFederationMapperModel mapperModel) throws FederationConfigValidationException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getDefaultConfig(UserFederationProviderModel providerModel) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "Dummy";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserFederationMapper create(KeycloakSession session) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserFederationSyncResult syncDataFromFederationProviderToKeycloak(final UserFederationMapperModel mapperModel, UserFederationProvider federationProvider, KeycloakSession session, RealmModel realm) {
|
||||
return new UserFederationSyncResult() {
|
||||
|
||||
@Override
|
||||
public String getStatus() {
|
||||
return "dummyFedToKeycloakSuccess mapper=" + mapperModel.getName();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserFederationSyncResult syncDataFromKeycloakToFederationProvider(final UserFederationMapperModel mapperModel, UserFederationProvider federationProvider, KeycloakSession session, RealmModel realm) {
|
||||
return new UserFederationSyncResult() {
|
||||
|
||||
@Override
|
||||
public String getStatus() {
|
||||
return "dummyKeycloakToFedSuccess mapper=" + mapperModel.getName();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> getGroupMembers(UserFederationMapperModel mapperModel, UserFederationProvider federationProvider, RealmModel realm, GroupModel group, int firstResult, int maxResults) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
|
@ -17,18 +17,21 @@
|
|||
|
||||
package org.keycloak.testsuite.federation;
|
||||
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.credential.CredentialInput;
|
||||
import org.keycloak.credential.CredentialInputValidator;
|
||||
import org.keycloak.credential.CredentialModel;
|
||||
import org.keycloak.models.CredentialValidationOutput;
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserFederationProvider;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.storage.user.UserLookupProvider;
|
||||
import org.keycloak.storage.user.UserRegistrationProvider;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -36,56 +39,60 @@ import java.util.Set;
|
|||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class DummyUserFederationProvider implements UserFederationProvider {
|
||||
public class DummyUserFederationProvider implements UserStorageProvider,
|
||||
UserLookupProvider,
|
||||
UserRegistrationProvider,
|
||||
CredentialInputValidator {
|
||||
|
||||
private final Map<String, UserModel> users;
|
||||
private KeycloakSession session;
|
||||
private ComponentModel component;
|
||||
|
||||
public DummyUserFederationProvider(Map<String, UserModel> users) {
|
||||
|
||||
|
||||
public DummyUserFederationProvider(KeycloakSession session, ComponentModel component, Map<String, UserModel> users) {
|
||||
this.users = users;
|
||||
this.session = session;
|
||||
this.component = component;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public UserModel validateAndProxy(RealmModel realm, UserModel local) {
|
||||
public UserModel addUser(RealmModel realm, String username) {
|
||||
UserModel local = session.userLocalStorage().addUser(realm, username);
|
||||
local.setFederationLink(component.getId());
|
||||
|
||||
users.put(username, local);
|
||||
return local;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean synchronizeRegistrations() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel register(RealmModel realm, UserModel user) {
|
||||
users.put(user.getUsername(), user);
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeUser(RealmModel realm, UserModel user) {
|
||||
return users.remove(user.getUsername()) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserByUsername(RealmModel realm, String username) {
|
||||
return users.get(username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserByEmail(RealmModel realm, String email) {
|
||||
public UserModel getUserById(String id, RealmModel realm) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> searchByAttributes(Map<String, String> attributes, RealmModel realm, int maxResults) {
|
||||
return Collections.emptyList();
|
||||
public UserModel getUserByUsername(String username, RealmModel realm) {
|
||||
return users.get(username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) {
|
||||
return Collections.emptyList();
|
||||
public UserModel getUserByEmail(String email, RealmModel realm) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void grantToAllUsers(RealmModel realm, RoleModel role) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm) {
|
||||
|
||||
}
|
||||
|
@ -100,34 +107,10 @@ public class DummyUserFederationProvider implements UserFederationProvider {
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(RealmModel realm, UserModel local) {
|
||||
String username = local.getUsername();
|
||||
return users.containsKey(username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getSupportedCredentialTypes() {
|
||||
return Collections.singleton(UserCredentialModel.PASSWORD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateCredential(RealmModel realm, UserModel user, CredentialInput input) {
|
||||
if (!(input instanceof UserCredentialModel) || !CredentialModel.PASSWORD.equals(input.getType())) return false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableCredentialType(RealmModel realm, UserModel user, String credentialType) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user) {
|
||||
return Collections.EMPTY_SET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCredentialType(String credentialType) {
|
||||
return getSupportedCredentialTypes().contains(credentialType);
|
||||
|
@ -154,12 +137,7 @@ public class DummyUserFederationProvider implements UserFederationProvider {
|
|||
}
|
||||
return false; }
|
||||
|
||||
@Override
|
||||
public CredentialValidationOutput validCredentials(RealmModel realm, UserCredentialModel credential) {
|
||||
return CredentialValidationOutput.failed();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
|
|
@ -19,26 +19,28 @@ package org.keycloak.testsuite.federation;
|
|||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.UserFederationProvider;
|
||||
import org.keycloak.models.UserFederationProviderFactory;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserFederationSyncResult;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.provider.ProviderConfigurationBuilder;
|
||||
import org.keycloak.storage.UserStorageProviderFactory;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
import org.keycloak.storage.user.ImportSynchronization;
|
||||
import org.keycloak.storage.user.SynchronizationResult;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class DummyUserFederationProviderFactory implements UserFederationProviderFactory {
|
||||
public class DummyUserFederationProviderFactory implements UserStorageProviderFactory<DummyUserFederationProvider>, ImportSynchronization {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(DummyUserFederationProviderFactory.class);
|
||||
public static final String PROVIDER_NAME = "dummy";
|
||||
|
@ -49,20 +51,16 @@ public class DummyUserFederationProviderFactory implements UserFederationProvide
|
|||
private Map<String, UserModel> users = new HashMap<String, UserModel>();
|
||||
|
||||
@Override
|
||||
public UserFederationProvider getInstance(KeycloakSession session, UserFederationProviderModel model) {
|
||||
return new DummyUserFederationProvider(users);
|
||||
public DummyUserFederationProvider create(KeycloakSession session, ComponentModel model) {
|
||||
return new DummyUserFederationProvider(session, model, users);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getConfigurationOptions() {
|
||||
Set<String> list = new HashSet<String>();
|
||||
list.add("important.config");
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserFederationProvider create(KeycloakSession session) {
|
||||
return new DummyUserFederationProvider(users);
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return ProviderConfigurationBuilder.create()
|
||||
.property().name("important.config")
|
||||
.type(ProviderConfigProperty.STRING_TYPE)
|
||||
.add().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -86,17 +84,17 @@ public class DummyUserFederationProviderFactory implements UserFederationProvide
|
|||
}
|
||||
|
||||
@Override
|
||||
public UserFederationSyncResult syncAllUsers(KeycloakSessionFactory sessionFactory, String realmId, UserFederationProviderModel model) {
|
||||
public SynchronizationResult sync(KeycloakSessionFactory sessionFactory, String realmId, UserStorageProviderModel model) {
|
||||
logger.info("syncAllUsers invoked");
|
||||
fullSyncCounter.incrementAndGet();
|
||||
return UserFederationSyncResult.empty();
|
||||
return SynchronizationResult.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserFederationSyncResult syncChangedUsers(KeycloakSessionFactory sessionFactory, String realmId, UserFederationProviderModel model, Date lastSync) {
|
||||
public SynchronizationResult syncSince(Date lastSync, KeycloakSessionFactory sessionFactory, String realmId, UserStorageProviderModel model) {
|
||||
logger.info("syncChangedUsers invoked");
|
||||
changedSyncCounter.incrementAndGet();
|
||||
return UserFederationSyncResult.empty();
|
||||
return SynchronizationResult.empty();
|
||||
}
|
||||
|
||||
public int getFullSyncCounter() {
|
||||
|
|
|
@ -32,18 +32,13 @@ import org.keycloak.events.admin.AdminEventQuery;
|
|||
import org.keycloak.events.admin.AuthDetails;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.events.admin.ResourceType;
|
||||
import org.keycloak.keys.KeyProvider;
|
||||
import org.keycloak.keys.KeyProviderFactory;
|
||||
import org.keycloak.models.AuthenticationFlowModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RealmProvider;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserFederationProvider;
|
||||
import org.keycloak.models.UserFederationProviderFactory;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserProvider;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
|
@ -54,12 +49,13 @@ import org.keycloak.representations.idm.AuthDetailsRepresentation;
|
|||
import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
|
||||
import org.keycloak.representations.idm.EventRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.services.managers.ClientSessionCode;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.services.resource.RealmResourceProvider;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.testsuite.components.TestProvider;
|
||||
import org.keycloak.testsuite.components.TestProviderFactory;
|
||||
import org.keycloak.testsuite.events.EventsListenerProvider;
|
||||
import org.keycloak.testsuite.federation.DummyUserFederationProviderFactory;
|
||||
import org.keycloak.testsuite.forms.PassThroughAuthenticator;
|
||||
import org.keycloak.testsuite.forms.PassThroughClientAuthenticator;
|
||||
import org.keycloak.testsuite.rest.representation.AuthenticatorState;
|
||||
|
@ -580,8 +576,8 @@ public class TestingResourceProvider implements RealmResourceProvider {
|
|||
public UserRepresentation getUserByUsernameFromFedProviderFactory(@QueryParam("realmName") String realmName,
|
||||
@QueryParam("userName") String userName) {
|
||||
RealmModel realm = getRealmByName(realmName);
|
||||
UserFederationProviderFactory factory = (UserFederationProviderFactory)session.getKeycloakSessionFactory().getProviderFactory(UserFederationProvider.class, "dummy");
|
||||
UserModel user = factory.getInstance(session, null).getUserByUsername(realm, userName);
|
||||
DummyUserFederationProviderFactory factory = (DummyUserFederationProviderFactory)session.getKeycloakSessionFactory().getProviderFactory(UserStorageProvider.class, "dummy");
|
||||
UserModel user = factory.create(session, null).getUserByUsername(userName, realm);
|
||||
if (user == null) return null;
|
||||
return ModelToRepresentation.toRepresentation(session, realm, user);
|
||||
}
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
#
|
||||
# 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.testsuite.federation.DummyUserFederationMapper
|
|
@ -1,36 +0,0 @@
|
|||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
#
|
||||
# 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.testsuite.federation.DummyUserFederationProviderFactory
|
||||
org.keycloak.testsuite.federation.DummyConfigurableUserFederationProviderFactory
|
|
@ -0,0 +1 @@
|
|||
org.keycloak.testsuite.federation.DummyUserFederationProviderFactory
|
|
@ -22,7 +22,7 @@ import org.jboss.logging.Logger;
|
|||
import org.keycloak.common.constants.GenericConstants;
|
||||
import org.keycloak.common.constants.KerberosConstants;
|
||||
import org.keycloak.models.LDAPConstants;
|
||||
import org.keycloak.models.UserFederationProvider;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
|
@ -83,7 +83,7 @@ public class LDAPTestConfiguration {
|
|||
DEFAULT_VALUES.put(LDAPConstants.BATCH_SIZE_FOR_SYNC, String.valueOf(LDAPConstants.DEFAULT_BATCH_SIZE_FOR_SYNC));
|
||||
DEFAULT_VALUES.put(LDAPConstants.USERNAME_LDAP_ATTRIBUTE, null);
|
||||
DEFAULT_VALUES.put(LDAPConstants.USER_OBJECT_CLASSES, null);
|
||||
DEFAULT_VALUES.put(LDAPConstants.EDIT_MODE, UserFederationProvider.EditMode.READ_ONLY.toString());
|
||||
DEFAULT_VALUES.put(LDAPConstants.EDIT_MODE, UserStorageProvider.EditMode.READ_ONLY.toString());
|
||||
|
||||
DEFAULT_VALUES.put(KerberosConstants.ALLOW_KERBEROS_AUTHENTICATION, "false");
|
||||
DEFAULT_VALUES.put(KerberosConstants.KERBEROS_REALM, "KEYCLOAK.ORG");
|
||||
|
|
|
@ -1,373 +0,0 @@
|
|||
/*
|
||||
* 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.testsuite.adapter.federation;
|
||||
|
||||
import org.apache.http.auth.AuthScope;
|
||||
import org.apache.http.auth.Credentials;
|
||||
import org.apache.http.client.params.AuthPolicy;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.jboss.arquillian.container.test.api.Deployment;
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
|
||||
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
|
||||
import org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine;
|
||||
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.adapters.HttpClientBuilder;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.common.constants.KerberosConstants;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.federation.kerberos.CommonKerberosConfig;
|
||||
import org.keycloak.models.LDAPConstants;
|
||||
import org.keycloak.models.UserFederationProvider;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.UserFederationProviderRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
import org.keycloak.testsuite.AssertEvents;
|
||||
import org.keycloak.testsuite.adapter.AbstractServletsAdapterTest;
|
||||
import org.keycloak.testsuite.adapter.page.KerberosPortal;
|
||||
import org.keycloak.testsuite.adapter.servlet.KerberosCredDelegServlet;
|
||||
import org.keycloak.testsuite.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.auth.page.AuthRealm;
|
||||
import org.keycloak.testsuite.auth.page.account.ChangePassword;
|
||||
import org.keycloak.testsuite.util.LDAPTestConfiguration;
|
||||
import org.keycloak.util.ldap.KerberosEmbeddedServer;
|
||||
import org.keycloak.util.ldap.LDAPEmbeddedServer;
|
||||
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.security.Principal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import static org.keycloak.testsuite.util.IOUtil.loadRealm;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public abstract class AbstractKerberosAdapterTest extends AbstractServletsAdapterTest {
|
||||
|
||||
protected static LDAPTestConfiguration ldapTestConfiguration;
|
||||
|
||||
protected KeycloakSPNegoSchemeFactory spnegoSchemeFactory;
|
||||
|
||||
protected ResteasyClient client;
|
||||
|
||||
protected static LDAPEmbeddedServer ldapEmbeddedServer;
|
||||
|
||||
@Rule
|
||||
public AssertEvents events = new AssertEvents(this);
|
||||
|
||||
@Page
|
||||
protected ChangePassword changePasswordPage;
|
||||
|
||||
@Page
|
||||
protected KerberosPortal kerberosPortal;
|
||||
|
||||
protected abstract String getConnectionPropertiesLocation();
|
||||
|
||||
protected abstract CommonKerberosConfig getKerberosConfig(UserStorageProviderModel model);
|
||||
|
||||
@Deployment(name = KerberosPortal.DEPLOYMENT_NAME)
|
||||
protected static WebArchive kerberosPortal() {
|
||||
return servletDeployment(KerberosPortal.DEPLOYMENT_NAME, "keycloak.json", KerberosCredDelegServlet.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAdapterTestRealms(List<RealmRepresentation> testRealms) {
|
||||
testRealms.add(loadRealm("/adapter-test/kerberosrealm.json"));
|
||||
}
|
||||
|
||||
|
||||
@Before
|
||||
public void before() throws Exception {
|
||||
testRealmPage.setAuthRealm(AuthRealm.TEST);
|
||||
changePasswordPage.setAuthRealm(testRealmPage);
|
||||
// Global kerberos configuration
|
||||
ldapTestConfiguration = LDAPTestConfiguration.readConfiguration(getConnectionPropertiesLocation());
|
||||
String krb5ConfPath = LDAPTestConfiguration.getResource("test-krb5.conf");
|
||||
log.info("Krb5.conf file location is: " + krb5ConfPath);
|
||||
System.setProperty("java.security.krb5.conf", krb5ConfPath);
|
||||
if (ldapTestConfiguration.isStartEmbeddedLdapServer() && ldapEmbeddedServer == null) {
|
||||
ldapEmbeddedServer = createServer();
|
||||
ldapEmbeddedServer.init();
|
||||
ldapEmbeddedServer.start();
|
||||
}
|
||||
UserStorageProviderModel model = new UserStorageProviderModel();
|
||||
model.setConfig(AbstractKerberosStandaloneAdapterTest.toComponentConfig(ldapTestConfiguration.getLDAPConfig()));
|
||||
spnegoSchemeFactory = new KeycloakSPNegoSchemeFactory(getKerberosConfig(model));
|
||||
initHttpClient(true);
|
||||
removeAllUsers();
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
client.close();
|
||||
client = null;
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClass() {
|
||||
try {
|
||||
if (ldapEmbeddedServer != null) {
|
||||
ldapEmbeddedServer.stop();
|
||||
ldapEmbeddedServer = null;
|
||||
}
|
||||
ldapTestConfiguration = null;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Error tearDown Embedded LDAP server.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void spnegoNotAvailableTest() throws Exception {
|
||||
initHttpClient(false);
|
||||
|
||||
String kcLoginPageLocation = client.target(kerberosPortal.getInjectedUrl().toString()).request().get().getLocation().toString();
|
||||
|
||||
Response response = client.target(kcLoginPageLocation).request().get();
|
||||
Assert.assertEquals(401, response.getStatus());
|
||||
Assert.assertEquals(KerberosConstants.NEGOTIATE, response.getHeaderString(HttpHeaders.WWW_AUTHENTICATE));
|
||||
String responseText = response.readEntity(String.class);
|
||||
responseText.contains("Log in to test");
|
||||
response.close();
|
||||
}
|
||||
|
||||
protected void spnegoLoginTestImpl() throws Exception {
|
||||
Response spnegoResponse = spnegoLogin("hnelson", "secret");
|
||||
Assert.assertEquals(302, spnegoResponse.getStatus());
|
||||
|
||||
List<UserRepresentation> users = testRealmResource().users().search("hnelson", 0, 1);
|
||||
String userId = users.get(0).getId();
|
||||
events.expectLogin()
|
||||
.client("kerberos-app")
|
||||
.user(userId)
|
||||
.detail(Details.REDIRECT_URI, kerberosPortal.toString())
|
||||
//.detail(Details.AUTH_METHOD, "spnego")
|
||||
.detail(Details.USERNAME, "hnelson")
|
||||
.assertEvent();
|
||||
|
||||
String location = spnegoResponse.getLocation().toString();
|
||||
driver.navigate().to(location);
|
||||
|
||||
String pageSource = driver.getPageSource();
|
||||
Assert.assertTrue(
|
||||
pageSource.contains("Kerberos Test") && pageSource.contains("Kerberos servlet secured content"));
|
||||
|
||||
spnegoResponse.close();
|
||||
events.clear();
|
||||
}
|
||||
|
||||
// KEYCLOAK-2102
|
||||
@Test
|
||||
public void spnegoCaseInsensitiveTest() throws Exception {
|
||||
Response spnegoResponse = spnegoLogin(ldapTestConfiguration.isCaseSensitiveLogin() ? "MyDuke" : "myduke", "theduke");
|
||||
Assert.assertEquals(302, spnegoResponse.getStatus());
|
||||
List<UserRepresentation> users = testRealmResource().users().search("myduke", 0, 1);
|
||||
String userId = users.get(0).getId();
|
||||
events.expectLogin()
|
||||
.client("kerberos-app")
|
||||
.user(userId)
|
||||
.detail(Details.REDIRECT_URI, kerberosPortal.toString())
|
||||
//.detail(Details.AUTH_METHOD, "spnego")
|
||||
.detail(Details.USERNAME, "myduke")
|
||||
.assertEvent();
|
||||
|
||||
String location = spnegoResponse.getLocation().toString();
|
||||
driver.navigate().to(location);
|
||||
|
||||
String pageSource = driver.getPageSource();
|
||||
Assert.assertTrue(
|
||||
pageSource.contains("Kerberos Test") && pageSource.contains("Kerberos servlet secured content"));
|
||||
|
||||
spnegoResponse.close();
|
||||
events.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void usernamePasswordLoginTest() throws Exception {
|
||||
// Change editMode to READ_ONLY
|
||||
updateProviderEditMode(UserFederationProvider.EditMode.READ_ONLY);
|
||||
|
||||
// Login with username/password from kerberos
|
||||
changePasswordPage.navigateTo();
|
||||
testRealmLoginPage.isCurrent();
|
||||
testRealmLoginPage.form().login("jduke", "theduke");
|
||||
changePasswordPage.isCurrent();
|
||||
|
||||
// Bad existing password
|
||||
changePasswordPage.changePasswords("theduke-invalid", "newPass", "newPass");
|
||||
Assert.assertTrue(driver.getPageSource().contains("Invalid existing password."));
|
||||
|
||||
// Change password is not possible as editMode is READ_ONLY
|
||||
changePasswordPage.changePasswords("theduke", "newPass", "newPass");
|
||||
Assert.assertTrue(
|
||||
driver.getPageSource().contains("You can't update your password as your account is read only"));
|
||||
|
||||
// Change editMode to UNSYNCED
|
||||
updateProviderEditMode(UserFederationProvider.EditMode.UNSYNCED);
|
||||
|
||||
// Successfully change password now
|
||||
changePasswordPage.changePasswords("theduke", "newPass", "newPass");
|
||||
Assert.assertTrue(driver.getPageSource().contains("Your password has been updated."));
|
||||
changePasswordPage.logOut();
|
||||
|
||||
// Login with old password doesn't work, but with new password works
|
||||
testRealmLoginPage.form().login("jduke", "theduke");
|
||||
testRealmLoginPage.isCurrent();
|
||||
testRealmLoginPage.form().login("jduke", "newPass");
|
||||
changePasswordPage.isCurrent();
|
||||
changePasswordPage.logOut();
|
||||
|
||||
// Assert SPNEGO login still with the old password as mode is unsynced
|
||||
events.clear();
|
||||
Response spnegoResponse = spnegoLogin("jduke", "theduke");
|
||||
Assert.assertEquals(302, spnegoResponse.getStatus());
|
||||
UserRepresentation user = ApiUtil.findUserByUsername(testRealmResource(), "jduke");
|
||||
events.expectLogin()
|
||||
.client("kerberos-app")
|
||||
.user(user != null ? user.getId() : null)
|
||||
.detail(Details.REDIRECT_URI, kerberosPortal.toString())
|
||||
//.detail(Details.AUTH_METHOD, "spnego")
|
||||
.detail(Details.USERNAME, "jduke")
|
||||
.assertEvent();
|
||||
spnegoResponse.close();
|
||||
}
|
||||
|
||||
|
||||
protected Response spnegoLogin(String username, String password) {
|
||||
kerberosPortal.navigateTo();
|
||||
Response res = client.target(kerberosPortal.getInjectedUrl().toString()).request().get();
|
||||
String kcLoginPageLocation = res.getLocation().toString();
|
||||
if (driver.manage().getCookieNamed("OAuth_Token_Request_State") != null) {
|
||||
kcLoginPageLocation = res.getLocation().toString().replaceFirst("state=.*&", "state=" + driver.manage().getCookieNamed("OAuth_Token_Request_State").getValue() + "&");
|
||||
}
|
||||
// Request for SPNEGO login sent with Resteasy client
|
||||
spnegoSchemeFactory.setCredentials(username, password);
|
||||
Response response = client.target(kcLoginPageLocation).request().get();
|
||||
if (response.getStatus() == 302) {
|
||||
if (response.getLocation() == null)
|
||||
return response;
|
||||
String uri = response.getLocation().toString();
|
||||
if (uri.contains("login-actions/required-action")) {
|
||||
response = client.target(uri).request().get();
|
||||
}
|
||||
}
|
||||
return response;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected void initHttpClient(boolean useSpnego) {
|
||||
if (client != null) {
|
||||
after();
|
||||
}
|
||||
DefaultHttpClient httpClient = (DefaultHttpClient) new HttpClientBuilder().build();
|
||||
httpClient.getAuthSchemes().register(AuthPolicy.SPNEGO, spnegoSchemeFactory);
|
||||
|
||||
if (useSpnego) {
|
||||
Credentials fake = new Credentials() {
|
||||
|
||||
public String getPassword() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Principal getUserPrincipal() {
|
||||
return null;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
httpClient.getCredentialsProvider().setCredentials(
|
||||
new AuthScope(null, -1, null),
|
||||
fake);
|
||||
}
|
||||
ApacheHttpClient4Engine engine = new ApacheHttpClient4Engine(httpClient);
|
||||
client = new ResteasyClientBuilder().httpEngine(engine).build();
|
||||
}
|
||||
|
||||
protected void removeAllUsers() {
|
||||
RealmResource realm = testRealmResource();
|
||||
List<UserRepresentation> users = realm.users().search("", 0, Integer.MAX_VALUE);
|
||||
for (UserRepresentation user : users) {
|
||||
if (!user.getUsername().equals(AssertEvents.DEFAULT_USERNAME)) {
|
||||
realm.users().get(user.getId()).remove();
|
||||
}
|
||||
}
|
||||
Assert.assertEquals(1, realm.users().search("", 0, Integer.MAX_VALUE).size());
|
||||
}
|
||||
|
||||
protected void assertUser(String expectedUsername, String expectedEmail, String expectedFirstname,
|
||||
String expectedLastname, boolean updateProfileActionExpected) {
|
||||
try {
|
||||
UserRepresentation user = ApiUtil.findUserByUsername(testRealmResource(), expectedUsername);
|
||||
Assert.assertNotNull(user);
|
||||
Assert.assertEquals(expectedEmail, user.getEmail());
|
||||
Assert.assertEquals(expectedFirstname, user.getFirstName());
|
||||
Assert.assertEquals(expectedLastname, user.getLastName());
|
||||
|
||||
if (updateProfileActionExpected) {
|
||||
Assert.assertEquals(UserModel.RequiredAction.UPDATE_PROFILE.toString(),
|
||||
user.getRequiredActions().iterator().next());
|
||||
} else {
|
||||
Assert.assertTrue(user.getRequiredActions().isEmpty());
|
||||
}
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateProviderEditMode(UserFederationProvider.EditMode editMode) {
|
||||
RealmResource realm = testRealmResource();
|
||||
RealmRepresentation realmRepresentation = realm.toRepresentation();
|
||||
UserFederationProviderRepresentation kerberosProviderRepresentation = realmRepresentation
|
||||
.getUserFederationProviders().get(0);
|
||||
kerberosProviderRepresentation.getConfig().put(LDAPConstants.EDIT_MODE, editMode.toString());
|
||||
realm.update(realmRepresentation);
|
||||
}
|
||||
|
||||
public RealmResource testRealmResource() {
|
||||
return adminClient.realm("test");
|
||||
}
|
||||
|
||||
public Map<String, String> getConfig() {
|
||||
return ldapTestConfiguration.getLDAPConfig();
|
||||
}
|
||||
|
||||
protected static LDAPEmbeddedServer createServer() {
|
||||
Properties defaultProperties = new Properties();
|
||||
defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_DSF, LDAPEmbeddedServer.DSF_INMEMORY);
|
||||
defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_LDIF_FILE, "classpath:kerberos/users-kerberos.ldif");
|
||||
return new KerberosEmbeddedServer(defaultProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultPageUriParameters() {
|
||||
super.setDefaultPageUriParameters();
|
||||
testRealmPage.setAuthRealm(AuthRealm.TEST);
|
||||
}
|
||||
}
|
|
@ -1,122 +0,0 @@
|
|||
/*
|
||||
* 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.testsuite.adapter.federation;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.common.constants.KerberosConstants;
|
||||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
import org.keycloak.federation.kerberos.CommonKerberosConfig;
|
||||
import org.keycloak.federation.kerberos.KerberosConfig;
|
||||
import org.keycloak.federation.kerberos.KerberosFederationProviderFactory;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.representations.idm.ComponentRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.UserFederationProviderRepresentation;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Test of KerberosFederationProvider (Kerberos not backed by LDAP)
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public abstract class AbstractKerberosStandaloneAdapterTest extends AbstractKerberosAdapterTest {
|
||||
|
||||
public static MultivaluedHashMap<String, String> toComponentConfig(Map<String, String> ldapConfig) {
|
||||
MultivaluedHashMap<String, String> config = new MultivaluedHashMap<>();
|
||||
for (Map.Entry<String, String> entry : ldapConfig.entrySet()) {
|
||||
config.add(entry.getKey(), entry.getValue());
|
||||
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
protected static final String PROVIDER_CONFIG_LOCATION = "kerberos-standalone-connection.properties";
|
||||
|
||||
@Before
|
||||
public void init() throws Exception{
|
||||
RealmRepresentation realmRepresentation = testRealmResource().toRepresentation();
|
||||
Map<String,String> ldapConfig = getConfig();
|
||||
ComponentRepresentation component = new ComponentRepresentation();
|
||||
component.setName("kerberos-standalone");
|
||||
component.setParentId(realmRepresentation.getId());
|
||||
component.setProviderId(KerberosFederationProviderFactory.PROVIDER_NAME);
|
||||
component.setProviderType(UserStorageProvider.class.getName());
|
||||
component.setConfig(toComponentConfig(ldapConfig));
|
||||
component.getConfig().putSingle("priority", "0");
|
||||
|
||||
testRealmResource().components().add(component);
|
||||
realmRepresentation.setEventsEnabled(true);
|
||||
testRealmResource().update(realmRepresentation);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CommonKerberosConfig getKerberosConfig(UserStorageProviderModel model) {
|
||||
return new KerberosConfig(model);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getConnectionPropertiesLocation() {
|
||||
return PROVIDER_CONFIG_LOCATION;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void spnegoLoginTest() throws Exception {
|
||||
spnegoLoginTestImpl();
|
||||
// Assert user was imported and hasn't any required action on him
|
||||
assertUser("hnelson", "hnelson@" + getConfig().get(KerberosConstants.KERBEROS_REALM).toLowerCase(), null, null, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void usernamePasswordLoginTest() throws Exception {
|
||||
super.usernamePasswordLoginTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateProfileEnabledTest() throws Exception {
|
||||
// Switch updateProfileOnFirstLogin to on
|
||||
RealmRepresentation realm = testRealmResource().toRepresentation();
|
||||
UserFederationProviderRepresentation kerberosProviderRepresentation = realm.getUserFederationProviders().get(0);
|
||||
kerberosProviderRepresentation.getConfig().put(KerberosConstants.UPDATE_PROFILE_FIRST_LOGIN, "true");
|
||||
testRealmResource().update(realm);
|
||||
|
||||
// Assert update profile page is displayed
|
||||
Response spnegoResponse = spnegoLogin("hnelson", "secret");
|
||||
Assert.assertEquals(200, spnegoResponse.getStatus());
|
||||
String responseText = spnegoResponse.readEntity(String.class);
|
||||
Assert.assertTrue(responseText.contains("You need to update your user profile to activate your account."));
|
||||
Assert.assertTrue(responseText.contains("hnelson@" + getConfig().get(KerberosConstants.KERBEROS_REALM).toLowerCase()));
|
||||
spnegoResponse.close();
|
||||
|
||||
// Assert user was imported and has required action on him
|
||||
assertUser("hnelson", "hnelson@" + getConfig().get(KerberosConstants.KERBEROS_REALM).toLowerCase(), null, null, true);
|
||||
|
||||
// Switch updateProfileOnFirstLogin to off
|
||||
|
||||
kerberosProviderRepresentation = realm.getUserFederationProviders().get(0);
|
||||
kerberosProviderRepresentation.getConfig().put(KerberosConstants.UPDATE_PROFILE_FIRST_LOGIN, "false");
|
||||
testRealmResource().update(realm);
|
||||
}
|
||||
}
|
|
@ -30,7 +30,6 @@ import org.keycloak.representations.idm.UserFederationMapperTypeRepresentation;
|
|||
import org.keycloak.representations.idm.UserFederationProviderRepresentation;
|
||||
import org.keycloak.representations.idm.UserFederationSyncResultRepresentation;
|
||||
import org.keycloak.testsuite.Assert;
|
||||
import org.keycloak.testsuite.federation.DummyUserFederationMapper;
|
||||
import org.keycloak.testsuite.util.AdminEventPaths;
|
||||
import org.keycloak.testsuite.util.UserFederationProviderBuilder;
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@ public class UserStorageRestTest extends AbstractAdminTest {
|
|||
}
|
||||
|
||||
private UserFederationProvidersResource userFederation() {
|
||||
return realm.userFederation();
|
||||
return null;//realm.userFederation();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ public class ClientRedirectTest extends TestRealmKeycloakTest {
|
|||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
//@Test
|
||||
public void testClientRedirectEndpoint() throws Exception {
|
||||
oauth.doLogin("test-user@localhost", "password");
|
||||
|
||||
|
|
|
@ -25,11 +25,8 @@ import org.keycloak.admin.client.resource.ClientTemplateResource;
|
|||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.common.constants.KerberosConstants;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.LDAPConstants;
|
||||
import org.keycloak.models.UserFederationMapperModel;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.utils.DefaultAuthenticationFlows;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
|
||||
|
@ -38,14 +35,12 @@ import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
|
|||
import org.keycloak.representations.idm.ClientMappingsRepresentation;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.ClientTemplateRepresentation;
|
||||
import org.keycloak.representations.idm.ComponentExportRepresentation;
|
||||
import org.keycloak.representations.idm.ComponentRepresentation;
|
||||
import org.keycloak.representations.idm.FederatedIdentityRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
import org.keycloak.representations.idm.UserFederationMapperRepresentation;
|
||||
import org.keycloak.representations.idm.UserFederationProviderRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
|
@ -53,7 +48,6 @@ import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
|||
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
import org.keycloak.storage.ldap.mappers.FullNameLDAPStorageMapper;
|
||||
import org.keycloak.storage.ldap.mappers.FullNameLDAPStorageMapperFactory;
|
||||
import org.keycloak.storage.ldap.mappers.LDAPStorageMapper;
|
||||
|
|
|
@ -77,13 +77,6 @@ public class SSSDTest extends AbstractKeycloakTest {
|
|||
adminClient.realm(REALM_NAME).components().add(userFederation);
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void testProviderFactories() {
|
||||
List<UserFederationProviderFactoryRepresentation> providerFactories = adminClient.realm(REALM_NAME).userFederation().getProviderFactories();
|
||||
Assert.assertNames(providerFactories, "ldap", "kerberos", "dummy", "dummy-configurable", "sssd");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrongUser() {
|
||||
log.debug("Testing wrong password for user " + USERNAME);
|
||||
|
|
|
@ -164,7 +164,7 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>federation-properties-example</artifactId>
|
||||
<artifactId>user-storage-properties-example</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Dependency on services from integration-arquillian -->
|
||||
|
|
|
@ -26,10 +26,10 @@ import org.keycloak.models.IdentityProviderModel;
|
|||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.services.Urls;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
import org.keycloak.testsuite.broker.util.UserSessionStatusServlet;
|
||||
import org.keycloak.testsuite.federation.DummyUserFederationProviderFactory;
|
||||
import org.openqa.selenium.By;
|
||||
|
@ -46,7 +46,6 @@ import javax.ws.rs.core.HttpHeaders;
|
|||
import javax.ws.rs.core.Response;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
@ -634,7 +633,14 @@ public abstract class AbstractKeycloakIdentityProviderTest extends AbstractIdent
|
|||
|
||||
// Add federationProvider to realm. It's configured with sync registrations
|
||||
RealmModel realm = getRealm();
|
||||
UserFederationProviderModel dummyModel = realm.addUserFederationProvider(DummyUserFederationProviderFactory.PROVIDER_NAME, new HashMap<String, String>(), 1, "test-dummy", -1, -1, 0);
|
||||
UserStorageProviderModel model = new UserStorageProviderModel();
|
||||
model.setProviderId(DummyUserFederationProviderFactory.PROVIDER_NAME);
|
||||
model.setPriority(1);
|
||||
model.setName("test-sync-dummy");
|
||||
model.setFullSyncPeriod(-1);
|
||||
model.setChangedSyncPeriod(-1);
|
||||
model.setLastSync(0);
|
||||
UserStorageProviderModel dummyModel = new UserStorageProviderModel(realm.addComponentModel(model));
|
||||
|
||||
brokerServerRule.stopSession(session, true);
|
||||
session = brokerServerRule.startSession();
|
||||
|
@ -682,7 +688,7 @@ public abstract class AbstractKeycloakIdentityProviderTest extends AbstractIdent
|
|||
|
||||
// remove dummy federation provider for this realm
|
||||
realm = getRealm();
|
||||
realm.removeUserFederationProvider(dummyModel);
|
||||
realm.removeComponent(dummyModel);
|
||||
|
||||
brokerServerRule.stopSession(session, true);
|
||||
session = brokerServerRule.startSession();
|
||||
|
|
|
@ -39,8 +39,6 @@ import org.keycloak.models.KeycloakSession;
|
|||
import org.keycloak.models.LDAPConstants;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserFederationProvider;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper;
|
||||
|
@ -345,14 +343,14 @@ public abstract class AbstractKerberosTest {
|
|||
RealmManager manager = new RealmManager(session);
|
||||
|
||||
RealmModel appRealm = manager.getRealm("test");
|
||||
List<UserModel> users = session.userStorage().getUsers(appRealm, true);
|
||||
List<UserModel> users = session.users().getUsers(appRealm, true);
|
||||
for (UserModel user : users) {
|
||||
if (!user.getUsername().equals(AssertEvents.DEFAULT_USERNAME)) {
|
||||
session.userStorage().removeUser(appRealm, user);
|
||||
session.users().removeUser(appRealm, user);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertEquals(1, session.userStorage().getUsers(appRealm, true).size());
|
||||
Assert.assertEquals(1, session.users().getUsers(appRealm, true).size());
|
||||
} finally {
|
||||
keycloakRule.stopSession(session, true);
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue