diff --git a/admin/auth.go b/admin/auth.go index d5ac337c..cf3affed 100644 --- a/admin/auth.go +++ b/admin/auth.go @@ -25,8 +25,54 @@ const ( ) // Helper to verify if user is an admin -func checkAdminLevel(level string) bool { - return (level == adminLevel) +func checkAdminLevel(user users.AdminUser) bool { + return user.Admin +} + +// Helper to check if query access is granted +func checkQueryLevel(permissions users.UserPermissions) bool { + return permissions.Query +} + +// Helper to check if carve access is granted +func checkCarveLevel(permissions users.UserPermissions) bool { + return permissions.Carve +} + +// Helper to check if environment access is granted +func checkEnvironmentLevel(permissions users.UserPermissions, environment string) bool { + return permissions.Environments[environment] +} + +// Helper to check permissions for a user +func checkPermissions(username string, query, carve, env bool, environment string) bool { + exist, user := adminUsers.ExistsGet(username) + if !exist { + return false + } + // Admin always have access + if user.Admin { + return true + } + perms, err := adminUsers.ConvertPermissions(user.Permissions.RawMessage) + if err != nil { + log.Printf("error converting permissions %v", err) + return false + } + // Check for query access + if query { + return checkQueryLevel(perms) + } + // Check for carve access + if carve { + return checkCarveLevel(perms) + } + // Check for environment access + if env { + return checkEnvironmentLevel(perms, environment) + } + // At this point, no access granted + return false } // Handler to check access to a resource based on the authentication enabled diff --git a/admin/handlers-get.go b/admin/handlers-get.go index 8ea4537e..eef8e966 100644 --- a/admin/handlers-get.go +++ b/admin/handlers-get.go @@ -101,6 +101,14 @@ func environmentHandler(w http.ResponseWriter, r *http.Request) { log.Printf("error unknown environment (%s)", env) return } + // Get context data + ctx := r.Context().Value(contextKey("session")).(contextValue) + // Check permissions + if !checkPermissions(ctx[ctxUser], false, false, true, env) { + log.Printf("%s has insuficient permissions", ctx[ctxUser]) + incMetric(metricTokenErr) + return + } // Extract target // FIXME verify target target, ok := vars["target"] @@ -132,8 +140,6 @@ func environmentHandler(w http.ResponseWriter, r *http.Request) { log.Printf("error getting platforms: %v", err) return } - // Get context data - ctx := r.Context().Value(contextKey("session")).(contextValue) // Prepare template data templateData := TableTemplateData{ Title: "Nodes in " + env, @@ -176,6 +182,14 @@ func platformHandler(w http.ResponseWriter, r *http.Request) { log.Println("error getting target") return } + // Get context data + ctx := r.Context().Value(contextKey("session")).(contextValue) + // Check permissions + if !checkPermissions(ctx[ctxUser], false, false, false, "") { + log.Printf("%s has insuficient permissions", ctx[ctxUser]) + incMetric(metricTokenErr) + return + } // Prepare template t, err := template.ParseFiles( templatesFilesFolder+"/table.html", @@ -204,8 +218,6 @@ func platformHandler(w http.ResponseWriter, r *http.Request) { log.Printf("error getting platforms: %v", err) return } - // Get context data - ctx := r.Context().Value(contextKey("session")).(contextValue) // Prepare template data templateData := TableTemplateData{ Title: "Nodes in " + platform, @@ -234,7 +246,7 @@ func queryRunGETHandler(w http.ResponseWriter, r *http.Request) { // Get context data ctx := r.Context().Value(contextKey("session")).(contextValue) // Check permissions - if !checkAdminLevel(ctx[ctxLevel]) { + if !checkPermissions(ctx[ctxUser], true, false, false, "") { log.Printf("%s has insuficient permissions", ctx[ctxUser]) incMetric(metricAdminErr) return @@ -310,7 +322,7 @@ func queryListGETHandler(w http.ResponseWriter, r *http.Request) { // Get context data ctx := r.Context().Value(contextKey("session")).(contextValue) // Check permissions - if !checkAdminLevel(ctx[ctxLevel]) { + if !checkPermissions(ctx[ctxUser], true, false, false, "") { log.Printf("%s has insuficient permissions", ctx[ctxUser]) incMetric(metricAdminErr) return @@ -363,7 +375,7 @@ func carvesRunGETHandler(w http.ResponseWriter, r *http.Request) { // Get context data ctx := r.Context().Value(contextKey("session")).(contextValue) // Check permissions - if !checkAdminLevel(ctx[ctxLevel]) { + if !checkPermissions(ctx[ctxUser], false, true, false, "") { log.Printf("%s has insuficient permissions", ctx[ctxUser]) incMetric(metricAdminErr) return @@ -433,7 +445,7 @@ func carvesListGETHandler(w http.ResponseWriter, r *http.Request) { // Get context data ctx := r.Context().Value(contextKey("session")).(contextValue) // Check permissions - if !checkAdminLevel(ctx[ctxLevel]) { + if !checkPermissions(ctx[ctxUser], false, true, false, "") { log.Printf("%s has insuficient permissions", ctx[ctxUser]) incMetric(metricAdminErr) return @@ -486,7 +498,7 @@ func queryLogsHandler(w http.ResponseWriter, r *http.Request) { // Get context data ctx := r.Context().Value(contextKey("session")).(contextValue) // Check permissions - if !checkAdminLevel(ctx[ctxLevel]) { + if !checkPermissions(ctx[ctxUser], true, false, false, "") { log.Printf("%s has insuficient permissions", ctx[ctxUser]) incMetric(metricAdminErr) return @@ -568,7 +580,7 @@ func carvesDetailsHandler(w http.ResponseWriter, r *http.Request) { // Get context data ctx := r.Context().Value(contextKey("session")).(contextValue) // Check permissions - if !checkAdminLevel(ctx[ctxLevel]) { + if !checkPermissions(ctx[ctxUser], false, true, false, "") { log.Printf("%s has insuficient permissions", ctx[ctxUser]) incMetric(metricAdminErr) return @@ -676,6 +688,14 @@ func confGETHandler(w http.ResponseWriter, r *http.Request) { log.Printf("error unknown environment (%s)", envVar) return } + // Get context data + ctx := r.Context().Value(contextKey("session")).(contextValue) + // Check permissions + if !checkPermissions(ctx[ctxUser], false, false, true, envVar) { + log.Printf("%s has insuficient permissions", ctx[ctxUser]) + incMetric(metricAdminErr) + return + } // Prepare template tempateFiles := NewTemplateFiles(templatesFilesFolder, "conf.html").filepaths t, err := template.ParseFiles(tempateFiles...) @@ -705,8 +725,6 @@ func confGETHandler(w http.ResponseWriter, r *http.Request) { log.Printf("error getting environment %v", err) return } - // Get context data - ctx := r.Context().Value(contextKey("session")).(contextValue) // Prepare template data templateData := ConfTemplateData{ Title: envVar + " Configuration", @@ -744,6 +762,14 @@ func enrollGETHandler(w http.ResponseWriter, r *http.Request) { log.Printf("error unknown environment (%s)", envVar) return } + // Get context data + ctx := r.Context().Value(contextKey("session")).(contextValue) + // Check permissions + if !checkPermissions(ctx[ctxUser], false, false, true, envVar) { + log.Printf("%s has insuficient permissions", ctx[ctxUser]) + incMetric(metricAdminErr) + return + } // Prepare template tempateFiles := NewTemplateFiles(templatesFilesFolder, "enroll.html").filepaths t, err := template.ParseFiles(tempateFiles...) @@ -773,8 +799,6 @@ func enrollGETHandler(w http.ResponseWriter, r *http.Request) { log.Printf("error getting environment %v", err) return } - // Get context data - ctx := r.Context().Value(contextKey("session")).(contextValue) // Prepare template data shellQuickAdd, _ := environments.QuickAddOneLinerShell(env) powershellQuickAdd, _ := environments.QuickAddOneLinerPowershell(env) @@ -886,7 +910,7 @@ func envsGETHandler(w http.ResponseWriter, r *http.Request) { // Get context data ctx := r.Context().Value(contextKey("session")).(contextValue) // Check permissions - if !checkAdminLevel(ctx[ctxLevel]) { + if !checkPermissions(ctx[ctxUser], false, false, false, "") { log.Printf("%s has insuficient permissions", ctx[ctxUser]) incMetric(metricAdminErr) return @@ -939,7 +963,7 @@ func settingsGETHandler(w http.ResponseWriter, r *http.Request) { // Get context data ctx := r.Context().Value(contextKey("session")).(contextValue) // Check permissions - if !checkAdminLevel(ctx[ctxLevel]) { + if !checkPermissions(ctx[ctxUser], false, false, false, "") { log.Printf("%s has insuficient permissions", ctx[ctxUser]) incMetric(metricAdminErr) return @@ -1019,7 +1043,7 @@ func usersGETHandler(w http.ResponseWriter, r *http.Request) { // Get context data ctx := r.Context().Value(contextKey("session")).(contextValue) // Check permissions - if !checkAdminLevel(ctx[ctxLevel]) { + if !checkPermissions(ctx[ctxUser], false, false, false, "") { log.Printf("%s has insuficient permissions", ctx[ctxUser]) incMetric(metricAdminErr) return @@ -1077,6 +1101,38 @@ func usersGETHandler(w http.ResponseWriter, r *http.Request) { incMetric(metricAdminOK) } +// Handler for platform/environment stats in JSON +func permissionsGETHandler(w http.ResponseWriter, r *http.Request) { + incMetric(metricAdminReq) + utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), false) + vars := mux.Vars(r) + // Extract username and verify + usernameVar, ok := vars["username"] + if !ok || !adminUsers.Exists(usernameVar) { + if settingsmgr.DebugService(settings.ServiceAdmin) { + log.Printf("DebugService: error getting username") + } + return + } + // Get context data + ctx := r.Context().Value(contextKey("session")).(contextValue) + // Check permissions + if !checkPermissions(ctx[ctxUser], false, false, false, "") { + log.Printf("%s has insuficient permissions", ctx[ctxUser]) + incMetric(metricAdminErr) + return + } + // Get permissions + permissions, err := adminUsers.GetPermissions(usernameVar) + if err != nil { + incMetric(metricAdminErr) + log.Printf("error getting permissions %v", err) + } + // Serve JSON + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, permissions) + incMetric(metricJSONOK) +} + // Handler for GET requests to download carves func carvesDownloadHandler(w http.ResponseWriter, r *http.Request) { incMetric(metricAdminReq) @@ -1085,7 +1141,7 @@ func carvesDownloadHandler(w http.ResponseWriter, r *http.Request) { // Get context data ctx := r.Context().Value(contextKey("session")).(contextValue) // Check permissions - if !checkAdminLevel(ctx[ctxLevel]) { + if !checkPermissions(ctx[ctxUser], false, true, false, "") { log.Printf("%s has insuficient permissions", ctx[ctxUser]) incMetric(metricAdminErr) return diff --git a/admin/handlers-post.go b/admin/handlers-post.go index ded00415..9ffd355b 100644 --- a/admin/handlers-post.go +++ b/admin/handlers-post.go @@ -10,6 +10,7 @@ import ( "github.com/jmpsec/osctrl/environments" "github.com/jmpsec/osctrl/queries" "github.com/jmpsec/osctrl/settings" + "github.com/jmpsec/osctrl/users" "github.com/jmpsec/osctrl/utils" "github.com/gorilla/mux" @@ -25,1303 +26,1166 @@ const ( func loginPOSTHandler(w http.ResponseWriter, r *http.Request) { incMetric(metricAdminReq) utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), false) - responseMessage := "OK" - responseCode := http.StatusOK var l LoginRequest // Parse request JSON body - err := json.NewDecoder(r.Body).Decode(&l) + if settingsmgr.DebugService(settings.ServiceAdmin) { + log.Println("DebugService: Decoding POST body") + } + if err := json.NewDecoder(r.Body).Decode(&l); err != nil { + adminErrorResponse(w, "error parsing POST body", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + // Check credentials + access, user := adminUsers.CheckLoginCredentials(l.Username, l.Password) + if !access { + adminErrorResponse(w, "invalid credentials", http.StatusForbidden, nil) + incMetric(metricAdminErr) + return + } + _, err := sessionsmgr.Save(r, w, user) if err != nil { - responseMessage = "error parsing POST body" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } else { - // Check credentials - if access, user := adminUsers.CheckLoginCredentials(l.Username, l.Password); access { - _, err = sessionsmgr.Save(r, w, user) - if err != nil { - responseMessage = "session error" - responseCode = http.StatusForbidden - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v)", responseMessage, err) - } - } - } else { - responseMessage = "invalid credentials" - responseCode = http.StatusForbidden - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } + adminErrorResponse(w, "session error", http.StatusForbidden, err) + incMetric(metricAdminErr) + return } // Serialize and send response if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Login response sent") } - utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, AdminResponse{Message: responseMessage}) + adminOKResponse(w, "OK") incMetric(metricAdminOK) } // Handle POST requests to logout -func logoutHandler(w http.ResponseWriter, r *http.Request) { +func logoutPOSTHandler(w http.ResponseWriter, r *http.Request) { incMetric(metricAdminReq) - responseMessage := "OK" - responseCode := http.StatusOK utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), false) var l LogoutRequest // Get context data ctx := r.Context().Value(contextKey("session")).(contextValue) // Parse request JSON body - err := json.NewDecoder(r.Body).Decode(&l) - if err != nil { + if settingsmgr.DebugService(settings.ServiceAdmin) { + log.Println("DebugService: Decoding POST body") + } + if err := json.NewDecoder(r.Body).Decode(&l); err != nil { + adminErrorResponse(w, "error parsing POST body", http.StatusInternalServerError, err) incMetric(metricAdminErr) - responseMessage = "error parsing POST body" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } else { - // Check CSRF Token - if checkCSRFToken(ctx[ctxCSRF], l.CSRFToken) { - // Destroy existing session - err := sessionsmgr.Destroy(r) - if err != nil { - log.Printf("error destroying session [ %v ]", err) - http.Error(w, "Session Error", http.StatusInternalServerError) - return - } - } else { - responseMessage = "invalid CSRF token" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } + return + } + // Check CSRF Token + if !checkCSRFToken(ctx[ctxCSRF], l.CSRFToken) { + adminErrorResponse(w, "invalid CSRF token", http.StatusInternalServerError, nil) + incMetric(metricAdminErr) + return + } + // Destroy existing session + if err := sessionsmgr.Destroy(r); err != nil { + adminErrorResponse(w, "error destroying session", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return } // Serialize and send response if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Logout response sent") } - utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, AdminResponse{Message: responseMessage}) + adminOKResponse(w, "OK") incMetric(metricAdminOK) } // Handler for POST requests to run queries func queryRunPOSTHandler(w http.ResponseWriter, r *http.Request) { incMetric(metricAdminReq) - responseMessage := "The query was created successfully" - responseCode := http.StatusOK utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), true) var q DistributedQueryRequest // Get context data ctx := r.Context().Value(contextKey("session")).(contextValue) - // Check permissions - if !checkAdminLevel(ctx[ctxLevel]) { - responseMessage = "insuficient permissions" - responseCode = http.StatusForbidden - log.Printf("%s has %s", ctx[ctxUser], responseMessage) - goto send_response + // Check permissions for query + if !checkPermissions(ctx[ctxUser], true, false, false, "") { + adminErrorResponse(w, fmt.Sprintf("%s has insuficient permissions", ctx[ctxUser]), http.StatusForbidden, nil) + incMetric(metricAdminErr) + return } // Parse request JSON body + if settingsmgr.DebugService(settings.ServiceAdmin) { + log.Println("DebugService: Decoding POST body") + } if err := json.NewDecoder(r.Body).Decode(&q); err != nil { - responseMessage = "error parsing POST body" - responseCode = http.StatusInternalServerError - log.Printf("%s %v", responseMessage, err) - goto send_response + adminErrorResponse(w, "error parsing POST body", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return } // Check CSRF Token - if checkCSRFToken(ctx[ctxCSRF], q.CSRFToken) { - // FIXME check validity of query - // Query can not be empty - if q.Query == "" { - responseMessage = "query can not be empty" - responseCode = http.StatusInternalServerError - log.Printf("%s", responseMessage) - goto send_response - } - // Prepare and create new query - newQuery := newQueryReady(ctx[ctxUser], q.Query) - if err := queriesmgr.Create(newQuery); err != nil { - responseMessage = "error creating query" - responseCode = http.StatusInternalServerError - log.Printf("%s %v", responseMessage, err) - goto send_response - } - // Temporary list of UUIDs to calculate Expected - var expected []string - // Create environment target - if len(q.Environments) > 0 { - for _, e := range q.Environments { - if (e != "") && envs.Exists(e) { - if err := queriesmgr.CreateTarget(newQuery.Name, queries.QueryTargetEnvironment, e); err != nil { - responseMessage = "error creating query environment target" - responseCode = http.StatusInternalServerError - log.Printf("%s %v", responseMessage, err) - goto send_response - } - nodes, err := nodesmgr.GetByEnv(e, "active", settingsmgr.InactiveHours()) - if err != nil { - responseMessage = "error getting nodes by environment" - responseCode = http.StatusInternalServerError - log.Printf("%s %v", responseMessage, err) - goto send_response - } - for _, n := range nodes { - expected = append(expected, n.UUID) - } + if !checkCSRFToken(ctx[ctxCSRF], q.CSRFToken) { + adminErrorResponse(w, "invalid CSRF token", http.StatusInternalServerError, nil) + incMetric(metricAdminErr) + return + } + // FIXME check validity of query + // Query can not be empty + if q.Query == "" { + adminErrorResponse(w, "query can not be empty", http.StatusInternalServerError, nil) + incMetric(metricAdminErr) + return + } + // FIXME check if query is carve and user has permissions to carve + // Prepare and create new query + newQuery := newQueryReady(ctx[ctxUser], q.Query) + if err := queriesmgr.Create(newQuery); err != nil { + adminErrorResponse(w, "error creating query", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + // Temporary list of UUIDs to calculate Expected + var expected []string + // Create environment target + if len(q.Environments) > 0 { + for _, e := range q.Environments { + if (e != "") && envs.Exists(e) { + if err := queriesmgr.CreateTarget(newQuery.Name, queries.QueryTargetEnvironment, e); err != nil { + adminErrorResponse(w, "error creating query environment target", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + nodes, err := nodesmgr.GetByEnv(e, "active", settingsmgr.InactiveHours()) + if err != nil { + adminErrorResponse(w, "error getting nodes by environment", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + for _, n := range nodes { + expected = append(expected, n.UUID) } } } - // Create platform target - if len(q.Platforms) > 0 { - for _, p := range q.Platforms { - if (p != "") && checkValidPlatform(p) { - if err := queriesmgr.CreateTarget(newQuery.Name, queries.QueryTargetPlatform, p); err != nil { - responseMessage = "error creating query platform target" - responseCode = http.StatusInternalServerError - log.Printf("%s %v", responseMessage, err) - goto send_response - } - nodes, err := nodesmgr.GetByPlatform(p, "active", settingsmgr.InactiveHours()) - if err != nil { - responseMessage = "error getting nodes by platform" - responseCode = http.StatusInternalServerError - log.Printf("%s %v", responseMessage, err) - goto send_response - } - for _, n := range nodes { - expected = append(expected, n.UUID) - } + } + // Create platform target + if len(q.Platforms) > 0 { + for _, p := range q.Platforms { + if (p != "") && checkValidPlatform(p) { + if err := queriesmgr.CreateTarget(newQuery.Name, queries.QueryTargetPlatform, p); err != nil { + adminErrorResponse(w, "error creating query platform target", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + nodes, err := nodesmgr.GetByPlatform(p, "active", settingsmgr.InactiveHours()) + if err != nil { + adminErrorResponse(w, "error getting nodes by platform", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + for _, n := range nodes { + expected = append(expected, n.UUID) } } } - // Create UUIDs target - if len(q.UUIDs) > 0 { - for _, u := range q.UUIDs { - if (u != "") && nodesmgr.CheckByUUID(u) { - if err := queriesmgr.CreateTarget(newQuery.Name, queries.QueryTargetUUID, u); err != nil { - responseMessage = "error creating query UUID target" - responseCode = http.StatusInternalServerError - log.Printf("%s %v", responseMessage, err) - goto send_response - } - expected = append(expected, u) + } + // Create UUIDs target + if len(q.UUIDs) > 0 { + for _, u := range q.UUIDs { + if (u != "") && nodesmgr.CheckByUUID(u) { + if err := queriesmgr.CreateTarget(newQuery.Name, queries.QueryTargetUUID, u); err != nil { + adminErrorResponse(w, "error creating query UUID target", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return } + expected = append(expected, u) } } - // Create hostnames target - if len(q.Hosts) > 0 { - for _, h := range q.Hosts { - if (h != "") && nodesmgr.CheckByHost(h) { - if err := queriesmgr.CreateTarget(newQuery.Name, queries.QueryTargetLocalname, h); err != nil { - responseMessage = "error creating query hostname target" - responseCode = http.StatusInternalServerError - log.Printf("%s %v", responseMessage, err) - goto send_response - } - expected = append(expected, h) + } + // Create hostnames target + if len(q.Hosts) > 0 { + for _, h := range q.Hosts { + if (h != "") && nodesmgr.CheckByHost(h) { + if err := queriesmgr.CreateTarget(newQuery.Name, queries.QueryTargetLocalname, h); err != nil { + adminErrorResponse(w, "error creating query hostname target", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return } + expected = append(expected, h) } } - // Remove duplicates from expected - expectedClear := removeStringDuplicates(expected) - // Update value for expected - if err := queriesmgr.SetExpected(newQuery.Name, len(expectedClear)); err != nil { - responseMessage = "error setting expected" - responseCode = http.StatusInternalServerError - log.Printf("%s %v", responseMessage, err) - goto send_response - } - } else { - responseMessage = "invalid CSRF token" - responseCode = http.StatusInternalServerError - log.Printf("%s", responseMessage) } -send_response: + // Remove duplicates from expected + expectedClear := removeStringDuplicates(expected) + // Update value for expected + if err := queriesmgr.SetExpected(newQuery.Name, len(expectedClear)); err != nil { + adminErrorResponse(w, "error setting expected", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } // Serialize and send response if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Query run response sent") } - utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, AdminResponse{Message: responseMessage}) + adminOKResponse(w, "OK") incMetric(metricAdminOK) } // Handler for POST requests to run file carves func carvesRunPOSTHandler(w http.ResponseWriter, r *http.Request) { incMetric(metricAdminReq) - responseMessage := "The carve was created successfully" - responseCode := http.StatusOK utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), true) var c DistributedCarveRequest // Get context data ctx := r.Context().Value(contextKey("session")).(contextValue) // Check permissions - if !checkAdminLevel(ctx[ctxLevel]) { - responseMessage = "insuficient permissions" - responseCode = http.StatusForbidden - log.Printf("%s has %s", ctx[ctxUser], responseMessage) - goto send_response + if !checkPermissions(ctx[ctxUser], false, true, false, "") { + adminErrorResponse(w, fmt.Sprintf("%s has insuficient permissions", ctx[ctxUser]), http.StatusForbidden, nil) + incMetric(metricAdminErr) + return } // Parse request JSON body + if settingsmgr.DebugService(settings.ServiceAdmin) { + log.Println("DebugService: Decoding POST body") + } if err := json.NewDecoder(r.Body).Decode(&c); err != nil { - responseMessage = "error parsing POST body" - responseCode = http.StatusInternalServerError - log.Printf("%s %v", responseMessage, err) - goto send_response + adminErrorResponse(w, "error parsing POST body", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return } // Check CSRF Token - if checkCSRFToken(ctx[ctxCSRF], c.CSRFToken) { - // FIXME check validity of query - // Path can not be empty - if c.Path == "" { - responseMessage = "path can not be empty" - responseCode = http.StatusInternalServerError - log.Printf("%s", responseMessage) - goto send_response - } - query := generateCarveQuery(c.Path, false) - // Prepare and create new carve - carveName := generateCarveName() - newQuery := queries.DistributedQuery{ - Query: query, - Name: carveName, - Creator: ctx[ctxUser], - Expected: 0, - Executions: 0, - Active: true, - Completed: false, - Deleted: false, - Type: queries.CarveQueryType, - Path: c.Path, - } - if err := queriesmgr.Create(newQuery); err != nil { - responseMessage = "error creating carve" - responseCode = http.StatusInternalServerError - log.Printf("%s %v", responseMessage, err) - goto send_response - } - // Temporary list of UUIDs to calculate Expected - var expected []string - // Create environment target - if len(c.Environments) > 0 { - for _, e := range c.Environments { - if (e != "") && envs.Exists(e) { - if err := queriesmgr.CreateTarget(carveName, queries.QueryTargetEnvironment, e); err != nil { - responseMessage = "error creating carve environment target" - responseCode = http.StatusInternalServerError - log.Printf("%s %v", responseMessage, err) - goto send_response - } - nodes, err := nodesmgr.GetByEnv(e, "active", settingsmgr.InactiveHours()) - if err != nil { - responseMessage = "error getting nodes by environment" - responseCode = http.StatusInternalServerError - log.Printf("%s %v", responseMessage, err) - goto send_response - } - for _, n := range nodes { - expected = append(expected, n.UUID) - } + if !checkCSRFToken(ctx[ctxCSRF], c.CSRFToken) { + adminErrorResponse(w, "invalid CSRF token", http.StatusInternalServerError, nil) + incMetric(metricAdminErr) + return + } + // FIXME check validity of query + // Path can not be empty + if c.Path == "" { + adminErrorResponse(w, "path can not be empty", http.StatusInternalServerError, nil) + incMetric(metricAdminErr) + return + } + query := generateCarveQuery(c.Path, false) + // Prepare and create new carve + carveName := generateCarveName() + newQuery := queries.DistributedQuery{ + Query: query, + Name: carveName, + Creator: ctx[ctxUser], + Expected: 0, + Executions: 0, + Active: true, + Completed: false, + Deleted: false, + Type: queries.CarveQueryType, + Path: c.Path, + } + if err := queriesmgr.Create(newQuery); err != nil { + adminErrorResponse(w, "error creating carve", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + // Temporary list of UUIDs to calculate Expected + var expected []string + // Create environment target + if len(c.Environments) > 0 { + for _, e := range c.Environments { + if (e != "") && envs.Exists(e) { + if err := queriesmgr.CreateTarget(carveName, queries.QueryTargetEnvironment, e); err != nil { + adminErrorResponse(w, "error creating carve environment target", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + nodes, err := nodesmgr.GetByEnv(e, "active", settingsmgr.InactiveHours()) + if err != nil { + adminErrorResponse(w, "error getting nodes by environment", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + for _, n := range nodes { + expected = append(expected, n.UUID) } } } - // Create platform target - if len(c.Platforms) > 0 { - for _, p := range c.Platforms { - if (p != "") && checkValidPlatform(p) { - if err := queriesmgr.CreateTarget(carveName, queries.QueryTargetPlatform, p); err != nil { - responseMessage = "error creating carve platform target" - responseCode = http.StatusInternalServerError - log.Printf("%s %v", responseMessage, err) - goto send_response - } - nodes, err := nodesmgr.GetByPlatform(p, "active", settingsmgr.InactiveHours()) - if err != nil { - responseMessage = "error getting nodes by platform" - responseCode = http.StatusInternalServerError - log.Printf("%s %v", responseMessage, err) - goto send_response - } - for _, n := range nodes { - expected = append(expected, n.UUID) - } + } + // Create platform target + if len(c.Platforms) > 0 { + for _, p := range c.Platforms { + if (p != "") && checkValidPlatform(p) { + if err := queriesmgr.CreateTarget(carveName, queries.QueryTargetPlatform, p); err != nil { + adminErrorResponse(w, "error creating carve platform target", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + nodes, err := nodesmgr.GetByPlatform(p, "active", settingsmgr.InactiveHours()) + if err != nil { + adminErrorResponse(w, "error getting nodes by platform", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + for _, n := range nodes { + expected = append(expected, n.UUID) } } } - // Create UUIDs target - if len(c.UUIDs) > 0 { - for _, u := range c.UUIDs { - if (u != "") && nodesmgr.CheckByUUID(u) { - if err := queriesmgr.CreateTarget(carveName, queries.QueryTargetUUID, u); err != nil { - responseMessage = "error creating carve UUID target" - responseCode = http.StatusInternalServerError - log.Printf("%s %v", responseMessage, err) - goto send_response - } - expected = append(expected, u) + } + // Create UUIDs target + if len(c.UUIDs) > 0 { + for _, u := range c.UUIDs { + if (u != "") && nodesmgr.CheckByUUID(u) { + if err := queriesmgr.CreateTarget(carveName, queries.QueryTargetUUID, u); err != nil { + adminErrorResponse(w, "error creating carve UUID target", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return } + expected = append(expected, u) } } - // Create hostnames target - if len(c.Hosts) > 0 { - for _, h := range c.Hosts { - if (h != "") && nodesmgr.CheckByHost(h) { - if err := queriesmgr.CreateTarget(carveName, queries.QueryTargetLocalname, h); err != nil { - responseMessage = "error creating carve hostname target" - responseCode = http.StatusInternalServerError - log.Printf("%s %v", responseMessage, err) - goto send_response - } + } + // Create hostnames target + if len(c.Hosts) > 0 { + for _, h := range c.Hosts { + if (h != "") && nodesmgr.CheckByHost(h) { + if err := queriesmgr.CreateTarget(carveName, queries.QueryTargetLocalname, h); err != nil { + adminErrorResponse(w, "error creating carve hostname target", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return } } } - // Remove duplicates from expected - expectedClear := removeStringDuplicates(expected) - // Update value for expected - if err := queriesmgr.SetExpected(carveName, len(expectedClear)); err != nil { - responseMessage = "error setting expected" - responseCode = http.StatusInternalServerError - log.Printf("%s %v", responseMessage, err) - goto send_response - } - } else { - responseMessage = "invalid CSRF token" - responseCode = http.StatusInternalServerError - log.Printf("%s", responseMessage) } -send_response: + // Remove duplicates from expected + expectedClear := removeStringDuplicates(expected) + // Update value for expected + if err := queriesmgr.SetExpected(carveName, len(expectedClear)); err != nil { + adminErrorResponse(w, "error setting expected", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } // Serialize and send response if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Carve run response sent") } - utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, AdminResponse{Message: responseMessage}) + adminOKResponse(w, "OK") incMetric(metricAdminOK) } // Handler for POST requests to queries func queryActionsPOSTHandler(w http.ResponseWriter, r *http.Request) { incMetric(metricAdminReq) - responseMessage := "OK" - responseCode := http.StatusOK utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), true) var q DistributedQueryActionRequest // Get context data ctx := r.Context().Value(contextKey("session")).(contextValue) - // Check permissions - if !checkAdminLevel(ctx[ctxLevel]) { - responseMessage = "insuficient permissions" - responseCode = http.StatusForbidden - log.Printf("%s has %s", ctx[ctxUser], responseMessage) - goto send_response + // Check permissions for query + if !checkPermissions(ctx[ctxUser], true, false, false, "") { + adminErrorResponse(w, fmt.Sprintf("%s has insuficient permissions", ctx[ctxUser]), http.StatusForbidden, nil) + incMetric(metricAdminErr) + return } // Parse request JSON body + if settingsmgr.DebugService(settings.ServiceAdmin) { + log.Println("DebugService: Decoding POST body") + } if err := json.NewDecoder(r.Body).Decode(&q); err != nil { - responseMessage = "error parsing POST body" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) + adminErrorResponse(w, "error parsing POST body", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + // Check CSRF Token + if !checkCSRFToken(ctx[ctxCSRF], q.CSRFToken) { + adminErrorResponse(w, "invalid CSRF token", http.StatusInternalServerError, nil) + incMetric(metricAdminErr) + return + } + switch q.Action { + case "delete": + for _, n := range q.Names { + if err := queriesmgr.Delete(n); err != nil { + adminErrorResponse(w, "error deleting query", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } } - } else { - // Check CSRF Token - if checkCSRFToken(ctx[ctxCSRF], q.CSRFToken) { - switch q.Action { - case "delete": - for _, n := range q.Names { - err := queriesmgr.Delete(n) - if err != nil { - responseMessage = "error deleting query" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } - } - case "complete": - for _, n := range q.Names { - err := queriesmgr.Complete(n) - if err != nil { - responseMessage = "error completing query" - responseCode = http.StatusInternalServerError - log.Printf("%s %v", responseMessage, err) - } - } - case "activate": - for _, n := range q.Names { - err := queriesmgr.Activate(n) - if err != nil { - responseMessage = "error activating query" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } - } + adminOKResponse(w, "queries delete successfully") + case "complete": + for _, n := range q.Names { + if err := queriesmgr.Complete(n); err != nil { + adminErrorResponse(w, "error completing query", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return } - } else { - responseMessage = "invalid CSRF token" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) + } + adminOKResponse(w, "queries completed successfully") + case "activate": + for _, n := range q.Names { + if err := queriesmgr.Activate(n); err != nil { + adminErrorResponse(w, "error activating query", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return } } + adminOKResponse(w, "queries activated successfully") } -send_response: // Serialize and send response if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Query run response sent") } - utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, AdminResponse{Message: responseMessage}) incMetric(metricAdminOK) } // Handler for POST requests to carves func carvesActionsPOSTHandler(w http.ResponseWriter, r *http.Request) { incMetric(metricAdminReq) - responseMessage := "OK" - responseCode := http.StatusOK utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), true) var q DistributedCarvesActionRequest // Get context data ctx := r.Context().Value(contextKey("session")).(contextValue) // Check permissions - if !checkAdminLevel(ctx[ctxLevel]) { - responseMessage = "insuficient permissions" - responseCode = http.StatusForbidden - log.Printf("%s has %s", ctx[ctxUser], responseMessage) - goto send_response + if !checkPermissions(ctx[ctxUser], false, true, false, "") { + adminErrorResponse(w, fmt.Sprintf("%s has insuficient permissions", ctx[ctxUser]), http.StatusForbidden, nil) + incMetric(metricAdminErr) + return } // Parse request JSON body + if settingsmgr.DebugService(settings.ServiceAdmin) { + log.Println("DebugService: Decoding POST body") + } if err := json.NewDecoder(r.Body).Decode(&q); err != nil { - responseMessage = "error parsing POST body" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } else { - // Check CSRF Token - if checkCSRFToken(ctx[ctxCSRF], q.CSRFToken) { - switch q.Action { - case "delete": - for _, n := range q.IDs { - err := carvesmgr.Delete(n) - if err != nil { - responseMessage = "error deleting carve" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } - } - case "test": - log.Println("Testing action") - } - } else { - responseMessage = "invalid CSRF token" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) + adminErrorResponse(w, "error parsing POST body", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + // Check CSRF Token + if !checkCSRFToken(ctx[ctxCSRF], q.CSRFToken) { + adminErrorResponse(w, "invalid CSRF token", http.StatusInternalServerError, nil) + incMetric(metricAdminErr) + return + } + switch q.Action { + case "delete": + for _, n := range q.IDs { + if err := carvesmgr.Delete(n); err != nil { + adminErrorResponse(w, "error deleting carve", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return } } + adminOKResponse(w, "carves delete successfully") + case "test": + if settingsmgr.DebugService(settings.ServiceAdmin) { + log.Printf("DebugService: testing action") + } + adminOKResponse(w, "test successful") } -send_response: // Serialize and send response if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Carves action response sent") } - utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, AdminResponse{Message: responseMessage}) incMetric(metricAdminOK) } // Handler POST requests for saving configuration func confPOSTHandler(w http.ResponseWriter, r *http.Request) { incMetric(metricAdminReq) - responseMessage := "Configuration saved successfully" - responseCode := http.StatusOK utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), true) vars := mux.Vars(r) // Extract environment and verify environmentVar, ok := vars["environment"] if !ok || !envs.Exists(environmentVar) { - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: error getting environment") - } - return - } - // Verify environment - if !envs.Exists(environmentVar) { - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: error unknown environment (%s)", environmentVar) - } + adminErrorResponse(w, "error getting environment", http.StatusInternalServerError, nil) + incMetric(metricAdminErr) return } var c ConfigurationRequest // Get context data ctx := r.Context().Value(contextKey("session")).(contextValue) // Check permissions - if !checkAdminLevel(ctx[ctxLevel]) { - responseMessage = "insuficient permissions" - responseCode = http.StatusForbidden - log.Printf("%s has %s", ctx[ctxUser], responseMessage) - goto send_response + if !checkPermissions(ctx[ctxUser], false, false, true, environmentVar) { + adminErrorResponse(w, fmt.Sprintf("%s has insuficient permissions", ctx[ctxUser]), http.StatusForbidden, nil) + incMetric(metricAdminErr) + return } // Parse request JSON body + if settingsmgr.DebugService(settings.ServiceAdmin) { + log.Println("DebugService: Decoding POST body") + } if err := json.NewDecoder(r.Body).Decode(&c); err != nil { - responseMessage = "error parsing POST body" - responseCode = http.StatusInternalServerError + adminErrorResponse(w, "error parsing POST body", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + // Check CSRF Token + if !checkCSRFToken(ctx[ctxCSRF], c.CSRFToken) { + adminErrorResponse(w, "invalid CSRF token", http.StatusInternalServerError, nil) + incMetric(metricAdminErr) + return + } + if c.ConfigurationB64 == "" { + responseMessage := "empty configuration" + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusInternalServerError, AdminResponse{Message: responseMessage}) if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } else { - // Check CSRF Token - if checkCSRFToken(ctx[ctxCSRF], c.CSRFToken) { - if c.ConfigurationB64 != "" { - configuration, err := base64.StdEncoding.DecodeString(c.ConfigurationB64) - if err != nil { - responseMessage = "error decoding configuration" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } else { - err = envs.UpdateConfiguration(environmentVar, string(configuration)) - if err != nil { - responseMessage = "error saving configuration" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } - } - } else { - responseMessage = "empty configuration" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } - } else { - responseMessage = "invalid CSRF token" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } + log.Printf("DebugService: %s", responseMessage) } + incMetric(metricAdminErr) + return + } + // Decode received configuration + configuration, err := base64.StdEncoding.DecodeString(c.ConfigurationB64) + if err != nil { + adminErrorResponse(w, "error decoding configuration", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + // Update configuration + if err := envs.UpdateConfiguration(environmentVar, string(configuration)); err != nil { + adminErrorResponse(w, "error saving configuration", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return } -send_response: // Serialize and send response if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Configuration response sent") } - utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, AdminResponse{Message: responseMessage}) + adminOKResponse(w, "configuration saved successfully") incMetric(metricAdminOK) } // Handler POST requests for saving intervals func intervalsPOSTHandler(w http.ResponseWriter, r *http.Request) { incMetric(metricAdminReq) - responseMessage := "Intervals updated successfully" - responseCode := http.StatusOK utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), true) vars := mux.Vars(r) - // Extract environment + // Extract environment and verify environmentVar, ok := vars["environment"] - if !ok { - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: error getting environment") - } - return - } - // Verify environment - if !envs.Exists(environmentVar) { - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: error unknown environment (%s)", environmentVar) - } + if !ok || !envs.Exists(environmentVar) { + adminErrorResponse(w, "error getting environment", http.StatusInternalServerError, nil) + incMetric(metricAdminErr) return } var c IntervalsRequest // Get context data ctx := r.Context().Value(contextKey("session")).(contextValue) + // Check permissions + if !checkPermissions(ctx[ctxUser], false, false, true, environmentVar) { + adminErrorResponse(w, fmt.Sprintf("%s has insuficient permissions", ctx[ctxUser]), http.StatusForbidden, nil) + incMetric(metricAdminErr) + return + } // Parse request JSON body - err := json.NewDecoder(r.Body).Decode(&c) + if settingsmgr.DebugService(settings.ServiceAdmin) { + log.Println("DebugService: Decoding POST body") + } + if err := json.NewDecoder(r.Body).Decode(&c); err != nil { + adminErrorResponse(w, "error parsing POST body", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + // Check CSRF Token + if !checkCSRFToken(ctx[ctxCSRF], c.CSRFToken) { + adminErrorResponse(w, "invalid CSRF token", http.StatusInternalServerError, nil) + incMetric(metricAdminErr) + return + } + if err := envs.UpdateIntervals(environmentVar, c.ConfigInterval, c.LogInterval, c.QueryInterval); err != nil { + adminErrorResponse(w, "error updating intervals", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + // After updating interval, you need to re-generate flags + flags, err := envs.GenerateFlagsEnv(environmentVar, "", "") if err != nil { - responseMessage = "error parsing POST body" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } else { - // Check CSRF Token - if checkCSRFToken(ctx[ctxCSRF], c.CSRFToken) { - err = envs.UpdateIntervals(environmentVar, c.ConfigInterval, c.LogInterval, c.QueryInterval) - if err != nil { - responseMessage = "error updating intervals" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } - // After updating interval, you need to re-generate flags - flags, err := envs.GenerateFlagsEnv(environmentVar, "", "") - if err == nil { - // Update flags in the newly created environment - if err := envs.UpdateFlags(environmentVar, flags); err != nil { - responseMessage = "error updating flags" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } - } else { - responseMessage = "error re-generating flags" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } - } else { - responseMessage = "invalid CSRF token" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } + adminErrorResponse(w, "error re-generating flags", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + // Update flags in the newly created environment + if err := envs.UpdateFlags(environmentVar, flags); err != nil { + adminErrorResponse(w, "error updating flags", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return } // Serialize and send response if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Intervals response sent") } - utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, AdminResponse{Message: responseMessage}) + adminOKResponse(w, "intervals saved successfully") incMetric(metricAdminOK) } // Handler POST requests for expiring enroll links func expirationPOSTHandler(w http.ResponseWriter, r *http.Request) { incMetric(metricAdminReq) - responseMessage := "OK" - responseCode := http.StatusOK utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), true) vars := mux.Vars(r) - // Extract environment + // Extract environment and verify environmentVar, ok := vars["environment"] - if !ok { - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: error getting environment") - } - return - } - // Verify environment - if !envs.Exists(environmentVar) { - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: error unknown environment (%s)", environmentVar) - } + if !ok || !envs.Exists(environmentVar) { + adminErrorResponse(w, "error getting environment", http.StatusInternalServerError, nil) + incMetric(metricAdminErr) return } var e ExpirationRequest // Get context data ctx := r.Context().Value(contextKey("session")).(contextValue) + // Check permissions + if !checkPermissions(ctx[ctxUser], false, false, true, environmentVar) { + adminErrorResponse(w, fmt.Sprintf("%s has insuficient permissions", ctx[ctxUser]), http.StatusForbidden, nil) + incMetric(metricAdminErr) + return + } // Parse request JSON body - err := json.NewDecoder(r.Body).Decode(&e) - if err != nil { - responseMessage = "error parsing POST body" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) + if settingsmgr.DebugService(settings.ServiceAdmin) { + log.Println("DebugService: Decoding POST body") + } + if err := json.NewDecoder(r.Body).Decode(&e); err != nil { + adminErrorResponse(w, "error parsing POST body", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + // Check CSRF Token + if !checkCSRFToken(ctx[ctxCSRF], e.CSRFToken) { + adminErrorResponse(w, "invalid CSRF token", http.StatusInternalServerError, nil) + incMetric(metricAdminErr) + return + } + switch e.Type { + case "enroll": + switch e.Action { + case "expire": + if err := envs.ExpireEnroll(environmentVar); err != nil { + adminErrorResponse(w, "error expiring enroll", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + adminOKResponse(w, "link expired successfully") + case "extend": + if err := envs.RotateEnrollPath(environmentVar); err != nil { + adminErrorResponse(w, "error extending enroll", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + adminOKResponse(w, "link extended successfully") } - } else { - // Check CSRF Token - if checkCSRFToken(ctx[ctxCSRF], e.CSRFToken) { - switch e.Type { - case "enroll": - switch e.Action { - case "expire": - err = envs.ExpireEnroll(environmentVar) - if err != nil { - responseMessage = "error expiring enroll" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } - case "extend": - err = envs.RotateEnrollPath(environmentVar) - if err != nil { - responseMessage = "error extending enroll" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } - } - case "remove": - switch e.Action { - case "expire": - err = envs.ExpireRemove(environmentVar) - if err != nil { - responseMessage = "error expiring enroll" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } - case "extend": - err = envs.RotateRemove(environmentVar) - if err != nil { - responseMessage = "error extending enroll" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } - } + case "remove": + switch e.Action { + case "expire": + if err := envs.ExpireRemove(environmentVar); err != nil { + adminErrorResponse(w, "error expiring remove", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return } - } else { - responseMessage = "invalid CSRF token" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) + adminOKResponse(w, "link expired successfully") + case "extend": + if err := envs.RotateRemove(environmentVar); err != nil { + adminErrorResponse(w, "error extending remove", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return } + adminOKResponse(w, "link extended successfully") } } // Serialize and send response if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Expiration response sent") } - utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, AdminResponse{Message: responseMessage}) incMetric(metricAdminOK) } // Handler POST requests for multi node action func nodeActionsPOSTHandler(w http.ResponseWriter, r *http.Request) { incMetric(metricAdminReq) - responseMessage := "OK" - responseCode := http.StatusOK utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), true) var m NodeMultiActionRequest // Get context data ctx := r.Context().Value(contextKey("session")).(contextValue) // Parse request JSON body - err := json.NewDecoder(r.Body).Decode(&m) - if err != nil { - responseMessage = "error parsing POST body" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } else { - // Check CSRF Token - if checkCSRFToken(ctx[ctxCSRF], m.CSRFToken) { - switch m.Action { - case "delete": - okCount := 0 - errCount := 0 - for _, u := range m.UUIDs { - err := nodesmgr.ArchiveDeleteByUUID(u) - if err != nil { - errCount++ - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: error deleting node %s %v", u, err) - } - } else { - okCount++ - } - } - if errCount == 0 { - responseMessage = fmt.Sprintf("%d Node(s) have been deleted successfully", okCount) - } else { - responseMessage = fmt.Sprintf("Error deleting %d node(s)", errCount) - responseCode = http.StatusInternalServerError + if settingsmgr.DebugService(settings.ServiceAdmin) { + log.Println("DebugService: Decoding POST body") + } + if err := json.NewDecoder(r.Body).Decode(&m); err != nil { + adminErrorResponse(w, "error parsing POST body", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + // Check CSRF Token + if !checkCSRFToken(ctx[ctxCSRF], m.CSRFToken) { + adminErrorResponse(w, "invalid CSRF token", http.StatusInternalServerError, nil) + incMetric(metricAdminErr) + return + } + switch m.Action { + case "delete": + okCount := 0 + errCount := 0 + for _, u := range m.UUIDs { + if err := nodesmgr.ArchiveDeleteByUUID(u); err != nil { + errCount++ + if settingsmgr.DebugService(settings.ServiceAdmin) { + log.Printf("DebugService: error deleting node %s %v", u, err) } - case "archive": - log.Printf("DebugService: archiving node") + } else { + okCount++ } + } + if errCount == 0 { + adminOKResponse(w, fmt.Sprintf("%d Node(s) have been deleted successfully", okCount)) } else { - responseMessage = "invalid CSRF token" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } + adminErrorResponse(w, fmt.Sprintf("Error deleting %d node(s)", errCount), http.StatusInternalServerError, nil) + incMetric(metricAdminErr) + return + } + case "archive": + if settingsmgr.DebugService(settings.ServiceAdmin) { + log.Printf("DebugService: archiving node") } + adminOKResponse(w, "node archived successfully") } // Serialize and send response if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Multi-node action response sent") } - utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, AdminResponse{Message: responseMessage}) incMetric(metricAdminOK) } // Handler for POST request for /environments func envsPOSTHandler(w http.ResponseWriter, r *http.Request) { incMetric(metricAdminReq) - responseMessage := "OK" - responseCode := http.StatusOK utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), true) var c EnvironmentsRequest // Get context data ctx := r.Context().Value(contextKey("session")).(contextValue) // Parse request JSON body - err := json.NewDecoder(r.Body).Decode(&c) - if err != nil { - responseMessage = "error parsing POST body" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - goto send_response - } else { - // Check CSRF Token - if checkCSRFToken(ctx[ctxCSRF], c.CSRFToken) { - switch c.Action { - case "create": - // FIXME verify fields - if !envs.Exists(c.Name) { - env := envs.Empty(c.Name, c.Hostname) - env.Icon = c.Icon - env.Type = c.Type - if env.Configuration == "" { - env.Configuration = environments.ReadExternalFile(emptyConfiguration) - } - if env.Flags == "" { - // Generate flags - flags, err := environments.GenerateFlags(env, "", "") - if err != nil { - responseMessage = "error creating environment" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - goto send_response - } - env.Flags = flags - } - err := envs.Create(env) - if err != nil { - responseMessage = "error creating environment" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - goto send_response - } else { - responseMessage = "Environment created successfully" - } - } - case "delete": - if c.Name == settingsmgr.DefaultEnv(settings.ServiceAdmin) { - responseMessage = "Not a good idea" - responseCode = http.StatusInternalServerError - } else if envs.Exists(c.Name) { - err := envs.Delete(c.Name) - if err != nil { - responseMessage = "error deleting environment" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - goto send_response - } else { - responseMessage = "Environment deleted successfully" - } - } - case "debug": - // FIXME verify fields - if envs.Exists(c.Name) { - err := envs.ChangeDebugHTTP(c.Name, c.DebugHTTP) - if err != nil { - responseMessage = "error changing DebugHTTP" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - goto send_response - } else { - responseMessage = "DebugHTTP changed successfully" - } + if settingsmgr.DebugService(settings.ServiceAdmin) { + log.Println("DebugService: Decoding POST body") + } + if err := json.NewDecoder(r.Body).Decode(&c); err != nil { + adminErrorResponse(w, "error parsing POST body", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + // Check CSRF Token + if !checkCSRFToken(ctx[ctxCSRF], c.CSRFToken) { + adminErrorResponse(w, "invalid CSRF token", http.StatusInternalServerError, nil) + incMetric(metricAdminErr) + return + } + switch c.Action { + case "create": + // FIXME verify fields + if !envs.Exists(c.Name) { + env := envs.Empty(c.Name, c.Hostname) + env.Icon = c.Icon + env.Type = c.Type + if env.Configuration == "" { + env.Configuration = environments.ReadExternalFile(emptyConfiguration) + } + if env.Flags == "" { + // Generate flags + flags, err := environments.GenerateFlags(env, "", "") + if err != nil { + adminErrorResponse(w, "error generating flags", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return } + env.Flags = flags } - } else { - responseMessage = "invalid CSRF token" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) + if err := envs.Create(env); err != nil { + adminErrorResponse(w, "error creating environment", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + } + adminOKResponse(w, "environment created successfully") + case "delete": + if c.Name == settingsmgr.DefaultEnv(settings.ServiceAdmin) { + adminErrorResponse(w, "not a good idea", http.StatusInternalServerError, fmt.Errorf("attempt to remove enviornment %s", c.Name)) + incMetric(metricAdminErr) + return + } + if envs.Exists(c.Name) { + if err := envs.Delete(c.Name); err != nil { + adminErrorResponse(w, "error deleting environment", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + } + adminOKResponse(w, "environment deleted successfully") + case "debug": + // FIXME verify fields + if envs.Exists(c.Name) { + if err := envs.ChangeDebugHTTP(c.Name, c.DebugHTTP); err != nil { + adminErrorResponse(w, "error changing DebugHTTP", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return } - goto send_response } + adminOKResponse(w, "debug changed successfully") } -send_response: // Serialize and send response if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Environments response sent") } - utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, AdminResponse{Message: responseMessage}) incMetric(metricAdminOK) } // Handler for POST request for /settings func settingsPOSTHandler(w http.ResponseWriter, r *http.Request) { incMetric(metricAdminReq) - responseMessage := "OK" - responseCode := http.StatusOK utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), true) vars := mux.Vars(r) // Extract service serviceVar, ok := vars["service"] if !ok { - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: error getting service") - } + adminErrorResponse(w, "error getting service", http.StatusInternalServerError, nil) + incMetric(metricAdminErr) return } // Verify service if !checkTargetService(serviceVar) { - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: error unknown service (%s)", serviceVar) - } + adminErrorResponse(w, fmt.Sprintf("unknown service (%s)", serviceVar), http.StatusForbidden, nil) + incMetric(metricAdminErr) return } var s SettingsRequest // Get context data ctx := r.Context().Value(contextKey("session")).(contextValue) // Check permissions - if !checkAdminLevel(ctx[ctxLevel]) { - responseMessage = "insuficient permissions" - responseCode = http.StatusForbidden - log.Printf("%s has %s", ctx[ctxUser], responseMessage) - goto send_response + if !checkPermissions(ctx[ctxUser], false, false, false, "") { + adminErrorResponse(w, fmt.Sprintf("%s has insuficient permissions", ctx[ctxUser]), http.StatusForbidden, nil) + incMetric(metricAdminErr) + return } // Parse request JSON body + if settingsmgr.DebugService(settings.ServiceAdmin) { + log.Println("DebugService: Decoding POST body") + } if err := json.NewDecoder(r.Body).Decode(&s); err != nil { - responseMessage = "error parsing POST body" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) + adminErrorResponse(w, "error parsing POST body", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + // Check CSRF Token + if !checkCSRFToken(ctx[ctxCSRF], s.CSRFToken) { + adminErrorResponse(w, "invalid CSRF token", http.StatusInternalServerError, nil) + incMetric(metricAdminErr) + return + } + switch s.Action { + case "add": + if !settingsmgr.VerifyType(s.Type) { + adminErrorResponse(w, "invalid type", http.StatusInternalServerError, nil) + incMetric(metricAdminErr) + return } - } else { - // Check CSRF Token - if checkCSRFToken(ctx[ctxCSRF], s.CSRFToken) { - switch s.Action { - case "add": - // FIXME verify type - var err error - switch s.Type { - case settings.TypeBoolean: - err = settingsmgr.NewBooleanValue(serviceVar, s.Name, stringToBoolean(s.Value)) - case settings.TypeInteger: - err = settingsmgr.NewIntegerValue(serviceVar, s.Name, stringToInteger(s.Value)) - case settings.TypeString: - err = settingsmgr.NewStringValue(serviceVar, s.Name, s.Value) - } - if err != nil { - responseMessage = "error adding setting" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } else { - responseMessage = "Setting added successfully" - } - case "change": - // FIXME verify type - var err error - switch s.Type { - case settings.TypeBoolean: - err = settingsmgr.SetBoolean(s.Boolean, serviceVar, s.Name) - case settings.TypeInteger: - err = settingsmgr.SetInteger(stringToInteger(s.Value), serviceVar, s.Name) - case settings.TypeString: - err = settingsmgr.SetString(s.Value, serviceVar, s.Name, false) - } - if err != nil { - responseMessage = "error changing setting" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } else { - responseMessage = "Setting changed successfully" - } - case "delete": - var err error - err = settingsmgr.DeleteValue(serviceVar, s.Name) - if err != nil { - responseMessage = "error deleting setting" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } else { - responseMessage = "Setting deleted successfully" - } - } - } else { - responseMessage = "invalid CSRF token" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } + var err error + switch s.Type { + case settings.TypeBoolean: + err = settingsmgr.NewBooleanValue(serviceVar, s.Name, stringToBoolean(s.Value)) + case settings.TypeInteger: + err = settingsmgr.NewIntegerValue(serviceVar, s.Name, stringToInteger(s.Value)) + case settings.TypeString: + err = settingsmgr.NewStringValue(serviceVar, s.Name, s.Value) + } + if err != nil { + adminErrorResponse(w, "error adding setting", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + adminOKResponse(w, "setting added successfully") + case "change": + if !settingsmgr.VerifyType(s.Type) { + adminErrorResponse(w, "invalid type", http.StatusInternalServerError, nil) + incMetric(metricAdminErr) + return + } + var err error + switch s.Type { + case settings.TypeBoolean: + err = settingsmgr.SetBoolean(s.Boolean, serviceVar, s.Name) + case settings.TypeInteger: + err = settingsmgr.SetInteger(stringToInteger(s.Value), serviceVar, s.Name) + case settings.TypeString: + err = settingsmgr.SetString(s.Value, serviceVar, s.Name, false) + } + if err != nil { + adminErrorResponse(w, "error changing setting", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return } + adminOKResponse(w, "setting changed successfully") + case "delete": + if err := settingsmgr.DeleteValue(serviceVar, s.Name); err != nil { + adminErrorResponse(w, "error deleting setting", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + adminOKResponse(w, "setting deleted successfully") } -send_response: // Serialize and send response if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Settings response sent") } - utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, AdminResponse{Message: responseMessage}) incMetric(metricAdminOK) } // Handler for POST request for /users func usersPOSTHandler(w http.ResponseWriter, r *http.Request) { incMetric(metricAdminReq) - responseMessage := "OK" - responseCode := http.StatusOK utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), true) var u UsersRequest // Get context data ctx := r.Context().Value(contextKey("session")).(contextValue) // Check permissions - if !checkAdminLevel(ctx[ctxLevel]) { - responseMessage = "insuficient permissions" - responseCode = http.StatusForbidden - log.Printf("%s has %s", ctx[ctxUser], responseMessage) - goto send_response + if !checkPermissions(ctx[ctxUser], false, false, false, "") { + adminErrorResponse(w, fmt.Sprintf("%s has insuficient permissions", ctx[ctxUser]), http.StatusForbidden, nil) + incMetric(metricAdminErr) + return } // Parse request JSON body if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Decoding POST body") } if err := json.NewDecoder(r.Body).Decode(&u); err != nil { - responseMessage = "error parsing POST body" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) + adminErrorResponse(w, "error parsing POST body", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + // Check CSRF Token + if !checkCSRFToken(ctx[ctxCSRF], u.CSRFToken) { + adminErrorResponse(w, "invalid CSRF token", http.StatusInternalServerError, nil) + incMetric(metricAdminErr) + return + } + switch u.Action { + case "add": + // FIXME password complexity? + if adminUsers.Exists(u.Username) { + adminErrorResponse(w, "error adding user", http.StatusInternalServerError, fmt.Errorf("user %s already exists", u.Username)) + incMetric(metricAdminErr) + return } - } else { - // Check CSRF Token - if checkCSRFToken(ctx[ctxCSRF], u.CSRFToken) { - switch u.Action { - case "add": - // FIXME password complexity? - if adminUsers.Exists(u.Username) { - responseMessage = "user already exists" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } else { - newUser, err := adminUsers.New(u.Username, u.Password, u.Email, u.Fullname, u.Admin) - if err != nil { - responseMessage = "error with new user" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } else { - if err = adminUsers.Create(newUser); err != nil { - responseMessage = "error creating user" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } - if newUser.Admin { - token, exp, err := adminUsers.CreateToken(newUser.Username, jwtConfig.HoursToExpire, jwtConfig.JWTSecret) - if err != nil { - responseMessage = "error creating token" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - goto send_response - } - if err = adminUsers.UpdateToken(newUser.Username, token, exp); err != nil { - responseMessage = "error saving token" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - goto send_response - } - } - responseMessage = "User added successfully" - } - } - case "edit": - if u.Fullname != "" { - if err := adminUsers.ChangeFullname(u.Username, u.Fullname); err != nil { - responseMessage = "error changing fullname" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } else { - responseMessage = "User updated successfully" - } + // Prepare user to create + newUser, err := adminUsers.New(u.Username, u.Password, u.Email, u.Fullname, u.Admin) + if err != nil { + adminErrorResponse(w, "error with new user", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + // Create new user + if err = adminUsers.Create(newUser); err != nil { + adminErrorResponse(w, "error creating user", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + if u.Admin { + namesEnvs, err := envs.Names() + if err != nil { + adminErrorResponse(w, "error getting environments user", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + perms := adminUsers.GenPermissions(namesEnvs, u.Admin) + if err := adminUsers.ChangePermissions(u.Username, perms); err != nil { + adminErrorResponse(w, "error changing permissions", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + } + if u.Token { + token, exp, err := adminUsers.CreateToken(newUser.Username, jwtConfig.HoursToExpire, jwtConfig.JWTSecret) + if err != nil { + adminErrorResponse(w, "error creating token", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + if err = adminUsers.UpdateToken(newUser.Username, token, exp); err != nil { + adminErrorResponse(w, "error saving token", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + } + adminOKResponse(w, "user added successfully") + case "edit": + if u.Fullname != "" { + if err := adminUsers.ChangeFullname(u.Username, u.Fullname); err != nil { + adminErrorResponse(w, "error changing fullname", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + } + if u.Email != "" { + if err := adminUsers.ChangeEmail(u.Username, u.Email); err != nil { + adminErrorResponse(w, "error changing email", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + } + adminOKResponse(w, "user updated successfully") + case "remove": + if u.Username == ctx[ctxUser] { + adminErrorResponse(w, "not a good idea", http.StatusInternalServerError, fmt.Errorf("attempt to remove current user %s", u.Username)) + incMetric(metricAdminErr) + return + } + if adminUsers.Exists(u.Username) { + if err := adminUsers.Delete(u.Username); err != nil { + adminErrorResponse(w, "error removing user", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + } + adminOKResponse(w, "user removed successfully") + case "admin": + if u.Username == ctx[ctxUser] { + adminErrorResponse(w, "not a good idea", http.StatusInternalServerError, fmt.Errorf("attempt to de-admin current user %s", u.Username)) + incMetric(metricAdminErr) + return + } + if adminUsers.Exists(u.Username) { + if err := adminUsers.ChangeAdmin(u.Username, u.Admin); err != nil { + adminErrorResponse(w, "error changing admin", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + if u.Admin { + namesEnvs, err := envs.Names() + if err != nil { + adminErrorResponse(w, "error getting environments", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return } - if u.Email != "" { - if err := adminUsers.ChangeEmail(u.Username, u.Email); err != nil { - responseMessage = "error changing email" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } else { - responseMessage = "User updated successfully" - } + perms := adminUsers.GenPermissions(namesEnvs, u.Admin) + if err := adminUsers.ChangePermissions(u.Username, perms); err != nil { + adminErrorResponse(w, "error changing permissions", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return } - case "remove": - if u.Username == ctx[ctxUser] { - responseMessage = "Not a good idea" - responseCode = http.StatusInternalServerError - log.Printf("Attempt to self remove by %s", ctx[ctxUser]) - goto send_response - } else if adminUsers.Exists(u.Username) { - err = adminUsers.Delete(u.Username) - if err != nil { - responseMessage = "error removing user" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } else { - responseMessage = "User removed" - } + token, exp, err := adminUsers.CreateToken(u.Username, jwtConfig.HoursToExpire, jwtConfig.JWTSecret) + if err != nil { + adminErrorResponse(w, "error creating token", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return } - case "admin": - if u.Username == ctx[ctxUser] { - responseMessage = "Not a good idea" - responseCode = http.StatusInternalServerError - log.Printf("Attempt to change admin by %s", ctx[ctxUser]) - goto send_response - } else if adminUsers.Exists(u.Username) { - err = adminUsers.ChangeAdmin(u.Username, u.Admin) - if err != nil { - responseMessage = "error changing admin" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } - if u.Admin { - token, exp, err := adminUsers.CreateToken(u.Username, jwtConfig.HoursToExpire, jwtConfig.JWTSecret) - if err != nil { - responseMessage = "error creating token" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - goto send_response - } - if err = adminUsers.UpdateToken(u.Username, token, exp); err != nil { - responseMessage = "error saving token" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - goto send_response - } - } - responseMessage = "Admin changed" + if err := adminUsers.UpdateToken(u.Username, token, exp); err != nil { + adminErrorResponse(w, "error saving token", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return } } - } else { - responseMessage = "invalid CSRF token" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } + adminOKResponse(w, "admin changed successfully") } } -send_response: // Serialize and send response if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Users response sent") } - utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, AdminResponse{Message: responseMessage}) + incMetric(metricAdminOK) +} + +// Handler for POST request for /users/permissions +func permissionsPOSTHandler(w http.ResponseWriter, r *http.Request) { + incMetric(metricAdminReq) + utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), true) + vars := mux.Vars(r) + // Extract username and verify + usernameVar, ok := vars["username"] + if !ok || !adminUsers.Exists(usernameVar) { + adminErrorResponse(w, "error getting username", http.StatusInternalServerError, nil) + incMetric(metricAdminErr) + return + } + var p PermissionsRequest + // Get context data + ctx := r.Context().Value(contextKey("session")).(contextValue) + // Check permissions + if !checkPermissions(ctx[ctxUser], false, false, false, "") { + adminErrorResponse(w, fmt.Sprintf("%s has insuficient permissions", ctx[ctxUser]), http.StatusForbidden, nil) + incMetric(metricAdminErr) + return + } + // Parse request JSON body + if settingsmgr.DebugService(settings.ServiceAdmin) { + log.Println("DebugService: Decoding POST body") + } + if err := json.NewDecoder(r.Body).Decode(&p); err != nil { + adminErrorResponse(w, "error parsing POST body", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + // Check CSRF Token + if !checkCSRFToken(ctx[ctxCSRF], p.CSRFToken) { + adminErrorResponse(w, "invalid CSRF token", http.StatusInternalServerError, nil) + incMetric(metricAdminErr) + return + } + // TODO verify environments + perms := users.UserPermissions{ + Environments: p.Environments, + Query: p.Query, + Carve: p.Carve, + } + if err := adminUsers.ChangePermissions(usernameVar, perms); err != nil { + adminErrorResponse(w, "error changing permissions", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + // Serialize and send response + if settingsmgr.DebugService(settings.ServiceAdmin) { + log.Println("DebugService: Users response sent") + } + adminOKResponse(w, "OK") incMetric(metricAdminOK) } // Handler POST requests enroll data func enrollPOSTHandler(w http.ResponseWriter, r *http.Request) { incMetric(metricAdminReq) - responseMessage := "Enroll data saved successfully" - responseCode := http.StatusOK utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), true) vars := mux.Vars(r) // Extract environment and verify environmentVar, ok := vars["environment"] if !ok || !envs.Exists(environmentVar) { - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: error getting environment") - } - return - } - // Verify environment - if !envs.Exists(environmentVar) { - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: error unknown environment (%s)", environmentVar) - } + adminErrorResponse(w, "error getting environment", http.StatusInternalServerError, nil) + incMetric(metricAdminErr) return } var e EnrollRequest // Get context data ctx := r.Context().Value(contextKey("session")).(contextValue) // Check permissions - if !checkAdminLevel(ctx[ctxLevel]) { - responseMessage = "insuficient permissions" - responseCode = http.StatusForbidden - log.Printf("%s has %s", ctx[ctxUser], responseMessage) - goto send_response + if !checkPermissions(ctx[ctxUser], false, false, true, environmentVar) { + adminErrorResponse(w, fmt.Sprintf("%s has insuficient permissions", ctx[ctxUser]), http.StatusForbidden, nil) + incMetric(metricAdminErr) + return } // Parse request JSON body + if settingsmgr.DebugService(settings.ServiceAdmin) { + log.Println("DebugService: Decoding POST body") + } if err := json.NewDecoder(r.Body).Decode(&e); err != nil { - responseMessage = "error parsing POST body" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } else { - // Check CSRF Token - if checkCSRFToken(ctx[ctxCSRF], e.CSRFToken) { - if e.CertificateB64 != "" { - certificate, err := base64.StdEncoding.DecodeString(e.CertificateB64) - if err != nil { - responseMessage = "error decoding certificate" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } else { - err = envs.UpdateCertificate(environmentVar, string(certificate)) - if err != nil { - responseMessage = "error saving certificate" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } - } - } else { - responseMessage = "empty certificate" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } - } else { - responseMessage = "invalid CSRF token" - responseCode = http.StatusInternalServerError - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Printf("DebugService: %s %v", responseMessage, err) - } - } + adminErrorResponse(w, "error parsing POST body", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + // Check CSRF Token + if !checkCSRFToken(ctx[ctxCSRF], e.CSRFToken) { + adminErrorResponse(w, "invalid CSRF token", http.StatusInternalServerError, nil) + incMetric(metricAdminErr) + return + } + if e.CertificateB64 == "" { + adminErrorResponse(w, "empty certificate", http.StatusInternalServerError, nil) + incMetric(metricAdminErr) + return + } + certificate, err := base64.StdEncoding.DecodeString(e.CertificateB64) + if err != nil { + adminErrorResponse(w, "error decoding certificate", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + if err := envs.UpdateCertificate(environmentVar, string(certificate)); err != nil { + adminErrorResponse(w, "error saving certificate", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return } -send_response: // Serialize and send response if settingsmgr.DebugService(settings.ServiceAdmin) { log.Println("DebugService: Configuration response sent") } - utils.HTTPResponse(w, utils.JSONApplicationUTF8, responseCode, AdminResponse{Message: responseMessage}) + adminOKResponse(w, "enroll data saved") incMetric(metricAdminOK) } diff --git a/admin/handlers-tokens.go b/admin/handlers-tokens.go index e321b675..8e98d45d 100644 --- a/admin/handlers-tokens.go +++ b/admin/handlers-tokens.go @@ -2,6 +2,7 @@ package main import ( "encoding/json" + "fmt" "log" "net/http" @@ -30,25 +31,25 @@ func tokensGETHandler(w http.ResponseWriter, r *http.Request) { // Get context data ctx := r.Context().Value(contextKey("session")).(contextValue) // Check permissions - if !checkAdminLevel(ctx[ctxLevel]) { - log.Printf("%s has insuficient permissions", ctx[ctxUser]) - incMetric(metricTokenErr) + if !checkPermissions(ctx[ctxUser], false, false, false, "") { + adminErrorResponse(w, fmt.Sprintf("%s has insuficient permissions", ctx[ctxUser]), http.StatusForbidden, nil) + incMetric(metricAdminErr) return } vars := mux.Vars(r) // Extract username username, ok := vars["username"] if !ok { - log.Println("error getting username") - incMetric(metricTokenErr) + adminErrorResponse(w, "error getting username", http.StatusInternalServerError, nil) + incMetric(metricAdminErr) return } returned := TokenJSON{} if adminUsers.Exists(username) { user, err := adminUsers.Get(username) if err != nil { - log.Println("error getting user") - incMetric(metricTokenErr) + adminErrorResponse(w, "error getting user", http.StatusInternalServerError, err) + incMetric(metricAdminErr) return } // Prepare data to be returned @@ -70,17 +71,17 @@ func tokensPOSTHandler(w http.ResponseWriter, r *http.Request) { // Get context data ctx := r.Context().Value(contextKey("session")).(contextValue) // Check permissions - if !checkAdminLevel(ctx[ctxLevel]) { + if !checkPermissions(ctx[ctxUser], false, false, false, "") { adminErrorResponse(w, "insuficient permissions", http.StatusForbidden, nil) incMetric(metricTokenErr) return } vars := mux.Vars(r) - // Extract username + // Extract username and verify username, ok := vars["username"] - if !ok { - incMetric(metricTokenErr) + if !ok || !adminUsers.Exists(username) { adminErrorResponse(w, "error getting username", http.StatusInternalServerError, nil) + incMetric(metricAdminErr) return } // Parse request JSON body @@ -90,47 +91,44 @@ func tokensPOSTHandler(w http.ResponseWriter, r *http.Request) { var t TokenRequest var response TokenResponse if err := json.NewDecoder(r.Body).Decode(&t); err == nil { - // Check CSRF Token - if checkCSRFToken(ctx[ctxCSRF], t.CSRFToken) { - if adminUsers.Exists(username) { - user, err := adminUsers.Get(username) - if err != nil { - adminErrorResponse(w, "error getting user", http.StatusInternalServerError, err) - return - } - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Println("DebugService: Creating token") - } - token, exp, err := adminUsers.CreateToken(user.Username, jwtConfig.HoursToExpire, jwtConfig.JWTSecret) - if err != nil { - adminErrorResponse(w, "error creating token", http.StatusInternalServerError, err) - return - } - if settingsmgr.DebugService(settings.ServiceAdmin) { - log.Println("DebugService: Updating token") - } - if err := adminUsers.UpdateToken(user.Username, token, exp); err != nil { - adminErrorResponse(w, "error updating token", http.StatusInternalServerError, err) - return - } - response = TokenResponse{ - Token: token, - ExpirationTS: utils.TimeTimestamp(exp), - Expiration: utils.PastFutureTimes(exp), - } - } else { - adminErrorResponse(w, "user not found", http.StatusNotFound, nil) - return - } - } else { - adminErrorResponse(w, "invalid CSRF token", http.StatusForbidden, nil) - return - } - } else { - incMetric(metricTokenErr) - adminErrorResponse(w, "error parsing POST body", http.StatusInternalServerError, nil) + adminErrorResponse(w, "error parsing POST body", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + // Check CSRF Token + if !checkCSRFToken(ctx[ctxCSRF], t.CSRFToken) { + adminErrorResponse(w, "invalid CSRF token", http.StatusInternalServerError, nil) + incMetric(metricAdminErr) return } + user, err := adminUsers.Get(username) + if err != nil { + adminErrorResponse(w, "error getting user", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + if settingsmgr.DebugService(settings.ServiceAdmin) { + log.Println("DebugService: Creating token") + } + token, exp, err := adminUsers.CreateToken(user.Username, jwtConfig.HoursToExpire, jwtConfig.JWTSecret) + if err != nil { + adminErrorResponse(w, "error creating token", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + if settingsmgr.DebugService(settings.ServiceAdmin) { + log.Println("DebugService: Updating token") + } + if err := adminUsers.UpdateToken(user.Username, token, exp); err != nil { + adminErrorResponse(w, "error updating token", http.StatusInternalServerError, err) + incMetric(metricAdminErr) + return + } + response = TokenResponse{ + Token: token, + ExpirationTS: utils.TimeTimestamp(exp), + Expiration: utils.PastFutureTimes(exp), + } // Serialize and serve JSON utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, response) incMetric(metricTokenOK) diff --git a/admin/headers.go b/admin/headers.go index 9f1f8b87..81e2f603 100644 --- a/admin/headers.go +++ b/admin/headers.go @@ -24,7 +24,6 @@ func loadHeaders(file string) (types.JSONConfigurationHeaders, error) { if err != nil { return cfg, err } - // No errors! return cfg, nil } diff --git a/admin/json-carves.go b/admin/json-carves.go index b6aa775f..7bf80ee8 100644 --- a/admin/json-carves.go +++ b/admin/json-carves.go @@ -56,7 +56,7 @@ func jsonCarvesHandler(w http.ResponseWriter, r *http.Request) { // Get context data ctx := r.Context().Value(contextKey("session")).(contextValue) // Check permissions - if !checkAdminLevel(ctx[ctxLevel]) { + if !checkPermissions(ctx[ctxUser], false, true, false, "") { log.Printf("%s has insuficient permissions", ctx[ctxUser]) incMetric(metricJSONErr) return diff --git a/admin/json-logs.go b/admin/json-logs.go index 2d8fa124..32dac4da 100644 --- a/admin/json-logs.go +++ b/admin/json-logs.go @@ -78,6 +78,14 @@ func jsonLogsHandler(w http.ResponseWriter, r *http.Request) { incMetric(metricJSONErr) return } + // Get context data + ctx := r.Context().Value(contextKey("session")).(contextValue) + // Check permissions + if !checkPermissions(ctx[ctxUser], false, false, true, env) { + log.Printf("%s has insuficient permissions", ctx[ctxUser]) + incMetric(metricJSONErr) + return + } // Extract UUID // FIXME verify UUID UUID, ok := vars["uuid"] @@ -150,6 +158,14 @@ func jsonLogsHandler(w http.ResponseWriter, r *http.Request) { func jsonQueryLogsHandler(w http.ResponseWriter, r *http.Request) { incMetric(metricJSONReq) utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), false) + // Get context data + ctx := r.Context().Value(contextKey("session")).(contextValue) + // Check permissions + if !checkPermissions(ctx[ctxUser], true, false, false, "") { + log.Printf("%s has insuficient permissions", ctx[ctxUser]) + incMetric(metricJSONErr) + return + } vars := mux.Vars(r) // Extract query name // FIXME verify name diff --git a/admin/json-nodes.go b/admin/json-nodes.go index a557ae47..fd996ed0 100644 --- a/admin/json-nodes.go +++ b/admin/json-nodes.go @@ -106,7 +106,7 @@ func jsonPlatformHandler(w http.ResponseWriter, r *http.Request) { // Get context data ctx := r.Context().Value(contextKey("session")).(contextValue) // Check permissions - if !checkAdminLevel(ctx[ctxLevel]) { + if !checkPermissions(ctx[ctxUser], false, false, false, "") { log.Printf("%s has insuficient permissions", ctx[ctxUser]) incMetric(metricJSONErr) return diff --git a/admin/json-queries.go b/admin/json-queries.go index 203b31b2..e78dafee 100644 --- a/admin/json-queries.go +++ b/admin/json-queries.go @@ -55,7 +55,7 @@ func jsonQueryHandler(w http.ResponseWriter, r *http.Request) { // Get context data ctx := r.Context().Value(contextKey("session")).(contextValue) // Check permissions - if !checkAdminLevel(ctx[ctxLevel]) { + if !checkPermissions(ctx[ctxUser], true, false, false, "") { log.Printf("%s has insuficient permissions", ctx[ctxUser]) incMetric(metricJSONErr) return diff --git a/admin/json-stats.go b/admin/json-stats.go index 2e74cea1..95b95248 100644 --- a/admin/json-stats.go +++ b/admin/json-stats.go @@ -58,7 +58,7 @@ func jsonStatsHandler(w http.ResponseWriter, r *http.Request) { } } else if target == "platform" { // Check permissions - if !checkAdminLevel(ctx[ctxLevel]) { + if !checkPermissions(ctx[ctxUser], false, false, false, "") { log.Printf("%s has insuficient permissions", ctx[ctxUser]) incMetric(metricJSONErr) return diff --git a/admin/main.go b/admin/main.go index 5319b811..cd00787f 100644 --- a/admin/main.go +++ b/admin/main.go @@ -381,11 +381,13 @@ func main() { // Admin: manage users routerAdmin.Handle("/users", handlerAuthCheck(http.HandlerFunc(usersGETHandler))).Methods("GET") routerAdmin.Handle("/users", handlerAuthCheck(http.HandlerFunc(usersPOSTHandler))).Methods("POST") + routerAdmin.Handle("/users/permissions/{username}", handlerAuthCheck(http.HandlerFunc(permissionsGETHandler))).Methods("GET") + routerAdmin.Handle("/users/permissions/{username}", handlerAuthCheck(http.HandlerFunc(permissionsPOSTHandler))).Methods("POST") // Admin: manage tokens routerAdmin.Handle("/tokens/{username}", handlerAuthCheck(http.HandlerFunc(tokensGETHandler))).Methods("GET") routerAdmin.Handle("/tokens/{username}/refresh", handlerAuthCheck(http.HandlerFunc(tokensPOSTHandler))).Methods("POST") // logout - routerAdmin.Handle("/logout", handlerAuthCheck(http.HandlerFunc(logoutHandler))).Methods("POST") + routerAdmin.Handle("/logout", handlerAuthCheck(http.HandlerFunc(logoutPOSTHandler))).Methods("POST") // SAML ACS if adminConfig.Auth == settings.AuthSAML { diff --git a/admin/static/js/functions.js b/admin/static/js/functions.js index eb9c2de2..3afd3d6b 100644 --- a/admin/static/js/functions.js +++ b/admin/static/js/functions.js @@ -1,3 +1,32 @@ +function sendGetRequest(req_url, _modal, _callback) { + $.ajax({ + url: req_url, + dataType: 'json', + type: 'GET', + contentType: 'application/json', + success: function (data, textStatus, jQxhr) { + console.log('OK'); + console.log(data); + if (_modal) { + $("#successModalMessage").text(data.message); + $("#successModal").modal(); + } + if (_callback) { + _callback(data); + } + }, + error: function (jqXhr, textStatus, errorThrown) { + var _clientmsg = 'Client: ' + errorThrown; + var _serverJSON = $.parseJSON(jqXhr.responseText); + var _servermsg = 'Server: ' + _serverJSON.message; + $("#errorModalMessageClient").text(_clientmsg); + console.log(_clientmsg); + $("#errorModalMessageServer").text(_servermsg); + $("#errorModal").modal(); + } + }); +} + function sendPostRequest(req_data, req_url, _redir, _modal, _callback) { $.ajax({ url: req_url, diff --git a/admin/static/js/users.js b/admin/static/js/users.js index 2d1c8560..c3e68ee0 100644 --- a/admin/static/js/users.js +++ b/admin/static/js/users.js @@ -15,6 +15,8 @@ function confirmAddUser() { var _email = $("#user_email").val(); var _fullname = $("#user_fullname").val(); var _password = $("#user_password").val(); + var _admin = $("#user_admin").is(':checked'); + var _token = $("#user_token").is(':checked'); var data = { csrftoken: _csrftoken, @@ -23,7 +25,8 @@ function confirmAddUser() { email: _email, fullname: _fullname, password: _password, - admin: false + admin: _admin, + token: _token }; sendPostRequest(data, _url, _url, false); } @@ -42,6 +45,12 @@ function changeAdminUser(_user) { var _csrftoken = $("#csrftoken").val(); var _value = $("#" + _user).is(':checked'); + if (_value) { + $('#permissions-button-' + _user).hide(); + } else { + $('#permissions-button-' + _user).show(); + } + var _url = window.location.pathname; var data = { @@ -91,3 +100,54 @@ function refreshUserToken() { $("#refreshTokenButton").text('Refresh'); }); } + +function showPermissions(_username) { + $("#username_permissions").val(_username); + sendGetRequest('/users/permissions/' + _username, false, function (data) { + $('.switch-env-permission').each(function () { + var _env = $(this).attr('id'); + if (data.environments) { + if (data.environments[_env]) { + $(this).attr('checked', true); + } else { + $(this).attr('checked', false); + } + } else { + $(this).attr('checked', false); + } + }); + if (data.query) { + $("#permission-queries").attr('checked', true); + } else { + $("#permission-queries").attr('checked', false); + } + if (data.carve) { + $("#permission-carves").attr('checked', true); + } else { + $("#permission-carves").attr('checked', false); + } + }); + $("#permissionsModal").modal(); +} + +function savePermissions() { + var _csrftoken = $("#csrftoken").val(); + var _username = $("#username_permissions").val(); + + var _queries = $("#permission-queries").is(':checked'); + var _carves = $("#permission-carves").is(':checked'); + + var _envs = {}; + $('.switch-env-permission').each(function () { + _envs[$(this).attr('id')] = $(this).prop('checked'); + }); + var data = { + csrftoken: _csrftoken, + environments: _envs, + query: _queries, + carve: _carves, + }; + sendPostRequest(data, '/users/permissions/' + _username, '', false, function (data) { + console.log(data); + }); +} diff --git a/admin/templates/users.html b/admin/templates/users.html index a5d00065..b0355e8d 100644 --- a/admin/templates/users.html +++ b/admin/templates/users.html @@ -62,13 +62,31 @@

{{ $e.Fullname }}

- {{ $e.LastIPAddress }} - {{ $e.LastUserAgent }} + + {{ if eq $e.LastIPAddress "" }} + None + {{ else }} + {{ $e.LastIPAddress }} + {{ end }} + + + {{ if eq $e.LastUserAgent "" }} + None + {{ else }} + {{ $e.LastUserAgent }} + {{ end }} + + {{ if not $e.Admin }} + + {{ end }} {{ pastFutureTimes $e.LastAccess }} @@ -119,6 +137,22 @@ +
+ +
+ +
+ +
+ +
+
@@ -169,6 +201,84 @@
+ + + {{ template "page-modals" . }} diff --git a/admin/types-requests.go b/admin/types-requests.go index e08332e2..63997461 100644 --- a/admin/types-requests.go +++ b/admin/types-requests.go @@ -108,9 +108,18 @@ type UsersRequest struct { Email string `json:"email"` Fullname string `json:"fullname"` Password string `json:"password"` + Token bool `json:"token"` Admin bool `json:"admin"` } +// PermissionsRequest to receive user permissions changes requests +type PermissionsRequest struct { + CSRFToken string `json:"csrftoken"` + Environments map[string]bool `json:"environments"` + Query bool `json:"query"` + Carve bool `json:"carve"` +} + // AdminResponse to be returned to requests type AdminResponse struct { Message string `json:"message"` diff --git a/admin/utils.go b/admin/utils.go index e5d1ac63..7ee0461e 100644 --- a/admin/utils.go +++ b/admin/utils.go @@ -319,3 +319,8 @@ func adminErrorResponse(w http.ResponseWriter, msg string, code int, err error) log.Printf("%s: %v", msg, err) utils.HTTPResponse(w, utils.JSONApplicationUTF8, code, AdminResponse{Message: msg}) } + +// Helper to handle admin ok responses +func adminOKResponse(w http.ResponseWriter, msg string) { + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, AdminResponse{Message: msg}) +} diff --git a/api/auth.go b/api/auth.go index d2e4abb4..002df0de 100644 --- a/api/auth.go +++ b/api/auth.go @@ -22,6 +22,10 @@ const ( contextAPI string = "osctrl-api-context" ) +const ( + ctxUser = "user" +) + const ( adminLevel string = "admin" userLevel string = "user" diff --git a/api/handlers-environments.go b/api/handlers-environments.go index 4a90035a..c712a0c1 100644 --- a/api/handlers-environments.go +++ b/api/handlers-environments.go @@ -1,11 +1,13 @@ package main import ( + "fmt" "log" "net/http" "github.com/gorilla/mux" "github.com/jmpsec/osctrl/settings" + "github.com/jmpsec/osctrl/users" "github.com/jmpsec/osctrl/utils" ) @@ -20,37 +22,34 @@ func apiEnvironmentHandler(w http.ResponseWriter, r *http.Request) { incMetric(metricAPIEnvsReq) utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAPI), false) vars := mux.Vars(r) - // Extract name - name, ok := vars["name"] + // Extract environment + environment, ok := vars["environment"] if !ok { + apiErrorResponse(w, "error getting environment", http.StatusInternalServerError, nil) incMetric(metricAPIEnvsErr) - apiErrorResponse(w, "error getting name", http.StatusInternalServerError, nil) - utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusInternalServerError, errorContent) return } // Get context data and check access ctx := r.Context().Value(contextKey(contextAPI)).(contextValue) - if !apiUsers.IsAdmin(ctx["user"]) { + if !apiUsers.CheckPermissions(ctx[ctxUser], users.EnvLevel, environment) { + apiErrorResponse(w, "no access", http.StatusForbidden, fmt.Errorf("attempt to use API by user %s", ctx[ctxUser])) incMetric(metricAPIEnvsErr) - log.Printf("attempt to use API by user %s", ctx["user"]) - apiErrorResponse(w, "no access", http.StatusForbidden, nil) return } // Get environment by name - env, err := envs.Get(name) + env, err := envs.Get(environment) if err != nil { - incMetric(metricAPIEnvsErr) if err.Error() == "record not found" { - log.Printf("environment not found: %s", name) - apiErrorResponse(w, "environment not found", http.StatusNotFound, nil) + apiErrorResponse(w, "environment not found", http.StatusNotFound, err) } else { apiErrorResponse(w, "error getting environment", http.StatusInternalServerError, err) } + incMetric(metricAPIEnvsErr) return } // Serialize and serve JSON if settingsmgr.DebugService(settings.ServiceAPI) { - log.Printf("DebugService: Returned environment %s", name) + log.Printf("DebugService: Returned environment %s", environment) } utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, env) incMetric(metricAPIEnvsOK) @@ -62,17 +61,16 @@ func apiEnvironmentsHandler(w http.ResponseWriter, r *http.Request) { utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAPI), false) // Get context data and check access ctx := r.Context().Value(contextKey(contextAPI)).(contextValue) - if !apiUsers.IsAdmin(ctx["user"]) { + if !apiUsers.CheckPermissions(ctx[ctxUser], users.AdminLevel, users.NoEnvironment) { + apiErrorResponse(w, "no access", http.StatusForbidden, fmt.Errorf("attempt to use API by user %s", ctx[ctxUser])) incMetric(metricAPIEnvsErr) - log.Printf("attempt to use API by user %s", ctx["user"]) - apiErrorResponse(w, "no access", http.StatusForbidden, nil) return } // Get platforms envAll, err := envs.All() if err != nil { - incMetric(metricAPIEnvsErr) apiErrorResponse(w, "error getting environments", http.StatusInternalServerError, err) + incMetric(metricAPIEnvsErr) return } // Serialize and serve JSON diff --git a/api/handlers-nodes.go b/api/handlers-nodes.go index 2364e067..1aacc421 100644 --- a/api/handlers-nodes.go +++ b/api/handlers-nodes.go @@ -1,11 +1,13 @@ package main import ( + "fmt" "log" "net/http" "github.com/gorilla/mux" "github.com/jmpsec/osctrl/settings" + "github.com/jmpsec/osctrl/users" "github.com/jmpsec/osctrl/utils" ) @@ -23,28 +25,27 @@ func apiNodeHandler(w http.ResponseWriter, r *http.Request) { // Extract uuid uuid, ok := vars["uuid"] if !ok { - incMetric(metricAPINodesErr) apiErrorResponse(w, "error getting uuid", http.StatusInternalServerError, nil) - return - } - // Get context data and check access - ctx := r.Context().Value(contextKey(contextAPI)).(contextValue) - if !apiUsers.IsAdmin(ctx["user"]) { incMetric(metricAPINodesErr) - log.Printf("attempt to use API by user %s", ctx["user"]) - apiErrorResponse(w, "no access", http.StatusForbidden, nil) return } // Get node by UUID + // FIXME keep a cache of nodes by UUID node, err := nodesmgr.GetByUUID(uuid) if err != nil { - incMetric(metricAPINodesErr) if err.Error() == "record not found" { - log.Printf("node not found: %s", uuid) - apiErrorResponse(w, "node not found", http.StatusNotFound, nil) + apiErrorResponse(w, "node not found", http.StatusNotFound, err) } else { apiErrorResponse(w, "error getting node", http.StatusInternalServerError, err) } + incMetric(metricAPIEnvsErr) + return + } + // Get context data and check access + ctx := r.Context().Value(contextKey(contextAPI)).(contextValue) + if !apiUsers.CheckPermissions(ctx[ctxUser], users.EnvLevel, node.Environment) { + apiErrorResponse(w, "no access", http.StatusForbidden, fmt.Errorf("attempt to use API by user %s", ctx[ctxUser])) + incMetric(metricAPIEnvsErr) return } // Serialize and serve JSON @@ -61,23 +62,21 @@ func apiNodesHandler(w http.ResponseWriter, r *http.Request) { utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAPI), false) // Get context data and check access ctx := r.Context().Value(contextKey(contextAPI)).(contextValue) - if !apiUsers.IsAdmin(ctx["user"]) { - incMetric(metricAPINodesErr) - log.Printf("attempt to use API by user %s", ctx["user"]) - apiErrorResponse(w, "no access", http.StatusForbidden, nil) + if !apiUsers.CheckPermissions(ctx[ctxUser], users.AdminLevel, users.NoEnvironment) { + apiErrorResponse(w, "no access", http.StatusForbidden, fmt.Errorf("attempt to use API by user %s", ctx[ctxUser])) + incMetric(metricAPIEnvsErr) return } // Get nodes nodes, err := nodesmgr.Gets("all", 0) if err != nil { - incMetric(metricAPINodesErr) apiErrorResponse(w, "error getting nodes", http.StatusInternalServerError, err) + incMetric(metricAPINodesErr) return } if len(nodes) == 0 { - incMetric(metricAPINodesErr) - log.Printf("no nodes") apiErrorResponse(w, "no nodes", http.StatusNotFound, nil) + incMetric(metricAPINodesErr) return } // Serialize and serve JSON diff --git a/api/handlers-platforms.go b/api/handlers-platforms.go index b9652a2b..47bf0288 100644 --- a/api/handlers-platforms.go +++ b/api/handlers-platforms.go @@ -1,10 +1,12 @@ package main import ( + "fmt" "log" "net/http" "github.com/jmpsec/osctrl/settings" + "github.com/jmpsec/osctrl/users" "github.com/jmpsec/osctrl/utils" ) @@ -20,17 +22,16 @@ func apiPlatformsHandler(w http.ResponseWriter, r *http.Request) { utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAPI), false) // Get context data and check access ctx := r.Context().Value(contextKey(contextAPI)).(contextValue) - if !apiUsers.IsAdmin(ctx["user"]) { - incMetric(metricAPIPlatformsErr) - log.Printf("attempt to use API by user %s", ctx["user"]) - apiErrorResponse(w, "no access", http.StatusForbidden, nil) + if !apiUsers.CheckPermissions(ctx[ctxUser], users.AdminLevel, users.NoEnvironment) { + apiErrorResponse(w, "no access", http.StatusForbidden, fmt.Errorf("attempt to use API by user %s", ctx[ctxUser])) + incMetric(metricAPIEnvsErr) return } // Get platforms platforms, err := nodesmgr.GetAllPlatforms() if err != nil { - incMetric(metricAPIPlatformsErr) apiErrorResponse(w, "error getting platforms", http.StatusInternalServerError, err) + incMetric(metricAPIPlatformsErr) return } // Serialize and serve JSON diff --git a/api/handlers-queries.go b/api/handlers-queries.go index 8137017c..5859c7cb 100644 --- a/api/handlers-queries.go +++ b/api/handlers-queries.go @@ -2,12 +2,14 @@ package main import ( "encoding/json" + "fmt" "log" "net/http" "github.com/gorilla/mux" "github.com/jmpsec/osctrl/queries" "github.com/jmpsec/osctrl/settings" + "github.com/jmpsec/osctrl/users" "github.com/jmpsec/osctrl/utils" ) @@ -25,28 +27,26 @@ func apiQueryShowHandler(w http.ResponseWriter, r *http.Request) { // Extract name name, ok := vars["name"] if !ok { - incMetric(metricAPIQueriesErr) apiErrorResponse(w, "error getting name", http.StatusInternalServerError, nil) + incMetric(metricAPIQueriesErr) return } // Get context data and check access ctx := r.Context().Value(contextKey(contextAPI)).(contextValue) - if !apiUsers.IsAdmin(ctx["user"]) { - incMetric(metricAPIQueriesErr) - log.Printf("attempt to use API by user %s", ctx["user"]) - apiErrorResponse(w, "no access", http.StatusForbidden, nil) + if !apiUsers.CheckPermissions(ctx[ctxUser], users.QueryLevel, users.NoEnvironment) { + apiErrorResponse(w, "no access", http.StatusForbidden, fmt.Errorf("attempt to use API by user %s", ctx[ctxUser])) + incMetric(metricAPIEnvsErr) return } // Get query by name query, err := queriesmgr.Get(name) if err != nil { - incMetric(metricAPIQueriesErr) if err.Error() == "record not found" { - log.Printf("query not found: %s", name) - apiErrorResponse(w, "query not found", http.StatusNotFound, nil) + apiErrorResponse(w, "query not found", http.StatusNotFound, err) } else { apiErrorResponse(w, "error getting query", http.StatusInternalServerError, err) } + incMetric(metricAPIEnvsErr) return } // Serialize and serve JSON @@ -63,23 +63,23 @@ func apiQueriesRunHandler(w http.ResponseWriter, r *http.Request) { utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAPI), false) // Get context data and check access ctx := r.Context().Value(contextKey(contextAPI)).(contextValue) - if !apiUsers.IsAdmin(ctx["user"]) { - incMetric(metricAPIQueriesErr) - log.Printf("attempt to use API by user %s", ctx["user"]) - apiErrorResponse(w, "no access", http.StatusForbidden, nil) + if !apiUsers.CheckPermissions(ctx[ctxUser], users.QueryLevel, users.NoEnvironment) { + apiErrorResponse(w, "no access", http.StatusForbidden, fmt.Errorf("attempt to use API by user %s", ctx[ctxUser])) + incMetric(metricAPIEnvsErr) return } var q DistributedQueryRequest // Parse request JSON body if err := json.NewDecoder(r.Body).Decode(&q); err != nil { - incMetric(metricAPIQueriesErr) apiErrorResponse(w, "error parsing POST body", http.StatusInternalServerError, err) + incMetric(metricAPIQueriesErr) return } // FIXME check validity of query // Query can not be empty if q.Query == "" { apiErrorResponse(w, "query can not be empty", http.StatusInternalServerError, nil) + incMetric(metricAPIQueriesErr) return } // Prepare and create new query @@ -87,7 +87,7 @@ func apiQueriesRunHandler(w http.ResponseWriter, r *http.Request) { newQuery := queries.DistributedQuery{ Query: q.Query, Name: queryName, - Creator: ctx["user"], + Creator: ctx[ctxUser], Expected: 0, Executions: 0, Active: true, @@ -97,8 +97,8 @@ func apiQueriesRunHandler(w http.ResponseWriter, r *http.Request) { Type: queries.StandardQueryType, } if err := queriesmgr.Create(newQuery); err != nil { - incMetric(metricAPIQueriesErr) apiErrorResponse(w, "error creating query", http.StatusInternalServerError, err) + incMetric(metricAPIQueriesErr) return } // Temporary list of UUIDs to calculate Expected @@ -109,11 +109,13 @@ func apiQueriesRunHandler(w http.ResponseWriter, r *http.Request) { if (e != "") && envs.Exists(e) { if err := queriesmgr.CreateTarget(queryName, queries.QueryTargetEnvironment, e); err != nil { apiErrorResponse(w, "error creating query environment target", http.StatusInternalServerError, err) + incMetric(metricAPIQueriesErr) return } nodes, err := nodesmgr.GetByEnv(e, "active", settingsmgr.InactiveHours()) if err != nil { apiErrorResponse(w, "error getting nodes by environment", http.StatusInternalServerError, err) + incMetric(metricAPIQueriesErr) return } for _, n := range nodes { @@ -127,14 +129,14 @@ func apiQueriesRunHandler(w http.ResponseWriter, r *http.Request) { for _, p := range q.Platforms { if (p != "") && checkValidPlatform(p) { if err := queriesmgr.CreateTarget(queryName, queries.QueryTargetPlatform, p); err != nil { - incMetric(metricAPIQueriesErr) apiErrorResponse(w, "error creating query platform target", http.StatusInternalServerError, err) + incMetric(metricAPIQueriesErr) return } nodes, err := nodesmgr.GetByPlatform(p, "active", settingsmgr.InactiveHours()) if err != nil { - incMetric(metricAPIQueriesErr) apiErrorResponse(w, "error getting nodes by platform", http.StatusInternalServerError, err) + incMetric(metricAPIQueriesErr) return } for _, n := range nodes { @@ -147,9 +149,9 @@ func apiQueriesRunHandler(w http.ResponseWriter, r *http.Request) { if len(q.UUIDs) > 0 { for _, u := range q.UUIDs { if (u != "") && nodesmgr.CheckByUUID(u) { - incMetric(metricAPIQueriesErr) if err := queriesmgr.CreateTarget(queryName, queries.QueryTargetUUID, u); err != nil { apiErrorResponse(w, "error creating query UUID target", http.StatusInternalServerError, err) + incMetric(metricAPIQueriesErr) return } expected = append(expected, u) @@ -161,8 +163,8 @@ func apiQueriesRunHandler(w http.ResponseWriter, r *http.Request) { for _, h := range q.Hosts { if (h != "") && nodesmgr.CheckByHost(h) { if err := queriesmgr.CreateTarget(queryName, queries.QueryTargetLocalname, h); err != nil { - incMetric(metricAPIQueriesErr) apiErrorResponse(w, "error creating query hostname target", http.StatusInternalServerError, err) + incMetric(metricAPIQueriesErr) return } expected = append(expected, h) @@ -173,8 +175,8 @@ func apiQueriesRunHandler(w http.ResponseWriter, r *http.Request) { expectedClear := removeStringDuplicates(expected) // Update value for expected if err := queriesmgr.SetExpected(queryName, len(expectedClear)); err != nil { - incMetric(metricAPIQueriesErr) apiErrorResponse(w, "error setting expected", http.StatusInternalServerError, err) + incMetric(metricAPIQueriesErr) return } // Return query name as serialized response @@ -186,17 +188,23 @@ func apiQueriesRunHandler(w http.ResponseWriter, r *http.Request) { func apiAllQueriesShowHandler(w http.ResponseWriter, r *http.Request) { incMetric(metricAPIQueriesReq) utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAPI), false) + // Get context data and check access + ctx := r.Context().Value(contextKey(contextAPI)).(contextValue) + if !apiUsers.CheckPermissions(ctx[ctxUser], users.QueryLevel, users.NoEnvironment) { + apiErrorResponse(w, "no access", http.StatusForbidden, fmt.Errorf("attempt to use API by user %s", ctx[ctxUser])) + incMetric(metricAPIEnvsErr) + return + } // Get queries queries, err := queriesmgr.GetQueries(queries.TargetCompleted) if err != nil { - incMetric(metricAPIQueriesErr) apiErrorResponse(w, "error getting queries", http.StatusInternalServerError, err) + incMetric(metricAPIQueriesErr) return } if len(queries) == 0 { - incMetric(metricAPIQueriesErr) - log.Printf("no queries") apiErrorResponse(w, "no queries", http.StatusNotFound, nil) + incMetric(metricAPIQueriesErr) return } // Serialize and serve JSON @@ -208,17 +216,23 @@ func apiAllQueriesShowHandler(w http.ResponseWriter, r *http.Request) { func apiHiddenQueriesShowHandler(w http.ResponseWriter, r *http.Request) { incMetric(metricAPIQueriesReq) utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAPI), false) + // Get context data and check access + ctx := r.Context().Value(contextKey(contextAPI)).(contextValue) + if !apiUsers.CheckPermissions(ctx[ctxUser], users.QueryLevel, users.NoEnvironment) { + apiErrorResponse(w, "no access", http.StatusForbidden, fmt.Errorf("attempt to use API by user %s", ctx[ctxUser])) + incMetric(metricAPIEnvsErr) + return + } // Get queries queries, err := queriesmgr.GetQueries(queries.TargetHiddenCompleted) if err != nil { - incMetric(metricAPIQueriesErr) apiErrorResponse(w, "error getting queries", http.StatusInternalServerError, err) + incMetric(metricAPIQueriesErr) return } if len(queries) == 0 { - incMetric(metricAPIQueriesErr) - log.Printf("no queries") apiErrorResponse(w, "no queries", http.StatusNotFound, nil) + incMetric(metricAPIQueriesErr) return } // Serialize and serve JSON @@ -234,28 +248,26 @@ func apiQueryResultsHandler(w http.ResponseWriter, r *http.Request) { // Extract name name, ok := vars["name"] if !ok { - incMetric(metricAPIQueriesErr) apiErrorResponse(w, "error getting name", http.StatusInternalServerError, nil) + incMetric(metricAPIQueriesErr) return } // Get context data and check access ctx := r.Context().Value(contextKey(contextAPI)).(contextValue) - if !apiUsers.IsAdmin(ctx["user"]) { - incMetric(metricAPIQueriesErr) - log.Printf("attempt to use API by user %s", ctx["user"]) - apiErrorResponse(w, "no access", http.StatusForbidden, nil) + if !apiUsers.CheckPermissions(ctx[ctxUser], users.QueryLevel, users.NoEnvironment) { + apiErrorResponse(w, "no access", http.StatusForbidden, fmt.Errorf("attempt to use API by user %s", ctx[ctxUser])) + incMetric(metricAPIEnvsErr) return } // Get query by name queryLogs, err := postgresQueryLogs(name) if err != nil { - incMetric(metricAPIQueriesErr) if err.Error() == "record not found" { - log.Printf("query not found: %s", name) - apiErrorResponse(w, "query not found", http.StatusNotFound, nil) + apiErrorResponse(w, "query not found", http.StatusNotFound, err) } else { - apiErrorResponse(w, "error getting results", http.StatusInternalServerError, err) + apiErrorResponse(w, "error getting query", http.StatusInternalServerError, err) } + incMetric(metricAPIEnvsErr) return } // Serialize and serve JSON diff --git a/api/main.go b/api/main.go index b6410eb2..ea2e2738 100644 --- a/api/main.go +++ b/api/main.go @@ -249,8 +249,8 @@ func main() { routerAPI.Handle(_apiPath(apiPlatformsPath), handlerAuthCheck(http.HandlerFunc(apiPlatformsHandler))).Methods("GET") routerAPI.Handle(_apiPath(apiPlatformsPath)+"/", handlerAuthCheck(http.HandlerFunc(apiPlatformsHandler))).Methods("GET") // API: environments - routerAPI.Handle(_apiPath(apiEnvironmentsPath)+"/{name}", handlerAuthCheck(http.HandlerFunc(apiEnvironmentHandler))).Methods("GET") - routerAPI.Handle(_apiPath(apiEnvironmentsPath)+"/{name}/", handlerAuthCheck(http.HandlerFunc(apiEnvironmentHandler))).Methods("GET") + routerAPI.Handle(_apiPath(apiEnvironmentsPath)+"/{environment}", handlerAuthCheck(http.HandlerFunc(apiEnvironmentHandler))).Methods("GET") + routerAPI.Handle(_apiPath(apiEnvironmentsPath)+"/{environment}/", handlerAuthCheck(http.HandlerFunc(apiEnvironmentHandler))).Methods("GET") routerAPI.Handle(_apiPath(apiEnvironmentsPath), handlerAuthCheck(http.HandlerFunc(apiEnvironmentsHandler))).Methods("GET") routerAPI.Handle(_apiPath(apiEnvironmentsPath)+"/", handlerAuthCheck(http.HandlerFunc(apiEnvironmentsHandler))).Methods("GET") diff --git a/environments/environments.go b/environments/environments.go index 8ff1b932..7333bfdc 100644 --- a/environments/environments.go +++ b/environments/environments.go @@ -160,6 +160,19 @@ func (environment *Environment) All() ([]TLSEnvironment, error) { return envs, nil } +// Names gets just all TLS Environment names +func (environment *Environment) Names() ([]string, error) { + envs, err := environment.All() + if err != nil { + return []string{}, err + } + names := []string{} + for _, e := range envs { + names = append(names, e.Name) + } + return names, err +} + // GetMap returns the map of environments by name func (environment *Environment) GetMap() (MapEnvironments, error) { all, err := environment.All() diff --git a/environments/go.sum b/environments/go.sum new file mode 100644 index 00000000..1313ece8 --- /dev/null +++ b/environments/go.sum @@ -0,0 +1,125 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/denisenkom/go-mssqldb v0.0.0-20190423183735-731ef375ac02/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/jinzhu/gorm v1.9.8 h1:n5uvxqLepIP2R1XF7pudpt9Rv8I3m7G9trGxJVjLZ5k= +github.com/jinzhu/gorm v1.9.8/go.mod h1:bdqTT3q6dhSph2K3pWxrHP6nqxuAp2yQ3KFtc3U3F84= +github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a h1:eeaG9XMUvRBYXJi4pg1ZKM7nxc5AfXfojeLLW7O5J3k= +github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.0.0/go.mod h1:oHTiXerJ20+SfYcrdlBO7rzZRJWGwSTQ0iUY2jI6Gfc= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/segmentio/ksuid v1.0.2 h1:9yBfKyw4ECGTdALaF09Snw3sLJmYIX6AbPJrAy6MrDc= +github.com/segmentio/ksuid v1.0.2/go.mod h1:BXuJDr2byAiHuQaQtSKoXh1J0YmUDurywOXgB2w+OSU= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/settings/go.sum b/settings/go.sum new file mode 100644 index 00000000..cded202f --- /dev/null +++ b/settings/go.sum @@ -0,0 +1,123 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/denisenkom/go-mssqldb v0.0.0-20190423183735-731ef375ac02/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/jinzhu/gorm v1.9.8 h1:n5uvxqLepIP2R1XF7pudpt9Rv8I3m7G9trGxJVjLZ5k= +github.com/jinzhu/gorm v1.9.8/go.mod h1:bdqTT3q6dhSph2K3pWxrHP6nqxuAp2yQ3KFtc3U3F84= +github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a h1:eeaG9XMUvRBYXJi4pg1ZKM7nxc5AfXfojeLLW7O5J3k= +github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.0.0/go.mod h1:oHTiXerJ20+SfYcrdlBO7rzZRJWGwSTQ0iUY2jI6Gfc= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/settings/settings.go b/settings/settings.go index 51dda578..7c0149f3 100644 --- a/settings/settings.go +++ b/settings/settings.go @@ -102,6 +102,13 @@ type Settings struct { DB *gorm.DB } +// ValidTypes to check validity of settings type +var ValidTypes = map[string]struct{}{ + TypeString: struct{}{}, + TypeBoolean: struct{}{}, + TypeInteger: struct{}{}, +} + // NewSettings to initialize the access to settings and table func NewSettings(backend *gorm.DB) *Settings { var s *Settings @@ -182,6 +189,12 @@ func (conf *Settings) NewIntegerValue(service, name string, value int64) error { return conf.NewValue(service, name, TypeInteger, value) } +// VerifyType to make sure type is valid +func (conf *Settings) VerifyType(sType string) bool { + _, ok := ValidTypes[sType] + return ok +} + // DeleteValue deletes an existing settings value func (conf *Settings) DeleteValue(service, name string) error { value, err := conf.RetrieveValue(service, name) diff --git a/users/go.sum b/users/go.sum index 61a0429d..4c5f1f7e 100644 --- a/users/go.sum +++ b/users/go.sum @@ -48,6 +48,7 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/lib/pq v1.1.0 h1:/5u4a+KGJptBRqGzPvYQL9p0d/tPR4S31+Tnzj9lEO4= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= diff --git a/users/permissions.go b/users/permissions.go new file mode 100644 index 00000000..ad4c9d20 --- /dev/null +++ b/users/permissions.go @@ -0,0 +1,74 @@ +package users + +import ( + "encoding/json" + "fmt" +) + +// EnvPermissions to hold permissions for environments +type EnvPermissions map[string]bool + +// UserPermissions to abstract the permissions for a user +type UserPermissions struct { + Environments EnvPermissions `json:"environments"` + Query bool `json:"query"` + Carve bool `json:"carve"` +} + +// AccessLevel as abstraction of level of access for a user +type AccessLevel int + +const ( + // AdminLevel for admin privileges + AdminLevel AccessLevel = iota + // QueryLevel for query privileges + QueryLevel + // CarveLevel for carve privileges + CarveLevel + // EnvLevel for environment privileges + EnvLevel + // NoEnvironment to be explicit when used + NoEnvironment = "" +) + +// GenPermissions to generate the struct with empty permissions +func (m *UserManager) GenPermissions(environments []string, level bool) UserPermissions { + envs := make(EnvPermissions) + for _, e := range environments { + envs[e] = level + } + perms := UserPermissions{ + Environments: envs, + Query: level, + Carve: level, + } + return perms +} + +// CheckPermissions to verify access for a username +func (m *UserManager) CheckPermissions(username string, level AccessLevel, environment string) bool { + + return false +} + +// GetPermissions to extract permissions by username +func (m *UserManager) GetPermissions(username string) (UserPermissions, error) { + var perms UserPermissions + user, err := m.Get(username) + if err != nil { + return perms, fmt.Errorf("error getting user %v", err) + } + if err := json.Unmarshal(user.Permissions.RawMessage, &perms); err != nil { + return perms, fmt.Errorf("error parsing permissions %v", err) + } + return perms, nil +} + +// ConvertPermissions to convert from stored Jsonb to struct +func (m *UserManager) ConvertPermissions(raw json.RawMessage) (UserPermissions, error) { + var perms UserPermissions + if err := json.Unmarshal(raw, &perms); err != nil { + return perms, fmt.Errorf("error parsing permissions %v", err) + } + return perms, nil +} diff --git a/users/users.go b/users/users.go index 11a1e36d..51fd5334 100644 --- a/users/users.go +++ b/users/users.go @@ -1,12 +1,14 @@ package users import ( + "encoding/json" "fmt" "log" "time" "github.com/dgrijalva/jwt-go" "github.com/jinzhu/gorm" + "github.com/jinzhu/gorm/dialects/postgres" "golang.org/x/crypto/bcrypt" ) @@ -21,6 +23,7 @@ type AdminUser struct { TokenExpire time.Time Admin bool CSRFToken string + Permissions postgres.Jsonb LastIPAddress string LastUserAgent string LastAccess time.Time @@ -149,12 +152,17 @@ func (m *UserManager) New(username, password, email, fullname string, admin bool if err != nil { return AdminUser{}, err } + permsRaw, err := json.Marshal(m.GenPermissions([]string{}, admin)) + if err != nil { + permsRaw = []byte("{}") + } return AdminUser{ - Username: username, - PassHash: passhash, - Admin: admin, - Email: email, - Fullname: fullname, + Username: username, + PassHash: passhash, + Admin: admin, + Permissions: postgres.Jsonb{RawMessage: permsRaw}, + Email: email, + Fullname: fullname, }, nil } return AdminUser{}, fmt.Errorf("%s already exists", username) @@ -167,6 +175,15 @@ func (m *UserManager) Exists(username string) bool { return (results > 0) } +// ExistsGet checks if user exists and returns the user +func (m *UserManager) ExistsGet(username string) (bool, AdminUser) { + user, err := m.Get(username) + if err != nil { + return false, AdminUser{} + } + return true, user +} + // IsAdmin checks if user is an admin func (m *UserManager) IsAdmin(username string) bool { var results int @@ -188,6 +205,22 @@ func (m *UserManager) ChangeAdmin(username string, admin bool) error { return nil } +// ChangePermissions for setting user permissions by username +func (m *UserManager) ChangePermissions(username string, permissions UserPermissions) error { + user, err := m.Get(username) + if err != nil { + return fmt.Errorf("error getting user %v", err) + } + rawPerms, err := json.Marshal(permissions) + if err != nil { + return err + } + if err := m.DB.Model(&user).Update("permissions", postgres.Jsonb{RawMessage: rawPerms}).Error; err != nil { + return fmt.Errorf("Update %v", err) + } + return nil +} + // All get all users func (m *UserManager) All() ([]AdminUser, error) { var users []AdminUser @@ -238,7 +271,6 @@ func (m *UserManager) UpdateToken(username, token string, exp time.Time) error { AdminUser{ APIToken: token, TokenExpire: exp, - LastAccess: time.Now(), }).Error; err != nil { return fmt.Errorf("Update %v", err) } diff --git a/utils/http-utils.go b/utils/http-utils.go index 26fe697a..833fea88 100644 --- a/utils/http-utils.go +++ b/utils/http-utils.go @@ -95,7 +95,9 @@ func DebugHTTP(r *http.Request, debugCheck bool, showBody bool) string { // DebugHTTPDump - Helper for debugging purposes and dump a full HTTP request func DebugHTTPDump(r *http.Request, debugCheck bool, showBody bool) { - log.Println(DebugHTTP(r, debugCheck, showBody)) + if debugCheck { + log.Println(DebugHTTP(r, debugCheck, showBody)) + } } // HTTPResponse - Helper to send HTTP response