UI changes to IDP screen and remove old sections from settings screen (#2513)
This commit is contained in:
@@ -20,8 +20,6 @@ import CodeIcon from "@mui/icons-material/Code";
|
||||
import LocalHospitalIcon from "@mui/icons-material/LocalHospital";
|
||||
import FindReplaceIcon from "@mui/icons-material/FindReplace";
|
||||
import VpnKeyIcon from "@mui/icons-material/VpnKey";
|
||||
import LockOpenIcon from "@mui/icons-material/LockOpen";
|
||||
import LoginIcon from "@mui/icons-material/Login";
|
||||
import PendingActionsIcon from "@mui/icons-material/PendingActions";
|
||||
import CallToActionIcon from "@mui/icons-material/CallToAction";
|
||||
import { IElement, IElementValue } from "./types";
|
||||
@@ -57,16 +55,6 @@ export const configurationElements: IElement[] = [
|
||||
configuration_id: "etcd",
|
||||
configuration_label: "Etcd",
|
||||
},
|
||||
{
|
||||
icon: <LockOpenIcon />,
|
||||
configuration_id: "identity_openid",
|
||||
configuration_label: "Identity Openid",
|
||||
},
|
||||
{
|
||||
icon: <LoginIcon />,
|
||||
configuration_id: "identity_ldap",
|
||||
configuration_label: "Identity LDAP",
|
||||
},
|
||||
{
|
||||
icon: <CallToActionIcon />,
|
||||
configuration_id: "logger_webhook",
|
||||
@@ -261,158 +249,6 @@ export const fieldsConfigurations: any = {
|
||||
placeholder: "Enter custom notes if any",
|
||||
},
|
||||
],
|
||||
identity_openid: [
|
||||
{
|
||||
name: "config_url",
|
||||
required: false,
|
||||
label: "Config URL",
|
||||
tooltip: "Config URL for identity provider configuration",
|
||||
type: "string",
|
||||
placeholder:
|
||||
"https://identity-provider-url/.well-known/openid-configuration",
|
||||
},
|
||||
{
|
||||
name: "client_id",
|
||||
required: false,
|
||||
label: "Client ID",
|
||||
type: "string",
|
||||
placeholder: "Enter Client ID",
|
||||
},
|
||||
{
|
||||
name: "client_secret",
|
||||
required: false,
|
||||
label: "Secret ID",
|
||||
type: "string",
|
||||
placeholder: "Enter Secret ID",
|
||||
},
|
||||
{
|
||||
name: "claim_name",
|
||||
required: false,
|
||||
label: "Claim Name",
|
||||
tooltip: "Claim from which MinIO will read the policy or role to use",
|
||||
type: "string",
|
||||
placeholder: "Enter Claim Name",
|
||||
},
|
||||
{
|
||||
name: "claim_prefix",
|
||||
required: false,
|
||||
label: "Claim Prefix",
|
||||
tooltip: "Claim Prefix",
|
||||
type: "string",
|
||||
placeholder: "Enter Claim Prefix",
|
||||
},
|
||||
{
|
||||
name: "claim_userinfo",
|
||||
required: false,
|
||||
label: "Claim UserInfo",
|
||||
type: "on|off",
|
||||
},
|
||||
{
|
||||
name: "redirect_uri",
|
||||
required: false,
|
||||
label: "Redirect URI",
|
||||
type: "string",
|
||||
placeholder: "https://console-endpoint-url/oauth_callback",
|
||||
},
|
||||
{
|
||||
name: "scopes",
|
||||
required: false,
|
||||
label: "Scopes",
|
||||
type: "string",
|
||||
placeholder: "openid,profile,email",
|
||||
},
|
||||
],
|
||||
identity_ldap: [
|
||||
{
|
||||
name: "server_addr",
|
||||
required: true,
|
||||
label: "Server Addr",
|
||||
tooltip: 'AD/LDAP server address e.g. "myldapserver.com:636"',
|
||||
type: "string",
|
||||
placeholder: "myldapserver.com:636",
|
||||
},
|
||||
{
|
||||
name: "tls_skip_verify",
|
||||
required: false,
|
||||
label: "TLS Skip Verify",
|
||||
tooltip:
|
||||
'Trust server TLS without verification, defaults to "off" (verify)',
|
||||
type: "on|off",
|
||||
},
|
||||
{
|
||||
name: "server_insecure",
|
||||
required: false,
|
||||
label: "Server Insecure",
|
||||
tooltip:
|
||||
'Allow plain text connection to AD/LDAP server, defaults to "off"',
|
||||
type: "on|off",
|
||||
},
|
||||
{
|
||||
name: "server_starttls",
|
||||
required: false,
|
||||
label: "Start TLS connection to AD/LDAP server",
|
||||
tooltip: "Use StartTLS connection to AD/LDAP server",
|
||||
type: "on|off",
|
||||
},
|
||||
{
|
||||
name: "lookup_bind_dn",
|
||||
required: true,
|
||||
label: "Lookup Bind DN",
|
||||
tooltip:
|
||||
"DN for LDAP read-only service account used to perform DN and group lookups",
|
||||
type: "string",
|
||||
placeholder: "cn=admin,dc=min,dc=io",
|
||||
},
|
||||
{
|
||||
name: "lookup_bind_password",
|
||||
required: false,
|
||||
label: "Lookup Bind Password",
|
||||
tooltip:
|
||||
"Password for LDAP read-only service account used to perform DN and group lookups",
|
||||
type: "string",
|
||||
placeholder: "admin",
|
||||
},
|
||||
{
|
||||
name: "user_dn_search_base_dn",
|
||||
required: false,
|
||||
label: "User DN Search Base DN",
|
||||
tooltip: "Base LDAP DN to search for user DN",
|
||||
type: "csv",
|
||||
placeholder: "dc=myldapserver",
|
||||
},
|
||||
{
|
||||
name: "user_dn_search_filter",
|
||||
required: false,
|
||||
label: "User DN Search Filter",
|
||||
tooltip: "Search filter to lookup user DN",
|
||||
type: "string",
|
||||
placeholder: "(sAMAcountName=%s)",
|
||||
},
|
||||
{
|
||||
name: "group_search_filter",
|
||||
required: false,
|
||||
label: "Group Search Filter",
|
||||
tooltip: "Search filter for groups",
|
||||
type: "string",
|
||||
placeholder: "(&(objectclass=groupOfNames)(member=%d))",
|
||||
},
|
||||
{
|
||||
name: "group_search_base_dn",
|
||||
required: false,
|
||||
label: "Group Search Base DN",
|
||||
tooltip: "List of group search base DNs",
|
||||
type: "csv",
|
||||
placeholder: "dc=minioad,dc=local",
|
||||
},
|
||||
{
|
||||
name: "comment",
|
||||
required: false,
|
||||
label: "Comment",
|
||||
tooltip: "Optionally add a comment to this setting",
|
||||
type: "comment",
|
||||
placeholder: "Enter custom notes if any",
|
||||
},
|
||||
],
|
||||
logger_webhook: [
|
||||
{
|
||||
name: "endpoint",
|
||||
|
||||
@@ -38,6 +38,7 @@ import PageHeader from "../Common/PageHeader/PageHeader";
|
||||
import BackLink from "../../../common/BackLink";
|
||||
import PageLayout from "../Common/Layout/PageLayout";
|
||||
import SectionTitle from "../Common/SectionTitle";
|
||||
import FormSwitchWrapper from "../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
|
||||
|
||||
type AddIDPConfigurationProps = {
|
||||
classes?: any;
|
||||
@@ -127,6 +128,44 @@ const AddIDPConfiguration = ({
|
||||
invokeApi("POST", endpoint, { name, input });
|
||||
};
|
||||
|
||||
const renderFormField = (key: string, value: any) => {
|
||||
switch (value.type) {
|
||||
case "toggle":
|
||||
return (
|
||||
<FormSwitchWrapper
|
||||
indicatorLabels={["Enabled", "Disabled"]}
|
||||
checked={fields[key] === "on" ? true : false}
|
||||
value={"is-field-enabled"}
|
||||
id={"is-field-enabled"}
|
||||
name={"is-field-enabled"}
|
||||
label={value.label}
|
||||
tooltip={value.tooltip}
|
||||
onChange={(e) =>
|
||||
setFields({ ...fields, [key]: e.target.checked ? "on" : "off" })
|
||||
}
|
||||
description=""
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<InputBoxWrapper
|
||||
id={key}
|
||||
required={value.required}
|
||||
name={key}
|
||||
label={value.label}
|
||||
tooltip={value.tooltip}
|
||||
error={value.hasError(fields[key], true)}
|
||||
value={fields[key] ? fields[key] : ""}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setFields({ ...fields, [key]: e.target.value })
|
||||
}
|
||||
placeholder={value.placeholder}
|
||||
type={value.type}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Grid item xs={12}>
|
||||
<PageHeader label={<BackLink to={backLink} label={header} />} />
|
||||
@@ -161,20 +200,7 @@ const AddIDPConfiguration = ({
|
||||
className={classes.formFieldRow}
|
||||
key={key}
|
||||
>
|
||||
<InputBoxWrapper
|
||||
id={key}
|
||||
required={value.required}
|
||||
name={key}
|
||||
label={value.label}
|
||||
tooltip={value.tooltip}
|
||||
error={value.hasError(fields[key], true)}
|
||||
value={fields[key] ? fields[key] : ""}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setFields({ ...fields, [key]: e.target.value })
|
||||
}
|
||||
placeholder={value.placeholder}
|
||||
type={value.type}
|
||||
/>
|
||||
{renderFormField(key, value)}
|
||||
</Grid>
|
||||
))}
|
||||
<Grid item xs={12} textAlign={"right"}>
|
||||
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
} from "../Common/FormComponents/common/styleLibrary";
|
||||
import { IAM_PAGES } from "../../../common/SecureComponent/permissions";
|
||||
import AddIDPConfiguration from "./AddIDPConfiguration";
|
||||
import { ldapFormFields } from "./utils";
|
||||
import { ldapFormFields, ldapHelpBoxContents } from "./utils";
|
||||
import AddIDPConfigurationHelpBox from "./AddIDPConfigurationHelpbox";
|
||||
|
||||
type AddIDPLDAPConfigurationProps = {
|
||||
@@ -43,31 +43,13 @@ const styles = (theme: Theme) =>
|
||||
});
|
||||
|
||||
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: <LoginIcon />,
|
||||
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 (
|
||||
<AddIDPConfiguration
|
||||
icon={<LoginIcon />}
|
||||
helpBox={
|
||||
<AddIDPConfigurationHelpBox
|
||||
helpText={"Learn more about LDAP Configurations"}
|
||||
contents={helpBoxContents}
|
||||
contents={ldapHelpBoxContents}
|
||||
docLink={
|
||||
"https://min.io/docs/minio/linux/operations/external-iam.html?ref=con#minio-external-iam-ad-ldap"
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ 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 { openIDFormFields, openIDHelpBoxContents } from "./utils";
|
||||
import AddIDPConfigurationHelpBox from "./AddIDPConfigurationHelpbox";
|
||||
|
||||
type AddIDPOpenIDConfigurationProps = {
|
||||
@@ -34,25 +34,13 @@ 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: <LockIcon />,
|
||||
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 (
|
||||
<AddIDPConfiguration
|
||||
icon={<LockIcon />}
|
||||
helpBox={
|
||||
<AddIDPConfigurationHelpBox
|
||||
helpText={"Learn more about OpenID Connect Configurations"}
|
||||
contents={helpBoxContents}
|
||||
contents={openIDHelpBoxContents}
|
||||
docLink={
|
||||
"https://min.io/docs/minio/linux/operations/external-iam.html?ref=con#minio-external-iam-oidc"
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ import {
|
||||
pageContentStyles,
|
||||
searchField,
|
||||
} from "../Common/FormComponents/common/styleLibrary";
|
||||
import { RefreshIcon, TrashIcon } from "../../../icons";
|
||||
import { EditIcon, RefreshIcon, TrashIcon } from "../../../icons";
|
||||
import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
import { Button } from "mds";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
@@ -47,6 +47,7 @@ import BackLink from "../../../common/BackLink";
|
||||
import ScreenTitle from "../Common/ScreenTitle/ScreenTitle";
|
||||
import DeleteIDPConfigurationModal from "./DeleteIDPConfigurationModal";
|
||||
import FormSwitchWrapper from "../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
|
||||
import LabelValuePair from "../Common/UsageBarWrapper/LabelValuePair";
|
||||
|
||||
type IDPConfigurationDetailsProps = {
|
||||
classes?: any;
|
||||
@@ -55,6 +56,7 @@ type IDPConfigurationDetailsProps = {
|
||||
backLink: string;
|
||||
header: string;
|
||||
idpType: string;
|
||||
helpBox: React.ReactNode;
|
||||
icon: React.ReactNode;
|
||||
};
|
||||
|
||||
@@ -90,6 +92,7 @@ const IDPConfigurationDetails = ({
|
||||
header,
|
||||
idpType,
|
||||
icon,
|
||||
helpBox,
|
||||
}: IDPConfigurationDetailsProps) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const navigate = useNavigate();
|
||||
@@ -229,6 +232,144 @@ const IDPConfigurationDetails = ({
|
||||
invokeEnabledApi("PUT", `${endpoint}${configurationName}`, { input });
|
||||
};
|
||||
|
||||
const renderFormField = (key: string, value: any) => {
|
||||
switch (value.type) {
|
||||
case "toggle":
|
||||
return (
|
||||
<FormSwitchWrapper
|
||||
indicatorLabels={["Enabled", "Disabled"]}
|
||||
checked={fields[key] === "on" ? true : false}
|
||||
value={"is-field-enabled"}
|
||||
id={"is-field-enabled"}
|
||||
name={"is-field-enabled"}
|
||||
label={value.label}
|
||||
tooltip={value.tooltip}
|
||||
onChange={(e) =>
|
||||
setFields({ ...fields, [key]: e.target.checked ? "on" : "off" })
|
||||
}
|
||||
description=""
|
||||
disabled={!editMode}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<InputBoxWrapper
|
||||
id={key}
|
||||
required={value.required}
|
||||
name={key}
|
||||
label={value.label}
|
||||
tooltip={value.tooltip}
|
||||
error={value.hasError(fields[key], editMode)}
|
||||
value={fields[key] ? fields[key] : ""}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setFields({ ...fields, [key]: e.target.value })
|
||||
}
|
||||
placeholder={value.placeholder}
|
||||
disabled={!editMode}
|
||||
type={value.type}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const renderEditForm = () => {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: "grid",
|
||||
padding: "25px",
|
||||
gap: "25px",
|
||||
gridTemplateColumns: {
|
||||
md: "2fr 1.2fr",
|
||||
xs: "1fr",
|
||||
},
|
||||
border: "1px solid #eaeaea",
|
||||
}}
|
||||
>
|
||||
<form
|
||||
noValidate
|
||||
autoComplete="off"
|
||||
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
||||
saveRecord(e);
|
||||
}}
|
||||
>
|
||||
<Grid container item spacing="20" sx={{ marginTop: 1 }}>
|
||||
<Grid xs={12} item className={classes.fieldBox}>
|
||||
{Object.entries(formFields).map(([key, value]) => (
|
||||
<Grid item xs={12} className={classes.formFieldRow} key={key}>
|
||||
{renderFormField(key, value)}
|
||||
</Grid>
|
||||
))}
|
||||
<Grid item xs={12} textAlign={"right"}>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-end",
|
||||
marginTop: "20px",
|
||||
gap: "15px",
|
||||
}}
|
||||
>
|
||||
{editMode && (
|
||||
<Button
|
||||
id={"clear"}
|
||||
type="button"
|
||||
variant="regular"
|
||||
onClick={resetForm}
|
||||
label={"Clear"}
|
||||
/>
|
||||
)}
|
||||
{editMode && (
|
||||
<Button
|
||||
id={"cancel"}
|
||||
type="button"
|
||||
variant="regular"
|
||||
onClick={toggleEditMode}
|
||||
label={"Cancel"}
|
||||
/>
|
||||
)}
|
||||
{editMode && (
|
||||
<Button
|
||||
id={"save-key"}
|
||||
type="submit"
|
||||
variant="callAction"
|
||||
color="primary"
|
||||
disabled={loading || loadingSave || !validSave()}
|
||||
label={"Save"}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</form>
|
||||
{helpBox}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
const renderViewForm = () => {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: { xs: "1fr", sm: "2fr 1fr" },
|
||||
gridAutoFlow: { xs: "dense", sm: "row" },
|
||||
gap: 3,
|
||||
padding: "15px",
|
||||
border: "1px solid #eaeaea",
|
||||
}}
|
||||
>
|
||||
{Object.entries(formFields).map(([key, value]) => (
|
||||
<LabelValuePair
|
||||
key={key}
|
||||
label={value.label}
|
||||
value={fields[key] ? fields[key] : ""}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Grid item xs={12}>
|
||||
{deleteOpen && configurationName && (
|
||||
@@ -239,24 +380,9 @@ const IDPConfigurationDetails = ({
|
||||
closeDeleteModalAndRefresh={closeDeleteModalAndRefresh}
|
||||
/>
|
||||
)}
|
||||
<PageHeader
|
||||
label={<BackLink to={backLink} label={header} />}
|
||||
actions={
|
||||
<FormSwitchWrapper
|
||||
label={""}
|
||||
indicatorLabels={["Enabled", "Disabled"]}
|
||||
checked={isEnabled}
|
||||
value={"is-configuration-enabled"}
|
||||
id={"is-configuration-enabled"}
|
||||
name={"is-configuration-enabled"}
|
||||
onChange={(e) => toggleConfiguration(e.target.checked)}
|
||||
description=""
|
||||
disabled={loadingEnabledSave}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<PageHeader label={<BackLink to={backLink} label={header} />} />
|
||||
<PageLayout className={classes.pageContainer}>
|
||||
<Grid item xs={12}>
|
||||
<Box>
|
||||
<ScreenTitle
|
||||
classes={{
|
||||
screenTitle: classes.screenTitle,
|
||||
@@ -276,6 +402,22 @@ const IDPConfigurationDetails = ({
|
||||
variant={"secondary"}
|
||||
/>
|
||||
)}
|
||||
{!editMode && (
|
||||
<Button
|
||||
id={"edit"}
|
||||
type="button"
|
||||
variant={"callAction"}
|
||||
icon={<EditIcon />}
|
||||
onClick={toggleEditMode}
|
||||
label={"Edit"}
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
id={"is-configuration-enabled"}
|
||||
onClick={() => toggleConfiguration(!isEnabled)}
|
||||
label={isEnabled ? "Disable" : "Enable"}
|
||||
disabled={loadingEnabledSave}
|
||||
/>
|
||||
<Button
|
||||
id={"refresh-idp-config"}
|
||||
onClick={() => setLoading(true)}
|
||||
@@ -285,77 +427,9 @@ const IDPConfigurationDetails = ({
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
<form
|
||||
noValidate
|
||||
autoComplete="off"
|
||||
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
||||
saveRecord(e);
|
||||
}}
|
||||
>
|
||||
<Grid container item spacing="20" sx={{ marginTop: 1 }}>
|
||||
<Grid xs={12} item className={classes.fieldBox}>
|
||||
{Object.entries(formFields).map(([key, value]) => (
|
||||
<Grid item xs={12} className={classes.formFieldRow} key={key}>
|
||||
<InputBoxWrapper
|
||||
id={key}
|
||||
required={value.required}
|
||||
name={key}
|
||||
label={value.label}
|
||||
tooltip={value.tooltip}
|
||||
error={value.hasError(fields[key], editMode)}
|
||||
value={fields[key] ? fields[key] : ""}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setFields({ ...fields, [key]: e.target.value })
|
||||
}
|
||||
placeholder={value.placeholder}
|
||||
disabled={!editMode}
|
||||
type={value.type}
|
||||
/>
|
||||
</Grid>
|
||||
))}
|
||||
<Grid item xs={12} textAlign={"right"}>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-end",
|
||||
marginTop: "20px",
|
||||
gap: "15px",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
id={"edit"}
|
||||
type="button"
|
||||
variant={editMode ? "regular" : "callAction"}
|
||||
onClick={toggleEditMode}
|
||||
label={editMode ? "Cancel" : "Edit"}
|
||||
/>
|
||||
{editMode && (
|
||||
<Button
|
||||
id={"clear"}
|
||||
type="button"
|
||||
variant="regular"
|
||||
onClick={resetForm}
|
||||
label={"Clear"}
|
||||
/>
|
||||
)}
|
||||
|
||||
{editMode && (
|
||||
<Button
|
||||
id={"save-key"}
|
||||
type="submit"
|
||||
variant="callAction"
|
||||
color="primary"
|
||||
disabled={loading || loadingSave || !validSave()}
|
||||
label={"Save"}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</form>
|
||||
{editMode ? renderEditForm() : renderViewForm()}
|
||||
</Box>
|
||||
</PageLayout>
|
||||
</Grid>
|
||||
);
|
||||
|
||||
@@ -19,10 +19,11 @@ 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 { ldapFormFields, ldapHelpBoxContents } from "./utils";
|
||||
import LoginIcon from "@mui/icons-material/Login";
|
||||
import IDPConfigurationDetails from "./IDPConfigurationDetails";
|
||||
import { IAM_PAGES } from "../../../common/SecureComponent/permissions";
|
||||
import AddIDPConfigurationHelpBox from "./AddIDPConfigurationHelpbox";
|
||||
|
||||
type IDPLDAPConfigurationDetailsProps = {
|
||||
classes?: any;
|
||||
@@ -39,6 +40,16 @@ const IDPLDAPConfigurationDetails = ({
|
||||
header={"LDAP Configurations"}
|
||||
endpoint={"/api/v1/idp/ldap/"}
|
||||
idpType={"ldap"}
|
||||
helpBox={
|
||||
<AddIDPConfigurationHelpBox
|
||||
helpText={"Learn more about LDAP Configurations"}
|
||||
contents={ldapHelpBoxContents}
|
||||
docLink={
|
||||
"https://min.io/docs/minio/linux/operations/external-iam.html?ref=con#minio-external-iam-ad-ldap"
|
||||
}
|
||||
docText={"Learn more about LDAP Configurations"}
|
||||
/>
|
||||
}
|
||||
formFields={ldapFormFields}
|
||||
icon={<LoginIcon width={40} />}
|
||||
/>
|
||||
|
||||
@@ -21,8 +21,9 @@ 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 { openIDFormFields, openIDHelpBoxContents } from "./utils";
|
||||
import IDPConfigurationDetails from "./IDPConfigurationDetails";
|
||||
import AddIDPConfigurationHelpBox from "./AddIDPConfigurationHelpbox";
|
||||
|
||||
type IDPOpenIDConfigurationDetailsProps = {
|
||||
classes?: any;
|
||||
@@ -39,6 +40,16 @@ const IDPOpenIDConfigurationDetails = ({
|
||||
header={"OpenID Configurations"}
|
||||
endpoint={"/api/v1/idp/openid/"}
|
||||
idpType={"openid"}
|
||||
helpBox={
|
||||
<AddIDPConfigurationHelpBox
|
||||
helpText={"Learn more about OpenID Connect Configurations"}
|
||||
contents={openIDHelpBoxContents}
|
||||
docLink={
|
||||
"https://min.io/docs/minio/linux/operations/external-iam.html?ref=con#minio-external-iam-oidc"
|
||||
}
|
||||
docText={"Learn more about OpenID Connect Configurations"}
|
||||
/>
|
||||
}
|
||||
formFields={openIDFormFields}
|
||||
icon={<LockIcon width={40} />}
|
||||
/>
|
||||
|
||||
@@ -14,6 +14,40 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import LoginIcon from "@mui/icons-material/Login";
|
||||
import { LockIcon } from "../../../icons";
|
||||
|
||||
export const ldapHelpBoxContents = [
|
||||
{
|
||||
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: <LoginIcon />,
|
||||
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: "",
|
||||
},
|
||||
];
|
||||
|
||||
export const openIDHelpBoxContents = [
|
||||
{
|
||||
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: <LockIcon />,
|
||||
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: "",
|
||||
},
|
||||
];
|
||||
|
||||
export const openIDFormFields = {
|
||||
config_url: {
|
||||
required: true,
|
||||
@@ -46,14 +80,6 @@ export const openIDFormFields = {
|
||||
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",
|
||||
@@ -62,10 +88,18 @@ export const openIDFormFields = {
|
||||
type: "text",
|
||||
hasError: (s: string, editMode: boolean) => "",
|
||||
},
|
||||
display_name: {
|
||||
required: false,
|
||||
label: "Display Name",
|
||||
tooltip: "",
|
||||
placeholder: "Enter Display Name",
|
||||
type: "text",
|
||||
hasError: (s: string, editMode: boolean) => "",
|
||||
},
|
||||
claim_prefix: {
|
||||
required: false,
|
||||
label: "Claim Prefix",
|
||||
tooltip: "Claim Prefix",
|
||||
tooltip: "",
|
||||
placeholder: "Enter Claim Prefix",
|
||||
type: "text",
|
||||
hasError: (s: string, editMode: boolean) => "",
|
||||
@@ -73,7 +107,7 @@ export const openIDFormFields = {
|
||||
scopes: {
|
||||
required: false,
|
||||
label: "Scopes",
|
||||
tooltip: "Scopes",
|
||||
tooltip: "",
|
||||
placeholder: "openid,profile,email",
|
||||
type: "text",
|
||||
hasError: (s: string, editMode: boolean) => "",
|
||||
@@ -81,7 +115,7 @@ export const openIDFormFields = {
|
||||
redirect_uri: {
|
||||
required: false,
|
||||
label: "Redirect URI",
|
||||
tooltip: "Redirect URI",
|
||||
tooltip: "",
|
||||
placeholder: "https://console-endpoint-url/oauth_callback",
|
||||
type: "text",
|
||||
hasError: (s: string, editMode: boolean) => "",
|
||||
@@ -89,11 +123,27 @@ export const openIDFormFields = {
|
||||
role_policy: {
|
||||
required: false,
|
||||
label: "Role Policy",
|
||||
tooltip: "Role Policy",
|
||||
tooltip: "",
|
||||
placeholder: "readonly",
|
||||
type: "text",
|
||||
hasError: (s: string, editMode: boolean) => "",
|
||||
},
|
||||
claim_userinfo: {
|
||||
required: false,
|
||||
label: "Claim User Info",
|
||||
tooltip: "",
|
||||
placeholder: "Claim User Info",
|
||||
type: "toggle",
|
||||
hasError: (s: string, editMode: boolean) => "",
|
||||
},
|
||||
redirect_uri_dynamic: {
|
||||
required: false,
|
||||
label: "Redirect URI Dynamic",
|
||||
tooltip: "",
|
||||
placeholder: "Redirect URI Dynamic",
|
||||
type: "toggle",
|
||||
hasError: (s: string, editMode: boolean) => "",
|
||||
},
|
||||
};
|
||||
|
||||
export const ldapFormFields = {
|
||||
@@ -135,7 +185,7 @@ export const ldapFormFields = {
|
||||
return !s && editMode ? "User DN Search Base DN is required" : "";
|
||||
},
|
||||
label: "User DN Search Base",
|
||||
tooltip: "Base LDAP DN to search for user DN",
|
||||
tooltip: "",
|
||||
placeholder: "DC=example,DC=net",
|
||||
type: "text",
|
||||
},
|
||||
@@ -145,14 +195,14 @@ export const ldapFormFields = {
|
||||
return !s && editMode ? "User DN Search Filter is required" : "";
|
||||
},
|
||||
label: "User DN Search Filter",
|
||||
tooltip: "Search filter to lookup user DN",
|
||||
tooltip: "",
|
||||
placeholder: "(sAMAcountName=%s)",
|
||||
type: "text",
|
||||
},
|
||||
display_name: {
|
||||
required: false,
|
||||
label: "Display Name",
|
||||
tooltip: "Display Name",
|
||||
tooltip: "",
|
||||
placeholder: "Enter Display Name",
|
||||
type: "text",
|
||||
hasError: (s: string, editMode: boolean) => "",
|
||||
@@ -161,7 +211,7 @@ export const ldapFormFields = {
|
||||
required: false,
|
||||
hasError: (s: string, editMode: boolean) => "",
|
||||
label: "Group Search Base DN",
|
||||
tooltip: "Group Search Base DN",
|
||||
tooltip: "",
|
||||
placeholder: "ou=swengg,dc=min,dc=io",
|
||||
type: "text",
|
||||
},
|
||||
@@ -169,7 +219,7 @@ export const ldapFormFields = {
|
||||
required: false,
|
||||
hasError: (s: string, editMode: boolean) => "",
|
||||
label: "Group Search Filter",
|
||||
tooltip: "Group Search Filter",
|
||||
tooltip: "",
|
||||
placeholder: "(&(objectclass=groupofnames)(member=%d))",
|
||||
type: "text",
|
||||
},
|
||||
|
||||
@@ -62,10 +62,6 @@ test("All vertical tab items exist", async (t) => {
|
||||
.ok()
|
||||
.expect(settingsEtcdTabExists)
|
||||
.ok()
|
||||
.expect(settingsOpenIdTabExists)
|
||||
.ok()
|
||||
.expect(settingsLdapTabExists)
|
||||
.ok()
|
||||
.expect(settingsLoggerWebhookTabExists)
|
||||
.ok()
|
||||
.expect(settingsAuditWebhookTabExists)
|
||||
|
||||
Reference in New Issue
Block a user