From 457dd60aa07bd6d846e07406411fd27c940da4ec Mon Sep 17 00:00:00 2001 From: Catherine Date: Wed, 19 Nov 2025 02:50:58 +0000 Subject: [PATCH] Factor out authentication helpers. NFC --- src/auth.go | 86 ++++++++++++++++++++++--------------------------- src/wildcard.go | 23 +++++++++++++ 2 files changed, 61 insertions(+), 48 deletions(-) diff --git a/src/auth.go b/src/auth.go index d9e11fe..73b4d59 100644 --- a/src/auth.go +++ b/src/auth.go @@ -229,25 +229,7 @@ func authorizeWildcardMatchSite(r *http.Request, pattern *WildcardPattern) (*Aut } if userName, found := pattern.Matches(host); found { - var repoURLs []string - var branch string - repoURLTemplate := pattern.CloneURL - if projectName == ".index" { - for _, indexRepoTemplate := range pattern.IndexRepos { - indexRepo := indexRepoTemplate.ExecuteString(map[string]any{"user": userName}) - repoURLs = append(repoURLs, repoURLTemplate.ExecuteString(map[string]any{ - "user": userName, - "project": indexRepo, - })) - } - branch = pattern.IndexBranch - } else { - repoURLs = append(repoURLs, repoURLTemplate.ExecuteString(map[string]any{ - "user": userName, - "project": projectName, - })) - branch = "pages" - } + repoURLs, branch := pattern.ApplyTemplate(userName, projectName) return &Authorization{repoURLs, branch}, nil } else { return nil, AuthError{ @@ -441,37 +423,12 @@ func AuthorizeUpdateFromRepository(r *http.Request) (*Authorization, error) { return nil, joinErrors(causes...) } -var repoURLSchemeAllowlist []string = []string{"ssh", "http", "https"} - -func AuthorizeRepository(rawRepoURL string, auth *Authorization) error { - // Regardless of any other authorization, only the allowlisted URL schemes - // may ever be cloned from, so this check has to come first. - repoURL, err := url.Parse(rawRepoURL) - if err != nil { - if strings.HasPrefix(rawRepoURL, "git@") { - return AuthError{http.StatusBadRequest, "malformed clone URL; use ssh:// scheme"} - } else { - return AuthError{http.StatusBadRequest, "malformed clone URL"} - } - } - if !slices.Contains(repoURLSchemeAllowlist, repoURL.Scheme) { - return AuthError{ - http.StatusUnauthorized, - fmt.Sprintf("clone URL scheme not in allowlist %v", - repoURLSchemeAllowlist), - } - } - - if auth.repoURLs == nil { - return nil // any - } - - rawRepoURL = strings.ToLower(rawRepoURL) - +func checkAllowedURLPrefix(repoURL string) error { if config.Limits.AllowedRepositoryURLPrefixes != nil { allowedPrefix := false + repoURL = strings.ToLower(repoURL) for _, allowedRepoURLPrefix := range config.Limits.AllowedRepositoryURLPrefixes { - if strings.HasPrefix(rawRepoURL, strings.ToLower(allowedRepoURLPrefix)) { + if strings.HasPrefix(repoURL, strings.ToLower(allowedRepoURLPrefix)) { allowedPrefix = true break } @@ -485,9 +442,42 @@ func AuthorizeRepository(rawRepoURL string, auth *Authorization) error { } } + return nil +} + +var repoURLSchemeAllowlist []string = []string{"ssh", "http", "https"} + +func AuthorizeRepository(repoURL string, auth *Authorization) error { + // Regardless of any other authorization, only the allowlisted URL schemes + // may ever be cloned from, so this check has to come first. + parsedRepoURL, err := url.Parse(repoURL) + if err != nil { + if strings.HasPrefix(repoURL, "git@") { + return AuthError{http.StatusBadRequest, "malformed clone URL; use ssh:// scheme"} + } else { + return AuthError{http.StatusBadRequest, "malformed clone URL"} + } + } + if !slices.Contains(repoURLSchemeAllowlist, parsedRepoURL.Scheme) { + return AuthError{ + http.StatusUnauthorized, + fmt.Sprintf("clone URL scheme not in allowlist %v", + repoURLSchemeAllowlist), + } + } + + if auth.repoURLs == nil { + return nil // any + } + + if err = checkAllowedURLPrefix(repoURL); err != nil { + return err + } + allowed := false + repoURL = strings.ToLower(repoURL) for _, allowedRepoURL := range auth.repoURLs { - if rawRepoURL == strings.ToLower(allowedRepoURL) { + if repoURL == strings.ToLower(allowedRepoURL) { allowed = true break } diff --git a/src/wildcard.go b/src/wildcard.go index 4c1a179..0d979bc 100644 --- a/src/wildcard.go +++ b/src/wildcard.go @@ -57,6 +57,29 @@ func (pattern *WildcardPattern) Matches(host string) (string, bool) { return subdomain, true } +func (pattern *WildcardPattern) ApplyTemplate(userName string, projectName string) ([]string, string) { + var repoURLs []string + var branch string + repoURLTemplate := pattern.CloneURL + if projectName == ".index" { + for _, indexRepoTemplate := range pattern.IndexRepos { + indexRepo := indexRepoTemplate.ExecuteString(map[string]any{"user": userName}) + repoURLs = append(repoURLs, repoURLTemplate.ExecuteString(map[string]any{ + "user": userName, + "project": indexRepo, + })) + } + branch = pattern.IndexBranch + } else { + repoURLs = append(repoURLs, repoURLTemplate.ExecuteString(map[string]any{ + "user": userName, + "project": projectName, + })) + branch = "pages" + } + return repoURLs, branch +} + func (pattern *WildcardPattern) IsFallbackFor(host string) bool { if pattern.Fallback == nil { return false