package org.gcube.keycloak.event; import java.net.MalformedURLException; import java.net.URL; import java.util.Collections; import java.util.EventListener; import java.util.Set; import org.gcube.event.publisher.AbstractEventPublisher; import org.gcube.event.publisher.EventSender; import org.gcube.event.publisher.HTTPWithUMAAuthEventSender; import org.jboss.logging.Logger; import org.keycloak.events.Event; import org.keycloak.events.EventListenerProvider; import org.keycloak.events.EventListenerTransaction; import org.keycloak.events.EventType; import org.keycloak.events.admin.AdminEvent; import org.keycloak.events.admin.ResourceType; import org.keycloak.models.KeycloakTransactionManager; /** * @author Marco Lettere * @author Mauro Mugnaini */ public class OrchestratorEventPublisherProvider extends AbstractEventPublisher implements EventListenerProvider, EventListener { public static final Logger logger = Logger.getLogger(OrchestratorEventPublisherProvider.class); private final String realmName; private final EventListenerTransaction tx; private final String orchestratorEndpoint; private final String keycloakTokenEndpoint; private final String keycloakClientId; private final String keycloakClientSecret; private final String orchestratorAudience; private final Set interestingAdminEventResourceTypes; private final Set interestingEvents; /** * Default constructor for dummy use */ public OrchestratorEventPublisherProvider() { this.realmName = null; this.tx = null; orchestratorEndpoint = null; keycloakTokenEndpoint = null; keycloakClientId = null; keycloakClientSecret = null; orchestratorAudience = null; this.interestingAdminEventResourceTypes = Collections.emptySet(); this.interestingEvents = Collections.emptySet(); logger.tracef("Created new dummy instance"); } public OrchestratorEventPublisherProvider(String realmName, KeycloakTransactionManager transactionManager, String orchestratorEndpoint, String keycloakTokenEndpoint, String keycloakClientId, String keycloakClientSecret, String orchestratorAudience, Set interestingAdminEventResourceTypes, Set interestingEvents) { super(); this.realmName = realmName; this.interestingAdminEventResourceTypes = interestingAdminEventResourceTypes; this.interestingEvents = interestingEvents; tx = new EventListenerTransaction(this::sendAdminEvent, this::sendEvent); transactionManager.enlistAfterCompletion(tx); this.orchestratorEndpoint = orchestratorEndpoint; this.keycloakTokenEndpoint = keycloakTokenEndpoint; this.keycloakClientId = keycloakClientId; this.keycloakClientSecret = keycloakClientSecret; this.orchestratorAudience = orchestratorAudience; // Creating every time a new sender to be sure new settings are used, if changed this.setEventSender(newEventSender(realmName)); logger.tracef("Created new instance for realm: %s. Admin events: %2. Events:", realmName, interestingAdminEventResourceTypes, interestingEvents); } @Override protected EventSender createEventSender() { // Will be set in the constructor by using the setter, instance variables are still not available at this moment return null; } protected EventSender newEventSender(String realmName) { if (orchestratorEndpoint != null) { try { if (keycloakClientSecret != null) { logger.tracef( "Creating event sender for realm %s. Endpoint: %s, KC token endpoint: %s, UMA adience: %s", realmName, orchestratorEndpoint, keycloakTokenEndpoint, orchestratorAudience); return new HTTPWithUMAAuthEventSender(new URL(orchestratorEndpoint), keycloakClientId, keycloakClientSecret, new URL(keycloakTokenEndpoint), orchestratorAudience); } else { logger.debugf("Creating unauthorized event sender with endpoint: %s", orchestratorEndpoint); return new HTTPWithUMAAuthEventSender(new URL(orchestratorEndpoint), null, null, null, null); } } catch (MalformedURLException e) { logger.errorf("Can't construct endpoint URL from string", e); } } else { logger.errorf("Orchestrator endpoint is not available for realm: %s", realmName); } return null; } @Override public void close() { } @Override public void onEvent(AdminEvent adminEvent, boolean includeRepresentation) { if (getEventSender() != null) { if (adminEvent.getError() != null) { logger.debug("Skipping error admin event publish"); return; } if (interestingAdminEventResourceTypes.contains(adminEvent.getResourceType())) { logger.tracef("Enqued admin event for resource type: %s", adminEvent.getResourceType().name()); tx.addAdminEvent(adminEvent, includeRepresentation); } else { logger.tracef("Skipping not interesting admin event resource type: %s", adminEvent.getResourceType().name()); } } else { logger.debugf("Can't publish admin events since since event sender is null for realm: %s", realmName); } } public void sendAdminEvent(AdminEvent adminEvent, boolean includeRepresentation) { if (getEventSender() != null) { logger.debugf("Publishing new admin event '%s' to orchestrator for realm: %s", adminEvent.getOperationType().name(), realmName); publish(KeycloakEvent.newKeycloakAdminEvent(adminEvent, includeRepresentation)); } else { logger.debugf("Can't publish admin events since event sender is null. Current realm: %s", realmName); } } @Override public void onEvent(Event event) { if (getEventSender() != null) { EventType eventType = event.getType(); if (interestingEvents.contains(eventType)) { logger.tracef("Enqued event of type: %s", eventType.name()); tx.addEvent(event); return; } else { logger.tracef("Skipping not interesting event: %s", eventType.name()); } } else { logger.debugf("Can't publish events since since event sender is null for realm: %s", realmName); } } public void sendEvent(Event event) { if (getEventSender() != null) { logger.debugf("Publishing new event '%s' to orchestrator for realm %s", event.getType().name(), realmName); publish(KeycloakEvent.newKeycloakEvent(event)); } else { logger.debugf("Can't publish events since event sender is null. Current realm: %s", realmName); } } }