Initial version client scopes (#111)
* initial version client scopes * remove use of newer replaceAll * review
This commit is contained in:
parent
8210ba5ba0
commit
e7b108a623
11 changed files with 845 additions and 26 deletions
13
src/App.tsx
13
src/App.tsx
|
@ -1,12 +1,14 @@
|
|||
import React from "react";
|
||||
import { Page, PageSection } from "@patternfly/react-core";
|
||||
import { Page } from "@patternfly/react-core";
|
||||
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
|
||||
|
||||
import { Header } from "./PageHeader";
|
||||
import { PageNav } from "./PageNav";
|
||||
|
||||
import { Help } from "./components/help-enabler/HelpHeader";
|
||||
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
|
||||
import { NewRealmForm } from "./realm/add/NewRealmForm";
|
||||
import { NewClientForm } from "./clients/add/NewClientForm";
|
||||
import { NewClientScopeForm } from "./client-scopes/add/NewClientScopeForm";
|
||||
|
||||
import { ImportForm } from "./clients/import/ImportForm";
|
||||
import { ClientsSection } from "./clients/ClientsSection";
|
||||
import { ClientScopesSection } from "./client-scopes/ClientScopesSection";
|
||||
|
@ -47,6 +49,11 @@ export const App = () => {
|
|||
path="/client-scopes"
|
||||
component={ClientScopesSection}
|
||||
></Route>
|
||||
<Route
|
||||
exact
|
||||
path="/add-client-scopes"
|
||||
component={NewClientScopeForm}
|
||||
></Route>
|
||||
<Route
|
||||
exact
|
||||
path="/realm-roles"
|
||||
|
|
58
src/client-scopes/ClientScopesList.tsx
Normal file
58
src/client-scopes/ClientScopesList.tsx
Normal file
|
@ -0,0 +1,58 @@
|
|||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableHeader,
|
||||
TableVariant,
|
||||
} from "@patternfly/react-table";
|
||||
|
||||
import { ClientScopeRepresentation } from "./models/client-scope";
|
||||
|
||||
type ClientScopeListProps = {
|
||||
clientScopes: ClientScopeRepresentation[];
|
||||
};
|
||||
|
||||
export const ClientScopeList = ({ clientScopes }: ClientScopeListProps) => {
|
||||
const { t } = useTranslation("client-scopes");
|
||||
|
||||
const columns: (keyof ClientScopeRepresentation)[] = [
|
||||
"name",
|
||||
"description",
|
||||
"protocol",
|
||||
];
|
||||
|
||||
const data = clientScopes.map((c) => {
|
||||
return { cells: columns.map((col) => c[col]) };
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Table
|
||||
variant={TableVariant.compact}
|
||||
cells={[
|
||||
{ title: t("name") },
|
||||
{ title: t("description") },
|
||||
{
|
||||
title: t("protocol"),
|
||||
},
|
||||
]}
|
||||
rows={data}
|
||||
actions={[
|
||||
{
|
||||
title: t("common:export"),
|
||||
onClick: () => {},
|
||||
},
|
||||
{
|
||||
title: t("common:delete"),
|
||||
onClick: () => {},
|
||||
},
|
||||
]}
|
||||
aria-label={t("clientScopeList")}
|
||||
>
|
||||
<TableHeader />
|
||||
<TableBody />
|
||||
</Table>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -1,10 +1,53 @@
|
|||
import { PageSection } from "@patternfly/react-core";
|
||||
import React from "react";
|
||||
import React, { useContext, useState } from "react";
|
||||
import { Button, PageSection } from "@patternfly/react-core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useHistory } from "react-router-dom";
|
||||
|
||||
import { RealmContext } from "../components/realm-context/RealmContext";
|
||||
import { HttpClientContext } from "../http-service/HttpClientContext";
|
||||
import { ClientRepresentation } from "../realm/models/Realm";
|
||||
import { DataLoader } from "../components/data-loader/DataLoader";
|
||||
import { TableToolbar } from "../components/table-toolbar/TableToolbar";
|
||||
import { ClientScopeList } from "./ClientScopesList";
|
||||
|
||||
export const ClientScopesSection = () => {
|
||||
const { t } = useTranslation("client-scopes");
|
||||
const history = useHistory();
|
||||
|
||||
const [max, setMax] = useState(10);
|
||||
const [first, setFirst] = useState(0);
|
||||
const httpClient = useContext(HttpClientContext)!;
|
||||
const { realm } = useContext(RealmContext);
|
||||
|
||||
const loader = async () => {
|
||||
return await httpClient
|
||||
.doGet(`/admin/realms/${realm}/client-scopes`, { params: { first, max } })
|
||||
.then((r) => r.data as ClientRepresentation[]);
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<PageSection variant="light">The Client Scopes Page</PageSection>
|
||||
</>
|
||||
<PageSection variant="light">
|
||||
<DataLoader loader={loader}>
|
||||
{(scopes) => (
|
||||
<TableToolbar
|
||||
count={scopes!.length}
|
||||
first={first}
|
||||
max={max}
|
||||
onNextClick={setFirst}
|
||||
onPreviousClick={setFirst}
|
||||
onPerPageSelect={(first, max) => {
|
||||
setFirst(first);
|
||||
setMax(max);
|
||||
}}
|
||||
toolbarItem={
|
||||
<Button onClick={() => history.push("/add-client-scopes")}>
|
||||
{t("createClientScope")}
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<ClientScopeList clientScopes={scopes} />
|
||||
</TableToolbar>
|
||||
)}
|
||||
</DataLoader>
|
||||
</PageSection>
|
||||
);
|
||||
};
|
||||
|
|
485
src/client-scopes/__tests__/mock-client-scopes.json
Normal file
485
src/client-scopes/__tests__/mock-client-scopes.json
Normal file
|
@ -0,0 +1,485 @@
|
|||
[
|
||||
{
|
||||
"id": "3507ed12-d8b0-455c-b91a-62a6765ecf0f",
|
||||
"name": "address",
|
||||
"description": "OpenID Connect built-in scope: address",
|
||||
"protocol": "openid-connect",
|
||||
"attributes": {
|
||||
"include.in.token.scope": "true",
|
||||
"display.on.consent.screen": "true",
|
||||
"consent.screen.text": "${addressScopeConsentText}"
|
||||
},
|
||||
"protocolMappers": [
|
||||
{
|
||||
"id": "b0582082-abab-4c63-b3b7-a92afe6b3436",
|
||||
"name": "address",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-address-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"user.attribute.formatted": "formatted",
|
||||
"user.attribute.country": "country",
|
||||
"user.attribute.postal_code": "postal_code",
|
||||
"userinfo.token.claim": "true",
|
||||
"user.attribute.street": "street",
|
||||
"id.token.claim": "true",
|
||||
"user.attribute.region": "region",
|
||||
"access.token.claim": "true",
|
||||
"user.attribute.locality": "locality"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "eb8c7985-5459-45a9-ace5-2959ce0fd1c9",
|
||||
"name": "email",
|
||||
"description": "OpenID Connect built-in scope: email",
|
||||
"protocol": "openid-connect",
|
||||
"attributes": {
|
||||
"include.in.token.scope": "true",
|
||||
"display.on.consent.screen": "true",
|
||||
"consent.screen.text": "${emailScopeConsentText}"
|
||||
},
|
||||
"protocolMappers": [
|
||||
{
|
||||
"id": "348dfe5c-26e6-43e8-bc80-b7db9f842f24",
|
||||
"name": "email",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-property-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"userinfo.token.claim": "true",
|
||||
"user.attribute": "email",
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"claim.name": "email",
|
||||
"jsonType.label": "String"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "bfe77908-4ca3-40ea-b5be-75bea87f5bb1",
|
||||
"name": "email verified",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-property-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"userinfo.token.claim": "true",
|
||||
"user.attribute": "emailVerified",
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"claim.name": "email_verified",
|
||||
"jsonType.label": "boolean"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "e604d76d-20ec-4d80-acee-1885af201568",
|
||||
"name": "microprofile-jwt",
|
||||
"description": "Microprofile - JWT built-in scope",
|
||||
"protocol": "openid-connect",
|
||||
"attributes": {
|
||||
"include.in.token.scope": "true",
|
||||
"display.on.consent.screen": "false"
|
||||
},
|
||||
"protocolMappers": [
|
||||
{
|
||||
"id": "63a71cf3-df7c-4a81-a23f-d3ba62801c72",
|
||||
"name": "upn",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-property-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"userinfo.token.claim": "true",
|
||||
"user.attribute": "username",
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"claim.name": "upn",
|
||||
"jsonType.label": "String"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "5eb3444b-8e96-4267-9afc-20abd56613aa",
|
||||
"name": "groups",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-realm-role-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"multivalued": "true",
|
||||
"user.attribute": "foo",
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"claim.name": "groups",
|
||||
"jsonType.label": "String"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "2cdac876-a8ce-4cde-8bcb-00e28804ec91",
|
||||
"name": "offline_access",
|
||||
"description": "OpenID Connect built-in scope: offline_access",
|
||||
"protocol": "openid-connect",
|
||||
"attributes": {
|
||||
"consent.screen.text": "${offlineAccessScopeConsentText}",
|
||||
"display.on.consent.screen": "true"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "3db88729-214e-4c71-8fac-ee744279538b",
|
||||
"name": "phone",
|
||||
"description": "OpenID Connect built-in scope: phone",
|
||||
"protocol": "openid-connect",
|
||||
"attributes": {
|
||||
"include.in.token.scope": "true",
|
||||
"display.on.consent.screen": "true",
|
||||
"consent.screen.text": "${phoneScopeConsentText}"
|
||||
},
|
||||
"protocolMappers": [
|
||||
{
|
||||
"id": "00ca4abc-fc26-4273-9d77-d7a793f38976",
|
||||
"name": "phone number verified",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-attribute-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"userinfo.token.claim": "true",
|
||||
"user.attribute": "phoneNumberVerified",
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"claim.name": "phone_number_verified",
|
||||
"jsonType.label": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "98885779-b84e-4565-bc1b-a0c703f03be0",
|
||||
"name": "phone number",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-attribute-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"userinfo.token.claim": "true",
|
||||
"user.attribute": "phoneNumber",
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"claim.name": "phone_number",
|
||||
"jsonType.label": "String"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "82aca51c-22b4-4156-93a9-3ed33ec2adcc",
|
||||
"name": "profile",
|
||||
"description": "OpenID Connect built-in scope: profile",
|
||||
"protocol": "openid-connect",
|
||||
"attributes": {
|
||||
"include.in.token.scope": "true",
|
||||
"display.on.consent.screen": "true",
|
||||
"consent.screen.text": "${profileScopeConsentText}"
|
||||
},
|
||||
"protocolMappers": [
|
||||
{
|
||||
"id": "d974a079-8416-4dea-9e49-76dab694e836",
|
||||
"name": "full name",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-full-name-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"userinfo.token.claim": "true"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "2b0e5ec3-cc38-44c4-8851-98c0e3e3f60d",
|
||||
"name": "birthdate",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-attribute-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"userinfo.token.claim": "true",
|
||||
"user.attribute": "birthdate",
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"claim.name": "birthdate",
|
||||
"jsonType.label": "String"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "feef3c77-5a8e-4f22-94c8-fc606eb8dad0",
|
||||
"name": "website",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-attribute-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"userinfo.token.claim": "true",
|
||||
"user.attribute": "website",
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"claim.name": "website",
|
||||
"jsonType.label": "String"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "e0340530-efde-4bdf-8399-c98b994e3c4f",
|
||||
"name": "nickname",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-attribute-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"userinfo.token.claim": "true",
|
||||
"user.attribute": "nickname",
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"claim.name": "nickname",
|
||||
"jsonType.label": "String"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "bef63a97-20a4-4595-9e31-881273af8b47",
|
||||
"name": "locale",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-attribute-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"userinfo.token.claim": "true",
|
||||
"user.attribute": "locale",
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"claim.name": "locale",
|
||||
"jsonType.label": "String"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "3a4e571b-9ee4-4553-8a54-dcf0ab757b39",
|
||||
"name": "picture",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-attribute-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"userinfo.token.claim": "true",
|
||||
"user.attribute": "picture",
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"claim.name": "picture",
|
||||
"jsonType.label": "String"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "d0c55da1-f814-4bfe-a311-b34ddd7ee2fb",
|
||||
"name": "updated at",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-attribute-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"userinfo.token.claim": "true",
|
||||
"user.attribute": "updatedAt",
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"claim.name": "updated_at",
|
||||
"jsonType.label": "String"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "392fa527-96e9-41a5-8fa4-6deb1f3916a5",
|
||||
"name": "middle name",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-attribute-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"userinfo.token.claim": "true",
|
||||
"user.attribute": "middleName",
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"claim.name": "middle_name",
|
||||
"jsonType.label": "String"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "042e6e1e-f041-432f-88bc-79421366fb99",
|
||||
"name": "gender",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-attribute-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"userinfo.token.claim": "true",
|
||||
"user.attribute": "gender",
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"claim.name": "gender",
|
||||
"jsonType.label": "String"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "e269f729-2eca-4ff0-9caf-3baa4f6188c5",
|
||||
"name": "zoneinfo",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-attribute-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"userinfo.token.claim": "true",
|
||||
"user.attribute": "zoneinfo",
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"claim.name": "zoneinfo",
|
||||
"jsonType.label": "String"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "b3929aa6-6acf-4b13-9d23-ee459926feef",
|
||||
"name": "given name",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-property-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"userinfo.token.claim": "true",
|
||||
"user.attribute": "firstName",
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"claim.name": "given_name",
|
||||
"jsonType.label": "String"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "877d4b97-2520-40f7-9e58-cd99560a4637",
|
||||
"name": "profile",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-attribute-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"userinfo.token.claim": "true",
|
||||
"user.attribute": "profile",
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"claim.name": "profile",
|
||||
"jsonType.label": "String"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "f6aab00d-4b15-4ef3-a037-50d8a6c047ff",
|
||||
"name": "family name",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-property-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"userinfo.token.claim": "true",
|
||||
"user.attribute": "lastName",
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"claim.name": "family_name",
|
||||
"jsonType.label": "String"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "52483504-1da0-4645-8df0-d7ec36bf835a",
|
||||
"name": "username",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-property-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"userinfo.token.claim": "true",
|
||||
"user.attribute": "username",
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"claim.name": "preferred_username",
|
||||
"jsonType.label": "String"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "dc401683-7876-4a01-a670-73deae0a10c2",
|
||||
"name": "role_list",
|
||||
"description": "SAML role list",
|
||||
"protocol": "saml",
|
||||
"attributes": {
|
||||
"consent.screen.text": "${samlRoleListScopeConsentText}",
|
||||
"display.on.consent.screen": "true"
|
||||
},
|
||||
"protocolMappers": [
|
||||
{
|
||||
"id": "4903f029-ca74-4447-b9ac-cf7799f2391c",
|
||||
"name": "role list",
|
||||
"protocol": "saml",
|
||||
"protocolMapper": "saml-role-list-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"single": "false",
|
||||
"attribute.nameformat": "Basic",
|
||||
"attribute.name": "Role"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "715eec20-9d2b-45cf-b2c3-fd11aae96b63",
|
||||
"name": "roles",
|
||||
"description": "OpenID Connect scope for add user roles to the access token",
|
||||
"protocol": "openid-connect",
|
||||
"attributes": {
|
||||
"include.in.token.scope": "false",
|
||||
"display.on.consent.screen": "true",
|
||||
"consent.screen.text": "${rolesScopeConsentText}"
|
||||
},
|
||||
"protocolMappers": [
|
||||
{
|
||||
"id": "d1464021-822d-41d8-8195-d8962fe70f61",
|
||||
"name": "audience resolve",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-audience-resolve-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"id": "b85d197d-f195-4dcd-a873-77ee4ec9fcea",
|
||||
"name": "realm roles",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-realm-role-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"user.attribute": "foo",
|
||||
"access.token.claim": "true",
|
||||
"claim.name": "realm_access.roles",
|
||||
"jsonType.label": "String",
|
||||
"multivalued": "true"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "ef5b5c95-5236-41f1-ab9b-3e4213abbe76",
|
||||
"name": "client roles",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-client-role-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"user.attribute": "foo",
|
||||
"access.token.claim": "true",
|
||||
"claim.name": "resource_access.${client_id}.roles",
|
||||
"jsonType.label": "String",
|
||||
"multivalued": "true"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "30b4d89f-bfd9-45d4-b71f-01dd0f64da57",
|
||||
"name": "web-origins",
|
||||
"description": "OpenID Connect scope for add allowed web origins to the access token",
|
||||
"protocol": "openid-connect",
|
||||
"attributes": {
|
||||
"include.in.token.scope": "false",
|
||||
"display.on.consent.screen": "false",
|
||||
"consent.screen.text": ""
|
||||
},
|
||||
"protocolMappers": [
|
||||
{
|
||||
"id": "76f254c5-dc78-4048-abc9-c9de9d55f5a4",
|
||||
"name": "allowed web origins",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-allowed-origins-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
181
src/client-scopes/add/NewClientScopeForm.tsx
Normal file
181
src/client-scopes/add/NewClientScopeForm.tsx
Normal file
|
@ -0,0 +1,181 @@
|
|||
import React, { useContext, useState } from "react";
|
||||
import {
|
||||
ActionGroup,
|
||||
AlertVariant,
|
||||
Button,
|
||||
Form,
|
||||
FormGroup,
|
||||
PageSection,
|
||||
Select,
|
||||
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 { HttpClientContext } from "../../http-service/HttpClientContext";
|
||||
import { RealmContext } from "../../components/realm-context/RealmContext";
|
||||
import { useAlerts } from "../../components/alert/Alerts";
|
||||
|
||||
export const NewClientScopeForm = () => {
|
||||
const { t } = useTranslation("client-scopes");
|
||||
const { register, control, handleSubmit } = useForm<
|
||||
ClientScopeRepresentation
|
||||
>();
|
||||
|
||||
const httpClient = useContext(HttpClientContext)!;
|
||||
const { realm } = useContext(RealmContext);
|
||||
|
||||
const [open, isOpen] = useState(false);
|
||||
const [add, Alerts] = 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 httpClient.doPost(
|
||||
`/admin/realms/${realm}/client-scopes`,
|
||||
clientScopes
|
||||
);
|
||||
add(t("createClientScopeSuccess"), AlertVariant.success);
|
||||
} catch (error) {
|
||||
add(`${t("createClientScopeError")} '${error}'`, AlertVariant.danger);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<PageSection variant="light">
|
||||
<Alerts />
|
||||
<Form isHorizontal onSubmit={handleSubmit(save)}>
|
||||
<FormGroup
|
||||
label={
|
||||
<>
|
||||
{t("name")} <HelpItem item="clientScope.name" />
|
||||
</>
|
||||
}
|
||||
fieldId="kc-name"
|
||||
isRequired
|
||||
>
|
||||
<TextInput
|
||||
ref={register({ required: true })}
|
||||
type="text"
|
||||
id="kc-name"
|
||||
name="name"
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
label={
|
||||
<>
|
||||
{t("description")} <HelpItem item="clientScope.description" />
|
||||
</>
|
||||
}
|
||||
fieldId="kc-description"
|
||||
>
|
||||
<TextInput
|
||||
ref={register}
|
||||
type="text"
|
||||
id="kc-description"
|
||||
name="description"
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
label={
|
||||
<>
|
||||
{t("protocol")} <HelpItem item="clientScope.protocol" />
|
||||
</>
|
||||
}
|
||||
fieldId="kc-protocol"
|
||||
>
|
||||
<Controller
|
||||
name="protocol"
|
||||
defaultValue=""
|
||||
control={control}
|
||||
render={({ onChange, value }) => (
|
||||
<Select
|
||||
id="kc-protocol"
|
||||
required
|
||||
onToggle={() => isOpen(!open)}
|
||||
onSelect={(_, value, isPlaceholder) => {
|
||||
onChange(isPlaceholder ? "" : (value as string));
|
||||
isOpen(false);
|
||||
}}
|
||||
selections={value}
|
||||
variant={SelectVariant.single}
|
||||
aria-label="Select Encryption type"
|
||||
isOpen={open}
|
||||
></Select>
|
||||
)}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
label={
|
||||
<>
|
||||
{t("displayOnConsentScreen")}{" "}
|
||||
<HelpItem item="clientScope.displayOnConsentScreen" />
|
||||
</>
|
||||
}
|
||||
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")}{" "}
|
||||
<HelpItem item="clientScope.consentScreenText" />
|
||||
</>
|
||||
}
|
||||
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")} <HelpItem item="clientScope.guiOrder" />
|
||||
</>
|
||||
}
|
||||
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">{t("common:cancel")}</Button>
|
||||
</ActionGroup>
|
||||
</Form>
|
||||
</PageSection>
|
||||
);
|
||||
};
|
14
src/client-scopes/messages.json
Normal file
14
src/client-scopes/messages.json
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"client-scopes": {
|
||||
"createClientScope": "Create client scope",
|
||||
"clientScopeList": "List of client scopes",
|
||||
"name": "Name",
|
||||
"description": "Description",
|
||||
"protocol": "Protocol",
|
||||
"createClientScopeSuccess": "Client scope created",
|
||||
"createClientScopeError": "Could not create client scope:",
|
||||
"displayOnConsentScreen": "Display on consent screen",
|
||||
"consentScreenText": "Consent screen text",
|
||||
"guiOrder": "GUI Order"
|
||||
}
|
||||
}
|
24
src/client-scopes/models/client-scope.ts
Normal file
24
src/client-scopes/models/client-scope.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* 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,7 +1,8 @@
|
|||
import React from "react";
|
||||
import React, { useContext } from "react";
|
||||
import { Tooltip } from "@patternfly/react-core";
|
||||
import { HelpIcon } from "@patternfly/react-icons";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { HelpContext } from "./HelpHeader";
|
||||
|
||||
type HelpItemProps = {
|
||||
item: string;
|
||||
|
@ -9,11 +10,16 @@ type HelpItemProps = {
|
|||
|
||||
export const HelpItem = ({ item }: HelpItemProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { enabled } = useContext(HelpContext);
|
||||
return (
|
||||
<Tooltip position="right" content={t(`help:${item}`)}>
|
||||
<span id={item} data-testid={item}>
|
||||
<HelpIcon />
|
||||
</span>
|
||||
</Tooltip>
|
||||
<>
|
||||
{enabled && (
|
||||
<Tooltip position="right" content={t(`help:${item}`)}>
|
||||
<span id={item} data-testid={item}>
|
||||
<HelpIcon />
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
{
|
||||
"help": {
|
||||
"storybook": "Sometimes you need some help and it's nice when the app does that"
|
||||
"storybook": "Sometimes you need some help and it's nice when the app does that",
|
||||
"clientScope": {
|
||||
"name": "Name of the client scope. Must be unique in the realm. Name should not contain space characters as it is used as value of scope parameter",
|
||||
"description": "Description of the client scope",
|
||||
"protocol": "Which SSO protocol configuration is being supplied by this client scope",
|
||||
"displayOnConsentScreen": "If on, and this client scope is added to some client with consent required, the text specified by 'Consent Screen Text' will be displayed on consent screen. If off, this client scope will not be displayed on the consent screen",
|
||||
"consentScreenText": "Text that will be shown on the consent screen when this client scope is added to some client with consent required. Defaults to name of client scope if it is not filled",
|
||||
"guiOrder": "Specify order of the provider in GUI (such as in Consent page) as integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,15 +4,15 @@ import { initReactI18next } from "react-i18next";
|
|||
|
||||
import common from "./common-messages.json";
|
||||
import clients from "./clients/messages.json";
|
||||
import clientScopes from "./client-scopes/messages.json";
|
||||
import realm from "./realm/messages.json";
|
||||
import roles from "./realm-roles/messages.json";
|
||||
import help from "./help.json";
|
||||
|
||||
const initOptions = {
|
||||
ns: ["common", "help", "clients", "realm", "roles"],
|
||||
defaultNS: "common",
|
||||
resources: {
|
||||
en: { ...common, ...help, ...clients, ...realm, ...roles },
|
||||
en: { ...common, ...help, ...clients, ...clientScopes, ...realm, ...roles },
|
||||
},
|
||||
lng: "en",
|
||||
fallbackLng: "en",
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { ClientScopeRepresentation } from "../../client-scopes/models/client-scope";
|
||||
|
||||
export interface RealmRepresentation {
|
||||
id: string;
|
||||
realm: string;
|
||||
|
@ -231,15 +233,6 @@ export interface ClientRepresentation {
|
|||
origin: string;
|
||||
}
|
||||
|
||||
export interface ClientScopeRepresentation {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
protocol: string;
|
||||
attributes: { [index: string]: string };
|
||||
protocolMappers: ProtocolMapperRepresentation[];
|
||||
}
|
||||
|
||||
export interface UserFederationProviderRepresentation {
|
||||
id: string;
|
||||
displayName: string;
|
||||
|
|
Loading…
Reference in a new issue