Deprecate IDP related methods in RealmModel

- delegate to the new provider

Closes #31253

Signed-off-by: Stefan Guilhen <sguilhen@redhat.com>
This commit is contained in:
Stefan Guilhen 2024-07-23 13:18:12 -03:00 committed by Alexander Schwartz
parent c16e88bcee
commit f45529de8c
7 changed files with 55 additions and 233 deletions

View file

@ -906,20 +906,16 @@ public class RealmAdapter implements CachedRealmModel {
@Override @Override
public Stream<IdentityProviderModel> getIdentityProvidersStream() { public Stream<IdentityProviderModel> getIdentityProvidersStream() {
if (isUpdated()) return updated.getIdentityProvidersStream().map(this::createOrganizationAwareIdentityProviderModel); return session.identityProviders().getAllStream().map(this::createOrganizationAwareIdentityProviderModel);
return cached.getIdentityProviders().stream().map(this::createOrganizationAwareIdentityProviderModel);
} }
@Override @Override
public IdentityProviderModel getIdentityProviderByAlias(String alias) { public IdentityProviderModel getIdentityProviderByAlias(String alias) {
if (isUpdated()) return createOrganizationAwareIdentityProviderModel(updated.getIdentityProviderByAlias(alias)); IdentityProviderModel idp = session.identityProviders().getByAlias(alias);
return getIdentityProvidersStream() return idp != null ? createOrganizationAwareIdentityProviderModel(idp) : null;
.filter(model -> Objects.equals(model.getAlias(), alias))
.findFirst()
.map(this::createOrganizationAwareIdentityProviderModel)
.orElse(null);
} }
// TODO move this to the infinispan IDPProvider implementation.
private IdentityProviderModel createOrganizationAwareIdentityProviderModel(IdentityProviderModel idp) { private IdentityProviderModel createOrganizationAwareIdentityProviderModel(IdentityProviderModel idp) {
if (!Profile.isFeatureEnabled(Profile.Feature.ORGANIZATION)) return idp; if (!Profile.isFeatureEnabled(Profile.Feature.ORGANIZATION)) return idp;
return new IdentityProviderModel(idp) { return new IdentityProviderModel(idp) {
@ -938,20 +934,17 @@ public class RealmAdapter implements CachedRealmModel {
@Override @Override
public void addIdentityProvider(IdentityProviderModel identityProvider) { public void addIdentityProvider(IdentityProviderModel identityProvider) {
getDelegateForUpdate(); session.identityProviders().create(identityProvider);
updated.addIdentityProvider(identityProvider);
} }
@Override @Override
public void updateIdentityProvider(IdentityProviderModel identityProvider) { public void updateIdentityProvider(IdentityProviderModel identityProvider) {
getDelegateForUpdate(); session.identityProviders().update(identityProvider);
updated.updateIdentityProvider(identityProvider);
} }
@Override @Override
public void removeIdentityProviderByAlias(String alias) { public void removeIdentityProviderByAlias(String alias) {
getDelegateForUpdate(); session.identityProviders().remove(alias);
updated.removeIdentityProviderByAlias(alias);
} }
@Override @Override
@ -1147,8 +1140,7 @@ public class RealmAdapter implements CachedRealmModel {
@Override @Override
public boolean isIdentityFederationEnabled() { public boolean isIdentityFederationEnabled() {
if (isUpdated()) return updated.isIdentityFederationEnabled(); return session.identityProviders().isIdentityFederationEnabled();
return cached.isIdentityFederationEnabled();
} }

View file

@ -40,7 +40,6 @@ import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientScopeModel; import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.GroupModel; import org.keycloak.models.GroupModel;
import org.keycloak.models.IdentityProviderMapperModel; import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.OAuth2DeviceConfig; import org.keycloak.models.OAuth2DeviceConfig;
import org.keycloak.models.OTPPolicy; import org.keycloak.models.OTPPolicy;
import org.keycloak.models.ParConfig; import org.keycloak.models.ParConfig;
@ -127,7 +126,6 @@ public class CachedRealm extends AbstractExtendableRevisioned {
protected MultivaluedMap<String, ComponentModel> componentsByParent = new MultivaluedHashMap<>(); protected MultivaluedMap<String, ComponentModel> componentsByParent = new MultivaluedHashMap<>();
protected MultivaluedMap<String, ComponentModel> componentsByParentAndType = new ConcurrentMultivaluedHashMap<>(); protected MultivaluedMap<String, ComponentModel> componentsByParentAndType = new ConcurrentMultivaluedHashMap<>();
protected Map<String, ComponentModel> components; protected Map<String, ComponentModel> components;
protected List<IdentityProviderModel> identityProviders;
protected Map<String, String> browserSecurityHeaders; protected Map<String, String> browserSecurityHeaders;
protected Map<String, String> smtpConfig; protected Map<String, String> smtpConfig;
@ -145,7 +143,6 @@ public class CachedRealm extends AbstractExtendableRevisioned {
protected AuthenticationFlowModel browserFlow; protected AuthenticationFlowModel browserFlow;
protected AuthenticationFlowModel registrationFlow; protected AuthenticationFlowModel registrationFlow;
protected AuthenticationFlowModel orgRegistrationFlow;
protected AuthenticationFlowModel directGrantFlow; protected AuthenticationFlowModel directGrantFlow;
protected AuthenticationFlowModel resetCredentialsFlow; protected AuthenticationFlowModel resetCredentialsFlow;
protected AuthenticationFlowModel clientAuthenticationFlow; protected AuthenticationFlowModel clientAuthenticationFlow;
@ -195,7 +192,6 @@ public class CachedRealm extends AbstractExtendableRevisioned {
loginWithEmailAllowed = model.isLoginWithEmailAllowed(); loginWithEmailAllowed = model.isLoginWithEmailAllowed();
duplicateEmailsAllowed = model.isDuplicateEmailsAllowed(); duplicateEmailsAllowed = model.isDuplicateEmailsAllowed();
resetPasswordAllowed = model.isResetPasswordAllowed(); resetPasswordAllowed = model.isResetPasswordAllowed();
identityFederationEnabled = model.isIdentityFederationEnabled();
editUsernameAllowed = model.isEditUsernameAllowed(); editUsernameAllowed = model.isEditUsernameAllowed();
organizationsEnabled = model.isOrganizationsEnabled(); organizationsEnabled = model.isOrganizationsEnabled();
//--- brute force settings //--- brute force settings
@ -249,10 +245,6 @@ public class CachedRealm extends AbstractExtendableRevisioned {
requiredCredentials = model.getRequiredCredentialsStream().collect(Collectors.toList()); requiredCredentials = model.getRequiredCredentialsStream().collect(Collectors.toList());
userActionTokenLifespans = Collections.unmodifiableMap(new HashMap<>(model.getUserActionTokenLifespans())); userActionTokenLifespans = Collections.unmodifiableMap(new HashMap<>(model.getUserActionTokenLifespans()));
this.identityProviders = model.getIdentityProvidersStream().map(IdentityProviderModel::new)
.collect(Collectors.toList());
this.identityProviders = Collections.unmodifiableList(this.identityProviders);
this.identityProviderMapperSet = model.getIdentityProviderMappersStream().collect(Collectors.toSet()); this.identityProviderMapperSet = model.getIdentityProviderMappersStream().collect(Collectors.toSet());
for (IdentityProviderMapperModel mapper : identityProviderMapperSet) { for (IdentityProviderMapperModel mapper : identityProviderMapperSet) {
identityProviderMappers.add(mapper.getIdentityProviderAlias(), mapper); identityProviderMappers.add(mapper.getIdentityProviderAlias(), mapper);
@ -561,10 +553,6 @@ public class CachedRealm extends AbstractExtendableRevisioned {
return passwordPolicy; return passwordPolicy;
} }
public boolean isIdentityFederationEnabled() {
return identityFederationEnabled;
}
public Map<String, String> getSmtpConfig() { public Map<String, String> getSmtpConfig() {
return smtpConfig; return smtpConfig;
} }
@ -617,10 +605,6 @@ public class CachedRealm extends AbstractExtendableRevisioned {
return adminEventsDetailsEnabled; return adminEventsDetailsEnabled;
} }
public List<IdentityProviderModel> getIdentityProviders() {
return identityProviders;
}
public boolean isInternationalizationEnabled() { public boolean isInternationalizationEnabled() {
return internationalizationEnabled; return internationalizationEnabled;
} }

View file

@ -194,6 +194,8 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider, ClientSc
.setParameter("realmId", realm.getId()).executeUpdate(); .setParameter("realmId", realm.getId()).executeUpdate();
session.groups().preRemove(adapter); session.groups().preRemove(adapter);
session.identityProviders().removeAll();
em.createNamedQuery("removeClientInitialAccessByRealm") em.createNamedQuery("removeClientInitialAccessByRealm")
.setParameter("realm", realm).executeUpdate(); .setParameter("realm", realm).executeUpdate();

View file

@ -21,9 +21,6 @@ import org.keycloak.Config;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.authentication.RequiredActionFactory; import org.keycloak.authentication.RequiredActionFactory;
import org.keycloak.authentication.RequiredActionProvider; import org.keycloak.authentication.RequiredActionProvider;
import org.keycloak.broker.provider.IdentityProvider;
import org.keycloak.broker.provider.IdentityProviderFactory;
import org.keycloak.broker.social.SocialIdentityProvider;
import org.keycloak.common.enums.SslRequired; import org.keycloak.common.enums.SslRequired;
import org.keycloak.common.util.MultivaluedHashMap; import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.common.util.Time; import org.keycloak.common.util.Time;
@ -50,7 +47,6 @@ import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Arrays; import java.util.Arrays;
import java.util.Optional;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -1220,157 +1216,32 @@ public class RealmAdapter implements StorageProviderRealmModel, JpaModel<RealmEn
@Override @Override
public Stream<IdentityProviderModel> getIdentityProvidersStream() { public Stream<IdentityProviderModel> getIdentityProvidersStream() {
return realm.getIdentityProviders().stream().map(this::entityToModel); return session.identityProviders().getAllStream();
}
private IdentityProviderModel entityToModel(IdentityProviderEntity entity) {
IdentityProviderModel identityProviderModel = getModelFromProviderFactory(entity.getProviderId());
identityProviderModel.setProviderId(entity.getProviderId());
identityProviderModel.setAlias(entity.getAlias());
identityProviderModel.setDisplayName(entity.getDisplayName());
identityProviderModel.setInternalId(entity.getInternalId());
Map<String, String> config = entity.getConfig();
Map<String, String> copy = new HashMap<>();
copy.putAll(config);
identityProviderModel.setConfig(copy);
identityProviderModel.setEnabled(entity.isEnabled());
identityProviderModel.setLinkOnly(entity.isLinkOnly());
identityProviderModel.setTrustEmail(entity.isTrustEmail());
identityProviderModel.setAuthenticateByDefault(entity.isAuthenticateByDefault());
identityProviderModel.setFirstBrokerLoginFlowId(entity.getFirstBrokerLoginFlowId());
identityProviderModel.setPostBrokerLoginFlowId(entity.getPostBrokerLoginFlowId());
identityProviderModel.setStoreToken(entity.isStoreToken());
identityProviderModel.setAddReadTokenRoleOnCreate(entity.isAddReadTokenRoleOnCreate());
return identityProviderModel;
}
private IdentityProviderModel getModelFromProviderFactory(String providerId) {
Optional<IdentityProviderFactory> factory = Stream.concat(session.getKeycloakSessionFactory().getProviderFactoriesStream(IdentityProvider.class),
session.getKeycloakSessionFactory().getProviderFactoriesStream(SocialIdentityProvider.class))
.filter(providerFactory -> Objects.equals(providerFactory.getId(), providerId))
.map(IdentityProviderFactory.class::cast)
.findFirst();
if (factory.isPresent()) {
return factory.get().createConfig();
} else {
logger.warn("Couldn't find a suitable identity provider factory for " + providerId);
return new IdentityProviderModel();
}
} }
@Override @Override
public IdentityProviderModel getIdentityProviderByAlias(String alias) { public IdentityProviderModel getIdentityProviderByAlias(String alias) {
return getIdentityProvidersStream() return session.identityProviders().getByAlias(alias);
.filter(model -> Objects.equals(model.getAlias(), alias))
.findFirst()
.orElse(null);
} }
@Override @Override
public void addIdentityProvider(IdentityProviderModel identityProvider) { public void addIdentityProvider(IdentityProviderModel identityProvider) {
IdentityProviderEntity entity = new IdentityProviderEntity(); session.identityProviders().create(identityProvider);
if (identityProvider.getInternalId() == null) {
entity.setInternalId(KeycloakModelUtils.generateId());
} else {
entity.setInternalId(identityProvider.getInternalId());
}
entity.setAlias(identityProvider.getAlias());
entity.setDisplayName(identityProvider.getDisplayName());
entity.setProviderId(identityProvider.getProviderId());
entity.setEnabled(identityProvider.isEnabled());
entity.setStoreToken(identityProvider.isStoreToken());
entity.setAddReadTokenRoleOnCreate(identityProvider.isAddReadTokenRoleOnCreate());
entity.setTrustEmail(identityProvider.isTrustEmail());
entity.setAuthenticateByDefault(identityProvider.isAuthenticateByDefault());
entity.setFirstBrokerLoginFlowId(identityProvider.getFirstBrokerLoginFlowId());
entity.setPostBrokerLoginFlowId(identityProvider.getPostBrokerLoginFlowId());
entity.setConfig(identityProvider.getConfig());
entity.setLinkOnly(identityProvider.isLinkOnly());
realm.addIdentityProvider(entity);
identityProvider.setInternalId(entity.getInternalId());
em.persist(entity);
em.flush();
} }
@Override @Override
public void removeIdentityProviderByAlias(String alias) { public void removeIdentityProviderByAlias(String alias) {
for (IdentityProviderEntity entity : realm.getIdentityProviders()) { session.identityProviders().remove(alias);
if (entity.getAlias().equals(alias)) {
IdentityProviderModel model = entityToModel(entity);
em.remove(entity);
em.flush();
session.getKeycloakSessionFactory().publish(new RealmModel.IdentityProviderRemovedEvent() {
@Override
public RealmModel getRealm() {
return RealmAdapter.this;
}
@Override
public IdentityProviderModel getRemovedIdentityProvider() {
return model;
}
@Override
public KeycloakSession getKeycloakSession() {
return session;
}
});
}
}
} }
@Override @Override
public void updateIdentityProvider(IdentityProviderModel identityProvider) { public void updateIdentityProvider(IdentityProviderModel identityProvider) {
for (IdentityProviderEntity entity : this.realm.getIdentityProviders()) { session.identityProviders().update(identityProvider);
if (entity.getInternalId().equals(identityProvider.getInternalId())) {
entity.setAlias(identityProvider.getAlias());
entity.setDisplayName(identityProvider.getDisplayName());
entity.setEnabled(identityProvider.isEnabled());
entity.setTrustEmail(identityProvider.isTrustEmail());
entity.setAuthenticateByDefault(identityProvider.isAuthenticateByDefault());
entity.setFirstBrokerLoginFlowId(identityProvider.getFirstBrokerLoginFlowId());
entity.setPostBrokerLoginFlowId(identityProvider.getPostBrokerLoginFlowId());
entity.setAddReadTokenRoleOnCreate(identityProvider.isAddReadTokenRoleOnCreate());
entity.setStoreToken(identityProvider.isStoreToken());
entity.setConfig(identityProvider.getConfig());
entity.setLinkOnly(identityProvider.isLinkOnly());
}
}
em.flush();
session.getKeycloakSessionFactory().publish(new RealmModel.IdentityProviderUpdatedEvent() {
@Override
public RealmModel getRealm() {
return RealmAdapter.this;
}
@Override
public IdentityProviderModel getUpdatedIdentityProvider() {
return identityProvider;
}
@Override
public KeycloakSession getKeycloakSession() {
return session;
}
});
} }
@Override @Override
public boolean isIdentityFederationEnabled() { public boolean isIdentityFederationEnabled() {
return !this.realm.getIdentityProviders().isEmpty(); return session.identityProviders().isIdentityFederationEnabled();
} }
@Override @Override

View file

@ -186,9 +186,6 @@ public class RealmEntity {
@Column(name="DEFAULT_ROLE") @Column(name="DEFAULT_ROLE")
protected String defaultRoleId; protected String defaultRoleId;
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true)
protected List<IdentityProviderEntity> identityProviders = new LinkedList<>();
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm") @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
Collection<IdentityProviderMapperEntity> identityProviderMappers = new LinkedList<>(); Collection<IdentityProviderMapperEntity> identityProviderMappers = new LinkedList<>();
@ -615,22 +612,6 @@ public class RealmEntity {
this.attributes = attributes; this.attributes = attributes;
} }
public List<IdentityProviderEntity> getIdentityProviders() {
if (identityProviders == null) {
identityProviders = new LinkedList<>();
}
return this.identityProviders;
}
public void setIdentityProviders(List<IdentityProviderEntity> identityProviders) {
this.identityProviders = identityProviders;
}
public void addIdentityProvider(IdentityProviderEntity entity) {
entity.setRealmId(this.id);
getIdentityProviders().add(entity);
}
public boolean isInternationalizationEnabled() { public boolean isInternationalizationEnabled() {
return internationalizationEnabled; return internationalizationEnabled;
} }

View file

@ -441,13 +441,35 @@ public interface RealmModel extends RoleContainerModel {
/** /**
* Returns identity providers as a stream. * Returns identity providers as a stream.
*
* @return Stream of {@link IdentityProviderModel}. Never returns {@code null}. * @return Stream of {@link IdentityProviderModel}. Never returns {@code null}.
* @deprecated Use {@link IDPProvider#getAllStream()} instead.
*/ */
@Deprecated
Stream<IdentityProviderModel> getIdentityProvidersStream(); Stream<IdentityProviderModel> getIdentityProvidersStream();
/**
* @deprecated Use {@link IDPProvider#getByAlias(String)} instead.
*/
@Deprecated
IdentityProviderModel getIdentityProviderByAlias(String alias); IdentityProviderModel getIdentityProviderByAlias(String alias);
/**
* @deprecated Use {@link IDPProvider#create(IdentityProviderModel)} instead.
*/
@Deprecated
void addIdentityProvider(IdentityProviderModel identityProvider); void addIdentityProvider(IdentityProviderModel identityProvider);
/**
* @deprecated Use {@link IDPProvider#remove(String)} instead.
*/
@Deprecated
void removeIdentityProviderByAlias(String alias); void removeIdentityProviderByAlias(String alias);
/**
* @deprecated Use {@link IDPProvider#update(IdentityProviderModel)} instead.
*/
@Deprecated
void updateIdentityProvider(IdentityProviderModel identityProvider); void updateIdentityProvider(IdentityProviderModel identityProvider);
/** /**
@ -616,6 +638,10 @@ public interface RealmModel extends RoleContainerModel {
*/ */
void setDefaultRole(RoleModel role); void setDefaultRole(RoleModel role);
/**
* @deprecated use {@link IDPProvider#isIdentityFederationEnabled()} instead.
*/
@Deprecated
boolean isIdentityFederationEnabled(); boolean isIdentityFederationEnabled();
boolean isInternationalizationEnabled(); boolean isInternationalizationEnabled();

View file

@ -43,7 +43,6 @@ import org.keycloak.services.ErrorResponse;
import org.keycloak.services.resources.KeycloakOpenAPI; import org.keycloak.services.resources.KeycloakOpenAPI;
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import org.keycloak.utils.ReservedCharValidator; import org.keycloak.utils.ReservedCharValidator;
import org.keycloak.utils.StringUtil;
import jakarta.ws.rs.BadRequestException; import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.Consumes; import jakarta.ws.rs.Consumes;
@ -57,11 +56,9 @@ import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.MultivaluedMap; import jakarta.ws.rs.core.MultivaluedMap;
import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response;
import java.io.IOException; import java.io.IOException;
import java.util.Comparator;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream; import java.util.stream.Stream;
import static jakarta.ws.rs.core.Response.Status.BAD_REQUEST; import static jakarta.ws.rs.core.Response.Status.BAD_REQUEST;
@ -186,36 +183,14 @@ public class IdentityProvidersResource {
this.auth.realm().requireViewIdentityProviders(); this.auth.realm().requireViewIdentityProviders();
if (maxResults == null) { if (maxResults == null) {
maxResults = 100; // always set a maximum of 100 maxResults = 100; // always set a maximum of 100 by default
} }
Function<IdentityProviderModel, IdentityProviderRepresentation> toRepresentation = briefRepresentation != null && briefRepresentation Function<IdentityProviderModel, IdentityProviderRepresentation> toRepresentation = briefRepresentation != null && briefRepresentation
? m -> ModelToRepresentation.toBriefRepresentation(realm, m) ? m -> ModelToRepresentation.toBriefRepresentation(realm, m)
: m -> StripSecretsUtils.stripSecrets(session, ModelToRepresentation.toRepresentation(realm, m)); : m -> StripSecretsUtils.stripSecrets(session, ModelToRepresentation.toRepresentation(realm, m));
Stream<IdentityProviderModel> stream = realm.getIdentityProvidersStream().sorted(new IdPComparator()); return session.identityProviders().getAllStream(search, firstResult, maxResults).map(toRepresentation);
if (!StringUtil.isBlank(search)) {
stream = stream.filter(predicateByName(search));
}
if (firstResult != null) {
stream = stream.skip(firstResult);
}
return stream.limit(maxResults).map(toRepresentation);
}
private Predicate<IdentityProviderModel> predicateByName(final String search) {
if (search.startsWith("\"") && search.endsWith("\"")) {
final String name = search.substring(1, search.length() - 1);
return (m) -> m.getAlias().equals(name);
} else if (search.startsWith("*") && search.endsWith("*")) {
final String name = search.substring(1, search.length() - 1);
return (m) -> m.getAlias().contains(name);
} else if (search.endsWith("*")) {
final String name = search.substring(0, search.length() - 1);
return (m) -> m.getAlias().startsWith(name);
} else {
return (m) -> m.getAlias().startsWith(search);
}
} }
/** /**
@ -278,13 +253,4 @@ public class IdentityProvidersResource {
return Stream.concat(session.getKeycloakSessionFactory().getProviderFactoriesStream(IdentityProvider.class), return Stream.concat(session.getKeycloakSessionFactory().getProviderFactoriesStream(IdentityProvider.class),
session.getKeycloakSessionFactory().getProviderFactoriesStream(SocialIdentityProvider.class)); session.getKeycloakSessionFactory().getProviderFactoriesStream(SocialIdentityProvider.class));
} }
// TODO: for the moment just sort the identity provider list. But the
// idea is modifying the Model API to get the result already ordered.
private static class IdPComparator implements Comparator<IdentityProviderModel> {
@Override
public int compare(IdentityProviderModel idp1, IdentityProviderModel idp2) {
return idp1.getAlias().compareTo(idp2.getAlias());
}
}
} }