diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 6cd797f0..a0d72ede 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -2,7 +2,7 @@ module shuffle go 1.13 -//replace github.com/frikky/shuffle-shared => ../../../../git/shuffle-shared +replace github.com/frikky/shuffle-shared => ../../../../git/shuffle-shared //replace github.com/frikky/kin-openapi => ../../../../git/kin-openapi diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 6112eaba..24404c6c 100644 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -3502,7 +3502,7 @@ func handleAppHotload(ctx context.Context, location string, forceUpdate bool) er } //log.Printf("Reading app folder: %#v", dir) - _, _, err = iterateAppGithubFolders(fs, dir, "", "", forceUpdate) + _, _, err = shuffle.IterateAppGithubFolders(ctx, fs, dir, "", "", forceUpdate) if err != nil { log.Printf("[WARNING] Githubfolders error: %s", err) return err @@ -4174,7 +4174,7 @@ func runInitEs(ctx context.Context) { //iterateAppGithubFolders(fs, dir, "", "testing") // FIXME: Get all the apps? - _, _, err = iterateAppGithubFolders(fs, dir, "", "", forceUpdate) + _, _, err = shuffle.IterateAppGithubFolders(ctx, fs, dir, "", "", forceUpdate) if err != nil { log.Printf("[WARNING] Error from app load in init: %s", err) } @@ -4829,7 +4829,7 @@ func runInit(ctx context.Context) { //iterateAppGithubFolders(fs, dir, "", "testing") // FIXME: Get all the apps? - _, _, err = iterateAppGithubFolders(fs, dir, "", "", forceUpdate) + _, _, err = shuffle.IterateAppGithubFolders(ctx, fs, dir, "", "", forceUpdate) if err != nil { log.Printf("[WARNING] Error from app load in init: %s", err) } @@ -5791,8 +5791,8 @@ func initHandlers() { r.HandleFunc("/api/v1/apps/{appId}", shuffle.DeleteWorkflowApp).Methods("DELETE", "OPTIONS") r.HandleFunc("/api/v1/apps/{appId}/config", shuffle.GetWorkflowAppConfig).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/apps/run_hotload", handleAppHotloadRequest).Methods("GET", "OPTIONS") - r.HandleFunc("/api/v1/apps/get_existing", loadSpecificApps).Methods("POST", "OPTIONS") - r.HandleFunc("/api/v1/apps/download_remote", loadSpecificApps).Methods("POST", "OPTIONS") + r.HandleFunc("/api/v1/apps/get_existing", shuffle.LoadSpecificApps).Methods("POST", "OPTIONS") + r.HandleFunc("/api/v1/apps/download_remote", shuffle.LoadSpecificApps).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/apps/validate", validateAppInput).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/apps", getWorkflowApps).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/apps", setNewWorkflowApp).Methods("PUT", "OPTIONS") diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index d39f55e0..205dea93 100644 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -19,10 +19,10 @@ import ( "strings" "time" - "github.com/docker/docker/api/types" - "github.com/docker/docker/client" + //"github.com/docker/docker/api/types" + //"github.com/docker/docker/client" + //gyaml "github.com/ghodss/yaml" - gyaml "github.com/ghodss/yaml" "github.com/h2non/filetype" uuid "github.com/satori/go.uuid" @@ -2735,29 +2735,6 @@ func getWorkflowApps(resp http.ResponseWriter, request *http.Request) { resp.Write(newbody) } -// Bad check for workflowapps :) -// FIXME - use tags and struct reflection -func checkWorkflowApp(workflowApp shuffle.WorkflowApp) error { - // Validate fields - if workflowApp.Name == "" { - return errors.New("App field name doesn't exist") - } - - if workflowApp.Description == "" { - return errors.New("App field description doesn't exist") - } - - if workflowApp.AppVersion == "" { - return errors.New("App field app_version doesn't exist") - } - - if workflowApp.ContactInfo.Name == "" { - return errors.New("App field contact_info.name doesn't exist") - } - - return nil -} - func handleGetfile(resp http.ResponseWriter, request *http.Request) ([]byte, error) { // Upload file here first request.ParseMultipartForm(32 << 20) @@ -3084,141 +3061,6 @@ func handleAppHotloadRequest(resp http.ResponseWriter, request *http.Request) { resp.WriteHeader(200) resp.Write([]byte(fmt.Sprintf(`{"success": true}`))) } - -func loadSpecificApps(resp http.ResponseWriter, request *http.Request) { - cors := handleCors(resp, request) - if cors { - return - } - - // Just need to be logged in - // FIXME - should have some permissions? - _, err := shuffle.HandleApiAuthentication(resp, request) - if err != nil { - log.Printf("[WARNING] Api authentication failed in load specific apps: %s", err) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - body, err := ioutil.ReadAll(request.Body) - if err != nil { - log.Printf("Error with body read: %s", err) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - // Field1 & 2 can be a lot of things. - // Field1 = Username - // Field2 = Password - type tmpStruct struct { - URL string `json:"url"` - Branch string `json:"branch"` - Field1 string `json:"field_1"` - Field2 string `json:"field_2"` - ForceUpdate bool `json:"force_update"` - } - //log.Printf("Body: %s", string(body)) - - var tmpBody tmpStruct - err = json.Unmarshal(body, &tmpBody) - if err != nil { - log.Printf("Error with unmarshal tmpBody: %s", err) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - fs := memfs.New() - - if strings.Contains(tmpBody.URL, "github") || strings.Contains(tmpBody.URL, "gitlab") || strings.Contains(tmpBody.URL, "bitbucket") { - cloneOptions := &git.CloneOptions{ - URL: tmpBody.URL, - } - - if len(tmpBody.Branch) > 0 && tmpBody.Branch != "master" && tmpBody.Branch != "main" { - cloneOptions.ReferenceName = plumbing.ReferenceName(tmpBody.Branch) - } - - // FIXME: Better auth. - if len(tmpBody.Field1) > 0 && len(tmpBody.Field2) > 0 { - cloneOptions.Auth = &http2.BasicAuth{ - Username: tmpBody.Field1, - Password: tmpBody.Field2, - } - } - - storer := memory.NewStorage() - r, err := git.Clone(storer, fs, cloneOptions) - if err != nil { - log.Printf("Failed loading repo %s into memory (github workflows 2): %s", tmpBody.URL, err) - resp.WriteHeader(401) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err))) - return - } - - dir, err := fs.ReadDir("/") - if err != nil { - log.Printf("FAiled reading folder: %s", err) - } - _ = r - - if tmpBody.ForceUpdate { - log.Printf("[INFO] Running with force update!") - } else { - log.Printf("[INFO] Updating apps with updates (no force)") - } - - if tmpBody.ForceUpdate { - ctx := context.Background() - dockercli, err := client.NewEnvClient() - if err == nil { - _, err := dockercli.ImagePull(ctx, "frikky/shuffle:app_sdk", types.ImagePullOptions{}) - if err != nil { - log.Printf("[WARNING] Failed to download apps with the new App SDK: %s", err) - } - } else { - log.Printf("[WARNING] Failed to download apps with the new App SDK because of docker cli: %s", err) - } - } - - iterateAppGithubFolders(fs, dir, "", "", tmpBody.ForceUpdate) - - } else if strings.Contains(tmpBody.URL, "s3") { - //https://docs.aws.amazon.com/sdk-for-go/api/service/s3/ - - //sess := session.Must(session.NewSession()) - //downloader := s3manager.NewDownloader(sess) - - //// Write the contents of S3 Object to the file - //storer := memory.NewStorage() - //n, err := downloader.Download(storer, &s3.GetObjectInput{ - // Bucket: aws.String(myBucket), - // Key: aws.String(myString), - //}) - //if err != nil { - // return fmt.Errorf("failed to download file, %v", err) - //} - //fmt.Printf("file downloaded, %d bytes\n", n) - } else { - resp.WriteHeader(401) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s is unsupported"}`, tmpBody.URL))) - return - } - - ctx := context.Background() - cacheKey := fmt.Sprintf("workflowapps-sorted-100") - shuffle.DeleteCache(ctx, cacheKey) - cacheKey = fmt.Sprintf("workflowapps-sorted-500") - shuffle.DeleteCache(ctx, cacheKey) - cacheKey = fmt.Sprintf("workflowapps-sorted-1000") - shuffle.DeleteCache(ctx, cacheKey) - - resp.WriteHeader(200) - resp.Write([]byte(fmt.Sprintf(`{"success": true}`))) -} - func iterateOpenApiGithub(fs billy.Filesystem, dir []os.FileInfo, extra string, onlyname string) error { ctx := context.Background() @@ -3482,341 +3324,6 @@ func iterateWorkflowGithubFolders(fs billy.Filesystem, dir []os.FileInfo, extra return err } -type buildLaterStruct struct { - Tags []string - Extra string - Id string -} - -// Onlyname is used to -func iterateAppGithubFolders(fs billy.Filesystem, dir []os.FileInfo, extra string, onlyname string, forceUpdate bool) ([]buildLaterStruct, []buildLaterStruct, error) { - var err error - - allapps := []shuffle.WorkflowApp{} - - // These are slow apps to build with some funky mechanisms - reservedNames := []string{ - "OWA", - "NLP", - "YARA", - } - - // It's here to prevent getting them in every iteration - buildLaterFirst := []buildLaterStruct{} - buildLaterList := []buildLaterStruct{} - ctx := context.Background() - for _, file := range dir { - if len(onlyname) > 0 && file.Name() != onlyname { - continue - } - - // Folder? - switch mode := file.Mode(); { - case mode.IsDir(): - tmpExtra := fmt.Sprintf("%s%s/", extra, file.Name()) - dir, err := fs.ReadDir(tmpExtra) - if err != nil { - log.Printf("Failed to read dir: %s", err) - continue - } - - // Go routine? Hmm, this can be super quick I guess - buildFirst, buildLast, err := iterateAppGithubFolders(fs, dir, tmpExtra, "", forceUpdate) - - for _, item := range buildFirst { - buildLaterFirst = append(buildLaterFirst, item) - } - - for _, item := range buildLast { - buildLaterList = append(buildLaterList, item) - } - - if err != nil { - log.Printf("[WARNING] Error reading folder: %s", err) - //buildFirst, buildLast, err := iterateAppGithubFolders(fs, dir, tmpExtra, "", forceUpdate) - - if !forceUpdate { - return buildLaterFirst, buildLaterList, err - } - } - - case mode.IsRegular(): - // Check the file - filename := file.Name() - if filename == "Dockerfile" { - // Set up to make md5 and check if the app is new (api.yaml+src/app.py+Dockerfile) - // Check if Dockerfile, app.py or api.yaml has changed. Hash? - //log.Printf("Handle Dockerfile in location %s", extra) - // Try api.yaml and api.yml - fullPath := fmt.Sprintf("%s%s", extra, "api.yaml") - fileReader, err := fs.Open(fullPath) - if err != nil { - fullPath = fmt.Sprintf("%s%s", extra, "api.yml") - fileReader, err = fs.Open(fullPath) - if err != nil { - log.Printf("Failed finding api.yaml/yml: %s", err) - continue - } - } - - //log.Printf("HANDLING DOCKER FILEREADER - SEARCH&REPLACE?") - - appfileData, err := ioutil.ReadAll(fileReader) - if err != nil { - log.Printf("Failed reading %s: %s", fullPath, err) - continue - } - - if len(appfileData) == 0 { - log.Printf("Failed reading %s - length is 0.", fullPath) - continue - } - - // func md5sum(data []byte) string { - // Make hash - appPython := fmt.Sprintf("%s/src/app.py", extra) - appPythonReader, err := fs.Open(appPython) - if err != nil { - log.Printf("Failed to read python app %s", appPython) - continue - } - - appPythonData, err := ioutil.ReadAll(appPythonReader) - if err != nil { - log.Printf("Failed reading appdata %s: %s", appPython, err) - continue - } - - dockerFp := fmt.Sprintf("%s/Dockerfile", extra) - dockerfile, err := fs.Open(dockerFp) - if err != nil { - log.Printf("Failed to read dockerfil %s", appPython) - continue - } - - dockerfileData, err := ioutil.ReadAll(dockerfile) - if err != nil { - log.Printf("Failed to read dockerfile") - continue - } - - combined := []byte{} - combined = append(combined, appfileData...) - combined = append(combined, appPythonData...) - combined = append(combined, dockerfileData...) - md5 := md5sum(combined) - - var workflowapp shuffle.WorkflowApp - err = gyaml.Unmarshal(appfileData, &workflowapp) - if err != nil { - log.Printf("[WARNING] Failed building workflowapp %s: %s", extra, err) - return buildLaterFirst, buildLaterList, errors.New(fmt.Sprintf("Failed building %s: %s", extra, err)) - //continue - } - - newName := workflowapp.Name - newName = strings.ReplaceAll(newName, " ", "-") - - tags := []string{ - fmt.Sprintf("%s:%s_%s", baseDockerName, strings.ToLower(newName), workflowapp.AppVersion), - } - - if len(allapps) == 0 { - allapps, err = shuffle.GetAllWorkflowApps(ctx, 0) - if err != nil { - log.Printf("[WARNING] Failed getting apps to verify: %s", err) - continue - } - } - - // Make an option to override existing apps? - //Hash string `json:"hash" datastore:"hash" yaml:"hash"` // api.yaml+dockerfile+src/app.py for apps - removeApps := []string{} - skip := false - for _, app := range allapps { - if app.Name == workflowapp.Name && app.AppVersion == workflowapp.AppVersion { - // FIXME: Check if there's a new APP_SDK as well. - // Skip this check if app_sdk is new. - if app.Hash == md5 && app.Hash != "" && !forceUpdate { - skip = true - break - } - - //log.Printf("Overriding app %s:%s as it exists but has different hash.", app.Name, app.AppVersion) - removeApps = append(removeApps, app.ID) - } - } - - if skip && !forceUpdate { - continue - } - - // Fixes (appends) authentication parameters if they're required - if workflowapp.Authentication.Required { - //log.Printf("[INFO] Checking authentication fields and appending for %s!", workflowapp.Name) - // FIXME: - // Might require reflection into the python code to append the fields as well - for index, action := range workflowapp.Actions { - if action.AuthNotRequired { - log.Printf("Skipping auth setup: %s", action.Name) - continue - } - - // 1. Check if authentication params exists at all - // 2. Check if they're present in the action - // 3. Add them IF they DONT exist - // 4. Fix python code with reflection (FIXME) - appendParams := []shuffle.WorkflowAppActionParameter{} - for _, fieldname := range workflowapp.Authentication.Parameters { - found := false - for index, param := range action.Parameters { - if param.Name == fieldname.Name { - found = true - - action.Parameters[index].Configuration = true - //log.Printf("Set config to true for field %s!", param.Name) - break - } - } - - if !found { - appendParams = append(appendParams, shuffle.WorkflowAppActionParameter{ - Name: fieldname.Name, - Description: fieldname.Description, - Example: fieldname.Example, - Required: fieldname.Required, - Configuration: true, - Schema: fieldname.Schema, - }) - } - } - - if len(appendParams) > 0 { - //log.Printf("[AUTH] Appending %d params to the START of %s", len(appendParams), action.Name) - workflowapp.Actions[index].Parameters = append(appendParams, workflowapp.Actions[index].Parameters...) - } - - } - } - - err = checkWorkflowApp(workflowapp) - if err != nil { - log.Printf("[DEBUG] %s for app %s:%s", err, workflowapp.Name, workflowapp.AppVersion) - continue - } - - if len(removeApps) > 0 { - for _, item := range removeApps { - log.Printf("[WARNING] Removing duplicate app: %s", item) - err = shuffle.DeleteKey(ctx, "workflowapp", item) - if err != nil { - log.Printf("[ERROR] Failed deleting duplicate %s: %s", item, err) - } - } - } - - workflowapp.ID = uuid.NewV4().String() - workflowapp.IsValid = true - workflowapp.Verified = true - workflowapp.Sharing = true - workflowapp.Downloaded = true - workflowapp.Hash = md5 - workflowapp.Public = true - - err = shuffle.SetWorkflowAppDatastore(ctx, workflowapp, workflowapp.ID) - if err != nil { - log.Printf("[WARNING] Failed setting workflowapp in intro: %s", err) - continue - } - - /* - err = increaseStatisticsField(ctx, "total_apps_created", workflowapp.ID, 1, "") - if err != nil { - log.Printf("Failed to increase total apps created stats: %s", err) - } - - err = increaseStatisticsField(ctx, "total_apps_loaded", workflowapp.ID, 1, "") - if err != nil { - log.Printf("Failed to increase total apps loaded stats: %s", err) - } - */ - - //log.Printf("Added %s:%s to the database", workflowapp.Name, workflowapp.AppVersion) - - // ID can be used to e.g. set a build status. - buildLater := buildLaterStruct{ - Tags: tags, - Extra: extra, - Id: workflowapp.ID, - } - - reservedFound := false - for _, appname := range reservedNames { - if strings.ToUpper(workflowapp.Name) == strings.ToUpper(appname) { - buildLaterList = append(buildLaterList, buildLater) - - reservedFound = true - break - } - } - - /// Only upload if successful and no errors - if !reservedFound { - buildLaterFirst = append(buildLaterFirst, buildLater) - } else { - log.Printf("[WARNING] Skipping build of %s to later", workflowapp.Name) - } - } - } - } - - if len(buildLaterFirst) == 0 && len(buildLaterList) == 0 { - return buildLaterFirst, buildLaterList, err - } - - // This is getting silly - cacheKey := fmt.Sprintf("workflowapps-sorted-100") - shuffle.DeleteCache(ctx, cacheKey) - cacheKey = fmt.Sprintf("workflowapps-sorted-500") - shuffle.DeleteCache(ctx, cacheKey) - cacheKey = fmt.Sprintf("workflowapps-sorted-1000") - shuffle.DeleteCache(ctx, cacheKey) - - //log.Printf("BUILDLATERFIRST: %d, BUILDLATERLIST: %d", len(buildLaterFirst), len(buildLaterList)) - if len(extra) == 0 { - log.Printf("[INFO] Starting build of %d containers (FIRST)", len(buildLaterFirst)) - for _, item := range buildLaterFirst { - err = buildImageMemory(fs, item.Tags, item.Extra, true) - if err != nil { - log.Printf("Failed image build memory: %s", err) - } else { - if len(item.Tags) > 0 { - log.Printf("[INFO] Successfully built image %s", item.Tags[0]) - - } else { - log.Printf("[INFO] Successfully built Docker image") - } - } - } - - log.Printf("[INFO] Starting build of %d skipped docker images", len(buildLaterList)) - for _, item := range buildLaterList { - err = buildImageMemory(fs, item.Tags, item.Extra, true) - if err != nil { - log.Printf("[INFO] Failed image build memory: %s", err) - } else { - if len(item.Tags) > 0 { - log.Printf("[INFO] Successfully built image %s", item.Tags[0]) - } else { - log.Printf("[INFO] Successfully built Docker image") - } - } - } - } - - return buildLaterFirst, buildLaterList, err -} - func setNewWorkflowApp(resp http.ResponseWriter, request *http.Request) { cors := handleCors(resp, request) if cors { @@ -3875,7 +3382,7 @@ func setNewWorkflowApp(resp http.ResponseWriter, request *http.Request) { return } - err = checkWorkflowApp(workflowapp) + err = shuffle.CheckWorkflowApp(workflowapp) if err != nil { log.Printf("%s for app %s:%s", err, workflowapp.Name, workflowapp.AppVersion) resp.WriteHeader(401) diff --git a/docker-compose.yml b/docker-compose.yml index 513fd4a9..466f1546 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -36,8 +36,7 @@ services: - SHUFFLE_FILE_LOCATION=/shuffle-files restart: unless-stopped #depends_on: - #- opensearch - #- database + # - opensearch #- Not necessary because dependancy is handled within the backend itself instead orborus: #build: ./functions/onprem/orborus image: ghcr.io/frikky/shuffle-orborus:nightly diff --git a/frontend/src/components/ConfigureWorkflow.jsx b/frontend/src/components/ConfigureWorkflow.jsx index 0cc6182a..e82ebb6b 100644 --- a/frontend/src/components/ConfigureWorkflow.jsx +++ b/frontend/src/components/ConfigureWorkflow.jsx @@ -12,7 +12,7 @@ import { FixName } from "../views/Apps.jsx"; // // Specifically used for UNSAVED workflows only? const Workflow = (props) => { - const { globalUrl, theme, workflow, appAuthentication, setSelectedAction, setAuthenticationModalOpen, setSelectedApp, apps, selectedAction,setConfigureWorkflowModalOpen, saveWorkflow, newWebhook, submitSchedule, referenceUrl, isCloud, } = props + const { globalUrl, theme, workflow, appAuthentication, setSelectedAction, setAuthenticationModalOpen, setSelectedApp, apps, selectedAction,setConfigureWorkflowModalOpen, saveWorkflow, newWebhook, submitSchedule, referenceUrl, isCloud, setAuthenticationType, } = props const [requiredActions, setRequiredActions] = React.useState([]) const [requiredVariables, setRequiredVariables] = React.useState([]) const [requiredTriggers, setRequiredTriggers] = React.useState([]) @@ -374,6 +374,17 @@ const Workflow = (props) => { : { + setAuthenticationType(action.app.authentication.type === "oauth2" && action.app.authentication.redirect_uri !== undefined && action.app.authentication.redirect_uri !== null ? + { + "type": "oauth2", + "redirect_uri": action.app.authentication.redirect_uri, + "token_uri": action.app.authentication.token_uri, + "scope": action.app.authentication.scope, + } : { + "type": "" + } + ) + setItemChanged(true) setSelectedAction(action.action) setSelectedApp(action.app) diff --git a/frontend/src/components/Oauth2Auth.jsx b/frontend/src/components/Oauth2Auth.jsx new file mode 100644 index 00000000..2203a7a3 --- /dev/null +++ b/frontend/src/components/Oauth2Auth.jsx @@ -0,0 +1,258 @@ +import React, {useRef, useState, useEffect, useLayoutEffect} from 'react'; +import { useTheme } from '@material-ui/core/styles'; + +import { v4 as uuidv4 } from 'uuid'; +import { TextField, Drawer, Button, Paper, Grid, Tabs, InputAdornment, Tab, ButtonBase, Tooltip, Select, MenuItem, Divider, Dialog, Modal, DialogActions, DialogTitle, InputLabel, DialogContent, FormControl, IconButton, Menu, Input, FormGroup, FormControlLabel, Typography, Checkbox, Breadcrumbs, CircularProgress, Switch, Fade } from '@material-ui/core'; +import { LockOpen as LockOpenIcon } from '@material-ui/icons'; + +const AuthenticationOauth2 = (props) => { + const { saveWorkflow, selectedApp, workflow, selectedAction, authenticationType, getAppAuthentication, appAuthentication, setSelectedAction, setNewAppAuth, setAuthenticationModalOpen} = props; + const theme = useTheme(); + + //const [update, setUpdate] = React.useState("|") + const [clientId, setClientId] = React.useState("") + const [clientSecret, setClientSecret] = React.useState("") + const [buttonClicked, setButtonClicked] = React.useState(false) + const [authenticationOption, setAuthenticationOptions] = React.useState({ + app: JSON.parse(JSON.stringify(selectedApp)), + fields: {}, + label: "", + usage: [{ + workflow_id: workflow.id, + }], + id: uuidv4(), + active: true, + }) + + const handleOauth2Request = (client_id, client_secret) => { + setButtonClicked(true) + //if (authenticationType.type === "oauth2" && authenticationType.redirect_uri !== undefined && authenticationType.redirect_uri !== null) { + // These are test credentials + //const client_id = "dae24316-4bec-4832-b660-4cba6dc2477b" + //const client_secret = "._Qu3EvYY-OW_D57uy79qwEo.32qD6.l0z" + + const authentication_url = authenticationType.token_uri + + const resources = "UserAuthenticationMethod.ReadWrite.All" + if (authenticationType.scope !== undefined && authenticationType.scope !== null) { + console.log("EDIT SCOPE!") + } + + const redirectUri = `http://${window.location.host}/set_authentication` + const state = `workflow_id%3D${workflow.id}%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}` + + const url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&scope=${resources}&state=${state}` + // &resource=https%3A%2F%2Fgraph.microsoft.com& + + // FIXME: Awful, but works for prototyping + // How can we get a callback properly realtime? + // How can we properly try-catch without breaks on error? + try { + + var newwin = window.open(url, "", "width=400,height=200") + console.log(newwin) + setTimeout(() => { + console.log(newwin) + console.log("CLOSED", newwin.closed) + }, 1000) + + setTimeout(() => { + console.log(newwin) + console.log("CLOSED", newwin.closed) + if (newwin.closed) { + getAppAuthentication(true, true) + setTimeout(() => { + console.log("APPAUTH: ", appAuthentication) + + //{selectedAction.authentication.map(data => { + + setAuthenticationModalOpen(false) + saveWorkflow(workflow) + }, 1500) + } + }, 10000) + } catch (e) { + alert.error("Failed authentication - probably bad credentials. Try again") + setButtonClicked(false) + } + + return + //do { + //} while ( + } + + if (selectedApp.authentication === undefined) { + return null + } + + if (selectedApp.authentication.parameters === null || selectedApp.authentication.parameters === undefined || selectedApp.authentication.parameters.length === 0) { + return null + } + + 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 = () => { + console.log("NEW AUTH: ", authenticationOption) + if (authenticationOption.label.length === 0) { + authenticationOption.label = `Auth for ${selectedApp.name}` + //alert.info("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 { + alert.info("Field "+selectedApp.authentication.parameters[key].name+" 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 + setNewAppAuth(newAuthOption) + //appAuthentication.push(newAuthOption) + //setAppAuthentication(appAuthentication) + // + + //if (configureWorkflowModalOpen) { + // setSelectedAction({}) + //} + //setUpdate(authenticationOption.id) + + /* + {selectedAction.authentication.map(data => ( + + */ + + } + + if (authenticationOption.label === null || authenticationOption.label === undefined) { + authenticationOption.label = selectedApp.name+" authentication" + } + + //console.log( + return ( + + + + Oauth2 requires a client ID and secret to authenticate. This is usually made in the remote system. + Learn more about Oauth2 + + {/* { + authenticationOption.label = event.target.value + }} + /> + + */} + { + setClientId(event.target.value) + //authenticationOption.label = event.target.value + }} + /> + { + setClientSecret(event.target.value) + //authenticationOption.label = event.target.value + }} + /> + { + //setAuthenticationModalOpen(false) + handleOauth2Request(clientId, clientSecret) + }} + color="primary" + > + {buttonClicked ? + + : + "Oauth2 request" + } + + + + ) +} + +export default AuthenticationOauth2 diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index e5b9bdd8..3d0013ea 100644 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -1116,6 +1116,14 @@ const ParsedAction = (props) => { // itemColor = "#ffeb3b" //} {/**/} + + + + if (authenticationType.type === "oauth2" && data.configuration === true) { + return null + } + //&& authenticationType.redirect_uri !== undefined && authenticationType.redirect_uri !== null) { + return ( @@ -1389,47 +1397,7 @@ const ParsedAction = (props) => { { - console.log(authenticationType) - if (authenticationType.type === "oauth2" && authenticationType.redirect_uri !== undefined && authenticationType.redirect_uri !== null) { - // FIXME: Sending client secret and senitive info like this may not be ok. - const client_id = "dae24316-4bec-4832-b660-4cba6dc2477b" - const client_secret = "._Qu3EvYY-OW_D57uy79qwEo.32qD6.l0z" - - const authentication_url = authenticationType.token_uri - - const resources = "UserAuthenticationMethod.ReadWrite.All" - if (authenticationType.scope !== undefined && authenticationType.scope !== null) { - console.log("EDIT SCOPE!") - } - - const redirectUri = `http://${window.location.host}/set_authentication` - const state = `workflow_id%3D${workflow.id}%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}` - - const url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&scope=${resources}&state=${state}` - // &resource=https%3A%2F%2Fgraph.microsoft.com& - - // Awful, but works for prototyping - var newwin = window.open(url, "", "width=200,height=100") - console.log(newwin) - setTimeout(() => { - console.log(newwin) - console.log("CLOSED", newwin.closed) - }, 1000) - setTimeout(() => { - console.log(newwin) - console.log("CLOSED", newwin.closed) - if (newwin.closed) { - getAppAuthentication(true) - setTimeout(() => { - console.log("APPAUTH: ", appAuthentication) - }, 1500) - } - }, 10000) - - return - //do { - //} while ( - } + console.log(authenticationType) setAuthenticationModalOpen(true) }}> diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 98ef2984..e63065df 100644 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -28,6 +28,7 @@ import { useAlert } from "react-alert"; import { validateJson, GetIconInfo } from "./Workflows.jsx"; import { GetParsedPaths } from "./Apps.jsx"; import ConfigureWorkflow from '../components/ConfigureWorkflow.jsx'; +import AuthenticationOauth2 from "../components/Oauth2Auth.jsx"; import ParsedAction from '../components/ParsedAction.jsx'; import Scroll from 'react-scroll' import { Element as ScrollElement, animateScroll as scroll, scrollSpy, scroller } from 'react-scroll' @@ -404,7 +405,7 @@ const AngularWorkflow = (props) => { if (!responseJson.success) { alert.error("Failed to set app auth: "+responseJson.reason) } else { - getAppAuthentication(true) + getAppAuthentication(true, false) setAuthenticationModalOpen(false) // Needs a refresh with the new authentication.. @@ -1128,7 +1129,7 @@ const AngularWorkflow = (props) => { "http", ] - const getAppAuthentication = (reset) => { + const getAppAuthentication = (reset, updateAction) => { fetch(globalUrl+"/api/v1/apps/authentication", { method: 'GET', headers: { @@ -1154,6 +1155,8 @@ const AngularWorkflow = (props) => { newauth.push(responseJson.data[key]) } + + //{selectedAction.authentication.map(data => { if (cy !== undefined) { console.log("NEW AUTH = reset cy's onnodeselect") @@ -1166,6 +1169,65 @@ const AngularWorkflow = (props) => { setAppAuthentication(newauth) setAuthLoaded(true) + + if (updateAction === true) { + //console.log("Should update authentication for selectedAction!!") + console.log(responseJson) + + if (selectedApp.authentication.required) { + //console.log("App requires auth!!") + // Setup auth here :) + const authenticationOptions = [] + var findAuthId = "" + if (selectedAction.authentication_id !== null && selectedAction.authentication_id !== undefined && selectedAction.authentication_id.length > 0) { + findAuthId = selectedAction.authentication_id + } + + var tmpAuth = JSON.parse(JSON.stringify(responseJson.data)) + console.log("FOUND AUTH: ", tmpAuth) + + //console.log("Checking authentication: ", tmpAuth) + var latest = 0 + for (var key in tmpAuth) { + var item = tmpAuth[key] + + const newfields = {} + for (var filterkey in item.fields) { + newfields[item.fields[filterkey].key] = item.fields[filterkey].value + } + + item.fields = newfields + if (item.app.name === selectedApp.name) { + authenticationOptions.push(item) + + // Always becoming the last one + if (item.edited > latest) { + latest = item.edited + selectedAction.selectedAuthentication = item + } + } + } + + //if (item.id === findAuthId) { + // selectedAction.selectedAuthentication = item + //} + + console.log("ACTION: ", selectedAction) + + //console.log("OPTIONS: ", authenticationOptions) + selectedAction.authentication = authenticationOptions + //console.log("Authentication: ", authenticationOptions) + if (selectedAction.selectedAuthentication === null || selectedAction.selectedAuthentication === undefined || selectedAction.selectedAuthentication.length === "") { + selectedAction.selectedAuthentication = {} + } + + setSelectedAction(selectedAction) + + alert.info("Updated authentication for app?") + } else { + alert.info("No authentication to update") + } + } } else { setAuthLoaded(true) //alert.error("Failed getting authentications") @@ -1945,7 +2007,8 @@ const AngularWorkflow = (props) => { setSelectedAction(curaction) //return } else { - console.log("AUTHENTICATION: ", curapp.authentication) + //console.log("AUTHENTICATION: ", curapp.authentication) + //console.log(curapp.authentication) setAuthenticationType(curapp.authentication.type === "oauth2" && curapp.authentication.redirect_uri !== undefined && curapp.authentication.redirect_uri !== null ? { "type": "oauth2", @@ -5450,6 +5513,10 @@ const AngularWorkflow = (props) => { setLocalFirstrequest(false) } + const handleButtonRequest = () => { + + } + const outlookButton = { : null + const AuthenticationData = (props) => { const selectedApp = props.app @@ -8883,12 +8951,13 @@ const AngularWorkflow = (props) => { }}> - + : null // This whole part is redundant. Made it part of Arguments instead. + //console.log("TYPE: ", authenticationType) const authenticationModal = authenticationModalOpen ? { Authentication for {selectedApp.name} - + {authenticationType.type === "oauth2" ? + + : + + } : null //const loadedCheck = isLoaded && isLoggedIn && workflowDone ? diff --git a/frontend/src/views/Apps.jsx b/frontend/src/views/Apps.jsx index 8ef4c199..8a2ff410 100644 --- a/frontend/src/views/Apps.jsx +++ b/frontend/src/views/Apps.jsx @@ -984,19 +984,19 @@ const Apps = (props) => { {isCloud ? null : - - { - hotloadApps() - }} - > - - - + + { + hotloadApps() + }} + > + + + { - } + } { setLoadAppsModalOpen(false)} color="primary"> Cancel - { - handleGithubValidation(true) - }} color="primary"> - Force update - + {isCloud ? null : + { + handleGithubValidation(true) + }} color="primary"> + Force update + + } { handleGithubValidation(false) }} color="primary"> diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 9afab33a..c01c27b1 100644 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -51,7 +51,6 @@ var baseimagetagsuffix = os.Getenv("SHUFFLE_BASE_IMAGE_TAG_SUFFIX") var orgId = os.Getenv("ORG_ID") var baseUrl = os.Getenv("BASE_URL") - var environment = os.Getenv("ENVIRONMENT_NAME") var dockerApiVersion = os.Getenv("DOCKER_API_VERSION") var runningMode = strings.ToLower(os.Getenv("RUNNING_MODE"))