Build

The deployment process is pretty straightforward. We need to copy the sample-event-listener.jar to $KEYCLOAK_DIR/standalone/deployments/ where $KEYCLOAK_DIR is the main KeyCloak directory.

  • The $KEYCLOAK_DIR used to be /opt/jboss/keycloak/

Build .jar as output in /target:

$ mvn clean install

Testing with docker:

  • Copy the .jar to /docker folder.
$ cd docker && docker build .
$ sudo docker run -p 8080 --network="host" <image_id>

FROM quay.io/keycloak/keycloak:12.0.4
 
COPY ./kafka-user-register-event-listener.jar /opt/jboss/keycloak/standalone/deployments/kafka-user-register-event-listener.jar
 
ENV KEYCLOAK_USER=admin 
ENV KEYCLOAK_PASSWORD=admin
 
CMD ["-b", "0.0.0.0", "-c", "standalone.xml"]

KeycloakCustomEventListener.java

package plugin;
 
import org.keycloak.events.Event;
import org.keycloak.events.EventListenerProvider;
import org.keycloak.events.EventType;
import org.keycloak.events.admin.AdminEvent;
 
public class KeycloakCustomEventListener implements EventListenerProvider {
 
    @Override
    public void onEvent(Event event) {
 
        if (event.getType() == EventType.REGISTER) {
 
            System.out.println("[+] NEW USER REGISTER: " + event.getUserId());
            Producer.publishEvent(Producer.KAFKA_TOPIC,"{" + event.getType().toString() + ":" + event.getUserId() + "}");
 
        }
 
    }
 
    @Override
    public void onEvent(AdminEvent adminEvent, boolean b) {
 
        System.out.println("[+] ADMIN EVENT: "+adminEvent.getResourceType().name());
        Producer.publishEvent(adminEvent.getOperationType().toString(), adminEvent.getAuthDetails().getUserId());
 
    }
 
 
    @Override
    public void close() {
 
    }
}

KeycloakCustomEventListenerProviderFactory.java

package plugin;
 
 
import org.keycloak.Config;
import org.keycloak.events.EventListenerProvider;
import org.keycloak.events.EventListenerProviderFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
 
public class KeycloakCustomEventListenerProviderFactory implements EventListenerProviderFactory {
 
    @Override
    public EventListenerProvider create(KeycloakSession keycloakSession) {
 
        return new KeycloakCustomEventListener();
 
    }
 
    @Override
    public void init(Config.Scope scope) {
 
    }
 
    @Override
    public void postInit(KeycloakSessionFactory keycloakSessionFactory) {
 
    }
 
    @Override
    public void close() {
 
    }
 
 
    // Defines the name for keycloak GUI selection
    @Override
    public String getId() {
        return "kafka-user-register-event-listener";
    }
}

Producer.java

package plugin;
 
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.serialization.StringSerializer;
import java.util.Properties;
 
public class Producer {
 
    final static String KAFKA_BOOTSTRAP_SERVER = "kafka-cp-kafka.kafka.svc.cluster.local:9092";
    //final static String KAFKA_BOOTSTRAP_SERVER = "localhost:9092";
    final static String KAFKA_TOPIC = "keycloak-user-register";
 
    public static void publishEvent(String topic, String value){
 
        //reset thread context
        resetThreadContext();
 
        // create the producer
        KafkaProducer<String, String> producer = new KafkaProducer<String, String>(getProperties());
 
        // create a producer record
        ProducerRecord<String, String> eventRecord = new ProducerRecord<String, String>(topic, value);
 
        // send data - asynchronous
        producer.send(eventRecord);
 
        // flush data
        producer.flush();
 
        // flush and close producer
        producer.close();
 
    }
 
    private static void resetThreadContext() {
 
        Thread.currentThread().setContextClassLoader(null);
 
    }
 
    public static Properties getProperties() {
 
        Properties properties = new Properties();
 
        properties.setProperty(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, KAFKA_BOOTSTRAP_SERVER);
        properties.setProperty(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
        properties.setProperty(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
 
        return properties;
 
    }
 
 
}

🌱 Back to Garden