From 333b8545338141d0a8bce82cc34fcc991e0afca6 Mon Sep 17 00:00:00 2001 From: Stavros Date: Mon, 29 Dec 2025 22:44:23 +0200 Subject: [PATCH] wip --- internal/bootstrap/service_bootstrap.go | 2 +- internal/config/config.go | 53 ++++---- internal/service/access_controls_service.go | 128 +++++--------------- 3 files changed, 55 insertions(+), 128 deletions(-) diff --git a/internal/bootstrap/service_bootstrap.go b/internal/bootstrap/service_bootstrap.go index 6110aeb..e4abc8d 100644 --- a/internal/bootstrap/service_bootstrap.go +++ b/internal/bootstrap/service_bootstrap.go @@ -57,7 +57,7 @@ func (app *BootstrapApp) initServices() (Services, error) { services.dockerService = dockerService - accessControlsService := service.NewAccessControlsService(dockerService) + accessControlsService := service.NewAccessControlsService(dockerService, app.config.Apps) err = accessControlsService.Init() diff --git a/internal/config/config.go b/internal/config/config.go index f69c473..f892757 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -15,6 +15,7 @@ var RedirectCookieName = "tinyauth-redirect" // Main app config type Config struct { + Apps AppURL string `description:"The base URL where the app is hosted." yaml:"appUrl"` LogLevel string `description:"Log level (trace, debug, info, warn, error)." yaml:"logLevel"` ResourcesDir string `description:"The directory where resources are stored." yaml:"resourcesDir"` @@ -156,61 +157,55 @@ type RedirectQuery struct { RedirectURI string `url:"redirect_uri"` } -// Labels +// ACLs type Apps struct { - Apps map[string]App + Apps map[string]App `description:"App ACLs configuration." yaml:"apps"` } type App struct { - Config AppConfig - Users AppUsers - OAuth AppOAuth - IP AppIP - Response AppResponse - Path AppPath + Config AppConfig `description:"App configuration." yaml:"config"` + Users AppUsers `description:"User access configuration." yaml:"users"` + OAuth AppOAuth `description:"OAuth access configuration." yaml:"oauth"` + IP AppIP `description:"IP access configuration." yaml:"ip"` + Response AppResponse `description:"Response customization." yaml:"response"` + Path AppPath `description:"Path access configuration." yaml:"path"` } type AppConfig struct { - Domain string + Domain string `description:"The domain of the app." yaml:"domain"` } type AppUsers struct { - Allow string - Block string + Allow string `description:"Comma-separated list of allowed users." yaml:"allow"` + Block string `description:"Comma-separated list of blocked users." yaml:"block"` } type AppOAuth struct { - Whitelist string - Groups string + Whitelist string `description:"Comma-separated list of allowed OAuth groups." yaml:"whitelist"` + Groups string `description:"Comma-separated list of required OAuth groups." yaml:"groups"` } type AppIP struct { - Allow []string - Block []string - Bypass []string + Allow []string `description:"List of allowed IPs or CIDR ranges." yaml:"allow"` + Block []string `description:"List of blocked IPs or CIDR ranges." yaml:"block"` + Bypass []string `description:"List of IPs or CIDR ranges that bypass authentication." yaml:"bypass"` } type AppResponse struct { - Headers []string - BasicAuth AppBasicAuth + Headers []string `description:"Custom headers to add to the response." yaml:"headers"` + BasicAuth AppBasicAuth `description:"Basic authentication for the app." yaml:"basicAuth"` } type AppBasicAuth struct { - Username string - Password string - PasswordFile string + Username string `description:"Basic auth username." yaml:"username"` + Password string `description:"Basic auth password." yaml:"password"` + PasswordFile string `description:"Path to the file containing the basic auth password." yaml:"passwordFile"` } type AppPath struct { - Allow string - Block string -} - -// Flags - -type Providers struct { - Providers map[string]OAuthServiceConfig + Allow string `description:"Comma-separated list of allowed paths." yaml:"allow"` + Block string `description:"Comma-separated list of blocked paths." yaml:"block"` } // API server diff --git a/internal/service/access_controls_service.go b/internal/service/access_controls_service.go index e5af779..b9c3dad 100644 --- a/internal/service/access_controls_service.go +++ b/internal/service/access_controls_service.go @@ -1,122 +1,54 @@ package service import ( + "errors" + "strings" + + "github.com/rs/zerolog/log" "github.com/steveiliop56/tinyauth/internal/config" ) -/* -Environment variable/flag based ACLs are disabled until v5 due to a technical challenge -with the current parsing logic. - -The current parser works for simple OAuth provider configs like: -- PROVIDERS_MY_AMAZING_PROVIDER_CLIENT_ID - -However, it breaks down when handling nested structs required for ACLs. The custom parsing -solution that worked for v4 OAuth providers is incompatible with the ACL parsing logic, -making the codebase unmaintainable and fragile. - -A solution is being considered for v5 that would standardize the format to something like: -- TINYAUTH_PROVIDERS_GOOGLE_CLIENTSECRET -- TINYAUTH_APPS_MYAPP_CONFIG_DOMAIN - -This would allow the Traefik parser to handle everything consistently, but requires a -config migration. Until this is resolved, environment-based ACLs are disabled and only -Docker label-based ACLs are supported. - -See: https://discord.com/channels/1337450123600465984/1337459086270271538/1434986689935179838 for more information -*/ - type AccessControlsService struct { docker *DockerService - // envACLs config.Apps + config config.Apps } -func NewAccessControlsService(docker *DockerService) *AccessControlsService { +func NewAccessControlsService(docker *DockerService, config config.Apps) *AccessControlsService { return &AccessControlsService{ docker: docker, + config: config, } } func (acls *AccessControlsService) Init() error { - // acls.envACLs = config.Apps{} - // env := os.Environ() - // appEnvVars := []string{} - - // for _, e := range env { - // if strings.HasPrefix(e, "TINYAUTH_APPS_") { - // appEnvVars = append(appEnvVars, e) - // } - // } - - // err := acls.loadEnvACLs(appEnvVars) - - // if err != nil { - // return err - // } - - // return nil - - return nil - + return nil // No initialization needed } -// func (acls *AccessControlsService) loadEnvACLs(appEnvVars []string) error { -// if len(appEnvVars) == 0 { -// return nil -// } +func (acls *AccessControlsService) lookupConfigACLs(domain string) (config.App, error) { + for app, config := range acls.config.Apps { + if config.Config.Domain == domain { + log.Debug().Str("name", app).Msg("Found matching container by domain") + return config, nil + } -// envAcls := map[string]string{} + if strings.SplitN(domain, ".", 2)[0] == app { + log.Debug().Str("name", app).Msg("Found matching container by app name") + return config, nil + } + } + return config.App{}, errors.New("no results") +} -// for _, e := range appEnvVars { -// parts := strings.SplitN(e, "=", 2) -// if len(parts) != 2 { -// continue -// } +func (acls *AccessControlsService) GetAccessControls(domain string) (config.App, error) { + // First check in the static config + app, err := acls.lookupConfigACLs(domain) -// key := parts[0] -// key = strings.ToLower(key) -// key = strings.ReplaceAll(key, "_", ".") -// value := parts[1] -// envAcls[key] = value -// } - -// apps, err := decoders.DecodeLabels(envAcls) - -// if err != nil { -// return err -// } - -// acls.envACLs = apps -// return nil -// } - -// func (acls *AccessControlsService) lookupEnvACLs(appDomain string) *config.App { -// if len(acls.envACLs.Apps) == 0 { -// return nil -// } - -// for appName, appACLs := range acls.envACLs.Apps { -// if appACLs.Config.Domain == appDomain { -// return &appACLs -// } - -// if strings.SplitN(appDomain, ".", 2)[0] == appName { -// return &appACLs -// } -// } - -// return nil -// } - -func (acls *AccessControlsService) GetAccessControls(appDomain string) (config.App, error) { - // First check environment variables - // envACLs := acls.lookupEnvACLs(appDomain) - - // if envACLs != nil { - // log.Debug().Str("domain", appDomain).Msg("Found matching access controls in environment variables") - // return *envACLs, nil - // } + if err == nil { + log.Debug().Msg("Using ACls from static configuration") + return app, nil + } // Fallback to Docker labels - return acls.docker.GetLabels(appDomain) + log.Debug().Msg("Falling back to Docker labels for ACLs") + return acls.docker.GetLabels(domain) }