Files
unkinben 20613afb26 Initial vault-plugin-secrets-gitea engine
Add a Vault/OpenBao secrets engine that mints ephemeral, scoped Gitea
access tokens on demand. The engine holds a single seeded Gitea site-admin
Basic-Auth credential and, per role, mints a fresh per-user token via the
admin API, bound to a Vault lease and deleted from Gitea on revocation.
Gitea requires Basic Auth for token management (token auth is rejected),
and reqSelfOrAdmin lets a site admin manage any user's tokens, which is the
mechanism this relies on. Gitea tokens never expire server-side, so the
Vault lease is the sole expiry mechanism.

- add backend wiring, config (+ rotate-root), roles, creds paths
- add the gitea client (Basic Auth create/delete token, admin password change)
- add scope validation against Gitea's access-token scope set
- add unit tests (fake Gitea API) and a Vault+OpenBao e2e harness
- add Makefile, nfpm RPM packaging, and Woodpecker build/test/release pipelines

Claude-Session: https://claude.ai/code/session_015ur3i7D2azsMAWTSVABApv
2026-07-27 00:54:59 +10:00

54 lines
1.5 KiB
Go

package gitea
import (
"strings"
"testing"
)
func TestNormalizeScopes(t *testing.T) {
cases := []struct {
name string
in []string
want []string
wantErr bool
}{
{"single", []string{"read:repository"}, []string{"read:repository"}, false},
{"trim+case", []string{" Write:Issue "}, []string{"write:issue"}, false},
{"dedupe", []string{"read:user", "read:user", "write:user"}, []string{"read:user", "write:user"}, false},
{"all", []string{"all"}, []string{"all"}, false},
{"public-only", []string{"public-only", "read:repository"}, []string{"public-only", "read:repository"}, false},
{"blank-only", []string{"", " "}, nil, true},
{"empty", nil, nil, true},
{"invalid", []string{"read:repository", "sudo"}, nil, true},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
got, err := normalizeScopes(c.in)
if c.wantErr {
if err == nil {
t.Fatalf("expected error, got %v", got)
}
return
}
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if strings.Join(got, ",") != strings.Join(c.want, ",") {
t.Errorf("normalizeScopes(%v) = %v, want %v", c.in, got, c.want)
}
})
}
}
func TestKnownScopesSorted(t *testing.T) {
scopes := knownScopes()
if len(scopes) != len(validScopes) {
t.Fatalf("knownScopes len = %d, want %d", len(scopes), len(validScopes))
}
for i := 1; i < len(scopes); i++ {
if scopes[i-1] > scopes[i] {
t.Errorf("knownScopes not sorted at %d: %q > %q", i, scopes[i-1], scopes[i])
}
}
}