From 4cd669b9d0041fa9250fe76589337df232ede3ff Mon Sep 17 00:00:00 2001 From: hunterlong Date: Fri, 1 May 2020 17:53:35 -0700 Subject: [PATCH] comments and organization --- handlers/authentication.go | 51 ++++++++++++++++++++++++++++++++ handlers/groups.go | 6 ++-- handlers/handlers.go | 59 ++++++++++++-------------------------- handlers/middleware.go | 3 ++ handlers/services.go | 3 +- types/services/yaml.go | 8 ++++-- 6 files changed, 81 insertions(+), 49 deletions(-) create mode 100644 handlers/authentication.go diff --git a/handlers/authentication.go b/handlers/authentication.go new file mode 100644 index 00000000..9dfc93ae --- /dev/null +++ b/handlers/authentication.go @@ -0,0 +1,51 @@ +package handlers + +import ( + "crypto/subtle" + "github.com/statping/statping/types/core" + "github.com/statping/statping/utils" + "net/http" + "strings" +) + +// hasSetupEnv checks to see if the GO_ENV is set to 'true' +// or if the Statping instance has not been setup yet +func hasSetupEnv() bool { + if utils.Params.Get("GO_ENV") == "test" { + return true + } + if core.App == nil { + return true + } + if !core.App.Setup { + return false + } + return false +} + +// hasAPIQuery checks the `api` query parameter against the API Secret Key +func hasAPIQuery(r *http.Request) bool { + query := r.URL.Query() + key := query.Get("api") + if key == "" { + return false + } + if subtle.ConstantTimeCompare([]byte(key), []byte(core.App.ApiSecret)) == 1 { + return true + } + return false +} + +// hasAuthorizationHeader check to see if the Authorization header is the correct API Secret Key +func hasAuthorizationHeader(r *http.Request) bool { + var token string + tokens, ok := r.Header["Authorization"] + if ok && len(tokens) >= 1 { + token = tokens[0] + token = strings.TrimPrefix(token, "Bearer ") + if subtle.ConstantTimeCompare([]byte(token), []byte(core.App.ApiSecret)) == 1 { + return true + } + } + return false +} diff --git a/handlers/groups.go b/handlers/groups.go index 7f4b2c77..d06e2f88 100644 --- a/handlers/groups.go +++ b/handlers/groups.go @@ -21,10 +21,8 @@ func findGroup(r *http.Request) (*groups.Group, error) { if err != nil { return nil, err } - if !g.Public.Bool { - if !IsReadAuthenticated(r) { - return nil, errors.NotAuthenticated - } + if !g.Public.Bool && !IsReadAuthenticated(r) { + return nil, errors.NotAuthenticated } return g, nil } diff --git a/handlers/handlers.go b/handlers/handlers.go index 5beb44a8..c63b763c 100644 --- a/handlers/handlers.go +++ b/handlers/handlers.go @@ -1,17 +1,14 @@ package handlers import ( - "crypto/subtle" "crypto/tls" "encoding/json" "fmt" "github.com/dgrijalva/jwt-go" - "github.com/statping/statping/types/core" "github.com/statping/statping/types/errors" "html/template" "net/http" "path" - "strings" "time" "github.com/statping/statping/source" @@ -86,22 +83,14 @@ func RunHTTPServer(ip string, port int) error { // IsReadAuthenticated will allow Read Only authentication for some routes func IsReadAuthenticated(r *http.Request) bool { - if !core.App.Setup { - return false - } - var token string - query := r.URL.Query() - key := query.Get("api") - if subtle.ConstantTimeCompare([]byte(key), []byte(core.App.ApiSecret)) == 1 { + if ok := hasSetupEnv(); ok { return true } - tokens, ok := r.Header["Authorization"] - if ok && len(tokens) >= 1 { - token = tokens[0] - token = strings.TrimPrefix(token, "Bearer ") - if subtle.ConstantTimeCompare([]byte(token), []byte(core.App.ApiSecret)) == 1 { - return true - } + if ok := hasAPIQuery(r); ok { + return true + } + if ok := hasAuthorizationHeader(r); ok { + return true } return IsFullAuthenticated(r) } @@ -109,23 +98,14 @@ func IsReadAuthenticated(r *http.Request) bool { // IsFullAuthenticated returns true if the HTTP request is authenticated. You can set the environment variable GO_ENV=test // to bypass the admin authenticate to the dashboard features. func IsFullAuthenticated(r *http.Request) bool { - if utils.Params.Get("GO_ENV") == "test" { + if ok := hasSetupEnv(); ok { return true } - if core.App == nil { + if ok := hasAPIQuery(r); ok { return true } - if !core.App.Setup { - return false - } - var token string - tokens, ok := r.Header["Authorization"] - if ok && len(tokens) >= 1 { - token = tokens[0] - token = strings.TrimPrefix(token, "Bearer ") - if subtle.ConstantTimeCompare([]byte(token), []byte(core.App.ApiSecret)) == 1 { - return true - } + if ok := hasAuthorizationHeader(r); ok { + return true } return IsAdmin(r) } @@ -155,7 +135,15 @@ func getJwtToken(r *http.Request) (JwtClaim, error) { return claims, err } +// ScopeName will show private JSON fields in the API. +// It will return "admin" if request has valid admin authentication. func ScopeName(r *http.Request) string { + if ok := hasAPIQuery(r); ok { + return "admin" + } + if ok := hasAuthorizationHeader(r); ok { + return "admin" + } claim, err := getJwtToken(r) if err != nil { return "" @@ -168,12 +156,6 @@ func ScopeName(r *http.Request) string { // IsAdmin returns true if the user session is an administrator func IsAdmin(r *http.Request) bool { - if !core.App.Setup { - return false - } - if utils.Params.GetString("GO_ENV") == "test" { - return true - } claim, err := getJwtToken(r) if err != nil { return false @@ -183,10 +165,7 @@ func IsAdmin(r *http.Request) bool { // IsUser returns true if the user is registered func IsUser(r *http.Request) bool { - if !core.App.Setup { - return false - } - if utils.Params.Get("GO_ENV") == "test" { + if ok := hasSetupEnv(); ok { return true } tk, err := getJwtToken(r) diff --git a/handlers/middleware.go b/handlers/middleware.go index 4b0aa746..5430e403 100644 --- a/handlers/middleware.go +++ b/handlers/middleware.go @@ -90,6 +90,9 @@ func sendLog(next http.Handler) http.Handler { }) } +// scoped is a middleware handler that will remove private fields based on struct tags +// this will look for the `scope:"user,admin"` tag and remove the JSON field from response +// if user is not authenticated based on the scope. func scoped(handler func(r *http.Request) interface{}) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { data := handler(r) diff --git a/handlers/services.go b/handlers/services.go index 972003f6..be1a7b12 100644 --- a/handlers/services.go +++ b/handlers/services.go @@ -215,10 +215,9 @@ func apiServiceDeleteHandler(w http.ResponseWriter, r *http.Request) { } func apiAllServicesHandler(r *http.Request) interface{} { - user := IsUser(r) var srvs []services.Service for _, v := range services.AllInOrder() { - if !v.Public.Bool && !user { + if !v.Public.Bool && !IsUser(r) { continue } srvs = append(srvs, v) diff --git a/types/services/yaml.go b/types/services/yaml.go index 1d4dc87d..e3a73da8 100644 --- a/types/services/yaml.go +++ b/types/services/yaml.go @@ -26,14 +26,16 @@ func LoadServicesYaml() (*yamlFile, error) { log.Infof("Found %d services inside services.yml file", len(svrs.Services)) for _, svr := range svrs.Services { - log.Infof("Service %s %d, hash: %s", svr.Name, svr.Id, svr.Hash()) - if findServiceByHash(svr.Hash()) == nil { + serviceByHash := findServiceByHash(svr.Hash()) + if serviceByHash == nil { if err := svr.Create(); err != nil { return nil, errors.Wrapf(err, "could not create service %s", svr.Name) } - log.Infof("Automatically created service '%s' checking %s", svr.Name, svr.Domain) + log.Infof("Automatically creating service '%s' checking %s", svr.Name, svr.Domain) go ServiceCheckQueue(svr, true) + } else { + log.Infof("Service %s #%d, already inserted", svr.Name, serviceByHash.Id) } }