diff --git a/backend/go-app/main.go b/backend/go-app/main.go index ef55cead..fde61383 100644 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -8164,6 +8164,7 @@ func initHandlers() { r.HandleFunc("/api/v1/apps/authentication", getAppAuthentication).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/apps/authentication", addAppAuthentication).Methods("PUT", "OPTIONS") + r.HandleFunc("/api/v1/apps/authentication/{appauthId}/config", setAuthenticationConfig).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/apps/authentication/{appauthId}", deleteAppAuthentication).Methods("DELETE", "OPTIONS") diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index cdafdca8..5be7cad8 100644 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -4614,6 +4614,149 @@ func getWorkflowAppConfig(resp http.ResponseWriter, request *http.Request) { resp.Write(data) } +//r.HandleFunc("/api/v1/apps/authentication/{appauthId}/config", setAuthenticationConfig).Methods("POST", "OPTIONS") +func setAuthenticationConfig(resp http.ResponseWriter, request *http.Request) { + cors := handleCors(resp, request) + if cors { + return + } + + user, userErr := handleApiAuthentication(resp, request) + if userErr != nil { + log.Printf("Api authentication failed in get all apps: %s", userErr) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + if user.Role != "admin" { + log.Printf("[WARNING] User isn't admin during auth edit config") + resp.WriteHeader(409) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Must be admin to perform this action"}`))) + return + } + + var fileId string + location := strings.Split(request.URL.String(), "/") + if location[1] == "api" { + if len(location) <= 5 { + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + fileId = location[5] + } + log.Printf("FILE: %s", fileId) + + 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 + } + + type configAuth struct { + Id string `json:"id"` + Action string `json:"action"` + } + + var config configAuth + err = json.Unmarshal(body, &config) + if err != nil { + log.Printf("Failed unmarshaling (appauth): %s", err) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + if config.Id != fileId { + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false, "reason": "Bad ID match"}`)) + return + } + + log.Printf("body: %s", string(body)) + ctx := context.Background() + auth, err := getWorkflowAppAuthDatastore(ctx, fileId) + if err != nil { + log.Printf("Authget error: %s", err) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false, "reason": ":("}`)) + return + } + + if auth.OrgId != user.ActiveOrg.Id { + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false, "reason": "User can't edit this org"}`)) + return + } + + if config.Action == "assign_everywhere" { + q := datastore.NewQuery("workflow").Filter("org_id =", user.ActiveOrg.Id) + q = q.Order("-edited").Limit(35) + + var workflows []Workflow + _, err = dbclient.GetAll(ctx, q, &workflows) + if err != nil { + log.Printf("Getall error in auth update: %s", err) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false, "reason": "Failed getting workflows to update"}`)) + return + } + + // FIXME: Add function to remove auth from other auth's + actionCnt := 0 + workflowCnt := 0 + for _, workflow := range workflows { + newActions := []Action{} + edited := false + for _, action := range workflow.Actions { + if action.AppName == auth.App.Name { + //log.Printf("FOUND ACTION TO UPDATE: %#v", action) + edited = true + actionCnt += 1 + } + + newActions = append(newActions, action) + } + + workflow.Actions = newActions + if edited { + err = setWorkflow(ctx, workflow, workflow.ID) + if err != nil { + log.Printf("Failed setting (authupdate) workflow: %s", err) + continue + } + + workflowCnt += 1 + } + } + + if actionCnt > 0 && workflowCnt > 0 { + auth.WorkflowCount = int64(workflowCnt) + auth.NodeCount = int64(actionCnt) + + err = setWorkflowAppAuthDatastore(ctx, *auth, auth.Id) + if err != nil { + log.Printf("Failed setting appauth: %s", err) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false, "reason": "Failed setting app auth for all workflows"}`)) + return + } else { + // FIXME: Remove ALL workflows from other auths using the same + } + } + } + + resp.WriteHeader(200) + resp.Write([]byte(`{"success": true}`)) + //var config configAuth + + //log.Printf("Should set %s +} + func addAppAuthentication(resp http.ResponseWriter, request *http.Request) { cors := handleCors(resp, request) if cors { @@ -4645,11 +4788,43 @@ func addAppAuthentication(resp http.ResponseWriter, request *http.Request) { return } + ctx := context.Background() if len(appAuth.Id) == 0 { appAuth.Id = uuid.NewV4().String() + } else { + auth, err := getWorkflowAppAuthDatastore(ctx, appAuth.Id) + if err == nil { + // OrgId string `json:"org_id" datastore:"org_id"` + if auth.OrgId != user.ActiveOrg.Id { + log.Printf("[WARNING] User isn't a part of the right org during auth edit") + resp.WriteHeader(409) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": ":("}`))) + return + } + + if user.Role != "admin" { + log.Printf("[WARNING] User isn't admin during auth edit") + resp.WriteHeader(409) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": ":("}`))) + return + } + + if !auth.Active { + log.Printf("[WARNING] Auth isn't active for edit") + resp.WriteHeader(409) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Can't update an inactive auth"}`))) + return + } + + if auth.App.Name != appAuth.App.Name { + log.Printf("[WARNING] User tried to modify auth") + resp.WriteHeader(409) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Bad app configuration: need to specify correct name"}`))) + return + } + } } - ctx := context.Background() if len(appAuth.Label) == 0 { resp.WriteHeader(409) resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Label can't be empty"}`))) @@ -6434,6 +6609,18 @@ func getAllWorkflowAppAuth(ctx context.Context, OrgId string) ([]AppAuthenticati return allworkflowapps, nil } +func getWorkflowAppAuthDatastore(ctx context.Context, id string) (*AppAuthenticationStorage, error) { + + key := datastore.NameKey("workflowappauth", id, nil) + appAuth := &AppAuthenticationStorage{} + // New struct, to not add body, author etc + if err := dbclient.Get(ctx, key, appAuth); err != nil { + return &AppAuthenticationStorage{}, err + } + + return appAuth, nil +} + func setWorkflowAppAuthDatastore(ctx context.Context, workflowappauth AppAuthenticationStorage, id string) error { timeNow := int64(time.Now().Unix()) if workflowappauth.Created == 0 { diff --git a/frontend/src/components/OrgHeader.js b/frontend/src/components/OrgHeader.js index da6cf847..5d86745a 100644 --- a/frontend/src/components/OrgHeader.js +++ b/frontend/src/components/OrgHeader.js @@ -11,6 +11,7 @@ import { useAlert } from "react-alert"; import IconButton from '@material-ui/core/IconButton'; import ExpandLessIcon from '@material-ui/icons/ExpandLess'; import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; +import SaveIcon from '@material-ui/icons/Save'; const useStyles = makeStyles({ notchedOutline: { @@ -121,7 +122,7 @@ const OrgHeader = (props) => { "workflow_download_branch": workflowDownloadBranch, })} > - Save Changes + var imageData = file.length > 0 ? file : fileBase64 diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index b809cf62..ae7ec058 100644 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -31,6 +31,8 @@ import { useTheme } from '@material-ui/core/styles'; import HandlePayment from './HandlePayment' import OrgHeader from '../components/OrgHeader' +import EditIcon from '@material-ui/icons/Edit'; +import SelectAllIcon from '@material-ui/icons/SelectAll'; import OpenInNewIcon from '@material-ui/icons/OpenInNew'; import CloudDownloadIcon from '@material-ui/icons/CloudDownload'; import DescriptionIcon from '@material-ui/icons/Description'; @@ -87,6 +89,7 @@ const Admin = (props) => { const [selectedUserModalOpen, setSelectedUserModalOpen] = React.useState(false) const [selectedAuthentication, setSelectedAuthentication] = React.useState({}) const [selectedAuthenticationModalOpen, setSelectedAuthenticationModalOpen] = React.useState(false) + const [authenticationFields, setAuthenticationFields] = React.useState([]) const [showArchived, setShowArchived] = React.useState(false) const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" @@ -276,9 +279,69 @@ const Admin = (props) => { }) } - - - + const saveAuthentication = (authentication) => { + const data = authentication + const url = globalUrl + '/api/v1/apps/authentication'; + + fetch(url, { + mode: 'cors', + method: 'PUT', + body: JSON.stringify(data), + credentials: 'include', + crossDomain: true, + withCredentials: true, + headers: { + 'Content-Type': 'application/json; charset=utf-8', + }, + }) + .then(response => + response.json().then(responseJson => { + if (responseJson["success"] === false) { + alert.error("Failed changing authentication") + } else { + //alert.success("Successfully password!") + setSelectedUserModalOpen(false) + } + }), + ) + .catch(error => { + alert.error("Err: " + error.toString()) + }); + } + + const editAuthenticationConfig = (id) => { + const data = { + "id": id, + "action": "assign_everywhere", + } + const url = globalUrl + '/api/v1/apps/authentication/'+id+"/config"; + + fetch(url, { + mode: 'cors', + method: 'POST', + body: JSON.stringify(data), + credentials: 'include', + crossDomain: true, + withCredentials: true, + headers: { + 'Content-Type': 'application/json; charset=utf-8', + }, + }) + .then(response => + response.json().then(responseJson => { + if (responseJson["success"] === false) { + alert.error("Failed overwriting appauth in workflows") + } else { + alert.success("Successfully updated auth everywhere!") + setSelectedUserModalOpen(false) + getAppAuthentication() + } + }), + ) + .catch(error => { + alert.error("Err: " + error.toString()) + }); + } const onPasswordChange = () => { const data = { "username": selectedUser.username, "newpassword": newPassword } @@ -300,7 +363,7 @@ const Admin = (props) => { if (responseJson["success"] === false) { alert.error("Failed setting new password") } else { - alert.success("Successfully password!") + alert.success("Successfully updated password!") setSelectedUserModalOpen(false) } }), @@ -599,7 +662,7 @@ const Admin = (props) => { return response.json() }) .then((responseJson) => { - console.log(responseJson) + //console.log(responseJson) setFiles(responseJson) }) .catch(error => { @@ -966,8 +1029,8 @@ const Admin = (props) => { }); } - const editAuthenticationModal = - { setSelectedAuthenticationModalOpen(false) }} PaperProps={{ @@ -979,56 +1042,71 @@ const Admin = (props) => { }, }} > - Edit authentication + Edit authentication for {selectedAuthentication.app.name} ({selectedAuthentication.label}) - - setNewPassword(e.target.value)} - /> - onPasswordChange()} - > - Submit - - - - deleteUser(selectedUser)} - > - {selectedUser.active ? "Deactivate" : "Activate"} - - generateApikey(selectedUser.id)} - > - Get new API key - + {selectedAuthentication.fields.map((data, index) => { + return ( + + {data.key} + { + authenticationFields[index].value = e.target.value + setAuthenticationFields(authenticationFields) + }} + /> + + ) + })} + + setSelectedAuthenticationModalOpen(false)} color="primary"> + Cancel + + { + var error = false + for (var key in authenticationFields) { + const item = authenticationFields[key] + if (item.value.length === 0) { + console.log("ITEM: ", item) + //var currentnode = cy.getElementById(data.id) + var textfield = document.getElementById(`authentication-${key}`) + if (textfield !== null && textfield !== undefined) { + console.log("HANDLE ERROR FOR KEY ", key) + } + error = true + } + } + + if (error) { + alert.error("All fields must have a new value") + } else { + alert.success("Saving new version of this authentication") + selectedAuthentication.fields = authenticationFields + saveAuthentication(selectedAuthentication) + setSelectedAuthentication({}) + setSelectedAuthenticationModalOpen(false) + } + }} color="primary"> + Submit + + + : null const editUserModal = { }, }} > - Edit user + { : null + const updateAppAuthentication = (field) => { + setSelectedAuthenticationModalOpen(true) + setSelectedAuthentication(field) + //{selectedAuthentication.fields.map((data, index) => { + var newfields = [] + for (var key in field.fields) { + newfields.push({ + "key": field.fields[key].key, + "value": "", + }) + } + setAuthenticationFields(newfields) + } + const authenticationView = curTab === 2 ? @@ -1952,7 +2044,7 @@ const Admin = (props) => { /> { /> { /> { /> { @@ -2009,16 +2101,28 @@ const Admin = (props) => { style={{minWidth: 200, maxWidth: 200, overflow: "hidden"}} /> - { + updateAppAuthentication(data) + }} + > + + + { + editAuthenticationConfig(data.id) + }} + > + + + { deleteAuthentication(data) }} > - Delete - + + )