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

io.phasetwo.service.model.jpa.JpaOrganizationProvider Maven / Gradle / Ivy

There is a newer version: 0.79
Show newest version
package io.phasetwo.service.model.jpa;

import static org.keycloak.models.jpa.PaginationUtils.paginateQuery;
import static org.keycloak.utils.StreamsUtil.closing;

import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.common.net.InternetDomainName;
import io.phasetwo.service.model.InvitationModel;
import io.phasetwo.service.model.OrganizationModel;
import io.phasetwo.service.model.OrganizationProvider;
import io.phasetwo.service.model.jpa.entity.DomainEntity;
import io.phasetwo.service.model.jpa.entity.InvitationEntity;
import io.phasetwo.service.model.jpa.entity.OrganizationAttributeEntity;
import io.phasetwo.service.model.jpa.entity.OrganizationEntity;
import io.phasetwo.service.model.jpa.entity.OrganizationMemberEntity;
import io.phasetwo.service.resource.OrganizationAdminAuth;
import jakarta.persistence.EntityManager;
import jakarta.persistence.TypedQuery;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Join;
import jakarta.persistence.criteria.JoinType;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.KeycloakModelUtils;

public class JpaOrganizationProvider implements OrganizationProvider {

  protected final KeycloakSession session;
  protected final EntityManager em;

  public JpaOrganizationProvider(KeycloakSession session, EntityManager em) {
    this.session = session;
    this.em = em;
  }

  @Override
  public OrganizationModel createOrganization(
      RealmModel realm, String name, UserModel createdBy, boolean admin) {
    OrganizationEntity e = new OrganizationEntity();
    e.setId(KeycloakModelUtils.generateId());
    e.setRealmId(realm.getId());
    e.setName(name);
    e.setCreatedBy(createdBy.getId());
    em.persist(e);
    em.flush();
    OrganizationModel org = new OrganizationAdapter(session, realm, em, e);
    session.getKeycloakSessionFactory().publish(orgCreationEvent(realm, org));

    // creator if admin, but not a service account
    if (admin && createdBy.getServiceAccountClientLink() == null) {
      org.grantMembership(createdBy);
      for (String role : OrganizationAdminAuth.DEFAULT_ORG_ROLES) {
        org.getRoleByName(role).grantRole(createdBy);
      }
    }

    return org;
  }

  @Override
  public OrganizationModel getOrganizationById(RealmModel realm, String id) {
    OrganizationEntity org = em.find(OrganizationEntity.class, id);
    if (org != null && org.getRealmId().equals(realm.getId())) {
      return new OrganizationAdapter(session, realm, em, org);
    } else {
      return null;
    }
  }

  @Override
  public Stream getOrganizationsStreamForDomain(
      RealmModel realm, String domain, boolean verified) {
    domain = InternetDomainName.from(domain).toString();
    TypedQuery query =
        em.createNamedQuery(
            verified ? "getVerifiedDomainsByName" : "getDomainsByName", DomainEntity.class);
    query.setParameter("domain", domain);
    query.setParameter("realmId", realm.getId());
    if (verified) {
      query.setParameter("verified", verified);
    }
    return query
        .getResultStream()
        .map(de -> new OrganizationAdapter(session, realm, em, de.getOrganization()));
  }

  public static String createSearchString(String search) {
    if (Strings.isNullOrEmpty(search)) return "%";
    if (!search.startsWith("%")) search = "%" + search;
    if (!search.endsWith("%")) search = search + "%";
    return search;
  }

  @Override
  public Stream getUserOrganizationsStream(RealmModel realm, UserModel user) {
    TypedQuery query =
        em.createNamedQuery("getOrganizationMembershipsByUserId", OrganizationMemberEntity.class);
    query.setParameter("id", user.getId());
    return query
        .getResultStream()
        .map(e -> new OrganizationAdapter(session, realm, em, e.getOrganization()));
  }

  @Override
  @SuppressWarnings("unchecked")
  public Stream searchForOrganizationStream(
      RealmModel realm,
      Map attributes,
      Integer firstResult,
      Integer maxResults,
      Optional member) {
    if (attributes == null) {
      attributes = ImmutableMap.of();
    }
    CriteriaBuilder builder = em.getCriteriaBuilder();
    CriteriaQuery queryBuilder = builder.createQuery(OrganizationEntity.class);
    Root root = queryBuilder.from(OrganizationEntity.class);

    List predicates = attributePredicates(attributes, root);

    predicates.add(builder.equal(root.get("realmId"), realm.getId()));

    member.ifPresent(u -> predicates.add(memberPredicate(u, root)));

    queryBuilder.where(predicates.toArray(new Predicate[0])).orderBy(builder.asc(root.get("name")));

    TypedQuery query = em.createQuery(queryBuilder);

    return closing(paginateQuery(query, firstResult, maxResults).getResultStream())
        .map(orgEntity -> getOrganizationById(realm, orgEntity.getId()))
        .filter(Objects::nonNull);
  }

  @Override
  public Long getOrganizationsCount(RealmModel realm, String search) {
    TypedQuery query = em.createNamedQuery("countOrganizationsByRealmIdAndName", Long.class);
    query.setParameter("realmId", realm.getId());
    search = createSearchString(search);
    query.setParameter("search", search);
    return query.getSingleResult();
  }

  @Override
  public boolean removeOrganization(RealmModel realm, String id) {
    OrganizationModel org = getOrganizationById(realm, id);
    OrganizationEntity e = em.find(OrganizationEntity.class, id);
    em.remove(e);
    session.getKeycloakSessionFactory().publish(orgRemovedEvent(realm, org));
    em.flush();
    return true;
  }

  @Override
  public void removeOrganizations(RealmModel realm) {
    searchForOrganizationStream(realm, null, null, null, Optional.empty())
        .forEach(o -> removeOrganization(realm, o.getId()));
  }

  @Override
  public Stream getUserInvitationsStream(RealmModel realm, UserModel user) {
    TypedQuery query =
        em.createNamedQuery("getInvitationsByRealmAndEmail", InvitationEntity.class);
    query.setParameter("realmId", realm.getId());
    query.setParameter("search", user.getEmail());
    return query.getResultStream().map(i -> new InvitationAdapter(session, realm, em, i));
  }

  @Override
  public void close() {}

  public OrganizationModel.OrganizationCreationEvent orgCreationEvent(
      RealmModel realm, OrganizationModel org) {
    return new OrganizationModel.OrganizationCreationEvent() {
      @Override
      public OrganizationModel getOrganization() {
        return org;
      }

      @Override
      public KeycloakSession getKeycloakSession() {
        return session;
      }

      @Override
      public RealmModel getRealm() {
        return realm;
      }
    };
  }

  public OrganizationModel.OrganizationRemovedEvent orgRemovedEvent(
      RealmModel realm, OrganizationModel org) {
    return new OrganizationModel.OrganizationRemovedEvent() {
      @Override
      public OrganizationModel getOrganization() {
        return org;
      }

      @Override
      public KeycloakSession getKeycloakSession() {
        return session;
      }

      @Override
      public RealmModel getRealm() {
        return realm;
      }
    };
  }

  private List attributePredicates(
      Map attributes, Root root) {
    CriteriaBuilder builder = em.getCriteriaBuilder();

    List predicates = new ArrayList<>();
    List attributePredicates = new ArrayList<>();

    for (Map.Entry entry : attributes.entrySet()) {
      String key = entry.getKey();
      String value = entry.getValue();

      if (value == null) {
        continue;
      }

      switch (key) {
        case "name":
          predicates.add(
              builder.or(
                  builder.like(builder.lower(root.get("name")), "%" + value.toLowerCase() + "%"),
                  builder.like(
                      builder.lower(root.get("displayName")), "%" + value.toLowerCase() + "%")));
          break;
        default:
          Join attributesJoin =
              root.join("attributes", JoinType.LEFT);

          attributePredicates.add(
              builder.and(
                  builder.equal(builder.lower(attributesJoin.get("name")), key.toLowerCase()),
                  builder.equal(builder.lower(attributesJoin.get("value")), value.toLowerCase())));
          break;
      }
    }

    if (!attributePredicates.isEmpty()) {
      predicates.add(builder.and(attributePredicates.toArray(new Predicate[0])));
    }

    return predicates;
  }

  private Predicate memberPredicate(UserModel member, Root root) {
    CriteriaBuilder builder = em.getCriteriaBuilder();

    Join membersJoin =
        root.join("members", JoinType.LEFT);

    return builder.equal(membersJoin.get("userId"), member.getId());
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy