226 lines
9.0 KiB
Java
226 lines
9.0 KiB
Java
package org.gcube.keycloak.event;
|
|
|
|
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>
|
|
* @author <a href="mailto:mauro.mugnaini@nubisware.com">Mauro Mugnaini</a>
|
|
*/
|
|
public class OrchestratorEventPublisherProviderFactory implements EventListenerProviderFactory {
|
|
|
|
private static final Logger logger = Logger.getLogger(OrchestratorEventPublisherProviderFactory.class);
|
|
|
|
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 = 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);
|
|
|
|
private static String THIS_KEYCLOAK_TOKEN_ENDPOINT;
|
|
private static ClientModel ORCHESTRATOR_CLIENT;
|
|
private static ClientModel KEYCLOAK_CLIENT;
|
|
|
|
private String targetOrchestratorRealm;
|
|
private Set<String> interestingRealms = new HashSet<>();
|
|
private Set<EventType> interestingEvents = new HashSet<>();
|
|
private Set<ResourceType> interestingAdminEventResourceTypes = new HashSet<>();
|
|
|
|
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
|
|
public void close() {
|
|
}
|
|
|
|
@Override
|
|
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);
|
|
}
|
|
}
|
|
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;
|
|
};
|
|
};
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
@Override
|
|
public String getId() {
|
|
return "orchestrator-event-publisher";
|
|
}
|
|
|
|
@Override
|
|
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
|
|
public void postInit(KeycloakSessionFactory keycloakSessionFactory) {
|
|
}
|
|
|
|
}
|