From a903eb76054f8ef76055100a68fff2edf92987cd Mon Sep 17 00:00:00 2001 From: mposolda Date: Tue, 27 May 2014 21:29:47 +0200 Subject: [PATCH 1/2] Added example for AuthenticationProvider SPI --- .../authentication-properties/README.md | 20 +++++ .../authentication-properties/pom.xml | 45 +++++++++++ .../PropertiesAuthenticationProvider.java | 75 +++++++++++++++++++ ...opertiesAuthenticationProviderFactory.java | 62 +++++++++++++++ ...thentication.AuthenticationProviderFactory | 1 + .../src/main/resources/users.properties | 2 + examples/providers/pom.xml | 1 + 7 files changed, 206 insertions(+) create mode 100644 examples/providers/authentication-properties/README.md create mode 100644 examples/providers/authentication-properties/pom.xml create mode 100644 examples/providers/authentication-properties/src/main/java/org/keycloak/examples/providers/authentication/PropertiesAuthenticationProvider.java create mode 100644 examples/providers/authentication-properties/src/main/java/org/keycloak/examples/providers/authentication/PropertiesAuthenticationProviderFactory.java create mode 100644 examples/providers/authentication-properties/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticationProviderFactory create mode 100644 examples/providers/authentication-properties/src/main/resources/users.properties diff --git a/examples/providers/authentication-properties/README.md b/examples/providers/authentication-properties/README.md new file mode 100644 index 0000000000..b55db4c6b3 --- /dev/null +++ b/examples/providers/authentication-properties/README.md @@ -0,0 +1,20 @@ +Example Authentication Provider based on property file values +============================================================= + +* To deploy copy "target/authentication-properties-example.jar" to "standalone/deployments/auth-server.war/WEB-INF/lib" . Then edit "standalone/configuration/keycloak-server.json" and add this: +````shell + "authentication": { + "properties": { + "propertiesFileLocation": "users.properties" + } + } +```` + +* Then start (or restart)the server. Once started open the admin console, select your realm, then click on "Authentication" and then "Add provider" and select "properties" from the list. +This will mean that realm will use PropertiesAuthenticationProvider for authentication. + +* Once you try to login to realm, you can login with username/password like "joe/password1" or "james/password2" . Once joe is authenticated, +you can see in Keycloak admin console in "Users" list that user "joe" was added to the list. + +* You can try to login as joe and then go to [http://localhost:8080/auth/realms/keycloak-admin/account/password](http://localhost:8080/auth/realms/keycloak-admin/account/password) and change the password. +You will then be able to logout and login with new password because properties were updated. But this is just in memory-properties, so after server restart the password will be again "password1" . diff --git a/examples/providers/authentication-properties/pom.xml b/examples/providers/authentication-properties/pom.xml new file mode 100644 index 0000000000..844566b480 --- /dev/null +++ b/examples/providers/authentication-properties/pom.xml @@ -0,0 +1,45 @@ + + + examples-providers-pom + org.keycloak + 1.0-beta-1-SNAPSHOT + ../pom.xml + + Properties Authentication Provider Example + + 4.0.0 + + authentication-properties-example + jar + + + + org.keycloak + keycloak-core + ${project.version} + provided + + + org.keycloak + keycloak-model-api + ${project.version} + provided + + + org.keycloak + keycloak-authentication-api + ${project.version} + provided + + + org.jboss.logging + jboss-logging + provided + + + + + authentication-properties-example + + diff --git a/examples/providers/authentication-properties/src/main/java/org/keycloak/examples/providers/authentication/PropertiesAuthenticationProvider.java b/examples/providers/authentication-properties/src/main/java/org/keycloak/examples/providers/authentication/PropertiesAuthenticationProvider.java new file mode 100644 index 0000000000..87d0b8e064 --- /dev/null +++ b/examples/providers/authentication-properties/src/main/java/org/keycloak/examples/providers/authentication/PropertiesAuthenticationProvider.java @@ -0,0 +1,75 @@ +package org.keycloak.examples.providers.authentication; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import org.jboss.logging.Logger; +import org.keycloak.authentication.AuthProviderStatus; +import org.keycloak.authentication.AuthUser; +import org.keycloak.authentication.AuthenticationProvider; +import org.keycloak.authentication.AuthenticationProviderException; +import org.keycloak.models.RealmModel; + +/** + * @author Marek Posolda + */ +public class PropertiesAuthenticationProvider implements AuthenticationProvider { + + private static final Logger log = Logger.getLogger(PropertiesAuthenticationProvider.class); + + private final Properties properties; + + public PropertiesAuthenticationProvider(Properties properties) { + this.properties = properties; + } + + @Override + public String getName() { + return "properties"; + } + + @Override + public List getAvailableOptions() { + return Collections.emptyList(); + } + + @Override + public AuthUser getUser(RealmModel realm, Map configuration, String username) throws AuthenticationProviderException { + if (properties.getProperty(username) != null) { + return new AuthUser(username, username, getName()); + } else { + return null; + } + } + + @Override + public String registerUser(RealmModel realm, Map configuration, String username) throws AuthenticationProviderException { + // Registration ignored + return username; + } + + @Override + public AuthProviderStatus validatePassword(RealmModel realm, Map configuration, String username, String password) throws AuthenticationProviderException { + String propertyFilePassword = properties.getProperty(username); + if (propertyFilePassword != null && propertyFilePassword.equals(password)) { + return AuthProviderStatus.SUCCESS; + } else { + return AuthProviderStatus.INVALID_CREDENTIALS; + } + } + + @Override + public boolean updateCredential(RealmModel realm, Map configuration, String username, String password) throws AuthenticationProviderException { + // Just update in memory (Won't survive restart) + log.info("Going to update password for user " + username + " in PropertiesAuthenticationProvider"); + properties.put(username, password); + return true; + } + + @Override + public void close() { + } + +} diff --git a/examples/providers/authentication-properties/src/main/java/org/keycloak/examples/providers/authentication/PropertiesAuthenticationProviderFactory.java b/examples/providers/authentication-properties/src/main/java/org/keycloak/examples/providers/authentication/PropertiesAuthenticationProviderFactory.java new file mode 100644 index 0000000000..b0aef64a84 --- /dev/null +++ b/examples/providers/authentication-properties/src/main/java/org/keycloak/examples/providers/authentication/PropertiesAuthenticationProviderFactory.java @@ -0,0 +1,62 @@ +package org.keycloak.examples.providers.authentication; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +import org.jboss.logging.Logger; +import org.keycloak.Config; +import org.keycloak.authentication.AuthenticationProvider; +import org.keycloak.authentication.AuthenticationProviderFactory; +import org.keycloak.provider.ProviderSession; + +/** + * @author Marek Posolda + */ +public class PropertiesAuthenticationProviderFactory implements AuthenticationProviderFactory { + + private static final Logger log = Logger.getLogger(PropertiesAuthenticationProviderFactory.class); + + private Properties properties; + + @Override + public AuthenticationProvider create(ProviderSession providerSession) { + return new PropertiesAuthenticationProvider(properties); + } + + @Override + public void init(Config.Scope config) { + String propsFileLocation = config.get("propertiesFileLocation"); + if (propsFileLocation == null) { + throw new IllegalStateException("Properties file location is not configured in PropertiesAuthenticationProviderFactory"); + } else { + log.info("Using properties file: " + propsFileLocation); + } + + this.properties = new Properties(); + InputStream propertiesStream = null; + try { + propertiesStream = getClass().getClassLoader().getResourceAsStream(propsFileLocation); + this.properties.load(propertiesStream); + } catch (IOException ioException) { + throw new RuntimeException(ioException); + } finally { + if (propertiesStream != null) { + try { + propertiesStream.close(); + } catch (IOException e) { + log.error("Error when closing InputStream", e); + } + } + } + } + + @Override + public void close() { + } + + @Override + public String getId() { + return "properties"; + } +} diff --git a/examples/providers/authentication-properties/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticationProviderFactory b/examples/providers/authentication-properties/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticationProviderFactory new file mode 100644 index 0000000000..0e35fee25f --- /dev/null +++ b/examples/providers/authentication-properties/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticationProviderFactory @@ -0,0 +1 @@ +org.keycloak.examples.providers.authentication.PropertiesAuthenticationProviderFactory \ No newline at end of file diff --git a/examples/providers/authentication-properties/src/main/resources/users.properties b/examples/providers/authentication-properties/src/main/resources/users.properties new file mode 100644 index 0000000000..6e1b8cd8d9 --- /dev/null +++ b/examples/providers/authentication-properties/src/main/resources/users.properties @@ -0,0 +1,2 @@ +joe=password1 +james=password2 diff --git a/examples/providers/pom.xml b/examples/providers/pom.xml index ff0cf917f5..223736ac23 100755 --- a/examples/providers/pom.xml +++ b/examples/providers/pom.xml @@ -35,5 +35,6 @@ audit-listener-sysout audit-provider-mem + authentication-properties From e67032a9d71278fb0577377a8e6ec57dfa317fea Mon Sep 17 00:00:00 2001 From: mposolda Date: Tue, 27 May 2014 22:37:06 +0200 Subject: [PATCH 2/2] Documentation about Authentication SPI, LDAP and Export/import --- docbook/reference/en/en-US/master.xml | 6 ++ .../en/en-US/modules/authentication-spi.xml | 66 +++++++++++++++++++ .../en/en-US/modules/export-import.xml | 31 +++++++++ docbook/reference/en/en-US/modules/ldap.xml | 12 ++++ 4 files changed, 115 insertions(+) create mode 100644 docbook/reference/en/en-US/modules/authentication-spi.xml create mode 100644 docbook/reference/en/en-US/modules/export-import.xml create mode 100644 docbook/reference/en/en-US/modules/ldap.xml diff --git a/docbook/reference/en/en-US/master.xml b/docbook/reference/en/en-US/master.xml index b8469c3c70..0eca23eb7a 100755 --- a/docbook/reference/en/en-US/master.xml +++ b/docbook/reference/en/en-US/master.xml @@ -23,6 +23,9 @@ + + + ]> @@ -103,6 +106,9 @@ This one is short &CORS; &Timeouts; &Audit; + &Authentication; + &Ldap; + &ExportImport; &Migration; diff --git a/docbook/reference/en/en-US/modules/authentication-spi.xml b/docbook/reference/en/en-US/modules/authentication-spi.xml new file mode 100644 index 0000000000..c19dffa9b7 --- /dev/null +++ b/docbook/reference/en/en-US/modules/authentication-spi.xml @@ -0,0 +1,66 @@ + + Authentication SPI + + Keycloak provides Authentication SPI, which allows to choose the AuthenticationProvider for authenticating users. + AuthenticationProvider is the interface, which states how will be your usernames/passwords validated. You can choose from + the set of available AuthenticationProviders or you can even implement and plug your own AuthenticationProvider, which + will allow to provide your own way how will Keycloak validates users and their passwords. + +
+ Available Authentication Providers + + + Model - This provider validates users and their passwords based on the Keycloak model. So it just delegates + to model implementation provided either by RDBMS or Mongo at this moment. This is default AuthenticationProvider, + which is configured for keycloak-admin realm by default and it's also automatically configured for newly created realms. + + External-model - This provider also uses Keycloak model, but it uses different realm to validate your users against. + For example if you want to create new realm "foo" and you want all users of already existing realm "bar" that they are automatically + able to login into realm "foo" with their usernames and passwords, you can choose this provider. + + Picketlink - This provider delegates Authentication to Picketlink IDM + framework. Right now, Picketlink IDM in Keycloak is configured to always use LDAP based Identity store, which means that picketlink provider + allows you to authenticate your users against LDAP server. Note that you will first need to configure LDAP server as described + here . PicketlinkAuthenticationProvider configured for the realm will automatically use LDAP configuration for this realm. + + + +
+
+ Features and configuration + + + + You can configure AuthenticationProviders separately for each realm. So for example you can choose that just realm + "foo" will use PicketlinkAuthenticationProvider and authenticate users against LDAP but realm "keycloak-admin" will still use default ModelAuthenticationProvider. + + + There is also possibility to choose more authentication providers for the realm, which actually means that Keycloak + will use first available AuthenticationProvider and just in case that user doesn't exist here, + it will fallback to second AuthenticationProvider in chain. So this may allow for example scenario, in which + you authenticate user against Keycloak database (model) and just if he doesn't exist in database, it will fallback to LDAP (picketlink). + + + You can configure for each AuthenticationProvider if you want to update passwords - option passwordUpdateSupported. + This means that when user update password or his profile through Keycloak UI, this change will be propagated into AuthenticationProvider. + So for example password in LDAP will be updated if it's true, but for read-only LDAP, you will likely switch it to false. + It also means that newly registered users will be propagated to particular AuthenticationProvider too, + but note that each user is always bind just to one AuthenticationProvider. + + + You can add/edit/remove AuthenticationProviders in the Authentication tab in admin console, which is under URL + http://localhost:8080/auth/admin/keycloak-admin/console/#/realms/YOUR_REALM/auth-settings + + + + +
+
+ Creating your own Authentication Provider + + You need to implement interface AuthenticationProvider and add the name of your AuthenticationProviderFactory class into + META-INF/services/org.keycloak.authentication.AuthenticationProviderFactory file inside your JAR with AuthenticationProvider. You also need to copy this JAR into + standalone/deployments/auth-server.war/WEB-INF/lib . The best is to look at example and try it out. + +
+
\ No newline at end of file diff --git a/docbook/reference/en/en-US/modules/export-import.xml b/docbook/reference/en/en-US/modules/export-import.xml new file mode 100644 index 0000000000..534ee80d61 --- /dev/null +++ b/docbook/reference/en/en-US/modules/export-import.xml @@ -0,0 +1,31 @@ + + Export and Import + + Export/import is useful especially if you want to migrate your whole Keycloak database from one environment to another or migrate to different database (For example from MySQL to Oracle). + You can trigger export/import at startup of Keycloak server and it's configurable with System properties right now. The fact it's done at server startup means that no-one can access Keycloak UI or REST endpoints + and edit Keycloak database on the fly when export or import is in progress. Otherwise it could lead to inconsistent results. + + + You can export/import your database either to directory on local filesystem (useful just for testing purposes or if your filesystem is properly protected) + or to encrypted ZIP file on local filesystem. Encrypted ZIP is recommended as export contains many sensitive informations like passwords of your users (even if they are hashed), + but also their email addresses, and especially private keys of the realms. + + + So to export the content of your Keycloak database into encrypted ZIP, you can execute Keycloak server with the System properties like: + +-Dkeycloak.migration.zipPassword= +]]> + Then you can move or copy the encrypted ZIP file into second environment and you can trigger import from it into Keycloak server with the same command but use + -Dkeycloak.migration.action=import instead of export . + + + To export into unencrypted directory you can use: + +]]> + And similarly for import just use -Dkeycloak.migration.action=import instead of export . + + \ No newline at end of file diff --git a/docbook/reference/en/en-US/modules/ldap.xml b/docbook/reference/en/en-US/modules/ldap.xml new file mode 100644 index 0000000000..dd7d4aba61 --- /dev/null +++ b/docbook/reference/en/en-US/modules/ldap.xml @@ -0,0 +1,12 @@ + + LDAP Integration + + Right now, LDAP server is configured separately for each Realm. Configuration is in admin console in tab Ldap + under realm settings. It's under URL like http://localhost:8080/auth/admin/keycloak-admin/console/index.html#/realms/YOUR_REALM/ldap-settings . + There is nothing like "shared" LDAP server for more realms in Keycloak, but it's planned for the future. + + + LDAP is currently used just for authentication of users done through PicketlinkAuthenticationProvider as described here . + In the future, we have plan to have full Sync SPI, which will allow one-way or two-way synchronization between LDAP server and Keycloak database including users and roles. + + \ No newline at end of file