mirror of
https://codeberg.org/git-pages/git-pages.git
synced 2026-08-17 09:26:04 +00:00
Implement pull request preview authorization.
Requires `feature = ["preview"]`.
This commit is contained in:
@@ -121,17 +121,19 @@ The authorization flow for content updates (`PUT`, `PATCH`, `DELETE`, `POST` req
|
||||
- **`Basic` scheme:** Request includes an `Authorization: Basic <basic>` header, where `<basic>` is equal to `Base64("Pages:<token>")`. (Useful for non-Forgejo forges.)
|
||||
3. **DNS Allowlist:** If the method is `PUT` or `POST`, and the request URL is `scheme://<user>.<host>/`, and a TXT record lookup at `_git-pages-repository.<host>` returns a set of well-formed absolute URLs, and (for `PUT` requests) the body contains a repository URL or (for `POST` requests) the body contains a GitHub-style webhook payload, and the requested clone URLs is contained in this set of URLs, and the requested branch is `pages`, the request is authorized.
|
||||
4. **Wildcard Match (content):** If the method is `POST`, and the body contains a GitHub-style webhook payload, and a `[[wildcard]]` configuration section exists such that `[[wildcard]].domain` is a suffix of the site hostname (compared label-wise), and the body contains a repository URL, and the requested clone URL is a *matching* clone URL, and the requested branch is a *matching* branch, the request is authorized.
|
||||
- **Index repository:** If the request URL is `scheme://<user>.<host>/`: a *matching* clone URL is computed by templating `[[wildcard]].clone-url` with `<user>` and `<project>`, where `<project>` is computed by templating `[[wildcard]].index-repo` with `<user>`, and `[[wildcard]]` is the section where the match occurred; and a *matching* branch is specified by `[[wildcard]].index-repo-branch`.
|
||||
- **Project repository:** If the request URL is `scheme://<user>.<host>/<project>/`: a *matching* clone URL is computed by templating `[[wildcard]].clone-url` with `<user>` and `<project>`, and `[[wildcard]]` is the section where the match occurred; and a *matching* branch is `pages`.
|
||||
- **Index repository:** If the request URL is `scheme://<user>.<host>/`: a *matching* clone URL is computed by templating `[[wildcard]].clone-url` with `<user>` and `<project>`, where `<project>` is computed by templating `[[wildcard]].index-repo` with `<user>`; and a *matching* branch is specified by `[[wildcard]].index-repo-branch`.
|
||||
- **Project repository:** If the request URL is `scheme://<user>.<host>/<project>/`: a *matching* clone URL is computed by templating `[[wildcard]].clone-url` with `<user>` and `<project>`; and a *matching* branch is `pages`.
|
||||
5. **Forge Authorization (wildcard):** If the method is `PUT` or `PATCH` or `DELETE`, and (unless the method is `DELETE`) the body contains an archive, and a `[[wildcard]]` configuration section exists such that `[[wildcard]].domain` is a suffix of the site hostname (compared label-wise), and `[[wildcard]].authorization` is defined, and the request includes a `Forge-Authorization:` header, and the header (when forwarded as `Authorization:`) grants push permissions to a repository at the *matching* clone URL (as defined above) as determined by an API call to the forge, the request is authorized.
|
||||
6. **Forge Authorization (DNS allowlist):** If the method is `PUT` or `PATCH` or `DELETE`, and (unless the method is `DELETE`) the body contains an archive, and the request URL is `scheme://<host>/`, and a TXT record lookup at `_git-pages-forge-allowlist.<host>` returns a set of well-formed absolute URLs, and the request includes a `Forge-Authorization:` header, and the header (when forwarded as `Authorization:`) grants push permissions to a repository at any of the URLs in the TXT records as determined by an API call to the forge, the request is authorized.
|
||||
7. **Default Deny:** Otherwise, the request is not authorized.
|
||||
6. **Forge Authorization (wildcard, preview):** If the method is `PUT` or `PATCH` or `DELETE`, and (unless the method is `DELETE`) the body contains an archive, and a `[[wildcard]]` configuration section exists such that `[[wildcard]].preview-domain` is a suffix of the site hostname (compared label-wise), and `[[wildcard]].authorization` is `forgejo`, and the request includes a `Forge-Authorization:` header, and the header (when forwarded as `Authorization:`) is mapped by the forge (via the `/api/v1/actions/run` endpoint) to a Forgejo Actions workflow run, and the workflow run corresponds to a *matching* pull request, the request is authorized.
|
||||
- If the request URL is `scheme://<user>.<host>/<project>@<number>/`: a *matching* pull request has a number `<number>` and belongs to the repository with a clone URL computed by templating `[[wildcard]].clone-url` with `<user>` and `<project>`.
|
||||
7. **Forge Authorization (DNS allowlist):** If the method is `PUT` or `PATCH` or `DELETE`, and (unless the method is `DELETE`) the body contains an archive, and the request URL is `scheme://<host>/`, and a TXT record lookup at `_git-pages-forge-allowlist.<host>` returns a set of well-formed absolute URLs, and the request includes a `Forge-Authorization:` header, and the header (when forwarded as `Authorization:`) grants push permissions to a repository at any of the URLs in the TXT records as determined by an API call to the forge, the request is authorized.
|
||||
8. **Default Deny:** Otherwise, the request is not authorized.
|
||||
|
||||
The authorization flow for metadata retrieval (`GET` requests with site paths starting with `.git-pages/`) in the following order, with the first of multiple applicable rule taking precedence:
|
||||
|
||||
1. **Development Mode:** Same as for content updates.
|
||||
2. **DNS Challenge:** Same as for content updates.
|
||||
3. **Wildcard Match (metadata):** If a `[[wildcard]]` configuration section exists where the suffix of a hostname (compared label-wise) is equal to `[[wildcard]].domain`, and the site never uses the `Basic-Auth:` pseudo-header, the request is authorized.
|
||||
3. **Wildcard Match (metadata):** If a `[[wildcard]]` configuration section exists where the suffix of a hostname (compared label-wise) is equal to `[[wildcard]].domain` or `[[wildcard]].preview-domain`, and the site never uses the `Basic-Auth:` pseudo-header, the request is authorized.
|
||||
4. **Default Deny:** Otherwise, the request is not authorized.
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -108,7 +108,7 @@ func (record *AuditRecord) DescribePrincipal() string {
|
||||
if record.Principal.GetForgeUser() != nil {
|
||||
items = append(items, fmt.Sprintf("%s/%s(%d)",
|
||||
record.Principal.GetForgeUser().GetOrigin(),
|
||||
record.Principal.GetForgeUser().GetHandle(),
|
||||
record.Principal.GetForgeUser().GetName(),
|
||||
record.Principal.GetForgeUser().GetId()))
|
||||
}
|
||||
if record.Principal.GetRepoUrl() != "" {
|
||||
|
||||
+48
-5
@@ -256,7 +256,7 @@ func authorizeWildcardMatchHost(r *http.Request, pattern *WildcardPattern) (*Aut
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, found := pattern.Matches(host); found {
|
||||
if _, found := pattern.Matches(host, WildcardDomainAny); found {
|
||||
return &Authorization{
|
||||
repoURLs: []string{},
|
||||
branch: "",
|
||||
@@ -281,7 +281,7 @@ func authorizeWildcardMatchSite(r *http.Request, pattern *WildcardPattern) (*Aut
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if userName, found := pattern.Matches(host); found {
|
||||
if userName, found := pattern.Matches(host, WildcardDomainPrimary); found {
|
||||
repoURL, branch := pattern.ApplyTemplate(userName, projectName)
|
||||
return &Authorization{repoURLs: []string{repoURL}, branch: branch}, nil
|
||||
} else {
|
||||
@@ -583,7 +583,7 @@ func authorizeGogsUser(repoURL string, forgeToken string) (*Authorization, error
|
||||
|
||||
ownerAndRepo := strings.TrimPrefix(path.Clean(parsedRepoURL.Path), "/")
|
||||
owner, _, found := strings.Cut(ownerAndRepo, "/")
|
||||
if found && owner == authorizedUser.GetHandle() {
|
||||
if found && owner == authorizedUser.GetName() {
|
||||
// The authorized user owns any repository in their namespace.
|
||||
return &Authorization{
|
||||
repoURLs: []string{repoURL},
|
||||
@@ -601,6 +601,31 @@ func authorizeGogsUser(repoURL string, forgeToken string) (*Authorization, error
|
||||
}, nil
|
||||
}
|
||||
|
||||
func authorizeForgejoActionRun(repoURL string, forgeToken string, prNumber string) (
|
||||
*Authorization, error,
|
||||
) {
|
||||
parsedRepoURL, err := url.Parse(repoURL)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
actionRun, err := FetchForgejoActionRun(parsedRepoURL, forgeToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if actionRun.PullRequest == nil {
|
||||
return nil, AuthError{http.StatusUnauthorized, "workflow run not triggered by pull request"}
|
||||
}
|
||||
if fmt.Sprintf("%d", actionRun.PullRequest.Number) != prNumber {
|
||||
return nil, AuthError{http.StatusUnauthorized, "pull request number does not match"}
|
||||
}
|
||||
|
||||
return &Authorization{
|
||||
repoURLs: []string{repoURL},
|
||||
forgeUser: actionRun.TriggerUser,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Validates a provided forge token against a repository URL constructed by mapping the host
|
||||
// and project name via the `[[wildcard]]` section of the configuration file.
|
||||
func authorizeForgeWildcard(r *http.Request) (*Authorization, error) {
|
||||
@@ -622,8 +647,9 @@ func authorizeForgeWildcard(r *http.Request) (*Authorization, error) {
|
||||
var errs []error
|
||||
for _, pattern := range wildcards {
|
||||
if pattern.Authorization {
|
||||
if userName, found := pattern.Matches(host); found {
|
||||
repoURL, branch := pattern.ApplyTemplate(userName, projectName)
|
||||
if userName, found := pattern.Matches(host, WildcardDomainPrimary); found {
|
||||
repoName := projectName
|
||||
repoURL, branch := pattern.ApplyTemplate(userName, repoName)
|
||||
auth, err := authorizeGogsUser(repoURL, forgeToken)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
@@ -632,6 +658,23 @@ func authorizeForgeWildcard(r *http.Request) (*Authorization, error) {
|
||||
return auth, nil
|
||||
}
|
||||
}
|
||||
|
||||
if userName, found := pattern.Matches(host, WildcardDomainPreview); found {
|
||||
if repoName, atSuffix, foundAt := strings.Cut(projectName, "@"); !foundAt {
|
||||
errs = append([]error{
|
||||
AuthError{http.StatusUnauthorized, "not a preview project name (no `@`)"},
|
||||
}, errs...)
|
||||
} else {
|
||||
repoURL, branch := pattern.ApplyTemplate(userName, repoName)
|
||||
auth, err := authorizeForgejoActionRun(repoURL, forgeToken, atSuffix)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
} else {
|
||||
auth.branch = branch
|
||||
return auth, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(errs) == 0 {
|
||||
|
||||
@@ -80,6 +80,7 @@ type ServerConfig struct {
|
||||
|
||||
type WildcardConfig struct {
|
||||
Domain string `toml:"domain"`
|
||||
PreviewDomain string `toml:"preview-domain"`
|
||||
CloneURL string `toml:"clone-url"` // URL template, not an exact URL
|
||||
IndexRepo string `toml:"index-repo" default:"pages"`
|
||||
IndexRepoBranch string `toml:"index-repo-branch" default:"pages"`
|
||||
|
||||
+116
-6
@@ -48,12 +48,12 @@ func FetchGogsAuthorizedUser(baseURL *url.URL, authorization string) (*ForgeUser
|
||||
),
|
||||
}
|
||||
}
|
||||
decoder := json.NewDecoder(response.Body)
|
||||
|
||||
var userInfo struct {
|
||||
ID int64
|
||||
Login string
|
||||
ID int64 `json:"id"`
|
||||
Login string `json:"login"`
|
||||
}
|
||||
decoder := json.NewDecoder(response.Body)
|
||||
if err := decoder.Decode(&userInfo); err != nil {
|
||||
return nil, errors.Join(AuthError{
|
||||
http.StatusServiceUnavailable,
|
||||
@@ -68,7 +68,7 @@ func FetchGogsAuthorizedUser(baseURL *url.URL, authorization string) (*ForgeUser
|
||||
return &ForgeUser{
|
||||
Origin: &origin,
|
||||
Id: &userInfo.ID,
|
||||
Handle: &userInfo.Login,
|
||||
Name: &userInfo.Login,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -105,9 +105,13 @@ func CheckGogsRepositoryPushPermission(baseURL *url.URL, authorization string) e
|
||||
),
|
||||
}
|
||||
}
|
||||
decoder := json.NewDecoder(response.Body)
|
||||
|
||||
var repositoryInfo struct{ Permissions struct{ Push bool } }
|
||||
var repositoryInfo struct {
|
||||
Permissions struct {
|
||||
Push bool `json:"push"`
|
||||
} `json:"permissions"`
|
||||
}
|
||||
decoder := json.NewDecoder(response.Body)
|
||||
if err := decoder.Decode(&repositoryInfo); err != nil {
|
||||
return errors.Join(AuthError{
|
||||
http.StatusServiceUnavailable,
|
||||
@@ -128,3 +132,109 @@ func CheckGogsRepositoryPushPermission(baseURL *url.URL, authorization string) e
|
||||
// this token authorizes pushing to the repo, yay!
|
||||
return nil
|
||||
}
|
||||
|
||||
type ForgeActionRun struct {
|
||||
TriggerUser *ForgeUser
|
||||
PullRequest *ForgePullRequest // only if `event == "pull_request"`
|
||||
}
|
||||
|
||||
type ForgePullRequest struct {
|
||||
OwnerID int64
|
||||
OwnerName string
|
||||
RepositoryID int64
|
||||
RepositoryName string
|
||||
Number int64
|
||||
}
|
||||
|
||||
// This is a Forgejo-specific API added in https://codeberg.org/forgejo/forgejo/pulls/12727
|
||||
// and available starting in Forgejo vX.Y.
|
||||
func FetchForgejoActionRun(baseURL *url.URL, authorization string) (*ForgeActionRun, error) {
|
||||
request, response, err := makeGogsAPIRequest(baseURL, authorization, "actions/run")
|
||||
if err != nil {
|
||||
return nil, AuthError{
|
||||
http.StatusServiceUnavailable,
|
||||
fmt.Sprintf("cannot fetch workflow run's pull request: %s", err),
|
||||
}
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
if response.StatusCode == http.StatusForbidden {
|
||||
return nil, AuthError{http.StatusForbidden, "not an automatic actions token"}
|
||||
} else if response.StatusCode == http.StatusUnauthorized {
|
||||
return nil, AuthError{http.StatusUnauthorized, "malformed token"}
|
||||
} else if response.StatusCode == http.StatusNotFound {
|
||||
return nil, AuthError{http.StatusInternalServerError, "endpoint not available"}
|
||||
} else if response.StatusCode != http.StatusOK {
|
||||
return nil, AuthError{
|
||||
http.StatusServiceUnavailable,
|
||||
fmt.Sprintf(
|
||||
"cannot fetch workflow run's pull request: GET %s returned %s",
|
||||
request.URL,
|
||||
response.Status,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
var runInfo struct {
|
||||
Event string `json:"event"`
|
||||
EventPayload string `json:"event_payload"`
|
||||
TriggerUser struct {
|
||||
ID int64 `json:"id"`
|
||||
Username string `json:"username"`
|
||||
} `json:"trigger_user"`
|
||||
}
|
||||
decoder := json.NewDecoder(response.Body)
|
||||
if err := decoder.Decode(&runInfo); err != nil {
|
||||
return nil, errors.Join(AuthError{
|
||||
http.StatusServiceUnavailable,
|
||||
fmt.Sprintf(
|
||||
"cannot fetch workflow run's pull request: GET %s returned malformed JSON",
|
||||
request.URL,
|
||||
),
|
||||
}, err)
|
||||
}
|
||||
origin := request.URL.Hostname()
|
||||
actionRun := &ForgeActionRun{
|
||||
TriggerUser: &ForgeUser{
|
||||
Origin: &origin,
|
||||
Id: &runInfo.TriggerUser.ID,
|
||||
Name: &runInfo.TriggerUser.Username,
|
||||
},
|
||||
}
|
||||
|
||||
if runInfo.Event == "pull_request" {
|
||||
var eventInfo struct {
|
||||
Number int64 `json:"number"`
|
||||
Repository struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Owner struct {
|
||||
ID int64 `json:"id"`
|
||||
Username string `json:"username"`
|
||||
}
|
||||
}
|
||||
}
|
||||
decoder = json.NewDecoder(strings.NewReader(runInfo.EventPayload))
|
||||
if err := decoder.Decode(&eventInfo); err != nil {
|
||||
return nil, errors.Join(AuthError{
|
||||
http.StatusServiceUnavailable,
|
||||
fmt.Sprintf(
|
||||
"cannot fetch workflow run's pull request: GET %s returned malformed JSON "+
|
||||
"in event payload",
|
||||
request.URL,
|
||||
),
|
||||
}, err)
|
||||
}
|
||||
actionRun.PullRequest = &ForgePullRequest{
|
||||
OwnerID: eventInfo.Repository.Owner.ID,
|
||||
OwnerName: eventInfo.Repository.Owner.Username,
|
||||
RepositoryID: eventInfo.Repository.ID,
|
||||
RepositoryName: eventInfo.Repository.Name,
|
||||
Number: eventInfo.Number,
|
||||
}
|
||||
} else {
|
||||
// we aren't decoding the other events
|
||||
}
|
||||
|
||||
return actionRun, nil
|
||||
}
|
||||
|
||||
+7
-7
@@ -930,7 +930,7 @@ type ForgeUser struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Origin *string `protobuf:"bytes,1,opt,name=origin" json:"origin,omitempty"`
|
||||
Id *int64 `protobuf:"varint,2,opt,name=id" json:"id,omitempty"`
|
||||
Handle *string `protobuf:"bytes,3,opt,name=handle" json:"handle,omitempty"`
|
||||
Name *string `protobuf:"bytes,3,opt,name=name" json:"name,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
@@ -979,9 +979,9 @@ func (x *ForgeUser) GetId() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ForgeUser) GetHandle() string {
|
||||
if x != nil && x.Handle != nil {
|
||||
return *x.Handle
|
||||
func (x *ForgeUser) GetName() string {
|
||||
if x != nil && x.Name != nil {
|
||||
return *x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -1057,11 +1057,11 @@ const file_schema_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"forge_user\x18\x03 \x01(\v2\n" +
|
||||
".ForgeUserR\tforgeUser\x12\x19\n" +
|
||||
"\brepo_url\x18\x04 \x01(\tR\arepoUrl\"K\n" +
|
||||
"\brepo_url\x18\x04 \x01(\tR\arepoUrl\"G\n" +
|
||||
"\tForgeUser\x12\x16\n" +
|
||||
"\x06origin\x18\x01 \x01(\tR\x06origin\x12\x0e\n" +
|
||||
"\x02id\x18\x02 \x01(\x03R\x02id\x12\x16\n" +
|
||||
"\x06handle\x18\x03 \x01(\tR\x06handle*V\n" +
|
||||
"\x02id\x18\x02 \x01(\x03R\x02id\x12\x12\n" +
|
||||
"\x04name\x18\x03 \x01(\tR\x04name*V\n" +
|
||||
"\x04Type\x12\x10\n" +
|
||||
"\fInvalidEntry\x10\x00\x12\r\n" +
|
||||
"\tDirectory\x10\x01\x12\x0e\n" +
|
||||
|
||||
+1
-1
@@ -150,5 +150,5 @@ message Principal {
|
||||
message ForgeUser {
|
||||
string origin = 1;
|
||||
int64 id = 2;
|
||||
string handle = 3;
|
||||
string name = 3;
|
||||
}
|
||||
|
||||
+54
-28
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
type WildcardPattern struct {
|
||||
Domain []string
|
||||
PreviewDomain []string
|
||||
CloneURL *fasttemplate.Template
|
||||
IndexRepo *fasttemplate.Template
|
||||
IndexBranch string
|
||||
@@ -22,31 +23,44 @@ func (pattern *WildcardPattern) GetHost() string {
|
||||
return strings.Join(parts, ".")
|
||||
}
|
||||
|
||||
type WildcardDomainKind int
|
||||
|
||||
const (
|
||||
WildcardDomainPrimary WildcardDomainKind = iota
|
||||
WildcardDomainPreview
|
||||
WildcardDomainAny
|
||||
)
|
||||
|
||||
// Returns `subdomain, found` where if `found == true`, `subdomain` contains the part of `host`
|
||||
// corresponding to the * in the domain pattern.
|
||||
func (pattern *WildcardPattern) Matches(host string) (string, bool) {
|
||||
func (pattern *WildcardPattern) Matches(host string, kind WildcardDomainKind) (string, bool) {
|
||||
var suffixParts []string
|
||||
switch kind {
|
||||
case WildcardDomainPrimary:
|
||||
suffixParts = pattern.Domain
|
||||
case WildcardDomainPreview:
|
||||
suffixParts = pattern.PreviewDomain
|
||||
case WildcardDomainAny:
|
||||
if userName, found := pattern.Matches(host, WildcardDomainPrimary); found {
|
||||
return userName, found
|
||||
} else if userName, found := pattern.Matches(host, WildcardDomainPreview); found {
|
||||
return userName, found
|
||||
}
|
||||
return "", false
|
||||
default:
|
||||
panic("invalid wildcard domain kind")
|
||||
}
|
||||
|
||||
hostParts := strings.Split(host, ".")
|
||||
hostLen := len(hostParts)
|
||||
patternLen := len(pattern.Domain)
|
||||
|
||||
// host must have at least one more part than the pattern domain
|
||||
if hostLen <= patternLen {
|
||||
if len(suffixParts) == 0 {
|
||||
return "", false
|
||||
}
|
||||
|
||||
// break the host parts into <subdomain parts> and <domain parts>
|
||||
mid := hostLen - patternLen
|
||||
prefix := hostParts[:mid]
|
||||
suffix := hostParts[mid:]
|
||||
|
||||
// check if the suffix matches the domain
|
||||
if !slices.Equal(suffix, pattern.Domain) {
|
||||
} else if len(hostParts) != len(suffixParts)+1 {
|
||||
return "", false
|
||||
} else if !slices.Equal(hostParts[1:], suffixParts) {
|
||||
return "", false
|
||||
} else {
|
||||
return hostParts[0], true
|
||||
}
|
||||
|
||||
// return all the subdomain parts
|
||||
subdomain := strings.Join(prefix, ".")
|
||||
return subdomain, true
|
||||
}
|
||||
|
||||
func (pattern *WildcardPattern) ApplyTemplate(userName string, projectName string) (string, string) {
|
||||
@@ -69,36 +83,48 @@ func (pattern *WildcardPattern) ApplyTemplate(userName string, projectName strin
|
||||
return repoURL, branch
|
||||
}
|
||||
|
||||
func TranslateWildcards(configs []WildcardConfig) ([]*WildcardPattern, error) {
|
||||
func TranslateWildcards(wildcardConfigs []WildcardConfig) ([]*WildcardPattern, error) {
|
||||
var wildcardPatterns []*WildcardPattern
|
||||
for _, config := range configs {
|
||||
cloneURLTemplate, err := fasttemplate.NewTemplate(config.CloneURL, "<", ">")
|
||||
for _, wildcardConfig := range wildcardConfigs {
|
||||
cloneURLTemplate, err := fasttemplate.NewTemplate(wildcardConfig.CloneURL, "<", ">")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("wildcard pattern: clone URL: %w", err)
|
||||
}
|
||||
|
||||
var indexRepoBranch string = config.IndexRepoBranch
|
||||
indexRepoTemplate, err := fasttemplate.NewTemplate(config.IndexRepo, "<", ">")
|
||||
var indexRepoBranch string = wildcardConfig.IndexRepoBranch
|
||||
indexRepoTemplate, err := fasttemplate.NewTemplate(wildcardConfig.IndexRepo, "<", ">")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("wildcard pattern: index repo: %w", err)
|
||||
}
|
||||
|
||||
authorization := false
|
||||
if config.Authorization != "" {
|
||||
if slices.Contains([]string{"gogs", "gitea", "forgejo"}, config.Authorization) {
|
||||
if wildcardConfig.Authorization != "" {
|
||||
if slices.Contains([]string{"gogs", "gitea", "forgejo"}, wildcardConfig.Authorization) {
|
||||
// Currently these are the only supported forges, and the authorization mechanism
|
||||
// is the same for all of them.
|
||||
authorization = true
|
||||
} else {
|
||||
return nil, fmt.Errorf(
|
||||
"wildcard pattern: unknown authorization mechanism: %s",
|
||||
config.Authorization,
|
||||
wildcardConfig.Authorization,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if !config.Feature("preview") {
|
||||
wildcardConfig.PreviewDomain = ""
|
||||
}
|
||||
if wildcardConfig.PreviewDomain != "" {
|
||||
if wildcardConfig.Authorization != "forgejo" {
|
||||
return nil, fmt.Errorf(
|
||||
"wildcard pattern: previews require Forgejo authorization",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
wildcardPatterns = append(wildcardPatterns, &WildcardPattern{
|
||||
Domain: strings.Split(config.Domain, "."),
|
||||
Domain: strings.Split(wildcardConfig.Domain, "."),
|
||||
PreviewDomain: strings.Split(wildcardConfig.PreviewDomain, "."),
|
||||
CloneURL: cloneURLTemplate,
|
||||
IndexRepo: indexRepoTemplate,
|
||||
IndexBranch: indexRepoBranch,
|
||||
|
||||
Reference in New Issue
Block a user