mirror of
https://github.com/steveiliop56/tinyauth.git
synced 2025-10-27 20:25:41 +00:00
* feat: add flag decoder (candidate) * refactor: finalize flags decoder * feat: add env decoder * feat: add oauth config parsing logic * feat: implement backend logic for multiple oauth providers * feat: implement multiple oauth providers in the frontend * feat: add some default icons * chore: add credits for parser * feat: style oauth auto redirect screen * fix: bot suggestions * refactor: rework decoders using simpler and more efficient pattern * refactor: rework oauth name database migration
273 lines
6.9 KiB
Go
273 lines
6.9 KiB
Go
package bootstrap
|
|
|
|
import (
|
|
"fmt"
|
|
"net/url"
|
|
"os"
|
|
"strings"
|
|
"tinyauth/internal/config"
|
|
"tinyauth/internal/controller"
|
|
"tinyauth/internal/middleware"
|
|
"tinyauth/internal/service"
|
|
"tinyauth/internal/utils"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
type Controller interface {
|
|
SetupRoutes()
|
|
}
|
|
|
|
type Middleware interface {
|
|
Middleware() gin.HandlerFunc
|
|
Init() error
|
|
}
|
|
|
|
type Service interface {
|
|
Init() error
|
|
}
|
|
|
|
type BootstrapApp struct {
|
|
Config config.Config
|
|
}
|
|
|
|
func NewBootstrapApp(config config.Config) *BootstrapApp {
|
|
return &BootstrapApp{
|
|
Config: config,
|
|
}
|
|
}
|
|
|
|
func (app *BootstrapApp) Setup() error {
|
|
// Parse users
|
|
users, err := utils.GetUsers(app.Config.Users, app.Config.UsersFile)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Get OAuth configs
|
|
oauthProviders, err := utils.GetOAuthProvidersConfig(os.Environ(), os.Args, app.Config.AppURL)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Get cookie domain
|
|
cookieDomain, err := utils.GetCookieDomain(app.Config.AppURL)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Cookie names
|
|
appUrl, _ := url.Parse(app.Config.AppURL) // Already validated
|
|
cookieId := utils.GenerateIdentifier(appUrl.Hostname())
|
|
sessionCookieName := fmt.Sprintf("%s-%s", config.SessionCookieName, cookieId)
|
|
csrfCookieName := fmt.Sprintf("%s-%s", config.CSRFCookieName, cookieId)
|
|
redirectCookieName := fmt.Sprintf("%s-%s", config.RedirectCookieName, cookieId)
|
|
|
|
// 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()
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("failed to initialize database service: %w", err)
|
|
}
|
|
|
|
database := databaseService.GetDatabase()
|
|
|
|
// Create services
|
|
dockerService := service.NewDockerService()
|
|
authService := service.NewAuthService(authConfig, dockerService, ldapService, database)
|
|
oauthBrokerService := service.NewOAuthBrokerService(oauthProviders)
|
|
|
|
// Initialize services
|
|
services := []Service{
|
|
dockerService,
|
|
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
|
|
}
|
|
}
|
|
}
|
|
|
|
// Configured providers
|
|
babysit := map[string]string{
|
|
"google": "Google",
|
|
"github": "GitHub",
|
|
}
|
|
configuredProviders := make([]controller.Provider, 0)
|
|
|
|
for id, provider := range oauthProviders {
|
|
if id == "" {
|
|
continue
|
|
}
|
|
|
|
if provider.Name == "" {
|
|
if name, ok := babysit[id]; ok {
|
|
provider.Name = name
|
|
} else {
|
|
provider.Name = utils.Capitalize(id)
|
|
}
|
|
}
|
|
|
|
configuredProviders = append(configuredProviders, controller.Provider{
|
|
Name: provider.Name,
|
|
ID: id,
|
|
OAuth: true,
|
|
})
|
|
}
|
|
|
|
if authService.UserAuthConfigured() || ldapService != nil {
|
|
configuredProviders = append(configuredProviders, controller.Provider{
|
|
Name: "Username",
|
|
ID: "username",
|
|
OAuth: false,
|
|
})
|
|
}
|
|
|
|
log.Debug().Interface("providers", configuredProviders).Msg("Authentication providers")
|
|
|
|
if len(configuredProviders) == 0 {
|
|
return fmt.Errorf("no authentication providers configured")
|
|
}
|
|
|
|
// Create engine
|
|
engine := gin.New()
|
|
engine.SetTrustedProxies(strings.Split(app.Config.TrustedProxies, ","))
|
|
|
|
if config.Version != "development" {
|
|
gin.SetMode(gin.ReleaseMode)
|
|
}
|
|
|
|
// 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,
|
|
}, 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, dockerService, authService)
|
|
|
|
userController := controller.NewUserController(controller.UserControllerConfig{
|
|
CookieDomain: cookieDomain,
|
|
}, apiRouter, authService)
|
|
|
|
resourcesController := controller.NewResourcesController(controller.ResourcesControllerConfig{
|
|
ResourcesDir: app.Config.ResourcesDir,
|
|
}, 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 server
|
|
address := fmt.Sprintf("%s:%d", app.Config.Address, app.Config.Port)
|
|
log.Info().Msgf("Starting server on %s", address)
|
|
if err := engine.Run(address); err != nil {
|
|
log.Fatal().Err(err).Msg("Failed to start server")
|
|
}
|
|
|
|
return nil
|
|
}
|