1160 lines
36 KiB
React
Executable File
1160 lines
36 KiB
React
Executable File
import React, { useRef, useState, useEffect, useLayoutEffect, useContext } from "react";
|
|
import { Context } from "../context/ContextApi.jsx";
|
|
import { toast } from 'react-toastify';
|
|
import { useParams, useNavigate, Link } from "react-router-dom";
|
|
import { getTheme } from '../theme.jsx';
|
|
import ReactGA from 'react-ga4';
|
|
//import { useAlert
|
|
|
|
import { v4 as uuidv4 } from "uuid";
|
|
import {
|
|
ListItemText,
|
|
TextField,
|
|
Drawer,
|
|
Button,
|
|
Paper,
|
|
Grid,
|
|
Tabs,
|
|
InputAdornment,
|
|
Tab,
|
|
ButtonBase,
|
|
Tooltip,
|
|
Select,
|
|
Autocomplete,
|
|
MenuItem,
|
|
Divider,
|
|
Dialog,
|
|
Modal,
|
|
DialogActions,
|
|
DialogTitle,
|
|
InputLabel,
|
|
DialogContent,
|
|
FormControl,
|
|
IconButton,
|
|
Menu,
|
|
Input,
|
|
FormGroup,
|
|
FormControlLabel,
|
|
Typography,
|
|
Checkbox,
|
|
Breadcrumbs,
|
|
CircularProgress,
|
|
Switch,
|
|
Fade,
|
|
} from "@mui/material";
|
|
|
|
import {
|
|
LockOpen as LockOpenIcon,
|
|
SupervisorAccount as SupervisorAccountIcon,
|
|
} from "@mui/icons-material";
|
|
|
|
const ITEM_HEIGHT = 55;
|
|
const ITEM_PADDING_TOP = 8;
|
|
const MenuProps = {
|
|
PaperProps: {
|
|
style: {
|
|
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
|
|
minWidth: 500,
|
|
maxWidth: 500,
|
|
scrollX: "auto",
|
|
},
|
|
},
|
|
variant: "menu",
|
|
getContentAnchorEl: null,
|
|
};
|
|
|
|
const registeredApps = [
|
|
"gmail",
|
|
"slack",
|
|
"webex",
|
|
"zoho_desk",
|
|
"outlook_graph",
|
|
"outlook_office365",
|
|
"microsoft_teams",
|
|
"microsoft_teams_user_access",
|
|
"todoist",
|
|
"google_chat",
|
|
"google_sheets",
|
|
"google_drive",
|
|
"google_disk",
|
|
"jira",
|
|
"jira_service_desk",
|
|
"jira_service_management",
|
|
"github",
|
|
]
|
|
|
|
const AuthenticationOauth2 = (props) => {
|
|
const {
|
|
saveWorkflow,
|
|
selectedApp,
|
|
workflow,
|
|
selectedAction,
|
|
authenticationType,
|
|
getAppAuthentication,
|
|
appAuthentication,
|
|
setSelectedAction,
|
|
setNewAppAuth,
|
|
isCloud,
|
|
autoAuth,
|
|
authButtonOnly,
|
|
isLoggedIn,
|
|
org_id,
|
|
setFinalized,
|
|
} = props;
|
|
|
|
let navigate = useNavigate();
|
|
//const alert = useAlert()
|
|
|
|
//const [update, setUpdate] = React.useState("|")
|
|
const [defaultConfigSet, setDefaultConfigSet] = React.useState(
|
|
authenticationType.client_id !== undefined &&
|
|
authenticationType.client_id !== null &&
|
|
authenticationType.client_id.length > 0 &&
|
|
authenticationType.client_secret !== undefined &&
|
|
authenticationType.client_secret !== null &&
|
|
authenticationType.client_secret.length > 0
|
|
);
|
|
const {themeMode, brandColor} = useContext(Context);
|
|
const theme = getTheme(themeMode, brandColor);
|
|
|
|
const [clientId, setClientId] = React.useState(defaultConfigSet ? authenticationType.client_id : "");
|
|
const [clientSecret, setClientSecret] = React.useState(defaultConfigSet ? authenticationType.client_secret : "");
|
|
|
|
const [username, setUsername] = React.useState("");
|
|
const [password, setPassword] = React.useState("");
|
|
|
|
const [oauthUrl, setOauthUrl] = React.useState("");
|
|
const [buttonClicked, setButtonClicked] = React.useState(false);
|
|
const [offlineAccess, setOfflineAccess] = React.useState(true);
|
|
|
|
const allscopes = authenticationType.scope !== undefined && authenticationType.scope !== null ? authenticationType.scope : [];
|
|
const [selectedScopes, setSelectedScopes] = React.useState(allscopes !== null && allscopes !== undefined ? allscopes.length > 0 && allscopes.length <= 3 ? allscopes : [] : [])
|
|
|
|
const [manuallyConfigure, setManuallyConfigure] = React.useState(
|
|
defaultConfigSet ? false : true
|
|
);
|
|
const [authenticationOption, setAuthenticationOptions] = React.useState({
|
|
app: JSON.parse(JSON.stringify(selectedApp)),
|
|
fields: {},
|
|
label: "",
|
|
usage: [
|
|
{
|
|
workflow_id: workflow !== undefined ? workflow.id : "",
|
|
},
|
|
],
|
|
id: uuidv4(),
|
|
active: true,
|
|
});
|
|
|
|
|
|
useEffect(() => {
|
|
if (isLoggedIn === false) {
|
|
navigate(`/login?view=${window.location.pathname}&message=Log in to authenticate this app`)
|
|
}
|
|
|
|
if (autoAuth === true && selectedApp !== undefined) {
|
|
startOauth2Request()
|
|
}
|
|
}, [])
|
|
|
|
if (selectedApp.authentication === undefined) {
|
|
return null;
|
|
}
|
|
|
|
const startOauth2Request = (admin_consent) => {
|
|
// Admin consent also means to add refresh tokens
|
|
console.log("Inside oauth2 request for app: ", selectedApp.name)
|
|
selectedApp.name = selectedApp.name.replace(" ", "_").toLowerCase()
|
|
|
|
//console.log("APP: ", selectedApp)
|
|
if (selectedApp.name.toLowerCase() == "outlook_graph" || selectedApp.name.toLowerCase() == "outlook_office365") {
|
|
handleOauth2Request(
|
|
"fd55c175-aa30-4fa6-b303-09a29fb3f750",
|
|
"",
|
|
"https://graph.microsoft.com",
|
|
["Mail.ReadWrite", "Mail.Send", "offline_access"],
|
|
admin_consent,
|
|
);
|
|
} else if (selectedApp.name.toLowerCase() == "gmail") {
|
|
handleOauth2Request(
|
|
"253565968129-6ke8086pkp0at16m8t95rdcsas69ngt1.apps.googleusercontent.com",
|
|
"",
|
|
"https://gmail.googleapis.com",
|
|
["https://www.googleapis.com/auth/gmail.modify",
|
|
"https://www.googleapis.com/auth/gmail.send",
|
|
"https://www.googleapis.com/auth/gmail.insert",
|
|
"https://www.googleapis.com/auth/gmail.compose",
|
|
],
|
|
admin_consent,
|
|
"select_account%20consent",
|
|
)
|
|
} else if (selectedApp.name.toLowerCase() == "zoho_desk") {
|
|
handleOauth2Request(
|
|
"1000.ZR5MHUW6B0L6W1VUENFGIATFS0TOJT",
|
|
"",
|
|
"https://desk.zoho.com",
|
|
["Desk.tickets.READ",
|
|
"Desk.tickets.UPDATE",
|
|
"Desk.tickets.DELETE",
|
|
"Desk.tickets.CREATE",
|
|
"offline_access"],
|
|
admin_consent,
|
|
)
|
|
} else if (selectedApp.name.toLowerCase() == "slack") {
|
|
handleOauth2Request(
|
|
"5155508477298.5168162485601",
|
|
"",
|
|
"https://slack.com",
|
|
["chat:write:user", "im:read", "im:write", "search:read", "usergroups:read", "usergroups:write",],
|
|
admin_consent,
|
|
)
|
|
} else if (selectedApp.name.toLowerCase() == "webex") {
|
|
handleOauth2Request(
|
|
"Cab184f3d7271f540443c79b5b79845e3387abbbdb3db4233a87ea3a5432fb3d5",
|
|
"",
|
|
"https://webexapis.com",
|
|
["spark:all"],
|
|
admin_consent,
|
|
)
|
|
} else if (selectedApp.name.toLowerCase().includes("microsoft_teams")) {
|
|
handleOauth2Request(
|
|
"31cb4c84-658e-43d5-ae84-22c9142e967a",
|
|
"",
|
|
"https://graph.microsoft.com",
|
|
["ChannelMessage.Edit", "ChannelMessage.Read.All", "ChannelMessage.Send", "Chat.Create", "Chat.ReadWrite", "Chat.Read", "offline_access", "Team.ReadBasic.All"],
|
|
admin_consent,
|
|
)
|
|
} else if (selectedApp.name.toLowerCase().includes("todoist")) {
|
|
handleOauth2Request(
|
|
"35fa3a384040470db0c8527e90a3c2eb",
|
|
"",
|
|
"https://api.todoist.com",
|
|
["task:add",],
|
|
admin_consent,
|
|
)
|
|
} else if (selectedApp.name.toLowerCase().includes("microsoft_sentinel")) {
|
|
handleOauth2Request(
|
|
"4c16e8c4-3d34-4aa1-ac94-262ea170b7f7",
|
|
"",
|
|
"https://management.azure.com",
|
|
["https://management.azure.com/user_impersonation",],
|
|
admin_consent,
|
|
)
|
|
} else if (selectedApp.name.toLowerCase().includes("microsoft_365_defender")) {
|
|
handleOauth2Request(
|
|
"4c16e8c4-3d34-4aa1-ac94-262ea170b7f7",
|
|
"",
|
|
"https://graph.microsoft.com",
|
|
["SecurityEvents.ReadWrite.All",],
|
|
admin_consent,
|
|
)
|
|
} else if (selectedApp.name.toLowerCase().includes("google_sheets")) {
|
|
handleOauth2Request(
|
|
"253565968129-mppu17aciek8slr3kpgnb37hp86dmvmb.apps.googleusercontent.com",
|
|
"",
|
|
"https://sheets.googleapis.com",
|
|
["https://www.googleapis.com/auth/spreadsheets"],
|
|
admin_consent,
|
|
"consent",
|
|
)
|
|
} else if (selectedApp.name.toLowerCase().includes("google_drive") || selectedApp.name.toLowerCase().includes("google_disk")) {
|
|
handleOauth2Request(
|
|
"253565968129-6pij4g6ojim4gpum0h9m9u3bc357qsq7.apps.googleusercontent.com",
|
|
"",
|
|
"https://www.googleapis.com",
|
|
["https://www.googleapis.com/auth/drive",],
|
|
admin_consent,
|
|
"consent",
|
|
)
|
|
} else if (selectedApp.name.toLowerCase().includes("google_chat") || selectedApp.name.toLowerCase().includes("google_hangout")) {
|
|
handleOauth2Request(
|
|
"253565968129-6pij4g6ojim4gpum0h9m9u3bc357qsq7.apps.googleusercontent.com",
|
|
"",
|
|
"https://www.googleapis.com",
|
|
["https://www.googleapis.com/auth/chat.messages",],
|
|
admin_consent,
|
|
"consent",
|
|
)
|
|
|
|
} else if (selectedApp.name.toLowerCase().includes("jira_service_desk") || selectedApp.name.toLowerCase().includes("jira") || selectedApp.name.toLowerCase().includes("jira_service_management")) {
|
|
handleOauth2Request(
|
|
"AI02egeCQh1Zskm1QAJaaR6dzjR97V2F",
|
|
"",
|
|
"https://api.atlassian.com",
|
|
["read:jira-work", "write:jira-work", "read:servicedesk:jira-service-management", "write:servicedesk:jira-service-management", "read:request:jira-service-management", "write:request:jira-service-management",],
|
|
admin_consent,
|
|
)
|
|
} else if (selectedApp.name.toLowerCase().includes("github")) {
|
|
handleOauth2Request(
|
|
"3d272b1b782b100b1e61",
|
|
"",
|
|
"https://api.github.com",
|
|
["repo","user","project","notifications",],
|
|
admin_consent,
|
|
)
|
|
} else {
|
|
console.log("No match found for: ", selectedApp.name)
|
|
}
|
|
// write:request:jira-service-management
|
|
}
|
|
|
|
|
|
const handleOauth2Request = (client_id, client_secret, oauth_url, scopes, admin_consent, prompt, skipScopeReplace) => {
|
|
|
|
if (skipScopeReplace === false || skipScopeReplace === undefined) {
|
|
|
|
console.log("Selected scopes: ", selectedScopes)
|
|
if (selectedScopes !== undefined && selectedScopes !== null && selectedScopes.length > 0) {
|
|
//toast("Using your scopes instead of the default ones")
|
|
scopes = selectedScopes
|
|
}
|
|
}
|
|
|
|
if ((authenticationType.redirect_uri === undefined || authenticationType.redirect_uri === null || authenticationType.redirect_uri.length === 0) && (authenticationType.token_uri !== undefined && authenticationType.token_uri !== null && authenticationType.token_uri.length > 0)) {
|
|
console.log("No redirect URI found, and token URI found. Assuming client credentials flow and saving directly in the database")
|
|
|
|
|
|
var tokenUri = authenticationType.token_uri;
|
|
if (oauthUrl !== undefined && oauthUrl !== null && oauthUrl.length > 0 && selectedApp !== undefined && selectedApp !== null) {
|
|
var same = false
|
|
for (var i = 0; i < selectedApp.authentication.parameters.length; i++) {
|
|
const param = selectedApp.authentication.parameters[i];
|
|
if (param.name === "url" && (param.value === oauthUrl || param.example === oauthUrl)) {
|
|
same = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if (!same) {
|
|
tokenUri = oauthUrl
|
|
}
|
|
}
|
|
|
|
// Find app.configuration=true fields in the app.paramters
|
|
var parsedFields = [{
|
|
"key": "client_id",
|
|
"value": client_id,
|
|
},
|
|
{
|
|
"key": "client_secret",
|
|
"value": client_secret,
|
|
},
|
|
{
|
|
"key": "scope",
|
|
"value": scopes.join(","),
|
|
},
|
|
{
|
|
"key": "token_uri",
|
|
"value": tokenUri,
|
|
}]
|
|
|
|
|
|
if (authenticationType.grant_type !== undefined && authenticationType.grant_type !== null && authenticationType.grant_type.length > 0) {
|
|
if (authenticationType.grant_type === "client_credentials") {
|
|
parsedFields.push({
|
|
"key": "grant_type",
|
|
"value": authenticationType.grant_type,
|
|
})
|
|
} else if (authenticationType.grant_type === "password") {
|
|
parsedFields.push({
|
|
"key": "grant_type",
|
|
"value": authenticationType.grant_type,
|
|
})
|
|
|
|
parsedFields.push({
|
|
"key": "username",
|
|
"value": username,
|
|
})
|
|
|
|
parsedFields.push({
|
|
"key": "password",
|
|
"value": password,
|
|
})
|
|
} else {
|
|
toast("Unknown grant type: " + authenticationType.grant_type)
|
|
}
|
|
}
|
|
|
|
let workflowIdNew = ""
|
|
if (workflow !== undefined && workflow !== null && workflow.id !== undefined && workflow.id !== null) {
|
|
workflowIdNew = workflow.id
|
|
}
|
|
|
|
const appAuthData = {
|
|
"label": "OAuth2 for " + selectedApp.name,
|
|
"app": {
|
|
"id": selectedApp.id,
|
|
"name": selectedApp.name,
|
|
"version": selectedApp.version,
|
|
"large_image": selectedApp.large_image,
|
|
},
|
|
"fields": parsedFields,
|
|
"type": "oauth2-app",
|
|
//"reference_workflow": workflowId,
|
|
}
|
|
|
|
if (setNewAppAuth !== undefined) {
|
|
setNewAppAuth(appAuthData, true)
|
|
} else {
|
|
console.log("setNewAppAuth is undefined")
|
|
}
|
|
|
|
// Wait 1 second, then get app auth with update
|
|
//if (getAppAuthentication !== undefined) {
|
|
// setTimeout(() => {
|
|
// getAppAuthentication(true, true, true);
|
|
// }, 1000)
|
|
//}
|
|
|
|
return
|
|
}
|
|
|
|
|
|
setButtonClicked(true);
|
|
//console.log("SCOPES: ", scopes);
|
|
|
|
client_id = client_id.trim()
|
|
client_secret = client_secret.trim()
|
|
oauth_url = oauth_url.trim()
|
|
|
|
var resources = "";
|
|
if (scopes !== undefined && (scopes !== null) & (scopes.length > 0)) {
|
|
console.log("IN scope 1")
|
|
if (offlineAccess === true && !scopes.includes("offline_access")) {
|
|
|
|
console.log("IN scope 2")
|
|
if (!authenticationType.redirect_uri.includes("google") && !authenticationType.redirect_uri.includes("slack")) {
|
|
console.log("Appending offline access")
|
|
scopes.push("offline_access")
|
|
}
|
|
}
|
|
|
|
resources = scopes.join(" ");
|
|
//resources = scopes.join(",");
|
|
}
|
|
|
|
const authentication_url = authenticationType.token_uri;
|
|
const redirectUri = `${window.location.protocol}//${window.location.host}/set_authentication`;
|
|
const workflowId = workflow !== undefined ? workflow.id : ""
|
|
var state = `workflow_id%3D${workflowId}%26reference_action_id%3d${selectedAction.app_id}%26app_name%3d${selectedAction.app_name}%26app_id%3d${selectedAction.app_id}%26app_version%3d${selectedAction.app_version}%26authentication_url%3d${authentication_url}%26scope%3d${resources}%26client_id%3d${client_id}%26client_secret%3d${client_secret}`;
|
|
|
|
|
|
// This is to make sure authorization can be handled WITHOUT being logged in,
|
|
// kind of making it act like an api key
|
|
// https://shuffler.io/authorization -> 3rd party integration auth
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const userAuth = urlParams.get("authorization");
|
|
if (userAuth !== undefined && userAuth !== null && userAuth.length > 0) {
|
|
console.log("Adding authorization from user side")
|
|
state += `%26authorization%3d${userAuth}`;
|
|
}
|
|
|
|
// Check for org_id
|
|
const orgId = urlParams.get("org_id");
|
|
if (orgId !== undefined && orgId !== null && orgId.length > 0) {
|
|
console.log("Adding org_id from user side")
|
|
state += `%26org_id%3d${orgId}`;
|
|
}else{
|
|
state += `%26org_id%3d${org_id}`
|
|
}
|
|
|
|
if (oauth_url !== undefined && oauth_url !== null && oauth_url.length > 0) {
|
|
state += `%26oauth_url%3d${oauth_url}`;
|
|
console.log("ADDING OAUTH2 URL: ", state);
|
|
}
|
|
|
|
|
|
if (authenticationType.refresh_uri !== undefined && authenticationType.refresh_uri !== null && authenticationType.refresh_uri.length > 0) {
|
|
state += `%26refresh_uri%3d${authenticationType.refresh_uri}`
|
|
} else {
|
|
state += `%26refresh_uri%3d${authentication_url}`
|
|
}
|
|
|
|
if (workflow?.org_id !== undefined && workflow?.org_id !== null && workflow?.org_id.length > 0) {
|
|
state += `%26org_id%3d${workflow.org_id}`
|
|
}
|
|
|
|
// FIXME: Should this be =consent?
|
|
var defaultPrompt = "login"
|
|
if (prompt !== undefined && prompt !== null && prompt.length > 0) {
|
|
defaultPrompt = prompt
|
|
}
|
|
|
|
var url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&prompt=${defaultPrompt}&scope=${resources}&state=${state}&access_type=offline`;
|
|
if (admin_consent === true) {
|
|
console.log("Running Oauth2 WITH admin consent")
|
|
//url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&prompt=consent&scope=${resources}&state=${state}&access_type=offline`;
|
|
url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&prompt=admin_consent&scope=${resources}&state=${state}&access_type=offline`;
|
|
}
|
|
|
|
if (url !== undefined && url !== null && url.length > 0) {
|
|
if (url.toLowerCase().includes("{tenant")) {
|
|
// Check location of {tenant, then find the next } and replace with 'common'. Make sure next } is AFTER {tenant
|
|
try {
|
|
const tenantIndex = url.toLowerCase().indexOf("{tenant")
|
|
const substring = url.substring(tenantIndex)
|
|
const nextBracket = substring.indexOf("}")
|
|
const newUrl = url.substring(0, tenantIndex) + "common" + url.substring(tenantIndex + nextBracket + 1)
|
|
url = newUrl
|
|
} catch (e) {
|
|
console.log("Failed to replace {tenant} with common: ", e)
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/*
|
|
console.log("OAUTH2 URL: ", url)
|
|
return
|
|
*/
|
|
|
|
|
|
// Force new consent
|
|
//const url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&scope=${resources}&prompt=consent&state=${state}&access_type=offline`;
|
|
|
|
// Admin consent
|
|
//const url = `https://accounts.zoho.com/oauth/v2/auth?response_type=code&client_id=${client_id}&scope=AaaServer.profile.Read&redirect_uri=${redirectUri}&prompt=consent`
|
|
|
|
|
|
console.log("URL: ", url)
|
|
try {
|
|
var newwin = window.open(url, "", "width=582,height=700");
|
|
|
|
// This happens if automatic clicks occurr without the user, which is
|
|
// disallowed in most cases
|
|
if (newwin === null || newwin === undefined) {
|
|
setButtonClicked(false);
|
|
if (setFinalized !== undefined) {
|
|
setFinalized(true)
|
|
}
|
|
|
|
} else {
|
|
var open = true;
|
|
const timer = setInterval(() => {
|
|
if (newwin.closed) {
|
|
console.log("Closing?")
|
|
|
|
setButtonClicked(false);
|
|
clearInterval(timer);
|
|
//alert('"Secure Payment" window closed!');
|
|
|
|
if (getAppAuthentication !== undefined) {
|
|
// This should be orgId, not action Id as to load auth properly
|
|
if (workflow !== undefined && workflow !== null && workflow.org_id !== undefined && workflow.org_id !== null && workflow.org_id.length > 0) {
|
|
getAppAuthentication(true, true, true, workflow.org_id)
|
|
} else {
|
|
getAppAuthentication(true, true, true)
|
|
}
|
|
}
|
|
|
|
toast.success("Authentication finished. You may close this window.",{
|
|
duration: 60,
|
|
})
|
|
|
|
// This is more a guess than anything
|
|
// Should be handled in getAppAuthentication()
|
|
// in the parent component to make it accurate,
|
|
// seeing as we don't know what the parent component
|
|
// wants to happen
|
|
if (setFinalized !== undefined) {
|
|
setFinalized(true)
|
|
}
|
|
} else {
|
|
//console.log("Not closed")
|
|
}
|
|
}, 2000);
|
|
//do {
|
|
// setTimeout(() => {
|
|
// console.log(newwin)
|
|
// console.log("CLOSED", newwin.closed)
|
|
// if (newwin.closed) {
|
|
|
|
// open = false
|
|
// }
|
|
// }, 1000)
|
|
//}
|
|
//while(open === true)
|
|
}
|
|
} catch (e) {
|
|
toast("Failed authentication - probably bad credentials. Try again: " + e)
|
|
|
|
setButtonClicked(false);
|
|
}
|
|
|
|
return;
|
|
};
|
|
|
|
authenticationOption.app.actions = [];
|
|
|
|
for (var key in selectedApp.authentication.parameters) {
|
|
if (
|
|
authenticationOption.fields[
|
|
selectedApp.authentication.parameters[key].name
|
|
] === undefined
|
|
) {
|
|
authenticationOption.fields[
|
|
selectedApp.authentication.parameters[key].name
|
|
] = "";
|
|
}
|
|
}
|
|
|
|
const handleSubmitCheck = () => {
|
|
if (authenticationOption.label.length === 0) {
|
|
authenticationOption.label = `Auth for ${selectedApp.name}`;
|
|
//toast("Label can't be empty")
|
|
//return
|
|
}
|
|
|
|
// Automatically mapping fields that already exist (predefined).
|
|
// Warning if fields are NOT filled
|
|
for (var key in selectedApp.authentication.parameters) {
|
|
if (
|
|
authenticationOption.fields[
|
|
selectedApp.authentication.parameters[key].name
|
|
].length === 0
|
|
) {
|
|
if (
|
|
selectedApp.authentication.parameters[key].value !== undefined &&
|
|
selectedApp.authentication.parameters[key].value !== null &&
|
|
selectedApp.authentication.parameters[key].value.length > 0
|
|
) {
|
|
authenticationOption.fields[
|
|
selectedApp.authentication.parameters[key].name
|
|
] = selectedApp.authentication.parameters[key].value;
|
|
} else {
|
|
if (
|
|
selectedApp.authentication.parameters[key].schema.type === "bool"
|
|
) {
|
|
authenticationOption.fields[
|
|
selectedApp.authentication.parameters[key].name
|
|
] = "false";
|
|
} else {
|
|
toast(
|
|
"Field " + selectedApp.authentication.parameters[key].name.replace("_basic", "", -1).replace("_", " ", -1) + " can't be empty"
|
|
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log("Action: ", selectedAction);
|
|
selectedAction.authentication_id = authenticationOption.id;
|
|
selectedAction.selectedAuthentication = authenticationOption;
|
|
if (
|
|
selectedAction.authentication === undefined ||
|
|
selectedAction.authentication === null
|
|
) {
|
|
selectedAction.authentication = [authenticationOption];
|
|
} else {
|
|
selectedAction.authentication.push(authenticationOption);
|
|
}
|
|
|
|
setSelectedAction(selectedAction);
|
|
|
|
var newAuthOption = JSON.parse(JSON.stringify(authenticationOption));
|
|
var newFields = [];
|
|
for (const key in newAuthOption.fields) {
|
|
const value = newAuthOption.fields[key];
|
|
newFields.push({
|
|
key: key,
|
|
value: value,
|
|
});
|
|
}
|
|
|
|
console.log("FIELDS: ", newFields);
|
|
newAuthOption.fields = newFields;
|
|
|
|
if (setNewAppAuth !== undefined) {
|
|
setNewAppAuth(newAuthOption);
|
|
}
|
|
//appAuthentication.push(newAuthOption)
|
|
//setAppAuthentication(appAuthentication)
|
|
//
|
|
|
|
//if (configureWorkflowModalOpen) {
|
|
// setSelectedAction({})
|
|
//}
|
|
//setUpdate(authenticationOption.id)
|
|
|
|
/*
|
|
{selectedAction.authentication.map(data => (
|
|
<MenuItem key={data.id} style={{backgroundColor: inputColor, color: "white"}} value={data}>
|
|
*/
|
|
};
|
|
|
|
const handleScopeChange = (event) => {
|
|
const {target: { value }} = event;
|
|
|
|
console.log("VALUE: ", value);
|
|
|
|
// On autofill we get a the stringified value.
|
|
setSelectedScopes(typeof value === "string" ? value.split(",") : value);
|
|
};
|
|
|
|
if (
|
|
authenticationOption.label === null ||
|
|
authenticationOption.label === undefined
|
|
) {
|
|
authenticationOption.label = selectedApp.name + " authentication";
|
|
}
|
|
|
|
|
|
const autoAuthButton =
|
|
<Button
|
|
fullWidth
|
|
variant="contained"
|
|
style={{
|
|
marginBottom: 20,
|
|
marginTop: 20,
|
|
flex: 1,
|
|
textTransform: "none",
|
|
textAlign: "left",
|
|
justifyContent: "flex-start",
|
|
backgroundColor: "#ffffff",
|
|
color: "#2f2f2f",
|
|
borderRadius: theme.palette?.borderRadius,
|
|
minWidth: 275,
|
|
maxWidth: 275,
|
|
maxHeight: 50,
|
|
overflow: "hidden",
|
|
border: `1px solid ${theme.palette.inputColor}`,
|
|
}}
|
|
color="primary"
|
|
disabled={
|
|
clientSecret.length > 0 || clientId.length > 0
|
|
}
|
|
fullWidth
|
|
onClick={() => {
|
|
// Hardcode some stuff?
|
|
// This could prolly be added to the app itself with a "default" client ID
|
|
if (isCloud) {
|
|
ReactGA.event({
|
|
category: "Integration",
|
|
action: "Authenticate",
|
|
label: `${selectedApp?.name} - One click`,
|
|
})
|
|
}
|
|
startOauth2Request()
|
|
}}
|
|
color="primary"
|
|
>
|
|
{buttonClicked ? (
|
|
<CircularProgress style={{ color: "#f86a3e", width: 45, height: 45, margin: "auto", }} />
|
|
) : (
|
|
<span style={{display: "flex"}}>
|
|
<img
|
|
alt={selectedAction.app_name}
|
|
style={{ margin: 4, minHeight: 30, maxHeight: 30, borderRadius: theme.palette?.borderRadius, }}
|
|
src={selectedAction.large_image}
|
|
/>
|
|
<Typography style={{ margin: 0, marginLeft: 10, marginTop: 8, color: "#2f2f2f",}} variant="body1">
|
|
One-click Login
|
|
</Typography>
|
|
</span>
|
|
)}
|
|
</Button>
|
|
|
|
if (authButtonOnly === true && (authenticationType.redirect_uri !== undefined && authenticationType.redirect_uri !== null && authenticationType.redirect_uri.length > 0) && (authenticationType.token_uri !== undefined && authenticationType.token_uri !== null && authenticationType.token_uri.length > 0)) {
|
|
return autoAuthButton
|
|
}
|
|
|
|
return (
|
|
<div>
|
|
<DialogTitle>
|
|
<div style={{ color: theme.palette.text.primary }}>
|
|
Authenticate {selectedApp.name.replaceAll("_", " ")}
|
|
</div>
|
|
</DialogTitle>
|
|
<DialogContent>
|
|
<span style={{}}>
|
|
Oauth2 requires a Client ID and Client Secret to authenticate, defined in your apps' remote website. <span>Your redirect URL: <br/><b>{window.location.origin}/set_authentication</b><br/>
|
|
</span>
|
|
<a
|
|
target="_blank"
|
|
rel="norefferer"
|
|
href="/docs/apps#authentication"
|
|
style={{ textDecoration: "none", color: theme.palette.linkColor}}
|
|
>
|
|
{" "}
|
|
Learn more about Oauth2 with Shuffle
|
|
</a>
|
|
<div />
|
|
</span>
|
|
|
|
{isCloud && registeredApps?.includes(selectedApp?.name?.replaceAll(" ", "_").toLowerCase()) ?
|
|
<span>
|
|
<span style={{display: "flex"}}>
|
|
{autoAuthButton}
|
|
|
|
{buttonClicked ?
|
|
null
|
|
:
|
|
<Tooltip
|
|
color="primary"
|
|
title={"Force Admin Consent"}
|
|
placement="top"
|
|
>
|
|
<Button
|
|
fullWidth
|
|
variant="outlined"
|
|
style={{
|
|
maxWidth: 40,
|
|
marginBottom: 20,
|
|
marginTop: 20,
|
|
maxHeight: 50,
|
|
marginLeft: 10,
|
|
}}
|
|
color="secondary"
|
|
disabled={
|
|
clientSecret.length > 0 || clientId.length > 0
|
|
}
|
|
fullWidth
|
|
onClick={() => {
|
|
// Hardcode some stuff?
|
|
// This could prolly be added to the app itself with a "default" client ID
|
|
//startOauth2Request(true)
|
|
startOauth2Request()
|
|
}}
|
|
color="primary"
|
|
>
|
|
<SupervisorAccountIcon />
|
|
</Button>
|
|
</Tooltip>
|
|
}
|
|
</span>
|
|
<Typography style={{textAlign: "center", marginTop: 0, marginBottom: 0, }}>
|
|
OR
|
|
</Typography>
|
|
</span>
|
|
: null}
|
|
{/*<TextField
|
|
style={{backgroundColor: theme.palette.inputColor, borderRadius: theme.palette?.borderRadius,}}
|
|
InputProps={{
|
|
style:{
|
|
},
|
|
}}
|
|
fullWidth
|
|
color="primary"
|
|
placeholder={"Auth july 2020"}
|
|
defaultValue={`Auth for ${selectedApp.name}`}
|
|
onChange={(event) => {
|
|
authenticationOption.label = event.target.value
|
|
}}
|
|
/>
|
|
<Divider style={{marginTop: 15, marginBottom: 15, backgroundColor: "rgb(91, 96, 100)"}}/>
|
|
*/}
|
|
|
|
{!manuallyConfigure ? null : (
|
|
<span>
|
|
{selectedApp.authentication.parameters.map((data, index) => {
|
|
//console.log(data, index)
|
|
if (data.name === "client_id" || data.name === "client_secret") {
|
|
return null;
|
|
}
|
|
|
|
if (data.name !== "url") {
|
|
return null;
|
|
}
|
|
|
|
if (oauthUrl.length === 0) {
|
|
setOauthUrl(data.value);
|
|
}
|
|
|
|
const isNormalOauth = authenticationType.redirect_uri !== undefined && authenticationType.redirect_uri !== null && authenticationType.redirect_uri.length > 0
|
|
|
|
const defaultValue = !isNormalOauth && data.name === "url" && authenticationType.token_uri !== undefined && authenticationType.token_uri !== null && authenticationType.token_uri.length > 0 && (authenticationType.authorizationUrl === undefined || authenticationType.authorizationUrl === null || authenticationType.authorizationUrl.length === 0) && authenticationType.type === "oauth2-app" ? authenticationType.token_uri : data.value === undefined || data.value === null ? "" : data.value
|
|
|
|
const fieldname = !isNormalOauth && data.name === "url" && authenticationType.grant_type !== undefined && authenticationType.grant_type !== null && authenticationType.grant_type.length > 0 && authenticationType.type === "oauth2-app" ? "Token URL" : data.name
|
|
|
|
return (
|
|
<div key={index} style={{ marginTop: !isNormalOauth && authenticationType.type === "oauth2-app" ? 10 : 0, }}>
|
|
<LockOpenIcon style={{ marginRight: 10 }} />
|
|
|
|
<b>{fieldname}</b>
|
|
|
|
{data.schema !== undefined &&
|
|
data.schema !== null &&
|
|
data.schema.type === "bool" ?
|
|
<Select
|
|
SelectDisplayProps={{
|
|
style: {
|
|
marginLeft: 10,
|
|
},
|
|
}}
|
|
defaultValue={"false"}
|
|
fullWidth
|
|
label={fieldname}
|
|
onChange={(e) => {
|
|
console.log("Value: ", e.target.value);
|
|
authenticationOption.fields[data.name] = e.target.value;
|
|
}}
|
|
style={{
|
|
backgroundColor: theme.palette.surfaceColor,
|
|
color: "white",
|
|
height: 50,
|
|
}}
|
|
>
|
|
<MenuItem
|
|
key={"false"}
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
color: "white",
|
|
}}
|
|
value={"false"}
|
|
>
|
|
false
|
|
</MenuItem>
|
|
<MenuItem
|
|
key={"true"}
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
color: "white",
|
|
}}
|
|
value={"true"}
|
|
>
|
|
true
|
|
</MenuItem>
|
|
</Select>
|
|
:
|
|
<TextField
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
borderRadius: theme.palette?.borderRadius,
|
|
}}
|
|
InputProps={{
|
|
style: {
|
|
},
|
|
}}
|
|
fullWidth
|
|
type={
|
|
data.example !== undefined &&
|
|
data.example.includes("***")
|
|
? "password"
|
|
: "text"
|
|
}
|
|
color="primary"
|
|
defaultValue={defaultValue}
|
|
placeholder={data.example}
|
|
onChange={(event) => {
|
|
authenticationOption.fields[data.name] = event.target.value;
|
|
console.log("Setting oauth url: ", event.target.value);
|
|
setOauthUrl(event.target.value);
|
|
//const [oauthUrl, setOauthUrl] = React.useState("")
|
|
}}
|
|
/>
|
|
}
|
|
</div>
|
|
)
|
|
})}
|
|
<TextField
|
|
style={{
|
|
marginTop: 20,
|
|
backgroundColor: theme.palette.inputColor,
|
|
borderRadius: theme.palette?.borderRadius,
|
|
}}
|
|
InputProps={{
|
|
style: {
|
|
},
|
|
}}
|
|
fullWidth
|
|
color="primary"
|
|
label={"Client ID"}
|
|
placeholder={"Client ID"}
|
|
onChange={(event) => {
|
|
setClientId(event.target.value);
|
|
//authenticationOption.label = event.target.value
|
|
}}
|
|
/>
|
|
<TextField
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
borderRadius: theme.palette?.borderRadius,
|
|
marginBottom: 10,
|
|
}}
|
|
InputProps={{
|
|
style: {
|
|
},
|
|
}}
|
|
fullWidth
|
|
color="primary"
|
|
label={"Client Secret"}
|
|
placeholder={"Client Secret"}
|
|
onChange={(event) => {
|
|
setClientSecret(event.target.value);
|
|
//authenticationOption.label = event.target.value
|
|
}}
|
|
/>
|
|
|
|
{authenticationType.grant_type !== "password" ? null :
|
|
<div>
|
|
<TextField
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
borderRadius: theme.palette?.borderRadius,
|
|
}}
|
|
InputProps={{
|
|
style: {
|
|
},
|
|
}}
|
|
fullWidth
|
|
color="primary"
|
|
label={"Username"}
|
|
placeholder={"Username"}
|
|
onChange={(event) => {
|
|
setUsername(event.target.value);
|
|
//authenticationOption.label = event.target.value
|
|
}}
|
|
/>
|
|
<TextField
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
borderRadius: theme.palette?.borderRadius,
|
|
marginBottom: 10,
|
|
}}
|
|
InputProps={{
|
|
style: {
|
|
},
|
|
}}
|
|
fullWidth
|
|
color="primary"
|
|
label={"Password"}
|
|
placeholder={"Password"}
|
|
onChange={(event) => {
|
|
setPassword(event.target.value);
|
|
//authenticationOption.label = event.target.value
|
|
}}
|
|
/>
|
|
</div>
|
|
}
|
|
|
|
{allscopes === undefined || allscopes === null || allscopes.length === 0 ? null : "Scopes (access rights)"}
|
|
|
|
{allscopes === undefined || allscopes === null || allscopes.length === 0 ? null : (
|
|
<div style={{width: "100%", marginTop: 10, display: "flex"}}>
|
|
<span>
|
|
<Autocomplete
|
|
multiple
|
|
underline={false}
|
|
label="Scopes"
|
|
style={{
|
|
backgroundColor: theme.palette.inputColor,
|
|
color: "white",
|
|
padding: 5,
|
|
minWidth: 300,
|
|
}}
|
|
onChange={(e, value) => {
|
|
//handleScopeChange(e)
|
|
setSelectedScopes(typeof value === "string" ? value.split(",") : value);
|
|
}}
|
|
fullWidth
|
|
input={<Input id="select-multiple-native" />}
|
|
MenuProps={MenuProps}
|
|
options={allscopes}
|
|
getOptionLabel={(option) => option}
|
|
renderInput={(params) => {
|
|
return (
|
|
<div>
|
|
{/*<Checkbox checked={selectedScopes.indexOf(data) > -1} />*/}
|
|
<TextField
|
|
{...params}
|
|
label="Search Scopes"
|
|
variant="outlined"
|
|
/>
|
|
</div>
|
|
)
|
|
}}
|
|
/>
|
|
</span>
|
|
|
|
{((authenticationType.redirect_uri === undefined || authenticationType.redirect_uri === null || authenticationType.redirect_uri.length === 0) && (authenticationType.token_uri !== undefined && authenticationType.token_uri !== null && authenticationType.token_uri.length > 0)) ? null :
|
|
<span>
|
|
<Tooltip
|
|
color="primary"
|
|
title={"Automatic Refresh (default: true)"}
|
|
placement="top"
|
|
>
|
|
<Checkbox style={{paddingTop: 20}} color="secondary" checked={offlineAccess} onClick={() => {
|
|
setOfflineAccess(!offlineAccess)
|
|
}}/>
|
|
</Tooltip>
|
|
</span>
|
|
}
|
|
</div>
|
|
)}
|
|
</span>
|
|
)}
|
|
<Button
|
|
style={{
|
|
marginBottom: 40,
|
|
marginTop: 20,
|
|
borderRadius: theme.palette?.borderRadius,
|
|
}}
|
|
disabled={
|
|
clientSecret.length === 0 || clientId.length === 0 || buttonClicked || (allscopes.length !== 0 && selectedScopes.length === 0)
|
|
}
|
|
variant="contained"
|
|
fullWidth
|
|
onClick={() => {
|
|
toast.info("Starting authentication process", {
|
|
"autoClose": 1500,
|
|
})
|
|
|
|
if (isCloud) {
|
|
ReactGA.event({
|
|
category: "Integration",
|
|
action: "Authenticate",
|
|
label: `${selectedApp?.name} - Oauth2 manual`,
|
|
})
|
|
}
|
|
|
|
handleOauth2Request(clientId, clientSecret, oauthUrl, selectedScopes, undefined, true);
|
|
}}
|
|
color="primary"
|
|
>
|
|
{buttonClicked ? (
|
|
<CircularProgress style={{ color: "white" }} />
|
|
) : (
|
|
"Authenticate"
|
|
)}
|
|
</Button>
|
|
|
|
|
|
|
|
{defaultConfigSet ? (
|
|
<span style={{}}>
|
|
... or
|
|
<Button
|
|
style={{
|
|
marginLeft: 10,
|
|
borderRadius: theme.palette?.borderRadius,
|
|
}}
|
|
disabled={clientSecret.length === 0 || clientId.length === 0}
|
|
variant="text"
|
|
onClick={() => {
|
|
setManuallyConfigure(!manuallyConfigure);
|
|
|
|
if (manuallyConfigure) {
|
|
setClientId(authenticationType.client_id);
|
|
setClientSecret(authenticationType.client_secret);
|
|
} else {
|
|
setClientId("");
|
|
setClientSecret("");
|
|
}
|
|
}}
|
|
color="primary"
|
|
>
|
|
{manuallyConfigure
|
|
? "Use auto-config"
|
|
: "Manually configure Oauth2"}
|
|
</Button>
|
|
</span>
|
|
) : null}
|
|
</DialogContent>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default AuthenticationOauth2;
|