keycloak-uncached

Changes

Details

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 359c37c..d13d16a 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 <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;
     }
 
diff --git a/examples/providers/federation-provider/pom.xml b/examples/providers/federation-provider/pom.xml
new file mode 100755
index 0000000..ccf28f9
--- /dev/null
+++ b/examples/providers/federation-provider/pom.xml
@@ -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>
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 0000000..4548c67
--- /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 <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() {
+
+    }
+}
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 0000000..39b4d2c
--- /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 <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() {
+
+    }
+}
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 0000000..4b65685
--- /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 <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";
+    }
+}
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 0000000..107f164
--- /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 <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");
+    }
+
+}
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 0000000..ae39ce6
--- /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 <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";
+    }
+}
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 0000000..dbebfcc
--- /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 <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;
+        }
+    }
+
+
+
+}
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 0000000..0844c99
--- /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 <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);
+    }
+}
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 0000000..5230c18
--- /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 <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();
+            }
+        }
+    }
+}
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 0000000..fbbc583
--- /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 6844c7c..d706526 100755
--- a/examples/providers/pom.xml
+++ b/examples/providers/pom.xml
@@ -27,5 +27,6 @@
     <modules>
         <module>audit-listener-sysout</module>
         <module>audit-provider-mem</module>
+        <module>federation-provider</module>
     </modules>
 </project>
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 969d9b2..8da274e 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 <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();
     }
 }
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 39a51ef..7b00987 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 <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();
 }
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 2314a67..0177ea1 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 <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;
     }