Updated OpenID UX (#3050)

- Display ENV variables set in configuration
- Removed Password empty placeholders
- Added notification to re-enter password when modifying OpenID configuration

Signed-off-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
Alex
2023-09-22 13:50:41 -06:00
committed by GitHub
parent 1ce2846c95
commit 300ebfa19f
5 changed files with 195 additions and 63 deletions

View File

@@ -218,7 +218,9 @@ const EditEndpointModal = ({
<Tooltip <Tooltip
tooltip={ tooltip={
overrideValues.enable overrideValues.enable
? `This value is set from the ${overrideValues.enable.overrideEnv} environment variable` ? `This value is set from the ${
overrideValues.enable?.overrideEnv || "N/A"
} environment variable`
: "" : ""
} }
placement={"left"} placement={"left"}
@@ -245,7 +247,9 @@ const EditEndpointModal = ({
<Tooltip <Tooltip
tooltip={ tooltip={
overrideValues.enable overrideValues.enable
? `This value is set from the ${overrideValues.endpoint.overrideEnv} environment variable` ? `This value is set from the ${
overrideValues.endpoint?.overrideEnv || "N/A"
} environment variable`
: "" : ""
} }
placement={"left"} placement={"left"}
@@ -272,7 +276,9 @@ const EditEndpointModal = ({
<Tooltip <Tooltip
tooltip={ tooltip={
overrideValues.enable overrideValues.enable
? `This value is set from the ${overrideValues.auth_token.overrideEnv} environment variable` ? `This value is set from the ${
overrideValues.auth_token?.overrideEnv || "N/A"
} environment variable`
: "" : ""
} }
placement={"left"} placement={"left"}

View File

@@ -14,21 +14,26 @@
// You should have received a copy of the GNU Affero General Public License // 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/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { Fragment, useEffect, useState } from "react"; import React, { Fragment, useCallback, useEffect, useState } from "react";
import { import {
BackLink, BackLink,
Box,
breakPoints,
Button, Button,
ConsoleIcon,
EditIcon, EditIcon,
FormLayout,
Grid,
HelpBox,
InputBox,
PageLayout, PageLayout,
RefreshIcon, RefreshIcon,
TrashIcon,
Box,
Grid,
Switch,
InputBox,
FormLayout,
breakPoints,
ScreenTitle, ScreenTitle,
Switch,
Tooltip,
TrashIcon,
ValuePair,
WarnIcon,
} from "mds"; } from "mds";
import { useNavigate, useParams } from "react-router-dom"; import { useNavigate, useParams } from "react-router-dom";
import { modalStyleUtils } from "../Common/FormComponents/common/styleLibrary"; import { modalStyleUtils } from "../Common/FormComponents/common/styleLibrary";
@@ -42,7 +47,6 @@ import {
import api from "../../../common/api"; import api from "../../../common/api";
import useApi from "../Common/Hooks/useApi"; import useApi from "../Common/Hooks/useApi";
import DeleteIDPConfigurationModal from "./DeleteIDPConfigurationModal"; import DeleteIDPConfigurationModal from "./DeleteIDPConfigurationModal";
import LabelValuePair from "../Common/UsageBarWrapper/LabelValuePair";
import PageHeaderWrapper from "../Common/PageHeaderWrapper/PageHeaderWrapper"; import PageHeaderWrapper from "../Common/PageHeaderWrapper/PageHeaderWrapper";
import HelpMenu from "../HelpMenu"; import HelpMenu from "../HelpMenu";
@@ -74,10 +78,12 @@ const IDPConfigurationDetails = ({
const [loading, setLoading] = useState<boolean>(true); const [loading, setLoading] = useState<boolean>(true);
const [isEnabled, setIsEnabled] = useState<boolean>(false); const [isEnabled, setIsEnabled] = useState<boolean>(false);
const [fields, setFields] = useState<any>({}); const [fields, setFields] = useState<any>({});
const [overrideFields, setOverrideFields] = useState<any>({});
const [originalFields, setOriginalFields] = useState<any>({}); const [originalFields, setOriginalFields] = useState<any>({});
const [record, setRecord] = useState<any>({}); const [record, setRecord] = useState<any>({});
const [editMode, setEditMode] = useState<boolean>(false); const [editMode, setEditMode] = useState<boolean>(false);
const [deleteOpen, setDeleteOpen] = useState<boolean>(false); const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
const [envOverride, setEnvOverride] = useState<boolean>(false);
const onSuccess = (res: any) => { const onSuccess = (res: any) => {
dispatch(setServerNeedsRestart(res.restart === true)); dispatch(setServerNeedsRestart(res.restart === true));
@@ -102,6 +108,40 @@ const IDPConfigurationDetails = ({
onEnabledError, onEnabledError,
); );
const parseFields = useCallback(
(record: any) => {
let fields: any = {};
let overrideFields: any = {};
let totEnv = 0;
if (record.info) {
record.info.forEach((item: any) => {
if (item.key === "enable") {
setIsEnabled(item.value === "on");
}
if (item.isEnv) {
overrideFields[
item.key
] = `MINIO_IDENTITY_OPENID_${item.key.toUpperCase()}${
configurationName !== "_" ? `_${configurationName}` : ""
}`;
totEnv++;
}
fields[item.key] = item.value;
});
if (totEnv > 0) {
setEnvOverride(true);
}
}
setFields(fields);
setOverrideFields(overrideFields);
},
[configurationName],
);
const toggleEditMode = () => { const toggleEditMode = () => {
if (editMode) { if (editMode) {
parseFields(record); parseFields(record);
@@ -109,19 +149,6 @@ const IDPConfigurationDetails = ({
setEditMode(!editMode); 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) => { const parseOriginalFields = (record: any) => {
let fields: any = {}; let fields: any = {};
if (record.info) { if (record.info) {
@@ -157,7 +184,7 @@ const IDPConfigurationDetails = ({
if (loading) { if (loading) {
loadRecord(); loadRecord();
} }
}, [dispatch, loading, configurationName, endpoint]); }, [dispatch, loading, configurationName, endpoint, parseFields]);
const validSave = () => { const validSave = () => {
for (const [key, value] of Object.entries(formFields)) { for (const [key, value] of Object.entries(formFields)) {
@@ -255,6 +282,27 @@ const IDPConfigurationDetails = ({
}} }}
> >
<Grid container> <Grid container>
{editMode ? (
<Grid item xs={12} sx={{ marginBottom: 15 }}>
<HelpBox
title={
<Box
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
flexGrow: 1,
}}
>
Client Secret must be re-entered to change OpenID
configurations
</Box>
}
iconComponent={<WarnIcon />}
help={null}
/>
</Grid>
) : null}
<Grid xs={12} item> <Grid xs={12} item>
{Object.entries(formFields).map(([key, value]) => {Object.entries(formFields).map(([key, value]) =>
renderFormField(key, value), renderFormField(key, value),
@@ -298,26 +346,72 @@ const IDPConfigurationDetails = ({
const renderViewForm = () => { const renderViewForm = () => {
return ( return (
<Box <Box
withBorders
sx={{ sx={{
display: "grid", display: "grid",
gridTemplateColumns: "1fr", gridTemplateColumns: "1fr",
gridAutoFlow: "dense", gridAutoFlow: "dense",
gap: 3, gap: 3,
padding: "15px", padding: "15px",
border: "1px solid #eaeaea",
[`@media (min-width: ${breakPoints.sm}px)`]: { [`@media (min-width: ${breakPoints.sm}px)`]: {
gridTemplateColumns: "2fr 1fr", gridTemplateColumns: "2fr 1fr",
gridAutoFlow: "row", gridAutoFlow: "row",
}, },
}} }}
> >
{Object.entries(formFields).map(([key, value]) => ( {Object.entries(formFields).map(([key, value]) => {
<LabelValuePair if (!value.editOnly) {
key={key} let label: React.ReactNode = value.label;
label={value.label} let val: React.ReactNode = fields[key] ? fields[key] : "";
value={fields[key] ? fields[key] : ""}
/> if (value.type === "toggle" && fields[key]) {
))} if (val !== "on") {
val = "Off";
} else {
val = "On";
}
}
if (overrideFields[key]) {
label = (
<Box
sx={{
display: "flex",
alignItems: "center",
gap: 5,
"& .min-icon": {
height: 20,
width: 20,
},
"& span": {
height: 20,
display: "flex",
alignItems: "center",
},
}}
>
<span>{value.label}</span>
<Tooltip
tooltip={`This value is set from the ${overrideFields[key]} environment variable`}
placement={"right"}
>
<span className={"muted"}>
<ConsoleIcon />
</span>
</Tooltip>
</Box>
);
val = (
<i>
<span className={"muted"}>{val}</span>
</i>
);
}
return <ValuePair key={key} label={label} value={val} />;
}
return null;
})}
</Box> </Box>
); );
}; };
@@ -351,32 +445,58 @@ const IDPConfigurationDetails = ({
actions={ actions={
<Fragment> <Fragment>
{configurationName !== "_" && ( {configurationName !== "_" && (
<Button <Tooltip
id={"delete-idp-config"} tooltip={
onClick={() => { envOverride
setDeleteOpen(true); ? "This configuration cannot be deleted using this module as this was set using OpenID environment variables."
}} : ""
label={"Delete Configuration"} }
icon={<TrashIcon />} >
variant={"secondary"} <Button
/> id={"delete-idp-config"}
onClick={() => {
setDeleteOpen(true);
}}
label={"Delete Configuration"}
icon={<TrashIcon />}
variant={"secondary"}
disabled={envOverride}
/>
</Tooltip>
)} )}
{!editMode && ( {!editMode && (
<Button <Tooltip
id={"edit"} tooltip={
type="button" envOverride
variant={"callAction"} ? "Configuration cannot be edited in this module as OpenID environment variables are set for this MinIO instance."
icon={<EditIcon />} : ""
onClick={toggleEditMode} }
label={"Edit"} >
/> <Button
id={"edit"}
type="button"
variant={"callAction"}
icon={<EditIcon />}
onClick={toggleEditMode}
label={"Edit"}
disabled={envOverride}
/>
</Tooltip>
)} )}
<Button <Tooltip
id={"is-configuration-enabled"} tooltip={
onClick={() => toggleConfiguration(!isEnabled)} envOverride
label={isEnabled ? "Disable" : "Enable"} ? "Configuration cannot be disabled / enabled in this module as OpenID environment variables are set for this MinIO instance."
disabled={loadingEnabledSave} : ""
/> }
>
<Button
id={"is-configuration-enabled"}
onClick={() => toggleConfiguration(!isEnabled)}
label={isEnabled ? "Disable" : "Enable"}
disabled={loadingEnabledSave || envOverride}
/>
</Tooltip>
<Button <Button
id={"refresh-idp-config"} id={"refresh-idp-config"}
onClick={() => setLoading(true)} onClick={() => setLoading(true)}

View File

@@ -59,6 +59,7 @@ export const openIDFormFields = {
placeholder: placeholder:
"https://identity-provider-url/.well-known/openid-configuration", "https://identity-provider-url/.well-known/openid-configuration",
type: "text", type: "text",
editOnly: false,
}, },
client_id: { client_id: {
required: true, required: true,
@@ -69,6 +70,7 @@ export const openIDFormFields = {
tooltip: "Identity provider Client ID", tooltip: "Identity provider Client ID",
placeholder: "Enter Client ID", placeholder: "Enter Client ID",
type: "text", type: "text",
editOnly: false,
}, },
client_secret: { client_secret: {
required: true, required: true,
@@ -79,6 +81,7 @@ export const openIDFormFields = {
tooltip: "Identity provider Client Secret", tooltip: "Identity provider Client Secret",
placeholder: "Enter Client Secret", placeholder: "Enter Client Secret",
type: "password", type: "password",
editOnly: true,
}, },
claim_name: { claim_name: {
required: false, required: false,
@@ -87,6 +90,7 @@ export const openIDFormFields = {
placeholder: "Enter Claim Name", placeholder: "Enter Claim Name",
type: "text", type: "text",
hasError: (s: string, editMode: boolean) => "", hasError: (s: string, editMode: boolean) => "",
editOnly: false,
}, },
display_name: { display_name: {
required: false, required: false,
@@ -95,6 +99,7 @@ export const openIDFormFields = {
placeholder: "Enter Display Name", placeholder: "Enter Display Name",
type: "text", type: "text",
hasError: (s: string, editMode: boolean) => "", hasError: (s: string, editMode: boolean) => "",
editOnly: false,
}, },
claim_prefix: { claim_prefix: {
required: false, required: false,
@@ -103,6 +108,7 @@ export const openIDFormFields = {
placeholder: "Enter Claim Prefix", placeholder: "Enter Claim Prefix",
type: "text", type: "text",
hasError: (s: string, editMode: boolean) => "", hasError: (s: string, editMode: boolean) => "",
editOnly: false,
}, },
scopes: { scopes: {
required: false, required: false,
@@ -111,6 +117,7 @@ export const openIDFormFields = {
placeholder: "openid,profile,email", placeholder: "openid,profile,email",
type: "text", type: "text",
hasError: (s: string, editMode: boolean) => "", hasError: (s: string, editMode: boolean) => "",
editOnly: false,
}, },
redirect_uri: { redirect_uri: {
required: false, required: false,
@@ -119,6 +126,7 @@ export const openIDFormFields = {
placeholder: "https://console-endpoint-url/oauth_callback", placeholder: "https://console-endpoint-url/oauth_callback",
type: "text", type: "text",
hasError: (s: string, editMode: boolean) => "", hasError: (s: string, editMode: boolean) => "",
editOnly: false,
}, },
role_policy: { role_policy: {
required: false, required: false,
@@ -127,6 +135,7 @@ export const openIDFormFields = {
placeholder: "readonly", placeholder: "readonly",
type: "text", type: "text",
hasError: (s: string, editMode: boolean) => "", hasError: (s: string, editMode: boolean) => "",
editOnly: false,
}, },
claim_userinfo: { claim_userinfo: {
required: false, required: false,
@@ -135,6 +144,7 @@ export const openIDFormFields = {
placeholder: "Claim User Info", placeholder: "Claim User Info",
type: "toggle", type: "toggle",
hasError: (s: string, editMode: boolean) => "", hasError: (s: string, editMode: boolean) => "",
editOnly: false,
}, },
redirect_uri_dynamic: { redirect_uri_dynamic: {
required: false, required: false,
@@ -143,6 +153,7 @@ export const openIDFormFields = {
placeholder: "Redirect URI Dynamic", placeholder: "Redirect URI Dynamic",
type: "toggle", type: "toggle",
hasError: (s: string, editMode: boolean) => "", hasError: (s: string, editMode: boolean) => "",
editOnly: false,
}, },
}; };

View File

@@ -29,12 +29,6 @@ import { RedirectRule } from "api/consoleApi";
import { redirectRules } from "./login.utils"; import { redirectRules } from "./login.utils";
import { setHelpName } from "../../systemSlice"; import { setHelpName } from "../../systemSlice";
export interface LoginStrategyPayload {
accessKey: string;
secretKey: string;
sts?: string;
}
export const getTargetPath = () => { export const getTargetPath = () => {
let targetPath = "/browser"; let targetPath = "/browser";
if ( if (

View File

@@ -198,6 +198,7 @@ func getIDPConfiguration(ctx context.Context, idpType, name string, client Minio
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &models.IdpServerConfiguration{ return &models.IdpServerConfiguration{
Name: config.Name, Name: config.Name,
Type: config.Type, Type: config.Type,