more user storage
This commit is contained in:
parent
70e2f0b307
commit
8588ea9cd0
7 changed files with 166 additions and 0 deletions
|
@ -16,6 +16,7 @@
|
|||
},
|
||||
"community": false,
|
||||
"product": true,
|
||||
"images": "rhsso-images",
|
||||
"adminguide": {
|
||||
"name": "Server Administration Guide",
|
||||
"link": "https://access.redhat.com/documentation/en/red-hat-single-sign-on/7.0/server-administration-guide/"
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
},
|
||||
"community": true,
|
||||
"product": false,
|
||||
"images": "keycloak-images",
|
||||
"adminguide": {
|
||||
"name": "Server Administration Guide",
|
||||
"link": "https://keycloak.gitbooks.io/server-adminstration-guide/content/"
|
||||
|
|
BIN
keycloak-images/empty-user-federation-page.png
Normal file
BIN
keycloak-images/empty-user-federation-page.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 307 KiB |
BIN
keycloak-images/storage-provider-created.png
Normal file
BIN
keycloak-images/storage-provider-created.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 326 KiB |
BIN
keycloak-images/storage-provider-with-config.png
Normal file
BIN
keycloak-images/storage-provider-with-config.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 325 KiB |
BIN
keycloak-images/user-federation-page.png
Normal file
BIN
keycloak-images/user-federation-page.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 296 KiB |
|
@ -528,6 +528,170 @@ We simply allocate the `PropertyFileUserStorageProvider` class. This create met
|
|||
|
||||
==== Packaging and Deployment
|
||||
|
||||
The class files for our provider implementation should be placed in a jar. You also have to declare the provider
|
||||
factory class within the `META-INF/services/org.keycloak.storage.UserStorageProviderFactory` file.
|
||||
|
||||
----
|
||||
org.keycloak.examples.federation.properties.FilePropertiesStorageFactory
|
||||
----
|
||||
|
||||
Once you create the jar you can deploy it using regular JBoss/Wildfly means: copy the jar into the `deploy/` directory
|
||||
or using the JBoss CLI.
|
||||
|
||||
==== Enabling the Provider in Admin Console
|
||||
|
||||
You enable user storage providers per realm within the `User Federation` page in the admin console.
|
||||
|
||||
.User Federation
|
||||
image:../{{book.images}}/empty-user-federation-page.png[]
|
||||
|
||||
Select the provider we just created from the list: `readonly-property-file`. It brings you to the configuration
|
||||
page for our provider. We don't have anything to configure, so just click the `Save` button.
|
||||
|
||||
.Configured Provider
|
||||
image:../{{book.images}}/storage-provider-created.png[]
|
||||
|
||||
When you go back to the main `User Federation` page, you'll now see your provider listed.
|
||||
|
||||
.User Federation
|
||||
image:../{{book.images}}/user-federation-page.png[]
|
||||
|
||||
You will now be able to login with a user declared in the `users.properties` file. Of course, this user will have
|
||||
zero permissions to do anything and will be read only. You can though view the user on its account page after you
|
||||
login.
|
||||
|
||||
=== Advanced Configuration
|
||||
|
||||
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 may want to make the location of this file configurable per
|
||||
instance of the provider. In other words, we may want to re-use this provider mulitple times in multiple different realms
|
||||
and point to completely different user property files. We'll also want to do this configuration within the admin
|
||||
console UI.
|
||||
|
||||
The `UserStorageProviderFactory` has additional methods you can implement that deal with provider configuration.
|
||||
You describe the variables you want to configure per provider and the admin console will automatically render
|
||||
a generic input page to gather this configuration. There's also callback methods to validate 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.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
List<ProviderConfigProperty> getConfigProperties();
|
||||
|
||||
default
|
||||
void validateConfiguration(KeycloakSession session, RealmModel realm, ComponentModel model) throws ComponentValidationException
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
default
|
||||
void onCreate(KeycloakSession session, RealmModel realm, ComponentModel model) {
|
||||
|
||||
}
|
||||
|
||||
default
|
||||
void onUpdate(KeycloakSession session, RealmModel realm, ComponentModel model) {
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
The `ComponentFactory.getConfigProperties()` method returns a list of `org.keycloak.provider.ProviderConfigProperty`
|
||||
instances. These instances declare metadata that is needed to render and store each configuration variable of the
|
||||
provider.
|
||||
|
||||
==== Configuration Example
|
||||
|
||||
Let's expand our `PropertyFileUserStorageProviderFactory` example to allow you to to point a provider instance to a specific
|
||||
file on disk.
|
||||
|
||||
.PropertyFileUserStorageProviderFactory
|
||||
[source,java]
|
||||
----
|
||||
public class PropertyFileUserStorageProviderFactory implements UserStorageProviderFactory<PropertyFileUserStorageProvider> {
|
||||
protected static final List<ProviderConfigProperty> configMetadata;
|
||||
|
||||
static {
|
||||
configMetadata = ProviderConfigurationBuilder.create()
|
||||
.property().name("path")
|
||||
.type(ProviderConfigProperty.STRING_TYPE)
|
||||
.label("Path")
|
||||
.defaultValue("${jboss.server.config.dir}/example-users.properties")
|
||||
.helpText("File path to properties file")
|
||||
.default
|
||||
.add().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return configMetadata;
|
||||
}
|
||||
----
|
||||
|
||||
The `ProviderConfigurationBuilder` class is a great helper class to create a list of configuration properties. Here
|
||||
we specify a variable named `path` that is a string type. In the admin console config page for this provider,
|
||||
this config variable will be labed as `Path` and have a default value of `${jboss.server.config.dir}/example-users.properties`.
|
||||
When you hover over the tooltip of this config option, it will display the help text `File path to properties file`.
|
||||
|
||||
The next thing we want to do is to verify that this file exists on disk. We don't want to enable an instance of this
|
||||
provider in the realm unless it points to a valid user property file. To do this we implement the `validateConfiguration()`
|
||||
method.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Override
|
||||
public void validateConfiguration(KeycloakSession session, RealmModel realm, ComponentModel config)
|
||||
throws ComponentValidationException {
|
||||
String fp = config.getConfig().getFirst("path");
|
||||
if (fp == null) throw new ComponentValidationException("user property file does not exist");
|
||||
fp = EnvUtil.replace(fp);
|
||||
File file = new File(fp);
|
||||
if (!file.exists()) {
|
||||
throw new ComponentValidationException("user property file does not exist");
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
In the `validateConfiguration()` method we get the config variable from the `ComponentModel` and we check to see
|
||||
if that file exists on disk. Notice that we use the `org.keycloak.common.util.EnvUtil.replace()`method. With this method
|
||||
any string that has `${}` within it will replace that with a system property value. The `${jboss.server.config.dir}`
|
||||
string corresponds to the `configuration/` directory of our server and is really useful for this example.
|
||||
|
||||
Next thing we have to do is remove the old `init()` method. We do this because user property files are going to be
|
||||
unique per provider instance. We move this logic to the `create()` method.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Override
|
||||
public PropertyFileUserStorageProvider create(KeycloakSession session, ComponentModel model) {
|
||||
String path = model.getConfig().getFirst("path");
|
||||
|
||||
Properties props = new Properties();
|
||||
try {
|
||||
InputStream is = new FileInputStream(path);
|
||||
props.load(is);
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return new PropertyFileUserStorageProvider(session, model, props);
|
||||
}
|
||||
----
|
||||
|
||||
This logic, is of course, is really inefficient as every different transaction will read the entire user property file from disk,
|
||||
but hopefully this illustrates, in a simple way, how to hook in configuration variables.
|
||||
|
||||
==== Configure in Admin Console
|
||||
|
||||
Now that the configuration is enabled, you can set the `path` variable when you configure the provider in the admin console.
|
||||
|
||||
.Configured Provider
|
||||
image:../{{book.images}}/storage-provider-with-config.png[]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue