Skip to content
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -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=
Expand Down
1 change: 1 addition & 0 deletions internals/config/structure/structure.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
41 changes: 40 additions & 1 deletion internals/config/tokens.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package config

import (
"path/filepath"
"reflect"
"strconv"
"strings"

"github.com/codeshelldev/gotl/pkg/configutils"
"github.com/codeshelldev/gotl/pkg/logger"
Expand All @@ -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())
Expand Down Expand Up @@ -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)
}
}
100 changes: 59 additions & 41 deletions internals/proxy/middlewares/auth.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package middlewares

import (
"context"
"encoding/base64"
"errors"
"maps"
Expand All @@ -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")
Expand All @@ -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")
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand All @@ -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, "/")
Expand Down Expand Up @@ -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\"")

Expand Down
30 changes: 27 additions & 3 deletions internals/proxy/middlewares/common.go
Original file line number Diff line number Diff line change
@@ -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"
)
Expand All @@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions internals/proxy/middlewares/endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import (
"path"
"slices"
"strings"

"github.com/codeshelldev/gotl/pkg/logger"
)

var Endpoints Middleware = Middleware{
Expand All @@ -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
Expand Down
47 changes: 41 additions & 6 deletions internals/proxy/middlewares/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})
}
}
3 changes: 2 additions & 1 deletion internals/proxy/middlewares/mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand All @@ -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
Expand Down
Loading