From f4b86abda75f50a547bc78c0fdfb1e10f266d148 Mon Sep 17 00:00:00 2001 From: frikky Date: Tue, 23 Nov 2021 00:26:53 +0100 Subject: [PATCH] #583: Added granular multi-org control and read-only user --- backend/go-app/main.go | 23 +++++++++++- backend/go-app/walkoff.go | 61 ++++++++++++++++++++++++++++++-- frontend/package.json | 2 +- frontend/src/views/Admin.jsx | 13 +++++-- frontend/src/views/Workflows.jsx | 14 ++++++-- 5 files changed, 104 insertions(+), 9 deletions(-) diff --git a/backend/go-app/main.go b/backend/go-app/main.go index d76fbb96..88cfe1a4 100644 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -2853,7 +2853,7 @@ func echoOpenapiData(resp http.ResponseWriter, request *http.Request) { } // Just here to verify that the user is logged in - _, err := shuffle.HandleApiAuthentication(resp, request) + user, err := shuffle.HandleApiAuthentication(resp, request) if err != nil { log.Printf("Api authentication failed in validate swagger: %s", err) resp.WriteHeader(401) @@ -2861,6 +2861,13 @@ func echoOpenapiData(resp http.ResponseWriter, request *http.Request) { return } + if user.Role == "org-reader" { + log.Printf("[WARNING] Org-reader doesn't have access to echo OpenAPI data: %s (%s)", user.Username, user.Id) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false, "reason": "Read only user"}`)) + return + } + body, err := ioutil.ReadAll(request.Body) if err != nil { log.Printf("Bodyreader err: %s", err) @@ -3335,6 +3342,13 @@ func verifySwagger(resp http.ResponseWriter, request *http.Request) { return } + if user.Role == "org-reader" { + log.Printf("[WARNING] Org-reader doesn't have access to check swagger doc: %s (%s)", user.Username, user.Id) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false, "reason": "Read only user"}`)) + return + } + body, err := ioutil.ReadAll(request.Body) if err != nil { resp.WriteHeader(401) @@ -5571,6 +5585,13 @@ func makeWorkflowPublic(resp http.ResponseWriter, request *http.Request) { return } + if user.Role == "org-reader" { + log.Printf("[WARNING] Org-reader doesn't have access publish workflow: %s (%s)", user.Username, user.Id) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false, "reason": "Read only user"}`)) + return + } + location := strings.Split(request.URL.String(), "/") var fileId string if location[1] == "api" { diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index d86ed51a..b38f5d9f 100644 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -676,6 +676,13 @@ func deleteWorkflow(resp http.ResponseWriter, request *http.Request) { return } + if user.Role == "org-reader" { + log.Printf("[WARNING] Org-reader doesn't have access to stop schedule: %s (%s)", user.Username, user.Id) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false, "reason": "Read only user"}`)) + return + } + location := strings.Split(request.URL.String(), "/") var fileId string @@ -1036,6 +1043,13 @@ func executeWorkflow(resp http.ResponseWriter, request *http.Request) { return } + if user.Role == "org-reader" { + log.Printf("[WARNING] Org-reader doesn't have access to run workflow: %s (%s)", user.Username, user.Id) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false, "reason": "Read only user"}`)) + return + } + location := strings.Split(request.URL.String(), "/") var fileId string @@ -1106,6 +1120,13 @@ func stopSchedule(resp http.ResponseWriter, request *http.Request) { return } + if user.Role == "org-reader" { + log.Printf("[WARNING] Org-reader doesn't have access to stop schedule: %s (%s)", user.Username, user.Id) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false, "reason": "Read only user"}`)) + return + } + location := strings.Split(request.URL.String(), "/") var fileId string @@ -1358,6 +1379,13 @@ func scheduleWorkflow(resp http.ResponseWriter, request *http.Request) { return } + if user.Role == "org-reader" { + log.Printf("[WARNING] Org-reader doesn't have access to schedule workflow: %s (%s)", user.Username, user.Id) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false, "reason": "Read only user"}`)) + return + } + location := strings.Split(request.URL.String(), "/") var fileId string @@ -1744,7 +1772,7 @@ func validateAppInput(resp http.ResponseWriter, request *http.Request) { // Just need to be logged in // FIXME - should have some permissions? - _, err := shuffle.HandleApiAuthentication(resp, request) + user, err := shuffle.HandleApiAuthentication(resp, request) if err != nil { log.Printf("Api authentication failed in set new app: %s", err) resp.WriteHeader(401) @@ -1752,6 +1780,13 @@ func validateAppInput(resp http.ResponseWriter, request *http.Request) { return } + if user.Role == "org-reader" { + log.Printf("[WARNING] Org-reader doesn't have access to delete apps: %s (%s)", user.Username, user.Id) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false, "reason": "Read only user"}`)) + return + } + filebytes, err := handleGetfile(resp, request) if err != nil { resp.WriteHeader(401) @@ -2237,8 +2272,7 @@ func setNewWorkflowApp(resp http.ResponseWriter, request *http.Request) { } // Just need to be logged in - // FIXME - should have some permissions? - _, err := shuffle.HandleApiAuthentication(resp, request) + user, err := shuffle.HandleApiAuthentication(resp, request) if err != nil { log.Printf("Api authentication failed in set new app: %s", err) resp.WriteHeader(401) @@ -2246,6 +2280,13 @@ func setNewWorkflowApp(resp http.ResponseWriter, request *http.Request) { return } + if user.Role == "org-reader" { + log.Printf("[WARNING] Org-reader doesn't have access to set new workflowapp: %s (%s)", user.Username, user.Id) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false, "reason": "Read only user"}`)) + return + } + body, err := ioutil.ReadAll(request.Body) if err != nil { log.Printf("Error with body read: %s", err) @@ -2470,6 +2511,13 @@ func executeSingleAction(resp http.ResponseWriter, request *http.Request) { return } + if user.Role == "org-reader" { + log.Printf("[WARNING] Org-reader doesn't have access to execute single action: %s (%s)", user.Username, user.Id) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false, "reason": "Read only user"}`)) + return + } + location := strings.Split(request.URL.String(), "/") var fileId string if location[1] == "api" { @@ -2925,6 +2973,13 @@ func LoadSpecificApps(resp http.ResponseWriter, request *http.Request) { return } + if user.Role != "admin" { + log.Printf("[WARNING] Not admin during app loading: %s (%s).", user.Username, user.Id) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false, "reason": "Not admin"}`)) + return + } + body, err := ioutil.ReadAll(request.Body) if err != nil { log.Printf("Error with body read: %s", err) diff --git a/frontend/package.json b/frontend/package.json index 6987b0c2..87981549 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -17,7 +17,7 @@ "babel-eslint": "^10.1.0", "class-transformer": "^0.4.0", "create-react-app": "^4.0.3", - "cytoscape": "^3.20.0", + "cytoscape": "^3.15.1", "cytoscape-clipboard": "^2.2.1", "cytoscape-cxtmenu": "^3.1.1", "cytoscape-edgehandles": "^3.6.0", diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index ca59cc48..fc2c1429 100644 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -2834,7 +2834,7 @@ const Admin = (props) => { }} value={"admin"} > - Admin + Org Admin { }} value={"user"} > - User + Org User + + + Org Reader } diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index c1688af1..de86d8c9 100644 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -1194,7 +1194,7 @@ const Workflows = (props) => { .then((response) => { if (response.status !== 200) { console.log("Status not 200 for setting workflows :O!"); - alert.error("Failed deleting workflow"); + alert.error("Failed deleting workflow. Do you have access?"); } else { alert.success("Deleted workflow " + id); } @@ -1302,7 +1302,7 @@ const Workflows = (props) => { style={{ backgroundColor: inputColor, color: "white" }} onClick={() => { setModalOpen(true); - setEditingWorkflow(data); + setEditingWorkflow(JSON.parse(JSON.stringify(data))); setNewWorkflowName(data.name); setNewWorkflowDescription(data.description); setDefaultReturnValue(data.default_return_value); @@ -1728,6 +1728,16 @@ const Workflows = (props) => { return response.json(); }) .then((responseJson) => { + if (responseJson.success === false) { + if (responseJson.reason !== undefined) { + alert.error("Error setting workflow: ", responseJson.reason) + } else { + alert.error("Error setting workflow.") + } + + return + } + if (method === "POST" && redirect) { window.location.pathname = "/workflows/" + responseJson["id"]; setModalOpen(false);