feat(pact): add HasFrontendTokenScopes and the shared ValidTokenScopeName rule (E-2)

- pact.FrontendTokenScope and pact.HasFrontendTokenScopes declare member
  (usr_) token scopes; the admin token catalog never reads them
- pact.ValidTokenScopeName carries cabana's scope-name pattern; cabana's
  g15_ catalog switches to it with unchanged behaviour and messages
This commit is contained in:
Jakub Zych
2026-10-23 16:15:25 +02:00
parent c14238b94b
commit 9eedfbbb29
5 changed files with 43 additions and 5 deletions

View File

@@ -2,7 +2,6 @@ package cabana
import (
"fmt"
"regexp"
"strings"
"git.golem15.com/golem15/summercms/modules/bouncer"
@@ -16,8 +15,6 @@ const (
scopeAdminRead = "admin:read"
)
var tokenScopeNameRE = regexp.MustCompile(`^[a-z][a-z0-9_-]*(:[a-z][a-z0-9_-]*)?$`)
var allowedTokenMethods = map[string]bool{
"GET": true, "HEAD": true, "POST": true, "PUT": true, "PATCH": true, "DELETE": true,
}
@@ -107,7 +104,7 @@ func compileTokenScopes(reg *Registry, plugins []party.Plugin) error {
func validatePluginTokenScope(reg *Registry, pluginID string, scope pact.TokenScope, cat *tokenScopeCatalog) error {
name := strings.TrimSpace(scope.Name)
if name == "" || !tokenScopeNameRE.MatchString(name) {
if name == "" || !pact.ValidTokenScopeName(name) {
return fmt.Errorf("cabana: plugin %s registered invalid token scope name %q", pluginID, scope.Name)
}
if name == scopeAll || name == "admin" || strings.HasPrefix(name, "admin:") {