Merge pull request #582 from patriot1burke/master

fed example
This commit is contained in:
Bill Burke 2014-08-02 22:18:47 -04:00
commit 0ae3eafe10
15 changed files with 561 additions and 8 deletions

View file

@ -2,6 +2,7 @@ package org.keycloak.representations.idm;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author <a href="mailto:bburke@redhat.com">Bill Burke</a>
@ -9,7 +10,7 @@ import java.util.Map;
public class UserFederationProviderFactoryRepresentation {
private String id;
private List<String> options;
private Set<String> options;
public String getId() {
return id;
@ -19,11 +20,11 @@ public class UserFederationProviderFactoryRepresentation {
this.id = id;
}
public List<String> getOptions() {
public Set<String> getOptions() {
return options;
}
public void setOptions(List<String> options) {
public void setOptions(Set<String> options) {
this.options = options;
}

View file

@ -0,0 +1,48 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>examples-providers-pom</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-beta-4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<name>Properties Authentication Provider Example</name>
<description/>
<modelVersion>4.0.0</modelVersion>
<artifactId>federation-properties-example</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>federation-properties-example</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -0,0 +1,76 @@
package org.keycloak.examples.federation.properties;
import org.keycloak.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.UserFederationProvider;
import org.keycloak.models.UserFederationProviderFactory;
import org.keycloak.models.UserFederationProviderModel;
import java.io.IOException;
import java.io.InputStream;
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) {
String path = model.getConfig().get("path");
if (path == null) {
throw new IllegalStateException("Path attribute not configured for provider");
}
Properties props = files.get(path);
if (props != null) return createProvider(session, model, props);
props = new Properties();
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
if (is == null) {
throw new IllegalStateException("Path attribute not configured for provider");
}
try {
props.load(is);
is.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
props.put(path, props);
return createProvider(session, model, props);
}
protected abstract BasePropertiesFederationProvider createProvider(KeycloakSession session, UserFederationProviderModel model, Properties props);
@Override
public Set<String> getConfigurationOptions() {
return configOptions;
}
@Override
public UserFederationProvider create(KeycloakSession session) {
return null;
}
@Override
public void init(Config.Scope config) {
}
@Override
public void close() {
}
}

View file

@ -0,0 +1,138 @@
package org.keycloak.examples.federation.properties;
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;
}
public static Set<String> getSupportedCredentialTypes() {
return supportedCredentialTypes;
}
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.updateCredential(UserCredentialModel.password(password));
return userModel;
}
return null;
}
@Override
public UserModel getUserByEmail(RealmModel realm, String email) {
return null;
}
@Override
public List<UserModel> searchByAttributes(Map<String, String> attributes, RealmModel realm, int maxResults) {
if (attributes.containsKey(USERNAME)) {
UserModel user = getUserByUsername(realm, attributes.get(USERNAME));
if (user != null) {
List<UserModel> list = new ArrayList<UserModel>(1);
list.add(user);
return list;
}
}
return Collections.emptyList();
}
@Override
public void preRemove(RealmModel realm) {
}
@Override
public void preRemove(RealmModel realm, RoleModel role) {
}
@Override
public boolean isValid(UserModel local) {
return properties.containsKey(local.getUsername());
}
@Override
public Set<String> getSupportedCredentialTypes(UserModel user) {
return supportedCredentialTypes;
}
@Override
public boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> input) {
for (UserCredentialModel cred : input) {
if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
String password = properties.getProperty(user.getUsername());
if (password == null) return false;
return password.equals(cred.getValue());
} else {
return false; // invalid cred type
}
}
return false;
}
@Override
public boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input) {
for (UserCredentialModel cred : input) {
if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
String password = properties.getProperty(user.getUsername());
if (password == null) return false;
return password.equals(cred.getValue());
} else {
return false; // invalid cred type
}
}
return true;
}
@Override
public void close() {
}
}

View file

@ -0,0 +1,34 @@
package org.keycloak.examples.federation.properties;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.UserFederationProvider;
import org.keycloak.models.UserFederationProviderFactory;
import org.keycloak.models.UserFederationProviderModel;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.List;
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 class ClasspathPropertiesFederationFactory extends BasePropertiesFederationFactory {
@Override
protected BasePropertiesFederationProvider createProvider(KeycloakSession session, UserFederationProviderModel model, Properties props) {
return new ClasspathPropertiesFederationProvider(session, model, props);
}
@Override
public String getId() {
return "classpath-properties";
}
}

View file

@ -0,0 +1,49 @@
package org.keycloak.examples.federation.properties;
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 class ClasspathPropertiesFederationProvider extends BasePropertiesFederationProvider {
public ClasspathPropertiesFederationProvider(KeycloakSession session, UserFederationProviderModel model, Properties properties) {
super(session, model, properties);
}
@Override
public UserModel proxy(UserModel local) {
return new ReadonlyUserModelProxy(local);
}
@Override
public boolean synchronizeRegistrations() {
return false;
}
@Override
public UserModel register(RealmModel realm, UserModel user) {
throw new IllegalStateException("Registration not supported");
}
@Override
public boolean removeUser(RealmModel realm, UserModel user) {
throw new IllegalStateException("Remove not supported");
}
}

View file

@ -0,0 +1,24 @@
package org.keycloak.examples.federation.properties;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.UserFederationProviderModel;
import java.util.Properties;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class FilePropertiesFederationFactory extends BasePropertiesFederationFactory {
@Override
protected BasePropertiesFederationProvider createProvider(KeycloakSession session, UserFederationProviderModel model, Properties props) {
return new FilePropertiesFederationProvider(session, props, model);
}
@Override
public String getId() {
return "file-properties";
}
}

View file

@ -0,0 +1,72 @@
package org.keycloak.examples.federation.properties;
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.io.FileOutputStream;
import java.io.IOException;
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 class FilePropertiesFederationProvider extends BasePropertiesFederationProvider {
public FilePropertiesFederationProvider(KeycloakSession session, Properties properties, UserFederationProviderModel model) {
super(session, model, properties);
}
@Override
public UserModel proxy(UserModel local) {
return new WritableUserModelProxy(local, this);
}
@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);
}
}
@Override
public UserModel register(RealmModel realm, UserModel user) {
synchronized (properties) {
properties.setProperty(user.getUsername(), "");
save();
}
return proxy(user);
}
@Override
public boolean removeUser(RealmModel realm, UserModel user) {
synchronized (properties) {
if (properties.remove(user.getUsername()) == null) return false;
save();
return true;
}
}
}

View file

@ -0,0 +1,38 @@
package org.keycloak.examples.federation.properties;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserCredentialValueModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.UserModelDelegate;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ReadonlyUserModelProxy extends UserModelDelegate {
public ReadonlyUserModelProxy(UserModel delegate) {
super(delegate);
}
@Override
public void setUsername(String username) {
throw new IllegalStateException("Username is readonly");
}
@Override
public void updateCredentialDirectly(UserCredentialValueModel cred) {
if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
throw new IllegalStateException("Passwords are readonly");
}
super.updateCredentialDirectly(cred);
}
@Override
public void updateCredential(UserCredentialModel cred) {
if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
throw new IllegalStateException("Passwords are readonly");
}
super.updateCredential(cred);
}
}

View file

@ -0,0 +1,66 @@
package org.keycloak.examples.federation.properties;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserCredentialValueModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.UserModelDelegate;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
/**
* @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;
}
@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();
}
}
@Override
public void updateCredentialDirectly(UserCredentialValueModel cred) {
if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
throw new IllegalStateException("Shouldn't be using this method");
}
super.updateCredentialDirectly(cred);
}
@Override
public void updateCredential(UserCredentialModel cred) {
if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
} else {
super.updateCredential(cred);
synchronized (provider.getProperties()) {
if (!provider.getProperties().containsKey(delegate.getUsername())) {
throw new IllegalStateException("no user of that in properties file");
}
provider.getProperties().put(delegate.getUsername(), cred.getValue());
provider.save();
}
}
}
}

View file

@ -0,0 +1,2 @@
org.keycloak.examples.federation.properties.ClasspathPropertiesFederationFactory
org.keycloak.examples.federation.properties.FilePropertiesFederationFactory

View file

@ -27,5 +27,6 @@
<modules>
<module>audit-listener-sysout</module>
<module>audit-provider-mem</module>
<module>federation-provider</module>
</modules>
</project>

View file

@ -9,6 +9,7 @@ import org.picketlink.idm.PartitionManager;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@ -46,7 +47,7 @@ public class LDAPFederationProviderFactory implements UserFederationProviderFact
}
@Override
public List<String> getConfigurationOptions() {
return Collections.emptyList();
public Set<String> getConfigurationOptions() {
return Collections.emptySet();
}
}

View file

@ -3,6 +3,7 @@ package org.keycloak.models;
import org.keycloak.provider.ProviderFactory;
import java.util.List;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -16,5 +17,5 @@ public interface UserFederationProviderFactory extends ProviderFactory<UserFeder
*
* @return
*/
List<String> getConfigurationOptions();
Set<String> getConfigurationOptions();
}

View file

@ -7,7 +7,9 @@ import org.keycloak.models.UserFederationProviderFactory;
import org.keycloak.models.UserFederationProviderModel;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -20,8 +22,8 @@ public class DummyUserFederationProviderFactory implements UserFederationProvide
}
@Override
public List<String> getConfigurationOptions() {
List<String> list = new ArrayList<String>();
public Set<String> getConfigurationOptions() {
Set<String> list = new HashSet<String>();
list.add("important.config");
return list;
}