Updated DataTable to be capable of select (#251)
* removed unused models * added selectable to `DataList` * changed groups to use new `DataList` * fixed filter for JSX.Elements columns * better refresh
This commit is contained in:
parent
af986e0f13
commit
6ae437c86a
15 changed files with 198 additions and 1172 deletions
|
@ -1,222 +0,0 @@
|
||||||
import React, { useState } from "react";
|
|
||||||
import { useHistory } from "react-router-dom";
|
|
||||||
import {
|
|
||||||
ActionGroup,
|
|
||||||
AlertVariant,
|
|
||||||
Button,
|
|
||||||
Form,
|
|
||||||
FormGroup,
|
|
||||||
PageSection,
|
|
||||||
Select,
|
|
||||||
SelectOption,
|
|
||||||
SelectVariant,
|
|
||||||
Switch,
|
|
||||||
TextInput,
|
|
||||||
} from "@patternfly/react-core";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { Controller, useForm } from "react-hook-form";
|
|
||||||
|
|
||||||
import { ClientScopeRepresentation } from "../models/client-scope";
|
|
||||||
import { HelpItem } from "../../components/help-enabler/HelpItem";
|
|
||||||
import { useAlerts } from "../../components/alert/Alerts";
|
|
||||||
import { useAdminClient } from "../../context/auth/AdminClient";
|
|
||||||
import { ViewHeader } from "../../components/view-header/ViewHeader";
|
|
||||||
import { useLoginProviders } from "../../context/server-info/ServerInfoProvider";
|
|
||||||
|
|
||||||
export const NewClientScopeForm = () => {
|
|
||||||
const { t } = useTranslation("client-scopes");
|
|
||||||
const helpText = useTranslation("client-scopes-help").t;
|
|
||||||
const { register, control, handleSubmit, errors } = useForm<
|
|
||||||
ClientScopeRepresentation
|
|
||||||
>();
|
|
||||||
const history = useHistory();
|
|
||||||
|
|
||||||
const adminClient = useAdminClient();
|
|
||||||
const providers = useLoginProviders();
|
|
||||||
|
|
||||||
const [open, isOpen] = useState(false);
|
|
||||||
const { addAlert } = useAlerts();
|
|
||||||
|
|
||||||
const save = async (clientScopes: ClientScopeRepresentation) => {
|
|
||||||
try {
|
|
||||||
const keyValues = Object.keys(clientScopes.attributes!).map((key) => {
|
|
||||||
const newKey = key.replace(/_/g, ".");
|
|
||||||
return { [newKey]: clientScopes.attributes![key] };
|
|
||||||
});
|
|
||||||
clientScopes.attributes = Object.assign({}, ...keyValues);
|
|
||||||
|
|
||||||
await adminClient.clientScopes.create({ ...clientScopes });
|
|
||||||
addAlert(t("createClientScopeSuccess"), AlertVariant.success);
|
|
||||||
} catch (error) {
|
|
||||||
addAlert(
|
|
||||||
`${t("createClientScopeError")} '${error}'`,
|
|
||||||
AlertVariant.danger
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ViewHeader
|
|
||||||
titleKey="client-scopes:createClientScope"
|
|
||||||
subKey="client-scopes:clientScopeExplain"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<PageSection variant="light">
|
|
||||||
<Form isHorizontal onSubmit={handleSubmit(save)}>
|
|
||||||
<FormGroup
|
|
||||||
label={t("name")}
|
|
||||||
labelIcon={
|
|
||||||
<HelpItem
|
|
||||||
helpText={helpText("name")}
|
|
||||||
forLabel={t("name")}
|
|
||||||
forID="kc-name"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
fieldId="kc-name"
|
|
||||||
isRequired
|
|
||||||
validated={errors.name ? "error" : "default"}
|
|
||||||
helperTextInvalid={t("common:required")}
|
|
||||||
>
|
|
||||||
<TextInput
|
|
||||||
ref={register({ required: true })}
|
|
||||||
type="text"
|
|
||||||
id="kc-name"
|
|
||||||
name="name"
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
<FormGroup
|
|
||||||
label={t("description")}
|
|
||||||
labelIcon={
|
|
||||||
<HelpItem
|
|
||||||
helpText={helpText("description")}
|
|
||||||
forLabel={t("description")}
|
|
||||||
forID="kc-description"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
fieldId="kc-description"
|
|
||||||
>
|
|
||||||
<TextInput
|
|
||||||
ref={register}
|
|
||||||
type="text"
|
|
||||||
id="kc-description"
|
|
||||||
name="description"
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
<FormGroup
|
|
||||||
label={t("protocol")}
|
|
||||||
labelIcon={
|
|
||||||
<HelpItem
|
|
||||||
helpText={helpText("protocol")}
|
|
||||||
forLabel="protocol"
|
|
||||||
forID="kc-protocol"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
fieldId="kc-protocol"
|
|
||||||
>
|
|
||||||
<Controller
|
|
||||||
name="protocol"
|
|
||||||
defaultValue=""
|
|
||||||
control={control}
|
|
||||||
render={({ onChange, value }) => (
|
|
||||||
<Select
|
|
||||||
toggleId="kc-protocol"
|
|
||||||
required
|
|
||||||
onToggle={() => isOpen(!open)}
|
|
||||||
onSelect={(_, value, isPlaceholder) => {
|
|
||||||
onChange(isPlaceholder ? "" : (value as string));
|
|
||||||
isOpen(false);
|
|
||||||
}}
|
|
||||||
selections={value}
|
|
||||||
variant={SelectVariant.single}
|
|
||||||
aria-label={t("selectEncryptionType")}
|
|
||||||
placeholderText={t("common:selectOne")}
|
|
||||||
isOpen={open}
|
|
||||||
>
|
|
||||||
{providers.map((option) => (
|
|
||||||
<SelectOption
|
|
||||||
selected={option === value}
|
|
||||||
key={option}
|
|
||||||
value={option}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
<FormGroup
|
|
||||||
hasNoPaddingTop
|
|
||||||
label={t("displayOnConsentScreen")}
|
|
||||||
labelIcon={
|
|
||||||
<HelpItem
|
|
||||||
helpText={helpText("displayOnConsentScreen")}
|
|
||||||
forLabel={t("displayOnConsentScreen")}
|
|
||||||
forID="kc-display.on.consent.screen"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
fieldId="kc-display.on.consent.screen"
|
|
||||||
>
|
|
||||||
<Controller
|
|
||||||
name="attributes.display_on_consent_screen"
|
|
||||||
control={control}
|
|
||||||
defaultValue={false}
|
|
||||||
render={({ onChange, value }) => (
|
|
||||||
<Switch
|
|
||||||
id="kc-display.on.consent.screen"
|
|
||||||
label={t("common:on")}
|
|
||||||
labelOff={t("common:off")}
|
|
||||||
isChecked={value}
|
|
||||||
onChange={onChange}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
<FormGroup
|
|
||||||
label={t("consentScreenText")}
|
|
||||||
labelIcon={
|
|
||||||
<HelpItem
|
|
||||||
helpText={helpText("consentScreenText")}
|
|
||||||
forLabel={t("consentScreenText")}
|
|
||||||
forID="kc-consent-screen-text"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
fieldId="kc-consent-screen-text"
|
|
||||||
>
|
|
||||||
<TextInput
|
|
||||||
ref={register}
|
|
||||||
type="text"
|
|
||||||
id="kc-consent-screen-text"
|
|
||||||
name="attributes.consent_screen_text"
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
<FormGroup
|
|
||||||
label={t("guiOrder")}
|
|
||||||
labelIcon={
|
|
||||||
<HelpItem
|
|
||||||
helpText={helpText("guiOrder")}
|
|
||||||
forLabel={t("guiOrder")}
|
|
||||||
forID="kc-gui-order"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
fieldId="kc-gui-order"
|
|
||||||
>
|
|
||||||
<TextInput
|
|
||||||
ref={register}
|
|
||||||
type="number"
|
|
||||||
id="kc-gui-order"
|
|
||||||
name="attributes.gui_order"
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
<ActionGroup>
|
|
||||||
<Button variant="primary" type="submit">
|
|
||||||
{t("common:save")}
|
|
||||||
</Button>
|
|
||||||
<Button variant="link" onClick={() => history.push("..")}>
|
|
||||||
{t("common:cancel")}
|
|
||||||
</Button>
|
|
||||||
</ActionGroup>
|
|
||||||
</Form>
|
|
||||||
</PageSection>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -21,13 +21,13 @@ import {
|
||||||
|
|
||||||
import RoleRepresentation from "keycloak-admin/lib/defs/roleRepresentation";
|
import RoleRepresentation from "keycloak-admin/lib/defs/roleRepresentation";
|
||||||
import ClientRepresentation from "keycloak-admin/lib/defs/clientRepresentation";
|
import ClientRepresentation from "keycloak-admin/lib/defs/clientRepresentation";
|
||||||
|
import ProtocolMapperRepresentation from "keycloak-admin/lib/defs/protocolMapperRepresentation";
|
||||||
import { useAlerts } from "../../components/alert/Alerts";
|
import { useAlerts } from "../../components/alert/Alerts";
|
||||||
import { RealmContext } from "../../context/realm-context/RealmContext";
|
import { RealmContext } from "../../context/realm-context/RealmContext";
|
||||||
import { useAdminClient } from "../../context/auth/AdminClient";
|
import { useAdminClient } from "../../context/auth/AdminClient";
|
||||||
|
|
||||||
import { ViewHeader } from "../../components/view-header/ViewHeader";
|
import { ViewHeader } from "../../components/view-header/ViewHeader";
|
||||||
import { HelpItem } from "../../components/help-enabler/HelpItem";
|
import { HelpItem } from "../../components/help-enabler/HelpItem";
|
||||||
import { ProtocolMapperRepresentation } from "../models/client-scope";
|
|
||||||
|
|
||||||
export const RoleMappingForm = () => {
|
export const RoleMappingForm = () => {
|
||||||
const { realm } = useContext(RealmContext);
|
const { realm } = useContext(RealmContext);
|
||||||
|
|
|
@ -21,10 +21,10 @@ import {
|
||||||
ValidatedOptions,
|
ValidatedOptions,
|
||||||
} from "@patternfly/react-core";
|
} from "@patternfly/react-core";
|
||||||
import { ConfigPropertyRepresentation } from "keycloak-admin/lib/defs/configPropertyRepresentation";
|
import { ConfigPropertyRepresentation } from "keycloak-admin/lib/defs/configPropertyRepresentation";
|
||||||
|
import ProtocolMapperRepresentation from "keycloak-admin/lib/defs/protocolMapperRepresentation";
|
||||||
|
|
||||||
import { ViewHeader } from "../../components/view-header/ViewHeader";
|
import { ViewHeader } from "../../components/view-header/ViewHeader";
|
||||||
import { useAdminClient } from "../../context/auth/AdminClient";
|
import { useAdminClient } from "../../context/auth/AdminClient";
|
||||||
import { ProtocolMapperRepresentation } from "../models/client-scope";
|
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog";
|
import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog";
|
||||||
import { useAlerts } from "../../components/alert/Alerts";
|
import { useAlerts } from "../../components/alert/Alerts";
|
||||||
|
|
|
@ -19,8 +19,8 @@ import {
|
||||||
} from "@patternfly/react-core";
|
} from "@patternfly/react-core";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
import ClientScopeRepresentation from "keycloak-admin/lib/defs/clientScopeRepresentation";
|
||||||
|
|
||||||
import { ClientScopeRepresentation } from "../models/client-scope";
|
|
||||||
import { HelpItem } from "../../components/help-enabler/HelpItem";
|
import { HelpItem } from "../../components/help-enabler/HelpItem";
|
||||||
import { useAdminClient } from "../../context/auth/AdminClient";
|
import { useAdminClient } from "../../context/auth/AdminClient";
|
||||||
import { useAlerts } from "../../components/alert/Alerts";
|
import { useAlerts } from "../../components/alert/Alerts";
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
/**
|
|
||||||
* https://www.keycloak.org/docs-api/4.1/rest-api/#_protocolmapperrepresentation
|
|
||||||
*/
|
|
||||||
|
|
||||||
export interface ProtocolMapperRepresentation {
|
|
||||||
config?: Record<string, any>;
|
|
||||||
id?: string;
|
|
||||||
name?: string;
|
|
||||||
protocol?: string;
|
|
||||||
protocolMapper?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* https://www.keycloak.org/docs-api/6.0/rest-api/index.html#_clientscoperepresentation
|
|
||||||
*/
|
|
||||||
|
|
||||||
export interface ClientScopeRepresentation {
|
|
||||||
attributes?: Record<string, any>;
|
|
||||||
description?: string;
|
|
||||||
id?: string;
|
|
||||||
name?: string;
|
|
||||||
protocol?: string;
|
|
||||||
protocolMappers?: ProtocolMapperRepresentation[];
|
|
||||||
}
|
|
|
@ -1,119 +0,0 @@
|
||||||
export interface ClientRepresentation {
|
|
||||||
id?: string;
|
|
||||||
clientId?: string;
|
|
||||||
name?: string;
|
|
||||||
description?: string;
|
|
||||||
rootUrl?: string;
|
|
||||||
adminUrl?: string;
|
|
||||||
baseUrl?: string;
|
|
||||||
surrogateAuthRequired?: boolean;
|
|
||||||
enabled?: boolean;
|
|
||||||
alwaysDisplayInConsole?: boolean;
|
|
||||||
clientAuthenticatorType?: string;
|
|
||||||
secret?: string;
|
|
||||||
registrationAccessToken?: string;
|
|
||||||
defaultRoles?: string[];
|
|
||||||
redirectUris?: string[];
|
|
||||||
webOrigins?: string[];
|
|
||||||
notBefore?: number;
|
|
||||||
bearerOnly?: boolean;
|
|
||||||
consentRequired?: boolean;
|
|
||||||
standardFlowEnabled?: boolean;
|
|
||||||
implicitFlowEnabled?: boolean;
|
|
||||||
directAccessGrantsEnabled?: boolean;
|
|
||||||
serviceAccountsEnabled?: boolean;
|
|
||||||
authorizationServicesEnabled?: boolean;
|
|
||||||
directGrantsOnly?: boolean;
|
|
||||||
publicClient?: boolean;
|
|
||||||
frontchannelLogout?: boolean;
|
|
||||||
protocol?: string;
|
|
||||||
attributes?: { [index: string]: string };
|
|
||||||
authenticationFlowBindingOverrides?: { [index: string]: string };
|
|
||||||
fullScopeAllowed?: boolean;
|
|
||||||
nodeReRegistrationTimeout?: number;
|
|
||||||
registeredNodes?: { [index: string]: number };
|
|
||||||
protocolMappers?: ProtocolMapperRepresentation[];
|
|
||||||
clientTemplate?: string;
|
|
||||||
useTemplateConfig?: boolean;
|
|
||||||
useTemplateScope?: boolean;
|
|
||||||
useTemplateMappers?: boolean;
|
|
||||||
defaultClientScopes?: string[];
|
|
||||||
optionalClientScopes?: string[];
|
|
||||||
authorizationSettings?: ResourceServerRepresentation;
|
|
||||||
access?: { [index: string]: boolean };
|
|
||||||
origin?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ProtocolMapperRepresentation {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
protocol: string;
|
|
||||||
protocolMapper: string;
|
|
||||||
consentRequired: boolean;
|
|
||||||
consentText: string;
|
|
||||||
config: { [index: string]: string };
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ResourceServerRepresentation {
|
|
||||||
id: string;
|
|
||||||
clientId: string;
|
|
||||||
name: string;
|
|
||||||
allowRemoteResourceManagement: boolean;
|
|
||||||
policyEnforcementMode: PolicyEnforcementMode;
|
|
||||||
resources: ResourceRepresentation[];
|
|
||||||
policies: PolicyRepresentation[];
|
|
||||||
scopes: ScopeRepresentation[];
|
|
||||||
decisionStrategy: DecisionStrategy;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ResourceRepresentation {
|
|
||||||
name: string;
|
|
||||||
type: string;
|
|
||||||
owner: ResourceOwnerRepresentation;
|
|
||||||
ownerManagedAccess: boolean;
|
|
||||||
displayName: string;
|
|
||||||
attributes: { [index: string]: string[] };
|
|
||||||
_id: string;
|
|
||||||
uris: string[];
|
|
||||||
scopes: ScopeRepresentation[];
|
|
||||||
icon_uri: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PolicyRepresentation extends AbstractPolicyRepresentation {
|
|
||||||
config: { [index: string]: string };
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ScopeRepresentation {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
iconUri: string;
|
|
||||||
policies: PolicyRepresentation[];
|
|
||||||
resources: ResourceRepresentation[];
|
|
||||||
displayName: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ResourceOwnerRepresentation {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AbstractPolicyRepresentation {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
description: string;
|
|
||||||
type: string;
|
|
||||||
policies: string[];
|
|
||||||
resources: string[];
|
|
||||||
scopes: string[];
|
|
||||||
logic: Logic;
|
|
||||||
decisionStrategy: DecisionStrategy;
|
|
||||||
owner: string;
|
|
||||||
resourcesData: ResourceRepresentation[];
|
|
||||||
scopesData: ScopeRepresentation[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export type PolicyEnforcementMode = "ENFORCING" | "PERMISSIVE" | "DISABLED";
|
|
||||||
|
|
||||||
export type DecisionStrategy = "AFFIRMATIVE" | "UNANIMOUS" | "CONSENSUS";
|
|
||||||
|
|
||||||
export type Logic = "POSITIVE" | "NEGATIVE";
|
|
|
@ -9,14 +9,16 @@ import {
|
||||||
TableHeader,
|
TableHeader,
|
||||||
TableVariant,
|
TableVariant,
|
||||||
} from "@patternfly/react-table";
|
} from "@patternfly/react-table";
|
||||||
import { PaginatingTableToolbar } from "./PaginatingTableToolbar";
|
|
||||||
import { Spinner } from "@patternfly/react-core";
|
import { Spinner } from "@patternfly/react-core";
|
||||||
import { TableToolbar } from "./TableToolbar";
|
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
|
||||||
|
import { PaginatingTableToolbar } from "./PaginatingTableToolbar";
|
||||||
|
import { TableToolbar } from "./TableToolbar";
|
||||||
|
|
||||||
type Row<T> = {
|
type Row<T> = {
|
||||||
data: T;
|
data: T;
|
||||||
cells: (keyof T)[];
|
selected: boolean;
|
||||||
|
cells: (keyof T | JSX.Element)[];
|
||||||
};
|
};
|
||||||
|
|
||||||
type DataTableProps<T> = {
|
type DataTableProps<T> = {
|
||||||
|
@ -24,6 +26,8 @@ type DataTableProps<T> = {
|
||||||
columns: Field<T>[];
|
columns: Field<T>[];
|
||||||
rows: Row<T>[];
|
rows: Row<T>[];
|
||||||
actions?: IActions;
|
actions?: IActions;
|
||||||
|
onSelect?: (isSelected: boolean, rowIndex: number) => void;
|
||||||
|
canSelectAll: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
function DataTable<T>({
|
function DataTable<T>({
|
||||||
|
@ -31,11 +35,19 @@ function DataTable<T>({
|
||||||
rows,
|
rows,
|
||||||
actions,
|
actions,
|
||||||
ariaLabelKey,
|
ariaLabelKey,
|
||||||
|
onSelect,
|
||||||
|
canSelectAll,
|
||||||
}: DataTableProps<T>) {
|
}: DataTableProps<T>) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<Table
|
<Table
|
||||||
variant={TableVariant.compact}
|
variant={TableVariant.compact}
|
||||||
|
onSelect={
|
||||||
|
onSelect
|
||||||
|
? (_, isSelected, rowIndex) => onSelect(isSelected, rowIndex)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
canSelectAll={canSelectAll}
|
||||||
cells={columns.map((column) => {
|
cells={columns.map((column) => {
|
||||||
return { ...column, title: t(column.displayKey || column.name) };
|
return { ...column, title: t(column.displayKey || column.name) };
|
||||||
})}
|
})}
|
||||||
|
@ -62,6 +74,8 @@ export type Action<T> = IAction & {
|
||||||
|
|
||||||
export type DataListProps<T> = {
|
export type DataListProps<T> = {
|
||||||
loader: (first?: number, max?: number, search?: string) => Promise<T[]>;
|
loader: (first?: number, max?: number, search?: string) => Promise<T[]>;
|
||||||
|
onSelect?: (value: T[]) => void;
|
||||||
|
canSelectAll?: boolean;
|
||||||
isPaginated?: boolean;
|
isPaginated?: boolean;
|
||||||
ariaLabelKey: string;
|
ariaLabelKey: string;
|
||||||
searchPlaceholderKey: string;
|
searchPlaceholderKey: string;
|
||||||
|
@ -96,6 +110,8 @@ export function KeycloakDataTable<T>({
|
||||||
ariaLabelKey,
|
ariaLabelKey,
|
||||||
searchPlaceholderKey,
|
searchPlaceholderKey,
|
||||||
isPaginated = false,
|
isPaginated = false,
|
||||||
|
onSelect,
|
||||||
|
canSelectAll = false,
|
||||||
loader,
|
loader,
|
||||||
columns,
|
columns,
|
||||||
actions,
|
actions,
|
||||||
|
@ -119,6 +135,7 @@ export function KeycloakDataTable<T>({
|
||||||
data!.map((value) => {
|
data!.map((value) => {
|
||||||
return {
|
return {
|
||||||
data: value,
|
data: value,
|
||||||
|
selected: false,
|
||||||
cells: columns.map((col) => {
|
cells: columns.map((col) => {
|
||||||
if (col.cellRenderer) {
|
if (col.cellRenderer) {
|
||||||
return col.cellRenderer(value);
|
return col.cellRenderer(value);
|
||||||
|
@ -135,12 +152,26 @@ export function KeycloakDataTable<T>({
|
||||||
load();
|
load();
|
||||||
}, [first, max]);
|
}, [first, max]);
|
||||||
|
|
||||||
|
const getNodeText = (node: keyof T | JSX.Element): string => {
|
||||||
|
if (["string", "number"].includes(typeof node)) {
|
||||||
|
return node!.toString();
|
||||||
|
}
|
||||||
|
if (node instanceof Array) {
|
||||||
|
return node.map(getNodeText).join("");
|
||||||
|
}
|
||||||
|
if (typeof node === "object" && node) {
|
||||||
|
return getNodeText(node.props.children);
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
};
|
||||||
|
|
||||||
const filter = (search: string) => {
|
const filter = (search: string) => {
|
||||||
setFilteredData(
|
setFilteredData(
|
||||||
rows!.filter((row) =>
|
rows!.filter((row) =>
|
||||||
row.cells.some(
|
row.cells.some(
|
||||||
(cell) =>
|
(cell) =>
|
||||||
cell && cell.toString().toLowerCase().includes(search.toLowerCase())
|
cell &&
|
||||||
|
getNodeText(cell).toLowerCase().includes(search.toLowerCase())
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -173,6 +204,21 @@ export function KeycloakDataTable<T>({
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const _onSelect = (isSelected: boolean, rowIndex: number) => {
|
||||||
|
if (rowIndex === -1) {
|
||||||
|
setRows(
|
||||||
|
rows!.map((row) => {
|
||||||
|
row.selected = isSelected;
|
||||||
|
return row;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
rows![rowIndex].selected = isSelected;
|
||||||
|
setRows([...rows!]);
|
||||||
|
}
|
||||||
|
onSelect!(rows!.filter((row) => row.selected).map((row) => row.data));
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{!rows && <Loading />}
|
{!rows && <Loading />}
|
||||||
|
@ -195,6 +241,8 @@ export function KeycloakDataTable<T>({
|
||||||
>
|
>
|
||||||
{!loading && (emptyState === undefined || rows.length !== 0) && (
|
{!loading && (emptyState === undefined || rows.length !== 0) && (
|
||||||
<DataTable
|
<DataTable
|
||||||
|
canSelectAll={canSelectAll}
|
||||||
|
onSelect={onSelect ? _onSelect : undefined}
|
||||||
actions={convertAction()}
|
actions={convertAction()}
|
||||||
rows={rows}
|
rows={rows}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
|
@ -215,6 +263,8 @@ export function KeycloakDataTable<T>({
|
||||||
>
|
>
|
||||||
{(emptyState === undefined || rows.length !== 0) && (
|
{(emptyState === undefined || rows.length !== 0) && (
|
||||||
<DataTable
|
<DataTable
|
||||||
|
canSelectAll={canSelectAll}
|
||||||
|
onSelect={onSelect ? _onSelect : undefined}
|
||||||
actions={convertAction()}
|
actions={convertAction()}
|
||||||
rows={filteredData || rows}
|
rows={filteredData || rows}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
|
|
||||||
import { useAdminClient } from "../context/auth/AdminClient";
|
import { useAdminClient } from "../context/auth/AdminClient";
|
||||||
import { DataList } from "../components/table-toolbar/DataList";
|
import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable";
|
||||||
import { RealmContext } from "../context/realm-context/RealmContext";
|
import { RealmContext } from "../context/realm-context/RealmContext";
|
||||||
import {
|
import {
|
||||||
Table,
|
Table,
|
||||||
|
@ -88,7 +88,7 @@ export const AdminEvents = () => {
|
||||||
some json from the changed values
|
some json from the changed values
|
||||||
</DisplayDialog>
|
</DisplayDialog>
|
||||||
)}
|
)}
|
||||||
<DataList
|
<KeycloakDataTable
|
||||||
key={key}
|
key={key}
|
||||||
loader={loader}
|
loader={loader}
|
||||||
isPaginated
|
isPaginated
|
||||||
|
|
|
@ -14,7 +14,7 @@ import EventRepresentation from "keycloak-admin/lib/defs/eventRepresentation";
|
||||||
|
|
||||||
import { useAdminClient } from "../context/auth/AdminClient";
|
import { useAdminClient } from "../context/auth/AdminClient";
|
||||||
import { ViewHeader } from "../components/view-header/ViewHeader";
|
import { ViewHeader } from "../components/view-header/ViewHeader";
|
||||||
import { DataList } from "../components/table-toolbar/DataList";
|
import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable";
|
||||||
import { RealmContext } from "../context/realm-context/RealmContext";
|
import { RealmContext } from "../context/realm-context/RealmContext";
|
||||||
import { InfoCircleIcon } from "@patternfly/react-icons";
|
import { InfoCircleIcon } from "@patternfly/react-icons";
|
||||||
import { AdminEvents } from "./AdminEvents";
|
import { AdminEvents } from "./AdminEvents";
|
||||||
|
@ -64,7 +64,7 @@ export const EventsSection = () => {
|
||||||
eventKey={0}
|
eventKey={0}
|
||||||
title={<TabTitleText>{t("userEvents")}</TabTitleText>}
|
title={<TabTitleText>{t("userEvents")}</TabTitleText>}
|
||||||
>
|
>
|
||||||
<DataList
|
<KeycloakDataTable
|
||||||
key={key}
|
key={key}
|
||||||
loader={loader}
|
loader={loader}
|
||||||
isPaginated
|
isPaginated
|
||||||
|
|
|
@ -1,125 +0,0 @@
|
||||||
import React, { useState, useEffect } from "react";
|
|
||||||
import {
|
|
||||||
Table,
|
|
||||||
TableHeader,
|
|
||||||
TableBody,
|
|
||||||
TableVariant,
|
|
||||||
} from "@patternfly/react-table";
|
|
||||||
import { Button, AlertVariant } from "@patternfly/react-core";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { GroupRepresentation } from "./models/groups";
|
|
||||||
import { UsersIcon } from "@patternfly/react-icons";
|
|
||||||
import { useAdminClient } from "../context/auth/AdminClient";
|
|
||||||
import { useAlerts } from "../components/alert/Alerts";
|
|
||||||
|
|
||||||
export type GroupsListProps = {
|
|
||||||
list?: GroupRepresentation[];
|
|
||||||
refresh: () => void;
|
|
||||||
tableRowSelectedArray: number[];
|
|
||||||
setTableRowSelectedArray: (tableRowSelectedArray: number[]) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
type FormattedData = {
|
|
||||||
cells: JSX.Element[];
|
|
||||||
selected: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const GroupsList = ({
|
|
||||||
list,
|
|
||||||
refresh,
|
|
||||||
tableRowSelectedArray,
|
|
||||||
setTableRowSelectedArray,
|
|
||||||
}: GroupsListProps) => {
|
|
||||||
const { t } = useTranslation("groups");
|
|
||||||
const adminClient = useAdminClient();
|
|
||||||
const columnGroupName: keyof GroupRepresentation = "name";
|
|
||||||
const columnGroupNumber: keyof GroupRepresentation = "membersLength";
|
|
||||||
const { addAlert } = useAlerts();
|
|
||||||
const [formattedData, setFormattedData] = useState<FormattedData[]>([]);
|
|
||||||
|
|
||||||
const formatData = (data: GroupRepresentation[]) =>
|
|
||||||
data.map((group: { [key: string]: any }, index) => {
|
|
||||||
const groupName = group[columnGroupName];
|
|
||||||
const groupNumber = group[columnGroupNumber];
|
|
||||||
return {
|
|
||||||
cells: [
|
|
||||||
<Button variant="link" key={index}>
|
|
||||||
{groupName}
|
|
||||||
</Button>,
|
|
||||||
<div className="keycloak-admin--groups__member-count" key={index}>
|
|
||||||
<UsersIcon key={`user-icon-${index}`} />
|
|
||||||
{groupNumber}
|
|
||||||
</div>,
|
|
||||||
],
|
|
||||||
selected: false,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setFormattedData(formatData(list!));
|
|
||||||
}, [list]);
|
|
||||||
|
|
||||||
function onSelect(isSelected: boolean, rowId: number) {
|
|
||||||
const localRow = [...formattedData];
|
|
||||||
const localTableRow = [...tableRowSelectedArray];
|
|
||||||
if (localRow[rowId].selected !== isSelected) {
|
|
||||||
localRow[rowId].selected = isSelected;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (localTableRow.includes(rowId)) {
|
|
||||||
const index = localTableRow.indexOf(rowId);
|
|
||||||
if (index === 0) {
|
|
||||||
localTableRow.shift();
|
|
||||||
} else {
|
|
||||||
localTableRow.splice(index, 1);
|
|
||||||
}
|
|
||||||
setTableRowSelectedArray(localTableRow);
|
|
||||||
} else {
|
|
||||||
setTableRowSelectedArray([rowId, ...tableRowSelectedArray]);
|
|
||||||
}
|
|
||||||
setFormattedData(localRow);
|
|
||||||
}
|
|
||||||
|
|
||||||
const tableHeader = [{ title: t("groupName") }, { title: t("members") }];
|
|
||||||
const actions = [
|
|
||||||
{
|
|
||||||
title: t("moveTo"),
|
|
||||||
onClick: () => console.log("TO DO: Add move to functionality"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t("common:Delete"),
|
|
||||||
onClick: async (
|
|
||||||
_: React.MouseEvent<Element, MouseEvent>,
|
|
||||||
rowId: number
|
|
||||||
) => {
|
|
||||||
try {
|
|
||||||
await adminClient.groups.del({ id: list![rowId].id! });
|
|
||||||
refresh();
|
|
||||||
setTableRowSelectedArray([]);
|
|
||||||
addAlert(t("Group deleted"), AlertVariant.success);
|
|
||||||
} catch (error) {
|
|
||||||
addAlert(`${t("clientDeleteError")} ${error}`, AlertVariant.danger);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{formattedData && (
|
|
||||||
<Table
|
|
||||||
actions={actions}
|
|
||||||
variant={TableVariant.compact}
|
|
||||||
onSelect={(_, isSelected, rowId) => onSelect(isSelected, rowId)}
|
|
||||||
canSelectAll={false}
|
|
||||||
aria-label={t("tableOfGroups")}
|
|
||||||
cells={tableHeader}
|
|
||||||
rows={formattedData}
|
|
||||||
>
|
|
||||||
<TableHeader />
|
|
||||||
<TableBody />
|
|
||||||
</Table>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,12 +1,5 @@
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { GroupsList } from "./GroupsList";
|
|
||||||
import { GroupsCreateModal } from "./GroupsCreateModal";
|
|
||||||
import { TableToolbar } from "../components/table-toolbar/TableToolbar";
|
|
||||||
import { ViewHeader } from "../components/view-header/ViewHeader";
|
|
||||||
import { ListEmptyState } from "../components/list-empty-state/ListEmptyState";
|
|
||||||
import { useAdminClient } from "../context/auth/AdminClient";
|
|
||||||
import { useAlerts } from "../components/alert/Alerts";
|
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Dropdown,
|
Dropdown,
|
||||||
|
@ -14,180 +7,180 @@ import {
|
||||||
KebabToggle,
|
KebabToggle,
|
||||||
PageSection,
|
PageSection,
|
||||||
PageSectionVariants,
|
PageSectionVariants,
|
||||||
Spinner,
|
|
||||||
ToolbarItem,
|
ToolbarItem,
|
||||||
AlertVariant,
|
AlertVariant,
|
||||||
} from "@patternfly/react-core";
|
} from "@patternfly/react-core";
|
||||||
import "./GroupsSection.css";
|
import { UsersIcon } from "@patternfly/react-icons";
|
||||||
import GroupRepresentation from "keycloak-admin/lib/defs/groupRepresentation";
|
import GroupRepresentation from "keycloak-admin/lib/defs/groupRepresentation";
|
||||||
|
|
||||||
|
import { GroupsCreateModal } from "./GroupsCreateModal";
|
||||||
|
import { ViewHeader } from "../components/view-header/ViewHeader";
|
||||||
|
import { ListEmptyState } from "../components/list-empty-state/ListEmptyState";
|
||||||
|
import { useAdminClient } from "../context/auth/AdminClient";
|
||||||
|
import { useAlerts } from "../components/alert/Alerts";
|
||||||
|
import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable";
|
||||||
|
|
||||||
|
import "./GroupsSection.css";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
|
type GroupTableData = GroupRepresentation & {
|
||||||
|
membersLength?: number;
|
||||||
|
};
|
||||||
|
|
||||||
export const GroupsSection = () => {
|
export const GroupsSection = () => {
|
||||||
const { t } = useTranslation("groups");
|
const { t } = useTranslation("groups");
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
const [rawData, setRawData] = useState<{ [key: string]: any }[]>();
|
|
||||||
const [filteredData, setFilteredData] = useState<{ [key: string]: any }[]>();
|
|
||||||
const [isKebabOpen, setIsKebabOpen] = useState(false);
|
const [isKebabOpen, setIsKebabOpen] = useState(false);
|
||||||
const [createGroupName, setCreateGroupName] = useState("");
|
const [createGroupName, setCreateGroupName] = useState("");
|
||||||
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
|
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
|
||||||
const [tableRowSelectedArray, setTableRowSelectedArray] = useState<
|
const [selectedRows, setSelectedRows] = useState<GroupRepresentation[]>([]);
|
||||||
Array<number>
|
|
||||||
>([]);
|
|
||||||
const columnID: keyof GroupRepresentation = "id";
|
|
||||||
const columnGroupName: keyof GroupRepresentation = "name";
|
|
||||||
const { addAlert } = useAlerts();
|
const { addAlert } = useAlerts();
|
||||||
const membersLength = "membersLength";
|
const [key, setKey] = useState("");
|
||||||
|
const refresh = () => setKey(`${new Date().getTime()}`);
|
||||||
|
|
||||||
|
const getMembers = async (id: string) => {
|
||||||
|
const response = await adminClient.groups.listMembers({ id });
|
||||||
|
return response ? response.length : 0;
|
||||||
|
};
|
||||||
|
|
||||||
const loader = async () => {
|
const loader = async () => {
|
||||||
const groupsData = await adminClient.groups.find();
|
const groupsData = await adminClient.groups.find();
|
||||||
|
|
||||||
const getMembers = async (id: string) => {
|
const memberPromises = groupsData.map((group) => getMembers(group.id!));
|
||||||
const response = await adminClient.groups.listMembers({ id });
|
|
||||||
return response.length;
|
|
||||||
};
|
|
||||||
|
|
||||||
const memberPromises = groupsData.map((group: { [key: string]: any }) =>
|
|
||||||
getMembers(group[columnID])
|
|
||||||
);
|
|
||||||
const memberData = await Promise.all(memberPromises);
|
const memberData = await Promise.all(memberPromises);
|
||||||
const updatedObject = groupsData.map(
|
const updatedObject = groupsData.map((group: GroupTableData, i) => {
|
||||||
(group: { [key: string]: any }, i: number) => {
|
group.membersLength = memberData[i];
|
||||||
const object = Object.assign({}, group);
|
return group;
|
||||||
object[membersLength] = memberData[i];
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
setFilteredData(updatedObject);
|
|
||||||
setRawData(updatedObject);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
loader();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Filter groups
|
|
||||||
const filterGroups = (newInput: string) => {
|
|
||||||
const localRowData = rawData!.filter((obj: { [key: string]: string }) => {
|
|
||||||
const groupName = obj[columnGroupName];
|
|
||||||
return groupName.toLowerCase().includes(newInput.toLowerCase());
|
|
||||||
});
|
});
|
||||||
setFilteredData(localRowData);
|
return updatedObject;
|
||||||
};
|
|
||||||
|
|
||||||
// Kebab delete action
|
|
||||||
const onKebabToggle = (isOpen: boolean) => {
|
|
||||||
setIsKebabOpen(isOpen);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onKebabSelect = () => {
|
|
||||||
setIsKebabOpen(!isKebabOpen);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleModalToggle = () => {
|
const handleModalToggle = () => {
|
||||||
setIsCreateModalOpen(!isCreateModalOpen);
|
setIsCreateModalOpen(!isCreateModalOpen);
|
||||||
};
|
};
|
||||||
|
|
||||||
const multiDelete = async () => {
|
const deleteGroup = (group: GroupRepresentation) => {
|
||||||
if (tableRowSelectedArray.length !== 0) {
|
try {
|
||||||
const deleteGroup = async (rowId: number) => {
|
return adminClient.groups.del({
|
||||||
try {
|
id: group.id!,
|
||||||
await adminClient.groups.del({
|
|
||||||
id: filteredData ? filteredData![rowId].id : rawData![rowId].id,
|
|
||||||
});
|
|
||||||
loader();
|
|
||||||
} catch (error) {
|
|
||||||
addAlert(`${t("groupDeleteError")} ${error}`, AlertVariant.danger);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const chainedPromises = tableRowSelectedArray.map((rowId: number) => {
|
|
||||||
deleteGroup(rowId);
|
|
||||||
});
|
});
|
||||||
|
} catch (error) {
|
||||||
await Promise.all(chainedPromises)
|
addAlert(t("groupDeleteError", { error }), AlertVariant.danger);
|
||||||
.then(() => addAlert(t("groupsDeleted"), AlertVariant.success))
|
|
||||||
.then(() => setTableRowSelectedArray([]));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const multiDelete = async () => {
|
||||||
|
if (selectedRows!.length !== 0) {
|
||||||
|
const chainedPromises = selectedRows!.map((group) => deleteGroup(group));
|
||||||
|
|
||||||
|
await Promise.all(chainedPromises);
|
||||||
|
addAlert(t("groupsDeleted"), AlertVariant.success);
|
||||||
|
setSelectedRows([]);
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const GroupNameCell = (group: GroupTableData) => (
|
||||||
|
<>
|
||||||
|
<Link key={group.id} to={`/groups/${group.id}`}>
|
||||||
|
{group.name}
|
||||||
|
</Link>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
const GroupMemberCell = (group: GroupTableData) => (
|
||||||
|
<div className="keycloak-admin--groups__member-count">
|
||||||
|
<UsersIcon key={`user-icon-${group.id}`} />
|
||||||
|
{group.membersLength}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ViewHeader titleKey="groups:groups" subKey="groups:groupsDescription" />
|
<ViewHeader titleKey="groups:groups" subKey="groups:groupsDescription" />
|
||||||
<PageSection variant={PageSectionVariants.light}>
|
<PageSection variant={PageSectionVariants.light}>
|
||||||
{!rawData && (
|
<KeycloakDataTable
|
||||||
<div className="pf-u-text-align-center">
|
key={key}
|
||||||
<Spinner />
|
onSelect={(rows) => setSelectedRows([...rows])}
|
||||||
</div>
|
canSelectAll={false}
|
||||||
)}
|
loader={loader}
|
||||||
{rawData && rawData.length > 0 ? (
|
ariaLabelKey="client-scopes:clientScopeList"
|
||||||
<>
|
searchPlaceholderKey="client-scopes:searchFor"
|
||||||
<TableToolbar
|
toolbarItem={
|
||||||
inputGroupName="groupsToolbarTextInput"
|
<>
|
||||||
inputGroupPlaceholder={t("searchGroups")}
|
<ToolbarItem>
|
||||||
inputGroupOnChange={filterGroups}
|
<Button variant="primary" onClick={handleModalToggle}>
|
||||||
toolbarItem={
|
{t("createGroup")}
|
||||||
<>
|
</Button>
|
||||||
<ToolbarItem>
|
</ToolbarItem>
|
||||||
<Button
|
<ToolbarItem>
|
||||||
variant="primary"
|
<Dropdown
|
||||||
onClick={() => handleModalToggle()}
|
toggle={
|
||||||
>
|
<KebabToggle
|
||||||
{t("createGroup")}
|
onToggle={() => setIsKebabOpen(!isKebabOpen)}
|
||||||
</Button>
|
|
||||||
</ToolbarItem>
|
|
||||||
<ToolbarItem>
|
|
||||||
<Dropdown
|
|
||||||
onSelect={onKebabSelect}
|
|
||||||
toggle={<KebabToggle onToggle={onKebabToggle} />}
|
|
||||||
isOpen={isKebabOpen}
|
|
||||||
isPlain
|
|
||||||
dropdownItems={[
|
|
||||||
<DropdownItem
|
|
||||||
key="action"
|
|
||||||
component="button"
|
|
||||||
onClick={() => multiDelete()}
|
|
||||||
>
|
|
||||||
{t("common:Delete")}
|
|
||||||
</DropdownItem>,
|
|
||||||
]}
|
|
||||||
/>
|
/>
|
||||||
</ToolbarItem>
|
}
|
||||||
</>
|
isOpen={isKebabOpen}
|
||||||
}
|
isPlain
|
||||||
>
|
dropdownItems={[
|
||||||
{rawData && (
|
<DropdownItem
|
||||||
<GroupsList
|
key="action"
|
||||||
list={filteredData ? filteredData : rawData}
|
component="button"
|
||||||
refresh={loader}
|
onClick={() => {
|
||||||
tableRowSelectedArray={tableRowSelectedArray}
|
multiDelete();
|
||||||
setTableRowSelectedArray={setTableRowSelectedArray}
|
setIsKebabOpen(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("common:delete")}
|
||||||
|
</DropdownItem>,
|
||||||
|
]}
|
||||||
/>
|
/>
|
||||||
)}
|
</ToolbarItem>
|
||||||
{filteredData && filteredData.length === 0 && (
|
</>
|
||||||
<ListEmptyState
|
}
|
||||||
hasIcon={true}
|
actions={[
|
||||||
isSearchVariant={true}
|
{
|
||||||
message={t("noSearchResults")}
|
title: t("moveTo"),
|
||||||
instructions={t("noSearchResultsInstructions")}
|
onRowClick: () => console.log("TO DO: Add move to functionality"),
|
||||||
/>
|
},
|
||||||
)}
|
{
|
||||||
</TableToolbar>
|
title: t("common:delete"),
|
||||||
</>
|
onRowClick: async (group: GroupRepresentation) => {
|
||||||
) : (
|
await deleteGroup(group);
|
||||||
<ListEmptyState
|
return true;
|
||||||
hasIcon={true}
|
},
|
||||||
message={t("noGroupsInThisRealm")}
|
},
|
||||||
instructions={t("noGroupsInThisRealmInstructions")}
|
]}
|
||||||
primaryActionText={t("createGroup")}
|
columns={[
|
||||||
onPrimaryAction={() => handleModalToggle()}
|
{
|
||||||
/>
|
name: "name",
|
||||||
)}
|
displayKey: "groups:groupName",
|
||||||
|
cellRenderer: GroupNameCell,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "members",
|
||||||
|
displayKey: "groups:members",
|
||||||
|
cellRenderer: GroupMemberCell,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
emptyState={
|
||||||
|
<ListEmptyState
|
||||||
|
hasIcon={true}
|
||||||
|
message={t("noGroupsInThisRealm")}
|
||||||
|
instructions={t("noGroupsInThisRealmInstructions")}
|
||||||
|
primaryActionText={t("createGroup")}
|
||||||
|
onPrimaryAction={() => handleModalToggle()}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<GroupsCreateModal
|
<GroupsCreateModal
|
||||||
isCreateModalOpen={isCreateModalOpen}
|
isCreateModalOpen={isCreateModalOpen}
|
||||||
handleModalToggle={handleModalToggle}
|
handleModalToggle={handleModalToggle}
|
||||||
setIsCreateModalOpen={setIsCreateModalOpen}
|
setIsCreateModalOpen={setIsCreateModalOpen}
|
||||||
createGroupName={createGroupName}
|
createGroupName={createGroupName}
|
||||||
setCreateGroupName={setCreateGroupName}
|
setCreateGroupName={setCreateGroupName}
|
||||||
refresh={loader}
|
refresh={refresh}
|
||||||
/>
|
/>
|
||||||
</PageSection>
|
</PageSection>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -19,7 +19,8 @@
|
||||||
"noSearchResultsInstructions": "Click on the search bar above to search for groups",
|
"noSearchResultsInstructions": "Click on the search bar above to search for groups",
|
||||||
"noGroupsInThisRealm": "No groups in this realm",
|
"noGroupsInThisRealm": "No groups in this realm",
|
||||||
"noGroupsInThisRealmInstructions": "You haven't created any groups in this realm. Create a group to get started.",
|
"noGroupsInThisRealmInstructions": "You haven't created any groups in this realm. Create a group to get started.",
|
||||||
|
"groupDelete": "Group deleted",
|
||||||
"groupsDeleted": "Groups deleted",
|
"groupsDeleted": "Groups deleted",
|
||||||
"groupDeleteError": "Error deleting group"
|
"groupDeleteError": "Error deleting group {error}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
export interface GroupRepresentation {
|
|
||||||
id?: string;
|
|
||||||
name?: string;
|
|
||||||
path?: string;
|
|
||||||
attributes?: { [index: string]: string[] };
|
|
||||||
realmRoles?: string[];
|
|
||||||
clientRoles?: { [index: string]: string[] };
|
|
||||||
subGroups?: GroupRepresentation[];
|
|
||||||
access?: { [index: string]: boolean };
|
|
||||||
groupNumber?: number;
|
|
||||||
membersLength?: number;
|
|
||||||
list?: [];
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
export interface ServerGroupsRepresentation {
|
|
||||||
id?: number;
|
|
||||||
name?: string;
|
|
||||||
path?: string;
|
|
||||||
subGroups?: [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// TO DO: Update this to represent the data that is returned
|
|
||||||
export interface ServerGroupMembersRepresentation {
|
|
||||||
data?: [];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ServerGroupsArrayRepresentation {
|
|
||||||
groups: { [index: string]: ServerGroupsRepresentation[] };
|
|
||||||
}
|
|
|
@ -1,500 +0,0 @@
|
||||||
import { ClientScopeRepresentation } from "../../client-scopes/models/client-scope";
|
|
||||||
|
|
||||||
export interface RealmRepresentation {
|
|
||||||
id: string;
|
|
||||||
realm: string;
|
|
||||||
displayName?: string;
|
|
||||||
displayNameHtml?: string;
|
|
||||||
notBefore?: number;
|
|
||||||
defaultSignatureAlgorithm?: string;
|
|
||||||
revokeRefreshToken?: boolean;
|
|
||||||
refreshTokenMaxReuse?: number;
|
|
||||||
accessTokenLifespan?: number;
|
|
||||||
accessTokenLifespanForImplicitFlow?: number;
|
|
||||||
ssoSessionIdleTimeout?: number;
|
|
||||||
ssoSessionMaxLifespan?: number;
|
|
||||||
ssoSessionIdleTimeoutRememberMe?: number;
|
|
||||||
ssoSessionMaxLifespanRememberMe?: number;
|
|
||||||
offlineSessionIdleTimeout?: number;
|
|
||||||
offlineSessionMaxLifespanEnabled?: boolean;
|
|
||||||
offlineSessionMaxLifespan?: number;
|
|
||||||
clientSessionIdleTimeout?: number;
|
|
||||||
clientSessionMaxLifespan?: number;
|
|
||||||
clientOfflineSessionIdleTimeout?: number;
|
|
||||||
clientOfflineSessionMaxLifespan?: number;
|
|
||||||
accessCodeLifespan?: number;
|
|
||||||
accessCodeLifespanUserAction?: number;
|
|
||||||
accessCodeLifespanLogin?: number;
|
|
||||||
actionTokenGeneratedByAdminLifespan?: number;
|
|
||||||
actionTokenGeneratedByUserLifespan?: number;
|
|
||||||
enabled?: boolean;
|
|
||||||
sslRequired?: string;
|
|
||||||
passwordCredentialGrantAllowed?: boolean;
|
|
||||||
registrationAllowed?: boolean;
|
|
||||||
registrationEmailAsUsername?: boolean;
|
|
||||||
rememberMe?: boolean;
|
|
||||||
verifyEmail?: boolean;
|
|
||||||
loginWithEmailAllowed?: boolean;
|
|
||||||
duplicateEmailsAllowed?: boolean;
|
|
||||||
resetPasswordAllowed?: boolean;
|
|
||||||
editUsernameAllowed?: boolean;
|
|
||||||
bruteForceProtected?: boolean;
|
|
||||||
permanentLockout?: boolean;
|
|
||||||
maxFailureWaitSeconds?: number;
|
|
||||||
minimumQuickLoginWaitSeconds?: number;
|
|
||||||
waitIncrementSeconds?: number;
|
|
||||||
quickLoginCheckMilliSeconds?: number;
|
|
||||||
maxDeltaTimeSeconds?: number;
|
|
||||||
failureFactor?: number;
|
|
||||||
privateKey?: string;
|
|
||||||
publicKey?: string;
|
|
||||||
certificate?: string;
|
|
||||||
codeSecret?: string;
|
|
||||||
roles?: RolesRepresentation;
|
|
||||||
groups?: GroupRepresentation[];
|
|
||||||
defaultRoles?: string[];
|
|
||||||
defaultGroups?: string[];
|
|
||||||
requiredCredentials?: string[];
|
|
||||||
passwordPolicy?: string;
|
|
||||||
otpPolicyType?: string;
|
|
||||||
otpPolicyAlgorithm?: string;
|
|
||||||
otpPolicyInitialCounter?: number;
|
|
||||||
otpPolicyDigits?: number;
|
|
||||||
otpPolicyLookAheadWindow?: number;
|
|
||||||
otpPolicyPeriod?: number;
|
|
||||||
otpSupportedApplications?: string[];
|
|
||||||
webAuthnPolicyRpEntityName?: string;
|
|
||||||
webAuthnPolicySignatureAlgorithms?: string[];
|
|
||||||
webAuthnPolicyRpId?: string;
|
|
||||||
webAuthnPolicyAttestationConveyancePreference?: string;
|
|
||||||
webAuthnPolicyAuthenticatorAttachment?: string;
|
|
||||||
webAuthnPolicyRequireResidentKey?: string;
|
|
||||||
webAuthnPolicyUserVerificationRequirement?: string;
|
|
||||||
webAuthnPolicyCreateTimeout?: number;
|
|
||||||
webAuthnPolicyAvoidSameAuthenticatorRegister?: boolean;
|
|
||||||
webAuthnPolicyAcceptableAaguids?: string[];
|
|
||||||
webAuthnPolicyPasswordlessRpEntityName?: string;
|
|
||||||
webAuthnPolicyPasswordlessSignatureAlgorithms?: string[];
|
|
||||||
webAuthnPolicyPasswordlessRpId?: string;
|
|
||||||
webAuthnPolicyPasswordlessAttestationConveyancePreference?: string;
|
|
||||||
webAuthnPolicyPasswordlessAuthenticatorAttachment?: string;
|
|
||||||
webAuthnPolicyPasswordlessRequireResidentKey?: string;
|
|
||||||
webAuthnPolicyPasswordlessUserVerificationRequirement?: string;
|
|
||||||
webAuthnPolicyPasswordlessCreateTimeout?: number;
|
|
||||||
webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister?: boolean;
|
|
||||||
webAuthnPolicyPasswordlessAcceptableAaguids?: string[];
|
|
||||||
users?: UserRepresentation[];
|
|
||||||
federatedUsers?: UserRepresentation[];
|
|
||||||
scopeMappings?: ScopeMappingRepresentation[];
|
|
||||||
clientScopeMappings?: { [index: string]: ScopeMappingRepresentation[] };
|
|
||||||
clients?: ClientRepresentation[];
|
|
||||||
clientScopes?: ClientScopeRepresentation[];
|
|
||||||
defaultDefaultClientScopes?: string[];
|
|
||||||
defaultOptionalClientScopes?: string[];
|
|
||||||
browserSecurityHeaders?: { [index: string]: string };
|
|
||||||
smtpServe?: { [index: string]: string };
|
|
||||||
userFederationProviders?: UserFederationProviderRepresentation[];
|
|
||||||
userFederationMappers?: UserFederationMapperRepresentation[];
|
|
||||||
loginTheme?: string;
|
|
||||||
accountTheme?: string;
|
|
||||||
adminTheme?: string;
|
|
||||||
emailTheme?: string;
|
|
||||||
eventsEnabled?: boolean;
|
|
||||||
eventsExpiration?: number;
|
|
||||||
eventsListeners?: string[];
|
|
||||||
enabledEventTypes?: string[];
|
|
||||||
adminEventsEnabled?: boolean;
|
|
||||||
adminEventsDetailsEnabled?: boolean;
|
|
||||||
identityProviders?: IdentityProviderRepresentation[];
|
|
||||||
identityProviderMappers?: IdentityProviderMapperRepresentation[];
|
|
||||||
protocolMappers?: ProtocolMapperRepresentation[];
|
|
||||||
components?: { [index: string]: ComponentExportRepresentation };
|
|
||||||
internationalizationEnabled?: boolean;
|
|
||||||
supportedLocales?: string[];
|
|
||||||
defaultLocale?: string;
|
|
||||||
authenticationFlows?: AuthenticationFlowRepresentation[];
|
|
||||||
authenticatorConfig?: AuthenticatorConfigRepresentation[];
|
|
||||||
requiredActions?: RequiredActionProviderRepresentation[];
|
|
||||||
browserFlow?: string;
|
|
||||||
registrationFlow?: string;
|
|
||||||
directGrantFlow?: string;
|
|
||||||
resetCredentialsFlow?: string;
|
|
||||||
clientAuthenticationFlow?: string;
|
|
||||||
dockerAuthenticationFlow?: string;
|
|
||||||
attributes?: { [index: string]: string };
|
|
||||||
keycloakVersion?: string;
|
|
||||||
userManagedAccessAllowed?: boolean;
|
|
||||||
social?: boolean;
|
|
||||||
updateProfileOnInitialSocialLogin?: boolean;
|
|
||||||
socialProviders?: { [index: string]: string };
|
|
||||||
applicationScopeMappings?: { [index: string]: ScopeMappingRepresentation[] };
|
|
||||||
applications?: ApplicationRepresentation[];
|
|
||||||
oauthClients?: OAuthClientRepresentation[];
|
|
||||||
clientTemplates?: ClientTemplateRepresentation[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RolesRepresentation {
|
|
||||||
realm: RoleRepresentation[];
|
|
||||||
client: { [index: string]: RoleRepresentation[] };
|
|
||||||
application: { [index: string]: RoleRepresentation[] };
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GroupRepresentation {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
path: string;
|
|
||||||
attributes: { [index: string]: string[] };
|
|
||||||
realmRoles: string[];
|
|
||||||
clientRoles: { [index: string]: string[] };
|
|
||||||
subGroups: GroupRepresentation[];
|
|
||||||
access: { [index: string]: boolean };
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UserRepresentation {
|
|
||||||
self: string;
|
|
||||||
id: string;
|
|
||||||
origin: string;
|
|
||||||
createdTimestamp: number;
|
|
||||||
username: string;
|
|
||||||
enabled: boolean;
|
|
||||||
totp: boolean;
|
|
||||||
emailVerified: boolean;
|
|
||||||
firstName: string;
|
|
||||||
lastName: string;
|
|
||||||
email: string;
|
|
||||||
federationLink: string;
|
|
||||||
serviceAccountClientId: string;
|
|
||||||
attributes: { [index: string]: string[] };
|
|
||||||
credentials: CredentialRepresentation[];
|
|
||||||
disableableCredentialTypes: string[];
|
|
||||||
requiredActions: string[];
|
|
||||||
federatedIdentities: FederatedIdentityRepresentation[];
|
|
||||||
realmRoles: string[];
|
|
||||||
clientRoles: { [index: string]: string[] };
|
|
||||||
clientConsents: UserConsentRepresentation[];
|
|
||||||
notBefore: number;
|
|
||||||
applicationRoles: { [index: string]: string[] };
|
|
||||||
socialLinks: SocialLinkRepresentation[];
|
|
||||||
groups: string[];
|
|
||||||
access: { [index: string]: boolean };
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ScopeMappingRepresentation {
|
|
||||||
self: string;
|
|
||||||
client: string;
|
|
||||||
clientTemplate: string;
|
|
||||||
clientScope: string;
|
|
||||||
roles: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ClientRepresentation {
|
|
||||||
id: string;
|
|
||||||
clientId: string;
|
|
||||||
name: string;
|
|
||||||
description: string;
|
|
||||||
rootUrl: string;
|
|
||||||
adminUrl: string;
|
|
||||||
baseUrl: string;
|
|
||||||
surrogateAuthRequired: boolean;
|
|
||||||
enabled: boolean;
|
|
||||||
alwaysDisplayInConsole: boolean;
|
|
||||||
clientAuthenticatorType: string;
|
|
||||||
secret: string;
|
|
||||||
registrationAccessToken: string;
|
|
||||||
defaultRoles: string[];
|
|
||||||
redirectUris: string[];
|
|
||||||
webOrigins: string[];
|
|
||||||
notBefore: number;
|
|
||||||
bearerOnly: boolean;
|
|
||||||
consentRequired: boolean;
|
|
||||||
standardFlowEnabled: boolean;
|
|
||||||
implicitFlowEnabled: boolean;
|
|
||||||
directAccessGrantsEnabled: boolean;
|
|
||||||
serviceAccountsEnabled: boolean;
|
|
||||||
authorizationServicesEnabled: boolean;
|
|
||||||
directGrantsOnly: boolean;
|
|
||||||
publicClient: boolean;
|
|
||||||
frontchannelLogout: boolean;
|
|
||||||
protocol: string;
|
|
||||||
attributes: { [index: string]: string };
|
|
||||||
authenticationFlowBindingOverrides: { [index: string]: string };
|
|
||||||
fullScopeAllowed: boolean;
|
|
||||||
nodeReRegistrationTimeout: number;
|
|
||||||
registeredNodes: { [index: string]: number };
|
|
||||||
protocolMappers: ProtocolMapperRepresentation[];
|
|
||||||
clientTemplate: string;
|
|
||||||
useTemplateConfig: boolean;
|
|
||||||
useTemplateScope: boolean;
|
|
||||||
useTemplateMappers: boolean;
|
|
||||||
defaultClientScopes: string[];
|
|
||||||
optionalClientScopes: string[];
|
|
||||||
authorizationSettings: ResourceServerRepresentation;
|
|
||||||
access: { [index: string]: boolean };
|
|
||||||
origin: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UserFederationProviderRepresentation {
|
|
||||||
id: string;
|
|
||||||
displayName: string;
|
|
||||||
providerName: string;
|
|
||||||
config: { [index: string]: string };
|
|
||||||
priority: number;
|
|
||||||
fullSyncPeriod: number;
|
|
||||||
changedSyncPeriod: number;
|
|
||||||
lastSync: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UserFederationMapperRepresentation {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
federationProviderDisplayName: string;
|
|
||||||
federationMapperType: string;
|
|
||||||
config: { [index: string]: string };
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IdentityProviderRepresentation {
|
|
||||||
alias: string;
|
|
||||||
displayName: string;
|
|
||||||
internalId: string;
|
|
||||||
providerId: string;
|
|
||||||
enabled: boolean;
|
|
||||||
updateProfileFirstLoginMode: string;
|
|
||||||
trustEmail: boolean;
|
|
||||||
storeToken: boolean;
|
|
||||||
addReadTokenRoleOnCreate: boolean;
|
|
||||||
authenticateByDefault: boolean;
|
|
||||||
linkOnly: boolean;
|
|
||||||
firstBrokerLoginFlowAlias: string;
|
|
||||||
postBrokerLoginFlowAlias: string;
|
|
||||||
config: { [index: string]: string };
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IdentityProviderMapperRepresentation {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
identityProviderAlias: string;
|
|
||||||
identityProviderMapper: string;
|
|
||||||
config: { [index: string]: string };
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ProtocolMapperRepresentation {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
protocol: string;
|
|
||||||
protocolMapper: string;
|
|
||||||
consentRequired: boolean;
|
|
||||||
consentText: string;
|
|
||||||
config: { [index: string]: string };
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ComponentExportRepresentation {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
providerId: string;
|
|
||||||
subType: string;
|
|
||||||
subComponents: { [index: string]: ComponentExportRepresentation };
|
|
||||||
config: { [index: string]: string };
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AuthenticationFlowRepresentation extends Serializable {
|
|
||||||
id: string;
|
|
||||||
alias: string;
|
|
||||||
description: string;
|
|
||||||
providerId: string;
|
|
||||||
topLevel: boolean;
|
|
||||||
builtIn: boolean;
|
|
||||||
authenticationExecutions: AuthenticationExecutionExportRepresentation[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AuthenticatorConfigRepresentation extends Serializable {
|
|
||||||
id: string;
|
|
||||||
alias: string;
|
|
||||||
config: { [index: string]: string };
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RequiredActionProviderRepresentation {
|
|
||||||
alias: string;
|
|
||||||
name: string;
|
|
||||||
providerId: string;
|
|
||||||
enabled: boolean;
|
|
||||||
defaultAction: boolean;
|
|
||||||
priority: number;
|
|
||||||
config: { [index: string]: string };
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ApplicationRepresentation extends ClientRepresentation {
|
|
||||||
claims: ClaimRepresentation;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface OAuthClientRepresentation extends ApplicationRepresentation {}
|
|
||||||
|
|
||||||
export interface ClientTemplateRepresentation {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
description: string;
|
|
||||||
protocol: string;
|
|
||||||
fullScopeAllowed: boolean;
|
|
||||||
bearerOnly: boolean;
|
|
||||||
consentRequired: boolean;
|
|
||||||
standardFlowEnabled: boolean;
|
|
||||||
implicitFlowEnabled: boolean;
|
|
||||||
directAccessGrantsEnabled: boolean;
|
|
||||||
serviceAccountsEnabled: boolean;
|
|
||||||
publicClient: boolean;
|
|
||||||
frontchannelLogout: boolean;
|
|
||||||
attributes: { [index: string]: string };
|
|
||||||
protocolMappers: ProtocolMapperRepresentation[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RoleRepresentation {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
description: string;
|
|
||||||
scopeParamRequired: boolean;
|
|
||||||
composite: boolean;
|
|
||||||
composites: Composites;
|
|
||||||
clientRole: boolean;
|
|
||||||
containerId: string;
|
|
||||||
attributes: { [index: string]: string[] };
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CredentialRepresentation {
|
|
||||||
id: string;
|
|
||||||
type: string;
|
|
||||||
userLabel: string;
|
|
||||||
createdDate: number;
|
|
||||||
secretData: string;
|
|
||||||
credentialData: string;
|
|
||||||
priority: number;
|
|
||||||
value: string;
|
|
||||||
temporary: boolean;
|
|
||||||
device: string;
|
|
||||||
hashedSaltedValue: string;
|
|
||||||
salt: string;
|
|
||||||
hashIterations: number;
|
|
||||||
counter: number;
|
|
||||||
algorithm: string;
|
|
||||||
digits: number;
|
|
||||||
period: number;
|
|
||||||
config: { [index: string]: string };
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface FederatedIdentityRepresentation {
|
|
||||||
identityProvider: string;
|
|
||||||
userId: string;
|
|
||||||
userName: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UserConsentRepresentation {
|
|
||||||
clientId: string;
|
|
||||||
grantedClientScopes: string[];
|
|
||||||
createdDate: number;
|
|
||||||
lastUpdatedDate: number;
|
|
||||||
grantedRealmRoles: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SocialLinkRepresentation {
|
|
||||||
socialProvider: string;
|
|
||||||
socialUserId: string;
|
|
||||||
socialUsername: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ResourceServerRepresentation {
|
|
||||||
id: string;
|
|
||||||
clientId: string;
|
|
||||||
name: string;
|
|
||||||
allowRemoteResourceManagement: boolean;
|
|
||||||
policyEnforcementMode: PolicyEnforcementMode;
|
|
||||||
resources: ResourceRepresentation[];
|
|
||||||
policies: PolicyRepresentation[];
|
|
||||||
scopes: ScopeRepresentation[];
|
|
||||||
decisionStrategy: DecisionStrategy;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AuthenticationExecutionExportRepresentation
|
|
||||||
extends AbstractAuthenticationExecutionRepresentation {
|
|
||||||
flowAlias: string;
|
|
||||||
userSetupAllowed: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Serializable {}
|
|
||||||
|
|
||||||
export interface ClaimRepresentation {
|
|
||||||
name: boolean;
|
|
||||||
username: boolean;
|
|
||||||
profile: boolean;
|
|
||||||
picture: boolean;
|
|
||||||
website: boolean;
|
|
||||||
email: boolean;
|
|
||||||
gender: boolean;
|
|
||||||
locale: boolean;
|
|
||||||
address: boolean;
|
|
||||||
phone: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Composites {
|
|
||||||
realm: string[];
|
|
||||||
client: { [index: string]: string[] };
|
|
||||||
application: { [index: string]: string[] };
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ResourceRepresentation {
|
|
||||||
name: string;
|
|
||||||
type: string;
|
|
||||||
owner: ResourceOwnerRepresentation;
|
|
||||||
ownerManagedAccess: boolean;
|
|
||||||
displayName: string;
|
|
||||||
attributes: { [index: string]: string[] };
|
|
||||||
_id: string;
|
|
||||||
uris: string[];
|
|
||||||
scopes: ScopeRepresentation[];
|
|
||||||
icon_uri: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PolicyRepresentation extends AbstractPolicyRepresentation {
|
|
||||||
config: { [index: string]: string };
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ScopeRepresentation {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
iconUri: string;
|
|
||||||
policies: PolicyRepresentation[];
|
|
||||||
resources: ResourceRepresentation[];
|
|
||||||
displayName: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AbstractAuthenticationExecutionRepresentation
|
|
||||||
extends Serializable {
|
|
||||||
authenticatorConfig: string;
|
|
||||||
authenticator: string;
|
|
||||||
requirement: string;
|
|
||||||
priority: number;
|
|
||||||
autheticatorFlow: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ResourceOwnerRepresentation {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AbstractPolicyRepresentation {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
description: string;
|
|
||||||
type: string;
|
|
||||||
policies: string[];
|
|
||||||
resources: string[];
|
|
||||||
scopes: string[];
|
|
||||||
logic: Logic;
|
|
||||||
decisionStrategy: DecisionStrategy;
|
|
||||||
owner: string;
|
|
||||||
resourcesData: ResourceRepresentation[];
|
|
||||||
scopesData: ScopeRepresentation[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export type PolicyEnforcementMode = "ENFORCING" | "PERMISSIVE" | "DISABLED";
|
|
||||||
|
|
||||||
export type DecisionStrategy = "AFFIRMATIVE" | "UNANIMOUS" | "CONSENSUS";
|
|
||||||
|
|
||||||
export type Logic = "POSITIVE" | "NEGATIVE";
|
|
Loading…
Reference in a new issue