2016-12-03 00:55:47 +00:00
|
|
|
|
|
|
|
=== 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 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.
|
|
|
|
|
2017-03-01 18:44:28 +00:00
|
|
|
==== Configuring the Provider in Admin Console
|
2016-12-03 00:55:47 +00:00
|
|
|
|
|
|
|
Now that the configuration is enabled, you can set the `path` variable when you configure the provider in the admin console.
|
|
|
|
|
2017-03-01 18:44:28 +00:00
|
|
|
{% if book.community %}
|
2016-12-03 00:55:47 +00:00
|
|
|
.Configured Provider
|
|
|
|
image:../../{{book.images}}/storage-provider-with-config.png[]
|
2017-03-03 15:07:13 +00:00
|
|
|
{% endif %}
|