package org.gcube.keycloak.event; import java.net.MalformedURLException; import java.net.URL; import org.jboss.logging.Logger; import org.keycloak.Config.Scope; import org.keycloak.events.EventListenerProviderFactory; import org.keycloak.models.ClientModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.RealmModel; /** * @author Marco Lettere * @author Mauro Mugnaini */ public class OrchestratorEventPublisherProviderFactory implements EventListenerProviderFactory { 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 int CHECK_DELAY = 60 * 1000; // One minute public static URL ORCHESTRATOR_ENDPOINT; public static URL KEYCLOAK_ENDPOINT; public static String KEYCLOAK_CLIENT_SECRET; protected Long lastEndpointCheck = new Long(0); protected OrchestratorEventPublisherProvider oepp; public OrchestratorEventPublisherProviderFactory() { logger.info("New OrchestratorEventPublisherProviderFactory has been created"); } @Override public void close() { } @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; } 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; } 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."); } } logger.debugf("Client '%s' found", clientId); return client; } @Override public String getId() { return "orchestrator-event-publisher"; } @Override public void init(Scope scope) { } @Override public void postInit(KeycloakSessionFactory keycloakSessionFactory) { } }