Merge pull request #1512 from LalitDeore/sso_2.0.0

sso and billing cards changes
This commit is contained in:
Frikky
2024-10-11 17:13:11 +02:00
committed by GitHub
4 changed files with 566 additions and 449 deletions
+3 -3
View File
@@ -1131,9 +1131,9 @@ func checkAdminLogin(resp http.ResponseWriter, request *http.Request) {
} }
// No childorg setup, only parent org // No childorg setup, only parent org
if len(org.ManagerOrgs) > 0 || len(org.CreatorOrg) > 0 { // if len(org.ManagerOrgs) > 0 || len(org.CreatorOrg) > 0 {
continue // continue
} // }
// Should run calculations // Should run calculations
if len(org.SSOConfig.OpenIdAuthorization) > 0 { if len(org.SSOConfig.OpenIdAuthorization) > 0 {
+24 -11
View File
@@ -298,17 +298,21 @@ const Header = (props) => {
if (response.status !== 200) { if (response.status !== 200) {
console.log("Error in response"); console.log("Error in response");
} else { } else {
localStorage.removeItem("apps") localStorage.removeItem("apps");
localStorage.removeItem("workflows") localStorage.removeItem("workflows");
localStorage.removeItem("userinfo") localStorage.removeItem("userinfo");
} }
return response.json(); return response.json();
}) })
.then(function (responseJson) { .then(function (responseJson) {
console.log("In here?") console.log("In here?");
if (responseJson.success === true) { if (responseJson.success === true) {
if (responseJson.region_url !== undefined && responseJson.region_url !== null && responseJson.region_url.length > 0) { if (
responseJson.region_url !== undefined &&
responseJson.region_url !== null &&
responseJson.region_url.length > 0
) {
console.log("Region Change: ", responseJson.region_url); console.log("Region Change: ", responseJson.region_url);
localStorage.setItem("globalUrl", responseJson.region_url); localStorage.setItem("globalUrl", responseJson.region_url);
//globalUrl = responseJson.region_url //globalUrl = responseJson.region_url
@@ -317,20 +321,29 @@ const Header = (props) => {
if (responseJson["reason"] === "SSO_REDIRECT") { if (responseJson["reason"] === "SSO_REDIRECT") {
toast.info("Redirecting to SSO login page as SSO is required for this organization.") toast.info("Redirecting to SSO login page as SSO is required for this organization.")
setTimeout(() => { setTimeout(() => {
window.location.href = responseJson["url"] toast.info(
return "Redirecting to SSO login page as SSO is required for this organization."
}, 2000) );
window.location.href = responseJson["url"];
return;
}, 2000);
} else { } else {
toast("Successfully changed active organization - refreshing!"); toast("Successfully changed active organization - refreshing!");
setTimeout(() => { setTimeout(() => {
window.location.reload() window.location.reload();
}, 2000); }, 2000);
} }
} else { } else {
if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason.length > 0) { if (
responseJson.reason !== undefined &&
responseJson.reason !== null &&
responseJson.reason.length > 0
) {
toast(responseJson.reason); toast(responseJson.reason);
} else { } else {
toast("Failed changing org. Try again or contact support@shuffler.io if this persists."); toast(
"Failed changing org. Try again or contact support@shuffler.io if this persists."
);
} }
} }
}) })
+538 -434
View File
@@ -319,9 +319,71 @@ const OrgHeaderexpanded = (props) => {
</Tooltip> </Tooltip>
); );
const toggleBetweenRequiredOrOptional = (event) => { const toggleBetweenRequiredOrOptional = (event) => {
setSSORequired(event.target.checked); if (
}; ssoEntrypoint === "" &&
openidAuthorization === "" &&
openidToken === ""
) {
if (!SSORequired) {
toast.error(
"Please fill in fields for either OpenID connect or SSO before continuing. "
);
return;
}
} else {
toast.info("Toggled SSO. Remember to save.");
}
setSSORequired(event.target.checked);
};
const HandleTestSSO = () => {
const url = `${globalUrl}/api/v1/orgs/${selectedOrganization?.id}/change`;
const data = {
org_id: selectedOrganization?.id,
sso_test: true,
};
fetch(url, {
mode: "cors",
credentials: "include",
crossDomain: true,
method: "POST",
body: JSON.stringify(data),
withCredentials: true,
headers: {
"Content-Type": "application/json; charset=utf-8",
},
})
.then((response) => {
if (response.status !== 200) {
toast.error(
"Failed to test sso. Please try again later or contact support@shuffler.io if issue persist."
);
return;
}
return response.json();
})
.then((responjson) => {
if (responjson["reason"] === "SSO_REDIRECT") {
setTimeout(() => {
toast.info(
"Redirecting to SSO login page as SSO is required for this organization."
);
window.location.href = responjson["url"];
return;
}, 2000);
} else {
toast.error(
"No SSO found for this org. Please set up sso for this org."
);
}
})
.catch((err) => {
console.log("error for sso test is: ", err);
});
};
const HandleTestSSO = () => { const HandleTestSSO = () => {
@@ -702,434 +764,476 @@ const OrgHeaderexpanded = (props) => {
SSO Configuration SSO Configuration
</Typography> </Typography>
<div style={{ display: 'flex', flexDirection: 'column', marginLeft: 10, width: '100%', }}> <div
<Typography variant="body2" color="textSecondary" style={{ margin: '5px 0px 5px 10px' }}>Make SAML SSO or OpenID Authentication Required or Optional for Your Organization.</Typography> style={{
<div> display: "flex",
<Switch flexDirection: "column",
checked={SSORequired} marginLeft: 10,
onChange={toggleBetweenRequiredOrOptional} width: "100%",
name="onOffSwitch" }}
color="primary" >
title="Make SAML SSO or OpenID Authentication Required or Optional for Your Organization" <Typography
/> variant="body2"
{SSORequired ? 'Required' : 'Optional'} color="textSecondary"
</div> style={{ margin: "5px 0px 5px 10px" }}
</div> >
<div style={{ display: 'flex', flexDirection: 'column', marginTop: 20, marginLeft: 10, width: "100%", borderBottom: '1px solid #414347', paddingBottom: 10 }}> Make SAML SSO or OpenID Authentication Required or Optional for Your
<Typography variant="body1" style={{ margin: '5px 0px 5px 10px' }}> Organization.
You can test your SSO configuration by clicking the button below. Before testing, ensure you have set Open ID Connect or SAML SSO credentials. </Typography>
</Typography> <div>
<Tooltip <Switch
title={ checked={SSORequired}
!( onChange={toggleBetweenRequiredOrOptional}
ssoEntrypoint.length > 0 || name="onOffSwitch"
ssoCertificate.length > 0 || color="primary"
openidAuthorization.length > 0 || title="Make SAML SSO or OpenID Authentication Required or Optional for Your Organization"
openidClientId.length > 0 />
) {SSORequired ? "Required" : "Optional"}
? "Please ensure all SSO credentials are set before testing." </div>
: "" </div>
} <div
> style={{
<span style={{ width: 100 }}> display: "flex",
<Button flexDirection: "column",
variant="outlined" marginTop: 20,
color="primary" marginLeft: 10,
style={{ width: 100, textTransform: 'none', margin: 10 }} width: "100%",
disabled={ borderBottom: "1px solid #414347",
!( paddingBottom: 10,
ssoEntrypoint.length > 0 || }}
ssoCertificate.length > 0 || >
openidAuthorization.length > 0 || <Typography variant="body1" style={{ margin: "5px 0px 5px 10px" }}>
openidClientId.length > 0 You can test your SSO configuration by clicking the button below.
) Before testing, ensure you have set Open ID Connect or SAML SSO
} credentials.
onClick={HandleTestSSO} </Typography>
> <Tooltip
Test SSO title={
</Button> !(
</span> ssoEntrypoint.length > 0 ||
</Tooltip> ssoCertificate.length > 0 ||
</div> openidAuthorization.length > 0 ||
<Grid item xs={12} style={{}}> openidClientId.length > 0
<Typography variant="h6" style={{ textAlign: "center", }}>OpenID connect</Typography> )
<Grid container style={{ marginTop: 10, }}> ? "Please ensure all SSO credentials are set before testing."
<Grid item xs={6} style={{}}> : ""
<span> }
<Typography>Client ID</Typography> >
<TextField <span style={{ width: 100 }}>
required <Button
style={{ variant="outlined"
flex: "1", color="primary"
marginTop: "5px", style={{ width: 100, textTransform: "none", margin: 10 }}
marginRight: "15px", disabled={
backgroundColor: theme.palette.inputColor, !(
}} ssoEntrypoint.length > 0 ||
fullWidth={true} ssoCertificate.length > 0 ||
type="name" openidAuthorization.length > 0 ||
multiline={true} openidClientId.length > 0
rows={2} )
disabled={ }
selectedOrganization.manager_orgs !== undefined && onClick={HandleTestSSO}
selectedOrganization.manager_orgs !== null && >
selectedOrganization.manager_orgs.length > 0 Test SSO
} </Button>
id="outlined-with-placeholder" </span>
margin="normal" </Tooltip>
variant="outlined" </div>
placeholder="The OpenID client ID from the identity provider" <div></div>
value={openidClientId} <Grid item xs={12} style={{}}>
onChange={(e) => { <Typography variant="h6" style={{ textAlign: "center" }}>
setOpenidClientId(e.target.value); OpenID connect
}} </Typography>
InputProps={{ <Grid container style={{ marginTop: 10 }}>
classes: { <Grid item xs={6} style={{}}>
notchedOutline: classes.notchedOutline, <span>
}, <Typography>Client ID</Typography>
style: { <TextField
color: "white", required
}, style={{
}} flex: "1",
/> marginTop: "5px",
</span> marginRight: "15px",
</Grid> backgroundColor: theme.palette.inputColor,
<Grid item xs={6} style={{}}> }}
<span> fullWidth={true}
<Typography>Client Secret (optional)</Typography> type="name"
<TextField multiline={true}
required rows={2}
style={{ disabled={
flex: "1", selectedOrganization.manager_orgs !== undefined &&
marginTop: "5px", selectedOrganization.manager_orgs !== null &&
marginRight: "15px", selectedOrganization.manager_orgs.length > 0
backgroundColor: theme.palette.inputColor, }
}} id="outlined-with-placeholder"
fullWidth={true} margin="normal"
type="name" variant="outlined"
multiline={true} placeholder="The OpenID client ID from the identity provider"
rows={2} value={openidClientId}
disabled={ onChange={(e) => {
selectedOrganization.manager_orgs !== undefined && setOpenidClientId(e.target.value);
selectedOrganization.manager_orgs !== null && }}
selectedOrganization.manager_orgs.length > 0 InputProps={{
} classes: {
id="outlined-with-placeholder" notchedOutline: classes.notchedOutline,
margin="normal" },
variant="outlined" style: {
placeholder="The OpenID client secret - DONT use this if dealing with implicit auth / PKCE" color: "white",
value={openidClientSecret} },
onChange={(e) => { }}
setOpenidClientSecret(e.target.value); />
}} </span>
InputProps={{ </Grid>
classes: { <Grid item xs={6} style={{}}>
notchedOutline: classes.notchedOutline, <span>
}, <Typography>Client Secret (optional)</Typography>
style: { <TextField
color: "white", required
}, style={{
}} flex: "1",
/> marginTop: "5px",
</span> marginRight: "15px",
</Grid> backgroundColor: theme.palette.inputColor,
</Grid> }}
<Grid container style={{ marginTop: 10, }}> fullWidth={true}
<Grid item xs={6} style={{}}> type="name"
<span> multiline={true}
<Typography>Authorization URL</Typography> rows={2}
<TextField disabled={
required selectedOrganization.manager_orgs !== undefined &&
style={{ selectedOrganization.manager_orgs !== null &&
flex: "1", selectedOrganization.manager_orgs.length > 0
marginTop: "5px", }
marginRight: "15px", id="outlined-with-placeholder"
backgroundColor: theme.palette.inputColor, margin="normal"
}} variant="outlined"
fullWidth={true} placeholder="The OpenID client secret - DONT use this if dealing with implicit auth / PKCE"
type="name" value={openidClientSecret}
id="outlined-with-placeholder" onChange={(e) => {
margin="normal" setOpenidClientSecret(e.target.value);
variant="outlined" }}
multiline={true} InputProps={{
rows={2} classes: {
placeholder="The OpenID authorization URL (usually ends with /authorize)" notchedOutline: classes.notchedOutline,
value={openidAuthorization} },
onChange={(e) => { style: {
setOpenidAuthorization(e.target.value) color: "white",
}} },
InputProps={{ }}
classes: { />
notchedOutline: classes.notchedOutline, </span>
}, </Grid>
style: { </Grid>
color: "white", <Grid container style={{ marginTop: 10 }}>
}, <Grid item xs={6} style={{}}>
}} <span>
/> <Typography>Authorization URL</Typography>
</span> <TextField
</Grid> required
<Grid item xs={6} style={{}}> style={{
<span> flex: "1",
<Typography>Token URL</Typography> marginTop: "5px",
<TextField marginRight: "15px",
required backgroundColor: theme.palette.inputColor,
style={{ }}
flex: "1", fullWidth={true}
marginTop: "5px", type="name"
marginRight: "15px", id="outlined-with-placeholder"
backgroundColor: theme.palette.inputColor, margin="normal"
}} variant="outlined"
fullWidth={true} multiline={true}
type="name" rows={2}
id="outlined-with-placeholder" placeholder="The OpenID authorization URL (usually ends with /authorize)"
margin="normal" value={openidAuthorization}
variant="outlined" onChange={(e) => {
multiline={true} setOpenidAuthorization(e.target.value);
rows={2} }}
placeholder="The OpenID token URL (usually ends with /token)" InputProps={{
value={openidToken} classes: {
onChange={(e) => { notchedOutline: classes.notchedOutline,
setOpenidToken(e.target.value) },
}} style: {
InputProps={{ color: "white",
classes: { },
notchedOutline: classes.notchedOutline, }}
}, />
style: { </span>
color: "white", </Grid>
}, <Grid item xs={6} style={{}}>
}} <span>
/> <Typography>Token URL</Typography>
</span> <TextField
</Grid> required
</Grid> style={{
</Grid> flex: "1",
{/* } */} marginTop: "5px",
{/*isCloud ? null : */} marginRight: "15px",
<Grid item xs={12} style={{ marginTop: 50, }}> backgroundColor: theme.palette.inputColor,
<Typography variant="h6" style={{ textAlign: "center", }}>SAML SSO (v1.1)</Typography> }}
<Grid container style={{ marginTop: 20, }}> fullWidth={true}
<Grid item xs={6} style={{}}> type="name"
<span> id="outlined-with-placeholder"
<Typography>SSO Entrypoint (IdP)</Typography> margin="normal"
<TextField variant="outlined"
required multiline={true}
style={{ rows={2}
flex: "1", placeholder="The OpenID token URL (usually ends with /token)"
marginTop: "5px", value={openidToken}
marginRight: "15px", onChange={(e) => {
backgroundColor: theme.palette.inputColor, setOpenidToken(e.target.value);
}} }}
fullWidth={true} InputProps={{
type="name" classes: {
multiline={true} notchedOutline: classes.notchedOutline,
rows={2} },
disabled={ style: {
selectedOrganization.manager_orgs !== undefined && color: "white",
selectedOrganization.manager_orgs !== null && },
selectedOrganization.manager_orgs.length > 0 }}
} />
id="outlined-with-placeholder" </span>
margin="normal" </Grid>
variant="outlined" </Grid>
placeholder="The entrypoint URL from your provider" </Grid>
value={ssoEntrypoint} {/* } */}
onChange={(e) => { {/*isCloud ? null : */}
setSsoEntrypoint(e.target.value); <Grid item xs={12} style={{ marginTop: 50 }}>
}} <Typography variant="h6" style={{ textAlign: "center" }}>
InputProps={{ SAML SSO (v1.1)
classes: { </Typography>
notchedOutline: classes.notchedOutline, <Grid container style={{ marginTop: 20 }}>
}, <Grid item xs={6} style={{}}>
style: { <span>
color: "white", <Typography>SSO Entrypoint (IdP)</Typography>
}, <TextField
}} required
/> style={{
</span> flex: "1",
</Grid> marginTop: "5px",
<Grid item xs={6} style={{}}> marginRight: "15px",
<span> backgroundColor: theme.palette.inputColor,
<Typography>SSO Certificate (X509)</Typography> }}
<TextField fullWidth={true}
required type="name"
style={{ multiline={true}
flex: "1", rows={2}
marginTop: "5px", disabled={
marginRight: "15px", selectedOrganization.manager_orgs !== undefined &&
backgroundColor: theme.palette.inputColor, selectedOrganization.manager_orgs !== null &&
}} selectedOrganization.manager_orgs.length > 0
fullWidth={true} }
type="name" id="outlined-with-placeholder"
id="outlined-with-placeholder" margin="normal"
margin="normal" variant="outlined"
variant="outlined" placeholder="The entrypoint URL from your provider"
multiline={true} value={ssoEntrypoint}
rows={2} onChange={(e) => {
placeholder="The X509 certificate to use" setSsoEntrypoint(e.target.value);
value={ssoCertificate} }}
onChange={(e) => { InputProps={{
setSsoCertificate(e.target.value); classes: {
}} notchedOutline: classes.notchedOutline,
InputProps={{ },
classes: { style: {
notchedOutline: classes.notchedOutline, color: "white",
}, },
style: { }}
color: "white", />
}, </span>
}} </Grid>
/> <Grid item xs={6} style={{}}>
</span> <span>
</Grid> <Typography>SSO Certificate (X509)</Typography>
</Grid> <TextField
{isCloud ? required
<Typography variant="body2" style={{ textAlign: "left", }} color="textSecondary"> style={{
IdP URL for Shuffle: https://shuffler.io/api/v1/login_sso flex: "1",
</Typography> marginTop: "5px",
: null} marginRight: "15px",
</Grid> backgroundColor: theme.palette.inputColor,
{isCloud ? null : ( }}
<Grid item xs={6} style={{}}> fullWidth={true}
<span> type="name"
<Typography>App Download URL</Typography> id="outlined-with-placeholder"
<TextField margin="normal"
required variant="outlined"
style={{ multiline={true}
flex: "1", rows={2}
marginTop: "5px", placeholder="The X509 certificate to use"
marginRight: "15px", value={ssoCertificate}
backgroundColor: theme.palette.inputColor, onChange={(e) => {
}} setSsoCertificate(e.target.value);
fullWidth={true} }}
type="name" InputProps={{
id="outlined-with-placeholder" classes: {
margin="normal" notchedOutline: classes.notchedOutline,
variant="outlined" },
placeholder="A description for the organization" style: {
value={appDownloadUrl} color: "white",
onChange={(e) => { },
setAppDownloadUrl(e.target.value); }}
}} />
InputProps={{ </span>
classes: { </Grid>
notchedOutline: classes.notchedOutline, </Grid>
}, {isCloud ? (
style: { <Typography
color: "white", variant="body2"
}, style={{ textAlign: "left" }}
}} color="textSecondary"
/> >
</span> IdP URL for Shuffle: https://shuffler.io/api/v1/login_sso
</Grid> </Typography>
)} ) : null}
{isCloud ? null : ( </Grid>
<Grid item xs={6} style={{}}> {isCloud ? null : (
<span> <Grid item xs={6} style={{}}>
<Typography>App Download Branch</Typography> <span>
<TextField <Typography>App Download URL</Typography>
required <TextField
style={{ required
flex: "1", style={{
marginTop: "5px", flex: "1",
marginRight: "15px", marginTop: "5px",
backgroundColor: theme.palette.inputColor, marginRight: "15px",
}} backgroundColor: theme.palette.inputColor,
fullWidth={true} }}
type="name" fullWidth={true}
id="outlined-with-placeholder" type="name"
margin="normal" id="outlined-with-placeholder"
variant="outlined" margin="normal"
placeholder="A description for the organization" variant="outlined"
value={appDownloadBranch} placeholder="A description for the organization"
onChange={(e) => { value={appDownloadUrl}
setAppDownloadBranch(e.target.value); onChange={(e) => {
}} setAppDownloadUrl(e.target.value);
InputProps={{ }}
classes: { InputProps={{
notchedOutline: classes.notchedOutline, classes: {
}, notchedOutline: classes.notchedOutline,
style: { },
color: "white", style: {
}, color: "white",
}} },
/> }}
</span> />
</Grid> </span>
)} </Grid>
{isCloud ? null : ( )}
<Grid item xs={6} style={{}}> {isCloud ? null : (
<span> <Grid item xs={6} style={{}}>
<Typography>Workflow Download URL</Typography> <span>
<TextField <Typography>App Download Branch</Typography>
required <TextField
style={{ required
flex: "1", style={{
marginTop: "5px", flex: "1",
marginRight: "15px", marginTop: "5px",
backgroundColor: theme.palette.inputColor, marginRight: "15px",
}} backgroundColor: theme.palette.inputColor,
fullWidth={true} }}
type="name" fullWidth={true}
id="outlined-with-placeholder" type="name"
margin="normal" id="outlined-with-placeholder"
variant="outlined" margin="normal"
placeholder="A description for the organization" variant="outlined"
value={workflowDownloadUrl} placeholder="A description for the organization"
onChange={(e) => { value={appDownloadBranch}
setWorkflowDownloadUrl(e.target.value); onChange={(e) => {
}} setAppDownloadBranch(e.target.value);
InputProps={{ }}
classes: { InputProps={{
notchedOutline: classes.notchedOutline, classes: {
}, notchedOutline: classes.notchedOutline,
style: { },
color: "white", style: {
}, color: "white",
}} },
/> }}
</span> />
</Grid> </span>
)} </Grid>
{isCloud ? null : ( )}
<Grid item xs={6} style={{}}> {isCloud ? null : (
<span> <Grid item xs={6} style={{}}>
<Typography>Workflow Download Branch</Typography> <span>
<TextField <Typography>Workflow Download URL</Typography>
required <TextField
style={{ required
flex: "1", style={{
marginTop: "5px", flex: "1",
marginRight: "15px", marginTop: "5px",
backgroundColor: theme.palette.inputColor, marginRight: "15px",
}} backgroundColor: theme.palette.inputColor,
fullWidth={true} }}
type="name" fullWidth={true}
id="outlined-with-placeholder" type="name"
margin="normal" id="outlined-with-placeholder"
variant="outlined" margin="normal"
placeholder="A description for the organization" variant="outlined"
value={workflowDownloadBranch} placeholder="A description for the organization"
onChange={(e) => { value={workflowDownloadUrl}
setWorkflowDownloadBranch(e.target.value); onChange={(e) => {
}} setWorkflowDownloadUrl(e.target.value);
InputProps={{ }}
classes: { InputProps={{
notchedOutline: classes.notchedOutline, classes: {
}, notchedOutline: classes.notchedOutline,
style: { },
color: "white", style: {
}, color: "white",
}} },
/> }}
</span> />
</Grid> </span>
)} </Grid>
)}
{isCloud ? null : (
<Grid item xs={6} style={{}}>
<span>
<Typography>Workflow Download Branch</Typography>
<TextField
required
style={{
flex: "1",
marginTop: "5px",
marginRight: "15px",
backgroundColor: theme.palette.inputColor,
}}
fullWidth={true}
type="name"
id="outlined-with-placeholder"
margin="normal"
variant="outlined"
placeholder="A description for the organization"
value={workflowDownloadBranch}
onChange={(e) => {
setWorkflowDownloadBranch(e.target.value);
}}
InputProps={{
classes: {
notchedOutline: classes.notchedOutline,
},
style: {
color: "white",
},
}}
/>
</span>
</Grid>
)}
<div style={{ margin: "auto", textalign: "center", marginTop: 15, marginBottom: 15, }}> <div
{orgSaveButton} style={{
</div> margin: "auto",
{/* textalign: "center",
marginTop: 15,
marginBottom: 15,
}}
>
{orgSaveButton}
</div>
{/*
<span style={{textAlign: "center"}}> <span style={{textAlign: "center"}}>
{expanded ? {expanded ?
<ExpandLessIcon /> <ExpandLessIcon />
@@ -1138,9 +1242,9 @@ const OrgHeaderexpanded = (props) => {
} }
</span> </span>
*/} */}
</Grid> </Grid>
</div> </div>
) );
} };
export default OrgHeaderexpanded; export default OrgHeaderexpanded;