KEYCLOAK-6289 Documentation for theme selector SPI
This commit is contained in:
parent
781eb3d050
commit
c8c7a7cebe
4 changed files with 73 additions and 119 deletions
|
@ -1,12 +1,13 @@
|
|||
include::topics/preface.adoc[]
|
||||
include::topics/admin-rest-api.adoc[]
|
||||
include::topics/themes.adoc[]
|
||||
include::topics/themes-selector.adoc[leveloffset=+2]
|
||||
include::topics/custom-attributes.adoc[]
|
||||
include::topics/identity-brokering.adoc[]
|
||||
include::topics/identity-brokering/tokens.adoc[]
|
||||
include::topics/identity-brokering/account-linking.adoc[]
|
||||
ifeval::[{project_community}==true]
|
||||
include::topics/providers.adoc[]
|
||||
ifeval::[{project_community}==true]
|
||||
include::topics/extensions.adoc[]
|
||||
include::topics/auth-spi.adoc[]
|
||||
include::topics/action-token-spi.adoc[]
|
||||
|
|
|
@ -2,17 +2,17 @@
|
|||
|
||||
== Service Provider Interfaces (SPI)
|
||||
|
||||
Keycloak is designed to cover most use-cases without requiring custom code, but we also want it to be customizable.
|
||||
To achieve this Keycloak has a number of Service Provider Interfaces (SPI) for which you can implement your own providers.
|
||||
{project_name} is designed to cover most use-cases without requiring custom code, but we also want it to be customizable.
|
||||
To achieve this {project_name} has a number of Service Provider Interfaces (SPI) for which you can implement your own providers.
|
||||
|
||||
=== Implementing a SPI
|
||||
|
||||
To implement an SPI you need to implement its ProviderFactory and Provider interfaces. You also need to create a service configuration file.
|
||||
|
||||
For example, to implement the Event Listener SPI you need to implement EventListenerProviderFactory and EventListenerProvider and also provide the file
|
||||
`META-INF/services/org.keycloak.events.EventListenerProviderFactory`.
|
||||
For example, to implement the Theme Selector Spi you need to implement ThemeSelectorProviderFactory and ThemeSelectorProvider and also provide the file
|
||||
`META-INF/services/org.keycloak.theme.ThemeSelectorProviderFactory`.
|
||||
|
||||
Example EventListenerProviderFactory:
|
||||
Example ThemeSelectorProviderFactory:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
|
@ -20,34 +20,36 @@ package org.acme.provider;
|
|||
|
||||
import ...
|
||||
|
||||
public class MyEventListenerProviderFactory implements EventListenerProviderFactory {
|
||||
public class MyThemeSelectorProviderFactory implements ThemeSelectorProviderFactory {
|
||||
|
||||
private List<Event> events;
|
||||
@Override
|
||||
public ThemeSelectorProvider create(KeycloakSession session) {
|
||||
return new MyThemeSelectorProvider(session);
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return "my-event-listener";
|
||||
}
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
}
|
||||
|
||||
public void init(Config.Scope config) {
|
||||
events = new LinkedList();
|
||||
}
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
}
|
||||
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
}
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
public EventListenerProvider create(KeycloakSession session) {
|
||||
return new MyEventListenerProvider(events);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
}
|
||||
@Override
|
||||
public String getId() {
|
||||
return "myThemeSelector";
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
NOTE: Keycloak creates a single instance of `EventListenerProviderFactory` which makes it possible to store state for multiple requests.
|
||||
`EventListenerProvider` instances are created by calling create on the factory for each requests so these should be light-weight object.
|
||||
NOTE: Keycloak creates a single instance of provider factories which makes it possible to store state for multiple requests.
|
||||
Provider instances are created by calling create on the factory for each requests so these should be light-weight object.
|
||||
|
||||
Example EventListenerProvider:
|
||||
Example ThemeSelectorProvider:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
|
@ -55,36 +57,24 @@ package org.acme.provider;
|
|||
|
||||
import ...
|
||||
|
||||
public class MyEventListenerProvider implements EventListenerProvider {
|
||||
public class MyThemeSelectorProvider implements EventListenerProvider {
|
||||
|
||||
private List<Event> events;
|
||||
|
||||
public MyEventListenerProvider(List<Event> events) {
|
||||
this.events = events;
|
||||
public MyThemeSelectorProvider(KeycloakSession session) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(Event event) {
|
||||
events.add(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(AdminEvent event, boolean includeRepresentation) {
|
||||
// Assume this implementation just ignores admin events
|
||||
public String getThemeName(Theme.Type type) {
|
||||
return "my-theme";
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Example service configuration file (`META-INF/services/org.keycloak.events.EventListenerProviderFactory`):
|
||||
Example service configuration file (`META-INF/services/org.keycloak.theme.ThemeSelectorProviderFactory`):
|
||||
|
||||
[source]
|
||||
----
|
||||
org.acme.provider.MyEventListenerProviderFactory
|
||||
org.acme.provider.MyThemeSelectorProviderFactory
|
||||
----
|
||||
|
||||
You can configure your provider through `standalone.xml`, `standalone-ha.xml`, or `domain.xml`.
|
||||
|
@ -95,11 +85,10 @@ For example by adding the following to `standalone.xml`:
|
|||
|
||||
[source,xml]
|
||||
----
|
||||
<spi name="eventsListener">
|
||||
<provider name="my-event-listener" enabled="true">
|
||||
<spi name="themeSelector">
|
||||
<provider name="myThemeSelector" enabled="true">
|
||||
<properties>
|
||||
<property name="aNumber" value="10"/>
|
||||
<property name="aString" value="Foo"/>
|
||||
<property name="theme" value="my-theme"/>
|
||||
</properties>
|
||||
</provider>
|
||||
</spi>
|
||||
|
@ -110,8 +99,7 @@ Then you can retrieve the config in the `ProviderFactory` init method:
|
|||
[source,java]
|
||||
----
|
||||
public void init(Config.Scope config) {
|
||||
Integer aNumber = config.getInt("aNumber");
|
||||
String aString = config.get("aString");
|
||||
String themeName = config.get("theme");
|
||||
}
|
||||
----
|
||||
|
||||
|
@ -119,39 +107,31 @@ Your provider can also lookup other providers if needed. For example:
|
|||
|
||||
[source,java]
|
||||
----
|
||||
public class MyEventListenerProvider implements EventListenerProvider {
|
||||
public class MyThemeSelectorProvider implements EventListenerProvider {
|
||||
|
||||
private KeycloakSession session;
|
||||
private List<Event> events;
|
||||
|
||||
public MyEventListenerProvider(KeycloakSession session, List<Event> events) {
|
||||
public MyThemeSelectorProvider(KeycloakSession session) {
|
||||
this.session = session;
|
||||
this.events = events;
|
||||
}
|
||||
|
||||
public void onEvent(Event event) {
|
||||
RealmModel realm = session.realms().getRealm(event.getRealmId());
|
||||
UserModel user = session.users().getUserById(event.getUserId(), realm);
|
||||
|
||||
EmailSenderProvider emailSender = session.getProvider(EmailSenderProvider.class);
|
||||
emailSender.send(realm, user, "Hello", "Hello plain text", "<h1>Hello html</h1>" );
|
||||
@Override
|
||||
public String getThemeName(Theme.Type type) {
|
||||
return session.getContext().getRealm().getLoginTheme();
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
[[_providers_admin_console]]
|
||||
==== Show info from your SPI implementation in Keycloak admin console
|
||||
==== Show info from your SPI implementation in admin console
|
||||
|
||||
Sometimes it is useful to show additional info about your Provider to a Keycloak administrator. You can show provider build time informations (eg. version of
|
||||
Sometimes it is useful to show additional info about your Provider to a {project_name} administrator. You can show provider build time informations (eg. version of
|
||||
custom provider currently installed), current configuration of the provider (eg. url of remote system your provider talks to) or some operational info
|
||||
(average time of response from remote system your provider talks to). Keycloak admin console provides Server Info page to show this kind of information.
|
||||
(average time of response from remote system your provider talks to). {project_name} admin console provides Server Info page to show this kind of information.
|
||||
|
||||
To show info from your provider it is enough to implement `org.keycloak.provider.ServerInfoAwareProviderFactory` interface in your `ProviderFactory`.
|
||||
|
||||
Example implementation for `MyEventListenerProviderFactory` from previous example:
|
||||
Example implementation for `MyThemeSelectorProviderFactory` from previous example:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
|
@ -159,15 +139,13 @@ package org.acme.provider;
|
|||
|
||||
import ...
|
||||
|
||||
public class MyEventListenerProviderFactory implements EventListenerProviderFactory, ServerInfoAwareProviderFactory {
|
||||
public class MyThemeSelectorProvider implements ThemeSelectorProvider, ServerInfoAwareProviderFactory {
|
||||
...
|
||||
|
||||
@Override
|
||||
public Map<String, String> getOperationalInfo() {
|
||||
Map<String, String> ret = new LinkedHashMap<>();
|
||||
ret.put("version", "1.0");
|
||||
ret.put("listSizeMax", max + "");
|
||||
ret.put("listSizeCurrent", events.size() + "");
|
||||
ret.put("theme-name", "my-theme");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
@ -175,16 +153,16 @@ public class MyEventListenerProviderFactory implements EventListenerProviderFact
|
|||
|
||||
=== Registering provider implementations
|
||||
|
||||
There are two ways to register provider implementations. In most cases the simplest way is to use the Keyclopak Deployer
|
||||
There are two ways to register provider implementations. In most cases the simplest way is to use the {project_name} deployer
|
||||
approach as this handles a number of dependencies automatically for you. It also supports hot deployment as well as re-deployment.
|
||||
|
||||
The alternative approach is to deploy as a module.
|
||||
|
||||
If you are creating a custom SPI you will need to deploy it as a module, otherwise we recommend using the Keycloak Deployer approach.
|
||||
If you are creating a custom SPI you will need to deploy it as a module, otherwise we recommend using the {project_name} deployer approach.
|
||||
|
||||
==== Using the Keycloak Deployer
|
||||
==== Using the {project_name} deployer Deployer
|
||||
|
||||
If you copy your provider jar to the Keycloak `deploy/` directory, your provider will automatically be deployed.
|
||||
If you copy your provider jar to the {project_name} deployer `deploy/` directory, your provider will automatically be deployed.
|
||||
Hot deployment works too. Additionally, your provider jar works similarly to other components deployed in a {appserver_name}
|
||||
environment in that they can use facilities like the `jboss-deployment-structure.xml` file. This file allows you to
|
||||
set up dependencies on other components and load third-party jars and modules.
|
||||
|
@ -200,18 +178,18 @@ For example to add the event listener sysout example provider using the `jboss-c
|
|||
|
||||
[source]
|
||||
----
|
||||
KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.event-sysout --resources=target/event-listener-sysout-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi,org.keycloak.keycloak-events-api"
|
||||
KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.acme.provider --resources=target/provider.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi"
|
||||
----
|
||||
Or to manually create it start by creating the folder `KEYCLOAK_HOME/modules/org/keycloak/examples/event-sysout/main`.
|
||||
Then copy `event-listener-sysout-example.jar` to this folder and create `module.xml` with the following content:
|
||||
Or to manually create it start by creating the folder `KEYCLOAK_HOME/modules/org/acme/provider/main`.
|
||||
Then copy `provider.jar` to this folder and create `module.xml` with the following content:
|
||||
|
||||
[source]
|
||||
----
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.examples.event-sysout">
|
||||
<module xmlns="urn:jboss:module:1.3" name="org.acme.provider">
|
||||
<resources>
|
||||
<resource-root path="event-listener-sysout-example.jar"/>
|
||||
<resource-root path="provider.jar"/>
|
||||
</resources>
|
||||
<dependencies>
|
||||
<module name="org.keycloak.keycloak-core"/>
|
||||
|
@ -220,7 +198,7 @@ Then copy `event-listener-sysout-example.jar` to this folder and create `module.
|
|||
</module>
|
||||
----
|
||||
|
||||
Once you've created the module you need to register this module with Keycloak.
|
||||
Once you've created the module you need to register this module with {project_name}.
|
||||
This is done by editing the keycloak-server subsystem section of
|
||||
`standalone.xml`, `standalone-ha.xml`, or `domain.xml`, and adding it to the providers:
|
||||
|
||||
|
@ -234,22 +212,6 @@ This is done by editing the keycloak-server subsystem section of
|
|||
...
|
||||
----
|
||||
|
||||
==== Configuring a provider
|
||||
|
||||
You can pass configuration options to your provider by setting them in `standalone.xml`, `standalone-ha.xml`, or `domain.xml`.
|
||||
For example to set the max value for `my-event-listener` add:
|
||||
|
||||
[source.xml]
|
||||
----
|
||||
<spi name="eventsListener">
|
||||
<provider name="my-event-listener" enabled="true">
|
||||
<properties>
|
||||
<property name="max" value="100"/>
|
||||
</properties>
|
||||
</provider>
|
||||
</spi>
|
||||
----
|
||||
|
||||
==== Disabling a provider
|
||||
|
||||
You can disable a provider by setting the enabled attribute for the provider to false
|
||||
|
@ -341,25 +303,4 @@ public class EjbExampleUserStorageProviderFactory
|
|||
|
||||
=== Available SPIs
|
||||
|
||||
Here's a list of the most important available SPIs and a brief description. For more details on each SPI refer to individual sections.
|
||||
If you want to see list of all available SPIs at runtime, you can check `Server Info` page in admin console as described in <<_providers_admin_console,Admin Console>> section.
|
||||
|
||||
|
||||
|===
|
||||
|SPI|Description
|
||||
|
||||
|Connections Infinispan|Loads and configures Infinispan connections. The default implementation can load connections from the Infinispan subsystem, or alternatively can be manually configured in standalone.xml
|
||||
|Connections Jpa|Loads and configures Jpa connections. The default implementation can load datasources from WildFly/EAP, or alternatively can be manually configured in standalone.xml
|
||||
|Connections Mongo|Loads and configures MongoDB connections. The default implementation is configured in standalone.xml
|
||||
|Email Sender|Sends email. The default implementation uses JavaMail
|
||||
|Email Template|Format email and uses Email Sender to send the email. The default implementation uses FreeMarker templates
|
||||
|Events Listener|Listen to user related events for example user login success and failures. Keycloak provides two implementations out of box. One that logs events to the server log and another that can send email notifications to users on certain events
|
||||
|Login Protocol|Provides protocols. Keycloak provides implementations of OpenID Connect and SAML 2.0
|
||||
|Realm|Provides realm and application meta-data. Keycloak provides implementations for Relational Databases and MongoDB
|
||||
|Realm Cache|Caches realm and application meta-data to improve performance. Default implementation uses Infinispan
|
||||
|Timer|Executes scheduled tasks. Keycloak provides a basic implementation based on java.util.Timer
|
||||
|User|Provides users and role-mappings. Keycloak provides implementations for Relational Databases and MongoDB
|
||||
|User Cache|Caches users to improve performance. Default implementation uses Infinispan
|
||||
|User Federation|Support syncing users from an external source. Keycloak provides implementations for LDAP and Active Directory
|
||||
|User Sessions|Provides users session information. Keycloak provides implementations for basic in-memory, Infinispan, Relational Databases and MongoDB
|
||||
|===
|
||||
If you want to see list of all available SPIs at runtime, you can check `Server Info` page in admin console as described in <<_providers_admin_console,Admin Console>> section.
|
12
server_development/topics/themes-selector.adoc
Normal file
12
server_development/topics/themes-selector.adoc
Normal file
|
@ -0,0 +1,12 @@
|
|||
= Theme Selector
|
||||
|
||||
By default the theme configured for the realm is used, with the exception of clients being able to override the login
|
||||
theme. This behavior can be changed through the Theme Selector SPI.
|
||||
|
||||
This could be used to select different themes for desktop and mobile devices by looking at the user agent
|
||||
header, for example.
|
||||
|
||||
To create a custom theme selector you need to implement `ThemeSelectorProviderFactory` and `ThemeSelectorProvider`.
|
||||
|
||||
Follow the steps in <<_providers,Service Provider Interfaces>> for more details on how to create and deploy a custom
|
||||
provider.
|
|
@ -374,4 +374,4 @@ If the server is running you need to restart the server after changing `standalo
|
|||
[NOTE]
|
||||
====
|
||||
If the same theme is deployed to both the `themes` directory and as a module the version in the `themes` directory is used.
|
||||
====
|
||||
====
|
Loading…
Reference in a new issue