Changed IDP login support in console (#2695)
- Allowed to use External IDP + Built-in IDP at the same time - Added IDP type to IDP listing - Added IDP name when no display name is configured - Changed STS login link into new menu - Cleanup of Operator login strategies Signed-off-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
@@ -39,6 +39,9 @@ type RedirectRule struct {
|
||||
|
||||
// redirect
|
||||
Redirect string `json:"redirect,omitempty"`
|
||||
|
||||
// service type
|
||||
ServiceType string `json:"serviceType,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this redirect rule
|
||||
|
||||
@@ -16,38 +16,18 @@
|
||||
|
||||
import React, { Fragment, useEffect } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import {
|
||||
InputAdornment,
|
||||
LinearProgress,
|
||||
MenuItem,
|
||||
Select,
|
||||
} from "@mui/material";
|
||||
import {
|
||||
Button,
|
||||
Loader,
|
||||
LockIcon,
|
||||
LoginWrapper,
|
||||
LogoutIcon,
|
||||
RefreshIcon,
|
||||
} from "mds";
|
||||
import { Button, Loader, LoginWrapper, RefreshIcon } from "mds";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import makeStyles from "@mui/styles/makeStyles";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import { loginStrategyType, redirectRule } from "./types";
|
||||
import MainError from "../Console/Common/MainError/MainError";
|
||||
import { spacingUtils } from "../Console/Common/FormComponents/common/styleLibrary";
|
||||
import clsx from "clsx";
|
||||
import { AppState, useAppDispatch } from "../../store";
|
||||
import { useSelector } from "react-redux";
|
||||
import {
|
||||
doLoginAsync,
|
||||
getFetchConfigurationAsync,
|
||||
getVersionAsync,
|
||||
} from "./loginThunks";
|
||||
import { resetForm, setJwt } from "./loginSlice";
|
||||
import { getFetchConfigurationAsync, getVersionAsync } from "./loginThunks";
|
||||
import { resetForm } from "./loginSlice";
|
||||
import StrategyForm from "./StrategyForm";
|
||||
import { LoginField } from "./LoginField";
|
||||
import { redirectRules } from "../../utils/sortFunctions";
|
||||
import { getLogoVar } from "../../config";
|
||||
|
||||
@@ -250,19 +230,12 @@ const useStyles = makeStyles((theme: Theme) =>
|
||||
})
|
||||
);
|
||||
|
||||
export interface LoginStrategyRoutes {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
export interface LoginStrategyPayload {
|
||||
[key: string]: any;
|
||||
accessKey: string;
|
||||
secretKey: string;
|
||||
sts?: string;
|
||||
}
|
||||
|
||||
export const loginStrategyEndpoints: LoginStrategyRoutes = {
|
||||
form: "/api/v1/login",
|
||||
"service-account": "/api/v1/login/operator",
|
||||
};
|
||||
|
||||
export const getTargetPath = () => {
|
||||
let targetPath = "/";
|
||||
if (
|
||||
@@ -280,13 +253,9 @@ const Login = () => {
|
||||
const navigate = useNavigate();
|
||||
const classes = useStyles();
|
||||
|
||||
const jwt = useSelector((state: AppState) => state.login.jwt);
|
||||
const loginStrategy = useSelector(
|
||||
(state: AppState) => state.login.loginStrategy
|
||||
);
|
||||
const loginSending = useSelector(
|
||||
(state: AppState) => state.login.loginSending
|
||||
);
|
||||
const loadingFetchConfiguration = useSelector(
|
||||
(state: AppState) => state.login.loadingFetchConfiguration
|
||||
);
|
||||
@@ -295,13 +264,8 @@ const Login = () => {
|
||||
);
|
||||
const navigateTo = useSelector((state: AppState) => state.login.navigateTo);
|
||||
|
||||
const isDirectPV = useSelector((state: AppState) => state.login.isDirectPV);
|
||||
const isK8S = useSelector((state: AppState) => state.login.isK8S);
|
||||
|
||||
const isOperator =
|
||||
loginStrategy.loginStrategy === loginStrategyType.serviceAccount ||
|
||||
loginStrategy.loginStrategy === loginStrategyType.redirectServiceAccount;
|
||||
|
||||
useEffect(() => {
|
||||
if (navigateTo !== "") {
|
||||
dispatch(resetForm());
|
||||
@@ -309,11 +273,6 @@ const Login = () => {
|
||||
}
|
||||
}, [navigateTo, dispatch, navigate]);
|
||||
|
||||
const formSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
dispatch(doLoginAsync());
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (loadingFetchConfiguration) {
|
||||
dispatch(getFetchConfigurationAsync());
|
||||
@@ -329,12 +288,8 @@ const Login = () => {
|
||||
let loginComponent;
|
||||
|
||||
switch (loginStrategy.loginStrategy) {
|
||||
case loginStrategyType.form: {
|
||||
loginComponent = <StrategyForm />;
|
||||
break;
|
||||
}
|
||||
case loginStrategyType.redirect:
|
||||
case loginStrategyType.redirectServiceAccount: {
|
||||
case loginStrategyType.form: {
|
||||
let redirectItems: redirectRule[] = [];
|
||||
|
||||
if (
|
||||
@@ -344,111 +299,7 @@ const Login = () => {
|
||||
redirectItems = [...loginStrategy.redirectRules].sort(redirectRules);
|
||||
}
|
||||
|
||||
if (
|
||||
loginStrategy.redirectRules &&
|
||||
loginStrategy.redirectRules.length > 1
|
||||
) {
|
||||
loginComponent = (
|
||||
<Fragment>
|
||||
<div className={classes.loginSsoText}>Login with SSO:</div>
|
||||
<Select
|
||||
id="ssoLogin"
|
||||
name="ssoLogin"
|
||||
data-test-id="sso-login"
|
||||
onChange={(e) => {
|
||||
if (e.target.value) {
|
||||
window.location.href = e.target.value as string;
|
||||
}
|
||||
}}
|
||||
displayEmpty
|
||||
className={classes.ssoSelect}
|
||||
renderValue={() => "Select Provider"}
|
||||
>
|
||||
{redirectItems.map((r, idx) => (
|
||||
<MenuItem
|
||||
value={r.redirect}
|
||||
key={`sso-login-option-${idx}`}
|
||||
className={classes.ssoMenuItem}
|
||||
divider={true}
|
||||
>
|
||||
<LogoutIcon className={classes.ssoLoginIcon} />
|
||||
{r.displayName}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</Fragment>
|
||||
);
|
||||
} else if (redirectItems.length === 1) {
|
||||
loginComponent = (
|
||||
<div className={clsx(classes.submit, classes.ssoSubmit)}>
|
||||
<Button
|
||||
key={`login-button`}
|
||||
variant="callAction"
|
||||
id="sso-login"
|
||||
label={
|
||||
redirectItems[0].displayName === ""
|
||||
? "Login with SSO"
|
||||
: redirectItems[0].displayName
|
||||
}
|
||||
onClick={() => (window.location.href = redirectItems[0].redirect)}
|
||||
fullWidth
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
loginComponent = (
|
||||
<div className={classes.loginStrategyMessage}>
|
||||
Cannot retrieve redirect from login strategy
|
||||
</div>
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case loginStrategyType.serviceAccount: {
|
||||
loginComponent = (
|
||||
<Fragment>
|
||||
<form className={classes.form} noValidate onSubmit={formSubmit}>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<LoginField
|
||||
required
|
||||
className={classes.inputField}
|
||||
fullWidth
|
||||
id="jwt"
|
||||
value={jwt}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
dispatch(setJwt(e.target.value))
|
||||
}
|
||||
name="jwt"
|
||||
autoComplete="off"
|
||||
disabled={loginSending}
|
||||
placeholder={"Enter JWT"}
|
||||
variant={"outlined"}
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<LockIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.submitContainer}>
|
||||
<Button
|
||||
variant="callAction"
|
||||
id="do-login"
|
||||
disabled={jwt === "" || loginSending}
|
||||
label={"Login"}
|
||||
fullWidth
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.linearPredef}>
|
||||
{loginSending && <LinearProgress />}
|
||||
</Grid>
|
||||
</form>
|
||||
</Fragment>
|
||||
);
|
||||
loginComponent = <StrategyForm redirectRules={redirectItems} />;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -483,16 +334,6 @@ const Login = () => {
|
||||
);
|
||||
}
|
||||
|
||||
let modeLogo: "console" | "directpv" | "operator" | "kes" | "subnet" =
|
||||
"console";
|
||||
const logoVar = getLogoVar();
|
||||
|
||||
if (isDirectPV) {
|
||||
modeLogo = "directpv";
|
||||
} else if (isOperator) {
|
||||
modeLogo = "operator";
|
||||
}
|
||||
|
||||
let docsURL = "https://min.io/docs/minio/linux/index.html?ref=con";
|
||||
if (isK8S) {
|
||||
docsURL =
|
||||
@@ -503,7 +344,7 @@ const Login = () => {
|
||||
<Fragment>
|
||||
<MainError />
|
||||
<LoginWrapper
|
||||
logoProps={{ applicationName: modeLogo, subVariant: logoVar }}
|
||||
logoProps={{ applicationName: "console", subVariant: getLogoVar() }}
|
||||
form={loginComponent}
|
||||
formFooter={
|
||||
<Fragment>
|
||||
|
||||
@@ -15,18 +15,31 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import Grid from "@mui/material/Grid";
|
||||
import React, { Fragment } from "react";
|
||||
import { Button, LockFilledIcon, PasswordKeyIcon, UserFilledIcon } from "mds";
|
||||
import React from "react";
|
||||
import {
|
||||
Button,
|
||||
LockFilledIcon,
|
||||
LogoutIcon,
|
||||
PasswordKeyIcon,
|
||||
UserFilledIcon,
|
||||
} from "mds";
|
||||
import { setAccessKey, setSecretKey, setSTS, setUseSTS } from "./loginSlice";
|
||||
import { Box, InputAdornment, LinearProgress } from "@mui/material";
|
||||
import {
|
||||
InputAdornment,
|
||||
LinearProgress,
|
||||
MenuItem,
|
||||
Select,
|
||||
SelectChangeEvent,
|
||||
} from "@mui/material";
|
||||
import { AppState, useAppDispatch } from "../../store";
|
||||
import { useSelector } from "react-redux";
|
||||
import { LoginField } from "./LoginField";
|
||||
import makeStyles from "@mui/styles/makeStyles";
|
||||
import { Theme, useTheme } from "@mui/material/styles";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import { spacingUtils } from "../Console/Common/FormComponents/common/styleLibrary";
|
||||
import { doLoginAsync } from "./loginThunks";
|
||||
import { IStrategyForm } from "./types";
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -59,10 +72,9 @@ const useStyles = makeStyles((theme: Theme) =>
|
||||
})
|
||||
);
|
||||
|
||||
const StrategyForm = () => {
|
||||
const StrategyForm = ({ redirectRules }: IStrategyForm) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const classes = useStyles();
|
||||
const theme = useTheme();
|
||||
|
||||
const accessKey = useSelector((state: AppState) => state.login.accessKey);
|
||||
const secretKey = useSelector((state: AppState) => state.login.secretKey);
|
||||
@@ -78,6 +90,42 @@ const StrategyForm = () => {
|
||||
dispatch(doLoginAsync());
|
||||
};
|
||||
|
||||
let ssoOptions: React.ReactNode = null;
|
||||
|
||||
if (redirectRules.length > 0) {
|
||||
ssoOptions = redirectRules.map((r, idx) => (
|
||||
<MenuItem
|
||||
value={r.redirect}
|
||||
key={`sso-login-option-${idx}`}
|
||||
className={classes.ssoMenuItem}
|
||||
divider={true}
|
||||
>
|
||||
<LogoutIcon
|
||||
className={classes.ssoLoginIcon}
|
||||
style={{ width: 16, height: 16, marginRight: 8 }}
|
||||
/>
|
||||
{r.displayName}
|
||||
{r.serviceType ? ` - ${r.serviceType}` : ""}
|
||||
</MenuItem>
|
||||
));
|
||||
}
|
||||
|
||||
const extraActionSelector = (e: SelectChangeEvent) => {
|
||||
const value = e.target.value;
|
||||
|
||||
if (value) {
|
||||
console.log(value);
|
||||
if (value.includes("use-sts")) {
|
||||
console.log("si");
|
||||
dispatch(setUseSTS(!useSTS));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
window.location.href = e.target.value as string;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<form className={classes.form} noValidate onSubmit={formSubmit}>
|
||||
@@ -184,42 +232,32 @@ const StrategyForm = () => {
|
||||
<Grid item xs={12} className={classes.linearPredef}>
|
||||
{loginSending && <LinearProgress />}
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.linearPredef}>
|
||||
<Box
|
||||
style={{
|
||||
textAlign: "center",
|
||||
marginTop: 20,
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
className={classes.linearPredef}
|
||||
sx={{ marginTop: "16px" }}
|
||||
>
|
||||
<Select
|
||||
id="alternativeMethods"
|
||||
name="alternativeMethods"
|
||||
onChange={extraActionSelector}
|
||||
displayEmpty
|
||||
className={classes.ssoSelect}
|
||||
renderValue={() => "Other Authentication Methods"}
|
||||
sx={{
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
onClick={() => {
|
||||
dispatch(setUseSTS(!useSTS));
|
||||
}}
|
||||
style={{
|
||||
color: theme.colors.link,
|
||||
font: "normal normal normal 14px Inter",
|
||||
textDecoration: "underline",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
<MenuItem
|
||||
value={useSTS ? "use-sts-cred" : "use-sts"}
|
||||
className={classes.ssoMenuItem}
|
||||
divider={redirectRules.length > 0}
|
||||
>
|
||||
{!useSTS && <Fragment>Use STS</Fragment>}
|
||||
{useSTS && <Fragment>Use Credentials</Fragment>}
|
||||
</span>
|
||||
<span
|
||||
onClick={() => {
|
||||
dispatch(setUseSTS(!useSTS));
|
||||
}}
|
||||
style={{
|
||||
color: theme.colors.link,
|
||||
font: "normal normal normal 12px/15px Inter",
|
||||
textDecoration: "none",
|
||||
fontWeight: "bold",
|
||||
paddingLeft: 4,
|
||||
}}
|
||||
>
|
||||
➔
|
||||
</span>
|
||||
</Box>
|
||||
{useSTS ? "Use Credentials" : "Use STS"}
|
||||
</MenuItem>
|
||||
{ssoOptions}
|
||||
</Select>
|
||||
</Grid>
|
||||
</form>
|
||||
</React.Fragment>
|
||||
|
||||
@@ -28,8 +28,6 @@ export interface LoginState {
|
||||
sts: string;
|
||||
useSTS: boolean;
|
||||
|
||||
jwt: string;
|
||||
|
||||
loginStrategy: ILoginDetails;
|
||||
|
||||
loginSending: boolean;
|
||||
@@ -48,7 +46,6 @@ const initialState: LoginState = {
|
||||
secretKey: "",
|
||||
sts: "",
|
||||
useSTS: false,
|
||||
jwt: "",
|
||||
loginStrategy: {
|
||||
loginStrategy: loginStrategyType.unknown,
|
||||
redirectRules: [],
|
||||
@@ -79,9 +76,6 @@ export const loginSlice = createSlice({
|
||||
setSTS: (state, action: PayloadAction<string>) => {
|
||||
state.sts = action.payload;
|
||||
},
|
||||
setJwt: (state, action: PayloadAction<string>) => {
|
||||
state.jwt = action.payload;
|
||||
},
|
||||
setNavigateTo: (state, action: PayloadAction<string>) => {
|
||||
state.navigateTo = action.payload;
|
||||
},
|
||||
@@ -133,7 +127,6 @@ export const {
|
||||
setSecretKey,
|
||||
setUseSTS,
|
||||
setSTS,
|
||||
setJwt,
|
||||
setNavigateTo,
|
||||
resetForm,
|
||||
} = loginSlice.actions;
|
||||
|
||||
@@ -18,75 +18,41 @@ import { createAsyncThunk } from "@reduxjs/toolkit";
|
||||
import { AppState } from "../../store";
|
||||
import api from "../../common/api";
|
||||
import { ErrorResponseHandler } from "../../common/types";
|
||||
import {
|
||||
setErrorSnackMessage,
|
||||
showMarketplace,
|
||||
userLogged,
|
||||
} from "../../systemSlice";
|
||||
import { ILoginDetails, loginStrategyType } from "./types";
|
||||
import { setErrorSnackMessage, userLogged } from "../../systemSlice";
|
||||
import { ILoginDetails } from "./types";
|
||||
import { setNavigateTo } from "./loginSlice";
|
||||
import {
|
||||
getTargetPath,
|
||||
loginStrategyEndpoints,
|
||||
LoginStrategyPayload,
|
||||
} from "./LoginPage";
|
||||
import { getTargetPath, LoginStrategyPayload } from "./LoginPage";
|
||||
|
||||
export const doLoginAsync = createAsyncThunk(
|
||||
"login/doLoginAsync",
|
||||
async (_, { getState, rejectWithValue, dispatch }) => {
|
||||
const state = getState() as AppState;
|
||||
const loginStrategy = state.login.loginStrategy;
|
||||
const accessKey = state.login.accessKey;
|
||||
const secretKey = state.login.secretKey;
|
||||
const jwt = state.login.jwt;
|
||||
const sts = state.login.sts;
|
||||
const useSTS = state.login.useSTS;
|
||||
|
||||
const isOperator =
|
||||
loginStrategy.loginStrategy === loginStrategyType.serviceAccount ||
|
||||
loginStrategy.loginStrategy === loginStrategyType.redirectServiceAccount;
|
||||
|
||||
let loginStrategyPayload: LoginStrategyPayload = {
|
||||
form: { accessKey, secretKey },
|
||||
"service-account": { jwt },
|
||||
accessKey,
|
||||
secretKey,
|
||||
};
|
||||
if (useSTS) {
|
||||
loginStrategyPayload = {
|
||||
form: { accessKey, secretKey, sts },
|
||||
accessKey,
|
||||
secretKey,
|
||||
sts,
|
||||
};
|
||||
}
|
||||
|
||||
console.log("PAYLOAD:", loginStrategyPayload);
|
||||
|
||||
return api
|
||||
.invoke(
|
||||
"POST",
|
||||
loginStrategyEndpoints[loginStrategy.loginStrategy] || "/api/v1/login",
|
||||
loginStrategyPayload[loginStrategy.loginStrategy]
|
||||
)
|
||||
.invoke("POST", "/api/v1/login", loginStrategyPayload)
|
||||
.then((res) => {
|
||||
// We set the state in redux
|
||||
dispatch(userLogged(true));
|
||||
if (loginStrategy.loginStrategy === loginStrategyType.form) {
|
||||
localStorage.setItem("userLoggedIn", accessKey);
|
||||
}
|
||||
// if it's in operator mode, check the Marketplace integration
|
||||
if (isOperator) {
|
||||
api
|
||||
.invoke("GET", "/api/v1/mp-integration/")
|
||||
.then((res: any) => {
|
||||
dispatch(setNavigateTo(getTargetPath())); // Email already set, continue with normal flow
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
if (err.statusCode === 404) {
|
||||
dispatch(showMarketplace(true));
|
||||
dispatch(setNavigateTo("/marketplace"));
|
||||
} else {
|
||||
// Unexpected error, continue with normal flow
|
||||
dispatch(setNavigateTo(getTargetPath()));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
dispatch(setNavigateTo(getTargetPath()));
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
dispatch(setErrorSnackMessage(err));
|
||||
@@ -124,23 +90,7 @@ export const getVersionAsync = createAsyncThunk(
|
||||
}
|
||||
)
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
// try the operator version
|
||||
api
|
||||
.invoke("GET", "/api/v1/check-operator-version")
|
||||
.then(
|
||||
({
|
||||
current_version,
|
||||
latest_version,
|
||||
}: {
|
||||
current_version: string;
|
||||
latest_version: string;
|
||||
}) => {
|
||||
return latest_version;
|
||||
}
|
||||
)
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
return err;
|
||||
});
|
||||
return err.errorMessage;
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
@@ -24,6 +24,11 @@ export interface ILoginDetails {
|
||||
export interface redirectRule {
|
||||
redirect: string;
|
||||
displayName: string;
|
||||
serviceType?: string;
|
||||
}
|
||||
|
||||
export interface IStrategyForm {
|
||||
redirectRules: redirectRule[];
|
||||
}
|
||||
|
||||
export enum loginStrategyType {
|
||||
|
||||
@@ -7592,6 +7592,9 @@ func init() {
|
||||
},
|
||||
"redirect": {
|
||||
"type": "string"
|
||||
},
|
||||
"serviceType": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -16573,6 +16576,9 @@ func init() {
|
||||
},
|
||||
"redirect": {
|
||||
"type": "string"
|
||||
},
|
||||
"serviceType": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -20,7 +20,9 @@ import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
@@ -186,15 +188,25 @@ func getLoginDetailsResponse(params authApi.LoginDetailParams, openIDProviders o
|
||||
Client: oauth2Client,
|
||||
}
|
||||
|
||||
displayName := "Login with SSO"
|
||||
displayName := fmt.Sprintf("Login with SSO (%s)", name)
|
||||
serviceType := ""
|
||||
|
||||
if provider.DisplayName != "" {
|
||||
displayName = provider.DisplayName
|
||||
}
|
||||
|
||||
if provider.RoleArn != "" {
|
||||
splitRoleArn := strings.Split(provider.RoleArn, ":")
|
||||
|
||||
if len(splitRoleArn) > 2 {
|
||||
serviceType = splitRoleArn[2]
|
||||
}
|
||||
}
|
||||
|
||||
redirectRule := models.RedirectRule{
|
||||
Redirect: identityProvider.GenerateLoginURL(),
|
||||
DisplayName: displayName,
|
||||
ServiceType: serviceType,
|
||||
}
|
||||
|
||||
redirectRules = append(redirectRules, &redirectRule)
|
||||
|
||||
@@ -5891,6 +5891,8 @@ definitions:
|
||||
type: string
|
||||
displayName:
|
||||
type: string
|
||||
serviceType:
|
||||
type: string
|
||||
|
||||
idpServerConfiguration:
|
||||
type: object
|
||||
|
||||
Reference in New Issue
Block a user