From 3fadf35ff5787944d05219355603695b7a2a20b8 Mon Sep 17 00:00:00 2001 From: Chris Ellrich Date: Sun, 19 Oct 2025 18:40:08 +0200 Subject: [PATCH 1/5] feat: add LabelService to retrieve application labels from environment variables --- internal/bootstrap/app_bootstrap.go | 4 +- internal/controller/proxy_controller.go | 9 ++- internal/service/label_service.go | 89 +++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 internal/service/label_service.go diff --git a/internal/bootstrap/app_bootstrap.go b/internal/bootstrap/app_bootstrap.go index 2cb7e979..d6173f4b 100644 --- a/internal/bootstrap/app_bootstrap.go +++ b/internal/bootstrap/app_bootstrap.go @@ -136,12 +136,14 @@ func (app *BootstrapApp) Setup() error { // Create services dockerService := service.NewDockerService() + labelService := service.NewLabelService() authService := service.NewAuthService(authConfig, dockerService, ldapService, database) oauthBrokerService := service.NewOAuthBrokerService(oauthProviders) // Initialize services services := []Service{ dockerService, + labelService, authService, oauthBrokerService, } @@ -243,7 +245,7 @@ func (app *BootstrapApp) Setup() error { proxyController := controller.NewProxyController(controller.ProxyControllerConfig{ AppURL: app.config.AppURL, - }, apiRouter, dockerService, authService) + }, apiRouter, dockerService, labelService, authService) userController := controller.NewUserController(controller.UserControllerConfig{ CookieDomain: cookieDomain, diff --git a/internal/controller/proxy_controller.go b/internal/controller/proxy_controller.go index 8ded9dcd..8d0945a3 100644 --- a/internal/controller/proxy_controller.go +++ b/internal/controller/proxy_controller.go @@ -25,14 +25,16 @@ type ProxyController struct { config ProxyControllerConfig router *gin.RouterGroup docker *service.DockerService + label *service.LabelService auth *service.AuthService } -func NewProxyController(config ProxyControllerConfig, router *gin.RouterGroup, docker *service.DockerService, auth *service.AuthService) *ProxyController { +func NewProxyController(config ProxyControllerConfig, router *gin.RouterGroup, docker *service.DockerService, label *service.LabelService, auth *service.AuthService) *ProxyController { return &ProxyController{ config: config, router: router, docker: docker, + label: label, auth: auth, } } @@ -76,7 +78,10 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) { proto := c.Request.Header.Get("X-Forwarded-Proto") host := c.Request.Header.Get("X-Forwarded-Host") - labels, err := controller.docker.GetLabels(host) + // Ignore previous docker method for now + // TODO: Combine both methods if needed + // labels, err := controller.docker.GetLabels(host) + labels, err := controller.label.GetLabels(host) if err != nil { log.Error().Err(err).Msg("Failed to get labels from Docker") diff --git a/internal/service/label_service.go b/internal/service/label_service.go new file mode 100644 index 00000000..78e5edc5 --- /dev/null +++ b/internal/service/label_service.go @@ -0,0 +1,89 @@ +package service + +import ( + "os" + "strings" + "tinyauth/internal/config" + "tinyauth/internal/utils/decoders" + + "github.com/rs/zerolog/log" + "golang.org/x/exp/slices" +) + +type LabelService struct { + labelsFoundInEnv bool + labels config.Apps +} + +func NewLabelService() *LabelService { + return &LabelService{} +} + +func (label *LabelService) Init() error { + envVars := os.Environ() + // Check if any TINYAUTH_APPS_ environment variables exist + if slices.ContainsFunc(envVars, func(s string) bool { return strings.HasPrefix(s, "TINYAUTH_APPS_") }) { + log.Debug().Msg("TINYAUTH_APPS_ environment variables found, initializing LabelService") + label.LoadLabels(envVars) + return nil + } else { + log.Debug().Msg("No TINYAUTH_APPS_ environment variables found") + label.labelsFoundInEnv = false + return nil + } +} + +func (label *LabelService) LoadLabels(envVars []string) error { + // Load environment variables and map them to label format + labelsFromEnv := make(map[string]string) + for _, e := range envVars { + if strings.HasPrefix(e, "TINYAUTH_APPS_") { + parts := strings.SplitN(e, "=", 2) + if len(parts) == 2 { + key := parts[0] + + // Convert to label format + // e.g. TINYAUTH_APPS_[APP]_CONFIG_DOMAIN → tinyauth.apps.[app].config.domain + key = strings.ToLower(key) + key = strings.ReplaceAll(key, "_", ".") + + value := parts[1] + labelsFromEnv[key] = value + } + } + } + + // Decode converted labels using some decoder package as Docker labels + labels, err := decoders.DecodeLabels(labelsFromEnv) + if err != nil { + return err + } + + log.Debug().Msg("Labels loaded from environment variables successfully") + label.labels = labels + label.labelsFoundInEnv = true + return nil +} + +func (label *LabelService) GetLabels(appDomain string) (config.App, error) { + // If no labels found in env, return empty labels + if !label.labelsFoundInEnv { + log.Debug().Msg("No labels found in environment, returning empty labels") + return config.App{}, nil + } + // Find matching app by domain or app name + for appName, appLabels := range label.labels.Apps { + if appLabels.Config.Domain == appDomain { + log.Debug().Str("name", appName).Msg("Found matching label by domain") + return appLabels, nil + } + + if strings.SplitN(appDomain, ".", 2)[0] == appName { + log.Debug().Str("name", appName).Msg("Found matching label by app name") + return appLabels, nil + } + } + + log.Debug().Msg("No matching env found, returning empty labels") + return config.App{}, nil +} From e7075ab8e642b4689baae8b080d3df36d0c8b0c8 Mon Sep 17 00:00:00 2001 From: Chris Ellrich Date: Sun, 19 Oct 2025 19:31:48 +0200 Subject: [PATCH 2/5] feat: allow usage of labels from docker and env variables simultaneously Prioritize labels from environment variables over labels from docker labels --- internal/controller/proxy_controller.go | 28 +++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/internal/controller/proxy_controller.go b/internal/controller/proxy_controller.go index 8d0945a3..67643a02 100644 --- a/internal/controller/proxy_controller.go +++ b/internal/controller/proxy_controller.go @@ -3,6 +3,7 @@ package controller import ( "fmt" "net/http" + "reflect" "strings" "tinyauth/internal/config" "tinyauth/internal/service" @@ -78,10 +79,17 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) { proto := c.Request.Header.Get("X-Forwarded-Proto") host := c.Request.Header.Get("X-Forwarded-Host") - // Ignore previous docker method for now - // TODO: Combine both methods if needed - // labels, err := controller.docker.GetLabels(host) - labels, err := controller.label.GetLabels(host) + // Get labels from environment variables + envLabels, err := controller.label.GetLabels(host) + + if err != nil { + log.Error().Err(err).Msg("Failed to get labels from environment variables") + controller.handleError(c, req, isBrowser) + return + } + + // Get labels from Docker + dockerLabels, err := controller.docker.GetLabels(host) if err != nil { log.Error().Err(err).Msg("Failed to get labels from Docker") @@ -89,6 +97,18 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) { return } + // Determine which labels to use, prioritizing environment variables + var labels config.App + if !reflect.DeepEqual(envLabels, config.App{}) { + log.Debug().Msg("Using labels from environment variables") + labels = envLabels + } else if !reflect.DeepEqual(dockerLabels, config.App{}) { + log.Debug().Msg("Using labels from Docker") + labels = dockerLabels + } else { + log.Debug().Msg("No labels found for resource") + labels = config.App{} + } log.Trace().Interface("labels", labels).Msg("Labels for resource") clientIP := c.ClientIP() From f7a11828ebe5b2516d73f49de5dabc6290f52b61 Mon Sep 17 00:00:00 2001 From: Chris Ellrich Date: Sun, 19 Oct 2025 21:10:48 +0200 Subject: [PATCH 3/5] fix: handle error returned by label_serive.go/LoadLabels see https://github.com/steveiliop56/tinyauth/pull/422#discussion_r2443443032 --- internal/service/label_service.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/internal/service/label_service.go b/internal/service/label_service.go index 78e5edc5..38975aee 100644 --- a/internal/service/label_service.go +++ b/internal/service/label_service.go @@ -24,13 +24,11 @@ func (label *LabelService) Init() error { // Check if any TINYAUTH_APPS_ environment variables exist if slices.ContainsFunc(envVars, func(s string) bool { return strings.HasPrefix(s, "TINYAUTH_APPS_") }) { log.Debug().Msg("TINYAUTH_APPS_ environment variables found, initializing LabelService") - label.LoadLabels(envVars) - return nil - } else { - log.Debug().Msg("No TINYAUTH_APPS_ environment variables found") - label.labelsFoundInEnv = false - return nil + return label.LoadLabels(envVars) } + log.Debug().Msg("No TINYAUTH_APPS_ environment variables found") + label.labelsFoundInEnv = false + return nil } func (label *LabelService) LoadLabels(envVars []string) error { From c5ab1bb1496d92458cc18ef4558060d6c94347e7 Mon Sep 17 00:00:00 2001 From: Chris Ellrich Date: Sun, 19 Oct 2025 21:22:22 +0200 Subject: [PATCH 4/5] refactor(label_service): use simple loop instead of slices.ContainsFunc to avoid experimental slices package see https://github.com/steveiliop56/tinyauth/pull/422#pullrequestreview-3354632045 --- internal/service/label_service.go | 33 ++++++++++++++++++------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/internal/service/label_service.go b/internal/service/label_service.go index 38975aee..852d7c2b 100644 --- a/internal/service/label_service.go +++ b/internal/service/label_service.go @@ -7,7 +7,6 @@ import ( "tinyauth/internal/utils/decoders" "github.com/rs/zerolog/log" - "golang.org/x/exp/slices" ) type LabelService struct { @@ -22,9 +21,17 @@ func NewLabelService() *LabelService { func (label *LabelService) Init() error { envVars := os.Environ() // Check if any TINYAUTH_APPS_ environment variables exist - if slices.ContainsFunc(envVars, func(s string) bool { return strings.HasPrefix(s, "TINYAUTH_APPS_") }) { + var tinyauthAppEnvVars []string + for _, e := range envVars { + if strings.HasPrefix(e, "TINYAUTH_APPS_") { + tinyauthAppEnvVars = append(tinyauthAppEnvVars, e) + } + } + + // If found, load labels from environment variables + if len(tinyauthAppEnvVars) > 0 { log.Debug().Msg("TINYAUTH_APPS_ environment variables found, initializing LabelService") - return label.LoadLabels(envVars) + return label.LoadLabels(tinyauthAppEnvVars) } log.Debug().Msg("No TINYAUTH_APPS_ environment variables found") label.labelsFoundInEnv = false @@ -35,19 +42,17 @@ func (label *LabelService) LoadLabels(envVars []string) error { // Load environment variables and map them to label format labelsFromEnv := make(map[string]string) for _, e := range envVars { - if strings.HasPrefix(e, "TINYAUTH_APPS_") { - parts := strings.SplitN(e, "=", 2) - if len(parts) == 2 { - key := parts[0] + parts := strings.SplitN(e, "=", 2) + if len(parts) == 2 { + key := parts[0] - // Convert to label format - // e.g. TINYAUTH_APPS_[APP]_CONFIG_DOMAIN → tinyauth.apps.[app].config.domain - key = strings.ToLower(key) - key = strings.ReplaceAll(key, "_", ".") + // Convert to label format + // e.g. TINYAUTH_APPS_[APP]_CONFIG_DOMAIN → tinyauth.apps.[app].config.domain + key = strings.ToLower(key) + key = strings.ReplaceAll(key, "_", ".") - value := parts[1] - labelsFromEnv[key] = value - } + value := parts[1] + labelsFromEnv[key] = value } } From 3e87831b7521464f8337479506190c8580bb7763 Mon Sep 17 00:00:00 2001 From: Stavros Date: Tue, 21 Oct 2025 15:47:31 +0300 Subject: [PATCH 5/5] refactor: merge acl logic into one service --- internal/bootstrap/app_bootstrap.go | 8 +- internal/controller/proxy_controller.go | 66 ++++-------- internal/controller/proxy_controller_test.go | 7 +- internal/service/access_controls_service.go | 103 +++++++++++++++++++ internal/service/auth_service.go | 22 ++-- internal/service/label_service.go | 92 ----------------- 6 files changed, 145 insertions(+), 153 deletions(-) create mode 100644 internal/service/access_controls_service.go delete mode 100644 internal/service/label_service.go diff --git a/internal/bootstrap/app_bootstrap.go b/internal/bootstrap/app_bootstrap.go index d6173f4b..2f7687a2 100644 --- a/internal/bootstrap/app_bootstrap.go +++ b/internal/bootstrap/app_bootstrap.go @@ -136,14 +136,14 @@ func (app *BootstrapApp) Setup() error { // Create services dockerService := service.NewDockerService() - labelService := service.NewLabelService() + aclsService := service.NewAccessControlsService(dockerService) authService := service.NewAuthService(authConfig, dockerService, ldapService, database) oauthBrokerService := service.NewOAuthBrokerService(oauthProviders) - // Initialize services + // Initialize services (order matters) services := []Service{ dockerService, - labelService, + aclsService, authService, oauthBrokerService, } @@ -245,7 +245,7 @@ func (app *BootstrapApp) Setup() error { proxyController := controller.NewProxyController(controller.ProxyControllerConfig{ AppURL: app.config.AppURL, - }, apiRouter, dockerService, labelService, authService) + }, apiRouter, aclsService, authService) userController := controller.NewUserController(controller.UserControllerConfig{ CookieDomain: cookieDomain, diff --git a/internal/controller/proxy_controller.go b/internal/controller/proxy_controller.go index 67643a02..2b6738ac 100644 --- a/internal/controller/proxy_controller.go +++ b/internal/controller/proxy_controller.go @@ -3,7 +3,6 @@ package controller import ( "fmt" "net/http" - "reflect" "strings" "tinyauth/internal/config" "tinyauth/internal/service" @@ -25,17 +24,15 @@ type ProxyControllerConfig struct { type ProxyController struct { config ProxyControllerConfig router *gin.RouterGroup - docker *service.DockerService - label *service.LabelService + acls *service.AccessControlsService auth *service.AuthService } -func NewProxyController(config ProxyControllerConfig, router *gin.RouterGroup, docker *service.DockerService, label *service.LabelService, auth *service.AuthService) *ProxyController { +func NewProxyController(config ProxyControllerConfig, router *gin.RouterGroup, acls *service.AccessControlsService, auth *service.AuthService) *ProxyController { return &ProxyController{ config: config, router: router, - docker: docker, - label: label, + acls: acls, auth: auth, } } @@ -79,42 +76,21 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) { proto := c.Request.Header.Get("X-Forwarded-Proto") host := c.Request.Header.Get("X-Forwarded-Host") - // Get labels from environment variables - envLabels, err := controller.label.GetLabels(host) + // Get acls + acls, err := controller.acls.GetAccessControls(host) if err != nil { - log.Error().Err(err).Msg("Failed to get labels from environment variables") + log.Error().Err(err).Msg("Failed to get access controls for resource") controller.handleError(c, req, isBrowser) return } - // Get labels from Docker - dockerLabels, err := controller.docker.GetLabels(host) - - if err != nil { - log.Error().Err(err).Msg("Failed to get labels from Docker") - controller.handleError(c, req, isBrowser) - return - } - - // Determine which labels to use, prioritizing environment variables - var labels config.App - if !reflect.DeepEqual(envLabels, config.App{}) { - log.Debug().Msg("Using labels from environment variables") - labels = envLabels - } else if !reflect.DeepEqual(dockerLabels, config.App{}) { - log.Debug().Msg("Using labels from Docker") - labels = dockerLabels - } else { - log.Debug().Msg("No labels found for resource") - labels = config.App{} - } - log.Trace().Interface("labels", labels).Msg("Labels for resource") + log.Trace().Interface("acls", acls).Msg("ACLs for resource") clientIP := c.ClientIP() - if controller.auth.IsBypassedIP(labels.IP, clientIP) { - controller.setHeaders(c, labels) + if controller.auth.IsBypassedIP(acls.IP, clientIP) { + controller.setHeaders(c, acls) c.JSON(200, gin.H{ "status": 200, "message": "Authenticated", @@ -122,7 +98,7 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) { return } - authEnabled, err := controller.auth.IsAuthEnabled(uri, labels.Path) + authEnabled, err := controller.auth.IsAuthEnabled(uri, acls.Path) if err != nil { log.Error().Err(err).Msg("Failed to check if auth is enabled for resource") @@ -132,7 +108,7 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) { if !authEnabled { log.Debug().Msg("Authentication disabled for resource, allowing access") - controller.setHeaders(c, labels) + controller.setHeaders(c, acls) c.JSON(200, gin.H{ "status": 200, "message": "Authenticated", @@ -140,7 +116,7 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) { return } - if !controller.auth.CheckIP(labels.IP, clientIP) { + if !controller.auth.CheckIP(acls.IP, clientIP) { if req.Proxy == "nginx" || !isBrowser { c.JSON(401, gin.H{ "status": 401, @@ -185,7 +161,7 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) { } if userContext.IsLoggedIn { - appAllowed := controller.auth.IsResourceAllowed(c, userContext, labels) + appAllowed := controller.auth.IsResourceAllowed(c, userContext, acls) if !appAllowed { log.Warn().Str("user", userContext.Username).Str("resource", strings.Split(host, ".")[0]).Msg("User not allowed to access resource") @@ -219,7 +195,7 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) { } if userContext.OAuth { - groupOK := controller.auth.IsInOAuthGroup(c, userContext, labels.OAuth.Groups) + groupOK := controller.auth.IsInOAuthGroup(c, userContext, acls.OAuth.Groups) if !groupOK { log.Warn().Str("user", userContext.Username).Str("resource", strings.Split(host, ".")[0]).Msg("User OAuth groups do not match resource requirements") @@ -259,7 +235,7 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) { c.Header("Remote-Email", utils.SanitizeHeader(userContext.Email)) c.Header("Remote-Groups", utils.SanitizeHeader(userContext.OAuthGroups)) - controller.setHeaders(c, labels) + controller.setHeaders(c, acls) c.JSON(200, gin.H{ "status": 200, @@ -289,21 +265,21 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) { c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/login?%s", controller.config.AppURL, queries.Encode())) } -func (controller *ProxyController) setHeaders(c *gin.Context, labels config.App) { +func (controller *ProxyController) setHeaders(c *gin.Context, acls config.App) { c.Header("Authorization", c.Request.Header.Get("Authorization")) - headers := utils.ParseHeaders(labels.Response.Headers) + headers := utils.ParseHeaders(acls.Response.Headers) for key, value := range headers { log.Debug().Str("header", key).Msg("Setting header") c.Header(key, value) } - basicPassword := utils.GetSecret(labels.Response.BasicAuth.Password, labels.Response.BasicAuth.PasswordFile) + basicPassword := utils.GetSecret(acls.Response.BasicAuth.Password, acls.Response.BasicAuth.PasswordFile) - if labels.Response.BasicAuth.Username != "" && basicPassword != "" { - log.Debug().Str("username", labels.Response.BasicAuth.Username).Msg("Setting basic auth header") - c.Header("Authorization", fmt.Sprintf("Basic %s", utils.GetBasicAuth(labels.Response.BasicAuth.Username, basicPassword))) + if acls.Response.BasicAuth.Username != "" && basicPassword != "" { + log.Debug().Str("username", acls.Response.BasicAuth.Username).Msg("Setting basic auth header") + c.Header("Authorization", fmt.Sprintf("Basic %s", utils.GetBasicAuth(acls.Response.BasicAuth.Username, basicPassword))) } } diff --git a/internal/controller/proxy_controller_test.go b/internal/controller/proxy_controller_test.go index fce2ec38..e7e27cf0 100644 --- a/internal/controller/proxy_controller_test.go +++ b/internal/controller/proxy_controller_test.go @@ -39,6 +39,11 @@ func setupProxyController(t *testing.T, middlewares *[]gin.HandlerFunc) (*gin.En assert.NilError(t, dockerService.Init()) + // Access controls + accessControlsService := service.NewAccessControlsService(dockerService) + + assert.NilError(t, accessControlsService.Init()) + // Auth service authService := service.NewAuthService(service.AuthServiceConfig{ Users: []config.User{ @@ -59,7 +64,7 @@ func setupProxyController(t *testing.T, middlewares *[]gin.HandlerFunc) (*gin.En // Controller ctrl := controller.NewProxyController(controller.ProxyControllerConfig{ AppURL: "http://localhost:8080", - }, group, dockerService, authService) + }, group, accessControlsService, authService) ctrl.SetupRoutes() return router, recorder, authService diff --git a/internal/service/access_controls_service.go b/internal/service/access_controls_service.go new file mode 100644 index 00000000..cde27e50 --- /dev/null +++ b/internal/service/access_controls_service.go @@ -0,0 +1,103 @@ +package service + +import ( + "os" + "strings" + "tinyauth/internal/config" + "tinyauth/internal/utils/decoders" + + "github.com/rs/zerolog/log" +) + +type AccessControlsService struct { + docker *DockerService + envACLs config.Apps +} + +func NewAccessControlsService(docker *DockerService) *AccessControlsService { + return &AccessControlsService{ + docker: docker, + } +} + +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 +} + +func (acls *AccessControlsService) loadEnvACLs(appEnvVars []string) error { + if len(appEnvVars) == 0 { + return nil + } + + envAcls := map[string]string{} + + for _, e := range appEnvVars { + parts := strings.SplitN(e, "=", 2) + if len(parts) != 2 { + continue + } + + // Normalize key, this should use the same normalization logic as in utils/decoders/decoders.go + 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 + } + + // Fallback to Docker labels + return acls.docker.GetLabels(appDomain) +} diff --git a/internal/service/auth_service.go b/internal/service/auth_service.go index d9f792b0..25caefb5 100644 --- a/internal/service/auth_service.go +++ b/internal/service/auth_service.go @@ -287,21 +287,21 @@ func (auth *AuthService) UserAuthConfigured() bool { return len(auth.config.Users) > 0 || auth.ldap != nil } -func (auth *AuthService) IsResourceAllowed(c *gin.Context, context config.UserContext, labels config.App) bool { +func (auth *AuthService) IsResourceAllowed(c *gin.Context, context config.UserContext, acls config.App) bool { if context.OAuth { log.Debug().Msg("Checking OAuth whitelist") - return utils.CheckFilter(labels.OAuth.Whitelist, context.Email) + return utils.CheckFilter(acls.OAuth.Whitelist, context.Email) } - if labels.Users.Block != "" { + if acls.Users.Block != "" { log.Debug().Msg("Checking blocked users") - if utils.CheckFilter(labels.Users.Block, context.Username) { + if utils.CheckFilter(acls.Users.Block, context.Username) { return false } } log.Debug().Msg("Checking users") - return utils.CheckFilter(labels.Users.Allow, context.Username) + return utils.CheckFilter(acls.Users.Allow, context.Username) } func (auth *AuthService) IsInOAuthGroup(c *gin.Context, context config.UserContext, requiredGroups string) bool { @@ -369,8 +369,8 @@ func (auth *AuthService) GetBasicAuth(c *gin.Context) *config.User { } } -func (auth *AuthService) CheckIP(labels config.AppIP, ip string) bool { - for _, blocked := range labels.Block { +func (auth *AuthService) CheckIP(acls config.AppIP, ip string) bool { + for _, blocked := range acls.Block { res, err := utils.FilterIP(blocked, ip) if err != nil { log.Warn().Err(err).Str("item", blocked).Msg("Invalid IP/CIDR in block list") @@ -382,7 +382,7 @@ func (auth *AuthService) CheckIP(labels config.AppIP, ip string) bool { } } - for _, allowed := range labels.Allow { + for _, allowed := range acls.Allow { res, err := utils.FilterIP(allowed, ip) if err != nil { log.Warn().Err(err).Str("item", allowed).Msg("Invalid IP/CIDR in allow list") @@ -394,7 +394,7 @@ func (auth *AuthService) CheckIP(labels config.AppIP, ip string) bool { } } - if len(labels.Allow) > 0 { + if len(acls.Allow) > 0 { log.Debug().Str("ip", ip).Msg("IP not in allow list, denying access") return false } @@ -403,8 +403,8 @@ func (auth *AuthService) CheckIP(labels config.AppIP, ip string) bool { return true } -func (auth *AuthService) IsBypassedIP(labels config.AppIP, ip string) bool { - for _, bypassed := range labels.Bypass { +func (auth *AuthService) IsBypassedIP(acls config.AppIP, ip string) bool { + for _, bypassed := range acls.Bypass { res, err := utils.FilterIP(bypassed, ip) if err != nil { log.Warn().Err(err).Str("item", bypassed).Msg("Invalid IP/CIDR in bypass list") diff --git a/internal/service/label_service.go b/internal/service/label_service.go deleted file mode 100644 index 852d7c2b..00000000 --- a/internal/service/label_service.go +++ /dev/null @@ -1,92 +0,0 @@ -package service - -import ( - "os" - "strings" - "tinyauth/internal/config" - "tinyauth/internal/utils/decoders" - - "github.com/rs/zerolog/log" -) - -type LabelService struct { - labelsFoundInEnv bool - labels config.Apps -} - -func NewLabelService() *LabelService { - return &LabelService{} -} - -func (label *LabelService) Init() error { - envVars := os.Environ() - // Check if any TINYAUTH_APPS_ environment variables exist - var tinyauthAppEnvVars []string - for _, e := range envVars { - if strings.HasPrefix(e, "TINYAUTH_APPS_") { - tinyauthAppEnvVars = append(tinyauthAppEnvVars, e) - } - } - - // If found, load labels from environment variables - if len(tinyauthAppEnvVars) > 0 { - log.Debug().Msg("TINYAUTH_APPS_ environment variables found, initializing LabelService") - return label.LoadLabels(tinyauthAppEnvVars) - } - log.Debug().Msg("No TINYAUTH_APPS_ environment variables found") - label.labelsFoundInEnv = false - return nil -} - -func (label *LabelService) LoadLabels(envVars []string) error { - // Load environment variables and map them to label format - labelsFromEnv := make(map[string]string) - for _, e := range envVars { - parts := strings.SplitN(e, "=", 2) - if len(parts) == 2 { - key := parts[0] - - // Convert to label format - // e.g. TINYAUTH_APPS_[APP]_CONFIG_DOMAIN → tinyauth.apps.[app].config.domain - key = strings.ToLower(key) - key = strings.ReplaceAll(key, "_", ".") - - value := parts[1] - labelsFromEnv[key] = value - } - } - - // Decode converted labels using some decoder package as Docker labels - labels, err := decoders.DecodeLabels(labelsFromEnv) - if err != nil { - return err - } - - log.Debug().Msg("Labels loaded from environment variables successfully") - label.labels = labels - label.labelsFoundInEnv = true - return nil -} - -func (label *LabelService) GetLabels(appDomain string) (config.App, error) { - // If no labels found in env, return empty labels - if !label.labelsFoundInEnv { - log.Debug().Msg("No labels found in environment, returning empty labels") - return config.App{}, nil - } - // Find matching app by domain or app name - for appName, appLabels := range label.labels.Apps { - if appLabels.Config.Domain == appDomain { - log.Debug().Str("name", appName).Msg("Found matching label by domain") - return appLabels, nil - } - - if strings.SplitN(appDomain, ".", 2)[0] == appName { - log.Debug().Str("name", appName).Msg("Found matching label by app name") - return appLabels, nil - } - } - - log.Debug().Msg("No matching env found, returning empty labels") - return config.App{}, nil -}