+
+
+ {contents.map((content) => (
+
+ {content.icon && (
+
+
+
+ )}
+ {content.text}
+
+ ))}
+
+
+ {docText}
+
+
+
+
+ );
+};
+
+export default AddIDPConfigurationHelpBox;
diff --git a/portal-ui/src/screens/Console/IDP/AddIDPLDAPConfiguration.tsx b/portal-ui/src/screens/Console/IDP/AddIDPLDAPConfiguration.tsx
new file mode 100644
index 000000000..ecbab547a
--- /dev/null
+++ b/portal-ui/src/screens/Console/IDP/AddIDPLDAPConfiguration.tsx
@@ -0,0 +1,86 @@
+// 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 from "react";
+
+import { Theme } from "@mui/material/styles";
+import createStyles from "@mui/styles/createStyles";
+import withStyles from "@mui/styles/withStyles";
+import LoginIcon from "@mui/icons-material/Login";
+import {
+ formFieldStyles,
+ modalBasic,
+} from "../Common/FormComponents/common/styleLibrary";
+import { IAM_PAGES } from "../../../common/SecureComponent/permissions";
+import AddIDPConfiguration from "./AddIDPConfiguration";
+import { ldapFormFields } from "./utils";
+import AddIDPConfigurationHelpBox from "./AddIDPConfigurationHelpbox";
+
+type AddIDPLDAPConfigurationProps = {
+ classes?: any;
+};
+
+const styles = (theme: Theme) =>
+ createStyles({
+ ...formFieldStyles,
+ formFieldRow: {
+ ...formFieldStyles.formFieldRow,
+ },
+ ...modalBasic,
+ });
+
+const AddIDPLDAPConfiguration = ({ classes }: AddIDPLDAPConfigurationProps) => {
+ const helpBoxContents = [
+ {
+ text: "MinIO supports using an Active Directory or LDAP (AD/LDAP) service for external management of user identities. Configuring an external IDentity Provider (IDP) enables Single-Sign On (SSO) workflows, where applications authenticate against the external IDP before accessing MinIO.",
+ icon: ,
+ iconDescription: "Create Configurations",
+ },
+ {
+ text: "MinIO queries the configured Active Directory / LDAP server to verify the credentials specified by the application and optionally return a list of groups in which the user has membership. MinIO supports two modes (Lookup-Bind Mode and Username-Bind Mode) for performing these queries",
+ icon: null,
+ iconDescription: "",
+ },
+ {
+ text: "MinIO recommends using Lookup-Bind mode as the preferred method for verifying AD/LDAP credentials. Username-Bind mode is a legacy method retained for backwards compatibility only.",
+ icon: null,
+ iconDescription: "",
+ },
+ ];
+
+ return (
+ }
+ helpBox={
+
+ }
+ header={"LDAP Configurations"}
+ backLink={IAM_PAGES.IDP_LDAP_CONFIGURATIONS}
+ title={"Create LDAP Configuration"}
+ endpoint={"/api/v1/idp/ldap/"}
+ formFields={ldapFormFields}
+ />
+ );
+};
+
+export default withStyles(styles)(AddIDPLDAPConfiguration);
diff --git a/portal-ui/src/screens/Console/IDP/AddIDPOpenIDConfiguration.tsx b/portal-ui/src/screens/Console/IDP/AddIDPOpenIDConfiguration.tsx
new file mode 100644
index 000000000..52353f7ff
--- /dev/null
+++ b/portal-ui/src/screens/Console/IDP/AddIDPOpenIDConfiguration.tsx
@@ -0,0 +1,71 @@
+// 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 from "react";
+
+import { Theme } from "@mui/material/styles";
+import createStyles from "@mui/styles/createStyles";
+import withStyles from "@mui/styles/withStyles";
+import { IAM_PAGES } from "../../../common/SecureComponent/permissions";
+import { LockIcon } from "../../../icons";
+import AddIDPConfiguration from "./AddIDPConfiguration";
+import { openIDFormFields } from "./utils";
+import AddIDPConfigurationHelpBox from "./AddIDPConfigurationHelpbox";
+
+type AddIDPOpenIDConfigurationProps = {
+ classes?: any;
+};
+
+const styles = (theme: Theme) => createStyles({});
+
+const AddIDPOpenIDConfiguration = ({
+ classes,
+}: AddIDPOpenIDConfigurationProps) => {
+ const helpBoxContents = [
+ {
+ text: "MinIO supports using an OpenID Connect (OIDC) compatible IDentity Provider (IDP) such as Okta, KeyCloak, Dex, Google, or Facebook for external management of user identities.",
+ icon: ,
+ iconDescription: "Create Configurations",
+ },
+ {
+ text: "Configuring an external IDP enables Single-Sign On workflows, where applications authenticate against the external IDP before accessing MinIO.",
+ icon: null,
+ iconDescription: "",
+ },
+ ];
+ return (
+ }
+ helpBox={
+
+ }
+ header={"OpenID Configurations"}
+ backLink={IAM_PAGES.IDP_OPENID_CONFIGURATIONS}
+ title={"Create OpenID Configuration"}
+ endpoint={"/api/v1/idp/openid/"}
+ formFields={openIDFormFields}
+ />
+ );
+};
+
+export default withStyles(styles)(AddIDPOpenIDConfiguration);
diff --git a/portal-ui/src/screens/Console/IDP/DeleteIDPConfigurationModal.tsx b/portal-ui/src/screens/Console/IDP/DeleteIDPConfigurationModal.tsx
new file mode 100644
index 000000000..2cc2bcb5d
--- /dev/null
+++ b/portal-ui/src/screens/Console/IDP/DeleteIDPConfigurationModal.tsx
@@ -0,0 +1,86 @@
+// 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 from "react";
+
+import { DialogContentText } from "@mui/material";
+import { ErrorResponseHandler } from "../../../common/types";
+import useApi from "../Common/Hooks/useApi";
+import ConfirmDialog from "../Common/ModalWrapper/ConfirmDialog";
+import { ConfirmDeleteIcon } from "../../../icons";
+import {
+ setErrorSnackMessage,
+ setServerNeedsRestart,
+} from "../../../systemSlice";
+import { useAppDispatch } from "../../../store";
+
+interface IDeleteIDPConfigurationModalProps {
+ closeDeleteModalAndRefresh: (refresh: boolean) => void;
+ deleteOpen: boolean;
+ idp: string;
+ idpType: string;
+}
+
+const DeleteIDPConfigurationModal = ({
+ closeDeleteModalAndRefresh,
+ deleteOpen,
+ idp,
+ idpType,
+}: IDeleteIDPConfigurationModalProps) => {
+ const dispatch = useAppDispatch();
+ const onDelSuccess = (res: any) => {
+ closeDeleteModalAndRefresh(true);
+ dispatch(setServerNeedsRestart(res.restart === true));
+ };
+ const onDelError = (err: ErrorResponseHandler) =>
+ dispatch(setErrorSnackMessage(err));
+ const onClose = () => closeDeleteModalAndRefresh(false);
+
+ const [deleteLoading, invokeDeleteApi] = useApi(onDelSuccess, onDelError);
+
+ if (!idp) {
+ return null;
+ }
+
+ const onConfirmDelete = () => {
+ invokeDeleteApi("DELETE", `/api/v1/idp/${idpType}/${idp}`);
+ };
+
+ const displayName = idp === "_" ? "Default" : idp;
+
+ return (
+ }
+ isLoading={deleteLoading}
+ onConfirm={onConfirmDelete}
+ onClose={onClose}
+ confirmButtonProps={{
+ disabled: deleteLoading,
+ }}
+ confirmationContent={
+
+ Are you sure you want to delete IDP {displayName}{" "}
+ configuration?
+
+ }
+ />
+ );
+};
+
+export default DeleteIDPConfigurationModal;
diff --git a/portal-ui/src/screens/Console/IDP/IDPConfigurationDetails.tsx b/portal-ui/src/screens/Console/IDP/IDPConfigurationDetails.tsx
new file mode 100644
index 000000000..d833daddc
--- /dev/null
+++ b/portal-ui/src/screens/Console/IDP/IDPConfigurationDetails.tsx
@@ -0,0 +1,364 @@
+// 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, { Fragment, useEffect, 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 {
+ buttonsStyles,
+ containerForHeader,
+ formFieldStyles,
+ hrClass,
+ modalBasic,
+ pageContentStyles,
+ searchField,
+} from "../Common/FormComponents/common/styleLibrary";
+import { RefreshIcon, TrashIcon } from "../../../icons";
+import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
+import { Button } from "mds";
+import { useNavigate, useParams } 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 api from "../../../common/api";
+import PageLayout from "../Common/Layout/PageLayout";
+import PageHeader from "../Common/PageHeader/PageHeader";
+import BackLink from "../../../common/BackLink";
+import ScreenTitle from "../Common/ScreenTitle/ScreenTitle";
+import DeleteIDPConfigurationModal from "./DeleteIDPConfigurationModal";
+import FormSwitchWrapper from "../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
+
+type IDPConfigurationDetailsProps = {
+ classes?: any;
+ formFields: object;
+ endpoint: string;
+ backLink: string;
+ header: string;
+ idpType: string;
+ icon: React.ReactNode;
+};
+
+const styles = (theme: Theme) =>
+ createStyles({
+ ...formFieldStyles,
+ formFieldRow: {
+ ...formFieldStyles.formFieldRow,
+ },
+ ...modalBasic,
+ pageContainer: {
+ height: "100%",
+ },
+ screenTitle: {
+ border: 0,
+ paddingTop: 0,
+ },
+ ...pageContentStyles,
+ ...searchField,
+ capitalize: {
+ textTransform: "capitalize",
+ },
+ ...hrClass,
+ ...buttonsStyles,
+ ...containerForHeader(theme.spacing(4)),
+ });
+
+const IDPConfigurationDetails = ({
+ classes,
+ formFields,
+ endpoint,
+ backLink,
+ header,
+ idpType,
+ icon,
+}: IDPConfigurationDetailsProps) => {
+ const dispatch = useAppDispatch();
+ const navigate = useNavigate();
+ const params = useParams();
+
+ const configurationName = params.idpName;
+
+ const [loading, setLoading] = useState(true);
+ const [isEnabled, setIsEnabled] = useState(false);
+ const [fields, setFields] = useState({});
+ const [originalFields, setOriginalFields] = useState({});
+ const [record, setRecord] = useState({});
+ const [editMode, setEditMode] = useState(false);
+ const [deleteOpen, setDeleteOpen] = useState(false);
+
+ const onSuccess = (res: any) => {
+ dispatch(setServerNeedsRestart(res.restart === true));
+ };
+
+ const onError = (err: ErrorResponseHandler) =>
+ dispatch(setErrorSnackMessage(err));
+
+ const [loadingSave, invokeApi] = useApi(onSuccess, onError);
+
+ const onEnabledSuccess = (res: any) => {
+ setIsEnabled(!isEnabled);
+ dispatch(setServerNeedsRestart(res.restart === true));
+ };
+
+ const onEnabledError = (err: ErrorResponseHandler) => {
+ dispatch(setErrorSnackMessage(err));
+ };
+
+ const [loadingEnabledSave, invokeEnabledApi] = useApi(
+ onEnabledSuccess,
+ onEnabledError
+ );
+
+ const toggleEditMode = () => {
+ if (editMode) {
+ parseFields(record);
+ }
+ setEditMode(!editMode);
+ };
+
+ const parseFields = (record: any) => {
+ let fields: any = {};
+ if (record.info) {
+ record.info.forEach((item: any) => {
+ if (item.key === "enable") {
+ setIsEnabled(item.value === "on");
+ }
+ fields[item.key] = item.value;
+ });
+ }
+ setFields(fields);
+ };
+
+ const parseOriginalFields = (record: any) => {
+ let fields: any = {};
+ if (record.info) {
+ record.info.forEach((item: any) => {
+ fields[item.key] = item.value;
+ });
+ }
+ setOriginalFields(fields);
+ };
+
+ useEffect(() => {
+ setLoading(true);
+ }, []);
+
+ useEffect(() => {
+ const loadRecord = () => {
+ api
+ .invoke("GET", `${endpoint}${configurationName}`)
+ .then((result: any) => {
+ if (result) {
+ setRecord(result);
+ parseFields(result);
+ parseOriginalFields(result);
+ }
+ setLoading(false);
+ })
+ .catch((err: ErrorResponseHandler) => {
+ dispatch(setErrorSnackMessage(err));
+ setLoading(false);
+ });
+ };
+ if (loading) {
+ loadRecord();
+ }
+ }, [dispatch, loading, configurationName, endpoint]);
+
+ const validSave = () => {
+ for (const [key, value] of Object.entries(formFields)) {
+ if (
+ value.required &&
+ !(
+ fields[key] !== undefined &&
+ fields[key] !== null &&
+ fields[key] !== ""
+ )
+ ) {
+ return false;
+ }
+ }
+ return true;
+ };
+
+ const resetForm = () => {
+ setFields({});
+ };
+
+ const saveRecord = (event: React.FormEvent) => {
+ event.preventDefault();
+ let input = "";
+ for (const key of Object.keys(formFields)) {
+ if (fields[key] || fields[key] !== originalFields[key]) {
+ input += `${key}=${fields[key]} `;
+ }
+ }
+ invokeApi("PUT", `${endpoint}${configurationName}`, { input });
+ setEditMode(false);
+ };
+
+ const closeDeleteModalAndRefresh = async (refresh: boolean) => {
+ setDeleteOpen(false);
+
+ if (refresh) {
+ navigate(backLink);
+ }
+ };
+
+ const toggleConfiguration = (value: boolean) => {
+ const input = `enable=${value ? "on" : "off"}`;
+ invokeEnabledApi("PUT", `${endpoint}${configurationName}`, { input });
+ };
+
+ return (
+
+ {deleteOpen && configurationName && (
+
+ )}
+ }
+ actions={
+ toggleConfiguration(e.target.checked)}
+ description=""
+ disabled={loadingEnabledSave}
+ />
+ }
+ />
+
+
+
+ {configurationName !== "_" && (
+
+
+
+
+ );
+};
+
+export default withStyles(styles)(IDPConfigurationDetails);
diff --git a/portal-ui/src/screens/Console/IDP/IDPConfigurations.tsx b/portal-ui/src/screens/Console/IDP/IDPConfigurations.tsx
new file mode 100644
index 000000000..72be285df
--- /dev/null
+++ b/portal-ui/src/screens/Console/IDP/IDPConfigurations.tsx
@@ -0,0 +1,225 @@
+// 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, { Fragment, useEffect, useState } from "react";
+
+import { Theme } from "@mui/material/styles";
+import createStyles from "@mui/styles/createStyles";
+import withStyles from "@mui/styles/withStyles";
+import { useAppDispatch } from "../../../store";
+import { useNavigate } from "react-router-dom";
+import {
+ CONSOLE_UI_RESOURCE,
+ IAM_SCOPES,
+} from "../../../common/SecureComponent/permissions";
+import {
+ hasPermission,
+ SecureComponent,
+} from "../../../common/SecureComponent";
+import api from "../../../common/api";
+import { ErrorResponseHandler } from "../../../common/types";
+import { setErrorSnackMessage } from "../../../systemSlice";
+import PageHeader from "../Common/PageHeader/PageHeader";
+import PageLayout from "../Common/Layout/PageLayout";
+import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
+import { Grid } from "@mui/material";
+import TooltipWrapper from "../Common/TooltipWrapper/TooltipWrapper";
+import { Button } from "mds";
+import { AddIcon, RefreshIcon } from "../../../icons";
+import TableWrapper from "../Common/TableWrapper/TableWrapper";
+import DeleteIDPConfigurationModal from "./DeleteIDPConfigurationModal";
+
+type IDPConfigurationsProps = {
+ classes?: any;
+ idpType: string;
+};
+
+const styles = (theme: Theme) =>
+ createStyles({
+ ...containerForHeader(theme.spacing(4)),
+ });
+
+const IDPConfigurations = ({ classes, idpType }: IDPConfigurationsProps) => {
+ const dispatch = useAppDispatch();
+ const navigate = useNavigate();
+
+ const [deleteOpen, setDeleteOpen] = useState(false);
+ const [selectedIDP, setSelectedIDP] = useState("");
+ const [loading, setLoading] = useState(false);
+ const [records, setRecords] = useState<[]>([]);
+
+ const deleteIDP = hasPermission(CONSOLE_UI_RESOURCE, [
+ IAM_SCOPES.ADMIN_CONFIG_UPDATE,
+ ]);
+
+ const viewIDP = hasPermission(CONSOLE_UI_RESOURCE, [
+ IAM_SCOPES.ADMIN_CONFIG_UPDATE,
+ ]);
+
+ const displayIDPs = hasPermission(CONSOLE_UI_RESOURCE, [
+ IAM_SCOPES.ADMIN_CONFIG_UPDATE,
+ ]);
+
+ useEffect(() => {
+ fetchRecords();
+ }, []);
+
+ useEffect(() => {
+ if (loading) {
+ if (displayIDPs) {
+ api
+ .invoke("GET", `/api/v1/idp/${idpType}`)
+ .then((res) => {
+ setLoading(false);
+ setRecords(
+ res.results.map((r: any) => {
+ r.name = r.name === "_" ? "Default" : r.name;
+ r.enabled = r.enabled === true ? "Enabled" : "Disabled";
+ return r;
+ })
+ );
+ })
+ .catch((err: ErrorResponseHandler) => {
+ setLoading(false);
+ dispatch(setErrorSnackMessage(err));
+ });
+ } else {
+ setLoading(false);
+ }
+ }
+ }, [loading, setLoading, setRecords, dispatch, displayIDPs, idpType]);
+
+ const fetchRecords = () => {
+ setLoading(true);
+ };
+
+ const confirmDeleteIDP = (idp: string) => {
+ setDeleteOpen(true);
+ idp = idp === "Default" ? "_" : idp;
+ setSelectedIDP(idp);
+ };
+
+ const viewAction = (idp: any) => {
+ let name = idp.name === "Default" ? "_" : idp.name;
+ navigate(`/idp/${idpType}/configurations/${name}`);
+ };
+
+ const closeDeleteModalAndRefresh = async (refresh: boolean) => {
+ setDeleteOpen(false);
+
+ if (refresh) {
+ fetchRecords();
+ }
+ };
+
+ const tableActions = [
+ {
+ type: "view",
+ onClick: viewAction,
+ disableButtonFunction: () => !viewIDP,
+ },
+ {
+ type: "delete",
+ onClick: confirmDeleteIDP,
+ sendOnlyId: true,
+ disableButtonFunction: (idp: string) => !deleteIDP || idp === "Default",
+ },
+ ];
+
+ return (
+
+ {deleteOpen && (
+
+ )}
+
+
+
+
+
+
+ }
+ onClick={() => setLoading(true)}
+ />
+
+
+
+
+ }
+ onClick={() =>
+ navigate(`/idp/${idpType}/configurations/add-idp`)
+ }
+ />
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default withStyles(styles)(IDPConfigurations);
diff --git a/portal-ui/src/screens/Console/IDP/IDPLDAPConfigurationDetails.tsx b/portal-ui/src/screens/Console/IDP/IDPLDAPConfigurationDetails.tsx
new file mode 100644
index 000000000..bc514afd0
--- /dev/null
+++ b/portal-ui/src/screens/Console/IDP/IDPLDAPConfigurationDetails.tsx
@@ -0,0 +1,48 @@
+// 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 from "react";
+
+import { Theme } from "@mui/material/styles";
+import createStyles from "@mui/styles/createStyles";
+import withStyles from "@mui/styles/withStyles";
+import { ldapFormFields } from "./utils";
+import LoginIcon from "@mui/icons-material/Login";
+import IDPConfigurationDetails from "./IDPConfigurationDetails";
+import { IAM_PAGES } from "../../../common/SecureComponent/permissions";
+
+type IDPLDAPConfigurationDetailsProps = {
+ classes?: any;
+};
+
+const styles = (theme: Theme) => createStyles({});
+
+const IDPLDAPConfigurationDetails = ({
+ classes,
+}: IDPLDAPConfigurationDetailsProps) => {
+ return (
+ }
+ />
+ );
+};
+
+export default withStyles(styles)(IDPLDAPConfigurationDetails);
diff --git a/portal-ui/src/screens/Console/IDP/IDPLDAPConfigurations.tsx b/portal-ui/src/screens/Console/IDP/IDPLDAPConfigurations.tsx
new file mode 100644
index 000000000..a0185368e
--- /dev/null
+++ b/portal-ui/src/screens/Console/IDP/IDPLDAPConfigurations.tsx
@@ -0,0 +1,34 @@
+// 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 from "react";
+
+import { Theme } from "@mui/material/styles";
+import createStyles from "@mui/styles/createStyles";
+import withStyles from "@mui/styles/withStyles";
+import IDPConfigurations from "./IDPConfigurations";
+
+type IDPLDAPConfigurationsProps = {
+ classes?: any;
+};
+
+const styles = (theme: Theme) => createStyles({});
+
+const IDPLDAPConfigurations = ({ classes }: IDPLDAPConfigurationsProps) => {
+ return ;
+};
+
+export default withStyles(styles)(IDPLDAPConfigurations);
diff --git a/portal-ui/src/screens/Console/IDP/IDPOpenIDConfigurationDetails.tsx b/portal-ui/src/screens/Console/IDP/IDPOpenIDConfigurationDetails.tsx
new file mode 100644
index 000000000..f61d09fa8
--- /dev/null
+++ b/portal-ui/src/screens/Console/IDP/IDPOpenIDConfigurationDetails.tsx
@@ -0,0 +1,48 @@
+// 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 from "react";
+
+import { Theme } from "@mui/material/styles";
+import createStyles from "@mui/styles/createStyles";
+import withStyles from "@mui/styles/withStyles";
+import { IAM_PAGES } from "../../../common/SecureComponent/permissions";
+import { LockIcon } from "../../../icons";
+import { openIDFormFields } from "./utils";
+import IDPConfigurationDetails from "./IDPConfigurationDetails";
+
+type IDPOpenIDConfigurationDetailsProps = {
+ classes?: any;
+};
+
+const styles = (theme: Theme) => createStyles({});
+
+const IDPOpenIDConfigurationDetails = ({
+ classes,
+}: IDPOpenIDConfigurationDetailsProps) => {
+ return (
+ }
+ />
+ );
+};
+
+export default withStyles(styles)(IDPOpenIDConfigurationDetails);
diff --git a/portal-ui/src/screens/Console/IDP/IDPOpenIDConfigurations.tsx b/portal-ui/src/screens/Console/IDP/IDPOpenIDConfigurations.tsx
new file mode 100644
index 000000000..e29e22724
--- /dev/null
+++ b/portal-ui/src/screens/Console/IDP/IDPOpenIDConfigurations.tsx
@@ -0,0 +1,34 @@
+// 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 from "react";
+
+import { Theme } from "@mui/material/styles";
+import createStyles from "@mui/styles/createStyles";
+import withStyles from "@mui/styles/withStyles";
+import IDPConfigurations from "./IDPConfigurations";
+
+type IDPOpenIDConfigurationsProps = {
+ classes?: any;
+};
+
+const styles = (theme: Theme) => createStyles({});
+
+const IDPOpenIDConfigurations = ({ classes }: IDPOpenIDConfigurationsProps) => {
+ return ;
+};
+
+export default withStyles(styles)(IDPOpenIDConfigurations);
diff --git a/portal-ui/src/screens/Console/IDP/utils.tsx b/portal-ui/src/screens/Console/IDP/utils.tsx
new file mode 100644
index 000000000..4e43f75b1
--- /dev/null
+++ b/portal-ui/src/screens/Console/IDP/utils.tsx
@@ -0,0 +1,176 @@
+// 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 .
+
+export const openIDFormFields = {
+ config_url: {
+ required: true,
+ hasError: (s: string, editMode: boolean) => {
+ return !s && editMode ? "Config URL is required" : "";
+ },
+ label: "Config URL",
+ tooltip: "Config URL for identity provider configuration",
+ placeholder:
+ "https://identity-provider-url/.well-known/openid-configuration",
+ type: "text",
+ },
+ client_id: {
+ required: true,
+ hasError: (s: string, editMode: boolean) => {
+ return !s && editMode ? "Client ID is required" : "";
+ },
+ label: "Client ID",
+ tooltip: "Identity provider Client ID",
+ placeholder: "Enter Client ID",
+ type: "text",
+ },
+ client_secret: {
+ required: true,
+ hasError: (s: string, editMode: boolean) => {
+ return !s && editMode ? "Client Secret is required" : "";
+ },
+ label: "Client Secret",
+ tooltip: "Identity provider Client Secret",
+ placeholder: "Enter Client Secret",
+ type: "password",
+ },
+ display_name: {
+ required: false,
+ label: "Display Name",
+ tooltip: "Display Name",
+ placeholder: "Enter Display Name",
+ type: "text",
+ hasError: (s: string, editMode: boolean) => "",
+ },
+ claim_name: {
+ required: false,
+ label: "Claim Name",
+ tooltip: "Claim from which MinIO will read the policy or role to use",
+ placeholder: "Enter Claim Name",
+ type: "text",
+ hasError: (s: string, editMode: boolean) => "",
+ },
+ claim_prefix: {
+ required: false,
+ label: "Claim Prefix",
+ tooltip: "Claim Prefix",
+ placeholder: "Enter Claim Prefix",
+ type: "text",
+ hasError: (s: string, editMode: boolean) => "",
+ },
+ scopes: {
+ required: false,
+ label: "Scopes",
+ tooltip: "Scopes",
+ placeholder: "openid,profile,email",
+ type: "text",
+ hasError: (s: string, editMode: boolean) => "",
+ },
+ redirect_uri: {
+ required: false,
+ label: "Redirect URI",
+ tooltip: "Redirect URI",
+ placeholder: "https://console-endpoint-url/oauth_callback",
+ type: "text",
+ hasError: (s: string, editMode: boolean) => "",
+ },
+ role_policy: {
+ required: false,
+ label: "Role Policy",
+ tooltip: "Role Policy",
+ placeholder: "readonly",
+ type: "text",
+ hasError: (s: string, editMode: boolean) => "",
+ },
+};
+
+export const ldapFormFields = {
+ server_addr: {
+ required: true,
+ hasError: (s: string, editMode: boolean) => {
+ return !s && editMode ? "Server Address is required" : "";
+ },
+ label: "Server Address",
+ tooltip: 'AD/LDAP server address e.g. "myldapserver.com:636"',
+ placeholder: "myldapserver.com:636",
+ type: "text",
+ },
+ lookup_bind_dn: {
+ required: true,
+ hasError: (s: string, editMode: boolean) => {
+ return !s && editMode ? "Lookup Bind DN is required" : "";
+ },
+ label: "Lookup Bind DN",
+ tooltip:
+ "DN for LDAP read-only service account used to perform DN and group lookups",
+ placeholder: "cn=admin,dc=min,dc=io",
+ type: "text",
+ },
+ lookup_bind_password: {
+ required: true,
+ hasError: (s: string, editMode: boolean) => {
+ return !s && editMode ? "Lookup Bind Password is required" : "";
+ },
+ label: "Lookup Bind Password",
+ tooltip:
+ "Password for LDAP read-only service account used to perform DN and group lookups",
+ placeholder: "admin",
+ type: "password",
+ },
+ user_dn_search_base_dn: {
+ required: true,
+ hasError: (s: string, editMode: boolean) => {
+ return !s && editMode ? "User DN Search Base DN is required" : "";
+ },
+ label: "User DN Search Base",
+ tooltip: "Base LDAP DN to search for user DN",
+ placeholder: "DC=example,DC=net",
+ type: "text",
+ },
+ user_dn_search_filter: {
+ required: true,
+ hasError: (s: string, editMode: boolean) => {
+ return !s && editMode ? "User DN Search Filter is required" : "";
+ },
+ label: "User DN Search Filter",
+ tooltip: "Search filter to lookup user DN",
+ placeholder: "(sAMAcountName=%s)",
+ type: "text",
+ },
+ display_name: {
+ required: false,
+ label: "Display Name",
+ tooltip: "Display Name",
+ placeholder: "Enter Display Name",
+ type: "text",
+ hasError: (s: string, editMode: boolean) => "",
+ },
+ group_search_base_dn: {
+ required: false,
+ hasError: (s: string, editMode: boolean) => "",
+ label: "Group Search Base DN",
+ tooltip: "Group Search Base DN",
+ placeholder: "ou=swengg,dc=min,dc=io",
+ type: "text",
+ },
+ group_search_filter: {
+ required: false,
+ hasError: (s: string, editMode: boolean) => "",
+ label: "Group Search Filter",
+ tooltip: "Group Search Filter",
+ placeholder: "(&(objectclass=groupofnames)(member=%d))",
+ type: "text",
+ },
+};
diff --git a/portal-ui/src/screens/Console/valid-routes.ts b/portal-ui/src/screens/Console/valid-routes.ts
index cea744206..f58bf3720 100644
--- a/portal-ui/src/screens/Console/valid-routes.ts
+++ b/portal-ui/src/screens/Console/valid-routes.ts
@@ -59,6 +59,7 @@ import {
import SettingsIcon from "../../icons/SettingsIcon";
import React from "react";
import LicenseBadge from "./Menu/LicenseBadge";
+import { LockOpen, Login } from "@mui/icons-material";
export const validRoutes = (
features: string[] | null | undefined,
@@ -141,6 +142,20 @@ export const validRoutes = (
to: IAM_PAGES.POLICIES,
icon: AccessMenuIcon,
},
+ {
+ name: "OpenID",
+ component: NavLink,
+ id: "openID",
+ to: IAM_PAGES.IDP_OPENID_CONFIGURATIONS,
+ icon: LockOpen,
+ },
+ {
+ name: "LDAP",
+ component: NavLink,
+ id: "ldap",
+ to: IAM_PAGES.IDP_LDAP_CONFIGURATIONS,
+ icon: Login,
+ },
],
},