From 5968e1a7ee64cf8ec5904588fccabd91ad1946be Mon Sep 17 00:00:00 2001
From: Jenny <32821331+jenny-s51@users.noreply.github.com>
Date: Wed, 9 Feb 2022 17:37:31 -0500
Subject: [PATCH] Clients(Authentication): Adds "Export" tab (#1937)
---
.../client_authorization_test.spec.ts | 17 +++
.../manage/clients/AuthorizationTab.ts | 18 +++
package-lock.json | 14 +--
package.json | 2 +-
src/clients/ClientDetails.tsx | 11 +-
.../authorization/AuthorizationExport.tsx | 106 ++++++++++++++++++
.../authorization/authorization-details.css | 3 +
src/clients/help.ts | 2 +
src/clients/messages.ts | 6 +
src/clients/routes/AuthenticationTab.ts | 3 +-
10 files changed, 172 insertions(+), 10 deletions(-)
create mode 100644 src/clients/authorization/AuthorizationExport.tsx
create mode 100644 src/clients/authorization/authorization-details.css
diff --git a/cypress/integration/client_authorization_test.spec.ts b/cypress/integration/client_authorization_test.spec.ts
index 7a05ed2965..a588af525f 100644
--- a/cypress/integration/client_authorization_test.spec.ts
+++ b/cypress/integration/client_authorization_test.spec.ts
@@ -146,5 +146,22 @@ describe("Client authentication subtab", () => {
.save();
masthead.checkNotificationMessage("Successfully created the permission");
+ authenticationTab.cancel();
+ });
+
+ it("Should copy auth details", () => {
+ authenticationTab.goToExportSubTab();
+ authenticationTab.copy();
+
+ masthead.checkNotificationMessage("Authorization details copied.");
+ });
+
+ it("Should export auth details", () => {
+ authenticationTab.goToExportSubTab();
+ authenticationTab.export();
+
+ masthead.checkNotificationMessage(
+ "Successfully exported authorization details."
+ );
});
});
diff --git a/cypress/support/pages/admin_console/manage/clients/AuthorizationTab.ts b/cypress/support/pages/admin_console/manage/clients/AuthorizationTab.ts
index 9f1eb84c01..a4a700084a 100644
--- a/cypress/support/pages/admin_console/manage/clients/AuthorizationTab.ts
+++ b/cypress/support/pages/admin_console/manage/clients/AuthorizationTab.ts
@@ -10,6 +10,9 @@ export default class AuthorizationTab {
private scopeTabName = "authorizationScopes";
private policyTabName = "authorizationPolicies";
private permissionsTabName = "authorizationPermissions";
+ private exportTabName = "authorizationExport";
+ private exportDownloadButton = "authorization-export-download";
+ private exportCopyButton = "authorization-export-copy";
private nameColumnPrefix = "name-column-";
private emptyPolicyCreateButton = "no-policies-empty-action";
private createPolicyButton = "createPolicy";
@@ -43,6 +46,11 @@ export default class AuthorizationTab {
return this;
}
+ goToExportSubTab() {
+ cy.findByTestId(this.exportTabName).click();
+ return this;
+ }
+
goToCreateResource() {
cy.findByTestId(this.createResourceButton).click();
return this;
@@ -124,6 +132,16 @@ export default class AuthorizationTab {
return this;
}
+ copy() {
+ cy.findByTestId(this.exportCopyButton).click();
+ return this;
+ }
+
+ export() {
+ cy.findByTestId(this.exportDownloadButton).click();
+ return this;
+ }
+
save() {
cy.findByTestId("save").click();
return this;
diff --git a/package-lock.json b/package-lock.json
index 8fa3af4492..e026d0a7bb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7,7 +7,7 @@
"name": "keycloak-admin-ui",
"license": "Apache",
"dependencies": {
- "@keycloak/keycloak-admin-client": "^17.0.0-dev.26",
+ "@keycloak/keycloak-admin-client": "^17.0.0-dev.29",
"@patternfly/patternfly": "^4.171.1",
"@patternfly/react-code-editor": "^4.33.14",
"@patternfly/react-core": "^4.192.14",
@@ -3414,9 +3414,9 @@
}
},
"node_modules/@keycloak/keycloak-admin-client": {
- "version": "17.0.0-dev.26",
- "resolved": "https://registry.npmjs.org/@keycloak/keycloak-admin-client/-/keycloak-admin-client-17.0.0-dev.26.tgz",
- "integrity": "sha512-D7wP/cu4shzbq2FbmFVfZp1ipFoVDdH4YyFHRlnzb0Y0zvS58eGlDqE0YaI23I8dn5qsyj8oFUYgi4ntayAGCQ==",
+ "version": "17.0.0-dev.29",
+ "resolved": "https://registry.npmjs.org/@keycloak/keycloak-admin-client/-/keycloak-admin-client-17.0.0-dev.29.tgz",
+ "integrity": "sha512-EfPbXG0e2rbfvdJFc6je3gULHYmcBHGbxVmtEpRJOZ7PogaAOtSp6n8EPLIKeHsd1tszaS4g/87B8Ee1FYBmlg==",
"dependencies": {
"axios": "^0.25.0",
"camelize-ts": "^1.0.8",
@@ -23966,9 +23966,9 @@
}
},
"@keycloak/keycloak-admin-client": {
- "version": "17.0.0-dev.26",
- "resolved": "https://registry.npmjs.org/@keycloak/keycloak-admin-client/-/keycloak-admin-client-17.0.0-dev.26.tgz",
- "integrity": "sha512-D7wP/cu4shzbq2FbmFVfZp1ipFoVDdH4YyFHRlnzb0Y0zvS58eGlDqE0YaI23I8dn5qsyj8oFUYgi4ntayAGCQ==",
+ "version": "17.0.0-dev.29",
+ "resolved": "https://registry.npmjs.org/@keycloak/keycloak-admin-client/-/keycloak-admin-client-17.0.0-dev.29.tgz",
+ "integrity": "sha512-EfPbXG0e2rbfvdJFc6je3gULHYmcBHGbxVmtEpRJOZ7PogaAOtSp6n8EPLIKeHsd1tszaS4g/87B8Ee1FYBmlg==",
"requires": {
"axios": "^0.25.0",
"camelize-ts": "^1.0.8",
diff --git a/package.json b/package.json
index bf8f15add7..4e800f3e35 100644
--- a/package.json
+++ b/package.json
@@ -23,7 +23,7 @@
"prepare": "husky install"
},
"dependencies": {
- "@keycloak/keycloak-admin-client": "^17.0.0-dev.26",
+ "@keycloak/keycloak-admin-client": "^17.0.0-dev.29",
"@patternfly/patternfly": "^4.171.1",
"@patternfly/react-code-editor": "^4.33.14",
"@patternfly/react-core": "^4.192.14",
diff --git a/src/clients/ClientDetails.tsx b/src/clients/ClientDetails.tsx
index cc3767c0c7..96766f40db 100644
--- a/src/clients/ClientDetails.tsx
+++ b/src/clients/ClientDetails.tsx
@@ -70,6 +70,7 @@ import {
toAuthorizationTab,
} from "./routes/AuthenticationTab";
import { toClientScopesTab } from "./routes/ClientScopeTab";
+import { AuthorizationExport } from "./authorization/AuthorizationExport";
type ClientDetailHeaderProps = {
onChange: (value: boolean) => void;
@@ -586,7 +587,7 @@ export default function ClientDetails() {
{t("evaluate")}}
{...authenticationRoute("evaluate")}
@@ -600,6 +601,14 @@ export default function ClientDetails() {
reset={() => setupForm(client)}
/>
+ {t("common:export")}}
+ {...authenticationRoute("export")}
+ >
+
+
)}
diff --git a/src/clients/authorization/AuthorizationExport.tsx b/src/clients/authorization/AuthorizationExport.tsx
new file mode 100644
index 0000000000..b1d7ad1c25
--- /dev/null
+++ b/src/clients/authorization/AuthorizationExport.tsx
@@ -0,0 +1,106 @@
+import React, { useState } from "react";
+import { useTranslation } from "react-i18next";
+import {
+ FormGroup,
+ PageSection,
+ ActionGroup,
+ Button,
+ TextArea,
+ AlertVariant,
+} from "@patternfly/react-core";
+
+import { FormAccess } from "../../components/form-access/FormAccess";
+import { HelpItem } from "../../components/help-enabler/HelpItem";
+import { useAdminClient, useFetch } from "../../context/auth/AdminClient";
+import { useParams } from "react-router-dom";
+import FileSaver from "file-saver";
+import { prettyPrintJSON } from "../../util";
+import "./authorization-details.css";
+import { useAlerts } from "../../components/alert/Alerts";
+import type { ClientParams } from "../routes/Client";
+import type ResourceServerRepresentation from "@keycloak/keycloak-admin-client/lib/defs/resourceServerRepresentation";
+
+export const AuthorizationExport = () => {
+ const { t } = useTranslation("clients");
+ const adminClient = useAdminClient();
+ const { clientId } = useParams();
+ const { addAlert, addError } = useAlerts();
+
+ const [code, setCode] = useState();
+ const [authorizationDetails, setAuthorizationDetails] =
+ useState();
+
+ useFetch(
+ () =>
+ adminClient.clients.exportResource({
+ id: clientId,
+ }),
+
+ (authDetails) => {
+ setCode(JSON.stringify(authDetails, null, 2));
+ setAuthorizationDetails(authDetails);
+ },
+ []
+ );
+
+ const exportAuthDetails = () => {
+ try {
+ FileSaver.saveAs(
+ new Blob([prettyPrintJSON(authorizationDetails)], {
+ type: "application/json",
+ }),
+ "test-authz-config.json"
+ );
+ addAlert(t("exportAuthDetailsSuccess"), AlertVariant.success);
+ } catch (error) {
+ addError("exportAuthDetailsError", error);
+ }
+ };
+
+ return (
+
+
+
+ }
+ fieldId="client"
+ >
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/src/clients/authorization/authorization-details.css b/src/clients/authorization/authorization-details.css
new file mode 100644
index 0000000000..0c75c72c0f
--- /dev/null
+++ b/src/clients/authorization/authorization-details.css
@@ -0,0 +1,3 @@
+textarea#authorizationDetails {
+ height: 280px;
+}
diff --git a/src/clients/help.ts b/src/clients/help.ts
index 634047dd3c..01595ee9b6 100644
--- a/src/clients/help.ts
+++ b/src/clients/help.ts
@@ -10,6 +10,8 @@ export default {
"This defines the type of the OIDC client. When it's ON, the OIDC type is set to confidential access type. When it's OFF, it is set to public access type",
authorization:
"Enable/Disable fine-grained authorization support for a client",
+ authDetails:
+ "Export and download all resource settings for this resource server.",
directAccess:
"This enables support for Direct Access Grants, which means that client has access to username/password of user and exchange it directly with Keycloak server for access token. In terms of OAuth2 specification, this enables support of 'Resource Owner Password Credentials Grant' for this client.",
standardFlow:
diff --git a/src/clients/messages.ts b/src/clients/messages.ts
index a807277b93..03fbe9f737 100644
--- a/src/clients/messages.ts
+++ b/src/clients/messages.ts
@@ -6,6 +6,11 @@ export default {
all: "All",
},
protocol: "Protocol",
+ copy: "Copy",
+ copied: "Authorization details copied.",
+ copyError: "Error copying authorization details: {{error}}",
+ exportAuthDetailsSuccess: "Successfully exported authorization details.",
+ exportAuthDetailsError: "Error exporting authorization details: {{error}}",
clientType: "Client type",
clientAuthorization: "Authorization",
implicitFlow: "Implicit flow",
@@ -42,6 +47,7 @@ export default {
lastEvaluation: "Last Evaluation",
resourcesAndAuthScopes: "Resources and Authentication Scopes",
authScopes: "Authorization scopes",
+ authDetails: "Authorization details",
anyResource: "Any resource",
anyScope: "Any scope",
selectScope: "Select a scope",
diff --git a/src/clients/routes/AuthenticationTab.ts b/src/clients/routes/AuthenticationTab.ts
index adf3371072..039d1f9734 100644
--- a/src/clients/routes/AuthenticationTab.ts
+++ b/src/clients/routes/AuthenticationTab.ts
@@ -9,7 +9,8 @@ export type AuthorizationTab =
| "scopes"
| "policies"
| "permissions"
- | "evaluate";
+ | "evaluate"
+ | "export";
export type AuthorizationParams = {
realm: string;