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,
|
"community": false,
|
||||||
"product": true,
|
"product": true,
|
||||||
|
"images": "rhsso-images",
|
||||||
"adminguide": {
|
"adminguide": {
|
||||||
"name": "Server Administration Guide",
|
"name": "Server Administration Guide",
|
||||||
"link": "https://access.redhat.com/documentation/en/red-hat-single-sign-on/7.0/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,
|
"community": true,
|
||||||
"product": false,
|
"product": false,
|
||||||
|
"images": "keycloak-images",
|
||||||
"adminguide": {
|
"adminguide": {
|
||||||
"name": "Server Administration Guide",
|
"name": "Server Administration Guide",
|
||||||
"link": "https://keycloak.gitbooks.io/server-adminstration-guide/content/"
|
"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
|
==== 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