From 6cf708f6e1ce3a7ccaa762e1701618cdab079104 Mon Sep 17 00:00:00 2001 From: Bill Burke Date: Sat, 2 Aug 2014 22:18:17 -0400 Subject: [PATCH] fed exampe --- ...derationProviderFactoryRepresentation.java | 7 +- .../providers/federation-provider/pom.xml | 48 ++++++ .../BasePropertiesFederationFactory.java | 76 ++++++++++ .../BasePropertiesFederationProvider.java | 138 ++++++++++++++++++ .../ClasspathPropertiesFederationFactory.java | 34 +++++ ...ClasspathPropertiesFederationProvider.java | 49 +++++++ .../FilePropertiesFederationFactory.java | 24 +++ .../FilePropertiesFederationProvider.java | 72 +++++++++ .../properties/ReadonlyUserModelProxy.java | 38 +++++ .../properties/WritableUserModelProxy.java | 66 +++++++++ ...cloak.models.UserFederationProviderFactory | 2 + examples/providers/pom.xml | 1 + .../ldap/LDAPFederationProviderFactory.java | 5 +- .../models/UserFederationProviderFactory.java | 3 +- .../DummyUserFederationProviderFactory.java | 6 +- 15 files changed, 561 insertions(+), 8 deletions(-) create mode 100755 examples/providers/federation-provider/pom.xml create mode 100755 examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/BasePropertiesFederationFactory.java create mode 100755 examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/BasePropertiesFederationProvider.java create mode 100755 examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/ClasspathPropertiesFederationFactory.java create mode 100755 examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/ClasspathPropertiesFederationProvider.java create mode 100755 examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/FilePropertiesFederationFactory.java create mode 100755 examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/FilePropertiesFederationProvider.java create mode 100755 examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/ReadonlyUserModelProxy.java create mode 100755 examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/WritableUserModelProxy.java create mode 100755 examples/providers/federation-provider/src/main/resources/META-INF/services/org.keycloak.models.UserFederationProviderFactory diff --git a/core/src/main/java/org/keycloak/representations/idm/UserFederationProviderFactoryRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/UserFederationProviderFactoryRepresentation.java index 359c37c789..d13d16a6b4 100755 --- a/core/src/main/java/org/keycloak/representations/idm/UserFederationProviderFactoryRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/idm/UserFederationProviderFactoryRepresentation.java @@ -2,6 +2,7 @@ package org.keycloak.representations.idm; import java.util.List; import java.util.Map; +import java.util.Set; /** * @author Bill Burke @@ -9,7 +10,7 @@ import java.util.Map; public class UserFederationProviderFactoryRepresentation { private String id; - private List options; + private Set options; public String getId() { return id; @@ -19,11 +20,11 @@ public class UserFederationProviderFactoryRepresentation { this.id = id; } - public List getOptions() { + public Set getOptions() { return options; } - public void setOptions(List options) { + public void setOptions(Set options) { this.options = options; } diff --git a/examples/providers/federation-provider/pom.xml b/examples/providers/federation-provider/pom.xml new file mode 100755 index 0000000000..ccf28f917f --- /dev/null +++ b/examples/providers/federation-provider/pom.xml @@ -0,0 +1,48 @@ + + + examples-providers-pom + org.keycloak + 1.0-beta-4-SNAPSHOT + ../pom.xml + + Properties Authentication Provider Example + + 4.0.0 + + federation-properties-example + jar + + + + org.keycloak + keycloak-core + ${project.version} + provided + + + org.keycloak + keycloak-model-api + ${project.version} + provided + + + org.jboss.logging + jboss-logging + provided + + + + + federation-properties-example + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + + + + diff --git a/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/BasePropertiesFederationFactory.java b/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/BasePropertiesFederationFactory.java new file mode 100755 index 0000000000..4548c67536 --- /dev/null +++ b/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/BasePropertiesFederationFactory.java @@ -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 Bill Burke + * @version $Revision: 1 $ + */ +public abstract class BasePropertiesFederationFactory implements UserFederationProviderFactory { + static final Set configOptions = new HashSet(); + protected ConcurrentHashMap files = new ConcurrentHashMap(); + + 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 getConfigurationOptions() { + return configOptions; + } + + @Override + public UserFederationProvider create(KeycloakSession session) { + return null; + } + + @Override + public void init(Config.Scope config) { + + } + + @Override + public void close() { + + } +} diff --git a/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/BasePropertiesFederationProvider.java b/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/BasePropertiesFederationProvider.java new file mode 100755 index 0000000000..39b4d2c0de --- /dev/null +++ b/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/BasePropertiesFederationProvider.java @@ -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 Bill Burke + * @version $Revision: 1 $ + */ +public abstract class BasePropertiesFederationProvider implements UserFederationProvider { + protected static final Set supportedCredentialTypes = new HashSet(); + 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 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 searchByAttributes(Map attributes, RealmModel realm, int maxResults) { + if (attributes.containsKey(USERNAME)) { + UserModel user = getUserByUsername(realm, attributes.get(USERNAME)); + if (user != null) { + List list = new ArrayList(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 getSupportedCredentialTypes(UserModel user) { + return supportedCredentialTypes; + } + + @Override + public boolean validCredentials(RealmModel realm, UserModel user, List 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() { + + } +} diff --git a/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/ClasspathPropertiesFederationFactory.java b/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/ClasspathPropertiesFederationFactory.java new file mode 100755 index 0000000000..4b65685f01 --- /dev/null +++ b/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/ClasspathPropertiesFederationFactory.java @@ -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 Bill Burke + * @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"; + } +} diff --git a/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/ClasspathPropertiesFederationProvider.java b/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/ClasspathPropertiesFederationProvider.java new file mode 100755 index 0000000000..107f164028 --- /dev/null +++ b/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/ClasspathPropertiesFederationProvider.java @@ -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 Bill Burke + * @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"); + } + +} diff --git a/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/FilePropertiesFederationFactory.java b/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/FilePropertiesFederationFactory.java new file mode 100755 index 0000000000..ae39ce6c18 --- /dev/null +++ b/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/FilePropertiesFederationFactory.java @@ -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 Bill Burke + * @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"; + } +} diff --git a/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/FilePropertiesFederationProvider.java b/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/FilePropertiesFederationProvider.java new file mode 100755 index 0000000000..dbebfccdad --- /dev/null +++ b/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/FilePropertiesFederationProvider.java @@ -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 Bill Burke + * @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; + } + } + + + +} diff --git a/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/ReadonlyUserModelProxy.java b/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/ReadonlyUserModelProxy.java new file mode 100755 index 0000000000..0844c991ed --- /dev/null +++ b/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/ReadonlyUserModelProxy.java @@ -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 Bill Burke + * @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); + } +} diff --git a/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/WritableUserModelProxy.java b/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/WritableUserModelProxy.java new file mode 100755 index 0000000000..5230c18650 --- /dev/null +++ b/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/WritableUserModelProxy.java @@ -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 Bill Burke + * @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(); + } + } + } +} diff --git a/examples/providers/federation-provider/src/main/resources/META-INF/services/org.keycloak.models.UserFederationProviderFactory b/examples/providers/federation-provider/src/main/resources/META-INF/services/org.keycloak.models.UserFederationProviderFactory new file mode 100755 index 0000000000..fbbc583662 --- /dev/null +++ b/examples/providers/federation-provider/src/main/resources/META-INF/services/org.keycloak.models.UserFederationProviderFactory @@ -0,0 +1,2 @@ +org.keycloak.examples.federation.properties.ClasspathPropertiesFederationFactory +org.keycloak.examples.federation.properties.FilePropertiesFederationFactory \ No newline at end of file diff --git a/examples/providers/pom.xml b/examples/providers/pom.xml index 6844c7c503..d706526147 100755 --- a/examples/providers/pom.xml +++ b/examples/providers/pom.xml @@ -27,5 +27,6 @@ audit-listener-sysout audit-provider-mem + federation-provider diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java index 969d9b2363..8da274e916 100755 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java +++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java @@ -9,6 +9,7 @@ import org.picketlink.idm.PartitionManager; import java.util.Collections; import java.util.List; +import java.util.Set; /** * @author Marek Posolda @@ -46,7 +47,7 @@ public class LDAPFederationProviderFactory implements UserFederationProviderFact } @Override - public List getConfigurationOptions() { - return Collections.emptyList(); + public Set getConfigurationOptions() { + return Collections.emptySet(); } } diff --git a/model/api/src/main/java/org/keycloak/models/UserFederationProviderFactory.java b/model/api/src/main/java/org/keycloak/models/UserFederationProviderFactory.java index 39a51efdd2..7b00987cd0 100755 --- a/model/api/src/main/java/org/keycloak/models/UserFederationProviderFactory.java +++ b/model/api/src/main/java/org/keycloak/models/UserFederationProviderFactory.java @@ -3,6 +3,7 @@ package org.keycloak.models; import org.keycloak.provider.ProviderFactory; import java.util.List; +import java.util.Set; /** * @author Bill Burke @@ -16,5 +17,5 @@ public interface UserFederationProviderFactory extends ProviderFactory getConfigurationOptions(); + Set getConfigurationOptions(); } diff --git a/testsuite/integration/src/main/java/org/keycloak/testutils/DummyUserFederationProviderFactory.java b/testsuite/integration/src/main/java/org/keycloak/testutils/DummyUserFederationProviderFactory.java index 2314a67482..0177ea107a 100755 --- a/testsuite/integration/src/main/java/org/keycloak/testutils/DummyUserFederationProviderFactory.java +++ b/testsuite/integration/src/main/java/org/keycloak/testutils/DummyUserFederationProviderFactory.java @@ -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 Bill Burke @@ -20,8 +22,8 @@ public class DummyUserFederationProviderFactory implements UserFederationProvide } @Override - public List getConfigurationOptions() { - List list = new ArrayList(); + public Set getConfigurationOptions() { + Set list = new HashSet(); list.add("important.config"); return list; }