mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-09 23:12:06 -03:30
Filter out non-user authentication IdPs from account and login (#43798)
Closes #43553 Signed-off-by: stianst <stianst@gmail.com>
This commit is contained in:
parent
f92adda310
commit
1048c8d9c9
@ -44,6 +44,13 @@ public interface IdentityProvidersResource {
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
List<IdentityProviderRepresentation> findAll();
|
||||
|
||||
@GET
|
||||
@Path("instances")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
List<IdentityProviderRepresentation> find(@QueryParam("type") String type, @QueryParam("capability") String capability,
|
||||
@QueryParam("search") String search, @QueryParam("briefRepresentation") Boolean briefRepresentation,
|
||||
@QueryParam("first") Integer firstResult, @QueryParam("max") Integer maxResults);
|
||||
|
||||
@GET
|
||||
@Path("instances")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
|
||||
@ -103,6 +103,7 @@ export const UserIdentityProviderLinks = ({
|
||||
first: first!,
|
||||
max: max!,
|
||||
realmOnly: false,
|
||||
capability: "USER_LINKING",
|
||||
};
|
||||
if (search) {
|
||||
params.search = search;
|
||||
|
||||
@ -13,6 +13,8 @@ export interface PaginatedQuery {
|
||||
export interface IdentityProvidersQuery extends PaginatedQuery {
|
||||
search?: string;
|
||||
realmOnly?: boolean;
|
||||
type?: string;
|
||||
capability?: string;
|
||||
}
|
||||
|
||||
export class IdentityProviders extends Resource<{ realm?: string }> {
|
||||
|
||||
@ -20,6 +20,7 @@ package org.keycloak.models.cache.infinispan;
|
||||
import static org.keycloak.models.utils.KeycloakModelUtils.runOnRealm;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.models.IdentityProviderQuery;
|
||||
import org.keycloak.cluster.ClusterProvider;
|
||||
import org.keycloak.common.enums.SslRequired;
|
||||
import org.keycloak.common.Profile;
|
||||
@ -918,7 +919,7 @@ public class RealmAdapter implements CachedRealmModel {
|
||||
|
||||
@Override
|
||||
public Stream<IdentityProviderModel> getIdentityProvidersStream() {
|
||||
return runOnRealm(session, this, (session) -> session.identityProviders().getAllStream());
|
||||
return runOnRealm(session, this, (session) -> session.identityProviders().getAllStream(IdentityProviderQuery.userAuthentication()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -23,6 +23,8 @@ import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.keycloak.models.IdentityProviderQuery;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderStorageProvider;
|
||||
@ -181,7 +183,7 @@ public class InfinispanIdentityProviderStorageProvider implements IdentityProvid
|
||||
|
||||
if (cached == null) {
|
||||
Long loaded = realmCache.getCache().getCurrentRevision(cacheKey);
|
||||
long count = idpDelegate.getAllStream(Map.of(), 0, 1).count();
|
||||
long count = idpDelegate.getAllStream(IdentityProviderQuery.userAuthentication(), 0, 1).count();
|
||||
cached = new CachedCount(loaded, getRealm(), cacheKey, count);
|
||||
realmCache.getCache().addRevisioned(cached, realmCache.getStartupRevision());
|
||||
}
|
||||
@ -287,8 +289,8 @@ public class InfinispanIdentityProviderStorageProvider implements IdentityProvid
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<IdentityProviderModel> getAllStream(Map<String, String> attrs, Integer first, Integer max) {
|
||||
return idpDelegate.getAllStream(attrs, first, max).map(this::createOrganizationAwareIdentityProviderModel);
|
||||
public Stream<IdentityProviderModel> getAllStream(IdentityProviderQuery query, Integer first, Integer max) {
|
||||
return idpDelegate.getAllStream(query, first, max).map(this::createOrganizationAwareIdentityProviderModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -35,8 +35,11 @@ import jakarta.persistence.criteria.Predicate;
|
||||
import jakarta.persistence.criteria.Root;
|
||||
import org.hibernate.Session;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.IdentityProviderQuery;
|
||||
import org.keycloak.models.IdentityProviderType;
|
||||
import org.keycloak.broker.provider.IdentityProvider;
|
||||
import org.keycloak.broker.provider.IdentityProviderFactory;
|
||||
import org.keycloak.broker.provider.util.IdentityProviderTypeUtil;
|
||||
import org.keycloak.broker.social.SocialIdentityProvider;
|
||||
import org.keycloak.connections.jpa.JpaConnectionProvider;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
@ -218,16 +221,27 @@ public class JpaIdentityProviderStorageProvider implements IdentityProviderStora
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<IdentityProviderModel> getAllStream(Map<String, String> attrs, Integer first, Integer max) {
|
||||
public Stream<IdentityProviderModel> getAllStream(IdentityProviderQuery query, Integer first, Integer max) {
|
||||
CriteriaBuilder builder = em.getCriteriaBuilder();
|
||||
CriteriaQuery<IdentityProviderEntity> query = builder.createQuery(IdentityProviderEntity.class);
|
||||
Root<IdentityProviderEntity> idp = query.from(IdentityProviderEntity.class);
|
||||
CriteriaQuery<IdentityProviderEntity> cq = builder.createQuery(IdentityProviderEntity.class);
|
||||
Root<IdentityProviderEntity> idp = cq.from(IdentityProviderEntity.class);
|
||||
|
||||
List<Predicate> predicates = new ArrayList<>();
|
||||
predicates.add(builder.equal(idp.get("realmId"), getRealm().getId()));
|
||||
|
||||
if (attrs != null) {
|
||||
for (Map.Entry<String, String> entry : attrs.entrySet()) {
|
||||
List<String> includedProviderFactories = null;
|
||||
if (query.getType() != null && query.getType() != IdentityProviderType.ANY) {
|
||||
includedProviderFactories = IdentityProviderTypeUtil.listFactoriesByType(session, query.getType());
|
||||
} else if (query.getCapability() != null) {
|
||||
includedProviderFactories = IdentityProviderTypeUtil.listFactoriesByCapability(session, query.getCapability());
|
||||
}
|
||||
|
||||
if (includedProviderFactories != null) {
|
||||
predicates.add(builder.in(idp.get("providerId")).value(includedProviderFactories));
|
||||
}
|
||||
|
||||
if (query.getOptions() != null) {
|
||||
for (Map.Entry<String, String> entry : query.getOptions().entrySet()) {
|
||||
String key = entry.getKey();
|
||||
String value = entry.getValue();
|
||||
if (StringUtil.isBlank(key)) {
|
||||
@ -290,8 +304,8 @@ public class JpaIdentityProviderStorageProvider implements IdentityProviderStora
|
||||
}
|
||||
}
|
||||
|
||||
query.orderBy(builder.asc(idp.get(ALIAS)));
|
||||
TypedQuery<IdentityProviderEntity> typedQuery = em.createQuery(query.select(idp).where(predicates.toArray(Predicate[]::new)));
|
||||
cq.orderBy(builder.asc(idp.get(ALIAS)));
|
||||
TypedQuery<IdentityProviderEntity> typedQuery = em.createQuery(cq.select(idp).where(predicates.toArray(Predicate[]::new)));
|
||||
return closing(paginateQuery(typedQuery, first, max).getResultStream()).map(this::toModel);
|
||||
}
|
||||
|
||||
|
||||
@ -1316,7 +1316,7 @@ public class RealmAdapter implements StorageProviderRealmModel, JpaModel<RealmEn
|
||||
|
||||
@Override
|
||||
public Stream<IdentityProviderModel> getIdentityProvidersStream() {
|
||||
return session.identityProviders().getAllStream();
|
||||
return session.identityProviders().getAllStream(IdentityProviderQuery.userAuthentication());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -17,8 +17,7 @@
|
||||
|
||||
package org.keycloak.migration.migrators;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.keycloak.models.IdentityProviderQuery;
|
||||
import org.keycloak.migration.MigrationProvider;
|
||||
import org.keycloak.migration.ModelVersion;
|
||||
import org.keycloak.models.AuthenticationFlowModel;
|
||||
@ -69,7 +68,7 @@ public class MigrateTo1_7_0 implements Migration {
|
||||
DefaultAuthenticationFlows.migrateFlows(realm);
|
||||
AuthenticationFlowModel firstBrokerLoginFlow = realm.getFlowByAlias(DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_FLOW);
|
||||
|
||||
session.identityProviders().getAllStream(Map.of(IdentityProviderModel.FIRST_BROKER_LOGIN_FLOW_ID, ""), null, null)
|
||||
session.identityProviders().getAllStream(IdentityProviderQuery.userAuthentication().with(IdentityProviderModel.FIRST_BROKER_LOGIN_FLOW_ID, ""), null, null)
|
||||
.forEach(provider -> {
|
||||
provider.setFirstBrokerLoginFlowId(firstBrokerLoginFlow.getId());
|
||||
session.identityProviders().update(provider);
|
||||
|
||||
@ -17,9 +17,8 @@
|
||||
|
||||
package org.keycloak.migration.migrators;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.IdentityProviderQuery;
|
||||
import org.keycloak.migration.ModelVersion;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
@ -55,7 +54,7 @@ public class MigrateTo2_2_0 implements Migration {
|
||||
|
||||
private void addIdentityProviderAuthenticator(KeycloakSession session, RealmModel realm) {
|
||||
String defaultProvider = session.identityProviders()
|
||||
.getAllStream(Map.of(IdentityProviderModel.ENABLED, "true", IdentityProviderModel.AUTHENTICATE_BY_DEFAULT, "true"), 0, 1)
|
||||
.getAllStream(IdentityProviderQuery.userAuthentication().with(IdentityProviderModel.ENABLED, "true").with(IdentityProviderModel.AUTHENTICATE_BY_DEFAULT, "true"), 0, 1)
|
||||
.map(IdentityProviderModel::getAlias)
|
||||
.findFirst().orElse(null);
|
||||
DefaultAuthenticationFlows.addIdentityProviderAuthenticator(realm, defaultProvider);
|
||||
|
||||
@ -0,0 +1,69 @@
|
||||
package org.keycloak.broker.provider.util;
|
||||
|
||||
import org.keycloak.models.IdentityProviderCapability;
|
||||
import org.keycloak.models.IdentityProviderType;
|
||||
import org.keycloak.broker.provider.ClientAssertionIdentityProvider;
|
||||
import org.keycloak.broker.provider.ExchangeExternalToken;
|
||||
import org.keycloak.broker.provider.IdentityProvider;
|
||||
import org.keycloak.broker.provider.JWTAuthorizationGrantProvider;
|
||||
import org.keycloak.broker.provider.UserAuthenticationIdentityProvider;
|
||||
import org.keycloak.broker.social.SocialIdentityProvider;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class IdentityProviderTypeUtil {
|
||||
|
||||
private IdentityProviderTypeUtil() {
|
||||
}
|
||||
|
||||
public static List<String> listFactoriesByCapability(KeycloakSession session, IdentityProviderCapability capability) {
|
||||
Set<IdentityProviderType> types = Arrays.stream(IdentityProviderType.values()).filter(t -> t.getCapabilities().contains(capability)).collect(Collectors.toSet());
|
||||
return listFactoriesByTypes(session, types);
|
||||
}
|
||||
|
||||
public static List<String> listFactoriesByType(KeycloakSession session, IdentityProviderType type) {
|
||||
return listFactoriesByTypes(session, Set.of(type));
|
||||
}
|
||||
|
||||
private static List<String> listFactoriesByTypes(KeycloakSession session, Set<IdentityProviderType> types) {
|
||||
KeycloakSessionFactory sf = session.getKeycloakSessionFactory();
|
||||
|
||||
Stream<ProviderFactory> factories = sf.getProviderFactoriesStream(IdentityProvider.class);
|
||||
if (types.contains(IdentityProviderType.ANY) || types.contains(IdentityProviderType.USER_AUTHENTICATION)) {
|
||||
factories = Stream.concat(factories, sf.getProviderFactoriesStream(SocialIdentityProvider.class));
|
||||
}
|
||||
|
||||
Set<Class<?>> typeClasses = types.stream().map(IdentityProviderTypeUtil::toTypeClass).collect(Collectors.toSet());
|
||||
|
||||
return factories.filter(f -> typeClasses.stream().anyMatch(t -> t.isAssignableFrom(getType(f))))
|
||||
.map(ProviderFactory::getId)
|
||||
.toList();
|
||||
}
|
||||
|
||||
private static Class<?> getType(ProviderFactory<?> f) {
|
||||
try {
|
||||
return f.getClass().getMethod("create", KeycloakSession.class, IdentityProviderModel.class).getReturnType();
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Class<?> toTypeClass(IdentityProviderType type) {
|
||||
return switch (type) {
|
||||
case USER_AUTHENTICATION -> UserAuthenticationIdentityProvider.class;
|
||||
case CLIENT_ASSERTION -> ClientAssertionIdentityProvider.class;
|
||||
case EXCHANGE_EXTERNAL_TOKEN -> ExchangeExternalToken.class;
|
||||
case JWT_AUTHORIZATION_GRANT -> JWTAuthorizationGrantProvider.class;
|
||||
case ANY -> IdentityProvider.class;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@ -561,7 +561,7 @@ public class ModelToRepresentation {
|
||||
}
|
||||
|
||||
if (export) {
|
||||
List<IdentityProviderRepresentation> identityProviders = session.identityProviders().getAllStream()
|
||||
List<IdentityProviderRepresentation> identityProviders = session.identityProviders().getAllStream(IdentityProviderQuery.any())
|
||||
.map(provider -> toRepresentation(realm, provider, export)).collect(Collectors.toList());
|
||||
rep.setIdentityProviders(identityProviders);
|
||||
List<IdentityProviderMapperRepresentation> identityProviderMappers = session.identityProviders().getMappersStream()
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
package org.keycloak.models;
|
||||
|
||||
public enum IdentityProviderCapability {
|
||||
|
||||
USER_LINKING
|
||||
|
||||
}
|
||||
@ -0,0 +1,65 @@
|
||||
package org.keycloak.models;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class IdentityProviderQuery {
|
||||
|
||||
IdentityProviderType type;
|
||||
IdentityProviderCapability capability;
|
||||
Map<String, String> options;
|
||||
|
||||
public static IdentityProviderQuery any() {
|
||||
IdentityProviderQuery query = new IdentityProviderQuery();
|
||||
query.type = IdentityProviderType.ANY;
|
||||
return query;
|
||||
}
|
||||
|
||||
public static IdentityProviderQuery userAuthentication() {
|
||||
IdentityProviderQuery query = new IdentityProviderQuery();
|
||||
query.type = IdentityProviderType.USER_AUTHENTICATION;
|
||||
return query;
|
||||
}
|
||||
|
||||
public static IdentityProviderQuery type(IdentityProviderType type) {
|
||||
IdentityProviderQuery query = new IdentityProviderQuery();
|
||||
query.type = type;
|
||||
return query;
|
||||
}
|
||||
|
||||
public static IdentityProviderQuery capability(IdentityProviderCapability capability) {
|
||||
IdentityProviderQuery query = new IdentityProviderQuery();
|
||||
query.capability = capability;
|
||||
return query;
|
||||
}
|
||||
|
||||
public IdentityProviderQuery with(String key, String value) {
|
||||
if (this.options == null) {
|
||||
this.options = new HashMap<>();
|
||||
}
|
||||
this.options.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IdentityProviderQuery with(Map<String, String> options) {
|
||||
if (this.options == null) {
|
||||
this.options = new HashMap<>(options);
|
||||
} else {
|
||||
this.options.putAll(options);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public IdentityProviderType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public IdentityProviderCapability getCapability() {
|
||||
return capability;
|
||||
}
|
||||
|
||||
public Map<String, String> getOptions() {
|
||||
return options;
|
||||
}
|
||||
|
||||
}
|
||||
@ -93,12 +93,28 @@ public interface IdentityProviderStorageProvider extends Provider {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all identity providers in the current realm.
|
||||
*
|
||||
* @return a non-null stream of {@link IdentityProviderModel}s.
|
||||
* @deprecated Use {@link #getAllStream(IdentityProviderQuery)}
|
||||
*/
|
||||
@Deprecated
|
||||
default Stream<IdentityProviderModel> getAllStream() {
|
||||
return getAllStream(Map.of(), null, null);
|
||||
return getAllStream(IdentityProviderQuery.userAuthentication(), null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all identity providers in the current realm of a given type.
|
||||
* @param query the query of identity provider to return
|
||||
* @return a non-null stream of {@link IdentityProviderModel}s that match the specified type
|
||||
*/
|
||||
default Stream<IdentityProviderModel> getAllStream(IdentityProviderQuery query) {
|
||||
return getAllStream(query, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #getAllStream(IdentityProviderQuery, Integer, Integer)}
|
||||
*/
|
||||
@Deprecated
|
||||
default Stream<IdentityProviderModel> getAllStream(Map<String, String> options, Integer first, Integer max) {
|
||||
return getAllStream(IdentityProviderQuery.userAuthentication().with(options), first, max);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -113,12 +129,12 @@ public interface IdentityProviderStorageProvider extends Provider {
|
||||
* option</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param options a {@link Map} containing identity provider search options that must be matched.
|
||||
* @param query the query for identity providers to match.
|
||||
* @param first the position of the first result to be processed (pagination offset). Ignored if negative or {@code null}.
|
||||
* @param max the maximum number of results to be returned. Ignored if negative or {@code null}.
|
||||
* @return a non-null stream of {@link IdentityProviderModel}s that match the search criteria.
|
||||
*/
|
||||
Stream<IdentityProviderModel> getAllStream(Map<String, String> options, Integer first, Integer max);
|
||||
Stream<IdentityProviderModel> getAllStream(IdentityProviderQuery query, Integer first, Integer max);
|
||||
|
||||
/**
|
||||
* Returns all identity providers associated with the organization with the provided id.
|
||||
@ -129,7 +145,7 @@ public interface IdentityProviderStorageProvider extends Provider {
|
||||
* @return a non-null stream of {@link IdentityProviderModel}s that match the search criteria.
|
||||
*/
|
||||
default Stream<IdentityProviderModel> getByOrganization(String orgId, Integer first, Integer max) {
|
||||
return getAllStream(Map.of(IdentityProviderModel.ORGANIZATION_ID, orgId != null ? orgId : ""), first, max);
|
||||
return getAllStream(IdentityProviderQuery.userAuthentication().with(IdentityProviderModel.ORGANIZATION_ID, orgId != null ? orgId : ""), first, max);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -168,7 +184,7 @@ public interface IdentityProviderStorageProvider extends Provider {
|
||||
// fetch all realm-only IDPs - i.e. those not associated with orgs.
|
||||
Map<String, String> searchOptions = LoginFilter.getLoginSearchOptions();
|
||||
searchOptions.put(IdentityProviderModel.ORGANIZATION_ID, null);
|
||||
result = Stream.concat(result, getAllStream(searchOptions, null, null));
|
||||
result = Stream.concat(result, getAllStream(IdentityProviderQuery.userAuthentication().with(searchOptions), null, null));
|
||||
}
|
||||
if (mode == FetchMode.ORG_ONLY || mode == FetchMode.ALL) {
|
||||
// fetch IDPs associated with organizations.
|
||||
@ -179,7 +195,7 @@ public interface IdentityProviderStorageProvider extends Provider {
|
||||
} else {
|
||||
searchOptions.put(IdentityProviderModel.ORGANIZATION_ID_NOT_NULL, "");
|
||||
}
|
||||
result = Stream.concat(result, getAllStream(searchOptions, null, null));
|
||||
result = Stream.concat(result, getAllStream(IdentityProviderQuery.userAuthentication().with(searchOptions), null, null));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -198,7 +214,7 @@ public interface IdentityProviderStorageProvider extends Provider {
|
||||
* otherwise.
|
||||
*/
|
||||
default boolean isIdentityFederationEnabled() {
|
||||
return getAllStream(Map.of(), 0, 1).findFirst().isPresent();
|
||||
return getAllStream(IdentityProviderQuery.userAuthentication(), 0, 1).findFirst().isPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
package org.keycloak.models;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.keycloak.models.IdentityProviderCapability.USER_LINKING;
|
||||
|
||||
public enum IdentityProviderType {
|
||||
|
||||
ANY,
|
||||
USER_AUTHENTICATION(USER_LINKING),
|
||||
CLIENT_ASSERTION,
|
||||
EXCHANGE_EXTERNAL_TOKEN(USER_LINKING),
|
||||
JWT_AUTHORIZATION_GRANT(USER_LINKING);
|
||||
|
||||
private final Set<IdentityProviderCapability> capabilities;
|
||||
|
||||
IdentityProviderType(IdentityProviderCapability... capabilities) {
|
||||
if (capabilities == null || capabilities.length == 0) {
|
||||
this.capabilities = Collections.emptySet();
|
||||
} else {
|
||||
this.capabilities = Arrays.stream(capabilities).collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
||||
public Set<IdentityProviderCapability> getCapabilities() {
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
}
|
||||
@ -472,7 +472,7 @@ public interface RealmModel extends RoleContainerModel {
|
||||
* Returns identity providers as a stream.
|
||||
*
|
||||
* @return Stream of {@link IdentityProviderModel}. Never returns {@code null}.
|
||||
* @deprecated Use {@link IdentityProviderStorageProvider#getAllStream()} instead.
|
||||
* @deprecated Use {@link IdentityProviderStorageProvider#getAllStream(IdentityProviderQuery)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
Stream<IdentityProviderModel> getIdentityProvidersStream();
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package org.keycloak.broker.spiffe;
|
||||
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.IdentityProviderShowInAccountConsole;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
@ -15,18 +14,12 @@ public class SpiffeIdentityProviderConfig extends IdentityProviderModel {
|
||||
private static final Pattern TRUST_DOMAIN_PATTERN = Pattern.compile("spiffe://[a-z0-9.\\-_]*");
|
||||
|
||||
public SpiffeIdentityProviderConfig() {
|
||||
getConfig().put(IdentityProviderModel.SHOW_IN_ACCOUNT_CONSOLE, IdentityProviderShowInAccountConsole.NEVER.name());
|
||||
}
|
||||
|
||||
public SpiffeIdentityProviderConfig(IdentityProviderModel model) {
|
||||
super(model);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHideOnLogin() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int getAllowedClockSkew() {
|
||||
String allowedClockSkew = getConfig().get(ALLOWED_CLOCK_SKEW);
|
||||
if (allowedClockSkew == null || allowedClockSkew.isEmpty()) {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package org.keycloak.cache;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import org.keycloak.models.IdentityProviderQuery;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
@ -29,7 +30,7 @@ public class DefaultAlternativeLookupProvider implements AlternativeLookupProvid
|
||||
}
|
||||
}
|
||||
|
||||
IdentityProviderModel idp = session.identityProviders().getAllStream()
|
||||
IdentityProviderModel idp = session.identityProviders().getAllStream(IdentityProviderQuery.any())
|
||||
.filter(i -> issuerUrl.equals(i.getConfig().get(IdentityProviderModel.ISSUER)))
|
||||
.findFirst().orElse(null);
|
||||
if (idp != null && idp.getAlias() != null) {
|
||||
|
||||
@ -20,6 +20,7 @@ package org.keycloak.protocol.oidc;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.IdentityProviderQuery;
|
||||
import org.keycloak.broker.oidc.OIDCIdentityProviderConfig;
|
||||
import org.keycloak.common.Profile.Feature;
|
||||
import org.keycloak.common.util.SecretGenerator;
|
||||
@ -1534,8 +1535,8 @@ public class TokenManager {
|
||||
private Stream<OIDCIdentityProvider> getOIDCIdentityProviders(LogoutToken logoutToken, KeycloakSession session) {
|
||||
try {
|
||||
return session.identityProviders()
|
||||
.getAllStream(Map.of(
|
||||
OIDCIdentityProviderConfig.ISSUER, logoutToken.getIssuer()
|
||||
.getAllStream(IdentityProviderQuery.userAuthentication()
|
||||
.with(OIDCIdentityProviderConfig.ISSUER, logoutToken.getIssuer()
|
||||
), -1, -1)
|
||||
.map(model -> {
|
||||
var idp = IdentityBrokerService.getIdentityProvider(session, model.getAlias());
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
package org.keycloak.protocol.oidc.tokenexchange;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.IdentityProviderQuery;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.OAuthErrorException;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
@ -64,6 +65,7 @@ import org.keycloak.sessions.AuthenticationSessionModel;
|
||||
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
||||
|
||||
import static org.keycloak.authentication.authenticators.util.AuthenticatorUtils.getDisabledByBruteForceEventError;
|
||||
import static org.keycloak.models.IdentityProviderType.EXCHANGE_EXTERNAL_TOKEN;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -449,7 +451,7 @@ public abstract class AbstractTokenExchangeProvider implements TokenExchangeProv
|
||||
} catch (IdentityBrokerException ignore) {
|
||||
}
|
||||
|
||||
return session.identityProviders().getAllStream().map(idpModel -> {
|
||||
return session.identityProviders().getAllStream(IdentityProviderQuery.type(EXCHANGE_EXTERNAL_TOKEN)).map(idpModel -> {
|
||||
IdentityProvider<?> idp = IdentityBrokerService.getIdentityProvider(session, idpModel.getAlias());
|
||||
|
||||
if (idp instanceof ExchangeExternalToken external && external.isIssuer(alias, formParams)) {
|
||||
|
||||
@ -6,6 +6,7 @@ import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.core.MultivaluedMap;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.jboss.resteasy.reactive.NoCache;
|
||||
import org.keycloak.models.IdentityProviderQuery;
|
||||
import org.keycloak.authentication.requiredactions.DeleteAccount;
|
||||
import org.keycloak.authentication.requiredactions.UpdateEmail;
|
||||
import org.keycloak.common.Profile;
|
||||
@ -310,9 +311,10 @@ public class AccountConsole implements AccountResourceProvider {
|
||||
}
|
||||
|
||||
IdentityProviderStorageProvider identityProviders = session.identityProviders();
|
||||
Stream<IdentityProviderModel> realmBrokers = identityProviders.getAllStream(Map.of(
|
||||
IdentityProviderModel.ENABLED, "true",
|
||||
IdentityProviderModel.ORGANIZATION_ID, ""), 0, 1);
|
||||
Stream<IdentityProviderModel> realmBrokers = identityProviders.getAllStream(IdentityProviderQuery.userAuthentication()
|
||||
.with(IdentityProviderModel.ENABLED, "true")
|
||||
.with(IdentityProviderModel.ORGANIZATION_ID, ""),
|
||||
0, 1);
|
||||
Stream<IdentityProviderModel> linkedBrokers = session.users().getFederatedIdentitiesStream(realm, user)
|
||||
.map(FederatedIdentityModel::getIdentityProvider)
|
||||
.map(identityProviders::getByAlias);
|
||||
|
||||
@ -35,6 +35,7 @@ import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.IdentityProviderQuery;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.common.Profile.Feature;
|
||||
import org.keycloak.http.HttpRequest;
|
||||
@ -151,7 +152,7 @@ public class LinkedAccountsResource {
|
||||
IdentityProviderModel.ALIAS_NOT_IN, fedAliasesToExclude,
|
||||
IdentityProviderModel.SHOW_IN_ACCOUNT_CONSOLE, IdentityProviderShowInAccountConsole.ALWAYS.name());
|
||||
|
||||
linkedAccounts = session.identityProviders().getAllStream(searchOptions, firstResult, maxResults)
|
||||
linkedAccounts = session.identityProviders().getAllStream(IdentityProviderQuery.userAuthentication().with(searchOptions), firstResult, maxResults)
|
||||
.map(idp -> this.toLinkedAccount(idp, null, null))
|
||||
.toList();
|
||||
}
|
||||
@ -195,7 +196,7 @@ public class LinkedAccountsResource {
|
||||
|
||||
@Deprecated
|
||||
public List<LinkedAccountRepresentation> getLinkedAccounts(KeycloakSession session, RealmModel realm, UserModel user) {
|
||||
return session.identityProviders().getAllStream(Map.of(IdentityProviderModel.ENABLED, "true"), null, null)
|
||||
return session.identityProviders().getAllStream(IdentityProviderQuery.userAuthentication().with(IdentityProviderModel.ENABLED, "true"), null, null)
|
||||
.map(provider -> toLinkedAccountRepresentation(provider, session.users().getFederatedIdentitiesStream(realm, user)))
|
||||
.filter(Objects::nonNull)
|
||||
.sorted().toList();
|
||||
|
||||
@ -24,6 +24,9 @@ import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
|
||||
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
import org.jboss.resteasy.reactive.NoCache;
|
||||
import org.keycloak.models.IdentityProviderCapability;
|
||||
import org.keycloak.models.IdentityProviderQuery;
|
||||
import org.keycloak.models.IdentityProviderType;
|
||||
import org.keycloak.broker.provider.IdentityProvider;
|
||||
import org.keycloak.broker.provider.IdentityProviderFactory;
|
||||
import org.keycloak.broker.social.SocialIdentityProvider;
|
||||
@ -60,7 +63,6 @@ import jakarta.ws.rs.core.Response;
|
||||
import org.keycloak.utils.StringUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
@ -182,6 +184,8 @@ public class IdentityProvidersResource {
|
||||
@Tag(name = KeycloakOpenAPI.Admin.Tags.IDENTITY_PROVIDERS)
|
||||
@Operation(summary = "List identity providers")
|
||||
public Stream<IdentityProviderRepresentation> getIdentityProviders(
|
||||
@Parameter(description = "Filter by identity providers type") @QueryParam("type") String type,
|
||||
@Parameter(description = "Filter by identity providers capability") @QueryParam("capability") String capability,
|
||||
@Parameter(description = "Filter specific providers by name. Search can be prefix (name*), contains (*name*) or exact (\"name\"). Default prefixed.") @QueryParam("search") String search,
|
||||
@Parameter(description = "Boolean which defines whether brief representations are returned (default: false)") @QueryParam("briefRepresentation") Boolean briefRepresentation,
|
||||
@Parameter(description = "Pagination offset") @QueryParam("first") Integer firstResult,
|
||||
@ -199,14 +203,23 @@ public class IdentityProvidersResource {
|
||||
|
||||
boolean searchRealmOnlyIDPs = Optional.ofNullable(realmOnly).orElse(false);
|
||||
|
||||
Map<String, String> searchOptions = new HashMap<>();
|
||||
IdentityProviderQuery query;
|
||||
if (type != null) {
|
||||
query = IdentityProviderQuery.type(IdentityProviderType.valueOf(type));
|
||||
} else if (capability != null) {
|
||||
query = IdentityProviderQuery.capability(IdentityProviderCapability.valueOf(capability));
|
||||
} else {
|
||||
query = IdentityProviderQuery.any();
|
||||
}
|
||||
|
||||
if (StringUtil.isNotBlank(search)) {
|
||||
searchOptions.put(IdentityProviderModel.SEARCH, search);
|
||||
query.with(IdentityProviderModel.SEARCH, search);
|
||||
}
|
||||
if (searchRealmOnlyIDPs) {
|
||||
searchOptions.put(IdentityProviderModel.ORGANIZATION_ID, null);
|
||||
query.with(IdentityProviderModel.ORGANIZATION_ID, null);
|
||||
}
|
||||
return session.identityProviders().getAllStream(searchOptions, firstResult, maxResults).map(toRepresentation);
|
||||
|
||||
return session.identityProviders().getAllStream(query, firstResult, maxResults).map(toRepresentation);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -0,0 +1,84 @@
|
||||
package org.keycloak.tests.admin.identityprovider;
|
||||
|
||||
import org.hamcrest.MatcherAssert;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.models.IdentityProviderCapability;
|
||||
import org.keycloak.models.IdentityProviderType;
|
||||
import org.keycloak.broker.oidc.OIDCIdentityProviderFactory;
|
||||
import org.keycloak.broker.saml.SAMLIdentityProviderFactory;
|
||||
import org.keycloak.broker.spiffe.SpiffeIdentityProviderConfig;
|
||||
import org.keycloak.broker.spiffe.SpiffeIdentityProviderFactory;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.testframework.annotations.InjectRealm;
|
||||
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||
import org.keycloak.testframework.realm.ManagedRealm;
|
||||
import org.keycloak.testframework.realm.RealmConfig;
|
||||
import org.keycloak.testframework.realm.RealmConfigBuilder;
|
||||
import org.keycloak.testframework.remote.runonserver.InjectRunOnServer;
|
||||
import org.keycloak.testframework.remote.runonserver.RunOnServerClient;
|
||||
import org.keycloak.tests.client.authentication.external.SpiffeClientAuthTest;
|
||||
import org.keycloak.testsuite.util.IdentityProviderBuilder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@KeycloakIntegrationTest(config = SpiffeClientAuthTest.SpiffeServerConfig.class)
|
||||
public class IdentityProviderTypeTest {
|
||||
|
||||
@InjectRealm(config = MyRealm.class)
|
||||
ManagedRealm realm;
|
||||
|
||||
@InjectRunOnServer
|
||||
RunOnServerClient runOnServer;
|
||||
|
||||
@Test
|
||||
public void testFilterByType() {
|
||||
MatcherAssert.assertThat(getIdps(null, null), Matchers.containsInAnyOrder("myoidc", "myspiffe", "mysaml"));
|
||||
MatcherAssert.assertThat(getIdps(IdentityProviderType.ANY, null), Matchers.containsInAnyOrder("myoidc", "myspiffe", "mysaml"));
|
||||
MatcherAssert.assertThat(getIdps(IdentityProviderType.USER_AUTHENTICATION, null), Matchers.containsInAnyOrder("myoidc", "mysaml"));
|
||||
MatcherAssert.assertThat(getIdps(IdentityProviderType.CLIENT_ASSERTION, null), Matchers.containsInAnyOrder("myoidc", "myspiffe"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilterByCapability() {
|
||||
MatcherAssert.assertThat(getIdps(null, IdentityProviderCapability.USER_LINKING), Matchers.containsInAnyOrder("myoidc", "mysaml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultsToUserAuthenticationProviders() {
|
||||
runOnServer.run(s -> {
|
||||
List<String> idps = s.identityProviders().getAllStream().map(IdentityProviderModel::getAlias).toList();
|
||||
MatcherAssert.assertThat(idps, Matchers.containsInAnyOrder("myoidc", "mysaml"));
|
||||
});
|
||||
}
|
||||
|
||||
private List<String> getIdps(IdentityProviderType type, IdentityProviderCapability capability) {
|
||||
return realm.admin().identityProviders()
|
||||
.find(type != null ? type.name() : null, capability != null ? capability.name() : null, null, null, 0, 100)
|
||||
.stream().map(IdentityProviderRepresentation::getAlias).toList();
|
||||
}
|
||||
|
||||
public static class MyRealm implements RealmConfig {
|
||||
|
||||
@Override
|
||||
public RealmConfigBuilder configure(RealmConfigBuilder realm) {
|
||||
return realm
|
||||
.identityProvider(IdentityProviderBuilder.create()
|
||||
.providerId(SpiffeIdentityProviderFactory.PROVIDER_ID)
|
||||
.alias("myspiffe")
|
||||
.setAttribute(IdentityProviderModel.ISSUER, "spiffe://mytrust")
|
||||
.setAttribute(SpiffeIdentityProviderConfig.BUNDLE_ENDPOINT_KEY, "https://myendpoint")
|
||||
.build())
|
||||
.identityProvider(IdentityProviderBuilder.create()
|
||||
.providerId(OIDCIdentityProviderFactory.PROVIDER_ID)
|
||||
.alias("myoidc")
|
||||
.build())
|
||||
.identityProvider(IdentityProviderBuilder.create()
|
||||
.providerId(SAMLIdentityProviderFactory.PROVIDER_ID)
|
||||
.alias("mysaml")
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -11,8 +11,6 @@ import org.keycloak.broker.spiffe.SpiffeIdentityProviderConfig;
|
||||
import org.keycloak.broker.spiffe.SpiffeIdentityProviderFactory;
|
||||
import org.keycloak.http.simple.SimpleHttp;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.IdentityProviderShowInAccountConsole;
|
||||
import org.keycloak.models.IdentityProviderStorageProvider;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.testframework.annotations.InjectRealm;
|
||||
import org.keycloak.testframework.annotations.InjectSimpleHttp;
|
||||
@ -62,16 +60,6 @@ public class SpiffeConfigTest {
|
||||
|
||||
checkNotDisplayOnLoginPages("testConfig");
|
||||
checkNoIdpsInAccountConsole();
|
||||
|
||||
rep = realm.admin().identityProviders().get("testConfig").toRepresentation();
|
||||
Assertions.assertTrue(rep.isHideOnLogin());
|
||||
Assertions.assertEquals(IdentityProviderShowInAccountConsole.NEVER.name(), rep.getConfig().get(IdentityProviderModel.SHOW_IN_ACCOUNT_CONSOLE));
|
||||
|
||||
runOnServer.run(s -> {
|
||||
IdentityProviderModel idp = s.getProvider(IdentityProviderStorageProvider.class, "jpa").getByAlias("testConfig");
|
||||
Assertions.assertTrue(idp.isHideOnLogin());
|
||||
Assertions.assertEquals(IdentityProviderShowInAccountConsole.NEVER.name(), idp.getConfig().get(IdentityProviderModel.SHOW_IN_ACCOUNT_CONSOLE));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@ -8,6 +8,7 @@ import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.models.IdentityProviderQuery;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.admin.client.resource.IdentityProviderResource;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
@ -239,7 +240,7 @@ public class SocialLoginTest extends AbstractKeycloakTest {
|
||||
Policy clientPolicy = management.authz().getStoreFactory().getPolicyStore().create(server, clientPolicyRep);
|
||||
management.users().adminImpersonatingPermission().addAssociatedPolicy(clientPolicy);
|
||||
management.users().adminImpersonatingPermission().setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
|
||||
session.identityProviders().getAllStream().forEach(idp -> {
|
||||
session.identityProviders().getAllStream(IdentityProviderQuery.userAuthentication()).forEach(idp -> {
|
||||
management.idps().setPermissionsEnabled(idp, true);
|
||||
management.idps().exchangeToPermission(idp).addAssociatedPolicy(clientPolicy);
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user