Completely rewrote the implementation and configuration method, now both `admin` and `d4science` realms are configured by default as interesting realms.

Added specific SPI configurations with `spi-events-listener-orchestrator-event-publisher-` prefix: `include-realms`, `exclude-realms`, `include-admin-types`, `exclude-admin-types`, `include-events` and `exclude-events`
This commit is contained in:
Mauro Mugnaini 2024-04-18 12:16:23 +02:00
parent b9ae2f9b6b
commit fad5715a16
Signed by: mauro.mugnaini
GPG Key ID: 2440CFD0EB321EA8
5 changed files with 343 additions and 219 deletions

View File

@ -25,6 +25,32 @@ To build the JAR file it is sufficient to type
In order to deploy the module it is sufficient to copy into the `[keycloak-home]/providers` folder.
### Configuration
In order to configure the `Orchestrator`'s server endpoint URL it is sufficient to create a new client inside the `d4science` realm (or in the realm specified in the `target-orchestrator-realm` configuration) with id `conductor-server` and set-up the API URL in the `Home URL` field (e.g. https://conductor.dev.d4science.org/api/workflow/).
To perform authorized requests to the `orchestrator`, with an UMA token as `bearer`, a specific client with id `keycloak-server` has to be created in the same realm of the orchestrator client. This client should be configured to obtain an UMA ticket with audience: `conductor-server`.
Additionally, if the `Home URL` for this client is configured with a token endpoint URL, this will be used to obtain the UMA ticket, instead of the token URL of the Keycloak instance where the SPI is running.
Since version `2.24.0` it is possible to configure the SPI configurations with the following entries:
* `target-orchestrator-realm` - to specify in which realm looking for the the configured clients, overriding the default that is: `d4science`
* `include-realms` - to specify the interesting realms (default are: `d4science` and `master`)
* `exclude-realms` - to specify the non interesting realms
* `include-admin-types` - To specify the admin's `ResourceType`s to include (default are: `user`, `client` and `realm`)
* `exclude-admin-types`- To specify the admin's `ResourceType`s to exclude
* `include-events` - To specify the `EventType`s to include (default are: `register` and `delete_account`)
* `exclude-events` - To specify the `EventType`s to exclude
The prefix to add for the configuration is `spi-events-listener-orchestrator-event-publisher-` and f.e. to configure the interesting realms it is sufficient to use:
```bash
spi-events-listener-orchestrator-event-publisher-include-realms=d4science,master
```
This can be achieved either by adding some lines to the `[keycloak-home]/conf/keycloak.conf` configuration file, by using the CLI parameters or using the ENV variables.
(Please, refer to the [Keycloak documentation section](https://www.keycloak.org/server/configuration) for further info)
## Change log
See [CHANGELOG.md](CHANGELOG.md).

View File

@ -62,17 +62,23 @@ public class KeycloakEvent extends Event {
}
public static KeycloakEvent newKeycloakAdminEvent(AdminEvent adminEvent, boolean includeRepresentation) {
KeycloakEvent keycloakEvent = new KeycloakEvent(ADMIN_NAME, adminEvent.getRealmId(), adminEvent.getTime());
KeycloakEvent keycloakEvent = new KeycloakEvent(constructAdminEventName(adminEvent), adminEvent.getRealmId(),
adminEvent.getTime());
keycloakEvent.setOperation(adminEvent.getOperationType().name());
if (includeRepresentation) {
keycloakEvent.setOperation(adminEvent.getOperationType().name().toLowerCase());
if (includeRepresentation && adminEvent.getRepresentation() != null) {
keycloakEvent.setRepresentation(adminEvent.getRepresentation());
}
keycloakEvent.setResource(adminEvent.getResourcePath());
keycloakEvent.setResourceType(adminEvent.getResourceTypeAsString());
keycloakEvent.setResourceType(adminEvent.getResourceTypeAsString().toLowerCase());
return keycloakEvent;
}
protected static String constructAdminEventName(AdminEvent adminEvent) {
return ADMIN_NAME + "_" + adminEvent.getResourceTypeAsString().toLowerCase() + "_"
+ adminEvent.getOperationType().name().toLowerCase();
}
public static KeycloakEvent newKeycloakEvent(org.keycloak.events.Event event) {
KeycloakEvent keycloakEvent = new KeycloakEvent(event.getType().name().toLowerCase(), event.getRealmId(),
event.getTime(), event.getDetails());
@ -88,7 +94,7 @@ public class KeycloakEvent extends Event {
}
private static OffsetDateTime convertEventDate(long millis) {
OrchestratorEventPublisherProvider.logger.debugf("Creating offset date time from millis %d -> %t", millis, millis);
OrchestratorEventPublisherProvider.logger.debugf("Creating offset date time from millis: %d", millis);
return Instant.ofEpochMilli(millis).atZone(ZoneOffset.systemDefault()).toOffsetDateTime();
}
@ -102,11 +108,15 @@ public class KeycloakEvent extends Event {
@SuppressWarnings("unchecked")
public void setRepresentation(String representation) {
try {
put(REPRESENTATION, new JSONParser().parse(representation));
} catch (ParseException e) {
e.printStackTrace();
set(REPRESENTATION, representation);
if (representation != null) {
try {
put(REPRESENTATION, new JSONParser().parse(representation));
} catch (ParseException e) {
e.printStackTrace();
set(REPRESENTATION, representation);
}
} else {
remove(REPRESENTATION);
}
}

View File

@ -1,69 +0,0 @@
package org.gcube.keycloak.event;
import org.gcube.event.publisher.EventSender;
import org.json.simple.JSONObject;
import org.keycloak.events.Event;
import org.keycloak.events.admin.AdminEvent;
/**
* Added to avoid errors for not configured provider also in realm where event listener is not configured (KC bug?)
*
* @author <a href="mailto:mauro.mugnaini@nubisware.com">Mauro Mugnaini</a>
*
*/
public class NoOpEventPublisherProvider extends OrchestratorEventPublisherProvider {
public NoOpEventPublisherProvider() {
super();
}
@Override
public void close() {
}
@Override
public void onEvent(Event event) {
// Nothing to do
}
@Override
public void onEvent(AdminEvent event, boolean includeRepresentation) {
// Nothing to do
}
@Override
protected EventSender createEventSender() {
return new EventSender() {
@Override
public void send(org.gcube.event.publisher.Event event) {
// Nothing to do
}
@Override
public String sendAndGetResult(org.gcube.event.publisher.Event event) {
// Nothing to do
return null;
}
@Override
public JSONObject retrive(String id) {
// Nothing to do
return null;
}
@Override
public int getLastSendHTTPResponseCode() {
// Nothing to do
return 0;
}
@Override
public int getLastRetrieveHTTPResponseCode() {
// Nothing to do
return 0;
}
};
}
}

View File

@ -1,7 +1,9 @@
package org.gcube.keycloak.event;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.EventListener;
import java.util.HashSet;
import java.util.Set;
import org.gcube.event.publisher.AbstractEventPublisher;
@ -10,8 +12,11 @@ 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 <a href="mailto:marco.lettere@nubisware.com">Marco Lettere</a>
@ -22,69 +27,140 @@ public class OrchestratorEventPublisherProvider extends AbstractEventPublisher
public static final Logger logger = Logger.getLogger(OrchestratorEventPublisherProvider.class);
private static final Set<EventType> INTERESTING_EVENTS = new HashSet<>();
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<ResourceType> interestingAdminEventResourceTypes;
private final Set<EventType> interestingEvents;
static {
INTERESTING_EVENTS.add(EventType.CLIENT_DELETE);
INTERESTING_EVENTS.add(EventType.CLIENT_DELETE_ERROR);
INTERESTING_EVENTS.add(EventType.FEDERATED_IDENTITY_LINK);
INTERESTING_EVENTS.add(EventType.FEDERATED_IDENTITY_LINK_ERROR);
INTERESTING_EVENTS.add(EventType.IDENTITY_PROVIDER_FIRST_LOGIN);
INTERESTING_EVENTS.add(EventType.IDENTITY_PROVIDER_FIRST_LOGIN_ERROR);
INTERESTING_EVENTS.add(EventType.REGISTER);
INTERESTING_EVENTS.add(EventType.REGISTER_ERROR);
INTERESTING_EVENTS.add(EventType.UPDATE_EMAIL);
INTERESTING_EVENTS.add(EventType.VERIFY_EMAIL_ERROR);
INTERESTING_EVENTS.add(EventType.DELETE_ACCOUNT);
/**
* 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() {
public OrchestratorEventPublisherProvider(String realmName, KeycloakTransactionManager transactionManager,
String orchestratorEndpoint, String keycloakTokenEndpoint, String keycloakClientId,
String keycloakClientSecret, String orchestratorAudience,
Set<ResourceType> interestingAdminEventResourceTypes, Set<EventType> 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 (!INTERESTING_EVENTS.contains(event.getType())) {
logger.tracef("Skipping publish of not interesting event: %s", event.getType().toString());
return;
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);
}
logger.debug("Publishing new event to orchestrator");
publish(KeycloakEvent.newKeycloakEvent(event));
}
@Override
public void onEvent(AdminEvent event, boolean includeRepresentation) {
if (event.getError() != null) {
logger.debug("Skipping error admin event publish");
return;
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);
}
logger.debug("Publishing new admin event to orchestrator");
publish(KeycloakEvent.newKeycloakAdminEvent(event, includeRepresentation));
}
@Override
protected EventSender createEventSender() {
logger.infof(
"Creating the HTTP event sender with endpoint: %s, clientId: %s, KC token endpoint: %s, UMA adience: %s",
OrchestratorEventPublisherProviderFactory.ORCHESTRATOR_ENDPOINT,
OrchestratorEventPublisherProviderFactory.KEYCLOAK_CLIENT_ID,
OrchestratorEventPublisherProviderFactory.KEYCLOAK_ENDPOINT,
OrchestratorEventPublisherProviderFactory.ORCHESTRATOR_AUDIENCE_ID);
return OrchestratorEventPublisherProviderFactory.KEYCLOAK_ENDPOINT != null
? new HTTPWithUMAAuthEventSender(
OrchestratorEventPublisherProviderFactory.ORCHESTRATOR_ENDPOINT,
OrchestratorEventPublisherProviderFactory.KEYCLOAK_CLIENT_ID,
OrchestratorEventPublisherProviderFactory.KEYCLOAK_CLIENT_SECRET,
OrchestratorEventPublisherProviderFactory.KEYCLOAK_ENDPOINT,
OrchestratorEventPublisherProviderFactory.ORCHESTRATOR_AUDIENCE_ID)
: new HTTPWithUMAAuthEventSender(
OrchestratorEventPublisherProviderFactory.ORCHESTRATOR_ENDPOINT,
null, null, null, null);
}
}

View File

@ -1,15 +1,22 @@
package org.gcube.keycloak.event;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.gcube.event.publisher.EventSender;
import org.jboss.logging.Logger;
import org.keycloak.Config.Scope;
import org.keycloak.events.Event;
import org.keycloak.events.EventListenerProviderFactory;
import org.keycloak.events.EventType;
import org.keycloak.events.admin.ResourceType;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.protocol.oidc.OIDCWellKnownProviderFactory;
import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation;
/**
* @author <a href="mailto:marco.lettere@nubisware.com">Marco Lettere</a>
@ -19,23 +26,50 @@ public class OrchestratorEventPublisherProviderFactory implements EventListenerP
private static final Logger logger = Logger.getLogger(OrchestratorEventPublisherProviderFactory.class);
public static final String MASTER_REALM_NAME = "master";
public static final String ORCHESTRATOR_CLIENT_ID = "orchestrator";
public static final String ORCHESTRATOR_AUDIENCE_ID = "conductor-server";
public static final String KEYCLOAK_CLIENT_ID = "keycloak-client";
private static final String MASTER_REALM_NAME = "master";
private static final String D4SCIENCE_REALM_NAME = "d4science";
private static final String ORCHESTRATOR_CLIENT_ID = "conductor-server";
private static final String ORCHESTRATOR_AUDIENCE = ORCHESTRATOR_CLIENT_ID;
private static final String KEYCLOAK_CLIENT_ID = "keycloak-server";
private static final int CHECK_DELAY = 60 * 1000; // One minute
private static final int CHECK_DELAY = 30 * 1000;
private static final String DEFAULT_TARGET_ORCHESTRATOR_REALM;
private static final Set<String> DEFAULT_REALM_NAMES;
private static final Set<EventType> DEFAULT_INTERESTING_EVENTS;
private static final Set<ResourceType> DEFAULT_INTERESTING_ADMIN_EVENT_RESOURCE_TYPES;
protected static Long LAST_ENDPOINT_CHECK = Long.valueOf(0);
public static URL ORCHESTRATOR_ENDPOINT;
public static URL KEYCLOAK_ENDPOINT;
public static String KEYCLOAK_CLIENT_SECRET;
private static String THIS_KEYCLOAK_TOKEN_ENDPOINT;
private static ClientModel ORCHESTRATOR_CLIENT;
private static ClientModel KEYCLOAK_CLIENT;
protected Long lastEndpointCheck = Long.valueOf(0);
protected OrchestratorEventPublisherProvider oepp;
private String targetOrchestratorRealm;
private Set<String> interestingRealms = new HashSet<>();
private Set<EventType> interestingEvents = new HashSet<>();
private Set<ResourceType> interestingAdminEventResourceTypes = new HashSet<>();
public OrchestratorEventPublisherProviderFactory() {
logger.info("New OrchestratorEventPublisherProviderFactory has been created");
static {
DEFAULT_TARGET_ORCHESTRATOR_REALM = D4SCIENCE_REALM_NAME;
DEFAULT_REALM_NAMES = new HashSet<>();
DEFAULT_INTERESTING_EVENTS = new HashSet<>();
DEFAULT_INTERESTING_ADMIN_EVENT_RESOURCE_TYPES = new HashSet<>();
Collections.addAll(DEFAULT_REALM_NAMES,
MASTER_REALM_NAME,
D4SCIENCE_REALM_NAME);
Collections.addAll(DEFAULT_INTERESTING_ADMIN_EVENT_RESOURCE_TYPES,
ResourceType.CLIENT,
ResourceType.REALM,
ResourceType.USER);
Collections.addAll(DEFAULT_INTERESTING_EVENTS,
// EventType.FEDERATED_IDENTITY_LINK,
// EventType.IDENTITY_PROVIDER_FIRST_LOGIN,
// EventType.LOGIN,
EventType.REGISTER,
// EventType.UPDATE_EMAIL,
EventType.DELETE_ACCOUNT);
}
@Override
@ -43,88 +77,82 @@ public class OrchestratorEventPublisherProviderFactory implements EventListenerP
}
@Override
public synchronized OrchestratorEventPublisherProvider create(KeycloakSession keycloakSession) {
Long now = System.currentTimeMillis();
Long elapsed = now - lastEndpointCheck;
if (oepp == null || elapsed > CHECK_DELAY) {
lastEndpointCheck = now;
ClientModel orchestratorClient = getClientInActualOrMasterRealm(keycloakSession, ORCHESTRATOR_CLIENT_ID);
URL newOrchestratorEndpoint;
if (orchestratorClient != null) {
logger.trace("Getting configured orchestrator endpoint address from client's base URL");
try {
newOrchestratorEndpoint = new URL(orchestratorClient.getBaseUrl());
} catch (MalformedURLException e) {
logger.errorf("Can't create new orchestrator endpoint address: %s", orchestratorClient.getBaseUrl());
oepp = new NoOpEventPublisherProvider();
return oepp;
}
} else {
logger.debugf("Can't go ahead without a configured '%f' client", ORCHESTRATOR_CLIENT_ID);
oepp = null;
return oepp;
public OrchestratorEventPublisherProvider create(KeycloakSession keycloakSession) {
synchronized (this) {
Long now = System.currentTimeMillis();
Long elapsed = now - LAST_ENDPOINT_CHECK;
if (elapsed > CHECK_DELAY) {
LAST_ENDPOINT_CHECK = now;
checkForConfigChanges(keycloakSession);
// } else {
// logger.tracef("Next check is in %d millis", CHECK_DELAY - elapsed);
}
ClientModel keycloakClient = getClientInActualOrMasterRealm(keycloakSession, KEYCLOAK_CLIENT_ID);
URL newKeycloakEndpoint;
String keycloakClientSecret;
if (keycloakClient != null) {
try {
logger.debug("Getting configured keycloak endpoint address from client's base URL");
newKeycloakEndpoint = new URL(keycloakClient.getBaseUrl());
// Only do it if URL has been configured properly
logger.debug("Getting configured keycloak client client-secret from client");
keycloakClientSecret = keycloakClient.getSecret();
} catch (MalformedURLException e) {
logger.errorf("Can't create new keycloak token address: %s", keycloakClient.getBaseUrl(), e);
oepp = new NoOpEventPublisherProvider();
return oepp;
}
} else {
logger.debugf("Can't go ahead without a configured '%f' client", KEYCLOAK_CLIENT_ID);
oepp = new NoOpEventPublisherProvider();
return oepp;
}
if (oepp == null || !newOrchestratorEndpoint.equals(ORCHESTRATOR_ENDPOINT)
|| !newKeycloakEndpoint.equals(KEYCLOAK_ENDPOINT)
|| !keycloakClientSecret.equals(KEYCLOAK_CLIENT_SECRET)) {
logger.info("Creating new orchestrator event publisher provider");
// Address and other fileds will be then read from static fields in this class by
// the createEventSender() called by the superclass' constructor, overridden in the impl.
ORCHESTRATOR_ENDPOINT = newOrchestratorEndpoint;
KEYCLOAK_ENDPOINT = newKeycloakEndpoint;
KEYCLOAK_CLIENT_SECRET = keycloakClientSecret;
oepp = new OrchestratorEventPublisherProvider();
}
} else {
logger.tracef("Next check is in %d millis", CHECK_DELAY - elapsed);
}
return oepp;
String realmName = keycloakSession.getContext().getRealm().getName();
if (interestingRealms.contains(realmName)) {
// logger.debugf("Returning new provider to handle event in realm: %s", realmName);
return new OrchestratorEventPublisherProvider(realmName, keycloakSession.getTransactionManager(),
getOrchestratorEndpoint(), getKeycloakTokenEndpoint(), KEYCLOAK_CLIENT_ID,
getKeycloakClientSecret(), ORCHESTRATOR_AUDIENCE, interestingAdminEventResourceTypes,
interestingEvents);
} else {
logger.debugf("Returning a dummy provider to handle the event in realm: %s", realmName);
return new OrchestratorEventPublisherProvider() {
@Override
public void onEvent(Event event) {
}
@Override
public void onEvent(org.keycloak.events.admin.AdminEvent adminEvent, boolean includeRepresentation) {
};
@Override
protected EventSender createEventSender() {
return null;
};
};
}
}
protected ClientModel getClientInActualOrMasterRealm(KeycloakSession keycloakSession, String clientId) {
logger.debug("Getting actual realm from session's context");
RealmModel realm = keycloakSession.getContext().getRealm();
logger.debugf("Trying getting '%s' client in current realm: '%s'", clientId, realm.getName());
ClientModel client = realm.getClientByClientId(clientId);
if (client == null) {
if (!MASTER_REALM_NAME.equals(realm.getName())) {
logger.debugf("Not found. Now trying getting '%s' in '%s' realm", clientId, MASTER_REALM_NAME);
realm = keycloakSession.realms().getRealmByName(MASTER_REALM_NAME);
client = realm.getClientByClientId(clientId);
if (client == null) {
logger.warnf("Cannot find '%s' client not even in '%s' realm", clientId, realm.getName(),
MASTER_REALM_NAME);
return null;
}
} else {
logger.trace("Not found.");
public String getOrchestratorEndpoint() {
return ORCHESTRATOR_CLIENT != null ? ORCHESTRATOR_CLIENT.getBaseUrl() : null;
}
public String getKeycloakClientSecret() {
return KEYCLOAK_CLIENT != null ? KEYCLOAK_CLIENT.getSecret() : null;
}
public String getKeycloakTokenEndpoint() {
if (KEYCLOAK_CLIENT != null) {
if (KEYCLOAK_CLIENT.getBaseUrl() != null) {
return KEYCLOAK_CLIENT.getBaseUrl();
}
}
logger.debugf("Client '%s' found", clientId);
return THIS_KEYCLOAK_TOKEN_ENDPOINT;
}
protected void checkForConfigChanges(KeycloakSession keycloakSession) {
if (THIS_KEYCLOAK_TOKEN_ENDPOINT == null) {
THIS_KEYCLOAK_TOKEN_ENDPOINT = ((OIDCConfigurationRepresentation) new OIDCWellKnownProviderFactory()
.create(keycloakSession).getConfig()).getTokenEndpoint();
logger.debugf("Keycloak (this) token endpoint is: %s", THIS_KEYCLOAK_TOKEN_ENDPOINT);
}
RealmModel realm = keycloakSession.realms().getRealmByName(targetOrchestratorRealm);
if (realm == null) {
logger.errorf("Target orchestrator realm not: %s", targetOrchestratorRealm);
return;
}
ORCHESTRATOR_CLIENT = getClientInRealm(realm, ORCHESTRATOR_CLIENT_ID);
KEYCLOAK_CLIENT = getClientInRealm(realm, KEYCLOAK_CLIENT_ID);
}
protected ClientModel getClientInRealm(RealmModel realm, String clientId) {
ClientModel client = realm.getClientByClientId(clientId);
if (client == null) {
logger.errorf("Client '%s' not found in target orchestrator realm: %s", clientId, realm.getName());
}
return client;
}
@ -134,7 +162,60 @@ public class OrchestratorEventPublisherProviderFactory implements EventListenerP
}
@Override
public void init(Scope scope) {
public void init(Scope config) {
this.targetOrchestratorRealm = config.get("target-orchestrator-realm", DEFAULT_TARGET_ORCHESTRATOR_REALM);
logger.infof("Target orchestrator realm is: %s", targetOrchestratorRealm);
String[] includeRealms = config.getArray("include-realms");
if (includeRealms != null) {
for (String realm : includeRealms) {
this.interestingRealms.add(realm);
}
} else {
this.interestingRealms.addAll(DEFAULT_REALM_NAMES);
}
String[] excludeRealms = config.getArray("exclude-realms");
if (excludeRealms != null) {
for (String realm : excludeRealms) {
this.interestingRealms.remove(realm);
}
}
logger.infof("Interesting realms are: %s", interestingRealms);
String[] includeTypes = config.getArray("include-admin-types");
if (includeTypes != null) {
for (String type : includeTypes) {
this.interestingAdminEventResourceTypes.add(ResourceType.valueOf(type.toUpperCase()));
}
} else {
this.interestingAdminEventResourceTypes.addAll(DEFAULT_INTERESTING_ADMIN_EVENT_RESOURCE_TYPES);
}
String[] excludeTypes = config.getArray("exclude-admin-types");
if (excludeTypes != null) {
for (String type : excludeTypes) {
this.interestingAdminEventResourceTypes.remove(ResourceType.valueOf(type.toUpperCase()));
}
}
logger.infof("Interesting admin events resource types are: %s", interestingAdminEventResourceTypes);
String[] includeEvents = config.getArray("include-events");
if (includeEvents != null) {
for (String type : includeEvents) {
this.interestingEvents.add(EventType.valueOf(type.toUpperCase()));
}
} else {
this.interestingEvents.addAll(DEFAULT_INTERESTING_EVENTS);
}
String[] excludeEvents = config.getArray("exclude-events");
if (excludeEvents != null) {
for (String type : excludeEvents) {
this.interestingEvents.remove(EventType.valueOf(type.toUpperCase()));
}
}
logger.infof("Interesting events are: %s", interestingEvents);
}
@Override