feat: Add tests for the repository picking behaviour
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
parent
3692168346
commit
493758f56f
@ -23,7 +23,7 @@ import (
|
|||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func handleSubdomain(domain string, cname string, path, giteaUrl string, giteaClient *gitea.Client, w http.ResponseWriter) {
|
func handleSubdomain(domain string, cname string, path, giteaUrl string, giteaClient *repo.GiteaClient, w http.ResponseWriter) {
|
||||||
hostParts := strings.Split(domain, ".")
|
hostParts := strings.Split(domain, ".")
|
||||||
username := hostParts[0]
|
username := hostParts[0]
|
||||||
|
|
||||||
@ -45,10 +45,10 @@ func handleSubdomain(domain string, cname string, path, giteaUrl string, giteaCl
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pages.ServeFile(username, repo.Name, path, giteaUrl, w)
|
pages.ServeFile(username, repo.Name, path, giteaClient, w)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Handler(pagesDomain, giteaUrl string, giteaClient *gitea.Client) http.HandlerFunc {
|
func Handler(pagesDomain, giteaUrl string, giteaClient *repo.GiteaClient) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, req *http.Request) {
|
return func(w http.ResponseWriter, req *http.Request) {
|
||||||
w.Header().Set("Server", "rio")
|
w.Header().Set("Server", "rio")
|
||||||
|
|
||||||
@ -97,12 +97,16 @@ func runServer(ctx *cli.Context) error {
|
|||||||
|
|
||||||
// Setup the Gitea stuff
|
// Setup the Gitea stuff
|
||||||
httpClient := http.Client{Timeout: 10 * time.Second}
|
httpClient := http.Client{Timeout: 10 * time.Second}
|
||||||
giteaClient, err := gitea.NewClient(
|
giteaApiClient, err := gitea.NewClient(
|
||||||
giteaUrl,
|
giteaUrl,
|
||||||
gitea.SetHTTPClient(&httpClient),
|
gitea.SetHTTPClient(&httpClient),
|
||||||
gitea.SetToken(""),
|
gitea.SetToken(""),
|
||||||
gitea.SetUserAgent("rio"),
|
gitea.SetUserAgent("rio"),
|
||||||
)
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
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")
|
||||||
@ -163,12 +167,12 @@ func runServer(ctx *cli.Context) error {
|
|||||||
certsFile,
|
certsFile,
|
||||||
&cache,
|
&cache,
|
||||||
acmeClient,
|
acmeClient,
|
||||||
giteaClient,
|
&giteaClient,
|
||||||
)
|
)
|
||||||
listener = tls.NewListener(listener, tlsConfig)
|
listener = tls.NewListener(listener, tlsConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := http.Serve(listener, Handler(domain, giteaUrl, giteaClient)); err != nil {
|
if err := http.Serve(listener, Handler(domain, giteaUrl, &giteaClient)); err != nil {
|
||||||
fmt.Printf("Listening failed")
|
fmt.Printf("Listening failed")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
3
go.mod
3
go.mod
@ -8,6 +8,7 @@ require (
|
|||||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
github.com/urfave/cli/v2 v2.27.1
|
github.com/urfave/cli/v2 v2.27.1
|
||||||
|
go.uber.org/mock v0.4.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@ -18,6 +19,8 @@ require (
|
|||||||
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
|
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
|
||||||
github.com/hashicorp/go-version v1.6.0 // indirect
|
github.com/hashicorp/go-version v1.6.0 // indirect
|
||||||
github.com/miekg/dns v1.1.55 // indirect
|
github.com/miekg/dns v1.1.55 // indirect
|
||||||
|
github.com/onsi/gomega v1.27.6 // indirect
|
||||||
|
github.com/petergtz/pegomock/v4 v4.0.0 // indirect
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||||
golang.org/x/crypto v0.17.0 // indirect
|
golang.org/x/crypto v0.17.0 // indirect
|
||||||
|
7
go.sum
7
go.sum
@ -17,12 +17,17 @@ github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyM
|
|||||||
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
|
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
|
||||||
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
|
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
|
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
|
||||||
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||||
github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo=
|
github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo=
|
||||||
github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
|
github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
|
||||||
|
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
|
||||||
|
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||||
|
github.com/petergtz/pegomock/v4 v4.0.0 h1:BIGMUof4NXc+xBbuFk0VBfK5Ls7DplcP+LWz4hfYWsY=
|
||||||
|
github.com/petergtz/pegomock/v4 v4.0.0/go.mod h1:Xscaw/kXYcuh9sGsns+If19FnSMMQy4Wz60YJTn3XOU=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
@ -38,6 +43,8 @@ github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6S
|
|||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
|
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
||||||
|
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
5
internal/constants/const.go
Normal file
5
internal/constants/const.go
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
package constants
|
||||||
|
|
||||||
|
const (
|
||||||
|
PagesBranch = "pages"
|
||||||
|
)
|
@ -1,22 +1,18 @@
|
|||||||
package pages
|
package pages
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"mime"
|
"mime"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"git.polynom.me/rio/internal/constants"
|
||||||
|
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
// The branch name on which files must reside.
|
|
||||||
PagesBranch = "pages"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
pageCache = cache.New(6*time.Hour, 1*time.Hour)
|
pageCache = cache.New(6*time.Hour, 1*time.Hour)
|
||||||
)
|
)
|
||||||
@ -31,7 +27,7 @@ func makePageContentCacheEntry(username, path string) string {
|
|||||||
return username + ":" + path
|
return username + ":" + path
|
||||||
}
|
}
|
||||||
|
|
||||||
func ServeFile(username, reponame, path, giteaUrl string, w http.ResponseWriter) {
|
func ServeFile(username, reponame, path string, giteaClient *repo.GiteaClient, w http.ResponseWriter) {
|
||||||
// Provide a default
|
// Provide a default
|
||||||
if path == "" {
|
if path == "" {
|
||||||
path = "/index.html"
|
path = "/index.html"
|
||||||
@ -47,30 +43,23 @@ func ServeFile(username, reponame, path, giteaUrl string, w http.ResponseWriter)
|
|||||||
var content []byte
|
var content []byte
|
||||||
var mimeType string
|
var mimeType string
|
||||||
var err error
|
var err error
|
||||||
|
var since *time.Time = nil
|
||||||
if found {
|
if found {
|
||||||
log.Debugf("Returning %s from cache", path)
|
log.Debugf("Returning %s from cache", path)
|
||||||
content = entry.(PageContentCache).Content
|
content = entry.(PageContentCache).Content
|
||||||
mimeType = entry.(PageContentCache).mimeType
|
mimeType = entry.(PageContentCache).mimeType
|
||||||
|
sinceRaw := entry.(PageContentCache).RequestedAt
|
||||||
|
since = &sinceRaw
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have to do the raw request manually because the Gitea SDK does not allow
|
content, changed, err := giteaClient.GetFile(
|
||||||
// passing the If-Modfied-Since header.
|
|
||||||
apiUrl := fmt.Sprintf(
|
|
||||||
"%s/api/v1/repos/%s/%s/raw/%s?ref=%s",
|
|
||||||
giteaUrl,
|
|
||||||
username,
|
username,
|
||||||
reponame,
|
reponame,
|
||||||
|
constants.PagesBranch,
|
||||||
path,
|
path,
|
||||||
PagesBranch,
|
since,
|
||||||
)
|
)
|
||||||
client := &http.Client{}
|
|
||||||
req, err := http.NewRequest("GET", apiUrl, nil)
|
|
||||||
if found {
|
|
||||||
since := entry.(PageContentCache).RequestedAt.Format(time.RFC1123)
|
|
||||||
log.Debugf("Found %s in cache. Adding '%s' as If-Modified-Since", key, since)
|
|
||||||
req.Header.Add("If-Modified-Since", since)
|
|
||||||
}
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !found {
|
if !found {
|
||||||
log.Errorf("Failed to get file %s/%s/%s (%s)", username, reponame, path, err)
|
log.Errorf("Failed to get file %s/%s/%s (%s)", username, reponame, path, err)
|
||||||
@ -84,10 +73,8 @@ func ServeFile(username, reponame, path, giteaUrl string, w http.ResponseWriter)
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
log.Debugf("Gitea API request returned %d", resp.StatusCode)
|
if found && !changed {
|
||||||
if found && resp.StatusCode == 302 {
|
|
||||||
log.Debugf("Page %s is unchanged and cached in memory", path)
|
log.Debugf("Page %s is unchanged and cached in memory", path)
|
||||||
w.WriteHeader(200)
|
w.WriteHeader(200)
|
||||||
w.Header().Set("Content-Type", mimeType)
|
w.Header().Set("Content-Type", mimeType)
|
||||||
@ -95,19 +82,6 @@ func ServeFile(username, reponame, path, giteaUrl string, w http.ResponseWriter)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Correctly propagate 404s.
|
|
||||||
if resp.StatusCode == 404 {
|
|
||||||
w.WriteHeader(404)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
content, err = io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Failed to get file %s/%s/%s (%s)", username, reponame, path, err)
|
|
||||||
w.WriteHeader(404)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
pathParts := strings.Split(path, ".")
|
pathParts := strings.Split(path, ".")
|
||||||
ext := pathParts[len(pathParts)-1]
|
ext := pathParts[len(pathParts)-1]
|
||||||
mimeType = mime.TypeByExtension("." + ext)
|
mimeType = mime.TypeByExtension("." + ext)
|
||||||
|
111
internal/repo/client.go
Normal file
111
internal/repo/client.go
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
package repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
|
||||||
|
"git.polynom.me/rio/internal/dns"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Returns true if the repository at <username>/<repository> exists, false if it
|
||||||
|
// does not.
|
||||||
|
type GetRepositoryMethod func(username, repositoryName string) (Repository, error)
|
||||||
|
|
||||||
|
// Returns <file content>, nil if the file exists at path <path> (relative to the repository) in
|
||||||
|
// <username>/<repository>@<branch> exists. If not, returns "", error.
|
||||||
|
type GetFileMethod func(username, repositoryName, branch, path string, since *time.Time) ([]byte, bool, error)
|
||||||
|
|
||||||
|
type LookupCNAMEMethod func(domain string) (string, error)
|
||||||
|
|
||||||
|
type LookupRepoTXTMethod func(domain string) (string, error)
|
||||||
|
|
||||||
|
type HasBranchMethod func(username, repositoryName, branchName string) bool
|
||||||
|
|
||||||
|
type HasUserMethod func(username string) bool
|
||||||
|
|
||||||
|
type Repository struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type GiteaClient struct {
|
||||||
|
getRepository GetRepositoryMethod
|
||||||
|
hasBranch HasBranchMethod
|
||||||
|
hasUser HasUserMethod
|
||||||
|
GetFile GetFileMethod
|
||||||
|
lookupCNAME LookupCNAMEMethod
|
||||||
|
lookupRepoTXT LookupRepoTXTMethod
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGiteaClient(giteaUrl string, giteaClient *gitea.Client) GiteaClient {
|
||||||
|
return GiteaClient{
|
||||||
|
getRepository: func(username, repositoryName string) (Repository, error) {
|
||||||
|
repo, _, err := giteaClient.GetRepo(username, repositoryName)
|
||||||
|
if err != nil {
|
||||||
|
return Repository{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return Repository{
|
||||||
|
Name: repo.Name,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
hasBranch: func(username, repositoryName, branchName string) bool {
|
||||||
|
res, _, err := giteaClient.ListRepoBranches(username, repositoryName, gitea.ListRepoBranchesOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, branch := range res {
|
||||||
|
if branch.Name == branchName {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
hasUser: func(username string) bool {
|
||||||
|
_, _, err := giteaClient.GetUserInfo(username)
|
||||||
|
return err == nil
|
||||||
|
},
|
||||||
|
GetFile: func(username, repositoryName, branch, path string, since *time.Time) ([]byte, bool, error) {
|
||||||
|
// We have to do the raw request manually because the Gitea SDK does not allow
|
||||||
|
// passing the If-Modfied-Since header.
|
||||||
|
apiUrl := fmt.Sprintf(
|
||||||
|
"%s/api/v1/repos/%s/%s/raw/%s?ref=%s",
|
||||||
|
giteaUrl,
|
||||||
|
username,
|
||||||
|
repositoryName,
|
||||||
|
path,
|
||||||
|
branch,
|
||||||
|
)
|
||||||
|
client := &http.Client{}
|
||||||
|
req, err := http.NewRequest("GET", apiUrl, nil)
|
||||||
|
if since != nil {
|
||||||
|
sinceFormat := since.Format(time.RFC1123)
|
||||||
|
req.Header.Add("If-Modified-Since", sinceFormat)
|
||||||
|
}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return []byte{}, true, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
content, err := io.ReadAll(resp.Body)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return []byte{}, true, err
|
||||||
|
} else if resp.StatusCode == 302 {
|
||||||
|
return []byte{}, false, nil
|
||||||
|
} else {
|
||||||
|
return content, true, err
|
||||||
|
}
|
||||||
|
},
|
||||||
|
lookupCNAME: func(domain string) (string, error) {
|
||||||
|
return dns.LookupCNAME(domain)
|
||||||
|
},
|
||||||
|
lookupRepoTXT: func(domain string) (string, error) {
|
||||||
|
return dns.LookupRepoTXT(domain)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
@ -1,14 +1,14 @@
|
|||||||
package repo
|
package repo
|
||||||
|
|
||||||
|
//go:generate mockgen -destination mock_repo_test.go -package repo code.gitea.io/sdk/gitea Client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.polynom.me/rio/internal/dns"
|
"git.polynom.me/rio/internal/constants"
|
||||||
"git.polynom.me/rio/internal/pages"
|
|
||||||
|
|
||||||
"code.gitea.io/sdk/gitea"
|
|
||||||
"github.com/patrickmn/go-cache"
|
"github.com/patrickmn/go-cache"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
@ -21,7 +21,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type PageCacheEntry struct {
|
type PageCacheEntry struct {
|
||||||
Repository *gitea.Repository
|
Repository Repository
|
||||||
Path string
|
Path string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,24 +33,36 @@ func makePageCacheKey(domain, path string) string {
|
|||||||
// / is not "", then it also verifies that the repository contains a "CNAME" with
|
// / is not "", then it also verifies that the repository contains a "CNAME" with
|
||||||
// / the value of @cname as its content. @host, @domain, and @path are passed for
|
// / the value of @cname as its content. @host, @domain, and @path are passed for
|
||||||
// / caching on success.
|
// / caching on success.
|
||||||
func lookupRepositoryAndCache(username, reponame, host, domain, path, cname string, giteaClient *gitea.Client) (*gitea.Repository, error) {
|
func lookupRepositoryAndCache(username, reponame, branchName, host, domain, path, cname string, giteaClient *GiteaClient) (*Repository, error) {
|
||||||
|
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 := giteaClient.GetRepo(username, reponame)
|
repo, err := giteaClient.getRepository(username, reponame)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !giteaClient.hasBranch(username, reponame, branchName) {
|
||||||
|
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")
|
||||||
file, _, err := giteaClient.GetFile(
|
file, _, err := giteaClient.GetFile(
|
||||||
username,
|
username,
|
||||||
repo.Name,
|
reponame,
|
||||||
pages.PagesBranch,
|
constants.PagesBranch,
|
||||||
"CNAME",
|
"CNAME",
|
||||||
false,
|
nil,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Could not verify CNAME of %s/%s: %v\n", username, repo.Name, err)
|
log.Errorf(
|
||||||
|
"Could not verify CNAME of %s/%s@%s: %v\n",
|
||||||
|
username,
|
||||||
|
reponame,
|
||||||
|
constants.PagesBranch,
|
||||||
|
err,
|
||||||
|
)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,6 +70,8 @@ func lookupRepositoryAndCache(username, reponame, host, domain, path, cname stri
|
|||||||
string(file[:]),
|
string(file[:]),
|
||||||
"\n",
|
"\n",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
log.Debugf("CNAME Content: %s", cnameContent)
|
||||||
if cnameContent != cname {
|
if cnameContent != cname {
|
||||||
return nil, errors.New("CNAME mismatch")
|
return nil, errors.New("CNAME mismatch")
|
||||||
}
|
}
|
||||||
@ -72,10 +86,10 @@ func lookupRepositoryAndCache(username, reponame, host, domain, path, cname stri
|
|||||||
},
|
},
|
||||||
cache.DefaultExpiration,
|
cache.DefaultExpiration,
|
||||||
)
|
)
|
||||||
return repo, nil
|
return &repo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func RepoFromPath(username, host, cname, path string, giteaClient *gitea.Client) (*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
|
||||||
@ -83,16 +97,31 @@ func RepoFromPath(username, host, cname, path string, giteaClient *gitea.Client)
|
|||||||
entry, found := pathCache.Get(key)
|
entry, found := pathCache.Get(key)
|
||||||
if found {
|
if found {
|
||||||
pageEntry := entry.(PageCacheEntry)
|
pageEntry := entry.(PageCacheEntry)
|
||||||
return pageEntry.Repository, pageEntry.Path, nil
|
return &pageEntry.Repository, pageEntry.Path, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow specifying the repository name in the TXT record
|
||||||
|
reponame := ""
|
||||||
|
if cname != "" {
|
||||||
|
repoLookup, err := giteaClient.lookupRepoTXT(cname)
|
||||||
|
if err == nil && repoLookup != "" {
|
||||||
|
log.Infof(
|
||||||
|
"TXT lookup for %s resulted in choosing repository %s",
|
||||||
|
cname,
|
||||||
|
repoLookup,
|
||||||
|
)
|
||||||
|
reponame = repoLookup
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pathParts := strings.Split(path, "/")
|
pathParts := strings.Split(path, "/")
|
||||||
if len(pathParts) > 1 {
|
if reponame == "" && len(pathParts) > 1 {
|
||||||
log.Debugf("Trying repository %s", pathParts[0])
|
log.Debugf("Trying repository %s", pathParts[0])
|
||||||
modifiedPath := strings.Join(pathParts[1:], "/")
|
modifiedPath := strings.Join(pathParts[1:], "/")
|
||||||
repo, err := lookupRepositoryAndCache(
|
repo, err := lookupRepositoryAndCache(
|
||||||
username,
|
username,
|
||||||
pathParts[0],
|
pathParts[0],
|
||||||
|
constants.PagesBranch,
|
||||||
host,
|
host,
|
||||||
domain,
|
domain,
|
||||||
modifiedPath,
|
modifiedPath,
|
||||||
@ -104,29 +133,14 @@ func RepoFromPath(username, host, cname, path string, giteaClient *gitea.Client)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow specifying the repository name in the TXT record
|
if reponame == "" {
|
||||||
reponame := domain
|
reponame = domain
|
||||||
lookupDomain := domain
|
|
||||||
if cname != "" {
|
|
||||||
lookupDomain = cname
|
|
||||||
}
|
}
|
||||||
repoLookup, err := dns.LookupRepoTXT(lookupDomain)
|
|
||||||
if err != nil && repoLookup != "" {
|
|
||||||
log.Infof(
|
|
||||||
"TXT lookup for %s resulted in choosing repository %s",
|
|
||||||
lookupDomain,
|
|
||||||
repoLookup,
|
|
||||||
)
|
|
||||||
reponame = repoLookup
|
|
||||||
} else if cname != "" {
|
|
||||||
// Allow naming the repository "example.org" (But give the TXT record preference)
|
|
||||||
reponame = cname
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debugf("Trying repository %s/%s", username, reponame)
|
log.Debugf("Trying repository %s/%s", username, reponame)
|
||||||
repo, err := lookupRepositoryAndCache(
|
repo, err := lookupRepositoryAndCache(
|
||||||
username,
|
username,
|
||||||
reponame,
|
reponame,
|
||||||
|
constants.PagesBranch,
|
||||||
host,
|
host,
|
||||||
domain,
|
domain,
|
||||||
path,
|
path,
|
||||||
@ -139,15 +153,14 @@ func RepoFromPath(username, host, cname, path string, giteaClient *gitea.Client)
|
|||||||
// 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, giteaClient *gitea.Client) bool {
|
func CanRequestCertificate(username string, giteaClient *GiteaClient) bool {
|
||||||
if _, found := userCache.Get(username); found {
|
if _, found := userCache.Get(username); found {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
user, _, err := giteaClient.GetUserInfo(username)
|
hasUser := giteaClient.hasUser(username)
|
||||||
if user != nil && err == nil {
|
if hasUser {
|
||||||
userCache.Set(username, true, cache.DefaultExpiration)
|
userCache.Set(username, true, cache.DefaultExpiration)
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
return false
|
return hasUser
|
||||||
}
|
}
|
||||||
|
@ -1,39 +1,373 @@
|
|||||||
package repo
|
package repo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/sdk/gitea"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
func clearCache() {
|
||||||
giteaClient, _ = gitea.NewClient(
|
pathCache.Flush()
|
||||||
"https://git.polynom.me",
|
userCache.Flush()
|
||||||
gitea.SetHTTPClient(&http.Client{Timeout: 10 * time.Second}),
|
}
|
||||||
gitea.SetToken(""),
|
|
||||||
gitea.SetUserAgent("rio/testing"),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCanRequestCertificatePositiveUser(t *testing.T) {
|
func TestPickingCorrectRepositoryDefault(t *testing.T) {
|
||||||
res := CanRequestCertificate("papatutuwawa", giteaClient)
|
// Test that we default to the <username>.<pages domain> repository, if we have only
|
||||||
if !res {
|
// one path component.
|
||||||
t.Fatalf("User papatutuwawa should be servable")
|
defer clearCache()
|
||||||
|
|
||||||
|
log.SetLevel(log.DebugLevel)
|
||||||
|
client := GiteaClient{
|
||||||
|
getRepository: func(username, repositoryName string) (Repository, error) {
|
||||||
|
if username != "example-user" {
|
||||||
|
t.Fatalf("Called with unknown user %s", username)
|
||||||
|
}
|
||||||
|
if repositoryName != "example-user.pages.example.org" {
|
||||||
|
t.Fatalf("Called with unknown repository %s", repositoryName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Repository{}, nil
|
||||||
|
},
|
||||||
|
hasBranch: func(username, repositoryName, branchName string) bool {
|
||||||
|
if username == "example-user" && repositoryName == "example-user.pages.example.org" && branchName == "pages" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
GetFile: func(username, repositoryName, branch, path string, since *time.Time) ([]byte, bool, error) {
|
||||||
|
t.Fatal("getFile called")
|
||||||
|
return []byte{}, true, nil
|
||||||
|
},
|
||||||
|
lookupCNAME: func(domain string) (string, error) {
|
||||||
|
t.Fatal("lookupCNAME called")
|
||||||
|
return "", nil
|
||||||
|
},
|
||||||
|
lookupRepoTXT: func(domain string) (string, error) {
|
||||||
|
t.Fatal("lookupRepoTXT called")
|
||||||
|
return "", nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
res, path, err := RepoFromPath("example-user", "example-user.pages.example.org", "", "index.html", &client)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("An error occured: %v", err)
|
||||||
|
}
|
||||||
|
if res == nil {
|
||||||
|
t.Fatal("Result is nil")
|
||||||
|
}
|
||||||
|
if path != "index.html" {
|
||||||
|
t.Fatalf("Returned path is invalid: %s", path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCanRequestCertificatePositiveOrganisation(t *testing.T) {
|
func TestPickingCorrectRepositoryDefaultSubdirectory(t *testing.T) {
|
||||||
res := CanRequestCertificate("moxxy", giteaClient)
|
// Test that we return the default repository when the first path component does
|
||||||
if !res {
|
// not correspong to an existing repository.
|
||||||
t.Fatalf("Organisation moxxy should be servable")
|
defer clearCache()
|
||||||
|
|
||||||
|
log.SetLevel(log.DebugLevel)
|
||||||
|
client := GiteaClient{
|
||||||
|
getRepository: func(username, repositoryName string) (Repository, error) {
|
||||||
|
if username != "example-user" {
|
||||||
|
t.Fatalf("Called with unknown user %s", username)
|
||||||
|
}
|
||||||
|
if repositoryName == "assets" {
|
||||||
|
return Repository{}, errors.New("Repository does not exist")
|
||||||
|
} else if repositoryName == "example-user.pages.example.org" {
|
||||||
|
return Repository{}, nil
|
||||||
|
} else {
|
||||||
|
t.Fatalf("Called with unknown repository %s", repositoryName)
|
||||||
|
return Repository{}, nil
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hasBranch: func(username, repositoryName, branchName string) bool {
|
||||||
|
if username == "example-user" && repositoryName == "example-user.pages.example.org" && branchName == "pages" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
GetFile: func(username, repositoryName, branch, path string, since *time.Time) ([]byte, bool, error) {
|
||||||
|
t.Fatal("getFile called")
|
||||||
|
return []byte{}, true, nil
|
||||||
|
},
|
||||||
|
lookupCNAME: func(domain string) (string, error) {
|
||||||
|
t.Fatal("lookupCNAME called")
|
||||||
|
return "", nil
|
||||||
|
},
|
||||||
|
lookupRepoTXT: func(domain string) (string, error) {
|
||||||
|
t.Fatal("lookupRepoTXT called")
|
||||||
|
return "", nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
res, path, err := RepoFromPath("example-user", "example-user.pages.example.org", "", "assets/index.css", &client)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("An error occured: %v", err)
|
||||||
|
}
|
||||||
|
if res == nil {
|
||||||
|
t.Fatal("Result is nil")
|
||||||
|
}
|
||||||
|
if path != "assets/index.css" {
|
||||||
|
t.Fatalf("Returned path is invalid: %s", path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCanRequestCertificateNegative(t *testing.T) {
|
func TestPickingCorrectRepositorySubdirectoryNoPagesBranch(t *testing.T) {
|
||||||
res := CanRequestCertificate("user-who-does-not-exist", giteaClient)
|
// Test that we're picking the correct repository when the first path component
|
||||||
if res {
|
// returns a repository without a pages branch.
|
||||||
t.Fatalf("User user-who-does-not-exist should not be servable")
|
defer clearCache()
|
||||||
|
|
||||||
|
log.SetLevel(log.DebugLevel)
|
||||||
|
client := GiteaClient{
|
||||||
|
getRepository: func(username, repositoryName string) (Repository, error) {
|
||||||
|
if username != "example-user" {
|
||||||
|
t.Fatalf("Called with unknown user %s", username)
|
||||||
|
}
|
||||||
|
if repositoryName == "blog" {
|
||||||
|
return Repository{
|
||||||
|
Name: "blog",
|
||||||
|
}, nil
|
||||||
|
} else if repositoryName == "example-user.pages.example.org" {
|
||||||
|
return Repository{
|
||||||
|
Name: "example-user.pages.example.org",
|
||||||
|
}, nil
|
||||||
|
} else {
|
||||||
|
t.Fatalf("Called with unknown repository %s", repositoryName)
|
||||||
|
return Repository{}, nil
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hasBranch: func(username, repositoryName, branchName string) bool {
|
||||||
|
if username == "example-user" && repositoryName == "example-user.pages.example.org" && branchName == "pages" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
GetFile: func(username, repositoryName, branch, path string, since *time.Time) ([]byte, bool, error) {
|
||||||
|
t.Fatal("getFile called")
|
||||||
|
return []byte{}, true, nil
|
||||||
|
},
|
||||||
|
lookupCNAME: func(domain string) (string, error) {
|
||||||
|
t.Fatal("lookupCNAME called")
|
||||||
|
return "", nil
|
||||||
|
},
|
||||||
|
lookupRepoTXT: func(domain string) (string, error) {
|
||||||
|
t.Fatal("lookupRepoTXT called")
|
||||||
|
return "", nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
res, path, err := RepoFromPath("example-user", "example-user.pages.example.org", "", "blog/post1.html", &client)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("An error occured: %v", err)
|
||||||
|
}
|
||||||
|
if res == nil {
|
||||||
|
t.Fatal("Result is nil")
|
||||||
|
}
|
||||||
|
if res.Name != "example-user.pages.example.org" {
|
||||||
|
t.Fatalf("Invalid repository selected: %s", res.Name)
|
||||||
|
}
|
||||||
|
if path != "blog/post1.html" {
|
||||||
|
t.Fatalf("Returned path is invalid: %s", path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPickingNoRepositoryInvalidCNAME(t *testing.T) {
|
||||||
|
// Test that we're not picking a repository if the CNAME validation fails.
|
||||||
|
defer clearCache()
|
||||||
|
|
||||||
|
log.SetLevel(log.DebugLevel)
|
||||||
|
client := GiteaClient{
|
||||||
|
getRepository: func(username, repositoryName string) (Repository, error) {
|
||||||
|
if username == "example-user" && repositoryName == "example-user.pages.example.org" {
|
||||||
|
return Repository{
|
||||||
|
Name: "example-user.pages.example.org",
|
||||||
|
}, nil
|
||||||
|
} else {
|
||||||
|
t.Fatalf("Called with unknown repository %s", repositoryName)
|
||||||
|
return Repository{}, nil
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hasBranch: func(username, repositoryName, branchName string) bool {
|
||||||
|
if username == "example-user" && repositoryName == "example-user.pages.example.org" && branchName == "pages" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
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 == "CNAME" {
|
||||||
|
return []byte("some-other-domain.local"), 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) {
|
||||||
|
return "", nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err := RepoFromPath("example-user", "example-user.pages.example.org", "example-user.local", "index.html", &client)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Repository returned even though CNAME validation should fail")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPickingRepositoryValidCNAME(t *testing.T) {
|
||||||
|
// Test that we're picking a repository, given a CNAME, if the CNAME validation succeeds.
|
||||||
|
defer clearCache()
|
||||||
|
|
||||||
|
log.SetLevel(log.DebugLevel)
|
||||||
|
client := GiteaClient{
|
||||||
|
getRepository: func(username, repositoryName string) (Repository, error) {
|
||||||
|
if username == "example-user" && repositoryName == "example-user.pages.example.org" {
|
||||||
|
return Repository{
|
||||||
|
Name: "example-user.pages.example.org",
|
||||||
|
}, nil
|
||||||
|
} else {
|
||||||
|
t.Fatalf("Called with unknown repository %s", repositoryName)
|
||||||
|
return Repository{}, nil
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hasBranch: func(username, repositoryName, branchName string) bool {
|
||||||
|
if username == "example-user" && repositoryName == "example-user.pages.example.org" && branchName == "pages" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
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 == "CNAME" {
|
||||||
|
return []byte("example-user.local"), 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) {
|
||||||
|
return "", nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
repo, _, err := RepoFromPath("example-user", "example-user.pages.example.org", "example-user.local", "index.html", &client)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error returned: %v", err)
|
||||||
|
}
|
||||||
|
if repo.Name != "example-user.pages.example.org" {
|
||||||
|
t.Fatalf("Invalid repository name returned: %s", repo.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPickingRepositoryValidCNAMEWithTXTLookup(t *testing.T) {
|
||||||
|
// Test that we're picking a repository, given a CNAME, if the CNAME validation succeeds
|
||||||
|
// and the TXT lookup returns something different.
|
||||||
|
defer clearCache()
|
||||||
|
|
||||||
|
log.SetLevel(log.DebugLevel)
|
||||||
|
client := GiteaClient{
|
||||||
|
getRepository: func(username, repositoryName string) (Repository, error) {
|
||||||
|
if username == "example-user" && repositoryName == "some-different-repository" {
|
||||||
|
return Repository{
|
||||||
|
Name: "some-different-repository",
|
||||||
|
}, nil
|
||||||
|
} else {
|
||||||
|
t.Fatalf("Called with unknown repository %s", repositoryName)
|
||||||
|
return Repository{}, nil
|
||||||
|
}
|
||||||
|
},
|
||||||
|
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 == "CNAME" {
|
||||||
|
return []byte("example-user.local"), 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
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
repo, _, err := RepoFromPath("example-user", "example-user.pages.example.org", "example-user.local", "index.html", &client)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error returned: %v", err)
|
||||||
|
}
|
||||||
|
if repo.Name != "some-different-repository" {
|
||||||
|
t.Fatalf("Invalid repository name returned: %s", repo.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPickingRepositoryValidCNAMEWithTXTLookupAndSubdirectory(t *testing.T) {
|
||||||
|
// 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
|
||||||
|
defer clearCache()
|
||||||
|
|
||||||
|
log.SetLevel(log.DebugLevel)
|
||||||
|
client := GiteaClient{
|
||||||
|
getRepository: func(username, repositoryName string) (Repository, error) {
|
||||||
|
if username == "example-user" && repositoryName == "some-different-repository" {
|
||||||
|
return Repository{
|
||||||
|
Name: "some-different-repository",
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return 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 == "CNAME" {
|
||||||
|
return []byte("example-user.local"), 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
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
repo, _, err := RepoFromPath("example-user", "example-user.pages.example.org", "example-user.local", "blog/index.html", &client)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error returned: %v", err)
|
||||||
|
}
|
||||||
|
if repo.Name != "some-different-repository" {
|
||||||
|
t.Fatalf("Invalid repository name returned: %s", repo.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"git.polynom.me/rio/internal/dns"
|
"git.polynom.me/rio/internal/dns"
|
||||||
"git.polynom.me/rio/internal/repo"
|
"git.polynom.me/rio/internal/repo"
|
||||||
|
|
||||||
"code.gitea.io/sdk/gitea"
|
|
||||||
"github.com/go-acme/lego/v4/lego"
|
"github.com/go-acme/lego/v4/lego"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
@ -62,7 +61,7 @@ func getDomainKey(domain, pagesDomain string) string {
|
|||||||
return domain
|
return domain
|
||||||
}
|
}
|
||||||
|
|
||||||
func MakeTlsConfig(pagesDomain, cachePath string, cache *certificates.CertificatesCache, acmeClient *lego.Client, giteaClient *gitea.Client) *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
|
||||||
|
Loading…
Reference in New Issue
Block a user