Skip to content
quarry Artifact [ca28d0c447]
Login

Artifact [ca28d0c447]

Artifact ca28d0c447e14a604f81ffc5c4db8fb2a5c526a8f3dbc6b9d523e96a910b66b0:


package main

import (
	"net/http"
	"net/http/httptest"
	"net/url"
	"strings"
	"testing"
	"time"

	"craftdesign.group/quarry/internal/oltp"
)

// RFC 0023 across the doors: a disable answers 451 on the API and the
// pages to everybody but the repository's own admins, a hold to
// everybody, and restoring answers as before.
func TestATakedownAnswers451OnEverySurface(t *testing.T) {
	g := newSiteRig(t)
	api := "/api/v1/r/pub/counts"
	page := "/r/pub/timeline"
	status := func(user, path string) int {
		pass := doorPassword
		if user == "" {
			pass = ""
		}
		code, _ := g.doAs(user, http.MethodGet, path, pass, "", nil)
		return code
	}
	if c := status("", api); c != http.StatusOK {
		t.Fatalf("before: anonymous API %d", c)
	}

	if code, body := g.doAs("dmestas", http.MethodPost, "/api/v1/repos/disable", doorPassword, "",
		map[string]string{"repo": "pub", "category": "copyright", "notice": "N-1"}); code != http.StatusOK {
		t.Fatalf("FAIL: disable: %d %s", code, body)
	}
	for _, c := range []struct {
		user, path string
		want       int
	}{
		{"", api, http.StatusUnavailableForLegalReasons},
		{"", page, http.StatusUnavailableForLegalReasons},
		{"carol", api, http.StatusOK}, // a site admin, as a person, reaches it to judge the notice
		{"alice", api, http.StatusOK}, // the repository's own admin
		{"alice", page, http.StatusOK},
	} {
		if got := status(c.user, c.path); got != c.want {
			t.Errorf("FAIL: disabled: %q %s answered %d, want %d", c.user, c.path, got, c.want)
		}
	}
	if _, body := g.doAs("", http.MethodGet, page, "", "", nil); !strings.Contains(body, `data-hook="held"`) {
		t.Error("FAIL: the 451 page is not the held page")
	}
	// A private repository answers a stranger as one that is not there,
	// taken down or not.
	if err := g.db.SetTakedown(oltp.Takedown{Repo: "secret", Strength: oltp.Disabled, Category: "copyright", Actor: "dmestas"}); err != nil {
		t.Fatal(err)
	}
	g.rg.refreshPrivate()
	if c := status("", "/api/v1/r/secret/counts"); c == http.StatusUnavailableForLegalReasons {
		t.Error("FAIL: a private repository told a stranger it was taken down")
	}

	if code, body := g.doAs("dmestas", http.MethodPost, "/api/v1/repos/hold", doorPassword, "",
		map[string]string{"repo": "pub", "category": "child-abuse"}); code != http.StatusOK {
		t.Fatalf("FAIL: hold: %d %s", code, body)
	}
	if got := status("alice", api); got != http.StatusUnavailableForLegalReasons {
		t.Errorf("FAIL: on hold, the owner's API answered %d", got)
	}
	if got := status("carol", api); got != http.StatusOK {
		t.Errorf("FAIL: on hold, a site admin as a person answered %d", got)
	}
	siteV, _ := g.mint(t, oltp.ScopeAPI, oltp.ScopeWeb, oltp.ScopeSite)
	if code, _ := g.do(http.MethodGet, api, siteV, "", nil); code != http.StatusUnavailableForLegalReasons {
		t.Errorf("FAIL: on hold, a site token answered %d", code)
	}
	if _, body := g.doAs("alice", http.MethodGet, api, doorPassword, "", nil); strings.Contains(body, "child-abuse") {
		t.Errorf("FAIL: a hold said why: %s", body)
	}
	if code, _ := g.doAs("dmestas", http.MethodPost, "/api/v1/repos/restore", doorPassword, "",
		map[string]string{"repo": "pub"}); code == http.StatusOK {
		t.Error("FAIL: restore lifted a hold")
	}
	if code, body := g.doAs("dmestas", http.MethodPost, "/api/v1/repos/lift", doorPassword, "",
		map[string]string{"repo": "pub", "reason": "report filed"}); code != http.StatusOK {
		t.Fatalf("FAIL: lift: %d %s", code, body)
	}
	if c := status("", api); c != http.StatusOK {
		t.Errorf("FAIL: after the lift: anonymous API %d", c)
	}
	acts, _ := g.db.TakedownLog("pub", 10)
	if len(acts) != 3 {
		t.Errorf("FAIL: %d logged acts, want disable, hold, lift", len(acts))
	}
}

// A site token may disable and restore, which a host complaint needs in
// minutes; it may neither place nor lift a hold, and it reaches no
// private repository it holds no grant on.
func TestASiteTokenTakesDownWithinItsLine(t *testing.T) {
	g := newSiteRig(t)
	_, tok := g.mint(t, oltp.ScopeAPI, oltp.ScopeWeb, oltp.ScopeSite)
	v, _, err := g.db.MintToken("zeitgeist", oltp.TokenSpec{Name: "site2", Scopes: []string{oltp.ScopeAPI, oltp.ScopeWeb, oltp.ScopeSite}})
	if err != nil {
		t.Fatal(err)
	}
	_ = tok
	post := func(path string, body map[string]string) int {
		code, _ := g.do(http.MethodPost, path, v, "", body)
		return code
	}
	if c := post("/api/v1/repos/disable", map[string]string{"repo": "pub", "category": "host"}); c != http.StatusOK {
		t.Fatalf("FAIL: the site token's disable: %d", c)
	}
	if c := post("/api/v1/repos/hold", map[string]string{"repo": "pub", "category": "child-abuse"}); c == http.StatusOK {
		t.Error("FAIL: the site token placed a hold")
	}
	if c := post("/api/v1/repos/restore", map[string]string{"repo": "pub"}); c != http.StatusOK {
		t.Errorf("FAIL: the site token's restore: %d", c)
	}
	if c := post("/api/v1/repos/disable", map[string]string{"repo": "secret", "category": "host"}); c != http.StatusNotFound {
		t.Errorf("FAIL: the site token's disable of secret answered %d, want 404", c)
	}
	if err := g.db.SetTakedown(oltp.Takedown{Repo: "pub", Strength: oltp.LegalHold, Category: "preservation", Actor: "dmestas"}); err != nil {
		t.Fatal(err)
	}
	if c := post("/api/v1/repos/lift", map[string]string{"repo": "pub"}); c == http.StatusOK {
		t.Error("FAIL: the site token lifted a hold")
	}
}

// The fleet row offers the takedown acts and, while one is in force,
// neither stopping nor deleting; the form carries a disable across.
func TestTheFleetRowTakesDown(t *testing.T) {
	g := newSiteRig(t)
	_, body := g.doAs("dmestas", http.MethodGet, "/fleet", doorPassword, "", nil)
	if !strings.Contains(body, `href="/fleet?takedown=pub&strength=disabled"`) || !strings.Contains(body, `data-hook="fleet-hold-link"`) {
		t.Fatal("FAIL: no takedown links on the fleet rows")
	}
	if _, body := g.doAs("dmestas", http.MethodGet, "/fleet?takedown=pub&strength=disabled", doorPassword, "", nil); !strings.Contains(body, `data-hook="fleet-takedown"`) {
		t.Fatal("FAIL: no takedown form")
	}
	if code, body := g.fleetForm("dmestas", "/fleet/repo/takedown", url.Values{"repo": {"pub"}, "strength": {"disabled"},
		"category": {"copyright"}, "notice": {"N-9"}}); code != http.StatusSeeOther {
		t.Fatalf("FAIL: the form's disable: %d %s", code, body)
	}
	if td, ok, _ := g.db.TakedownOf("pub"); !ok || td.Notice != "N-9" || td.Actor != "dmestas" {
		t.Fatalf("FAIL: takedown %+v %v", td, ok)
	}
	_, body = g.doAs("dmestas", http.MethodGet, "/fleet", doorPassword, "", nil)
	row := body[strings.Index(body, `data-strength="disabled"`):]
	row = row[:strings.Index(row, "</li>")]
	if !strings.Contains(row, `data-hook="fleet-restore"`) {
		t.Error("FAIL: a disabled row offers no restore")
	}
	if strings.Contains(row, `data-hook="fleet-stop"`) || strings.Contains(row, `data-hook="fleet-delete-link"`) {
		t.Error("FAIL: a disabled row still offers stopping or deleting")
	}
}

// Stopping and deleting both refuse a repository under a takedown, from
// the registrar itself, before anything stops serving.
func TestTheRegistrarKeepsATakenDownRepository(t *testing.T) {
	rg := newTestRegistrar(t, 25)
	if err := rg.odb.AddRepo("held", t.TempDir(), "", oltp.Public); err != nil {
		t.Fatal(err)
	}
	if err := rg.odb.SetTakedown(oltp.Takedown{Repo: "held", Strength: oltp.Disabled, Category: "copyright", Actor: "dmestas"}); err != nil {
		t.Fatal(err)
	}
	if err := rg.Remove("held"); err == nil || !strings.Contains(err.Error(), "disabled") {
		t.Errorf("FAIL: stop serving: %v", err)
	}
	if err := rg.Delete("held", "held"); err == nil || !strings.Contains(err.Error(), "disabled") {
		t.Errorf("FAIL: delete: %v", err)
	}
	if _, ok, _ := rg.odb.RepoByName("held"); !ok {
		t.Error("FAIL: the registration went")
	}
}

// The sync front's answer: an anonymous pull of a disabled repository
// is refused with the sentence, its admin is served, and a hold refuses
// the admin too.
func TestTheSyncHoldGate(t *testing.T) {
	g := newSiteRig(t)
	req := httptest.NewRequest(http.MethodPost, "/r/pub", nil)
	if why := g.rg.SyncHoldGate(req, "pub", ""); why != "" {
		t.Fatalf("FAIL: refused before any takedown: %s", why)
	}
	if err := g.db.SetTakedown(oltp.Takedown{Repo: "pub", Strength: oltp.Disabled, Category: "copyright", Actor: "dmestas"}); err != nil {
		t.Fatal(err)
	}
	g.rg.refreshPrivate()
	if why := g.rg.SyncHoldGate(req, "pub", ""); !strings.Contains(why, "legal reasons") {
		t.Errorf("FAIL: an anonymous pull of a disabled repository: %q", why)
	}
	if why := g.rg.SyncHoldGate(req, "pub", "alice"); why != "" {
		t.Errorf("FAIL: the admin's pull was refused: %q", why)
	}
	if err := g.db.SetTakedown(oltp.Takedown{Repo: "pub", Strength: oltp.LegalHold, Category: "preservation", Actor: "dmestas"}); err != nil {
		t.Fatal(err)
	}
	g.rg.refreshPrivate()
	if why := g.rg.SyncHoldGate(req, "pub", "alice"); why == "" {
		t.Error("FAIL: a hold served the admin")
	}
}

// A takedown pauses the mirror pull, so a hold keeps the store as it is.
func TestATakedownPausesTheMirrorPull(t *testing.T) {
	g := newSiteRig(t)
	if err := g.db.SetMirror("pub", "https://example.invalid/pub", time.Hour); err != nil {
		t.Fatal(err)
	}
	if err := g.db.SetTakedown(oltp.Takedown{Repo: "pub", Strength: oltp.LegalHold, Category: "preservation", Actor: "dmestas"}); err != nil {
		t.Fatal(err)
	}
	g.fleetForm("dmestas", "/fleet/mirror/run", url.Values{"repo": {"pub"}})
	for _, r := range *g.pulled {
		if r == "pub" {
			t.Error("FAIL: a repository on legal hold was pulled from its upstream")
		}
	}
}