From 0473a7121e981e748000b7004fdb222265c0391a Mon Sep 17 00:00:00 2001 From: Jen Malloy Date: Mon, 12 Dec 2016 12:52:45 -0500 Subject: [PATCH] fixed typo in migration header and cleaned up chapter --- SUMMARY.adoc | 4 +- topics/user-storage/migration.adoc | 90 ++++++++++++------------- topics/user-storage/simple-example.adoc | 20 +++--- 3 files changed, 57 insertions(+), 57 deletions(-) diff --git a/SUMMARY.adoc b/SUMMARY.adoc index 93dd2d559d..dbdf7cdf0a 100755 --- a/SUMMARY.adoc +++ b/SUMMARY.adoc @@ -15,7 +15,7 @@ .. link:topics/user-storage/provider-capability-interfaces.adoc[Provider Capability Interfaces] .. link:topics/user-storage/model-interfaces.adoc[Model Interfaces] .. 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/registration-query.adoc[Add/Remove User and Query Capability interfaces] .. 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/javaee.adoc[Leveraging Java EE] .. 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] diff --git a/topics/user-storage/migration.adoc b/topics/user-storage/migration.adoc index 360d7ddb12..7a53a3c529 100644 --- a/topics/user-storage/migration.adoc +++ b/topics/user-storage/migration.adoc @@ -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) - User Federation SPI +NOTE: This chapter is only applicable if you have implemented a provider using the earlier (and now removed) + 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 -this older SPI available as well. This older User Federation SPI has been removed in Keycloak 2.5.0 and RH-SSO 7.1. -If you have written a provider with this older SPI, this chapter discusses some strategies you can use to port it. +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 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. ==== 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 -and import information from your exteral store to the local copy. 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. +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 external store to the local copy. However, this is no longer a requirement. You can still +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 -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 -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. +* {{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. +* 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 +imported user, and retire the earlier legacy external store. 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 -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. -* With the import approach, you have to keep local keycloak storage and external storage in sync. The User Storage SPI +* 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 +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 has capability interfaces that you can implement to support synchronization, but this can quickly become painful and messy. ==== UserFederationProvider vs. UserStorageProvider -The first thing to notice is that `UserFederationProvider` was a complete interface. You just implemented every method -in this interface. `UserStorageProvider` instead has broken up this interface into multiple capability interfaces that +The first thing to notice is that `UserFederationProvider` was a complete interface. You implemented every method +in this interface. However, `UserStorageProvider` has instead broken up this interface into multiple capability interfaces that you implement as needed. -`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 +`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 `KeycloakSession.userStorage().addUser()' to create the user locally. Instead you call `KeycloakSession.userLocalStorage().addUser()`. The `userStorage()` method no longer exists. 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. -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. -In the new SPI, this method is only called when the local user is loaded from local storage. If the local user is cached, +You want to implement this interface if you are porting your earlier provider as-is. +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 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. -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 -provider does not support creating and removing users, you don't have to implement it. If your old provider had switch -to toggle support for registering new users, this would be supported in the new SPI be returning `null` from +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 is supported in the new SPI, returning `null` from `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 -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. 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 -your credentials to be read-only, you should implement the `CredentialInputUpdater.updateCredential()` method and +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, implement the `CredentialInputUpdater.updateCredential()` method and 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 the admin console. You'll still be able to login though. ==== 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 `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}} -automatically runs a migration script. If any older User Federation providers are deployed for a realm, they will be 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 -with the same provider id (i.e. "ldap", "kerberos") as the old User Federation provider. +automatically runs a migration script. If any earlier User Federation providers are deployed for a realm, they are converted +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 earlier User Federation provider. 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. -. 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 - 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. -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 -before upgrading {{book.project.name}}. This will remove linked local imported copies of any user you imported. \ No newline at end of file +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. \ No newline at end of file diff --git a/topics/user-storage/simple-example.adoc b/topics/user-storage/simple-example.adoc index ac0a4e54d6..c0cf51d43a 100644 --- a/topics/user-storage/simple-example.adoc +++ b/topics/user-storage/simple-example.adoc @@ -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 -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. -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. ==== Provider Class @@ -22,11 +22,11 @@ public class PropertyFileUserStorageProvider implements } ---- -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 +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 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 -is going to be read only. We implement the `CredentialInputUpdater` because was want to post an error condition +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 when the user's password is attempted to be updated. [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 -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 -to do as many providers will need to do this (i.e. one that integrates with JPA). Remember also that provider class +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 +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. ===== UserLookupProvider implementation