All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.phasetwo.portal.PortalResourceProviderFactory Maven / Gradle / Ivy

The newest version!
package io.phasetwo.portal;

import com.google.auto.service.AutoService;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.util.Map;
import lombok.extern.jbosslog.JBossLog;
import org.keycloak.Config.Scope;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.provider.ProviderEvent;
import org.keycloak.services.resource.RealmResourceProvider;
import org.keycloak.services.resource.RealmResourceProviderFactory;

@JBossLog
@AutoService(RealmResourceProviderFactory.class)
public class PortalResourceProviderFactory implements RealmResourceProviderFactory {

  public static final String ID = "portal";
  public static final String NAME = "Admin portal";
  public static final String DESCRIPTION =
      "Portal for self-administration of profile and organizations.";

  @Override
  public String getId() {
    return ID;
  }

  @Override
  public RealmResourceProvider create(KeycloakSession session) {
    // get override config
    RealmModel realm = session.getContext().getRealm();
    String override = realm.getName();
    return new PortalResourceProvider(session, override);
  }

  @Override
  public void init(Scope config) {}

  @Override
  public void postInit(KeycloakSessionFactory factory) {
    // KeycloakModelUtils.runJobInTransaction(factory, this::initClients);
    factory.register(
        (ProviderEvent event) -> {
          if (event instanceof RealmModel.RealmPostCreateEvent) {
            log.debug("RealmPostCreateEvent");
            realmPostCreate((RealmModel.RealmPostCreateEvent) event);
          }
        });
  }

  private void initClients(KeycloakSession session) {
    try {
      session
          .realms()
          .getRealmsStream()
          .forEach(
              realm -> {
                createClient(realm, session);
              });
    } catch (Exception e) {
      log.warnf(
          "Error initializing admin-portal clients. Ignoring. You may have to create them manually. %s",
          e.getMessage());
    }
  }

  private void realmPostCreate(RealmModel.RealmPostCreateEvent event) {
    RealmModel realm = event.getCreatedRealm();
    KeycloakSession session = event.getKeycloakSession();
    createClient(realm, session);
  }

  private void createClient(RealmModel realm, KeycloakSession session) {
    log.debugf("Creating %s realm admin-portal client.", realm.getName());
    ClientModel adminPortal = session.clients().getClientByClientId(realm, "admin-portal");
    if (adminPortal == null) {
      log.debugf("No admin-portal client for %s realm. Creating...", realm.getName());
      if ("master".equals(realm.getName())) {
        adminPortal = createClientForMaster(realm, session);
      } else {
        adminPortal = createClientForRealm(realm, session);
      }
      updateAccountConsoleForRealm(realm, session);
    }
    setClientScopeDefaults(realm, session, adminPortal);
  }

  private String getRedirectPath(RealmModel realm) {
    return String.format("/realms/%s/%s/", realm.getName(), ID);
  }

  private ClientModel createClientForRealm(RealmModel realm, KeycloakSession session) {
    String path = getRedirectPath(realm);
    ClientModel adminPortal = session.clients().addClient(realm, "admin-portal");
    setDefaults(realm, adminPortal);
    adminPortal.setRedirectUris(ImmutableSet.of(String.format("%s*", path)));
    adminPortal.setWebOrigins(ImmutableSet.of("/*"));
    adminPortal.setAttribute("post.logout.redirect.uris", "+");
    return adminPortal;
  }

  private void updateAccountConsoleForRealm(RealmModel realm, KeycloakSession session) {
    ClientModel accountConsole = session.clients().getClientByClientId(realm, "account-console");
    accountConsole.addRedirectUri(String.format("%s*", getRedirectPath(realm)));
  }

  private ClientModel createClientForMaster(RealmModel realm, KeycloakSession session) {
    ClientModel adminPortal = session.clients().addClient(realm, "admin-portal");
    setDefaults(realm, adminPortal);
    adminPortal.setRedirectUris(ImmutableSet.of("/*"));
    adminPortal.setWebOrigins(ImmutableSet.of("/*"));
    adminPortal.setAttribute("post.logout.redirect.uris", "+");
    return adminPortal;
  }

  private void setDefaults(RealmModel realm, ClientModel adminPortal) {
    adminPortal.setProtocol("openid-connect");
    adminPortal.setPublicClient(true);
    adminPortal.setRootUrl("${authBaseUrl}");
    adminPortal.setName(NAME);
    adminPortal.setDescription(DESCRIPTION);
    adminPortal.setBaseUrl(getRedirectPath(realm));
  }

  private void setOrganizationRoleMapper(ClientModel adminPortal) {
    ProtocolMapperModel pro =
        adminPortal.getProtocolMapperByName("openid-connect", "organizations");
    if (pro != null) {
      return;
    } else {
      pro = new ProtocolMapperModel();
    }
    pro.setProtocolMapper("oidc-organization-role-mapper");
    pro.setProtocol("openid-connect");
    pro.setName("organizations");
    Map config =
        new ImmutableMap.Builder()
            .put("id.token.claim", "true")
            .put("access.token.claim", "true")
            .put("claim.name", "organizations")
            .put("jsonType.label", "JSON")
            .put("userinfo.token.claim", "true")
            .build();
    pro.setConfig(config);
    //      "consentRequired": false,
    adminPortal.addProtocolMapper(pro);
  }

  private void setOrganizationIdMapper(ClientModel adminPortal) {
    ProtocolMapperModel pro = adminPortal.getProtocolMapperByName("openid-connect", "org_id");
    if (pro != null) {
      return;
    } else {
      pro = new ProtocolMapperModel();
    }
    pro.setProtocolMapper("oidc-usersessionmodel-note-mapper");
    pro.setProtocol("openid-connect");
    pro.setName("org_id");
    Map config =
        new ImmutableMap.Builder()
            .put("user.session.note", "org_id")
            .put("id.token.claim", "true")
            .put("access.token.claim", "true")
            .put("claim.name", "org_id")
            .put("jsonType.label", "String")
            .put("userinfo.token.claim", "true")
            .build();
    pro.setConfig(config);
    //      "consentRequired": false,
    adminPortal.addProtocolMapper(pro);
  }

  private void setClientScopeDefaults(
      RealmModel realm, KeycloakSession session, ClientModel adminPortal) {
    adminPortal.setFullScopeAllowed(true);
    session
        .clientScopes()
        .getClientScopesStream(realm)
        .filter(c -> (c.getRealm().equals(realm) && c.getName().equals("roles")))
        .forEach(
            c -> {
              log.debugf("Found 'roles' client scope. Adding as default...");
              try {
                adminPortal.addClientScope(c, true);
              } catch (Exception e) {
                log.warn("'roles' client scope already exists. skipping...");
              }
            });
    setOrganizationRoleMapper(adminPortal);
    setOrganizationIdMapper(adminPortal);
  }

  @Override
  public void close() {}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy