mirror of
https://github.com/steveiliop56/tinyauth.git
synced 2025-12-13 19:56:38 +00:00
Compare commits
2 Commits
nightly
...
refactor/b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2932aba750 | ||
|
|
5cad1f0219 |
@@ -13,32 +13,26 @@ import (
|
||||
"time"
|
||||
"tinyauth/internal/config"
|
||||
"tinyauth/internal/controller"
|
||||
"tinyauth/internal/middleware"
|
||||
"tinyauth/internal/model"
|
||||
"tinyauth/internal/service"
|
||||
"tinyauth/internal/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/rs/zerolog/log"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Controller interface {
|
||||
SetupRoutes()
|
||||
}
|
||||
|
||||
type Middleware interface {
|
||||
Middleware() gin.HandlerFunc
|
||||
Init() error
|
||||
}
|
||||
|
||||
type Service interface {
|
||||
Init() error
|
||||
}
|
||||
|
||||
type BootstrapApp struct {
|
||||
config config.Config
|
||||
uuid string
|
||||
config config.Config
|
||||
context struct {
|
||||
uuid string
|
||||
cookieDomain string
|
||||
sessionCookieName string
|
||||
csrfCookieName string
|
||||
redirectCookieName string
|
||||
users []config.User
|
||||
oauthProviders map[string]config.OAuthServiceConfig
|
||||
configuredProviders []controller.Provider
|
||||
}
|
||||
services Services
|
||||
}
|
||||
|
||||
func NewBootstrapApp(config config.Config) *BootstrapApp {
|
||||
@@ -55,6 +49,8 @@ func (app *BootstrapApp) Setup() error {
|
||||
return err
|
||||
}
|
||||
|
||||
app.context.users = users
|
||||
|
||||
// Get OAuth configs
|
||||
oauthProviders, err := utils.GetOAuthProvidersConfig(os.Environ(), os.Args, app.config.AppURL)
|
||||
|
||||
@@ -62,6 +58,8 @@ func (app *BootstrapApp) Setup() error {
|
||||
return err
|
||||
}
|
||||
|
||||
app.context.oauthProviders = oauthProviders
|
||||
|
||||
// Get cookie domain
|
||||
cookieDomain, err := utils.GetCookieDomain(app.config.AppURL)
|
||||
|
||||
@@ -69,97 +67,33 @@ func (app *BootstrapApp) Setup() error {
|
||||
return err
|
||||
}
|
||||
|
||||
app.context.cookieDomain = cookieDomain
|
||||
|
||||
// Cookie names
|
||||
appUrl, _ := url.Parse(app.config.AppURL) // Already validated
|
||||
uuid := utils.GenerateUUID(appUrl.Hostname())
|
||||
app.uuid = uuid
|
||||
cookieId := strings.Split(uuid, "-")[0]
|
||||
sessionCookieName := fmt.Sprintf("%s-%s", config.SessionCookieName, cookieId)
|
||||
csrfCookieName := fmt.Sprintf("%s-%s", config.CSRFCookieName, cookieId)
|
||||
redirectCookieName := fmt.Sprintf("%s-%s", config.RedirectCookieName, cookieId)
|
||||
app.context.uuid = utils.GenerateUUID(appUrl.Hostname())
|
||||
cookieId := strings.Split(app.context.uuid, "-")[0]
|
||||
app.context.sessionCookieName = fmt.Sprintf("%s-%s", config.SessionCookieName, cookieId)
|
||||
app.context.csrfCookieName = fmt.Sprintf("%s-%s", config.CSRFCookieName, cookieId)
|
||||
app.context.redirectCookieName = fmt.Sprintf("%s-%s", config.RedirectCookieName, cookieId)
|
||||
|
||||
// Dumps
|
||||
log.Trace().Interface("config", app.config).Msg("Config dump")
|
||||
log.Trace().Interface("users", users).Msg("Users dump")
|
||||
log.Trace().Interface("oauthProviders", oauthProviders).Msg("OAuth providers dump")
|
||||
log.Trace().Str("cookieDomain", cookieDomain).Msg("Cookie domain")
|
||||
log.Trace().Str("sessionCookieName", sessionCookieName).Msg("Session cookie name")
|
||||
log.Trace().Str("csrfCookieName", csrfCookieName).Msg("CSRF cookie name")
|
||||
log.Trace().Str("redirectCookieName", redirectCookieName).Msg("Redirect cookie name")
|
||||
log.Trace().Interface("users", app.context.users).Msg("Users dump")
|
||||
log.Trace().Interface("oauthProviders", app.context.oauthProviders).Msg("OAuth providers dump")
|
||||
log.Trace().Str("cookieDomain", app.context.cookieDomain).Msg("Cookie domain")
|
||||
log.Trace().Str("sessionCookieName", app.context.sessionCookieName).Msg("Session cookie name")
|
||||
log.Trace().Str("csrfCookieName", app.context.csrfCookieName).Msg("CSRF cookie name")
|
||||
log.Trace().Str("redirectCookieName", app.context.redirectCookieName).Msg("Redirect cookie name")
|
||||
|
||||
// Create configs
|
||||
authConfig := service.AuthServiceConfig{
|
||||
Users: users,
|
||||
OauthWhitelist: app.config.OAuthWhitelist,
|
||||
SessionExpiry: app.config.SessionExpiry,
|
||||
SecureCookie: app.config.SecureCookie,
|
||||
CookieDomain: cookieDomain,
|
||||
LoginTimeout: app.config.LoginTimeout,
|
||||
LoginMaxRetries: app.config.LoginMaxRetries,
|
||||
SessionCookieName: sessionCookieName,
|
||||
}
|
||||
|
||||
// Setup services
|
||||
var ldapService *service.LdapService
|
||||
|
||||
if app.config.LdapAddress != "" {
|
||||
ldapConfig := service.LdapServiceConfig{
|
||||
Address: app.config.LdapAddress,
|
||||
BindDN: app.config.LdapBindDN,
|
||||
BindPassword: app.config.LdapBindPassword,
|
||||
BaseDN: app.config.LdapBaseDN,
|
||||
Insecure: app.config.LdapInsecure,
|
||||
SearchFilter: app.config.LdapSearchFilter,
|
||||
}
|
||||
|
||||
ldapService = service.NewLdapService(ldapConfig)
|
||||
|
||||
err := ldapService.Init()
|
||||
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("Failed to initialize LDAP service, continuing without LDAP")
|
||||
ldapService = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Bootstrap database
|
||||
databaseService := service.NewDatabaseService(service.DatabaseServiceConfig{
|
||||
DatabasePath: app.config.DatabasePath,
|
||||
})
|
||||
|
||||
log.Debug().Str("service", fmt.Sprintf("%T", databaseService)).Msg("Initializing service")
|
||||
|
||||
err = databaseService.Init()
|
||||
// Services
|
||||
services, err := app.initServices()
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initialize database service: %w", err)
|
||||
return fmt.Errorf("failed to initialize services: %w", err)
|
||||
}
|
||||
|
||||
database := databaseService.GetDatabase()
|
||||
|
||||
// Create services
|
||||
dockerService := service.NewDockerService()
|
||||
aclsService := service.NewAccessControlsService(dockerService)
|
||||
authService := service.NewAuthService(authConfig, dockerService, ldapService, database)
|
||||
oauthBrokerService := service.NewOAuthBrokerService(oauthProviders)
|
||||
|
||||
// Initialize services (order matters)
|
||||
services := []Service{
|
||||
dockerService,
|
||||
aclsService,
|
||||
authService,
|
||||
oauthBrokerService,
|
||||
}
|
||||
|
||||
for _, svc := range services {
|
||||
if svc != nil {
|
||||
log.Debug().Str("service", fmt.Sprintf("%T", svc)).Msg("Initializing service")
|
||||
err := svc.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
app.services = services
|
||||
|
||||
// Configured providers
|
||||
configuredProviders := make([]controller.Provider, 0)
|
||||
@@ -176,7 +110,7 @@ func (app *BootstrapApp) Setup() error {
|
||||
return configuredProviders[i].Name < configuredProviders[j].Name
|
||||
})
|
||||
|
||||
if authService.UserAuthConfigured() || ldapService != nil {
|
||||
if services.authService.UserAuthConfigured() {
|
||||
configuredProviders = append(configuredProviders, controller.Provider{
|
||||
Name: "Username",
|
||||
ID: "username",
|
||||
@@ -190,92 +124,16 @@ func (app *BootstrapApp) Setup() error {
|
||||
return fmt.Errorf("no authentication providers configured")
|
||||
}
|
||||
|
||||
// Create engine
|
||||
engine := gin.New()
|
||||
engine.Use(gin.Recovery())
|
||||
// Setup router
|
||||
engine, err := app.setupRouter()
|
||||
|
||||
if len(app.config.TrustedProxies) > 0 {
|
||||
err := engine.SetTrustedProxies(strings.Split(app.config.TrustedProxies, ","))
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to set trusted proxies: %w", err)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to setup routes: %w", err)
|
||||
}
|
||||
|
||||
// Create middlewares
|
||||
var middlewares []Middleware
|
||||
|
||||
contextMiddleware := middleware.NewContextMiddleware(middleware.ContextMiddlewareConfig{
|
||||
CookieDomain: cookieDomain,
|
||||
}, authService, oauthBrokerService)
|
||||
|
||||
uiMiddleware := middleware.NewUIMiddleware()
|
||||
zerologMiddleware := middleware.NewZerologMiddleware()
|
||||
|
||||
middlewares = append(middlewares, contextMiddleware, uiMiddleware, zerologMiddleware)
|
||||
|
||||
for _, middleware := range middlewares {
|
||||
log.Debug().Str("middleware", fmt.Sprintf("%T", middleware)).Msg("Initializing middleware")
|
||||
err := middleware.Init()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initialize middleware %T: %w", middleware, err)
|
||||
}
|
||||
engine.Use(middleware.Middleware())
|
||||
}
|
||||
|
||||
// Create routers
|
||||
mainRouter := engine.Group("")
|
||||
apiRouter := engine.Group("/api")
|
||||
|
||||
// Create controllers
|
||||
contextController := controller.NewContextController(controller.ContextControllerConfig{
|
||||
Providers: configuredProviders,
|
||||
Title: app.config.Title,
|
||||
AppURL: app.config.AppURL,
|
||||
CookieDomain: cookieDomain,
|
||||
ForgotPasswordMessage: app.config.ForgotPasswordMessage,
|
||||
BackgroundImage: app.config.BackgroundImage,
|
||||
OAuthAutoRedirect: app.config.OAuthAutoRedirect,
|
||||
DisableUIWarnings: app.config.DisableUIWarnings,
|
||||
}, apiRouter)
|
||||
|
||||
oauthController := controller.NewOAuthController(controller.OAuthControllerConfig{
|
||||
AppURL: app.config.AppURL,
|
||||
SecureCookie: app.config.SecureCookie,
|
||||
CSRFCookieName: csrfCookieName,
|
||||
RedirectCookieName: redirectCookieName,
|
||||
CookieDomain: cookieDomain,
|
||||
}, apiRouter, authService, oauthBrokerService)
|
||||
|
||||
proxyController := controller.NewProxyController(controller.ProxyControllerConfig{
|
||||
AppURL: app.config.AppURL,
|
||||
}, apiRouter, aclsService, authService)
|
||||
|
||||
userController := controller.NewUserController(controller.UserControllerConfig{
|
||||
CookieDomain: cookieDomain,
|
||||
}, apiRouter, authService)
|
||||
|
||||
resourcesController := controller.NewResourcesController(controller.ResourcesControllerConfig{
|
||||
ResourcesDir: app.config.ResourcesDir,
|
||||
ResourcesDisabled: app.config.DisableResources,
|
||||
}, mainRouter)
|
||||
|
||||
healthController := controller.NewHealthController(apiRouter)
|
||||
|
||||
// Setup routes
|
||||
controller := []Controller{
|
||||
contextController,
|
||||
oauthController,
|
||||
proxyController,
|
||||
userController,
|
||||
healthController,
|
||||
resourcesController,
|
||||
}
|
||||
|
||||
for _, ctrl := range controller {
|
||||
log.Debug().Msgf("Setting up %T controller", ctrl)
|
||||
ctrl.SetupRoutes()
|
||||
}
|
||||
// Start DB cleanup routine
|
||||
log.Debug().Msg("Starting database cleanup routine")
|
||||
go app.dbCleanup(services.databaseService.GetDatabase())
|
||||
|
||||
// If analytics are not disabled, start heartbeat
|
||||
if !app.config.DisableAnalytics {
|
||||
@@ -283,13 +141,8 @@ func (app *BootstrapApp) Setup() error {
|
||||
go app.heartbeat()
|
||||
}
|
||||
|
||||
// Start DB cleanup routine
|
||||
log.Debug().Msg("Starting database cleanup routine")
|
||||
go app.dbCleanup(database)
|
||||
|
||||
// If we have an socket path, bind to it
|
||||
if app.config.SocketPath != "" {
|
||||
// Remove existing socket file
|
||||
if _, err := os.Stat(app.config.SocketPath); err == nil {
|
||||
log.Info().Msgf("Removing existing socket file %s", app.config.SocketPath)
|
||||
err := os.Remove(app.config.SocketPath)
|
||||
@@ -298,7 +151,6 @@ func (app *BootstrapApp) Setup() error {
|
||||
}
|
||||
}
|
||||
|
||||
// Start server with unix socket
|
||||
log.Info().Msgf("Starting server on unix socket %s", app.config.SocketPath)
|
||||
if err := engine.RunUnix(app.config.SocketPath); err != nil {
|
||||
log.Fatal().Err(err).Msg("Failed to start server")
|
||||
@@ -328,7 +180,7 @@ func (app *BootstrapApp) heartbeat() {
|
||||
|
||||
var body heartbeat
|
||||
|
||||
body.UUID = app.uuid
|
||||
body.UUID = app.context.uuid
|
||||
body.Version = config.Version
|
||||
|
||||
bodyJson, err := json.Marshal(body)
|
||||
@@ -338,7 +190,9 @@ func (app *BootstrapApp) heartbeat() {
|
||||
return
|
||||
}
|
||||
|
||||
client := &http.Client{}
|
||||
client := &http.Client{
|
||||
Timeout: time.Duration(10) * time.Second, // The server should never take more than 10 seconds to respond
|
||||
}
|
||||
|
||||
heartbeatURL := config.ApiServer + "/v1/instances/heartbeat"
|
||||
|
||||
|
||||
105
internal/bootstrap/router_boostrap.go
Normal file
105
internal/bootstrap/router_boostrap.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"tinyauth/internal/controller"
|
||||
"tinyauth/internal/middleware"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func (app *BootstrapApp) setupRouter() (*gin.Engine, error) {
|
||||
engine := gin.New()
|
||||
engine.Use(gin.Recovery())
|
||||
|
||||
if len(app.config.TrustedProxies) > 0 {
|
||||
err := engine.SetTrustedProxies(strings.Split(app.config.TrustedProxies, ","))
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to set trusted proxies: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
contextMiddleware := middleware.NewContextMiddleware(middleware.ContextMiddlewareConfig{
|
||||
CookieDomain: app.context.cookieDomain,
|
||||
}, app.services.authService, app.services.oauthBrokerService)
|
||||
|
||||
err := contextMiddleware.Init()
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize context middleware: %w", err)
|
||||
}
|
||||
|
||||
engine.Use(contextMiddleware.Middleware())
|
||||
|
||||
uiMiddleware := middleware.NewUIMiddleware()
|
||||
|
||||
err = uiMiddleware.Init()
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize UI middleware: %w", err)
|
||||
}
|
||||
|
||||
engine.Use(uiMiddleware.Middleware())
|
||||
|
||||
zerologMiddleware := middleware.NewZerologMiddleware()
|
||||
|
||||
err = zerologMiddleware.Init()
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize zerolog middleware: %w", err)
|
||||
}
|
||||
|
||||
engine.Use(zerologMiddleware.Middleware())
|
||||
|
||||
apiRouter := engine.Group("/api")
|
||||
|
||||
contextController := controller.NewContextController(controller.ContextControllerConfig{
|
||||
Providers: app.context.configuredProviders,
|
||||
Title: app.config.Title,
|
||||
AppURL: app.config.AppURL,
|
||||
CookieDomain: app.context.cookieDomain,
|
||||
ForgotPasswordMessage: app.config.ForgotPasswordMessage,
|
||||
BackgroundImage: app.config.BackgroundImage,
|
||||
OAuthAutoRedirect: app.config.OAuthAutoRedirect,
|
||||
DisableUIWarnings: app.config.DisableUIWarnings,
|
||||
}, apiRouter)
|
||||
|
||||
contextController.SetupRoutes()
|
||||
|
||||
oauthController := controller.NewOAuthController(controller.OAuthControllerConfig{
|
||||
AppURL: app.config.AppURL,
|
||||
SecureCookie: app.config.SecureCookie,
|
||||
CSRFCookieName: app.context.csrfCookieName,
|
||||
RedirectCookieName: app.context.redirectCookieName,
|
||||
CookieDomain: app.context.cookieDomain,
|
||||
}, apiRouter, app.services.authService, app.services.oauthBrokerService)
|
||||
|
||||
oauthController.SetupRoutes()
|
||||
|
||||
proxyController := controller.NewProxyController(controller.ProxyControllerConfig{
|
||||
AppURL: app.config.AppURL,
|
||||
}, apiRouter, app.services.accessControlService, app.services.authService)
|
||||
|
||||
proxyController.SetupRoutes()
|
||||
|
||||
userController := controller.NewUserController(controller.UserControllerConfig{
|
||||
CookieDomain: app.context.cookieDomain,
|
||||
}, apiRouter, app.services.authService)
|
||||
|
||||
userController.SetupRoutes()
|
||||
|
||||
resourcesController := controller.NewResourcesController(controller.ResourcesControllerConfig{
|
||||
ResourcesDir: app.config.ResourcesDir,
|
||||
ResourcesDisabled: app.config.DisableResources,
|
||||
}, &engine.RouterGroup)
|
||||
|
||||
resourcesController.SetupRoutes()
|
||||
|
||||
healthController := controller.NewHealthController(apiRouter)
|
||||
|
||||
healthController.SetupRoutes()
|
||||
|
||||
return engine, nil
|
||||
}
|
||||
100
internal/bootstrap/service_bootstrap.go
Normal file
100
internal/bootstrap/service_bootstrap.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
"tinyauth/internal/service"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type Services struct {
|
||||
accessControlService *service.AccessControlsService
|
||||
authService *service.AuthService
|
||||
databaseService *service.DatabaseService
|
||||
dockerService *service.DockerService
|
||||
ldapService *service.LdapService
|
||||
oauthBrokerService *service.OAuthBrokerService
|
||||
}
|
||||
|
||||
func (app *BootstrapApp) initServices() (Services, error) {
|
||||
services := Services{}
|
||||
|
||||
databaseService := service.NewDatabaseService(service.DatabaseServiceConfig{
|
||||
DatabasePath: app.config.DatabasePath,
|
||||
})
|
||||
|
||||
err := databaseService.Init()
|
||||
|
||||
if err != nil {
|
||||
return Services{}, err
|
||||
}
|
||||
|
||||
services.databaseService = databaseService
|
||||
|
||||
ldapService := service.NewLdapService(service.LdapServiceConfig{
|
||||
Address: app.config.LdapAddress,
|
||||
BindDN: app.config.LdapBindDN,
|
||||
BindPassword: app.config.LdapBindPassword,
|
||||
BaseDN: app.config.LdapBaseDN,
|
||||
Insecure: app.config.LdapInsecure,
|
||||
SearchFilter: app.config.LdapSearchFilter,
|
||||
})
|
||||
|
||||
err = ldapService.Init()
|
||||
|
||||
if err == nil {
|
||||
services.ldapService = ldapService
|
||||
} else {
|
||||
log.Warn().Err(err).Msg("Failed to initialize LDAP service, continuing without it")
|
||||
}
|
||||
|
||||
dockerService := service.NewDockerService()
|
||||
|
||||
err = dockerService.Init()
|
||||
|
||||
if err != nil {
|
||||
return Services{}, err
|
||||
}
|
||||
|
||||
services.dockerService = dockerService
|
||||
|
||||
accessControlsService := service.NewAccessControlsService(dockerService)
|
||||
|
||||
err = accessControlsService.Init()
|
||||
|
||||
if err != nil {
|
||||
return Services{}, err
|
||||
}
|
||||
|
||||
services.accessControlService = accessControlsService
|
||||
|
||||
authService := service.NewAuthService(service.AuthServiceConfig{
|
||||
Users: app.context.users,
|
||||
OauthWhitelist: app.config.OAuthWhitelist,
|
||||
SessionExpiry: app.config.SessionExpiry,
|
||||
SecureCookie: app.config.SecureCookie,
|
||||
CookieDomain: app.context.cookieDomain,
|
||||
LoginTimeout: app.config.LoginTimeout,
|
||||
LoginMaxRetries: app.config.LoginMaxRetries,
|
||||
SessionCookieName: app.context.sessionCookieName,
|
||||
}, dockerService, ldapService, databaseService.GetDatabase())
|
||||
|
||||
err = authService.Init()
|
||||
|
||||
if err != nil {
|
||||
return Services{}, err
|
||||
}
|
||||
|
||||
services.authService = authService
|
||||
|
||||
oauthBrokerService := service.NewOAuthBrokerService(app.context.oauthProviders)
|
||||
|
||||
err = oauthBrokerService.Init()
|
||||
|
||||
if err != nil {
|
||||
return Services{}, err
|
||||
}
|
||||
|
||||
services.oauthBrokerService = oauthBrokerService
|
||||
|
||||
return services, nil
|
||||
}
|
||||
Reference in New Issue
Block a user