only use the names of the realms (#21576)

* only use the names of the realms

fixes: #21555

* fixed merge error

* fixed test

* removed braces from arrow function
This commit is contained in:
Erik Jan de Wit 2023-07-18 09:52:06 +02:00 committed by GitHub
parent 03716ed452
commit e24d51edca
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 69 additions and 31 deletions

View file

@ -95,20 +95,13 @@ describe("Realm tests", () => {
sidebarPage.goToCreateRealm(); sidebarPage.goToCreateRealm();
createRealmPage.fillRealmName(newRealmName).createRealm(); createRealmPage.fillRealmName(newRealmName).createRealm();
const fetchUrl = "/admin/realms?briefRepresentation=true";
cy.intercept(fetchUrl).as("fetch");
masthead.checkNotificationMessage("Realm created successfully"); masthead.checkNotificationMessage("Realm created successfully");
cy.wait(["@fetch"]);
sidebarPage.goToCreateRealm(); sidebarPage.goToCreateRealm();
createRealmPage.fillRealmName(editedRealmName).createRealm(); createRealmPage.fillRealmName(editedRealmName).createRealm();
masthead.checkNotificationMessage("Realm created successfully"); masthead.checkNotificationMessage("Realm created successfully");
cy.wait(["@fetch"]);
// Show current realms // Show current realms
sidebarPage.showCurrentRealms(4); sidebarPage.showCurrentRealms(4);
}); });

View file

@ -96,12 +96,8 @@ export const RealmSelector = () => {
}) })
.concat( .concat(
realms realms
.filter( .filter((name) => !recentRealms.includes(name) || name === realm)
(r) => !recentRealms.includes(r.realm!) || r.realm === realm, .map((name) => ({ name, used: false })),
)
.map((r) => {
return { name: r.realm!, used: false };
}),
), ),
[recentRealms, realm, realms], [recentRealms, realm, realms],
); );
@ -164,15 +160,15 @@ export const RealmSelector = () => {
</DropdownToggle> </DropdownToggle>
} }
dropdownItems={(realms.length !== 0 dropdownItems={(realms.length !== 0
? realms.map((r) => ( ? realms.map((name) => (
<DropdownItem <DropdownItem
key={r.realm} key={name}
component={ component={
<Link <Link
to={toDashboard({ realm: r.realm! })} to={toDashboard({ realm: name })}
onClick={() => setOpen(false)} onClick={() => setOpen(false)}
> >
<RealmText value={r.realm!} /> <RealmText value={name} />
</Link> </Link>
} }
/> />
@ -187,7 +183,7 @@ export const RealmSelector = () => {
{whoAmI.canCreateRealm() && ( {whoAmI.canCreateRealm() && (
<> <>
<Divider key="divider" /> <Divider key="divider" />
<DropdownItem key="add"> <DropdownItem key="add" component="div">
<AddRealm onClick={() => setOpen(false)} /> <AddRealm onClick={() => setOpen(false)} />
</DropdownItem> </DropdownItem>
</> </>

View file

@ -1,16 +1,14 @@
import { NetworkError } from "@keycloak/keycloak-admin-client"; import { NetworkError } from "@keycloak/keycloak-admin-client";
import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation";
import { sortBy } from "lodash-es";
import { PropsWithChildren, useCallback, useMemo, useState } from "react"; import { PropsWithChildren, useCallback, useMemo, useState } from "react";
import { createNamedContext, useRequiredContext } from "ui-shared"; import { createNamedContext, useRequiredContext } from "ui-shared";
import { adminClient } from "../admin-client";
import { keycloak } from "../keycloak"; import { keycloak } from "../keycloak";
import { useFetch } from "../utils/useFetch"; import { useFetch } from "../utils/useFetch";
import { fetchAdminUI } from "./auth/admin-ui-endpoint";
type RealmsContextProps = { type RealmsContextProps = {
/** A list of all the realms. */ /** A list of all the realms. */
realms: RealmRepresentation[]; realms: string[];
/** Refreshes the realms with the latest information. */ /** Refreshes the realms with the latest information. */
refresh: () => Promise<void>; refresh: () => Promise<void>;
}; };
@ -21,11 +19,11 @@ export const RealmsContext = createNamedContext<RealmsContextProps | undefined>(
); );
export const RealmsProvider = ({ children }: PropsWithChildren) => { export const RealmsProvider = ({ children }: PropsWithChildren) => {
const [realms, setRealms] = useState<RealmRepresentation[]>([]); const [realms, setRealms] = useState<string[]>([]);
const [refreshCount, setRefreshCount] = useState(0); const [refreshCount, setRefreshCount] = useState(0);
function updateRealms(realms: RealmRepresentation[]) { function updateRealms(realms: string[]) {
setRealms(sortBy(realms, "realm")); setRealms(realms.sort());
} }
useFetch( useFetch(
@ -36,7 +34,7 @@ export const RealmsProvider = ({ children }: PropsWithChildren) => {
} }
try { try {
return await adminClient.realms.find({ briefRepresentation: true }); return await fetchAdminUI<string[]>("ui-ext/realms", {});
} catch (error) { } catch (error) {
if (error instanceof NetworkError && error.response.status < 500) { if (error instanceof NetworkError && error.response.status < 500) {
return []; return [];

View file

@ -1,4 +1,3 @@
import RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation";
import { PropsWithChildren, useEffect, useMemo } from "react"; import { PropsWithChildren, useEffect, useMemo } from "react";
import { import {
@ -44,13 +43,12 @@ export const RecentRealmsProvider = ({ children }: PropsWithChildren) => {
export const useRecentRealms = () => useRequiredContext(RecentRealmsContext); export const useRecentRealms = () => useRequiredContext(RecentRealmsContext);
function filterRealmNames(realms: RealmRepresentation[], realmNames: string[]) { function filterRealmNames(realms: string[], storedRealms: string[]) {
// If no realms have been set yet we can't filter out any non-existent realm names. // If no realms have been set yet we can't filter out any non-existent realm names.
if (realms.length === 0) { if (realms.length === 0) {
return realmNames; return storedRealms;
} }
// Only keep realm names that actually still exist. // Only keep realm names that actually still exist.
const exisingRealmNames = realms.map(({ realm }) => realm!); return storedRealms.filter((realm) => realms.includes(realm));
return realmNames.filter((realm) => exisingRealmNames.includes(realm));
} }

View file

@ -50,4 +50,8 @@ public final class AdminExtResource {
return new SessionsResource(session, realm, auth); return new SessionsResource(session, realm, auth);
} }
@Path("/realms")
public RealmResource realms() {
return new RealmResource(session);
}
} }

View file

@ -0,0 +1,49 @@
package org.keycloak.admin.ui.rest;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.enums.SchemaType;
import org.eclipse.microprofile.openapi.annotations.media.Content;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.services.ForbiddenException;
import java.util.Objects;
import java.util.stream.Stream;
import static org.keycloak.utils.StreamsUtil.throwIfEmpty;
public class RealmResource {
private final KeycloakSession session;
public RealmResource(KeycloakSession session) {
this.session = session;
}
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
@Operation(
summary = "Lists only the names of the realms",
description = "Returns a list of realm names based on what the caller is allowed to view"
)
@APIResponse(
responseCode = "200",
description = "",
content = {@Content(
schema = @Schema(
implementation = String.class,
type = SchemaType.ARRAY
)
)}
)
public Stream<String> realmList() {
Stream<String> realms = session.realms().getRealmsStream().filter(Objects::nonNull).map(RealmModel::getName);
return throwIfEmpty(realms, new ForbiddenException());
}
}