mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-09 23:12:06 -03:30
added new endpoint that concatenates offline and regular sessions for clients (#36914)
fixes: #36596 Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
This commit is contained in:
parent
e53a56317e
commit
0e1f1c69af
@ -3,7 +3,7 @@ import type UserSessionRepresentation from "@keycloak/keycloak-admin-client/lib/
|
||||
import { PageSection } from "@patternfly/react-core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useAdminClient } from "../admin-client";
|
||||
import type { LoaderFunction } from "@keycloak/keycloak-ui-shared";
|
||||
import { fetchAdminUI } from "../context/auth/admin-ui-endpoint";
|
||||
import SessionsTable from "../sessions/SessionsTable";
|
||||
|
||||
type ClientSessionsProps = {
|
||||
@ -15,27 +15,18 @@ export const ClientSessions = ({ client }: ClientSessionsProps) => {
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const loader: LoaderFunction<UserSessionRepresentation> = async () => {
|
||||
const mapSessionsToType =
|
||||
(type: string) => (sessions: UserSessionRepresentation[]) =>
|
||||
sessions.map((session) => ({
|
||||
type,
|
||||
...session,
|
||||
}));
|
||||
|
||||
const allSessions = await Promise.all([
|
||||
adminClient.clients
|
||||
.listSessions({ id: client.id! })
|
||||
.then(mapSessionsToType(t("sessionsType.regularSSO"))),
|
||||
adminClient.clients
|
||||
.listOfflineSessions({
|
||||
id: client.id!,
|
||||
})
|
||||
.then(mapSessionsToType(t("sessionsType.offline"))),
|
||||
]);
|
||||
|
||||
return allSessions.flat();
|
||||
};
|
||||
const loader = async (first?: number, max?: number, search?: string) =>
|
||||
fetchAdminUI<UserSessionRepresentation[]>(
|
||||
adminClient,
|
||||
"ui-ext/sessions/client",
|
||||
{
|
||||
first: `${first}`,
|
||||
max: `${max}`,
|
||||
type: "ALL",
|
||||
clientId: client.id!,
|
||||
search: search || "",
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
<PageSection variant="light" className="pf-v5-u-p-0">
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package org.keycloak.admin.ui.rest;
|
||||
|
||||
import jakarta.ws.rs.Path;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.enums.SchemaType;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Content;
|
||||
@ -91,12 +92,55 @@ public class SessionsResource {
|
||||
return Stream.<SessionRepresentation>builder().build();
|
||||
});
|
||||
|
||||
return applySearch(search, result).distinct().skip(first).limit(max);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("client")
|
||||
@Consumes({"application/json"})
|
||||
@Produces({"application/json"})
|
||||
@Operation(
|
||||
summary = "List all sessions of the passed client containing regular and offline",
|
||||
description = "This endpoint returns a list of sessions and the clients that have been used including offline tokens"
|
||||
)
|
||||
@APIResponse(
|
||||
responseCode = "200",
|
||||
description = "",
|
||||
content = {@Content(
|
||||
schema = @Schema(
|
||||
implementation = SessionRepresentation.class,
|
||||
type = SchemaType.ARRAY
|
||||
)
|
||||
)}
|
||||
)
|
||||
public Stream<SessionRepresentation> clientSessions(@QueryParam("clientId") final String clientId,
|
||||
@QueryParam("type") @DefaultValue("ALL") final SessionType type,
|
||||
@QueryParam("search") @DefaultValue("") final String search, @QueryParam("first")
|
||||
@DefaultValue("0") int first, @QueryParam("max") @DefaultValue("10") int max) {
|
||||
ClientModel clientModel = realm.getClientById(clientId);
|
||||
auth.clients().requireView(clientModel);
|
||||
|
||||
Stream<SessionRepresentation> result = Stream.<SessionRepresentation>builder().build();
|
||||
if (type == ALL || type == REGULAR) {
|
||||
result = Stream.concat(result, session.sessions()
|
||||
.getUserSessionsStream(clientModel.getRealm(), clientModel).map(s -> toRepresentation(s, REGULAR)));
|
||||
}
|
||||
if (type == ALL || type == OFFLINE) {
|
||||
result = Stream.concat(result, session.sessions()
|
||||
.getOfflineUserSessionsStream(clientModel.getRealm(), clientModel, null, null)
|
||||
.map(s -> toRepresentation(s, OFFLINE)));
|
||||
}
|
||||
|
||||
return applySearch(search, result).distinct().skip(first).limit(max);
|
||||
}
|
||||
|
||||
private static Stream<SessionRepresentation> applySearch(String search, Stream<SessionRepresentation> result) {
|
||||
if (!StringUtil.isBlank(search)) {
|
||||
String searchTrimmed = search.trim();
|
||||
result = result.filter(s -> s.getUsername().contains(searchTrimmed) || s.getIpAddress().contains(searchTrimmed)
|
||||
|| s.getClients().values().stream().anyMatch(c -> c.contains(searchTrimmed)));
|
||||
|| s.getClients().values().stream().anyMatch(c -> c.contains(searchTrimmed)));
|
||||
}
|
||||
return result.distinct().skip(first).limit(max);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static SessionRepresentation toRepresentation(UserSessionModel session, SessionType type) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user