Skip to content

Commit

Permalink
RC 1.0 (#1)
Browse files Browse the repository at this point in the history
Implemented minimum of functionality
  • Loading branch information
aznamier authored Aug 27, 2020
1 parent 73cdda0 commit 745bcbf
Show file tree
Hide file tree
Showing 11 changed files with 568 additions and 0 deletions.
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
*.class

# Package Files #
*.jar
*.war
*.ear
target/
.settings/
.project
.classpath
81 changes: 81 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,82 @@
# keycloak-event-listener-rabbitmq

##### A Keycloak SPI plugin that publishes events to a RabbitMq server.

For example here is the notification of the user updated by administrator

* routing key: `KK.EVENT.ADMIN.MYREALM.SUCCESS.USER.UPDATE`
* published to exchange: `amq.topic`
* content:


```
{
"@class" : "com.github.aznamier.keycloak.event.provider.EventAdminNotificationMqMsg",
"time" : 1596951200408,
"realmId" : "MYREALM",
"authDetails" : {
"realmId" : "master",
"clientId" : "********-****-****-****-**********",
"userId" : "********-****-****-****-**********",
"ipAddress" : "192.168.1.1"
},
"resourceType" : "USER",
"operationType" : "UPDATE",
"resourcePath" : "users/********-****-****-****-**********",
"representation" : "representation details here....",
"error" : null,
"resourceTypeAsString" : "USER"
}
```

The routing key is calculated as follows:
* admin events: `KK.EVENT.ADMIN.<REALM>.<RESULT>.<RESOURCE_TYPE>.<OPERATION>`
* client events: `KK.EVENT.CLIENT.<REALM>.<RESULT>.<CLIENT>.<EVENT_TYPE>`

And because the recommended exchange is a **TOPIC (amq.topic)**,
therefore its easy for Rabbit client to subscribe to selective combinations eg:
* all events: `KK.EVENT.#`
* all events from my realm: `KK.EVENT.*.MYREALM.#`
* all error events from my realm: `KK.EVENT.*.MYREALM.ERROR.#`
* all user events from my-relam and my-client: `KK.EVENT.*.MY-REALM.*.MY-CLIENT.USER`

## USAGE:
1. [Download the latest jar](https://github.com/aznamier/keycloak-event-listener-rabbitmq/blob/target/keycloak-to-rabbit-1.0.jar?raw=true) or build from source: ``mvn clean install``
2. copy jar into your Keycloak `/opt/jboss/keycloak/standalone/deployments/keycloak-to-rabbit-1.0.jar`
3. Configure as described below (option 0 or 1 or 2)
4. Restart the Keycloak server
5. Enable logging in Keycloak UI by adding **keycloak-to-rabbitmq**
`Manage > Events > Config > Events Config > Event Listeners`

#### Configuration
###### OPTION 1: just configure **ENVIRONMENT VARIABLES**
- `KK_TO_RMQ_URL` - default: *localhost*
- `KK_TO_RMQ_PORT` - default: *5672*
- `KK_TO_RMQ_VHOST` - default: *empty*
- `KK_TO_RMQ_EXCHANGE` - default: *amq.topic*
- `KK_TO_RMQ_USERNAME` - default: *guest*
- `KK_TO_RMQ_PASSWORD` - default: *guest*

###### OPTION 2: edit Keycloak subsystem of WildFly standalone.xml or standalone-ha.xml:

```xml
<spi name="eventsListener">
<provider name="mqtt" enabled="true">
<properties>
<property name="url" value="${env.KK_TO_RMQ_URL:localhost}"/>
<property name="port" value="${env.KK_TO_RMQ_PORT:5672}"/>
<property name="vhost" value="${env.KK_TO_RMQ_VHOST:}"/>
<property name="exchange" value="${env.KK_TO_RMQ_EXCHANGE:amq.topic}"/>

<property name="username" value="${env.KK_TO_RMQ_USERNAME:guest}"/>
<property name="password" value="${env.KK_TO_RMQ_PASSWORD:guest}"/>
</properties>
</provider>
</spi>
```
###### OPTION 3: same effect as OPTION 2 but programatically:
```
echo "yes" | $KEYCLOAK_HOME/bin/jboss-cli.sh --file=$KEYCLOAK_HOME/KEYCLOAK_TO_RABBIT.cli
```


113 changes: 113 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.github.aznamier.keycloak.event.provider</groupId>
<artifactId>keycloak-to-rabbit</artifactId>
<packaging>jar</packaging>
<version>1.0-RC-SNAPSHOT-${maven.build.timestamp}</version>



<name>Keycloak: Event Publisher to RabbitMQ</name>
<properties>
<keycloak.version>11.0.0</keycloak.version>
<jar.finalName>${project.artifactId}-${project.version}</jar.finalName>
</properties>

<distributionManagement>
<repository>
<id>github</id>
<name>GitHub AZNAMIER Apache Maven Packages</name>
<url>https://maven.pkg.github.com/aznamier/keycloak-event-listener-rabbitmq</url>
</repository>
</distributionManagement>




<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<scope>provided</scope>
<version>${keycloak.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-server-spi</artifactId>
<scope>provided</scope>
<version>${keycloak.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-server-spi-private</artifactId>
<scope>provided</scope>
<version>${keycloak.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>
<scope>provided</scope>
<version>${keycloak.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-core-public</artifactId>
<scope>provided</scope>
<version>${keycloak.version}</version>
</dependency>

<!-- https://mvnrepository.com/artifact/com.rabbitmq/amqp-client -->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.9.0</version>
</dependency>

</dependencies>

<build>
<finalName>keycloak-to-rabbit</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>

<!-- Maven Assembly Plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<finalName>${jar.finalName}</finalName>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<appendAssemblyId>false</appendAssemblyId>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>

<plugin>
<groupId>org.wildfly.plugins</groupId>
<artifactId>wildfly-maven-plugin</artifactId>
<configuration>
<skip>false</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.github.aznamier.keycloak.event.provider;

import java.io.Serializable;

import javax.xml.bind.annotation.XmlRootElement;

import org.keycloak.events.admin.AdminEvent;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;

@JsonIgnoreProperties(ignoreUnknown = true)
@XmlRootElement
@JsonTypeInfo(use = Id.CLASS)
public class EventAdminNotificationMqMsg extends AdminEvent implements Serializable {

private static final long serialVersionUID = -7367949289101799624L;

public static EventAdminNotificationMqMsg create(AdminEvent adminEvent) {
EventAdminNotificationMqMsg msg = new EventAdminNotificationMqMsg();
msg.setAuthDetails(adminEvent.getAuthDetails());
msg.setError(adminEvent.getError());
msg.setOperationType(adminEvent.getOperationType());
msg.setRealmId(adminEvent.getRealmId());
msg.setRepresentation(adminEvent.getRepresentation());
msg.setResourcePath(adminEvent.getResourcePath());
msg.setResourceType(adminEvent.getResourceType());
msg.setResourceTypeAsString(adminEvent.getResourceTypeAsString());
msg.setTime(adminEvent.getTime());
return msg;
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.github.aznamier.keycloak.event.provider;

import java.io.Serializable;

import javax.xml.bind.annotation.XmlRootElement;

import org.keycloak.events.Event;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;

@JsonIgnoreProperties(ignoreUnknown = true)
@XmlRootElement
@JsonTypeInfo(use = Id.CLASS)
public class EventClientNotificationMqMsg extends Event implements Serializable {

private static final long serialVersionUID = -2192461924304841222L;

public static EventClientNotificationMqMsg create(Event event) {
EventClientNotificationMqMsg msg = new EventClientNotificationMqMsg();
msg.setClientId(event.getClientId());
msg.setDetails(event.getDetails());
msg.setError(event.getError());
msg.setIpAddress(event.getIpAddress());
msg.setRealmId(event.getRealmId());
msg.setSessionId(event.getSessionId());
msg.setTime(event.getTime());
msg.setType(event.getType());
msg.setUserId(event.getUserId());

return msg;
}


}
Loading

0 comments on commit 745bcbf

Please sign in to comment.