added changes for 7
This commit is contained in:
parent
cb1b73930f
commit
f9cb6f1c6b
10 changed files with 20 additions and 22 deletions
|
@ -1,7 +1,7 @@
|
||||||
[[_user-storage-spi]]
|
[[_user-storage-spi]]
|
||||||
== User Storage SPI
|
== User Storage SPI
|
||||||
|
|
||||||
You can use the User Storage SPI to write extensions to {project_name} to connect to external user databases and credential stores. The built-in LDAP and ActiveDirectory support is an implementation of this SPI in action. Out of the box, {project_name} uses its local database to create, update, and look up users and validation credentials. Often though, organizations have existing external proprietary user databases that they cannot migrate to {project_name}'s data model. For those situations, application developers can write implementations of the User Storage SPI to bridge the external user store and the internal user object model that {project_name} uses to log in users and manage them.
|
You can use the User Storage SPI to write extensions to {project_name} to connect to external user databases and credential stores. The built-in LDAP and ActiveDirectory support is an implementation of this SPI in action. Out of the box, {project_name} uses its local database to create, update, and look up users and validate credentials. Often though, organizations have existing external proprietary user databases that they cannot migrate to {project_name}'s data model. For those situations, application developers can write implementations of the User Storage SPI to bridge the external user store and the internal user object model that {project_name} uses to log in users and manage them.
|
||||||
|
|
||||||
When the {project_name} runtime needs to look up a user, such as when a user is logging in, it performs a number of steps to locate the user. It first looks to see if the user is in the user cache; if the user is found it uses that in-memory representation. Then it looks for the user within the {project_name} local database. If the user is not found, it then loops through User Storage SPI provider implementations to perform the user query until one of them returns the user the runtime is looking for. The provider queries the external user store for the user and maps the external data representation of the user to {project_name}'s user metamodel.
|
When the {project_name} runtime needs to look up a user, such as when a user is logging in, it performs a number of steps to locate the user. It first looks to see if the user is in the user cache; if the user is found it uses that in-memory representation. Then it looks for the user within the {project_name} local database. If the user is not found, it then loops through User Storage SPI provider implementations to perform the user query until one of them returns the user the runtime is looking for. The provider queries the external user store for the user and maps the external data representation of the user to {project_name}'s user metamodel.
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
=== Augmenting External Storage
|
=== Augmenting External Storage
|
||||||
|
|
||||||
The `PropertyProfileUserStorageProvider` example is really limited. While we will be able to login with users stored
|
The `PropertyFileUserStorageProvider` example is really limited. While we will be able to login with users stored
|
||||||
in a property file, we won't be able to do much else. If users loaded by this provider need special role or group
|
in a property file, we won't be able to do much else. If users loaded by this provider need special role or group
|
||||||
mappings to fully access particular applications there is no way for us to add additional role mappings to these users.
|
mappings to fully access particular applications there is no way for us to add additional role mappings to these users.
|
||||||
You also can't modify or add additional important attributes like email, first and last name.
|
You also can't modify or add additional important attributes like email, first and last name.
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
|
|
||||||
=== User Caches
|
=== User Caches
|
||||||
|
|
||||||
When a user is loaded by ID, username, or email queries it is cached. When a user is cached, it iterates through
|
When a user object is loaded by ID, username, or email queries it is cached. When a user object is bing cached, it iterates through
|
||||||
the entire `UserModel` interface and pulls this information to a local in-memory-only cache. In a cluster, this cache
|
the entire `UserModel` interface and pulls this information to a local in-memory-only cache. In a cluster, this cache
|
||||||
is still local, but it becomes an invalidation cache. When a user is modified, it is evicted. This eviction event
|
is still local, but it becomes an invalidation cache. When a user object is modified, it is evicted. This eviction event
|
||||||
is propagated to the entire cluster so that the other nodes' user cache is also invalidated.
|
is propagated to the entire cluster so that the other nodes' user cache is also invalidated.
|
||||||
|
|
||||||
==== Managing the user cache
|
==== Managing the user cache
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
=== Configuration Techniques
|
=== Configuration Techniques
|
||||||
|
|
||||||
Our `PropertyFileUserStorageProvider` example is bit contrived. It is hardcoded to a property file that is embedded in the jar of the provider. Not very useful at all. We might want to make the location of this file configurable per instance of the provider. In other words, we might want to reuse this provider mulitple times in multiple different realms and point to completely different user property files. We'll also want to perform this configuration within the administration console UI.
|
Our `PropertyFileUserStorageProvider` example is bit contrived. It is hardcoded to a property file that is embedded in the jar of the provider, which is not terribly useful. We might want to make the location of this file configurable per instance of the provider. In other words, we might want to reuse this provider mulitple times in multiple different realms and point to completely different user property files. We'll also want to perform this configuration within the administration console UI.
|
||||||
|
|
||||||
The `UserStorageProviderFactory` has additional methods you can implement that handle provider configuration. You describe the variables you want to configure per provider and the administration console automatically renders a generic input page to gather this configuration. When implemented, callback methods also validate the configuration before it is saved, when a provider is created for the first time, and when it is updated. `UserStorageProviderFactory` inherits these methods from the `org.keycloak.component.ComponentFactory` interface.
|
The `UserStorageProviderFactory` has additional methods you can implement that handle provider configuration. You describe the variables you want to configure per provider and the administration console automatically renders a generic input page to gather this configuration. When implemented, callback methods also validate the configuration before it is saved, when a provider is created for the first time, and when it is updated. `UserStorageProviderFactory` inherits these methods from the `org.keycloak.component.ComponentFactory` interface.
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ There are some obvious disadvantages though to using an import strategy:
|
||||||
* Looking up a user for the first time will require multiple updates to {project_name} database. This can
|
* Looking up a user for the first time will require multiple updates to {project_name} database. This can
|
||||||
be a big performance loss under load and put a lot of strain on the {project_name} database. The user federated
|
be a big performance loss under load and put a lot of strain on the {project_name} database. The user federated
|
||||||
storage approach will only store extra data as needed and may never be used depending on the capabilities of your external store.
|
storage approach will only store extra data as needed and may never be used depending on the capabilities of your external store.
|
||||||
* With the import approach, you have to keep local keycloak storage and external storage in sync. The User Storage SPI
|
* With the import approach, you have to keep local {project_name} storage and external storage in sync. The User Storage SPI
|
||||||
has capability interfaces that you can implement to support synchronization, but this can quickly become painful and messy.
|
has capability interfaces that you can implement to support synchronization, but this can quickly become painful and messy.
|
||||||
|
|
||||||
To implement the import strategy you simply check to see first if the user has been imported locally. If so return the
|
To implement the import strategy you simply check to see first if the user has been imported locally. If so return the
|
||||||
|
@ -51,7 +51,7 @@ begin first by modifying the `createAdapter()` method.
|
||||||
|
|
||||||
In this method we call the `KeycloakSession.userLocalStorage()` method to obtain a reference to local {project_name}
|
In this method we call the `KeycloakSession.userLocalStorage()` method to obtain a reference to local {project_name}
|
||||||
user storage. We see if the user is stored locally, if not, we add it locally. Do not set the `id` of the local user.
|
user storage. We see if the user is stored locally, if not, we add it locally. Do not set the `id` of the local user.
|
||||||
Let {project_name} set auto generate the `id`. Also note that we call
|
Let {project_name} automatically generate the `id`. Also note that we call
|
||||||
`UserModel.setFederationLink()` and pass in the ID of the `ComponentModel` of our provider. This sets a link between
|
`UserModel.setFederationLink()` and pass in the ID of the `ComponentModel` of our provider. This sets a link between
|
||||||
the provider and the imported user.
|
the provider and the imported user.
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,7 @@ not proxy the user correctly and your provider won't work.
|
||||||
You must put the `@Remove` annotation on the `close()` method of your provider. If you do not, the stateful bean
|
You must put the `@Remove` annotation on the `close()` method of your provider. If you do not, the stateful bean
|
||||||
will never be cleaned up and you might eventually see error messages.
|
will never be cleaned up and you might eventually see error messages.
|
||||||
|
|
||||||
Implementations of `UserStorageProviderFactory` are required to be plain java objects. Your factory class would
|
Implementations of `UserStorageProvider` are required to be plain Java objects. Your factory class would
|
||||||
perform a JNDI lookup of the Stateful EJB in its create() method.
|
perform a JNDI lookup of the Stateful EJB in its create() method.
|
||||||
|
|
||||||
[source,java]
|
[source,java]
|
||||||
|
@ -81,9 +81,7 @@ file as well as any JPA `@Entity` classes.
|
||||||
|
|
||||||
WARNING: When using JPA any additional datasource must be an XA datasource. The {project_name} datasource
|
WARNING: When using JPA any additional datasource must be an XA datasource. The {project_name} datasource
|
||||||
is not an XA datasource. If you interact with two or more non-XA datasources in the same transaction, the server returns
|
is not an XA datasource. If you interact with two or more non-XA datasources in the same transaction, the server returns
|
||||||
an error message. Only one non-XA resource is permitted in a single transaction.
|
an error message. Only one non-XA resource is permitted in a single transaction. See the {appserver_name} manual for more details on deploying an XA datasource.
|
||||||
|
|
||||||
See the {appserver_name} manual for more details on deploying an XA datasource.
|
|
||||||
|
|
||||||
CDI is not supported.
|
CDI is not supported.
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
NOTE: This chapter is only applicable if you have implemented a provider using the earlier (and now removed)
|
NOTE: This chapter is only applicable if you have implemented a provider using the earlier (and now removed)
|
||||||
User Federation SPI.
|
User Federation SPI.
|
||||||
|
|
||||||
In Keycloak version 2.4.0 and earlier there was a User Federation SPI. Red Hat Single Sign-On version 7.0, although unsupported, also had
|
In Keycloak version 2.4.0 and earlier there was a User Federation SPI. Red Hat Single Sign-On version 7.0, although unsupported, had
|
||||||
this earlier SPI available as well. This earlier User Federation SPI has been removed from Keycloak version 2.5.0 and Red Hat Single Sign-On version 7.1.
|
this earlier SPI available as well. This earlier User Federation SPI has been removed from Keycloak version 2.5.0 and Red Hat Single Sign-On version 7.1.
|
||||||
However, if you have written a provider with this earlier SPI, this chapter discusses some strategies you can use to port it.
|
However, if you have written a provider with this earlier SPI, this chapter discusses some strategies you can use to port it.
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ There are some obvious disadvantages though to using an import strategy:
|
||||||
* Looking up a user for the first time will require multiple updates to {project_name} database. This can
|
* Looking up a user for the first time will require multiple updates to {project_name} database. This can
|
||||||
be a big performance loss under load and put a lot of strain on the {project_name} database. The user federated
|
be a big performance loss under load and put a lot of strain on the {project_name} database. The user federated
|
||||||
storage approach will only store extra data as needed and might never be used depending on the capabilities of your external store.
|
storage approach will only store extra data as needed and might never be used depending on the capabilities of your external store.
|
||||||
* With the import approach, you have to keep local keycloak storage and external storage in sync. The User Storage SPI
|
* With the import approach, you have to keep local {project_name} storage and external storage in sync. The User Storage SPI
|
||||||
has capability interfaces that you can implement to support synchronization, but this can quickly become painful and messy.
|
has capability interfaces that you can implement to support synchronization, but this can quickly become painful and messy.
|
||||||
|
|
||||||
==== UserFederationProvider vs. UserStorageProvider
|
==== UserFederationProvider vs. UserStorageProvider
|
||||||
|
@ -44,7 +44,7 @@ Also note that in the earlier SPI, this method was called every time the user wa
|
||||||
In the later SPI, this method is only called when the local user is loaded from local storage. If the local user is cached,
|
In the later SPI, this method is only called when the local user is loaded from local storage. If the local user is cached,
|
||||||
then the `ImportedUserValidation.validate()` method is not called at all.
|
then the `ImportedUserValidation.validate()` method is not called at all.
|
||||||
|
|
||||||
The `UserFederationProvider.isValid()` method no longer exists in the later model.
|
The `UserFederationProvider.isValid()` method no longer exists in the later SPI.
|
||||||
|
|
||||||
The `UserFederationProvider` methods `synchronizeRegistrations()`, `registerUser()`, and `removeUser()` have been
|
The `UserFederationProvider` methods `synchronizeRegistrations()`, `registerUser()`, and `removeUser()` have been
|
||||||
moved to the `UserRegistrationProvider` capability interface. This new interface is optional to implement so if your
|
moved to the `UserRegistrationProvider` capability interface. This new interface is optional to implement so if your
|
||||||
|
@ -80,8 +80,8 @@ with the same provider ID (i.e., "ldap", "kerberos") as the earlier User Federat
|
||||||
|
|
||||||
So, knowing this there are different approaches you can take.
|
So, knowing this there are different approaches you can take.
|
||||||
|
|
||||||
. You can remove the earlier provider in your earlier {project_name} deployment. This will remove all local linked copies
|
. You can remove the earlier provider in your earlier {project_name} deployment. This will remove thedd local linked copies
|
||||||
of imported users. Then, when you upgrade {project_name}, just deploy and configure your new provider for your realm.
|
of all users you imported. Then, when you upgrade {project_name}, just deploy and configure your new provider for your realm.
|
||||||
. The second option is to write your new provider making sure it has the same provider ID: `UserStorageProviderFactory.getId()`.
|
. The second option is to write your new provider making sure it has the same provider ID: `UserStorageProviderFactory.getId()`.
|
||||||
Make sure this provider is in the `deploy/` directory of the new {project_name} installation. Boot the server, and have
|
Make sure this provider is in the `deploy/` directory of the new {project_name} installation. Boot the server, and have
|
||||||
the built-in migration script convert from the earlier data model to the later data model. In this case all your earlier linked imported
|
the built-in migration script convert from the earlier data model to the later data model. In this case all your earlier linked imported
|
||||||
|
|
|
@ -66,7 +66,7 @@ package org.keycloak.storage;
|
||||||
public interface UserStorageProviderFactory<T extends UserStorageProvider> extends ComponentFactory<T, UserStorageProvider> {
|
public interface UserStorageProviderFactory<T extends UserStorageProvider> extends ComponentFactory<T, UserStorageProvider> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the name of the provider and will be showed in the admin console as an option.
|
* This is the name of the provider and will be shown in the admin console as an option.
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
|
@ -102,10 +102,10 @@ public class FileProviderFactory implements UserStorageProviderFactory<FileProvi
|
||||||
----
|
----
|
||||||
|
|
||||||
The `getId()` method returns the name of the User Storage provider. This id will be displayed in the admin console's
|
The `getId()` method returns the name of the User Storage provider. This id will be displayed in the admin console's
|
||||||
`UserFederation` page when you want to enable the provider for a specific realm.
|
User Federation page when you want to enable the provider for a specific realm.
|
||||||
|
|
||||||
The `create()` method is responsible for allocating an instance of the provider class. It takes a `org.keycloak.models.KeycloakSession`
|
The `create()` method is responsible for allocating an instance of the provider class. It takes a `org.keycloak.models.KeycloakSession`
|
||||||
parameter. This object can be used to lookup other information and metadata as well as provide access to various other
|
parameter. This object can be used to look up other information and metadata as well as provide access to various other
|
||||||
components within the runtime. The `ComponentModel` parameter represents how the provider was enabled and configured within
|
components within the runtime. The `ComponentModel` parameter represents how the provider was enabled and configured within
|
||||||
a specific realm. It contains the instance id of the enabled provider as well as any configuration you may have specified
|
a specific realm. It contains the instance id of the enabled provider as well as any configuration you may have specified
|
||||||
for it when you enabled through the admin console.
|
for it when you enabled through the admin console.
|
||||||
|
|
|
@ -56,7 +56,7 @@ Notice that when adding a user we set the password value of the property map to
|
||||||
we can't have null values for a property in the property value. We also have to modify the `CredentialInputValidator`
|
we can't have null values for a property in the property value. We also have to modify the `CredentialInputValidator`
|
||||||
methods to reflect this.
|
methods to reflect this.
|
||||||
|
|
||||||
`addUser()` will be called if the provider implements the `UserRegistrationProvider` interface. If your provider has
|
The `addUser()` method will be called if the provider implements the `UserRegistrationProvider` interface. If your provider has
|
||||||
a configuration switch to turn of adding a user, returning `null` from this method will skip the provider and call
|
a configuration switch to turn of adding a user, returning `null` from this method will skip the provider and call
|
||||||
the next one.
|
the next one.
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ Since we can now save our property file, it also makes sense to allow password u
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
We can now also implement disabling a password too.
|
We can now also implement disabling a password.
|
||||||
|
|
||||||
.PropertyFileUserStorageProvider
|
.PropertyFileUserStorageProvider
|
||||||
[source,java]
|
[source,java]
|
||||||
|
|
|
@ -208,7 +208,7 @@ The `UserStorageProviderFactory` interface has an optional `init()` method you c
|
||||||
|
|
||||||
In our `init()` method implementation, we find the property file containing our user declarations from the classpath. We then load the `properties` field with the username and password combinations stored there.
|
In our `init()` method implementation, we find the property file containing our user declarations from the classpath. We then load the `properties` field with the username and password combinations stored there.
|
||||||
|
|
||||||
The `Config.Scope` parameter is factory configuration that can be set up within `standalone.xml`, `standalone-ha.xml`, or `domain.xml`. For more information on where the `standalone.xml`, `standalone-ha.xml`, or `domain.xml` file resides see the link:{installguide_link}[{installguide_name}].
|
The `Config.Scope` parameter is factory configuration that can be set up within `standalone.xml`, `standalone-ha.xml`, or `domain.xml`. For more information on where these files reside see the link:{installguide_link}[{installguide_name}].
|
||||||
|
|
||||||
For example, by adding the following to `standalone.xml`:
|
For example, by adding the following to `standalone.xml`:
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue