From df6d7f88de3fc3c0ee849a9fda46fec46d35cf8a Mon Sep 17 00:00:00 2001 From: Frikky Date: Wed, 15 Nov 2023 00:53:02 +0100 Subject: [PATCH] Fixed some search issues and Oauth2 Application perms + JWT problems --- frontend/src/components/Oauth2Auth.jsx | 148 +++++++--- frontend/src/components/Searchfield.jsx | 2 +- frontend/src/views/AngularWorkflow.jsx | 1 - frontend/src/views/AppCreator.jsx | 350 +++++++++++++++--------- 4 files changed, 321 insertions(+), 180 deletions(-) diff --git a/frontend/src/components/Oauth2Auth.jsx b/frontend/src/components/Oauth2Auth.jsx index 204af1c6..b70e536a 100755 --- a/frontend/src/components/Oauth2Auth.jsx +++ b/frontend/src/components/Oauth2Auth.jsx @@ -93,10 +93,10 @@ const AuthenticationOauth2 = (props) => { appAuthentication, setSelectedAction, setNewAppAuth, - isCloud, - autoAuth, - authButtonOnly, - isLoggedIn, + isCloud, + autoAuth, + authButtonOnly, + isLoggedIn, } = props; let navigate = useNavigate(); @@ -297,15 +297,74 @@ const AuthenticationOauth2 = (props) => { const handleOauth2Request = (client_id, client_secret, oauth_url, scopes, admin_consent, prompt) => { - setButtonClicked(true); - //console.log("SCOPES: ", scopes); + + 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") + + // 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": authenticationType.token_uri, + }] + + // Not necessary yet to do something like this due to not showing the fields anyway + /* + if (selectedApp.parameters !== undefined && selectedApp.parameters !== null && selectedApp.parameters.length > 0) { + + for (var i = 0; i < selectedApp.parameters.length; i++) { + if (selectedApp.parameters[i].configuration !== true) { + continue + } + + console.log("Found configuration field: ", selectedApp.parameters[i].key, " with example: ", selectedApp.parameters[i].example) + parsedFields.push({ + "key": selectedApp.parameters[i].key, + "value": selectedApp.parameters[i].example, + }) + } + } + */ + + + 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, + } + setNewAppAuth(appAuthData) + + 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)) { + var resources = ""; + if (scopes !== undefined && (scopes !== null) & (scopes.length > 0)) { console.log("IN scope 1") if (offlineAccess === true && !scopes.includes("offline_access")) { @@ -324,26 +383,26 @@ const AuthenticationOauth2 = (props) => { //console.log("AUTH: ", authenticationType) //console.log("SCOPES2: ", resources) const redirectUri = `${window.location.protocol}//${window.location.host}/set_authentication`; - const workflowId = workflow !== undefined ? workflow.id : ""; + 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}`; - } + // 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}`; - } + // 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}`; + } if (oauth_url !== undefined && oauth_url !== null && oauth_url.length > 0) { state += `%26oauth_url%3d${oauth_url}`; @@ -363,7 +422,7 @@ const AuthenticationOauth2 = (props) => { // No prompt forcing //var url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&prompt=login&scope=${resources}&state=${state}&access_type=offline`; - var defaultPrompt = "login" + var defaultPrompt = "login" if (prompt !== undefined && prompt !== null && prompt.length > 0) { defaultPrompt = prompt } @@ -798,6 +857,7 @@ const AuthenticationOauth2 = (props) => { }} fullWidth color="primary" + label={"Client ID"} placeholder={"Client ID"} onChange={(event) => { setClientId(event.target.value); @@ -816,20 +876,22 @@ const AuthenticationOauth2 = (props) => { }} fullWidth color="primary" + label={"Client Secret"} placeholder={"Client Secret"} onChange={(event) => { setClientSecret(event.target.value); //authenticationOption.label = event.target.value }} /> + {allscopes.length === 0 ? null : "Scopes"} {allscopes.length === 0 ? null : (
- Scopes - - - { - setOfflineAccess(!offlineAccess) - }}/> - - + + {((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 : + + + { + setOfflineAccess(!offlineAccess) + }}/> + + + }
)} @@ -882,19 +947,14 @@ const AuthenticationOauth2 = (props) => { variant="contained" fullWidth onClick={() => { - handleOauth2Request( - clientId, - clientSecret, - oauthUrl, - selectedScopes - ); + handleOauth2Request(clientId, clientSecret, oauthUrl, selectedScopes); }} color="primary" > {buttonClicked ? ( ) : ( - "Manually Authenticate" + "Authenticate" )} diff --git a/frontend/src/components/Searchfield.jsx b/frontend/src/components/Searchfield.jsx index 7edef2ca..726a2fa8 100644 --- a/frontend/src/components/Searchfield.jsx +++ b/frontend/src/components/Searchfield.jsx @@ -81,7 +81,7 @@ const SearchField = props => { style: { color: "white", minWidth: 750, - height: 685, + height: 785, borderRadius: 16, border: "1px solid var(--Container-Stroke, #494949)", background: "var(--Container, #000000)", diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index ad643a9b..27ba8f18 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -4593,7 +4593,6 @@ const AngularWorkflow = (defaultprops) => { } } - console.log("NODE: ", nodedata) if (nodedata.decorator !== true && nodedata.attachedTo === undefined) { var newdata = JSON.parse(JSON.stringify(nodedata)) newdata.large_image = "" diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx index 980e10c2..4cd2dfc7 100755 --- a/frontend/src/views/AppCreator.jsx +++ b/frontend/src/views/AppCreator.jsx @@ -374,8 +374,8 @@ const AppCreator = (defaultprops) => { "API key", "Bearer auth", "Basic auth", - "Oauth2", "JWT", + "Oauth2", ]; const apikeySelection = ["Header", "Query"]; @@ -411,6 +411,7 @@ const AppCreator = (defaultprops) => { const [appBuilding, setAppBuilding] = useState(false); const [fileDownloadEnabled, setFileDownloadEnabled] = useState(false); const [actionAmount, setActionAmount] = useState(increaseAmount); + const [oauth2Type, setOauth2Type] = useState("application"); const defaultAuth = { name: "", type: "header", @@ -1678,7 +1679,7 @@ const AppCreator = (defaultprops) => { value.in.length > 0 ) { setParameterName(value.in); - optionset = true + optionset = true } } else if (value.scheme === "bearer") { @@ -1709,24 +1710,24 @@ const AppCreator = (defaultprops) => { newauth.push({ "name": key, "type": value.in.toLowerCase(), - "in": value.in.toLowerCase(), + "in": value.in.toLowerCase(), "example": "", - }) - } else { - newauth.push({ - "name": key, - "type": value.in.toLowerCase(), - "in": value.in.toLowerCase(), - "example": "", - }) - } + }) + } else { + newauth.push({ + "name": key, + "type": value.in.toLowerCase(), + "in": value.in.toLowerCase(), + "example": "", + }) + } if (value.description !== undefined && value.description !== null && value.description.length > 0) { - // Don't want a real description - just the ones we're replacing with - if ((value.description.split(" ").length - 1) <= 2) { - setRefreshUrl(value.description) - } - } + // Don't want a real description - just the ones we're replacing with + if ((value.description.split(" ").length - 1) <= 2) { + setRefreshUrl(value.description) + } + } } else if (value.scheme === "basic") { setAuthenticationOption("Basic auth"); @@ -1734,15 +1735,15 @@ const AppCreator = (defaultprops) => { optionset = true } else if (value.scheme === "oauth2") { - setAuthenticationOption("Oauth2"); - setAuthenticationRequired(true); - optionset = true + setAuthenticationOption("Oauth2"); + setAuthenticationRequired(true); + optionset = true } else if (value.type === "oauth2" || key === "Oauth2" || key === "Oauth2c" || (key !== undefined && key !== null && key.toLowerCase().includes("oauth2"))) { //toast("Can't handle Oauth2 auth yet.") setAuthenticationOption("Oauth2"); setAuthenticationRequired(true); - optionset = true + optionset = true //console.log("FLOW-1: ", value) const flowkey = value.flow === undefined ? "flows" : "flow"; @@ -1754,12 +1755,11 @@ const AppCreator = (defaultprops) => { //console.log("FLOW2: ", value[flowkey][basekey]) if (value[flowkey] !== undefined && value[flowkey][basekey] !== undefined ) { - if ( - value[flowkey][basekey].authorizationUrl !== undefined && - parameterName.length === 0 - ) { - setParameterName(value[flowkey][basekey].authorizationUrl); - } + if (value[flowkey][basekey].authorizationUrl !== undefined && parameterName.length === 0) { + setParameterName(value[flowkey][basekey].authorizationUrl); + } else { + setOauth2Type("application") + } var tokenUrl = ""; if (value[flowkey][basekey].tokenUrl !== undefined) { @@ -2846,7 +2846,6 @@ const AppCreator = (defaultprops) => { style={{ margin: 0, flex: "1", backgroundColor: inputColor }} fullWidth={true} placeholder="/security/user/authenticate" - type="name" id="standard-required" margin="normal" variant="outlined" @@ -2866,6 +2865,40 @@ const AppCreator = (defaultprops) => { }, }} /> + + Optional: Authentication queries + + {/* + + Must use 'key=value&key=value' format + + } + onBlur={(e) => { + //setParameterName(e.target.value) + }} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + }, + }} + /> + */} ) : null; @@ -2878,56 +2911,64 @@ const AppCreator = (defaultprops) => { color="textSecondary" style={{ marginTop: 10 }} > - Find the Authorization URL, Token URL and scopes in question for the API. Ensure the app in question is pointed at https://shuffler.io/set_authentication + {oauth2Type === "delegated" ? + "Find the Authorization URL, Token URL and scopes in question for the API. Ensure your app in the service uses redirect url https://shuffler.io/set_authentication" + : + "Find the Token URL and scopes in question for the API" + } - - Base Authorization URL for Oauth2 - - setParameterName(e.target.value)} + {oauth2Type === "delegated" ? + + + Base Authorization URL for Oauth2 + + setParameterName(e.target.value)} onBlur={(event) => { - var tmpstring = event.target.value.trim(); + var tmpstring = event.target.value.trim(); - if ( - tmpstring.length > 4 && - !tmpstring.startsWith("http") && - !tmpstring.startsWith("ftp") - ) { - toast("Auth URL must start with http(s)://"); - } + if ( + tmpstring.length > 4 && + !tmpstring.startsWith("http") && + !tmpstring.startsWith("ftp") + ) { + toast("Auth URL must start with http(s)://"); + } - if (tmpstring.includes("?")) { - var newtmp = tmpstring.split("?") - if (tmpstring.length > 1) { - tmpstring = newtmp[0] - } - } + if (tmpstring.includes("?")) { + var newtmp = tmpstring.split("?") + if (tmpstring.length > 1) { + tmpstring = newtmp[0] + } + } - setParameterName(tmpstring) - }} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style: { - color: "white", - }, - }} - /> + setParameterName(tmpstring) + }} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + }, + }} + /> + + : null} { }, }} /> - - Refresh-token URL for Oauth2 (Optional) - - setRefreshUrl(e.target.value)} - onBlur={(event) => { - var tmpstring = event.target.value.trim(); + {oauth2Type === "delegated" ? + + + Refresh-token URL for Oauth2 (Optional) + + setRefreshUrl(e.target.value)} + onBlur={(event) => { + var tmpstring = event.target.value.trim(); - if ( - tmpstring.length > 4 && - !tmpstring.startsWith("http") && - !tmpstring.startsWith("ftp") - ) { - toast("Refresh URL must start with http(s)://"); - } + if ( + tmpstring.length > 4 && + !tmpstring.startsWith("http") && + !tmpstring.startsWith("ftp") + ) { + toast("Refresh URL must start with http(s)://"); + } - if (tmpstring.includes("?")) { - var newtmp = tmpstring.split("?") - if (tmpstring.length > 1) { - tmpstring = newtmp[0] - } - } + if (tmpstring.includes("?")) { + var newtmp = tmpstring.split("?") + if (tmpstring.length > 1) { + tmpstring = newtmp[0] + } + } - setRefreshUrl(tmpstring) - }} - InputProps={{ - style: { - color: "white", - }, - }} - /> - - Scopes for Oauth2 - - + + : null} + + Scopes for Oauth2 + + { setOauth2Scopes(chips) setUpdate(Math.random()) }} - /> + /> ) : null; @@ -5865,6 +5910,7 @@ const AppCreator = (defaultprops) => { }} />