diff --git a/go.mod b/go.mod index ed4ec3ea..8fc41b9c 100644 --- a/go.mod +++ b/go.mod @@ -2,9 +2,9 @@ module github.com/codeshelldev/secured-signal-api go 1.25.5 -require github.com/codeshelldev/gotl v0.0.9 +require github.com/codeshelldev/gotl v0.0.10 -require go.uber.org/zap v1.27.1 // indirect +require go.uber.org/zap v1.27.1 require ( github.com/fsnotify/fsnotify v1.9.0 // indirect diff --git a/go.sum b/go.sum index e272a597..72f06e3e 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/codeshelldev/gotl v0.0.9 h1:cdLA6XzPt+f4RIW24Yx3dqBbRAq5JO0obzuwhaOgsEo= -github.com/codeshelldev/gotl v0.0.9/go.mod h1:rDkJma6eQSRfCr7ieX9/esn3/uAWNzjHfpjlr9j6FFk= +github.com/codeshelldev/gotl v0.0.10 h1:/2HOPXTlG1HplbXvkB1cZG6NQlGHCiZfWR1pMj6X55M= +github.com/codeshelldev/gotl v0.0.10/go.mod h1:rDkJma6eQSRfCr7ieX9/esn3/uAWNzjHfpjlr9j6FFk= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= diff --git a/internals/config/structure/structure.go b/internals/config/structure/structure.go index ba2ce910..b276d57d 100644 --- a/internals/config/structure/structure.go +++ b/internals/config/structure/structure.go @@ -11,6 +11,7 @@ type ENV struct { } type CONFIG struct { + NAME string `koanf:"name"` SERVICE SERVICE `koanf:"service"` API API `koanf:"api"` //TODO: deprecate overrides for tkconfigs diff --git a/internals/config/tokens.go b/internals/config/tokens.go index bf37a16b..6fb44532 100644 --- a/internals/config/tokens.go +++ b/internals/config/tokens.go @@ -1,7 +1,10 @@ package config import ( + "path/filepath" + "reflect" "strconv" + "strings" "github.com/codeshelldev/gotl/pkg/configutils" "github.com/codeshelldev/gotl/pkg/logger" @@ -12,7 +15,7 @@ import ( func LoadTokens() { logger.Debug("Loading Configs in ", ENV.TOKENS_DIR) - err := tokenConf.LoadDir("tokenconfigs", ENV.TOKENS_DIR, ".yml", yaml.Parser()) + err := tokenConf.LoadDir("tokenconfigs", ENV.TOKENS_DIR, ".yml", yaml.Parser(), setTokenConfigName) if err != nil { logger.Error("Could not Load Configs in ", ENV.TOKENS_DIR, ": ", err.Error()) @@ -84,3 +87,39 @@ func parseTokenConfigs(configArray []structure.CONFIG) map[string]structure.CONF return configs } + +func getSchemeTagByPointer(config any, tag string, fieldPointer any) string { + v := reflect.ValueOf(config) + if v.Kind() == reflect.Pointer { + v = v.Elem() + } + + fieldValue := reflect.ValueOf(fieldPointer).Elem() + + for i := 0; i < v.NumField(); i++ { + if v.Field(i).Addr().Interface() == fieldValue.Addr().Interface() { + field := v.Type().Field(i) + + return field.Tag.Get(tag) + } + } + + return "" +} + +func setTokenConfigName(config *configutils.Config, p string) { + schema := structure.CONFIG{ + NAME: "", + } + + nameField := getSchemeTagByPointer(&schema, "koanf", &schema.NAME) + + filename := filepath.Base(p) + filenameWithoutExt := strings.TrimSuffix(filename, filepath.Ext(filename)) + + name := config.Layer.String(nameField) + + if strings.TrimSpace(name) == "" { + config.Layer.Set(nameField, filenameWithoutExt) + } +} \ No newline at end of file diff --git a/internals/proxy/middlewares/auth.go b/internals/proxy/middlewares/auth.go index 23967ed9..a6757d33 100644 --- a/internals/proxy/middlewares/auth.go +++ b/internals/proxy/middlewares/auth.go @@ -1,7 +1,6 @@ package middlewares import ( - "context" "encoding/base64" "errors" "maps" @@ -21,13 +20,67 @@ var Auth Middleware = Middleware{ } const tokenKey contextKey = "token" +const isAuthKey contextKey = "isAuthenticated" + +func authHandler(next http.Handler) http.Handler { + tokenKeys := maps.Keys(config.ENV.CONFIGS) + tokens := slices.Collect(tokenKeys) + + if tokens == nil { + tokens = []string{} + } + + var authChain = NewAuthChain(). + Use(BearerAuth). + Use(BasicAuth). + Use(BodyAuth). + Use(QueryAuth). + Use(PathAuth) + + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + if len(tokens) <= 0 { + next.ServeHTTP(w, req) + return + } + + token, _ := authChain.Eval(w, req, tokens) + + if token == "" { + onUnauthorized(w) + + req = setContext(req, isAuthKey, false) + } else { + req = setContext(req, isAuthKey, true) + req = setContext(req, tokenKey, token) + } + + next.ServeHTTP(w, req) + }) +} + +var InternalAuthRequirement Middleware = Middleware{ + Name: "_Auth_Requirement", + Use: authRequirementHandler, +} + +func authRequirementHandler(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + isAuthenticated := getContext[bool](req, isAuthKey) + + if !isAuthenticated { + return + } + + next.ServeHTTP(w, req) + }) +} type AuthMethod struct { Name string Authenticate func(w http.ResponseWriter, req *http.Request, tokens []string) (string, error) } -var BearerAuth = AuthMethod { +var BearerAuth = AuthMethod{ Name: "Bearer", Authenticate: func(w http.ResponseWriter, req *http.Request, tokens []string) (string, error) { header := req.Header.Get("Authorization") @@ -50,7 +103,7 @@ var BearerAuth = AuthMethod { }, } -var BasicAuth = AuthMethod { +var BasicAuth = AuthMethod{ Name: "Basic", Authenticate: func(w http.ResponseWriter, req *http.Request, tokens []string) (string, error) { header := req.Header.Get("Authorization") @@ -92,7 +145,7 @@ var BasicAuth = AuthMethod { }, } -var BodyAuth = AuthMethod { +var BodyAuth = AuthMethod{ Name: "Body", Authenticate: func(w http.ResponseWriter, req *http.Request, tokens []string) (string, error) { const authField = "auth" @@ -133,7 +186,7 @@ var BodyAuth = AuthMethod { }, } -var QueryAuth = AuthMethod { +var QueryAuth = AuthMethod{ Name: "Query", Authenticate: func(w http.ResponseWriter, req *http.Request, tokens []string) (string, error) { const authQuery = "@authorization" @@ -158,7 +211,7 @@ var QueryAuth = AuthMethod { }, } -var PathAuth = AuthMethod { +var PathAuth = AuthMethod{ Name: "Path", Authenticate: func(w http.ResponseWriter, req *http.Request, tokens []string) (string, error) { parts := strings.Split(req.URL.Path, "/") @@ -187,41 +240,6 @@ var PathAuth = AuthMethod { }, } -func authHandler(next http.Handler) http.Handler { - tokenKeys := maps.Keys(config.ENV.CONFIGS) - tokens := slices.Collect(tokenKeys) - - if tokens == nil { - tokens = []string{} - } - - var authChain = NewAuthChain(). - Use(BearerAuth). - Use(BasicAuth). - Use(BodyAuth). - Use(QueryAuth). - Use(PathAuth) - - return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - if len(tokens) <= 0 { - next.ServeHTTP(w, req) - return - } - - token, _ := authChain.Eval(w, req, tokens) - - if token == "" { - onUnauthorized(w) - return - } - - ctx := context.WithValue(req.Context(), tokenKey, token) - req = req.WithContext(ctx) - - next.ServeHTTP(w, req) - }) -} - func onUnauthorized(w http.ResponseWriter) { w.Header().Set("WWW-Authenticate", "Basic realm=\"Login Required\", Bearer realm=\"Access Token Required\"") diff --git a/internals/proxy/middlewares/common.go b/internals/proxy/middlewares/common.go index 4cb4e6c1..cd497e20 100644 --- a/internals/proxy/middlewares/common.go +++ b/internals/proxy/middlewares/common.go @@ -1,8 +1,10 @@ package middlewares import ( + "context" "net/http" + "github.com/codeshelldev/gotl/pkg/logger" "github.com/codeshelldev/secured-signal-api/internals/config" "github.com/codeshelldev/secured-signal-api/internals/config/structure" ) @@ -13,10 +15,32 @@ type Context struct { type contextKey string -func getConfigByReq(req *http.Request) *structure.CONFIG { - token := req.Context().Value(tokenKey).(string) +func setContext(req *http.Request, key, value any) *http.Request { + ctx := context.WithValue(req.Context(), key, value) + return req.WithContext(ctx) +} + +func getContext[T any](req *http.Request, key any) T { + value, ok := req.Context().Value(key).(T) + + if !ok { + var zero T + return zero + } + + return value +} + +func getLogger(req *http.Request) *logger.Logger { + return getContext[*logger.Logger](req, loggerKey) +} - return getConfig(token) +func getToken(req *http.Request) string { + return getContext[string](req, tokenKey) +} + +func getConfigByReq(req *http.Request) *structure.CONFIG { + return getConfig(getToken(req)) } func getConfig(token string) *structure.CONFIG { diff --git a/internals/proxy/middlewares/endpoints.go b/internals/proxy/middlewares/endpoints.go index 363c1d5b..707ef844 100644 --- a/internals/proxy/middlewares/endpoints.go +++ b/internals/proxy/middlewares/endpoints.go @@ -5,8 +5,6 @@ import ( "path" "slices" "strings" - - "github.com/codeshelldev/gotl/pkg/logger" ) var Endpoints Middleware = Middleware{ @@ -16,6 +14,8 @@ var Endpoints Middleware = Middleware{ func endpointsHandler(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + logger := getLogger(req) + conf := getConfigByReq(req) endpoints := conf.SETTINGS.ACCESS.ENDPOINTS diff --git a/internals/proxy/middlewares/log.go b/internals/proxy/middlewares/log.go index edcfbd47..96fe536c 100644 --- a/internals/proxy/middlewares/log.go +++ b/internals/proxy/middlewares/log.go @@ -2,30 +2,65 @@ package middlewares import ( "net/http" + "strings" "github.com/codeshelldev/gotl/pkg/logger" "github.com/codeshelldev/gotl/pkg/request" + "go.uber.org/zap/zapcore" ) -var Logging Middleware = Middleware{ +var RequestLogger Middleware = Middleware{ Name: "Logging", Use: loggingHandler, } +const loggerKey contextKey = "logger" + func loggingHandler(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - if !logger.IsDev() { - logger.Info(req.Method, " ", req.URL.Path, " ", req.URL.RawQuery) + conf := getConfigByReq(req) + + logLevel := conf.SERVICE.LOG_LEVEL + + if strings.TrimSpace(logLevel) == "" { + logLevel = getConfig("").SERVICE.LOG_LEVEL + } + + options := logger.DefaultOptions() + options.EncodeCaller = func(caller zapcore.EntryCaller, enc zapcore.PrimitiveArrayEncoder) { + var name string + + if strings.TrimSpace(conf.NAME) != "" { + name = " " + conf.NAME + } + + enc.AppendString(caller.TrimmedPath() + name) + } + + l, err := logger.New(logLevel, options) + + if err != nil { + logger.Error("Could not create Middleware Logger: ", err.Error()) + } + + if l == nil { + l = logger.Get() + } + + req = setContext(req, loggerKey, l) + + if !l.IsDev() { + l.Info(req.Method, " ", req.URL.Path, " ", req.URL.RawQuery) } else { body, _ := request.GetReqBody(req) if body.Data != nil && !body.Empty { - logger.Dev(req.Method, " ", req.URL.Path, " ", req.URL.RawQuery, body.Data) + l.Dev(req.Method, " ", req.URL.Path, " ", req.URL.RawQuery, body.Data) } else { - logger.Info(req.Method, " ", req.URL.Path, " ", req.URL.RawQuery) + l.Info(req.Method, " ", req.URL.Path, " ", req.URL.RawQuery) } } next.ServeHTTP(w, req) }) -} +} \ No newline at end of file diff --git a/internals/proxy/middlewares/mapping.go b/internals/proxy/middlewares/mapping.go index 74b1cba3..14418ead 100644 --- a/internals/proxy/middlewares/mapping.go +++ b/internals/proxy/middlewares/mapping.go @@ -4,7 +4,6 @@ import ( "net/http" jsonutils "github.com/codeshelldev/gotl/pkg/jsonutils" - "github.com/codeshelldev/gotl/pkg/logger" request "github.com/codeshelldev/gotl/pkg/request" "github.com/codeshelldev/secured-signal-api/internals/config/structure" ) @@ -16,6 +15,8 @@ var Mapping Middleware = Middleware{ func mappingHandler(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + logger := getLogger(req) + conf := getConfigByReq(req) variables := conf.SETTINGS.MESSAGE.VARIABLES diff --git a/internals/proxy/middlewares/message.go b/internals/proxy/middlewares/message.go index c5543074..51a1748c 100644 --- a/internals/proxy/middlewares/message.go +++ b/internals/proxy/middlewares/message.go @@ -2,8 +2,8 @@ package middlewares import ( "net/http" + "strings" - "github.com/codeshelldev/gotl/pkg/logger" request "github.com/codeshelldev/gotl/pkg/request" ) @@ -14,6 +14,8 @@ var Message Middleware = Middleware{ func messageHandler(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + logger := getLogger(req) + conf := getConfigByReq(req) variables := conf.SETTINGS.MESSAGE.VARIABLES @@ -23,7 +25,7 @@ func messageHandler(next http.Handler) http.Handler { variables = getConfig("").SETTINGS.MESSAGE.VARIABLES } - if messageTemplate == "" { + if strings.TrimSpace(messageTemplate) == "" { messageTemplate = getConfig("").SETTINGS.MESSAGE.TEMPLATE } diff --git a/internals/proxy/middlewares/middleware.go b/internals/proxy/middlewares/middleware.go index 3588e161..a6e76861 100644 --- a/internals/proxy/middlewares/middleware.go +++ b/internals/proxy/middlewares/middleware.go @@ -2,6 +2,7 @@ package middlewares import ( "net/http" + "strings" "github.com/codeshelldev/gotl/pkg/logger" ) @@ -22,7 +23,12 @@ func NewChain() *Chain { func (chain *Chain) Use(middleware Middleware) *Chain { chain.middlewares = append(chain.middlewares, middleware) - logger.Debug("Registered ", middleware.Name, " middleware") + if strings.HasPrefix(middleware.Name, "_") { + logger.Dev("Registered ", middleware.Name, " middleware") + } else { + logger.Debug("Registered ", middleware.Name, " middleware") + } + return chain } diff --git a/internals/proxy/middlewares/policy.go b/internals/proxy/middlewares/policy.go index 9371696f..bb00ab23 100644 --- a/internals/proxy/middlewares/policy.go +++ b/internals/proxy/middlewares/policy.go @@ -5,7 +5,6 @@ import ( "net/http" "reflect" - "github.com/codeshelldev/gotl/pkg/logger" request "github.com/codeshelldev/gotl/pkg/request" "github.com/codeshelldev/secured-signal-api/internals/config/structure" "github.com/codeshelldev/secured-signal-api/utils/requestkeys" @@ -18,6 +17,8 @@ var Policy Middleware = Middleware{ func policyHandler(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + logger := getLogger(req) + conf := getConfigByReq(req) policies := conf.SETTINGS.ACCESS.FIELD_POLICIES diff --git a/internals/proxy/middlewares/port.go b/internals/proxy/middlewares/port.go index f7f6151f..716e04a3 100644 --- a/internals/proxy/middlewares/port.go +++ b/internals/proxy/middlewares/port.go @@ -5,8 +5,6 @@ import ( "net" "net/http" "strings" - - "github.com/codeshelldev/gotl/pkg/logger" ) var Port Middleware = Middleware{ @@ -16,6 +14,8 @@ var Port Middleware = Middleware{ func portHandler(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + logger := getLogger(req) + conf := getConfigByReq(req) allowedPort := conf.SERVICE.PORT diff --git a/internals/proxy/middlewares/template.go b/internals/proxy/middlewares/template.go index 4577df46..f2eccb91 100644 --- a/internals/proxy/middlewares/template.go +++ b/internals/proxy/middlewares/template.go @@ -8,7 +8,6 @@ import ( "strings" jsonutils "github.com/codeshelldev/gotl/pkg/jsonutils" - "github.com/codeshelldev/gotl/pkg/logger" query "github.com/codeshelldev/gotl/pkg/query" request "github.com/codeshelldev/gotl/pkg/request" templating "github.com/codeshelldev/gotl/pkg/templating" @@ -22,6 +21,8 @@ var Template Middleware = Middleware{ func templateHandler(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + logger := getLogger(req) + conf := getConfigByReq(req) variables := conf.SETTINGS.MESSAGE.VARIABLES diff --git a/internals/proxy/proxy.go b/internals/proxy/proxy.go index c549e7d9..1ee5def7 100644 --- a/internals/proxy/proxy.go +++ b/internals/proxy/proxy.go @@ -31,9 +31,10 @@ func Create(targetUrl string) Proxy { func (proxy Proxy) Init() http.Handler { handler := m.NewChain(). - Use(m.Logging). Use(m.Server). Use(m.Auth). + Use(m.RequestLogger). + Use(m.InternalAuthRequirement). Use(m.Port). Use(m.Template). Use(m.Endpoints).