keycloak-d4science-spi-parent/event-listener-provider/src/main/java/org/gcube/keycloak/event/OrchestratorEventPublisherP...

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) {
}
}