In this article, we will share how to parameter RedHat SSO Keycloak SPI  adding a custom Event Listener module

1. Presentation

The goal of this article is to showcase the usage of SPI usage with keycloak. For this, it is illustrated with a very simple SPI example which an event listener.

2. Installing event Listener jar module
cd /keycloak_4.5/distribution/keycloak-4.6.0.Final-SNAPSHOT$ 

sh bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.event-sysout --resources=~/keycloak_4.5/keycloak/examples/providers/event-listener-sysout/target/event-listener-sysout-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi,org.keycloak.keycloak-server-spi-private"

2.1) you need to find out event-listener-sysout-example.jar

~/keycloak_4.5/keycloak/examples/providers/event-listener-sysout/target/event-listener-sysout-example.jar

command:

cd /keycloak_4.5/distribution/keycloak-4.6.0.Final-SNAPSHOT$ 
sh bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.event-sysout --resources=~/keycloak_4.5/keycloak/examples/providers/event-listener-sysout/target/event-listener-sysout-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi,org.keycloak.keycloak-server-spi-private"
2.2) adding event listener to keycloak providers

Goto standalone.xml and look-up for

<subsystem xmlns="urn:jboss:domain:keycloak-server:1.1">
            <web-context>auth</web-context>
            <providers>
                <provider>classpath:${jboss.home.dir}/providers/*</provider>
            </providers>

add to the provider list 
<provider>module:org.keycloak.examples.event-sysout</provider>


<subsystem xmlns="urn:jboss:domain:keycloak-server:1.1">
            <web-context>auth</web-context>
            <providers>
                <provider>classpath:${jboss.home.dir}/providers/*</provider>
                <provider>module:org.keycloak.examples.event-sysout</provider>
            </providers>
            <master-realm-name>master</master-realm-name>
2.3) restart keycloak server

sh standalone.sh

Check that event listener module has been loaded in the server.log file

orivat@asus:~/keycloak_4.5/distribution/keycloak-4.6.0.Final-SNAPSHOT/standalone/log$ grep event *

server.log:| 325: <provider>module:org.keycloak.examples.event-sysout</provider>

 

2.4) Configure Event Listener in the corresponding Realm pannel

You need to select:

  •  Event Listener (Sysout here)
  • and save the whole

Optionally you can also specify to save events in the database.

3) Using the event Listener Log

Each Login/logout is tracked in the log file

09:23:55,871 INFO [stdout] (default task-10) EVENT: type=LOGIN, realmId=master, clientId=security-admin-console, userId=67c30037-1147-4e72-acf4-bd79919f3072, ipAddress=127.0.0.1, auth_method=openid-connect, auth_type=code, redirect_uri=https://localhost:8080/auth/admin/master/console/#/realms/master/events-settings, consent=no_consent_required, code_id=7d829633-c724-4a71-8858-1e6b8502c021, username=admin

09:23:56,265 INFO [stdout] (default task-10) EVENT: type=CODE_TO_TOKEN, realmId=master, clientId=security-admin-console, userId=67c30037-1147-4e72-acf4-bd79919f3072, ipAddress=127.0.0.1, token_id=38d6820c-e9c0-4021-9364-214d18147fe5, grant_type=authorization_code, refresh_token_type=Refresh, scope='openid email profile', refresh_token_id=2b8ca89e-1654-421e-b593-54090d83b498, code_id=7d829633-c724-4a71-8858-1e6b8502c021, client_auth_method=client-secret

09:28:01,311 INFO [stdout] (default task-11) EVENT: type=LOGOUT, realmId=master, clientId=null, userId=67c30037-1147-4e72-acf4-bd79919f3072, ipAddress=127.0.0.1, redirect_uri=https://localhost:8080/auth/admin/master/console/#/realms/master/events-settings

 

4) Code of the event Listener SysoutEventListenerProvider.java
package org.keycloak.examples.providers.events;


import org.keycloak.events.Event;

import org.keycloak.events.EventListenerProvider;

import org.keycloak.events.EventType;

import org.keycloak.events.admin.AdminEvent;

import org.keycloak.events.admin.OperationType;


import java.util.Map;

import java.util.Set;


/**

* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>

*/

public class SysoutEventListenerProvider implements EventListenerProvider {


private Set<EventType> excludedEvents;

private Set<OperationType> excludedAdminOperations;


public SysoutEventListenerProvider(Set<EventType> excludedEvents, Set<OperationType> excludedAdminOpearations) {

this.excludedEvents = excludedEvents;

this.excludedAdminOperations = excludedAdminOpearations;

}


@Override

public void onEvent(Event event) {

// Ignore excluded events

if (excludedEvents != null && excludedEvents.contains(event.getType())) {

return;

} else {

System.out.println("EVENT: " + toString(event));

}

}


@Override

public void onEvent(AdminEvent event, boolean includeRepresentation) {

// Ignore excluded operations

if (excludedAdminOperations != null && excludedAdminOperations.contains(event.getOperationType())) {

return;

} else {

System.out.println("EVENT: " + toString(event));

}

}


private String toString(Event event) {

StringBuilder sb = new StringBuilder();


sb.append("type=");

sb.append(event.getType());

sb.append(", realmId=");

sb.append(event.getRealmId());

sb.append(", clientId=");

sb.append(event.getClientId());

sb.append(", userId=");

sb.append(event.getUserId());

sb.append(", ipAddress=");

sb.append(event.getIpAddress());


if (event.getError() != null) {

sb.append(", error=");

sb.append(event.getError());

}


if (event.getDetails() != null) {

for (Map.Entry<String, String> e : event.getDetails().entrySet()) {

sb.append(", ");

sb.append(e.getKey());

if (e.getValue() == null || e.getValue().indexOf(' ') == -1) {

sb.append("=");

sb.append(e.getValue());

} else {

sb.append("='");

sb.append(e.getValue());

sb.append("'");

}

}

}


return sb.toString();

}


private String toString(AdminEvent adminEvent) {

StringBuilder sb = new StringBuilder();


sb.append("operationType=");

sb.append(adminEvent.getOperationType());

sb.append(", realmId=");

sb.append(adminEvent.getAuthDetails().getRealmId());

sb.append(", clientId=");

sb.append(adminEvent.getAuthDetails().getClientId());

sb.append(", userId=");

sb.append(adminEvent.getAuthDetails().getUserId());

sb.append(", ipAddress=");

sb.append(adminEvent.getAuthDetails().getIpAddress());

sb.append(", resourcePath=");

sb.append(adminEvent.getResourcePath());


if (adminEvent.getError() != null) {

sb.append(", error=");

sb.append(adminEvent.getError());

}


return sb.toString();

}


@Override

public void close() {

}


}

Code of SysoutEventListenerProviderFactory.java

/*

* Copyright 2016 Red Hat, Inc. and/or its affiliates

* and other contributors as indicated by the @author tags.

*

* Licensed under the Apache License, Version 2.0 (the "License");

* you may not use this file except in compliance with the License.

* You may obtain a copy of the License at

*

* https://www.apache.org/licenses/LICENSE-2.0

*

* Unless required by applicable law or agreed to in writing, software

* distributed under the License is distributed on an "AS IS" BASIS,

* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

* See the License for the specific language governing permissions and

* limitations under the License.

*/


package org.keycloak.examples.providers.events;


import org.keycloak.Config;

import org.keycloak.events.EventListenerProvider;

import org.keycloak.events.EventListenerProviderFactory;

import org.keycloak.events.EventType;

import org.keycloak.events.admin.OperationType;

import org.keycloak.models.KeycloakSession;

import org.keycloak.models.KeycloakSessionFactory;


import java.util.HashSet;

import java.util.Set;


/**

* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>

*/

public class SysoutEventListenerProviderFactory implements EventListenerProviderFactory {


private Set<EventType> excludedEvents;

private Set<OperationType> excludedAdminOperations;


@Override

public EventListenerProvider create(KeycloakSession session) {

return new SysoutEventListenerProvider(excludedEvents, excludedAdminOperations);

}


@Override

public void init(Config.Scope config) {

String[] excludes = config.getArray("exclude-events");

if (excludes != null) {

excludedEvents = new HashSet<>();

for (String e : excludes) {

excludedEvents.add(EventType.valueOf(e));

}

}


String[] excludesOperations = config.getArray("excludesOperations");

if (excludesOperations != null) {

excludedAdminOperations = new HashSet<>();

for (String e : excludesOperations) {

excludedAdminOperations.add(OperationType.valueOf(e));

}

}

}


@Override

public void postInit(KeycloakSessionFactory factory) {


}

@Override

public void close() {

}


@Override

public String getId() {

return "sysout";

}


}

 

5) Pointers

pointer:

Adding Event Listener (official instructions)

To deploy copy target/event-listener-sysout-example.jar to providers directory. Alternatively you can deploy as a module by running:

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-server-spi-private"
Then registering the provider by editing standalone/configuration/standalone.xml and adding the module to the providers element:

<providers> ... <provider>module:org.keycloak.examples.event-sysout</provider> </providers>
Then start (or restart) the server. Once started open the admin console, select your realm, then click on Events, followed by config. Click on Listeners select box, then pick sysout from the dropdown. After this try to logout and login again to see events printed to System.out.

The example event listener can be configured to exclude certain events, for example to exclude REFRESH_TOKEN and CODE_TO_TOKEN events add the following to standalone.xml:

... <spi name="eventsListener"> <provider name="sysout"> <properties> <property name="exclude-events" value="[&quot;REFRESH_TOKEN&quot;, &quot;CODE_TO_TOKEN&quot;]"/> </properties> </provider </spi>

 

janua
Les derniers articles par janua (tout voir)