/* * Copyright 2016 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.keycloak.models.jpa; import org.keycloak.models.ClientModel; import org.keycloak.models.ClientTemplateModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleContainerModel; import org.keycloak.models.RoleModel; import org.keycloak.models.jpa.entities.ClientEntity; import org.keycloak.models.jpa.entities.ClientTemplateEntity; import org.keycloak.models.jpa.entities.ProtocolMapperEntity; import org.keycloak.models.jpa.entities.RoleEntity; import org.keycloak.models.jpa.entities.ScopeMappingEntity; import org.keycloak.models.utils.KeycloakModelUtils; import javax.persistence.EntityManager; import javax.persistence.TypedQuery; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @version $Revision: 1 $ */ public class ClientAdapter implements ClientModel, JpaModel<ClientEntity> { protected KeycloakSession session; protected RealmModel realm; protected EntityManager em; protected ClientEntity entity; public ClientAdapter(RealmModel realm, EntityManager em, KeycloakSession session, ClientEntity entity) { this.session = session; this.realm = realm; this.em = em; this.entity = entity; } public ClientEntity getEntity() { return entity; } @Override public String getId() { return entity.getId(); } @Override public RealmModel getRealm() { return realm; } @Override public String getName() { return entity.getName(); } @Override public void setName(String name) { entity.setName(name); } @Override public String getDescription() { return entity.getDescription(); } @Override public void setDescription(String description) { entity.setDescription(description); } @Override public boolean isEnabled() { return entity.isEnabled(); } @Override public void setEnabled(boolean enabled) { entity.setEnabled(enabled); } @Override public boolean isPublicClient() { return entity.isPublicClient(); } @Override public void setPublicClient(boolean flag) { entity.setPublicClient(flag); } @Override public boolean isFrontchannelLogout() { return entity.isFrontchannelLogout(); } @Override public void setFrontchannelLogout(boolean flag) { entity.setFrontchannelLogout(flag); } @Override public boolean isFullScopeAllowed() { return entity.isFullScopeAllowed(); } @Override public void setFullScopeAllowed(boolean value) { entity.setFullScopeAllowed(value); } @Override public Set<String> getWebOrigins() { Set<String> result = new HashSet<String>(); result.addAll(entity.getWebOrigins()); return result; } @Override public void setWebOrigins(Set<String> webOrigins) { entity.setWebOrigins(webOrigins); } @Override public void addWebOrigin(String webOrigin) { entity.getWebOrigins().add(webOrigin); } @Override public void removeWebOrigin(String webOrigin) { entity.getWebOrigins().remove(webOrigin); } @Override public Set<String> getRedirectUris() { Set<String> result = new HashSet<String>(); result.addAll(entity.getRedirectUris()); return result; } @Override public void setRedirectUris(Set<String> redirectUris) { entity.setRedirectUris(redirectUris); } @Override public void addRedirectUri(String redirectUri) { entity.getRedirectUris().add(redirectUri); } @Override public void removeRedirectUri(String redirectUri) { entity.getRedirectUris().remove(redirectUri); } @Override public String getClientAuthenticatorType() { return entity.getClientAuthenticatorType(); } @Override public void setClientAuthenticatorType(String clientAuthenticatorType) { entity.setClientAuthenticatorType(clientAuthenticatorType); } @Override public String getSecret() { return entity.getSecret(); } @Override public void setSecret(String secret) { entity.setSecret(secret); } @Override public String getRegistrationToken() { return entity.getRegistrationToken(); } @Override public void setRegistrationToken(String registrationToken) { entity.setRegistrationToken(registrationToken); } @Override public boolean validateSecret(String secret) { return secret.equals(entity.getSecret()); } @Override public int getNotBefore() { return entity.getNotBefore(); } @Override public void setNotBefore(int notBefore) { entity.setNotBefore(notBefore); } @Override public Set<RoleModel> getRealmScopeMappings() { Set<RoleModel> roleMappings = getScopeMappings(); Set<RoleModel> appRoles = new HashSet<>(); for (RoleModel role : roleMappings) { RoleContainerModel container = role.getContainer(); if (container instanceof RealmModel) { if (((RealmModel) container).getId().equals(realm.getId())) { appRoles.add(role); } } } return appRoles; } @Override public Set<RoleModel> getScopeMappings() { TypedQuery<String> query = em.createNamedQuery("clientScopeMappingIds", String.class); query.setParameter("client", getEntity()); List<String> ids = query.getResultList(); Set<RoleModel> roles = new HashSet<RoleModel>(); for (String roleId : ids) { RoleModel role = realm.getRoleById(roleId); if (role == null) continue; roles.add(role); } return roles; } @Override public void addScopeMapping(RoleModel role) { Set<RoleModel> roles = getScopeMappings(); if (roles.contains(role)) return; ScopeMappingEntity entity = new ScopeMappingEntity(); entity.setClient(getEntity()); RoleEntity roleEntity = RoleAdapter.toRoleEntity(role, em); entity.setRole(roleEntity); em.persist(entity); em.flush(); em.detach(entity); } @Override public void deleteScopeMapping(RoleModel role) { TypedQuery<ScopeMappingEntity> query = getRealmScopeMappingQuery(role); List<ScopeMappingEntity> results = query.getResultList(); if (results.size() == 0) return; for (ScopeMappingEntity entity : results) { em.remove(entity); } } protected TypedQuery<ScopeMappingEntity> getRealmScopeMappingQuery(RoleModel role) { TypedQuery<ScopeMappingEntity> query = em.createNamedQuery("hasScope", ScopeMappingEntity.class); query.setParameter("client", getEntity()); RoleEntity roleEntity = RoleAdapter.toRoleEntity(role, em); query.setParameter("role", roleEntity); return query; } @Override public String getProtocol() { return entity.getProtocol(); } @Override public void setProtocol(String protocol) { entity.setProtocol(protocol); } @Override public void setAttribute(String name, String value) { entity.getAttributes().put(name, value); } @Override public void removeAttribute(String name) { entity.getAttributes().remove(name); } @Override public String getAttribute(String name) { return entity.getAttributes().get(name); } @Override public Map<String, String> getAttributes() { Map<String, String> copy = new HashMap<>(); copy.putAll(entity.getAttributes()); return copy; } @Override public ClientTemplateModel getClientTemplate() { ClientTemplateEntity templateEntity = entity.getClientTemplate(); if (templateEntity == null) return null; return session.realms().getClientTemplateById(templateEntity.getId(), realm); } @Override public void setClientTemplate(ClientTemplateModel template) { if (template == null) { entity.setClientTemplate(null); } else { ClientTemplateEntity templateEntity = em.getReference(ClientTemplateEntity.class, template.getId()); entity.setClientTemplate(templateEntity); } } @Override public boolean useTemplateScope() { return entity.isUseTemplateScope(); } @Override public void setUseTemplateScope(boolean flag) { entity.setUseTemplateScope(flag); } @Override public boolean useTemplateMappers() { return entity.isUseTemplateMappers(); } @Override public void setUseTemplateMappers(boolean flag) { entity.setUseTemplateMappers(flag); } @Override public boolean useTemplateConfig() { return entity.isUseTemplateConfig(); } @Override public void setUseTemplateConfig(boolean flag) { entity.setUseTemplateConfig(flag); } public static boolean contains(String str, String[] array) { for (String s : array) { if (str.equals(s)) return true; } return false; } @Override public Set<ProtocolMapperModel> getProtocolMappers() { Set<ProtocolMapperModel> mappings = new HashSet<ProtocolMapperModel>(); for (ProtocolMapperEntity entity : this.entity.getProtocolMappers()) { ProtocolMapperModel mapping = new ProtocolMapperModel(); mapping.setId(entity.getId()); mapping.setName(entity.getName()); mapping.setProtocol(entity.getProtocol()); mapping.setProtocolMapper(entity.getProtocolMapper()); mapping.setConsentRequired(entity.isConsentRequired()); mapping.setConsentText(entity.getConsentText()); Map<String, String> config = new HashMap<String, String>(); if (entity.getConfig() != null) { config.putAll(entity.getConfig()); } mapping.setConfig(config); mappings.add(mapping); } return mappings; } @Override public ProtocolMapperModel addProtocolMapper(ProtocolMapperModel model) { if (getProtocolMapperByName(model.getProtocol(), model.getName()) != null) { throw new ModelDuplicateException("Protocol mapper name must be unique per protocol"); } String id = model.getId() != null ? model.getId() : KeycloakModelUtils.generateId(); ProtocolMapperEntity entity = new ProtocolMapperEntity(); entity.setId(id); entity.setName(model.getName()); entity.setProtocol(model.getProtocol()); entity.setProtocolMapper(model.getProtocolMapper()); entity.setClient(this.entity); entity.setConfig(model.getConfig()); entity.setConsentRequired(model.isConsentRequired()); entity.setConsentText(model.getConsentText()); em.persist(entity); this.entity.getProtocolMappers().add(entity); return entityToModel(entity); } protected ProtocolMapperEntity getProtocolMapperEntity(String id) { for (ProtocolMapperEntity entity : this.entity.getProtocolMappers()) { if (entity.getId().equals(id)) { return entity; } } return null; } protected ProtocolMapperEntity getProtocolMapperEntityByName(String protocol, String name) { for (ProtocolMapperEntity entity : this.entity.getProtocolMappers()) { if (entity.getProtocol().equals(protocol) && entity.getName().equals(name)) { return entity; } } return null; } @Override public void removeProtocolMapper(ProtocolMapperModel mapping) { ProtocolMapperEntity toDelete = getProtocolMapperEntity(mapping.getId()); if (toDelete != null) { session.users().preRemove(mapping); this.entity.getProtocolMappers().remove(toDelete); em.remove(toDelete); } } @Override public void updateProtocolMapper(ProtocolMapperModel mapping) { ProtocolMapperEntity entity = getProtocolMapperEntity(mapping.getId()); entity.setProtocolMapper(mapping.getProtocolMapper()); entity.setConsentRequired(mapping.isConsentRequired()); entity.setConsentText(mapping.getConsentText()); if (entity.getConfig() == null) { entity.setConfig(mapping.getConfig()); } else { entity.getConfig().clear(); entity.getConfig().putAll(mapping.getConfig()); } em.flush(); } @Override public ProtocolMapperModel getProtocolMapperById(String id) { ProtocolMapperEntity entity = getProtocolMapperEntity(id); if (entity == null) return null; return entityToModel(entity); } @Override public ProtocolMapperModel getProtocolMapperByName(String protocol, String name) { ProtocolMapperEntity entity = getProtocolMapperEntityByName(protocol, name); if (entity == null) return null; return entityToModel(entity); } protected ProtocolMapperModel entityToModel(ProtocolMapperEntity entity) { ProtocolMapperModel mapping = new ProtocolMapperModel(); mapping.setId(entity.getId()); mapping.setName(entity.getName()); mapping.setProtocol(entity.getProtocol()); mapping.setProtocolMapper(entity.getProtocolMapper()); mapping.setConsentRequired(entity.isConsentRequired()); mapping.setConsentText(entity.getConsentText()); Map<String, String> config = new HashMap<String, String>(); if (entity.getConfig() != null) config.putAll(entity.getConfig()); mapping.setConfig(config); return mapping; } @Override public void updateClient() { em.flush(); session.getKeycloakSessionFactory().publish(new RealmModel.ClientUpdatedEvent() { @Override public ClientModel getUpdatedClient() { return ClientAdapter.this; } @Override public KeycloakSession getKeycloakSession() { return session; } }); } @Override public String getClientId() { return entity.getClientId(); } @Override public void setClientId(String clientId) { entity.setClientId(clientId); } @Override public boolean isSurrogateAuthRequired() { return entity.isSurrogateAuthRequired(); } @Override public void setSurrogateAuthRequired(boolean surrogateAuthRequired) { entity.setSurrogateAuthRequired(surrogateAuthRequired); } @Override public String getManagementUrl() { return entity.getManagementUrl(); } @Override public void setManagementUrl(String url) { entity.setManagementUrl(url); } @Override public String getRootUrl() { return entity.getRootUrl(); } @Override public void setRootUrl(String url) { entity.setRootUrl(url); } @Override public String getBaseUrl() { return entity.getBaseUrl(); } @Override public void setBaseUrl(String url) { entity.setBaseUrl(url); } @Override public boolean isBearerOnly() { return entity.isBearerOnly(); } @Override public void setBearerOnly(boolean only) { entity.setBearerOnly(only); } @Override public boolean isConsentRequired() { return entity.isConsentRequired(); } @Override public void setConsentRequired(boolean consentRequired) { entity.setConsentRequired(consentRequired); } @Override public boolean isStandardFlowEnabled() { return entity.isStandardFlowEnabled(); } @Override public void setStandardFlowEnabled(boolean standardFlowEnabled) { entity.setStandardFlowEnabled(standardFlowEnabled); } @Override public boolean isImplicitFlowEnabled() { return entity.isImplicitFlowEnabled(); } @Override public void setImplicitFlowEnabled(boolean implicitFlowEnabled) { entity.setImplicitFlowEnabled(implicitFlowEnabled); } @Override public boolean isDirectAccessGrantsEnabled() { return entity.isDirectAccessGrantsEnabled(); } @Override public void setDirectAccessGrantsEnabled(boolean directAccessGrantsEnabled) { entity.setDirectAccessGrantsEnabled(directAccessGrantsEnabled); } @Override public boolean isServiceAccountsEnabled() { return entity.isServiceAccountsEnabled(); } @Override public void setServiceAccountsEnabled(boolean serviceAccountsEnabled) { entity.setServiceAccountsEnabled(serviceAccountsEnabled); } @Override public RoleModel getRole(String name) { return session.realms().getClientRole(realm, this, name); } @Override public RoleModel addRole(String name) { return session.realms().addClientRole(realm, this, name); } @Override public RoleModel addRole(String id, String name) { return session.realms().addClientRole(realm, this, id, name); } @Override public boolean removeRole(RoleModel roleModel) { return session.realms().removeRole(realm, roleModel); } @Override public Set<RoleModel> getRoles() { return session.realms().getClientRoles(realm, this); } @Override public boolean hasScope(RoleModel role) { if (isFullScopeAllowed()) return true; Set<RoleModel> roles = getScopeMappings(); if (roles.contains(role)) return true; for (RoleModel mapping : roles) { if (mapping.hasRole(role)) return true; } roles = getRoles(); if (roles.contains(role)) return true; for (RoleModel mapping : roles) { if (mapping.hasRole(role)) return true; } return false; } @Override public List<String> getDefaultRoles() { Collection<RoleEntity> entities = entity.getDefaultRoles(); List<String> roles = new ArrayList<String>(); if (entities == null) return roles; for (RoleEntity entity : entities) { roles.add(entity.getName()); } return roles; } @Override public void addDefaultRole(String name) { RoleModel role = getRole(name); if (role == null) { role = addRole(name); } Collection<RoleEntity> entities = entity.getDefaultRoles(); for (RoleEntity entity : entities) { if (entity.getId().equals(role.getId())) { return; } } RoleEntity roleEntity = RoleAdapter.toRoleEntity(role, em); entities.add(roleEntity); em.flush(); } @Override public void updateDefaultRoles(String... defaultRoles) { Collection<RoleEntity> entities = entity.getDefaultRoles(); Set<String> already = new HashSet<String>(); List<RoleEntity> remove = new ArrayList<>(); for (RoleEntity rel : entities) { if (!contains(rel.getName(), defaultRoles)) { remove.add(rel); } else { already.add(rel.getName()); } } for (RoleEntity entity : remove) { entities.remove(entity); } em.flush(); for (String roleName : defaultRoles) { if (!already.contains(roleName)) { addDefaultRole(roleName); } } em.flush(); } @Override public void removeDefaultRoles(String... defaultRoles) { Collection<RoleEntity> entities = entity.getDefaultRoles(); List<RoleEntity> remove = new ArrayList<RoleEntity>(); for (RoleEntity rel : entities) { if (contains(rel.getName(), defaultRoles)) { remove.add(rel); } } for (RoleEntity entity : remove) { entities.remove(entity); } em.flush(); } @Override public int getNodeReRegistrationTimeout() { return entity.getNodeReRegistrationTimeout(); } @Override public void setNodeReRegistrationTimeout(int timeout) { entity.setNodeReRegistrationTimeout(timeout); } @Override public Map<String, Integer> getRegisteredNodes() { return entity.getRegisteredNodes(); } @Override public void registerNode(String nodeHost, int registrationTime) { Map<String, Integer> currentNodes = getRegisteredNodes(); currentNodes.put(nodeHost, registrationTime); em.flush(); } @Override public void unregisterNode(String nodeHost) { Map<String, Integer> currentNodes = getRegisteredNodes(); currentNodes.remove(nodeHost); em.flush(); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || !(o instanceof ClientModel)) return false; ClientModel that = (ClientModel) o; return that.getId().equals(getId()); } @Override public int hashCode() { return getId().hashCode(); } public String toString() { return getClientId(); } }