Ensure identity providers returned to the org IDP selection are IDPs not associated with any orgs.
Closes #32238 Signed-off-by: Stefan Guilhen <sguilhen@redhat.com>
This commit is contained in:
parent
be766c751a
commit
585d179fe0
8 changed files with 38 additions and 44 deletions
|
@ -47,7 +47,15 @@ public interface IdentityProvidersResource {
|
||||||
@GET
|
@GET
|
||||||
@Path("instances")
|
@Path("instances")
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
List<IdentityProviderRepresentation> find(@QueryParam("search") String search, @QueryParam("briefRepresentation") Boolean briefRepresentation, @QueryParam("first") Integer firstResult, @QueryParam("max") Integer maxResults);
|
List<IdentityProviderRepresentation> find(@QueryParam("search") String search, @QueryParam("briefRepresentation") Boolean briefRepresentation,
|
||||||
|
@QueryParam("first") Integer firstResult, @QueryParam("max") Integer maxResults);
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("instances")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
List<IdentityProviderRepresentation> find(@QueryParam("search") String search, @QueryParam("briefRepresentation") Boolean briefRepresentation,
|
||||||
|
@QueryParam("first") Integer firstResult, @QueryParam("max") Integer maxResults,
|
||||||
|
@QueryParam("realmOnly") Boolean realmOnly);
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
@Path("instances")
|
@Path("instances")
|
||||||
|
|
|
@ -63,13 +63,14 @@ export const IdentityProviderSelect = ({
|
||||||
async () => {
|
async () => {
|
||||||
const params: IdentityProvidersQuery = {
|
const params: IdentityProvidersQuery = {
|
||||||
max: 20,
|
max: 20,
|
||||||
|
realmOnly: true,
|
||||||
};
|
};
|
||||||
if (search) {
|
if (search) {
|
||||||
params.search = search;
|
params.search = search;
|
||||||
}
|
}
|
||||||
|
|
||||||
const idps = await adminClient.identityProviders.find(params);
|
const idps = await adminClient.identityProviders.find(params);
|
||||||
return idps.filter((i) => !i.config?.["kc.org"]);
|
return idps;
|
||||||
},
|
},
|
||||||
setIdps,
|
setIdps,
|
||||||
[search],
|
[search],
|
||||||
|
|
|
@ -12,6 +12,7 @@ export interface PaginatedQuery {
|
||||||
|
|
||||||
export interface IdentityProvidersQuery extends PaginatedQuery {
|
export interface IdentityProvidersQuery extends PaginatedQuery {
|
||||||
search?: string;
|
search?: string;
|
||||||
|
realmOnly?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class IdentityProviders extends Resource<{ realm?: string }> {
|
export class IdentityProviders extends Resource<{ realm?: string }> {
|
||||||
|
|
|
@ -137,11 +137,6 @@ public class InfinispanIDPProvider implements IDPProvider {
|
||||||
return idpDelegate.getByFlow(flowId, search, first, max);
|
return idpDelegate.getByFlow(flowId, search, first, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Stream<IdentityProviderModel> getAllStream(String search, Integer first, Integer max) {
|
|
||||||
return idpDelegate.getAllStream(search, first, max);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Stream<IdentityProviderModel> getAllStream(Map<String, String> attrs, Integer first, Integer max) {
|
public Stream<IdentityProviderModel> getAllStream(Map<String, String> attrs, Integer first, Integer max) {
|
||||||
return idpDelegate.getAllStream(attrs, first, max);
|
return idpDelegate.getAllStream(attrs, first, max);
|
||||||
|
|
|
@ -54,6 +54,7 @@ import static org.keycloak.models.IdentityProviderModel.HIDE_ON_LOGIN;
|
||||||
import static org.keycloak.models.IdentityProviderModel.LINK_ONLY;
|
import static org.keycloak.models.IdentityProviderModel.LINK_ONLY;
|
||||||
import static org.keycloak.models.IdentityProviderModel.ORGANIZATION_ID;
|
import static org.keycloak.models.IdentityProviderModel.ORGANIZATION_ID;
|
||||||
import static org.keycloak.models.IdentityProviderModel.POST_BROKER_LOGIN_FLOW_ID;
|
import static org.keycloak.models.IdentityProviderModel.POST_BROKER_LOGIN_FLOW_ID;
|
||||||
|
import static org.keycloak.models.IdentityProviderModel.SEARCH;
|
||||||
import static org.keycloak.models.jpa.PaginationUtils.paginateQuery;
|
import static org.keycloak.models.jpa.PaginationUtils.paginateQuery;
|
||||||
import static org.keycloak.utils.StreamsUtil.closing;
|
import static org.keycloak.utils.StreamsUtil.closing;
|
||||||
|
|
||||||
|
@ -205,24 +206,6 @@ public class JpaIDPProvider implements IDPProvider {
|
||||||
return toModel(getEntityByAlias(alias));
|
return toModel(getEntityByAlias(alias));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Stream<IdentityProviderModel> getAllStream(String search, Integer first, Integer max) {
|
|
||||||
CriteriaBuilder builder = em.getCriteriaBuilder();
|
|
||||||
CriteriaQuery<IdentityProviderEntity> query = builder.createQuery(IdentityProviderEntity.class);
|
|
||||||
Root<IdentityProviderEntity> idp = query.from(IdentityProviderEntity.class);
|
|
||||||
|
|
||||||
List<Predicate> predicates = new ArrayList<>();
|
|
||||||
predicates.add(builder.equal(idp.get("realmId"), getRealm().getId()));
|
|
||||||
|
|
||||||
if (StringUtil.isNotBlank(search)) {
|
|
||||||
predicates.add(this.getAliasSearchPredicate(search, builder, idp));
|
|
||||||
}
|
|
||||||
|
|
||||||
query.orderBy(builder.asc(idp.get(ALIAS)));
|
|
||||||
TypedQuery<IdentityProviderEntity> typedQuery = em.createQuery(query.select(idp).where(predicates.toArray(Predicate[]::new)));
|
|
||||||
return closing(paginateQuery(typedQuery, first, max).getResultStream()).map(this::toModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Stream<IdentityProviderModel> getAllStream(Map<String, String> attrs, Integer first, Integer max) {
|
public Stream<IdentityProviderModel> getAllStream(Map<String, String> attrs, Integer first, Integer max) {
|
||||||
CriteriaBuilder builder = em.getCriteriaBuilder();
|
CriteriaBuilder builder = em.getCriteriaBuilder();
|
||||||
|
@ -259,6 +242,12 @@ public class JpaIDPProvider implements IDPProvider {
|
||||||
predicates.add(builder.equal(idp.get(key), value));
|
predicates.add(builder.equal(idp.get(key), value));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
case SEARCH: {
|
||||||
|
if (StringUtil.isNotBlank(value)) {
|
||||||
|
predicates.add(this.getAliasSearchPredicate(value, builder, idp));
|
||||||
|
}
|
||||||
|
break;
|
||||||
} default: {
|
} default: {
|
||||||
String dbProductName = em.unwrap(Session.class).doReturningWork(connection -> connection.getMetaData().getDatabaseProductName());
|
String dbProductName = em.unwrap(Session.class).doReturningWork(connection -> connection.getMetaData().getDatabaseProductName());
|
||||||
MapJoin<IdentityProviderEntity, String, String> configJoin = idp.joinMap("config");
|
MapJoin<IdentityProviderEntity, String, String> configJoin = idp.joinMap("config");
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.models;
|
package org.keycloak.models;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
@ -99,22 +98,9 @@ public interface IDPProvider extends Provider {
|
||||||
* @return a non-null stream of {@link IdentityProviderModel}s.
|
* @return a non-null stream of {@link IdentityProviderModel}s.
|
||||||
*/
|
*/
|
||||||
default Stream<IdentityProviderModel> getAllStream() {
|
default Stream<IdentityProviderModel> getAllStream() {
|
||||||
return getAllStream("", null, null);
|
return getAllStream(Map.of(), null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns all identity providers in the realm filtered according to the specified parameters.
|
|
||||||
*
|
|
||||||
* @param search a {@link String} representing an identity provider alias (partial or exact). If the value is enclosed
|
|
||||||
* in double quotes, the method treats it as an exact search (e.g. {@code "name"}). If the value is enclosed in
|
|
||||||
* wildcards, the method treats it as an infix search (e.g. {@code *name*}). Otherwise, the method treats it as a
|
|
||||||
* prefix search (i.e. {@code name*} and {@code name} return the same results).
|
|
||||||
* @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(String search, Integer first, Integer max);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all identity providers in the realm filtered according to the specified parameters.
|
* Returns all identity providers in the realm filtered according to the specified parameters.
|
||||||
*
|
*
|
||||||
|
@ -134,7 +120,7 @@ public interface IDPProvider extends Provider {
|
||||||
* @return a non-null stream of {@link IdentityProviderModel}s that match the search criteria.
|
* @return a non-null stream of {@link IdentityProviderModel}s that match the search criteria.
|
||||||
*/
|
*/
|
||||||
default Stream<IdentityProviderModel> getByOrganization(String orgId, Integer first, Integer max) {
|
default Stream<IdentityProviderModel> getByOrganization(String orgId, Integer first, Integer max) {
|
||||||
return getAllStream(Map.of(IdentityProviderModel.ORGANIZATION_ID, orgId), first, max);
|
return getAllStream(Map.of(IdentityProviderModel.ORGANIZATION_ID, orgId != null ? orgId : ""), first, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -50,6 +50,7 @@ public class IdentityProviderModel implements Serializable {
|
||||||
public static final String ORGANIZATION_ID = "organizationId";
|
public static final String ORGANIZATION_ID = "organizationId";
|
||||||
public static final String PASS_MAX_AGE = "passMaxAge";
|
public static final String PASS_MAX_AGE = "passMaxAge";
|
||||||
public static final String POST_BROKER_LOGIN_FLOW_ID = "postBrokerLoginFlowId";
|
public static final String POST_BROKER_LOGIN_FLOW_ID = "postBrokerLoginFlowId";
|
||||||
|
public static final String SEARCH = "search";
|
||||||
public static final String SYNC_MODE = "syncMode";
|
public static final String SYNC_MODE = "syncMode";
|
||||||
|
|
||||||
private String internalId;
|
private String internalId;
|
||||||
|
|
|
@ -55,7 +55,10 @@ import jakarta.ws.rs.QueryParam;
|
||||||
import jakarta.ws.rs.core.MediaType;
|
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 org.keycloak.utils.StringUtil;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
@ -180,18 +183,28 @@ public class IdentityProvidersResource {
|
||||||
@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 = "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 = "Boolean which defines whether brief representations are returned (default: false)") @QueryParam("briefRepresentation") Boolean briefRepresentation,
|
||||||
@Parameter(description = "Pagination offset") @QueryParam("first") Integer firstResult,
|
@Parameter(description = "Pagination offset") @QueryParam("first") Integer firstResult,
|
||||||
@Parameter(description = "Maximum results size (defaults to 100)") @QueryParam("max") Integer maxResults) {
|
@Parameter(description = "Maximum results size (defaults to 100)") @QueryParam("max") Integer maxResults,
|
||||||
|
@Parameter(description = "Boolean which defines if only realm-level IDPs (not associated with orgs) should be returned (default: false)") @QueryParam("realmOnly") Boolean realmOnly) {
|
||||||
this.auth.realm().requireViewIdentityProviders();
|
this.auth.realm().requireViewIdentityProviders();
|
||||||
|
|
||||||
if (maxResults == null) {
|
if (maxResults == null) {
|
||||||
maxResults = 100; // always set a maximum of 100 by default
|
maxResults = 100; // always set a maximum of 100 by default
|
||||||
}
|
}
|
||||||
|
|
||||||
Function<IdentityProviderModel, IdentityProviderRepresentation> toRepresentation = Optional.<Boolean>ofNullable(briefRepresentation).orElse(false)
|
Function<IdentityProviderModel, IdentityProviderRepresentation> toRepresentation = Optional.ofNullable(briefRepresentation).orElse(false)
|
||||||
? 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));
|
||||||
|
|
||||||
return session.identityProviders().getAllStream(search, firstResult, maxResults).map(toRepresentation);
|
boolean searchRealmOnlyIDPs = Optional.ofNullable(realmOnly).orElse(false);
|
||||||
|
|
||||||
|
Map<String, String> searchOptions = new HashMap<>();
|
||||||
|
if (StringUtil.isNotBlank(search)) {
|
||||||
|
searchOptions.put(IdentityProviderModel.SEARCH, search);
|
||||||
|
}
|
||||||
|
if (searchRealmOnlyIDPs) {
|
||||||
|
searchOptions.put(IdentityProviderModel.ORGANIZATION_ID, null);
|
||||||
|
}
|
||||||
|
return session.identityProviders().getAllStream(searchOptions, firstResult, maxResults).map(toRepresentation);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in a new issue