From 2368199e03f51c05108ae9929ea2c3b718c6d560 Mon Sep 17 00:00:00 2001 From: Javier Adriel Date: Fri, 9 Dec 2022 16:13:10 -0600 Subject: [PATCH] IDP management UI (#2487) Adds UI to interact with IDP Configurations (CRUD) --- .../src/common/SecureComponent/permissions.ts | 33 ++ portal-ui/src/screens/Console/Console.tsx | 42 ++ .../Console/IDP/AddIDPConfiguration.tsx | 219 +++++++++++ .../IDP/AddIDPConfigurationHelpbox.tsx | 106 +++++ .../Console/IDP/AddIDPLDAPConfiguration.tsx | 86 +++++ .../Console/IDP/AddIDPOpenIDConfiguration.tsx | 71 ++++ .../IDP/DeleteIDPConfigurationModal.tsx | 86 +++++ .../Console/IDP/IDPConfigurationDetails.tsx | 364 ++++++++++++++++++ .../screens/Console/IDP/IDPConfigurations.tsx | 225 +++++++++++ .../IDP/IDPLDAPConfigurationDetails.tsx | 48 +++ .../Console/IDP/IDPLDAPConfigurations.tsx | 34 ++ .../IDP/IDPOpenIDConfigurationDetails.tsx | 48 +++ .../Console/IDP/IDPOpenIDConfigurations.tsx | 34 ++ portal-ui/src/screens/Console/IDP/utils.tsx | 176 +++++++++ portal-ui/src/screens/Console/valid-routes.ts | 15 + 15 files changed, 1587 insertions(+) create mode 100644 portal-ui/src/screens/Console/IDP/AddIDPConfiguration.tsx create mode 100644 portal-ui/src/screens/Console/IDP/AddIDPConfigurationHelpbox.tsx create mode 100644 portal-ui/src/screens/Console/IDP/AddIDPLDAPConfiguration.tsx create mode 100644 portal-ui/src/screens/Console/IDP/AddIDPOpenIDConfiguration.tsx create mode 100644 portal-ui/src/screens/Console/IDP/DeleteIDPConfigurationModal.tsx create mode 100644 portal-ui/src/screens/Console/IDP/IDPConfigurationDetails.tsx create mode 100644 portal-ui/src/screens/Console/IDP/IDPConfigurations.tsx create mode 100644 portal-ui/src/screens/Console/IDP/IDPLDAPConfigurationDetails.tsx create mode 100644 portal-ui/src/screens/Console/IDP/IDPLDAPConfigurations.tsx create mode 100644 portal-ui/src/screens/Console/IDP/IDPOpenIDConfigurationDetails.tsx create mode 100644 portal-ui/src/screens/Console/IDP/IDPOpenIDConfigurations.tsx create mode 100644 portal-ui/src/screens/Console/IDP/utils.tsx diff --git a/portal-ui/src/common/SecureComponent/permissions.ts b/portal-ui/src/common/SecureComponent/permissions.ts index 85f1a6b92..f1f8fcfbb 100644 --- a/portal-ui/src/common/SecureComponent/permissions.ts +++ b/portal-ui/src/common/SecureComponent/permissions.ts @@ -130,6 +130,15 @@ export const IAM_PAGES = { ACCOUNT_ADD: "/access-keys/new-account", USER_SA_ACCOUNT_ADD: "/identity/users/new-user-sa/:userName", + /* IDP */ + IDP_LDAP_CONFIGURATIONS: "/idp/ldap/configurations", + IDP_LDAP_CONFIGURATIONS_VIEW: "/idp/ldap/configurations/:idpName", + IDP_LDAP_CONFIGURATIONS_ADD: "/idp/ldap/configurations/add-idp", + + IDP_OPENID_CONFIGURATIONS: "/idp/openid/configurations", + IDP_OPENID_CONFIGURATIONS_VIEW: "/idp/openid/configurations/:idpName", + IDP_OPENID_CONFIGURATIONS_ADD: "/idp/openid/configurations/add-idp", + POLICIES: "/identity/policies", POLICY_ADD: "/identity/add-policy", POLICIES_VIEW: "/identity/policies/*", @@ -430,6 +439,30 @@ export const IAM_PAGES_PERMISSIONS = { IAM_SCOPES.ADMIN_SERVER_INFO, IAM_SCOPES.ADMIN_CONFIG_UPDATE, ], + [IAM_PAGES.IDP_LDAP_CONFIGURATIONS]: [ + IAM_SCOPES.ADMIN_ALL_ACTIONS, + IAM_SCOPES.ADMIN_CONFIG_UPDATE, + ], + [IAM_PAGES.IDP_LDAP_CONFIGURATIONS_ADD]: [ + IAM_SCOPES.ADMIN_ALL_ACTIONS, + IAM_SCOPES.ADMIN_CONFIG_UPDATE, + ], + [IAM_PAGES.IDP_LDAP_CONFIGURATIONS_VIEW]: [ + IAM_SCOPES.ADMIN_ALL_ACTIONS, + IAM_SCOPES.ADMIN_CONFIG_UPDATE, + ], + [IAM_PAGES.IDP_OPENID_CONFIGURATIONS]: [ + IAM_SCOPES.ADMIN_ALL_ACTIONS, + IAM_SCOPES.ADMIN_CONFIG_UPDATE, + ], + [IAM_PAGES.IDP_OPENID_CONFIGURATIONS_ADD]: [ + IAM_SCOPES.ADMIN_ALL_ACTIONS, + IAM_SCOPES.ADMIN_CONFIG_UPDATE, + ], + [IAM_PAGES.IDP_OPENID_CONFIGURATIONS_VIEW]: [ + IAM_SCOPES.ADMIN_ALL_ACTIONS, + IAM_SCOPES.ADMIN_CONFIG_UPDATE, + ], }; export const S3_ALL_RESOURCES = "arn:aws:s3:::*"; diff --git a/portal-ui/src/screens/Console/Console.tsx b/portal-ui/src/screens/Console/Console.tsx index 02b8c6625..a088d15d8 100644 --- a/portal-ui/src/screens/Console/Console.tsx +++ b/portal-ui/src/screens/Console/Console.tsx @@ -122,6 +122,24 @@ const AccountCreate = React.lazy( const Users = React.lazy(() => import("./Users/Users")); const Groups = React.lazy(() => import("./Groups/Groups")); +const IDPLDAPConfigurations = React.lazy( + () => import("./IDP/IDPLDAPConfigurations") +); +const IDPOpenIDConfigurations = React.lazy( + () => import("./IDP/IDPOpenIDConfigurations") +); +const AddIDPLDAPConfiguration = React.lazy( + () => import("./IDP/AddIDPLDAPConfiguration") +); +const AddIDPOpenIDConfiguration = React.lazy( + () => import("./IDP/AddIDPOpenIDConfiguration") +); +const IDPLDAPConfigurationDetails = React.lazy( + () => import("./IDP/IDPLDAPConfigurationDetails") +); +const IDPOpenIDConfigurationDetails = React.lazy( + () => import("./IDP/IDPOpenIDConfigurationDetails") +); const TenantDetails = React.lazy( () => import("./Tenants/TenantDetails/TenantDetails") @@ -343,6 +361,30 @@ const Console = ({ classes }: IConsoleProps) => { component: Policies, path: IAM_PAGES.POLICIES, }, + { + component: IDPLDAPConfigurations, + path: IAM_PAGES.IDP_LDAP_CONFIGURATIONS, + }, + { + component: IDPOpenIDConfigurations, + path: IAM_PAGES.IDP_OPENID_CONFIGURATIONS, + }, + { + component: AddIDPLDAPConfiguration, + path: IAM_PAGES.IDP_LDAP_CONFIGURATIONS_ADD, + }, + { + component: AddIDPOpenIDConfiguration, + path: IAM_PAGES.IDP_OPENID_CONFIGURATIONS_ADD, + }, + { + component: IDPLDAPConfigurationDetails, + path: IAM_PAGES.IDP_LDAP_CONFIGURATIONS_VIEW, + }, + { + component: IDPOpenIDConfigurationDetails, + path: IAM_PAGES.IDP_OPENID_CONFIGURATIONS_VIEW, + }, { component: Heal, path: IAM_PAGES.TOOLS_HEAL, diff --git a/portal-ui/src/screens/Console/IDP/AddIDPConfiguration.tsx b/portal-ui/src/screens/Console/IDP/AddIDPConfiguration.tsx new file mode 100644 index 000000000..e30b6bb43 --- /dev/null +++ b/portal-ui/src/screens/Console/IDP/AddIDPConfiguration.tsx @@ -0,0 +1,219 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2022 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import React, { useState } from "react"; + +import { Theme } from "@mui/material/styles"; +import createStyles from "@mui/styles/createStyles"; +import withStyles from "@mui/styles/withStyles"; +import { Box, Grid } from "@mui/material"; +import { + formFieldStyles, + modalBasic, +} from "../Common/FormComponents/common/styleLibrary"; +import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper"; +import { Button } from "mds"; +import { useNavigate } from "react-router-dom"; +import { ErrorResponseHandler } from "../../../common/types"; +import { useAppDispatch } from "../../../store"; +import { + setErrorSnackMessage, + setServerNeedsRestart, +} from "../../../systemSlice"; +import useApi from "../Common/Hooks/useApi"; +import PageHeader from "../Common/PageHeader/PageHeader"; +import BackLink from "../../../common/BackLink"; +import PageLayout from "../Common/Layout/PageLayout"; +import SectionTitle from "../Common/SectionTitle"; + +type AddIDPConfigurationProps = { + classes?: any; + icon: React.ReactNode; + helpBox: React.ReactNode; + header: string; + title: string; + backLink: string; + formFields: object; + endpoint: string; +}; + +const styles = (theme: Theme) => + createStyles({ + ...formFieldStyles, + ...modalBasic, + }); + +const AddIDPConfiguration = ({ + classes, + icon, + helpBox, + header, + backLink, + title, + formFields, + endpoint, +}: AddIDPConfigurationProps) => { + const extraFormFields = { + name: { + required: true, + hasError: (s: string, editMode: boolean) => { + return !s && editMode ? "Config Name is required" : ""; + }, + label: "Name", + tooltip: "Name for identity provider configuration", + placeholder: "Name", + type: "text", + }, + ...formFields, + }; + + const navigate = useNavigate(); + const dispatch = useAppDispatch(); + + const [fields, setFields] = useState({}); + + const onSuccess = (res: any) => { + navigate(backLink); + dispatch(setServerNeedsRestart(res.restart === true)); + }; + + const onError = (err: ErrorResponseHandler) => + dispatch(setErrorSnackMessage(err)); + + const [loading, invokeApi] = useApi(onSuccess, onError); + + const validSave = () => { + for (const [key, value] of Object.entries(extraFormFields)) { + if ( + value.required && + !( + fields[key] !== undefined && + fields[key] !== null && + fields[key] !== "" + ) + ) { + return false; + } + } + return true; + }; + + const resetForm = () => { + setFields({}); + }; + + const addRecord = (event: React.FormEvent) => { + event.preventDefault(); + const name = fields["name"]; + let input = ""; + for (const key of Object.keys(formFields)) { + if (fields[key]) { + input += `${key}=${fields[key]} `; + } + } + invokeApi("POST", endpoint, { name, input }); + }; + + return ( + + } /> + + + + {title} +
) => { + addRecord(e); + }} + > + + + {Object.entries(extraFormFields).map(([key, value]) => ( + + ) => + setFields({ ...fields, [key]: e.target.value }) + } + placeholder={value.placeholder} + type={value.type} + /> + + ))} + + +