From 25a41bf62bf0e1e78bbeb9e1297dec4c21aaa832 Mon Sep 17 00:00:00 2001 From: Stavros Date: Mon, 13 Jul 2026 22:01:49 +0300 Subject: [PATCH] fix: fix domain check in acl service --- internal/controller/oauth_controller.go | 5 +- internal/service/access_controls_service.go | 56 ++++++++++++++++++--- 2 files changed, 53 insertions(+), 8 deletions(-) diff --git a/internal/controller/oauth_controller.go b/internal/controller/oauth_controller.go index 27fca206..1cbc520b 100644 --- a/internal/controller/oauth_controller.go +++ b/internal/controller/oauth_controller.go @@ -350,7 +350,10 @@ func (controller *OAuthController) isRedirectSafe(redirectURI string) bool { return false } - if strings.EqualFold(u.Hostname(), au.Hostname()) { + nu := strings.TrimSuffix(u.Hostname(), ".") + nau := strings.TrimSuffix(au.Hostname(), ".") + + if strings.EqualFold(nu, nau) { return true } diff --git a/internal/service/access_controls_service.go b/internal/service/access_controls_service.go index 3615cce1..07233faa 100644 --- a/internal/service/access_controls_service.go +++ b/internal/service/access_controls_service.go @@ -1,6 +1,8 @@ package service import ( + "fmt" + "net/url" "strings" "github.com/tinyauthapp/tinyauth/internal/model" @@ -35,27 +37,35 @@ func NewAccessControlsService(i AccessControlServiceInput) *AccessControlsServic } } -func (service *AccessControlsService) lookupStaticACLs(domain string) *model.App { +func (service *AccessControlsService) lookupStaticACLs(domain string) (*model.App, error) { var nameMatch *model.App // First try to find a matching app by domain, then fallback to matching by app name (subdomain) for app, config := range service.config.Apps { - if config.Config.Domain == domain { - service.log.App.Debug().Str("name", app).Msg("Found matching container by domain") - return &config + match, err := service.checkDomain(domain, config.Config.Domain) + if err != nil { + return nil, err } - if strings.SplitN(domain, ".", 2)[0] == app { + if match { + service.log.App.Debug().Str("name", app).Msg("Found matching container by domain") + return &config, nil + } + if strings.HasPrefix(domain, app+".") { service.log.App.Debug().Str("name", app).Msg("Found matching container by app name") nameMatch = &config } } - return nameMatch + return nameMatch, nil } func (service *AccessControlsService) GetAccessControls(domain string) (*model.App, error) { // First check in the static config - app := service.lookupStaticACLs(domain) + app, err := service.lookupStaticACLs(domain) + + if err != nil { + return nil, err + } if app != nil { service.log.App.Debug().Msg("Using static ACLs for app") @@ -70,3 +80,35 @@ func (service *AccessControlsService) GetAccessControls(domain string) (*model.A // no labels return nil, nil } + +func (service *AccessControlsService) checkDomain(check, target string) (bool, error) { + // Domains don't have a scheme, so we use a mock one + cu, err := url.Parse("tinyauth://" + check) + + if err != nil { + return false, fmt.Errorf("failed to parse check domain: %w", err) + } + + if cu.Host == "" { + return false, fmt.Errorf("check domain is empty") + } + + tu, err := url.Parse("tinyauth://" + target) + + if err != nil { + return false, fmt.Errorf("failed to parse target domain: %w", err) + } + + if tu.Host == "" { + return false, fmt.Errorf("target domain is empty") + } + + // non-dns check url + ndcu := strings.TrimSuffix(cu.Hostname(), ".") + + // non-dns target url + ndtu := strings.TrimSuffix(tu.Hostname(), ".") + + // Strip out the port + return strings.EqualFold(ndcu, ndtu), nil +}