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" + > +