CAT-G2: the packages token is renewed on the forge's refusal, said as a finding, never on a timer
The Go bundle raises packages-token-refused in the module's findings state when the forge answers 401 to a write; the watcher mints afresh on that key's event and clears it (lead's ruling on A3n, map-onto-how-the-mesh-is-built: nothing waits on a timer). Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
This commit is contained in:
@@ -27,6 +27,32 @@ type Forge struct {
|
||||
Owner string // the package registry's owner, as the seat serves it
|
||||
// Type is the registry's package type retention reads: TypeNpm when empty, or TypeGo.
|
||||
Type string
|
||||
// Refused says whether the forge refused the write side's token (401), as the finding PackagesTokenRefused in the
|
||||
// module's findings state: the TypeScript bundle mints a new token on that key's event and clears it (no timer
|
||||
// looks at the token). Nil says nothing.
|
||||
Refused func(raised bool)
|
||||
}
|
||||
|
||||
// PackagesTokenRefused is the finding this bundle raises when the forge refuses the packages account's token, and its
|
||||
// key in the module's findings state (packages-account.ts PACKAGES_TOKEN_REFUSED watches that key).
|
||||
const (
|
||||
PackagesTokenRefused = "packages-token-refused"
|
||||
PackagesTokenRefusedKey = "seat.gitea.packages-token-refused_write"
|
||||
)
|
||||
|
||||
// wrote is a write's answer, said on Refused: a 401 raises the finding, a write the forge took clears it.
|
||||
func (f *Forge) wrote(err error) error {
|
||||
if f.Refused == nil {
|
||||
return err
|
||||
}
|
||||
var status *forgehttp.StatusError
|
||||
switch {
|
||||
case errors.As(err, &status) && status.Status == http.StatusUnauthorized:
|
||||
f.Refused(true)
|
||||
case err == nil:
|
||||
f.Refused(false)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// The package types the retention reads: npm packages (the npm seat) and Go modules (the Go seat).
|
||||
@@ -59,7 +85,9 @@ func ForgeFromEnv() (*Forge, error) {
|
||||
missing = append(missing, m)
|
||||
}
|
||||
}
|
||||
f := &Forge{Client: c, Writer: &w, Owner: os.Getenv("MESH_NPM_OWNER")}
|
||||
f := &Forge{Client: c, Writer: &w, Owner: os.Getenv("MESH_NPM_OWNER"), Refused: func(raised bool) {
|
||||
finding(PackagesTokenRefused, "write", "the forge refused the package seats' write token; the module mints it afresh", raised)
|
||||
}}
|
||||
if f.Owner == "" {
|
||||
missing = append(missing, "MESH_NPM_OWNER")
|
||||
}
|
||||
@@ -152,7 +180,7 @@ func (f *Forge) Delete(ctx context.Context, name, version string) error {
|
||||
return err
|
||||
}
|
||||
_, err = w.Do(ctx, http.MethodDelete, f.versionPath(name, version), nil, maxBody)
|
||||
return err
|
||||
return f.wrote(err)
|
||||
}
|
||||
|
||||
// Repository is one repository on the forge, as far as reading its manifests needs.
|
||||
|
||||
@@ -90,14 +90,14 @@ func (s seatStore) Put(seat string, p packageseats.Publish, file []byte) (string
|
||||
return "", err
|
||||
}
|
||||
_, err = w.Send(ctx, http.MethodPut, fmt.Sprintf("/api/packages/%s/go/upload", url.PathEscape(f.Owner)), file, "application/zip", maxBody)
|
||||
return module, err
|
||||
return module, f.wrote(err)
|
||||
}
|
||||
doc, err := npmDocument(f.URL, f.Owner, p, file)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
_, err = w.Do(ctx, http.MethodPut, fmt.Sprintf("/api/packages/%s/npm/%s", url.PathEscape(f.Owner), url.PathEscape(p.Name)), doc, maxBody)
|
||||
return "", err
|
||||
return "", f.wrote(err)
|
||||
}
|
||||
|
||||
func (s seatStore) Versions(seat, name string) ([]packageseats.Version, error) {
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/health"
|
||||
"gitea/internal/forgehttp"
|
||||
"gitea/internal/packageseats"
|
||||
)
|
||||
@@ -63,7 +64,10 @@ func (g *registry) handle(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
want = "token writer"
|
||||
}
|
||||
if got := r.Header.Get("Authorization"); got != want {
|
||||
if got := r.Header.Get("Authorization"); got == "token stale" {
|
||||
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
} else if got != want {
|
||||
http.Error(w, r.Method+" "+p+" came as "+got+", not "+want, http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
@@ -125,6 +129,19 @@ func TestThePackageSeatsStoreIntoTheForge(t *testing.T) {
|
||||
t.Errorf("a forge handed no write side wrote: %v", err)
|
||||
}
|
||||
s := seatStore{forge: func() (*Forge, error) { return forge, nil }}
|
||||
// The forge refuses the write side's token: the finding the TypeScript bundle renews on is raised under its key,
|
||||
// and the next write the forge takes clears it (no timer looks at the token).
|
||||
var refused []bool
|
||||
forge.Refused = func(raised bool) { refused = append(refused, raised) }
|
||||
if k := health.FindingKey(health.ScopeSeat, packageseats.Self, PackagesTokenRefused+".write"); k != PackagesTokenRefusedKey {
|
||||
t.Errorf("the refusal's key is %s, the watcher watches %s", k, PackagesTokenRefusedKey)
|
||||
}
|
||||
_ = os.WriteFile(writer.TokenFile, []byte("stale\n"), 0o600)
|
||||
if _, err := s.Put(packageseats.GoSeat, packageseats.Publish{Version: "0.1.15"}, moduleZip(t, "git.example/acme/sdk@v0.1.15")); err == nil ||
|
||||
len(refused) != 1 || !refused[0] {
|
||||
t.Errorf("a write the forge refused with 401 raised no finding: %v %v", err, refused)
|
||||
}
|
||||
_ = os.WriteFile(writer.TokenFile, []byte("writer\n"), 0o600)
|
||||
|
||||
tgz := tarball(t, `{"name":"@acme/contracts","version":"0.1.15-feat-x.2","main":"index.js"}`)
|
||||
p := packageseats.Publish{Name: "@acme/contracts", Version: "0.1.15-feat-x.2", Tag: "feat-x", Kind: packageseats.KindPackage, Digest: "sha256:" + sha(tgz)}
|
||||
@@ -174,4 +191,7 @@ func TestThePackageSeatsStoreIntoTheForge(t *testing.T) {
|
||||
g.deleted[0] != "go/git.example%2Facme%2Fsdk/v0.1.15-feat.1" {
|
||||
t.Errorf("remove: %v %v", err, g.deleted)
|
||||
}
|
||||
if len(refused) < 2 || refused[len(refused)-1] {
|
||||
t.Errorf("a write the forge took after the refusal did not clear it: %v", refused)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user