Compare commits

..

No commits in common. "b9cc7f30e8301a3a730e7bb5b52675a5b2fe6f72" and "8630855374f8c875a731409c2625dca107e0f258" have entirely different histories.

10 changed files with 255 additions and 535 deletions

View File

@ -16,7 +16,6 @@ import (
"git.polynom.me/rio/internal/certificates" "git.polynom.me/rio/internal/certificates"
"git.polynom.me/rio/internal/context" "git.polynom.me/rio/internal/context"
"git.polynom.me/rio/internal/dns" "git.polynom.me/rio/internal/dns"
riogitea "git.polynom.me/rio/internal/gitea"
"git.polynom.me/rio/internal/metrics" "git.polynom.me/rio/internal/metrics"
"git.polynom.me/rio/internal/pages" "git.polynom.me/rio/internal/pages"
"git.polynom.me/rio/internal/repo" "git.polynom.me/rio/internal/repo"
@ -56,7 +55,7 @@ func handleSubdomain(ctx *context.GlobalContext, domain, cname, path string, req
domain, domain,
cname, cname,
path, path,
ctx, ctx.Gitea,
) )
if err != nil { if err != nil {
log.Errorf("Failed to get repo: %s", err) log.Errorf("Failed to get repo: %s", err)
@ -188,7 +187,7 @@ func runServer(ctx *cli.Context) error {
if err != nil { if err != nil {
return err return err
} }
giteaClient := riogitea.NewGiteaClient(giteaUrl, giteaApiClient) giteaClient := repo.NewGiteaClient(giteaUrl, giteaApiClient)
// Listen on the port // Listen on the port
addr := ctx.String("listen-host") + ":" + ctx.String("listen-port") addr := ctx.String("listen-host") + ":" + ctx.String("listen-port")
@ -209,20 +208,6 @@ func runServer(ctx *cli.Context) error {
return err return err
} }
// Prepare the context
cacheCtx := context.CacheContext{
RepositoryInformationCache: context.MakeRepoInfoCache(),
RepositoryPathCache: context.MakeRepoPathCache(),
UsernameCache: context.MakeUsernameCache(),
}
globalCtx := &context.GlobalContext{
DefaultCSP: defaultCsp,
PagesDomain: domain,
Gitea: &giteaClient,
MetricConfig: &lokiConfig,
Cache: &cacheCtx,
}
if !acmeDisable { if !acmeDisable {
if acmeEmail == "" || acmeFile == "" || certsFile == "" || acmeDnsProvider == "" { if acmeEmail == "" || acmeFile == "" || certsFile == "" || acmeDnsProvider == "" {
return errors.New("The options acme-dns-provider, acme-file, acme-email, and certs-file are required") return errors.New("The options acme-dns-provider, acme-file, acme-email, and certs-file are required")
@ -276,11 +261,18 @@ func runServer(ctx *cli.Context) error {
certsFile, certsFile,
&cache, &cache,
acmeClient, acmeClient,
globalCtx, &giteaClient,
) )
listener = tls.NewListener(listener, tlsConfig) listener = tls.NewListener(listener, tlsConfig)
} }
globalCtx := &context.GlobalContext{
DefaultCSP: defaultCsp,
PagesDomain: domain,
Gitea: &giteaClient,
MetricConfig: &lokiConfig,
}
var waitGroup sync.WaitGroup var waitGroup sync.WaitGroup
servers := 2 servers := 2
if acmeDisable { if acmeDisable {

View File

@ -3,28 +3,15 @@ package context
import ( import (
"net/http" "net/http"
"git.polynom.me/rio/internal/gitea"
"git.polynom.me/rio/internal/metrics" "git.polynom.me/rio/internal/metrics"
"github.com/patrickmn/go-cache" "git.polynom.me/rio/internal/repo"
) )
type CacheContext struct {
// Cache for general repository information
RepositoryInformationCache cache.Cache
// Cache for path resolutions
RepositoryPathCache cache.Cache
// Cache for username lookups
UsernameCache cache.Cache
}
type GlobalContext struct { type GlobalContext struct {
DefaultCSP string DefaultCSP string
PagesDomain string PagesDomain string
Gitea *gitea.GiteaClient Gitea *repo.GiteaClient
MetricConfig *metrics.LokiMetricConfig MetricConfig *metrics.LokiMetricConfig
Cache *CacheContext
} }
type Context struct { type Context struct {

View File

@ -1,40 +0,0 @@
package context
import (
"time"
"github.com/patrickmn/go-cache"
)
type RepositoryInformation struct {
// Headers to include in every response
Headers map[string]string
CNAME string
}
func repoInfoKey(owner, name string) string {
return owner + ":" + name
}
func (c *CacheContext) GetRepositoryInformation(owner, repoName string) *RepositoryInformation {
data, found := c.RepositoryInformationCache.Get(repoInfoKey(owner, repoName))
if !found {
return nil
}
typedData := data.(RepositoryInformation)
return &typedData
}
func (c *CacheContext) SetRepositoryInformation(owner, repoName string, info RepositoryInformation) {
c.RepositoryInformationCache.Set(
repoInfoKey(owner, repoName),
info,
cache.DefaultExpiration,
)
}
func MakeRepoInfoCache() cache.Cache {
return *cache.New(24*time.Hour, 12*time.Hour)
}

View File

@ -1,39 +0,0 @@
package context
import (
"time"
"git.polynom.me/rio/internal/gitea"
"github.com/patrickmn/go-cache"
)
type RepositoryPathInformation struct {
Repository gitea.Repository
Path string
}
func pathInfoKey(domain, path string) string {
return domain + "/" + path
}
func (c *CacheContext) GetRepositoryPath(domain, path string) *RepositoryPathInformation {
data, found := c.RepositoryPathCache.Get(pathInfoKey(domain, path))
if !found {
return nil
}
typedData := data.(RepositoryPathInformation)
return &typedData
}
func (c *CacheContext) SetRepositoryPath(domain, path string, info RepositoryPathInformation) {
c.RepositoryPathCache.Set(
pathInfoKey(domain, path),
info,
cache.DefaultExpiration,
)
}
func MakeRepoPathCache() cache.Cache {
return *cache.New(24*time.Hour, 12*time.Hour)
}

View File

@ -1,24 +0,0 @@
package context
import (
"time"
"github.com/patrickmn/go-cache"
)
func (c *CacheContext) GetUser(username string) bool {
_, found := c.UsernameCache.Get(username)
return found
}
func (c *CacheContext) SetUser(username string) {
c.UsernameCache.Set(
username,
true,
cache.DefaultExpiration,
)
}
func MakeUsernameCache() cache.Cache {
return *cache.New(24*time.Hour, 12*time.Hour)
}

View File

@ -9,6 +9,7 @@ import (
"git.polynom.me/rio/internal/constants" "git.polynom.me/rio/internal/constants"
"git.polynom.me/rio/internal/context" "git.polynom.me/rio/internal/context"
"git.polynom.me/rio/internal/repo"
"github.com/patrickmn/go-cache" "github.com/patrickmn/go-cache"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
@ -28,7 +29,7 @@ func makePageContentCacheEntry(username, path string) string {
return username + ":" + path return username + ":" + path
} }
func addHeaders(repoInfo *context.RepositoryInformation, contentType string, contentLength int, w http.ResponseWriter) { func addHeaders(csp, contentType string, contentLength int, w http.ResponseWriter) {
// Always set a content type // Always set a content type
if strings.Trim(contentType, " ") == "" { if strings.Trim(contentType, " ") == "" {
w.Header().Set("Content-Type", "application/octet-stream") w.Header().Set("Content-Type", "application/octet-stream")
@ -40,10 +41,8 @@ func addHeaders(repoInfo *context.RepositoryInformation, contentType string, con
w.Header().Set("Strict-Transport-Security", "max-age=31536000") w.Header().Set("Strict-Transport-Security", "max-age=31536000")
w.Header().Set("Content-Length", strconv.Itoa(contentLength)) w.Header().Set("Content-Length", strconv.Itoa(contentLength))
if repoInfo != nil { if csp != "" {
for key, value := range repoInfo.Headers { w.Header().Set("Content-Security-Policy", csp)
w.Header().Set(key, value)
}
} }
} }
@ -75,19 +74,16 @@ func ServeFile(context *context.Context) {
path, path,
since, since,
) )
repoInfo := context.Global.Cache.GetRepositoryInformation( csp := repo.GetCSPForRepository(context.Username, context.Reponame, "", context.Global.Gitea)
context.Username,
context.Reponame,
)
if err != nil { if err != nil {
if !found { if !found {
log.Errorf("Failed to get file %s/%s/%s (%s)", context.Username, context.Reponame, path, err) log.Errorf("Failed to get file %s/%s/%s (%s)", context.Username, context.Reponame, path, err)
addHeaders(repoInfo, "text/html", 0, context.Writer) addHeaders(csp, "text/html", 0, context.Writer)
context.Writer.WriteHeader(404) context.Writer.WriteHeader(404)
} else { } else {
log.Debugf("Request failed but page %s is cached in memory", path) log.Debugf("Request failed but page %s is cached in memory", path)
addHeaders(repoInfo, mimeType, len(content), context.Writer) addHeaders(csp, mimeType, len(content), context.Writer)
context.Writer.WriteHeader(200) context.Writer.WriteHeader(200)
context.Writer.Write(content) context.Writer.Write(content)
} }
@ -97,7 +93,7 @@ func ServeFile(context *context.Context) {
if found && !changed { if found && !changed {
log.Debugf("Page %s is unchanged and cached in memory", path) log.Debugf("Page %s is unchanged and cached in memory", path)
addHeaders(repoInfo, mimeType, len(content), context.Writer) addHeaders(csp, mimeType, len(content), context.Writer)
context.Writer.WriteHeader(200) context.Writer.WriteHeader(200)
context.Writer.Write(content) context.Writer.Write(content)
return return
@ -119,7 +115,7 @@ func ServeFile(context *context.Context) {
) )
log.Debugf("Page %s requested from Gitea and cached in memory at %v", path, now) log.Debugf("Page %s requested from Gitea and cached in memory at %v", path, now)
addHeaders(repoInfo, mimeType, len(content), context.Writer) addHeaders(csp, mimeType, len(content), context.Writer)
context.Writer.WriteHeader(200) context.Writer.WriteHeader(200)
context.Writer.Write(content) context.Writer.Write(content)

View File

@ -1,4 +1,4 @@
package gitea package repo
import ( import (
"fmt" "fmt"
@ -38,17 +38,17 @@ type Repository struct {
} }
type GiteaClient struct { type GiteaClient struct {
GetRepository GetRepositoryMethod getRepository GetRepositoryMethod
HasBranch HasBranchMethod hasBranch HasBranchMethod
HasUser HasUserMethod hasUser HasUserMethod
GetFile GetFileMethod GetFile GetFileMethod
LookupCNAME LookupCNAMEMethod lookupCNAME LookupCNAMEMethod
LookupRepoTXT LookupRepoTXTMethod lookupRepoTXT LookupRepoTXTMethod
} }
func NewGiteaClient(giteaUrl string, giteaClient *gitea.Client) GiteaClient { func NewGiteaClient(giteaUrl string, giteaClient *gitea.Client) GiteaClient {
return GiteaClient{ return GiteaClient{
GetRepository: func(username, repositoryName string) (Repository, error) { getRepository: func(username, repositoryName string) (Repository, error) {
repo, _, err := giteaClient.GetRepo(username, repositoryName) repo, _, err := giteaClient.GetRepo(username, repositoryName)
if err != nil { if err != nil {
return Repository{}, err return Repository{}, err
@ -58,7 +58,7 @@ func NewGiteaClient(giteaUrl string, giteaClient *gitea.Client) GiteaClient {
Name: repo.Name, Name: repo.Name,
}, nil }, nil
}, },
HasBranch: func(username, repositoryName, branchName string) bool { hasBranch: func(username, repositoryName, branchName string) bool {
res, _, err := giteaClient.ListRepoBranches(username, repositoryName, gitea.ListRepoBranchesOptions{}) res, _, err := giteaClient.ListRepoBranches(username, repositoryName, gitea.ListRepoBranchesOptions{})
if err != nil { if err != nil {
return false return false
@ -71,7 +71,7 @@ func NewGiteaClient(giteaUrl string, giteaClient *gitea.Client) GiteaClient {
} }
return false return false
}, },
HasUser: func(username string) bool { hasUser: func(username string) bool {
_, _, err := giteaClient.GetUserInfo(username) _, _, err := giteaClient.GetUserInfo(username)
return err == nil return err == nil
}, },
@ -109,10 +109,10 @@ func NewGiteaClient(giteaUrl string, giteaClient *gitea.Client) GiteaClient {
return content, true, err return content, true, err
} }
}, },
LookupCNAME: func(domain string) (string, error) { lookupCNAME: func(domain string) (string, error) {
return dns.LookupCNAME(domain) return dns.LookupCNAME(domain)
}, },
LookupRepoTXT: func(domain string) (string, error) { lookupRepoTXT: func(domain string) (string, error) {
return dns.LookupRepoTXT(domain) return dns.LookupRepoTXT(domain)
}, },
} }

View File

@ -3,85 +3,119 @@ package repo
//go:generate mockgen -destination mock_repo_test.go -package repo code.gitea.io/sdk/gitea Client //go:generate mockgen -destination mock_repo_test.go -package repo code.gitea.io/sdk/gitea Client
import ( import (
"encoding/json"
"errors" "errors"
"slices"
"strings" "strings"
"time"
"git.polynom.me/rio/internal/constants" "git.polynom.me/rio/internal/constants"
"git.polynom.me/rio/internal/context"
"git.polynom.me/rio/internal/gitea"
"github.com/patrickmn/go-cache"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
var ( var (
ForbiddenHeaders = []string{ pathCache = cache.New(1*time.Hour, 1*time.Hour)
"content-length",
"content-type", // Caching the existence of an user
"date", userCache = cache.New(24*time.Hour, 12*time.Hour)
"location",
"strict-transport-security", // Caches the existence of a Content-Security-Policy
"set-cookie", // Mapping: Repository key -> CSPCacheEntry
} cspCache = cache.New(24*time.Hour, 12*time.Hour)
) )
func lookupRepositoryAndCache(username, reponame, branchName, host, domain, path, cname string, ctx *context.GlobalContext) (*gitea.Repository, error) { type PageCacheEntry struct {
Repository Repository
Path string
}
type CSPCacheEntry struct {
CSP string
LastRequested time.Time
}
func makePageCacheKey(domain, path string) string {
return domain + "/" + path
}
func makeCSPCacheKey(username, repositoryName string) string {
return username + ":" + repositoryName
}
func lookupRepositoryAndCache(username, reponame, branchName, host, domain, path, cname string, giteaClient *GiteaClient) (*Repository, error) {
log.Debugf("CNAME: %s", cname) log.Debugf("CNAME: %s", cname)
log.Debugf("Looking up repository %s/%s", username, reponame) log.Debugf("Looking up repository %s/%s", username, reponame)
repo, err := ctx.Gitea.GetRepository(username, reponame) repo, err := giteaClient.getRepository(username, reponame)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if !ctx.Gitea.HasBranch(username, reponame, branchName) { if !giteaClient.hasBranch(username, reponame, branchName) {
return nil, errors.New("Specified branch does not exist") return nil, errors.New("Specified branch does not exist")
} }
// Check if the CNAME file matches // Check if the CNAME file matches
if cname != "" { if cname != "" {
log.Debug("Checking CNAME") log.Debug("Checking CNAME")
repoInfo := GetRepositoryInformation(username, reponame, ctx) file, _, err := giteaClient.GetFile(
if repoInfo == nil { username,
log.Warn("Repository does not contain a rio.json file") reponame,
return nil, errors.New("No CNAME available in repository") constants.PagesBranch,
"CNAME",
nil,
)
if err != nil {
log.Errorf(
"Could not verify CNAME of %s/%s@%s: %v\n",
username,
reponame,
constants.PagesBranch,
err,
)
return nil, err
} }
log.Debugf("CNAME Content: \"%s\"", repoInfo.CNAME) cnameContent := strings.Trim(
if repoInfo.CNAME != host { string(file[:]),
log.Warnf("CNAME mismatch: Repo '%s', Host '%s'", repoInfo.CNAME, host) "\n",
)
log.Debugf("CNAME Content: %s", cnameContent)
if cnameContent != host {
log.Warnf("CNAME mismatch: Repo '%s', Host '%s'", cnameContent, host)
return nil, errors.New("CNAME mismatch") return nil, errors.New("CNAME mismatch")
} }
} }
// Cache data // Cache data
ctx.Cache.SetRepositoryPath( pathCache.Set(
domain, makePageCacheKey(domain, path),
PageCacheEntry{
repo,
path, path,
context.RepositoryPathInformation{
Repository: repo,
Path: path,
}, },
cache.DefaultExpiration,
) )
return &repo, nil return &repo, nil
} }
// host is the domain name we're accessed from. cname is the domain that host is pointing // host is the domain name we're accessed from. cname is the domain that host is pointing
// if, if we're accessed via a CNAME. If not, then cname is "". // if, if we're accessed via a CNAME. If not, then cname is "".
func RepoFromPath(username, host, cname, path string, ctx *context.GlobalContext) (*gitea.Repository, string, error) { func RepoFromPath(username, host, cname, path string, giteaClient *GiteaClient) (*Repository, string, error) {
domain := host domain := host
// Guess the repository // Guess the repository
entry := ctx.Cache.GetRepositoryPath(domain, path) key := makePageCacheKey(domain, path)
if entry != nil { entry, found := pathCache.Get(key)
return &entry.Repository, entry.Path, nil if found {
pageEntry := entry.(PageCacheEntry)
return &pageEntry.Repository, pageEntry.Path, nil
} }
// Allow specifying the repository name in the TXT record // Allow specifying the repository name in the TXT record
reponame := "" reponame := ""
if cname != "" { if cname != "" {
repoLookup, err := ctx.Gitea.LookupRepoTXT(host) repoLookup, err := giteaClient.lookupRepoTXT(host)
if err == nil && repoLookup != "" { if err == nil && repoLookup != "" {
log.Infof( log.Infof(
"TXT lookup for %s resulted in choosing repository %s", "TXT lookup for %s resulted in choosing repository %s",
@ -105,7 +139,7 @@ func RepoFromPath(username, host, cname, path string, ctx *context.GlobalContext
domain, domain,
modifiedPath, modifiedPath,
cname, cname,
ctx, giteaClient,
) )
if err == nil { if err == nil {
return repo, modifiedPath, nil return repo, modifiedPath, nil
@ -124,7 +158,7 @@ func RepoFromPath(username, host, cname, path string, ctx *context.GlobalContext
domain, domain,
path, path,
cname, cname,
ctx, giteaClient,
) )
return repo, path, err return repo, path, err
} }
@ -132,92 +166,52 @@ func RepoFromPath(username, host, cname, path string, ctx *context.GlobalContext
// Checks if the username exists as an organisation or an user on the Gitea // Checks if the username exists as an organisation or an user on the Gitea
// instance, so that an attacker can't just request certificates for random // instance, so that an attacker can't just request certificates for random
// usernames. // usernames.
func CanRequestCertificate(username string, ctx *context.GlobalContext) bool { func CanRequestCertificate(username string, giteaClient *GiteaClient) bool {
found := ctx.Cache.GetUser(username) if _, found := userCache.Get(username); found {
if found {
return true return true
} }
hasUser := ctx.Gitea.HasUser(username) hasUser := giteaClient.hasUser(username)
if hasUser { if hasUser {
ctx.Cache.SetUser(username) userCache.Set(username, true, cache.DefaultExpiration)
} }
return hasUser return hasUser
} }
func filterHeaders(headers map[string]interface{}) map[string]string { // Checks the repository username/repository@PagesBranch for a file named CSP. If it exists,
newHeaders := make(map[string]string) // read it and return the value. If it does not exist, return defaultCsp.
func GetCSPForRepository(username, repositoryName, defaultCsp string, giteaClient *GiteaClient) string {
for key, value := range headers { key := makeCSPCacheKey(username, repositoryName)
if slices.Contains[[]string, string](ForbiddenHeaders, strings.ToLower(key)) { cachedCsp, found := cspCache.Get(key)
continue var since time.Time
}
switch value.(type) {
case string:
newHeaders[key] = value.(string)
}
}
return newHeaders
}
func GetRepositoryInformation(owner, repoName string, ctx *context.GlobalContext) *context.RepositoryInformation {
res := ctx.Cache.GetRepositoryInformation(owner, repoName)
if res != nil {
return res
}
fetchedConfig, _, err := ctx.Gitea.GetFile(
owner,
repoName,
constants.PagesBranch,
"rio.json",
nil,
)
if err != nil {
log.Errorf("Failed to request rio.json for %s/%s:%v", owner, repoName, err)
return nil
}
var payload map[string]interface{}
err = json.Unmarshal(fetchedConfig, &payload)
if err != nil {
log.Errorf("Failed to unmarshal rio.json for %s/%s:%v", owner, repoName, err)
return nil
}
headers, found := payload["headers"]
if !found {
log.Warnf("Did not find headers key in rio.json for %s/%s", owner, repoName)
headers = make(map[string]interface{})
} else {
switch headers.(type) {
case map[string]interface{}:
// NOOP
default:
log.Warn("headers attribute has invalid data type")
headers = make(map[string]string)
}
}
cname, found := payload["CNAME"]
if found { if found {
switch cname.(type) { since = cachedCsp.(CSPCacheEntry).LastRequested
case string:
// NOOP
default:
log.Warnf("CNAME attribute is not a string for %s/%s", owner, repoName)
cname = ""
}
} else {
cname = ""
} }
info := context.RepositoryInformation{ fetchedCsp, changed, err := giteaClient.GetFile(
Headers: filterHeaders(headers.(map[string]interface{})), username,
CNAME: cname.(string), repositoryName,
constants.PagesBranch,
"CSP",
&since,
)
csp := ""
if err != nil {
if found {
return cachedCsp.(CSPCacheEntry).CSP
} }
ctx.Cache.SetRepositoryInformation(owner, repoName, info)
return &info csp = defaultCsp
} else {
csp = string(fetchedCsp)
if !found || changed {
cspCache.Set(key, CSPCacheEntry{
CSP: csp,
LastRequested: time.Now(),
}, cache.DefaultExpiration)
}
}
return csp
} }

View File

@ -2,44 +2,28 @@ package repo
import ( import (
"errors" "errors"
"strings" "net/http"
"testing" "testing"
"time" "time"
"git.polynom.me/rio/internal/context" "code.gitea.io/sdk/gitea"
"git.polynom.me/rio/internal/gitea"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
func TestHeaderFilter(t *testing.T) { func clearCache() {
map1 := filterHeaders( pathCache.Flush()
map[string]interface{}{ userCache.Flush()
"Content-Type": "hallo", cspCache.Flush()
"content-Type": "welt",
"content-type": "uwu",
"CONTENT-TYPE": "lol",
"Content-Security-Policy": "none",
},
)
if len(map1) != 1 {
t.Fatalf("filterHeaders allowed %d != 1 headers", len(map1))
}
for key := range map1 {
if strings.ToLower(key) == "content-type" {
t.Fatalf("filterHeaders allowed Content-Type")
}
}
} }
func TestPickingCorrectRepositoryDefault(t *testing.T) { func TestPickingCorrectRepositoryDefault(t *testing.T) {
// Test that we default to the <username>.<pages domain> repository, if we have only // Test that we default to the <username>.<pages domain> repository, if we have only
// one path component. // one path component.
defer clearCache()
log.SetLevel(log.DebugLevel) log.SetLevel(log.DebugLevel)
client := gitea.GiteaClient{ client := GiteaClient{
GetRepository: func(username, repositoryName string) (gitea.Repository, error) { getRepository: func(username, repositoryName string) (Repository, error) {
if username != "example-user" { if username != "example-user" {
t.Fatalf("Called with unknown user %s", username) t.Fatalf("Called with unknown user %s", username)
} }
@ -47,9 +31,9 @@ func TestPickingCorrectRepositoryDefault(t *testing.T) {
t.Fatalf("Called with unknown repository %s", repositoryName) t.Fatalf("Called with unknown repository %s", repositoryName)
} }
return gitea.Repository{}, nil return Repository{}, nil
}, },
HasBranch: func(username, repositoryName, branchName string) bool { hasBranch: func(username, repositoryName, branchName string) bool {
if username == "example-user" && repositoryName == "example-user.pages.example.org" && branchName == "pages" { if username == "example-user" && repositoryName == "example-user.pages.example.org" && branchName == "pages" {
return true return true
} }
@ -60,24 +44,17 @@ func TestPickingCorrectRepositoryDefault(t *testing.T) {
t.Fatal("getFile called") t.Fatal("getFile called")
return []byte{}, true, nil return []byte{}, true, nil
}, },
LookupCNAME: func(domain string) (string, error) { lookupCNAME: func(domain string) (string, error) {
t.Fatal("LookupCNAME called") t.Fatal("lookupCNAME called")
return "", nil return "", nil
}, },
LookupRepoTXT: func(domain string) (string, error) { lookupRepoTXT: func(domain string) (string, error) {
t.Fatal("LookupRepoTXT called") t.Fatal("lookupRepoTXT called")
return "", nil return "", nil
}, },
} }
ctx := &context.GlobalContext{
Gitea: &client,
Cache: &context.CacheContext{
RepositoryInformationCache: context.MakeRepoInfoCache(),
RepositoryPathCache: context.MakeRepoPathCache(),
},
}
res, path, err := RepoFromPath("example-user", "example-user.pages.example.org", "", "index.html", ctx) res, path, err := RepoFromPath("example-user", "example-user.pages.example.org", "", "index.html", &client)
if err != nil { if err != nil {
t.Fatalf("An error occured: %v", err) t.Fatalf("An error occured: %v", err)
} }
@ -92,22 +69,24 @@ func TestPickingCorrectRepositoryDefault(t *testing.T) {
func TestPickingCorrectRepositoryDefaultSubdirectory(t *testing.T) { func TestPickingCorrectRepositoryDefaultSubdirectory(t *testing.T) {
// Test that we return the default repository when the first path component does // Test that we return the default repository when the first path component does
// not correspong to an existing repository. // not correspong to an existing repository.
defer clearCache()
log.SetLevel(log.DebugLevel) log.SetLevel(log.DebugLevel)
client := gitea.GiteaClient{ client := GiteaClient{
GetRepository: func(username, repositoryName string) (gitea.Repository, error) { getRepository: func(username, repositoryName string) (Repository, error) {
if username != "example-user" { if username != "example-user" {
t.Fatalf("Called with unknown user %s", username) t.Fatalf("Called with unknown user %s", username)
} }
if repositoryName == "assets" { if repositoryName == "assets" {
return gitea.Repository{}, errors.New("gitea.Repository does not exist") return Repository{}, errors.New("Repository does not exist")
} else if repositoryName == "example-user.pages.example.org" { } else if repositoryName == "example-user.pages.example.org" {
return gitea.Repository{}, nil return Repository{}, nil
} else { } else {
t.Fatalf("Called with unknown repository %s", repositoryName) t.Fatalf("Called with unknown repository %s", repositoryName)
return gitea.Repository{}, nil return Repository{}, nil
} }
}, },
HasBranch: func(username, repositoryName, branchName string) bool { hasBranch: func(username, repositoryName, branchName string) bool {
if username == "example-user" && repositoryName == "example-user.pages.example.org" && branchName == "pages" { if username == "example-user" && repositoryName == "example-user.pages.example.org" && branchName == "pages" {
return true return true
} }
@ -118,24 +97,17 @@ func TestPickingCorrectRepositoryDefaultSubdirectory(t *testing.T) {
t.Fatal("getFile called") t.Fatal("getFile called")
return []byte{}, true, nil return []byte{}, true, nil
}, },
LookupCNAME: func(domain string) (string, error) { lookupCNAME: func(domain string) (string, error) {
t.Fatal("LookupCNAME called") t.Fatal("lookupCNAME called")
return "", nil return "", nil
}, },
LookupRepoTXT: func(domain string) (string, error) { lookupRepoTXT: func(domain string) (string, error) {
t.Fatal("LookupRepoTXT called") t.Fatal("lookupRepoTXT called")
return "", nil return "", nil
}, },
} }
ctx := &context.GlobalContext{
Gitea: &client,
Cache: &context.CacheContext{
RepositoryInformationCache: context.MakeRepoInfoCache(),
RepositoryPathCache: context.MakeRepoPathCache(),
},
}
res, path, err := RepoFromPath("example-user", "example-user.pages.example.org", "", "assets/index.css", ctx) res, path, err := RepoFromPath("example-user", "example-user.pages.example.org", "", "assets/index.css", &client)
if err != nil { if err != nil {
t.Fatalf("An error occured: %v", err) t.Fatalf("An error occured: %v", err)
} }
@ -150,26 +122,28 @@ func TestPickingCorrectRepositoryDefaultSubdirectory(t *testing.T) {
func TestPickingCorrectRepositorySubdirectoryNoPagesBranch(t *testing.T) { func TestPickingCorrectRepositorySubdirectoryNoPagesBranch(t *testing.T) {
// Test that we're picking the correct repository when the first path component // Test that we're picking the correct repository when the first path component
// returns a repository without a pages branch. // returns a repository without a pages branch.
defer clearCache()
log.SetLevel(log.DebugLevel) log.SetLevel(log.DebugLevel)
client := gitea.GiteaClient{ client := GiteaClient{
GetRepository: func(username, repositoryName string) (gitea.Repository, error) { getRepository: func(username, repositoryName string) (Repository, error) {
if username != "example-user" { if username != "example-user" {
t.Fatalf("Called with unknown user %s", username) t.Fatalf("Called with unknown user %s", username)
} }
if repositoryName == "blog" { if repositoryName == "blog" {
return gitea.Repository{ return Repository{
Name: "blog", Name: "blog",
}, nil }, nil
} else if repositoryName == "example-user.pages.example.org" { } else if repositoryName == "example-user.pages.example.org" {
return gitea.Repository{ return Repository{
Name: "example-user.pages.example.org", Name: "example-user.pages.example.org",
}, nil }, nil
} else { } else {
t.Fatalf("Called with unknown repository %s", repositoryName) t.Fatalf("Called with unknown repository %s", repositoryName)
return gitea.Repository{}, nil return Repository{}, nil
} }
}, },
HasBranch: func(username, repositoryName, branchName string) bool { hasBranch: func(username, repositoryName, branchName string) bool {
if username == "example-user" && repositoryName == "example-user.pages.example.org" && branchName == "pages" { if username == "example-user" && repositoryName == "example-user.pages.example.org" && branchName == "pages" {
return true return true
} }
@ -180,24 +154,17 @@ func TestPickingCorrectRepositorySubdirectoryNoPagesBranch(t *testing.T) {
t.Fatal("getFile called") t.Fatal("getFile called")
return []byte{}, true, nil return []byte{}, true, nil
}, },
LookupCNAME: func(domain string) (string, error) { lookupCNAME: func(domain string) (string, error) {
t.Fatal("LookupCNAME called") t.Fatal("lookupCNAME called")
return "", nil return "", nil
}, },
LookupRepoTXT: func(domain string) (string, error) { lookupRepoTXT: func(domain string) (string, error) {
t.Fatal("LookupRepoTXT called") t.Fatal("lookupRepoTXT called")
return "", nil return "", nil
}, },
} }
ctx := &context.GlobalContext{
Gitea: &client,
Cache: &context.CacheContext{
RepositoryInformationCache: context.MakeRepoInfoCache(),
RepositoryPathCache: context.MakeRepoPathCache(),
},
}
res, path, err := RepoFromPath("example-user", "example-user.pages.example.org", "", "blog/post1.html", ctx) res, path, err := RepoFromPath("example-user", "example-user.pages.example.org", "", "blog/post1.html", &client)
if err != nil { if err != nil {
t.Fatalf("An error occured: %v", err) t.Fatalf("An error occured: %v", err)
} }
@ -214,19 +181,21 @@ func TestPickingCorrectRepositorySubdirectoryNoPagesBranch(t *testing.T) {
func TestPickingNoRepositoryInvalidCNAME(t *testing.T) { func TestPickingNoRepositoryInvalidCNAME(t *testing.T) {
// Test that we're not picking a repository if the CNAME validation fails. // Test that we're not picking a repository if the CNAME validation fails.
defer clearCache()
log.SetLevel(log.DebugLevel) log.SetLevel(log.DebugLevel)
client := gitea.GiteaClient{ client := GiteaClient{
GetRepository: func(username, repositoryName string) (gitea.Repository, error) { getRepository: func(username, repositoryName string) (Repository, error) {
if username == "example-user" && repositoryName == "example-user.pages.example.org" { if username == "example-user" && repositoryName == "example-user.pages.example.org" {
return gitea.Repository{ return Repository{
Name: "example-user.pages.example.org", Name: "example-user.pages.example.org",
}, nil }, nil
} else { } else {
t.Fatalf("Called with unknown repository %s", repositoryName) t.Fatalf("Called with unknown repository %s", repositoryName)
return gitea.Repository{}, nil return Repository{}, nil
} }
}, },
HasBranch: func(username, repositoryName, branchName string) bool { hasBranch: func(username, repositoryName, branchName string) bool {
if username == "example-user" && repositoryName == "example-user.pages.example.org" && branchName == "pages" { if username == "example-user" && repositoryName == "example-user.pages.example.org" && branchName == "pages" {
return true return true
} }
@ -234,49 +203,44 @@ func TestPickingNoRepositoryInvalidCNAME(t *testing.T) {
return false return false
}, },
GetFile: func(username, repositoryName, branch, path string, since *time.Time) ([]byte, bool, error) { GetFile: func(username, repositoryName, branch, path string, since *time.Time) ([]byte, bool, error) {
if username == "example-user" && repositoryName == "example-user.pages.example.org" && branch == "pages" && path == "rio.json" { if username == "example-user" && repositoryName == "example-user.pages.example.org" && branch == "pages" && path == "CNAME" {
return []byte("{\"CNAME\": \"some-other-domain.local\"}"), true, nil return []byte("some-other-domain.local"), true, nil
} }
t.Fatalf("Invalid file requested: %s/%s@%s:%s", username, repositoryName, branch, path) t.Fatalf("Invalid file requested: %s/%s@%s:%s", username, repositoryName, branch, path)
return []byte{}, true, nil return []byte{}, true, nil
}, },
LookupCNAME: func(domain string) (string, error) { lookupCNAME: func(domain string) (string, error) {
return "", errors.New("No CNAME") return "", errors.New("No CNAME")
}, },
LookupRepoTXT: func(domain string) (string, error) { lookupRepoTXT: func(domain string) (string, error) {
return "", nil return "", nil
}, },
} }
ctx := &context.GlobalContext{
Gitea: &client,
Cache: &context.CacheContext{
RepositoryInformationCache: context.MakeRepoInfoCache(),
RepositoryPathCache: context.MakeRepoPathCache(),
},
}
_, _, err := RepoFromPath("example-user", "example-user.pages.example.org", "example-user.local", "index.html", ctx) _, _, err := RepoFromPath("example-user", "example-user.pages.example.org", "example-user.local", "index.html", &client)
if err == nil { if err == nil {
t.Fatal("gitea.Repository returned even though CNAME validation should fail") t.Fatal("Repository returned even though CNAME validation should fail")
} }
} }
func TestPickingRepositoryValidCNAME(t *testing.T) { func TestPickingRepositoryValidCNAME(t *testing.T) {
// Test that we're picking a repository, given a CNAME, if the CNAME validation succeeds. // Test that we're picking a repository, given a CNAME, if the CNAME validation succeeds.
defer clearCache()
log.SetLevel(log.DebugLevel) log.SetLevel(log.DebugLevel)
client := gitea.GiteaClient{ client := GiteaClient{
GetRepository: func(username, repositoryName string) (gitea.Repository, error) { getRepository: func(username, repositoryName string) (Repository, error) {
if username == "example-user" && repositoryName == "example-user.local" { if username == "example-user" && repositoryName == "example-user.local" {
return gitea.Repository{ return Repository{
Name: "example-user.local", Name: "example-user.local",
}, nil }, nil
} else { } else {
t.Fatalf("Called with unknown repository %s", repositoryName) t.Fatalf("Called with unknown repository %s", repositoryName)
return gitea.Repository{}, nil return Repository{}, nil
} }
}, },
HasBranch: func(username, repositoryName, branchName string) bool { hasBranch: func(username, repositoryName, branchName string) bool {
if username == "example-user" && repositoryName == "example-user.local" && branchName == "pages" { if username == "example-user" && repositoryName == "example-user.local" && branchName == "pages" {
return true return true
} }
@ -284,29 +248,22 @@ func TestPickingRepositoryValidCNAME(t *testing.T) {
return false return false
}, },
GetFile: func(username, repositoryName, branch, path string, since *time.Time) ([]byte, bool, error) { GetFile: func(username, repositoryName, branch, path string, since *time.Time) ([]byte, bool, error) {
if username == "example-user" && repositoryName == "example-user.local" && branch == "pages" && path == "rio.json" { if username == "example-user" && repositoryName == "example-user.local" && branch == "pages" && path == "CNAME" {
return []byte("{\"CNAME\": \"example-user.local\"}"), true, nil return []byte("example-user.local"), true, nil
} }
t.Fatalf("Invalid file requested: %s/%s@%s:%s", username, repositoryName, branch, path) t.Fatalf("Invalid file requested: %s/%s@%s:%s", username, repositoryName, branch, path)
return []byte{}, true, nil return []byte{}, true, nil
}, },
LookupCNAME: func(domain string) (string, error) { lookupCNAME: func(domain string) (string, error) {
return "", errors.New("No CNAME") return "", errors.New("No CNAME")
}, },
LookupRepoTXT: func(domain string) (string, error) { lookupRepoTXT: func(domain string) (string, error) {
return "", nil return "", nil
}, },
} }
ctx := &context.GlobalContext{
Gitea: &client,
Cache: &context.CacheContext{
RepositoryInformationCache: context.MakeRepoInfoCache(),
RepositoryPathCache: context.MakeRepoPathCache(),
},
}
repo, _, err := RepoFromPath("example-user", "example-user.local", "example-user.pages.example.org", "index.html", ctx) repo, _, err := RepoFromPath("example-user", "example-user.local", "example-user.pages.example.org", "index.html", &client)
if err != nil { if err != nil {
t.Fatalf("Error returned: %v", err) t.Fatalf("Error returned: %v", err)
} }
@ -318,19 +275,21 @@ func TestPickingRepositoryValidCNAME(t *testing.T) {
func TestPickingRepositoryValidCNAMEWithTXTLookup(t *testing.T) { func TestPickingRepositoryValidCNAMEWithTXTLookup(t *testing.T) {
// Test that we're picking a repository, given a CNAME, if the CNAME validation succeeds // Test that we're picking a repository, given a CNAME, if the CNAME validation succeeds
// and the TXT lookup returns something different. // and the TXT lookup returns something different.
defer clearCache()
log.SetLevel(log.DebugLevel) log.SetLevel(log.DebugLevel)
client := gitea.GiteaClient{ client := GiteaClient{
GetRepository: func(username, repositoryName string) (gitea.Repository, error) { getRepository: func(username, repositoryName string) (Repository, error) {
if username == "example-user" && repositoryName == "some-different-repository" { if username == "example-user" && repositoryName == "some-different-repository" {
return gitea.Repository{ return Repository{
Name: "some-different-repository", Name: "some-different-repository",
}, nil }, nil
} else { } else {
t.Fatalf("Called with unknown repository %s", repositoryName) t.Fatalf("Called with unknown repository %s", repositoryName)
return gitea.Repository{}, nil return Repository{}, nil
} }
}, },
HasBranch: func(username, repositoryName, branchName string) bool { hasBranch: func(username, repositoryName, branchName string) bool {
if username == "example-user" && repositoryName == "some-different-repository" && branchName == "pages" { if username == "example-user" && repositoryName == "some-different-repository" && branchName == "pages" {
return true return true
} }
@ -338,32 +297,25 @@ func TestPickingRepositoryValidCNAMEWithTXTLookup(t *testing.T) {
return false return false
}, },
GetFile: func(username, repositoryName, branch, path string, since *time.Time) ([]byte, bool, error) { GetFile: func(username, repositoryName, branch, path string, since *time.Time) ([]byte, bool, error) {
if username == "example-user" && repositoryName == "some-different-repository" && branch == "pages" && path == "rio.json" { if username == "example-user" && repositoryName == "some-different-repository" && branch == "pages" && path == "CNAME" {
return []byte("{\"CNAME\": \"example-user.local\"}"), true, nil return []byte("example-user.local"), true, nil
} }
t.Fatalf("Invalid file requested: %s/%s@%s:%s", username, repositoryName, branch, path) t.Fatalf("Invalid file requested: %s/%s@%s:%s", username, repositoryName, branch, path)
return []byte{}, true, nil return []byte{}, true, nil
}, },
LookupCNAME: func(domain string) (string, error) { lookupCNAME: func(domain string) (string, error) {
return "", errors.New("No CNAME") return "", errors.New("No CNAME")
}, },
LookupRepoTXT: func(domain string) (string, error) { lookupRepoTXT: func(domain string) (string, error) {
if domain == "example-user.local" { if domain == "example-user.local" {
return "some-different-repository", nil return "some-different-repository", nil
} }
return "", nil return "", nil
}, },
} }
ctx := &context.GlobalContext{
Gitea: &client,
Cache: &context.CacheContext{
RepositoryInformationCache: context.MakeRepoInfoCache(),
RepositoryPathCache: context.MakeRepoPathCache(),
},
}
repo, _, err := RepoFromPath("example-user", "example-user.local", "example-user.pages.example.org", "index.html", ctx) repo, _, err := RepoFromPath("example-user", "example-user.local", "example-user.pages.example.org", "index.html", &client)
if err != nil { if err != nil {
t.Fatalf("Error returned: %v", err) t.Fatalf("Error returned: %v", err)
} }
@ -375,18 +327,20 @@ func TestPickingRepositoryValidCNAMEWithTXTLookup(t *testing.T) {
func TestPickingRepositoryValidCNAMEWithTXTLookupAndSubdirectory(t *testing.T) { func TestPickingRepositoryValidCNAMEWithTXTLookupAndSubdirectory(t *testing.T) {
// Test that we're picking a repository, given a CNAME, if the CNAME validation succeeds // Test that we're picking a repository, given a CNAME, if the CNAME validation succeeds
// and the TXT lookup returns something different. Additionally, we now have a subdirectory // and the TXT lookup returns something different. Additionally, we now have a subdirectory
defer clearCache()
log.SetLevel(log.DebugLevel) log.SetLevel(log.DebugLevel)
client := gitea.GiteaClient{ client := GiteaClient{
GetRepository: func(username, repositoryName string) (gitea.Repository, error) { getRepository: func(username, repositoryName string) (Repository, error) {
if username == "example-user" && repositoryName == "some-different-repository" { if username == "example-user" && repositoryName == "some-different-repository" {
return gitea.Repository{ return Repository{
Name: "some-different-repository", Name: "some-different-repository",
}, nil }, nil
} }
return gitea.Repository{}, errors.New("Unknown repository") return Repository{}, errors.New("Unknown repository")
}, },
HasBranch: func(username, repositoryName, branchName string) bool { hasBranch: func(username, repositoryName, branchName string) bool {
if username == "example-user" && repositoryName == "some-different-repository" && branchName == "pages" { if username == "example-user" && repositoryName == "some-different-repository" && branchName == "pages" {
return true return true
} }
@ -394,32 +348,25 @@ func TestPickingRepositoryValidCNAMEWithTXTLookupAndSubdirectory(t *testing.T) {
return false return false
}, },
GetFile: func(username, repositoryName, branch, path string, since *time.Time) ([]byte, bool, error) { GetFile: func(username, repositoryName, branch, path string, since *time.Time) ([]byte, bool, error) {
if username == "example-user" && repositoryName == "some-different-repository" && branch == "pages" && path == "rio.json" { if username == "example-user" && repositoryName == "some-different-repository" && branch == "pages" && path == "CNAME" {
return []byte("{\"CNAME\": \"example-user.local\"}"), true, nil return []byte("example-user.local"), true, nil
} }
t.Fatalf("Invalid file requested: %s/%s@%s:%s", username, repositoryName, branch, path) t.Fatalf("Invalid file requested: %s/%s@%s:%s", username, repositoryName, branch, path)
return []byte{}, true, nil return []byte{}, true, nil
}, },
LookupCNAME: func(domain string) (string, error) { lookupCNAME: func(domain string) (string, error) {
return "", errors.New("No CNAME") return "", errors.New("No CNAME")
}, },
LookupRepoTXT: func(domain string) (string, error) { lookupRepoTXT: func(domain string) (string, error) {
if domain == "example-user.local" { if domain == "example-user.local" {
return "some-different-repository", nil return "some-different-repository", nil
} }
return "", nil return "", nil
}, },
} }
ctx := &context.GlobalContext{
Gitea: &client,
Cache: &context.CacheContext{
RepositoryInformationCache: context.MakeRepoInfoCache(),
RepositoryPathCache: context.MakeRepoPathCache(),
},
}
repo, _, err := RepoFromPath("example-user", "example-user.local", "example-user.pages.example.org", "blog/index.html", ctx) repo, _, err := RepoFromPath("example-user", "example-user.local", "example-user.pages.example.org", "blog/index.html", &client)
if err != nil { if err != nil {
t.Fatalf("Error returned: %v", err) t.Fatalf("Error returned: %v", err)
} }
@ -428,123 +375,31 @@ func TestPickingRepositoryValidCNAMEWithTXTLookupAndSubdirectory(t *testing.T) {
} }
} }
func TestHeaderParsingEmpty(t *testing.T) { func TestGetCSPForRepositoryNegativeIntegration(t *testing.T) {
// Test that we are correctly handling a repository with no headers. defer clearCache()
log.SetLevel(log.DebugLevel)
client := gitea.GiteaClient{
GetRepository: func(username, repositoryName string) (gitea.Repository, error) {
if username == "example-user" && repositoryName == "some-different-repository" {
return gitea.Repository{
Name: "some-different-repository",
}, nil
}
return gitea.Repository{}, errors.New("Unknown repository") httpClient := http.Client{Timeout: 10 * time.Second}
}, giteaClient, err := gitea.NewClient(
HasBranch: func(username, repositoryName, branchName string) bool { "https://git.polynom.me",
if username == "example-user" && repositoryName == "some-different-repository" && branchName == "pages" { gitea.SetHTTPClient(&httpClient),
return true gitea.SetToken(""),
gitea.SetUserAgent("rio-testing"),
)
if err != nil {
t.Fatalf("Failed to create Gitea client: %v", err)
} }
client := NewGiteaClient("https://git.polynom.me", giteaClient)
return false // The repository has no CSP file, so it should return the invalid value
}, defaultValue := "<INVALID>"
GetFile: func(username, repositoryName, branch, path string, since *time.Time) ([]byte, bool, error) { csp := GetCSPForRepository(
if username == "example-user" && repositoryName == "some-different-repository" && branch == "pages" && path == "rio.json" { "papatutuwawa",
return []byte("{\"CNAME\": \"example-user.local\"}"), true, nil "rio",
} defaultValue,
&client,
)
t.Fatalf("Invalid file requested: %s/%s@%s:%s", username, repositoryName, branch, path) if csp != defaultValue {
return []byte{}, true, nil t.Fatalf("Unexpected CSP returned: %s", csp)
},
LookupCNAME: func(domain string) (string, error) {
return "", errors.New("No CNAME")
},
LookupRepoTXT: func(domain string) (string, error) {
if domain == "example-user.local" {
return "some-different-repository", nil
}
return "", nil
},
}
ctx := &context.GlobalContext{
Gitea: &client,
Cache: &context.CacheContext{
RepositoryInformationCache: context.MakeRepoInfoCache(),
RepositoryPathCache: context.MakeRepoPathCache(),
},
}
info := GetRepositoryInformation("example-user", "some-different-repository", ctx)
if info == nil {
t.Fatalf("No repository information returned")
}
if len(info.Headers) > 0 {
t.Fatalf("Headers returned: %v", info.Headers)
}
}
func TestHeaderParsing(t *testing.T) {
// Test that we are correctly handling a repository with no headers.
log.SetLevel(log.DebugLevel)
client := gitea.GiteaClient{
GetRepository: func(username, repositoryName string) (gitea.Repository, error) {
if username == "example-user" && repositoryName == "some-different-repository" {
return gitea.Repository{
Name: "some-different-repository",
}, nil
}
return gitea.Repository{}, errors.New("Unknown repository")
},
HasBranch: func(username, repositoryName, branchName string) bool {
if username == "example-user" && repositoryName == "some-different-repository" && branchName == "pages" {
return true
}
return false
},
GetFile: func(username, repositoryName, branch, path string, since *time.Time) ([]byte, bool, error) {
if username == "example-user" && repositoryName == "some-different-repository" && branch == "pages" && path == "rio.json" {
return []byte("{\"CNAME\": \"example-user.local\", \"headers\": {\"X-Cool-Header\": \"Very nice!\"}}"), true, nil
}
t.Fatalf("Invalid file requested: %s/%s@%s:%s", username, repositoryName, branch, path)
return []byte{}, true, nil
},
LookupCNAME: func(domain string) (string, error) {
return "", errors.New("No CNAME")
},
LookupRepoTXT: func(domain string) (string, error) {
if domain == "example-user.local" {
return "some-different-repository", nil
}
return "", nil
},
}
ctx := &context.GlobalContext{
Gitea: &client,
Cache: &context.CacheContext{
RepositoryInformationCache: context.MakeRepoInfoCache(),
RepositoryPathCache: context.MakeRepoPathCache(),
},
}
info := GetRepositoryInformation("example-user", "some-different-repository", ctx)
if info == nil {
t.Fatalf("No repository information returned")
}
if len(info.Headers) != 1 {
t.Fatalf("len(info.Headers) != 1: %v", info.Headers)
}
header, found := info.Headers["X-Cool-Header"]
if !found {
t.Fatal("Header X-Cool-Header not found")
}
if header != "Very nice!" {
t.Fatalf("Invalid header value for X-Cool-Header: \"%s\"", header)
} }
} }

View File

@ -7,7 +7,6 @@ import (
"sync" "sync"
"git.polynom.me/rio/internal/certificates" "git.polynom.me/rio/internal/certificates"
"git.polynom.me/rio/internal/context"
"git.polynom.me/rio/internal/dns" "git.polynom.me/rio/internal/dns"
"git.polynom.me/rio/internal/repo" "git.polynom.me/rio/internal/repo"
@ -82,7 +81,7 @@ func getUsername(sni, pagesDomain string) (string, error) {
return dns.ExtractUsername(pagesDomain, sni), nil return dns.ExtractUsername(pagesDomain, sni), nil
} }
func MakeTlsConfig(pagesDomain, cachePath string, cache *certificates.CertificatesCache, acmeClient *lego.Client, ctx *context.GlobalContext) *tls.Config { func MakeTlsConfig(pagesDomain, cachePath string, cache *certificates.CertificatesCache, acmeClient *lego.Client, giteaClient *repo.GiteaClient) *tls.Config {
return &tls.Config{ return &tls.Config{
GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
// Validate that we should even care about this domain // Validate that we should even care about this domain
@ -100,7 +99,7 @@ func MakeTlsConfig(pagesDomain, cachePath string, cache *certificates.Certificat
if cert.IsValid() { if cert.IsValid() {
return cert.TlsCertificate, nil return cert.TlsCertificate, nil
} else { } else {
if !isPagesDomain && !repo.CanRequestCertificate(username, ctx) { if !isPagesDomain && !repo.CanRequestCertificate(username, giteaClient) {
log.Warnf( log.Warnf(
"Cannot renew certificate for %s because CanRequestCertificate(%s) returned false", "Cannot renew certificate for %s because CanRequestCertificate(%s) returned false",
info.ServerName, info.ServerName,
@ -129,7 +128,7 @@ func MakeTlsConfig(pagesDomain, cachePath string, cache *certificates.Certificat
return newCert.TlsCertificate, nil return newCert.TlsCertificate, nil
} }
} else { } else {
if !isPagesDomain && !repo.CanRequestCertificate(username, ctx) { if !isPagesDomain && !repo.CanRequestCertificate(username, giteaClient) {
log.Warnf( log.Warnf(
"Cannot request certificate for %s because CanRequestCertificate(%s) returned false", "Cannot request certificate for %s because CanRequestCertificate(%s) returned false",
info.ServerName, info.ServerName,