refactor: use context fom middleware in handlers

This commit is contained in:
Stavros
2025-08-25 14:19:52 +03:00
parent ace22acdb2
commit e1d8ce3cb5
11 changed files with 142 additions and 57 deletions

View File

@@ -4,7 +4,7 @@ tmp_dir = "tmp"
[build] [build]
pre_cmd = ["mkdir -p internal/assets/dist", "echo 'backend running' > internal/assets/dist/index.html", "go install github.com/go-delve/delve/cmd/dlv@v1.25.0"] pre_cmd = ["mkdir -p internal/assets/dist", "echo 'backend running' > internal/assets/dist/index.html", "go install github.com/go-delve/delve/cmd/dlv@v1.25.0"]
cmd = "CGO_ENABLED=0 go build -gcflags=\"all=-N -l\" -o tmp/tinyauth ." cmd = "CGO_ENABLED=0 go build -gcflags=\"all=-N -l\" -o tmp/tinyauth ."
bin = "/go/bin/dlv --listen :4000 --headless=true --api-version=2 --accept-multiclient --log=true exec tmp/tinyauth --continue" bin = "/go/bin/dlv --listen :4000 --headless=true --api-version=2 --accept-multiclient --log=true exec tmp/tinyauth --continue --check-go-version=false"
include_ext = ["go"] include_ext = ["go"]
exclude_dir = ["internal/assets/dist"] exclude_dir = ["internal/assets/dist"]
exclude_regex = [".*_test\\.go"] exclude_regex = [".*_test\\.go"]

View File

@@ -10,8 +10,8 @@ import (
"tinyauth/internal/constants" "tinyauth/internal/constants"
"tinyauth/internal/docker" "tinyauth/internal/docker"
"tinyauth/internal/handlers" "tinyauth/internal/handlers"
"tinyauth/internal/hooks"
"tinyauth/internal/ldap" "tinyauth/internal/ldap"
"tinyauth/internal/middleware"
"tinyauth/internal/providers" "tinyauth/internal/providers"
"tinyauth/internal/server" "tinyauth/internal/server"
"tinyauth/internal/types" "tinyauth/internal/types"
@@ -84,7 +84,7 @@ var rootCmd = &cobra.Command{
AppURL: config.AppURL, AppURL: config.AppURL,
} }
handlersConfig := types.HandlersConfig{ handlersConfig := handlers.HandlersConfig{
AppURL: config.AppURL, AppURL: config.AppURL,
DisableContinue: config.DisableContinue, DisableContinue: config.DisableContinue,
Title: config.Title, Title: config.Title,
@@ -116,10 +116,6 @@ var rootCmd = &cobra.Command{
EncryptionSecret: encryptionSecret, EncryptionSecret: encryptionSecret,
} }
hooksConfig := types.HooksConfig{
Domain: domain,
}
var ldapService *ldap.LDAP var ldapService *ldap.LDAP
if config.LdapAddress != "" { if config.LdapAddress != "" {
@@ -151,9 +147,20 @@ var rootCmd = &cobra.Command{
HandleError(err, "Failed to initialize docker") HandleError(err, "Failed to initialize docker")
auth := auth.NewAuth(authConfig, docker, ldapService) auth := auth.NewAuth(authConfig, docker, ldapService)
providers := providers.NewProviders(oauthConfig) providers := providers.NewProviders(oauthConfig)
hooks := hooks.NewHooks(hooksConfig, auth, providers) handlers := handlers.NewHandlers(handlersConfig, auth, providers, docker)
handlers := handlers.NewHandlers(handlersConfig, auth, hooks, providers, docker)
srv, err := server.NewServer(serverConfig, handlers) // Setup the middlewares
var middlewares []server.Middleware
contextMiddleware := middleware.NewContextMiddleware(middleware.ContextMiddlewareConfig{
Domain: domain,
}, auth, providers)
uiMiddleware := middleware.NewUIMiddleware()
zerologMiddleware := middleware.NewZerologMiddleware()
middlewares = append(middlewares, contextMiddleware, uiMiddleware, zerologMiddleware)
srv, err := server.NewServer(serverConfig, handlers, middlewares)
HandleError(err, "Failed to create server") HandleError(err, "Failed to create server")
// Start up // Start up

View File

@@ -37,8 +37,28 @@ func (h *Handlers) AppContextHandler(c *gin.Context) {
func (h *Handlers) UserContextHandler(c *gin.Context) { func (h *Handlers) UserContextHandler(c *gin.Context) {
log.Debug().Msg("Getting user context") log.Debug().Msg("Getting user context")
// Create user context using hooks // Get user context from middleware
userContext := h.Hooks.UseUserContext(c) userContextValue, exists := c.Get("context")
if !exists {
c.JSON(200, types.UserContextResponse{
Status: 200,
Message: "Unauthorized",
IsLoggedIn: false,
})
return
}
userContext, ok := userContextValue.(*types.UserContext)
if !ok {
c.JSON(200, types.UserContextResponse{
Status: 200,
Message: "Unauthorized",
IsLoggedIn: false,
})
return
}
userContextResponse := types.UserContextResponse{ userContextResponse := types.UserContextResponse{
Status: 200, Status: 200,

View File

@@ -3,26 +3,36 @@ package handlers
import ( import (
"tinyauth/internal/auth" "tinyauth/internal/auth"
"tinyauth/internal/docker" "tinyauth/internal/docker"
"tinyauth/internal/hooks"
"tinyauth/internal/providers" "tinyauth/internal/providers"
"tinyauth/internal/types"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
type HandlersConfig struct {
AppURL string
Domain string
CookieSecure bool
DisableContinue bool
GenericName string
Title string
ForgotPasswordMessage string
BackgroundImage string
OAuthAutoRedirect string
CsrfCookieName string
RedirectCookieName string
}
type Handlers struct { type Handlers struct {
Config types.HandlersConfig Config HandlersConfig
Auth *auth.Auth Auth *auth.Auth
Hooks *hooks.Hooks
Providers *providers.Providers Providers *providers.Providers
Docker *docker.Docker Docker *docker.Docker
} }
func NewHandlers(config types.HandlersConfig, auth *auth.Auth, hooks *hooks.Hooks, providers *providers.Providers, docker *docker.Docker) *Handlers { func NewHandlers(config HandlersConfig, auth *auth.Auth, providers *providers.Providers, docker *docker.Docker) *Handlers {
return &Handlers{ return &Handlers{
Config: config, Config: config,
Auth: auth, Auth: auth,
Hooks: hooks,
Providers: providers, Providers: providers,
Docker: docker, Docker: docker,
} }

View File

@@ -146,7 +146,24 @@ func (h *Handlers) ProxyHandler(c *gin.Context) {
return return
} }
userContext := h.Hooks.UseUserContext(c) var userContext *types.UserContext
userContextValue, exists := c.Get("context")
if !exists {
userContext = &types.UserContext{
IsLoggedIn: false,
}
} else {
var ok bool
userContext, ok = userContextValue.(*types.UserContext)
if !ok {
userContext = &types.UserContext{
IsLoggedIn: false,
}
}
}
// If we are using basic auth, we need to check if the user has totp and if it does then disable basic auth // If we are using basic auth, we need to check if the user has totp and if it does then disable basic auth
if userContext.Provider == "basic" && userContext.TotpEnabled { if userContext.Provider == "basic" && userContext.TotpEnabled {
@@ -158,7 +175,7 @@ func (h *Handlers) ProxyHandler(c *gin.Context) {
log.Debug().Msg("Authenticated") log.Debug().Msg("Authenticated")
// Check if user is allowed to access subdomain, if request is nginx.example.com the subdomain (resource) is nginx // Check if user is allowed to access subdomain, if request is nginx.example.com the subdomain (resource) is nginx
appAllowed := h.Auth.ResourceAllowed(c, userContext, labels) appAllowed := h.Auth.ResourceAllowed(c, *userContext, labels)
log.Debug().Bool("appAllowed", appAllowed).Msg("Checking if app is allowed") log.Debug().Bool("appAllowed", appAllowed).Msg("Checking if app is allowed")
@@ -195,7 +212,7 @@ func (h *Handlers) ProxyHandler(c *gin.Context) {
} }
if userContext.OAuth { if userContext.OAuth {
groupOk := h.Auth.OAuthGroup(c, userContext, labels) groupOk := h.Auth.OAuthGroup(c, *userContext, labels)
log.Debug().Bool("groupOk", groupOk).Msg("Checking if user is in required groups") log.Debug().Bool("groupOk", groupOk).Msg("Checking if user is in required groups")

View File

@@ -141,7 +141,25 @@ func (h *Handlers) TOTPHandler(c *gin.Context) {
log.Debug().Msg("Checking totp") log.Debug().Msg("Checking totp")
// Get user context // Get user context
userContext := h.Hooks.UseUserContext(c) userContextValue, exists := c.Get("context")
if !exists {
c.JSON(401, gin.H{
"status": 401,
"message": "Unauthorized",
})
return
}
userContext, ok := userContextValue.(*types.UserContext)
if !ok {
c.JSON(401, gin.H{
"status": 401,
"message": "Unauthorized",
})
return
}
// Check if we have a user // Check if we have a user
if userContext.Username == "" { if userContext.Username == "" {
@@ -157,7 +175,7 @@ func (h *Handlers) TOTPHandler(c *gin.Context) {
user := h.Auth.GetLocalUser(userContext.Username) user := h.Auth.GetLocalUser(userContext.Username)
// Check if totp is correct // Check if totp is correct
ok := totp.Validate(totpReq.Code, user.TotpSecret) ok = totp.Validate(totpReq.Code, user.TotpSecret)
if !ok { if !ok {
log.Debug().Msg("Totp incorrect") log.Debug().Msg("Totp incorrect")

View File

@@ -1,4 +1,4 @@
package middlewares package middleware
import ( import (
"fmt" "fmt"
@@ -29,6 +29,14 @@ func NewContextMiddleware(config ContextMiddlewareConfig, auth *auth.Auth, provi
} }
} }
func (m *ContextMiddleware) Init() error {
return nil
}
func (m *ContextMiddleware) Name() string {
return "ContextMiddleware"
}
func (m *ContextMiddleware) Middleware() gin.HandlerFunc { func (m *ContextMiddleware) Middleware() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
cookie, err := m.Auth.GetSessionCookie(c) cookie, err := m.Auth.GetSessionCookie(c)

View File

@@ -1,4 +1,4 @@
package middlewares package middleware
import ( import (
"io/fs" "io/fs"
@@ -16,24 +16,29 @@ type UIMiddleware struct {
ResourcesFileServer http.Handler ResourcesFileServer http.Handler
} }
func NewUIMiddleware() (*UIMiddleware, error) { func NewUIMiddleware() *UIMiddleware {
return &UIMiddleware{}
}
func (m *UIMiddleware) Init() error {
ui, err := fs.Sub(assets.Assets, "dist") ui, err := fs.Sub(assets.Assets, "dist")
if err != nil { if err != nil {
return nil, err return nil
} }
uiFileServer := http.FileServer(http.FS(ui)) m.UIFS = ui
resourcesFileServer := http.FileServer(http.Dir("/data/resources")) m.UIFileServer = http.FileServer(http.FS(ui))
m.ResourcesFileServer = http.FileServer(http.Dir("/data/resources"))
return &UIMiddleware{ return nil
UIFS: ui,
UIFileServer: uiFileServer,
ResourcesFileServer: resourcesFileServer,
}, nil
} }
func (m UIMiddleware) Middlware() gin.HandlerFunc { func (m *UIMiddleware) Name() string {
return "UIMiddleware"
}
func (m *UIMiddleware) Middleware() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
switch strings.Split(c.Request.URL.Path, "/")[1] { switch strings.Split(c.Request.URL.Path, "/")[1] {
case "api": case "api":

View File

@@ -1,4 +1,4 @@
package middlewares package middleware
import ( import (
"strings" "strings"
@@ -22,7 +22,15 @@ func NewZerologMiddleware() *ZerologMiddleware {
return &ZerologMiddleware{} return &ZerologMiddleware{}
} }
func (m ZerologMiddleware) logPath(path string) bool { func (m *ZerologMiddleware) Init() error {
return nil
}
func (m *ZerologMiddleware) Name() string {
return "ZerologMiddleware"
}
func (m *ZerologMiddleware) logPath(path string) bool {
for _, prefix := range loggerSkipPathsPrefix { for _, prefix := range loggerSkipPathsPrefix {
if strings.HasPrefix(path, prefix) { if strings.HasPrefix(path, prefix) {
return false return false
@@ -31,7 +39,7 @@ func (m ZerologMiddleware) logPath(path string) bool {
return true return true
} }
func (m ZerologMiddleware) Middlware() gin.HandlerFunc { func (m *ZerologMiddleware) Middleware() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
tStart := time.Now() tStart := time.Now()

View File

@@ -15,15 +15,22 @@ type Server struct {
Router *gin.Engine Router *gin.Engine
} }
type Middlware interface { type Middleware interface {
Middlware() gin.HandlerFunc Middleware() gin.HandlerFunc
Init() error
Name() string
} }
func NewServer(config types.ServerConfig, handlers *handlers.Handlers, middlewares []Middlware) (*Server, error) { func NewServer(config types.ServerConfig, handlers *handlers.Handlers, middlewares []Middleware) (*Server, error) {
router := gin.New() router := gin.New()
for _, middleware := range middlewares { for _, middleware := range middlewares {
router.Use(middleware.Middlware()) log.Debug().Str("middleware", middleware.Name()).Msg("Initializing middleware")
err := middleware.Init()
if err != nil {
return nil, fmt.Errorf("failed to initialize middleware %s: %w", middleware.Name(), err)
}
router.Use(middleware.Middleware())
} }
// Proxy routes // Proxy routes

View File

@@ -44,21 +44,6 @@ type Config struct {
LdapSearchFilter string `mapstructure:"ldap-search-filter"` LdapSearchFilter string `mapstructure:"ldap-search-filter"`
} }
// Server configuration
type HandlersConfig struct {
AppURL string
Domain string
CookieSecure bool
DisableContinue bool
GenericName string
Title string
ForgotPasswordMessage string
BackgroundImage string
OAuthAutoRedirect string
CsrfCookieName string
RedirectCookieName string
}
// OAuthConfig is the configuration for the providers // OAuthConfig is the configuration for the providers
type OAuthConfig struct { type OAuthConfig struct {
GithubClientId string GithubClientId string