Merge pull request #23 from jenmalloy/fixheadertypo
fixed typo in migration header and cleaned up chapter
This commit is contained in:
commit
15dc756117
3 changed files with 57 additions and 57 deletions
|
@ -15,7 +15,7 @@
|
||||||
.. link:topics/user-storage/provider-capability-interfaces.adoc[Provider Capability Interfaces]
|
.. link:topics/user-storage/provider-capability-interfaces.adoc[Provider Capability Interfaces]
|
||||||
.. link:topics/user-storage/model-interfaces.adoc[Model Interfaces]
|
.. link:topics/user-storage/model-interfaces.adoc[Model Interfaces]
|
||||||
.. link:topics/user-storage/packaging.adoc[Packaging and Deployment]
|
.. link:topics/user-storage/packaging.adoc[Packaging and Deployment]
|
||||||
.. link:topics/user-storage/simple-example.adoc[Simple Read Only, Lookup Example]
|
.. link:topics/user-storage/simple-example.adoc[Simple Read-Only, Lookup Example]
|
||||||
.. link:topics/user-storage/configuration.adoc[Configuration Techniques]
|
.. link:topics/user-storage/configuration.adoc[Configuration Techniques]
|
||||||
.. link:topics/user-storage/registration-query.adoc[Add/Remove User and Query Capability interfaces]
|
.. link:topics/user-storage/registration-query.adoc[Add/Remove User and Query Capability interfaces]
|
||||||
.. link:topics/user-storage/augmenting.adoc[Augmenting External Storage]
|
.. link:topics/user-storage/augmenting.adoc[Augmenting External Storage]
|
||||||
|
@ -23,4 +23,4 @@
|
||||||
.. link:topics/user-storage/cache.adoc[User Caches]
|
.. link:topics/user-storage/cache.adoc[User Caches]
|
||||||
.. link:topics/user-storage/javaee.adoc[Leveraging Java EE]
|
.. link:topics/user-storage/javaee.adoc[Leveraging Java EE]
|
||||||
.. link:topics/user-storage/rest.adoc[REST Management API]
|
.. link:topics/user-storage/rest.adoc[REST Management API]
|
||||||
.. link:topics/user-storage/migration.adoc[Migratring from Old User Federation SPI]
|
.. link:topics/user-storage/migration.adoc[Migrating from an Earlier User Federation SPI]
|
||||||
|
|
|
@ -1,95 +1,95 @@
|
||||||
|
|
||||||
=== Migratring from Old User Federation SPI
|
=== Migrating from an Earlier User Federation SPI
|
||||||
|
|
||||||
NOTE: You do not need to read this chapter if you have not implemented a provider using the old (and 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.
|
||||||
|
|
||||||
Keycloak had an older User Federation SPI in Keycloak 2.4.0 and earlier. RH-SSO 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, also had
|
||||||
this older SPI available as well. This older User Federation SPI has been removed in Keycloak 2.5.0 and RH-SSO 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.
|
||||||
If you have written a provider with this older 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.
|
||||||
|
|
||||||
|
|
||||||
==== Import vs. Non-Import
|
==== Import vs. Non-Import
|
||||||
|
|
||||||
The old User Federation SPI required you to create a local copy of a user in the {{book.project.name}}'s database
|
The earlier User Federation SPI required you to create a local copy of a user in the {{book.project.name}}'s database
|
||||||
and import information from your exteral store to the local copy. This is no longer a requirement. You can still
|
and import information from your external store to the local copy. However, this is no longer a requirement. You can still
|
||||||
port your old provider as-is, but you should consider whether a non-import strategy might be a better approach.
|
port your earlier provider as-is, but you should consider whether a non-import strategy might be a better approach.
|
||||||
|
|
||||||
Advantages of Import Strategy:
|
Advantages of the import strategy:
|
||||||
|
|
||||||
* {{book.project.name}} basically becomes a persistence user cache for your external store. Once the user is imported
|
* {{book.project.name}} basically becomes a persistence user cache for your external store. Once the user is imported
|
||||||
you'll no longer hit the external store thus taking load off of it.
|
you'll no longer hit the external store, thus taking load off of it.
|
||||||
* If you are moving to {{book.project.name}} as your official user store and deprecating the old external store, you
|
* If you are moving to {{book.project.name}} as your official user store and deprecating the earlier external store, you
|
||||||
can slowly migrate applications to use {{book.project.name}}. When all applications have been migrated, unlink the
|
can slowly migrate applications to use {{book.project.name}}. When all applications have been migrated, unlink the
|
||||||
imported user, and retire the old legacy external store.
|
imported user, and retire the earlier legacy external store.
|
||||||
|
|
||||||
There are some obvious disadvantages though to using an import strategy:
|
There are some obvious disadvantages though to using an import strategy:
|
||||||
|
|
||||||
* Looking up a user for the first time will require multiple updates to {{book.project.name}} database. This can
|
* Looking up a user for the first time will require multiple updates to {{book.project.name}} database. This can
|
||||||
be a big performance loss under load and put a lot of strain on the {{book.project.name}} database. The user federated
|
be a big performance loss under load and put a lot of strain on the {{book.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 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 keycloak 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
|
||||||
|
|
||||||
The first thing to notice is that `UserFederationProvider` was a complete interface. You just implemented every method
|
The first thing to notice is that `UserFederationProvider` was a complete interface. You implemented every method
|
||||||
in this interface. `UserStorageProvider` instead has broken up this interface into multiple capability interfaces that
|
in this interface. However, `UserStorageProvider` has instead broken up this interface into multiple capability interfaces that
|
||||||
you implement as needed.
|
you implement as needed.
|
||||||
|
|
||||||
`UserFederationProvider.getUserByUsername()` and `getUserByEmail()` have exact equivalents in the new SPI. The difference
|
`UserFederationProvider.getUserByUsername()` and `getUserByEmail()` have exact equivalents in the new SPI. The difference
|
||||||
between the two is how you import. If you are going to continue with an import strategy, you no longer call
|
between the two is how you import. If you are going to continue with an import strategy, you no longer call
|
||||||
`KeycloakSession.userStorage().addUser()' to create the user locally. Instead you call `KeycloakSession.userLocalStorage().addUser()`.
|
`KeycloakSession.userStorage().addUser()' to create the user locally. Instead you call `KeycloakSession.userLocalStorage().addUser()`.
|
||||||
The `userStorage()` method no longer exists.
|
The `userStorage()` method no longer exists.
|
||||||
|
|
||||||
The `UserFederationProvider.validateAndProxy()` method has been moved to an optional capability interface, `ImportedUserValidation`.
|
The `UserFederationProvider.validateAndProxy()` method has been moved to an optional capability interface, `ImportedUserValidation`.
|
||||||
You'll want to implement this interface if you are porting your old provider as-is.
|
You want to implement this interface if you are porting your earlier provider as-is.
|
||||||
Also note that in the old SPI, this method was called every time the user was accessed, even if the local user is in the cache.
|
Also note that in the earlier SPI, this method was called every time the user was accessed, even if the local user is in the cache.
|
||||||
In the new 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 new model.
|
The `UserFederationProvider.isValid()` method no longer exists in the later model.
|
||||||
|
|
||||||
The `UserFederationProvider` methods `synchronizeRegistrations()`, `registerUser()`, and `removeUser()` methods 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
|
||||||
provider does not support creating and removing users, you don't have to implement it. If your old provider had switch
|
provider does not support creating and removing users, you don't have to implement it. If your earlier provider had switch
|
||||||
to toggle support for registering new users, this would be supported in the new SPI be returning `null` from
|
to toggle support for registering new users, this is supported in the new SPI, returning `null` from
|
||||||
`UserRegistrationProvider.addUser()` if the provider doesn't support adding users.
|
`UserRegistrationProvider.addUser()` if the provider doesn't support adding users.
|
||||||
|
|
||||||
The older `UserFederationProvider` methods centered around credentials are now encapsulated in the `CredentialInputValidator`
|
The earlier `UserFederationProvider` methods centered around credentials are now encapsulated in the `CredentialInputValidator`
|
||||||
and `CredentialInputUpdater` interfaces, which are also optional to implement depending on if you support validating or
|
and `CredentialInputUpdater` interfaces, which are also optional to implement depending on if you support validating or
|
||||||
updating credentials. Credential management used to exist in `UserModel` methods. These also have been moved to the
|
updating credentials. Credential management used to exist in `UserModel` methods. These also have been moved to the
|
||||||
`CredentialInputValidator` and `CredentialInputUpdater` interfaces.
|
`CredentialInputValidator` and `CredentialInputUpdater` interfaces.
|
||||||
One thing to note that if you do not implement the `CredentialInputUpdater` interface, then
|
One thing to note that if you do not implement the `CredentialInputUpdater` interface, then
|
||||||
any credentials provided by your provider may be overridden locally in {{book.project.name}} storage. So if you want
|
any credentials provided by your provider can be overridden locally in {{book.project.name}} storage. So if you want
|
||||||
your credentials to be read-only, you should implement the `CredentialInputUpdater.updateCredential()` method and
|
your credentials to be read-only, implement the `CredentialInputUpdater.updateCredential()` method and
|
||||||
return a `ReadOnlyException`.
|
return a `ReadOnlyException`.
|
||||||
|
|
||||||
The `UserFederationProvider` query methods like `searchByAttributes()` and `getGroupMembers()` are now encapsulated
|
The `UserFederationProvider` query methods such as `searchByAttributes()` and `getGroupMembers()` are now encapsulated
|
||||||
in an optional interface `UserQueryProvider`. If you do not implement this interface, then users will not be viewable
|
in an optional interface `UserQueryProvider`. If you do not implement this interface, then users will not be viewable
|
||||||
in the admin console. You'll still be able to login though.
|
in the admin console. You'll still be able to login though.
|
||||||
|
|
||||||
==== UserFederationProviderFactory vs. UserStorageProviderFactory
|
==== UserFederationProviderFactory vs. UserStorageProviderFactory
|
||||||
|
|
||||||
The synchronization methods in the old SPI are now encapsulated within an optional `ImportSynchronization` interface.
|
The synchronization methods in the earlier SPI are now encapsulated within an optional `ImportSynchronization` interface.
|
||||||
If you have implemented synchronization logic, then have your new `UserStorageProviderFactory` implement the
|
If you have implemented synchronization logic, then have your new `UserStorageProviderFactory` implement the
|
||||||
`ImportSynchronization` interface.
|
`ImportSynchronization` interface.
|
||||||
|
|
||||||
==== Upgrading to new Model
|
==== Upgrading to a New Model
|
||||||
|
|
||||||
The User Storage SPI instances are stored in a completely different set of relational tables or Mongo schema. {{book.project.name}}
|
The User Storage SPI instances are stored in a completely different set of relational tables or Mongo schema. {{book.project.name}}
|
||||||
automatically runs a migration script. If any older User Federation providers are deployed for a realm, they will be converted
|
automatically runs a migration script. If any earlier User Federation providers are deployed for a realm, they are converted
|
||||||
to the new storage model as is, including the `id` of the data. This migration will only happen if there exists a User Storage provider
|
to the later storage model as is, including the `id` of the data. This migration will only happen if a User Storage provider exists
|
||||||
with the same provider id (i.e. "ldap", "kerberos") as the old User Federation provider.
|
with the same provider ID (i.e., "ldap", "kerberos") as the earlier User Federation provider.
|
||||||
|
|
||||||
So, knowing this there are different approaches you can take.
|
So, knowing this there are different approaches you can take.
|
||||||
|
|
||||||
. You can remove the old provider in your old {{book.project.name}} deployment. This will remove all local linked copies
|
. You can remove the earlier provider in your earlier {{book.project.name}} deployment. This will remove all local linked copies
|
||||||
of imported users. Then, when you upgrade {{book.project.name}}, just deploy and configure your new provider for your realm.
|
of imported users. Then, when you upgrade {{book.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 {{book.project.name}} installation. Boot the server, and have
|
Make sure this provider is in the `deploy/` directory of the new {{book.project.name}} installation. Boot the server, and have
|
||||||
the built-in migration script convert from the old data model to the new data model. In this case all your old 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
|
||||||
users will work and be the same.
|
users will work and be the same.
|
||||||
|
|
||||||
If you have decided to get rid of the import strategy and rewrite your User Storage provider, we suggest that you remove the old provider
|
If you have decided to get rid of the import strategy and rewrite your User Storage provider, we suggest that you remove the earlier provider
|
||||||
before upgrading {{book.project.name}}. This will remove linked local imported copies of any user you imported.
|
before upgrading {{book.project.name}}. This will remove linked local imported copies of any user you imported.
|
|
@ -1,9 +1,9 @@
|
||||||
=== Simple Read Only, Lookup Example
|
=== Simple Read-Only, Lookup Example
|
||||||
|
|
||||||
To illustrate the basics of implementing the User Storage SPI let's walk through a simple example. In this chapter
|
To illustrate the basics of implementing the User Storage SPI let's walk through a simple example. In this chapter
|
||||||
you'll see the implementation of a simple `UserStorageProvider` that looks up users in a simple property file. The
|
you'll see the implementation of a simple `UserStorageProvider` that looks up users in a simple property file. The
|
||||||
property file contains username and password definitions and is hardcoded to a specific location on the classpath.
|
property file contains username and password definitions and is hardcoded to a specific location on the classpath.
|
||||||
The provider will be able to lookup the user by id, and username and also be able to validate passwords. Users that
|
The provider will be able to lookup the user by id and username and also be able to validate passwords. Users that
|
||||||
originate from this provider will be read only.
|
originate from this provider will be read only.
|
||||||
|
|
||||||
==== Provider Class
|
==== Provider Class
|
||||||
|
@ -22,11 +22,11 @@ public class PropertyFileUserStorageProvider implements
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
Our provider class, `PropertyFileUserStorageProvider`, implements a bunch of interfaces. It implements the
|
Our provider class, `PropertyFileUserStorageProvider`, implements a bunch of interfaces. It implements the
|
||||||
`UserStorageProvider` as that is a base requirement of the SPI. It implements the `UserLookupProvider` interface
|
`UserStorageProvider` as that is a base requirement of the SPI. It implements the `UserLookupProvider` interface
|
||||||
because we want to be able to login with users stored by this provider. It implements the `CredentialInputValidator`
|
because we want to be able to login with users stored by this provider. It implements the `CredentialInputValidator`
|
||||||
interface because we want to be able to validate passwords entered in via the login screen. Our property file
|
interface because we want to be able to validate passwords entered in via the login screen. Our property file
|
||||||
is going to be read only. We implement the `CredentialInputUpdater` because was want to post an error condition
|
is going to be read only. We implement the `CredentialInputUpdater` because was want to post an error condition
|
||||||
when the user's password is attempted to be updated.
|
when the user's password is attempted to be updated.
|
||||||
|
|
||||||
[source,java]
|
[source,java]
|
||||||
|
@ -45,9 +45,9 @@ when the user's password is attempted to be updated.
|
||||||
----
|
----
|
||||||
|
|
||||||
The constructor for this provider class is going to store the reference to the `KeycloakSession`, `ComponentModel`, and
|
The constructor for this provider class is going to store the reference to the `KeycloakSession`, `ComponentModel`, and
|
||||||
property file. We'll use all of these later. Also notice that there is a map of loaded users. Whenever we find a user
|
property file. We'll use all of these later. Also notice that there is a map of loaded users. Whenever we find a user
|
||||||
we will store it in this map so that we avoid recreating it again within the same transaction. This is a good practice
|
we will store it in this map so that we avoid recreating it again within the same transaction. This is a good practice
|
||||||
to do as many providers will need to do this (i.e. one that integrates with JPA). Remember also that provider class
|
to do as many providers will need to do this (i.e., one that integrates with JPA). Remember also that provider class
|
||||||
instances are created once per transaction and are closed after the transaction completes.
|
instances are created once per transaction and are closed after the transaction completes.
|
||||||
|
|
||||||
===== UserLookupProvider implementation
|
===== UserLookupProvider implementation
|
||||||
|
|
Loading…
Reference in a new issue