package main import ( "net/http" "testing" ) func TestValidRepoPath(t *testing.T) { ok := []string{ "dns", "infra/dns", "a", "x-1.2_3", "infra/x.y", "static", // cgit's routes are only taken one component deep, because that is the // only place they can be reached. "log", "tree", "about", "summary", } bad := []string{ "", ".", "..", "../etc", "infra/../etc", "a/b/c", "/dns", "dns/", "Infra/dns", "in fra/dns", "dns.git", "dns.issues", "dns.wiki", "admin", "settings", "login", "-dns", // The forge's own names are taken in both components: /infra/settings // has to be the settings of the repository infra, so a repository called // settings inside the section infra would make that page unreachable. "infra/settings", "infra/issues", "infra/wiki", "issues", "wiki", // And cgit's, in the second component, for the same reason. "infra/log", "infra/tree", "infra/about", "infra/summary", "infra/atom", "infra/plain", "infra/snapshot", } for _, p := range ok { if !validRepoPath(p) { t.Errorf("%q was refused", p) } } for _, p := range bad { if validRepoPath(p) { t.Errorf("%q was allowed", p) } } } func TestPermissions(t *testing.T) { anon := Viewer{} stranger := Viewer{Name: "eve"} reader := Viewer{Name: "carol"} writer := Viewer{Name: "bob"} owner := Viewer{Name: "dave"} root := Viewer{Name: "alice", Admin: true} grants := map[string]string{"carol": grantRead, "bob": grantWrite, "dave": grantAdmin} private := Repo{Visibility: visPrivate, Issues: featMembers, Wiki: featDisabled, Grants: grants} public := Repo{Visibility: visPublic, Issues: featUsers, Wiki: featMembers, Grants: grants} cases := []struct { what string v Viewer repo Repo read, write, admin, issue, wiki bool }{ {"anon on private", anon, private, false, false, false, false, false}, {"stranger on private", stranger, private, false, false, false, false, false}, {"reader on private", reader, private, true, false, false, true, false}, {"writer on private", writer, private, true, true, false, true, false}, {"owner on private", owner, private, true, true, true, true, false}, {"global admin on private", root, private, true, true, true, true, false}, {"anon on public", anon, public, true, false, false, false, false}, {"stranger on public", stranger, public, true, false, false, true, false}, {"reader on public", reader, public, true, false, false, true, true}, {"writer on public", writer, public, true, true, false, true, true}, {"global admin on public", root, public, true, true, true, true, true}, } for _, c := range cases { if got := c.v.CanRead(c.repo); got != c.read { t.Errorf("%s: read %v", c.what, got) } if got := c.v.CanWrite(c.repo); got != c.write { t.Errorf("%s: write %v", c.what, got) } if got := c.v.CanAdmin(c.repo); got != c.admin { t.Errorf("%s: admin %v", c.what, got) } if got := c.v.CanIssues(c.repo); got != c.issue { t.Errorf("%s: issues %v", c.what, got) } if got := c.v.CanWiki(c.repo); got != c.wiki { t.Errorf("%s: wiki %v", c.what, got) } } off := Repo{Visibility: visPublic, Issues: featDisabled, Wiki: featDisabled, Grants: grants} for _, v := range []Viewer{anon, reader, writer, owner, root} { if v.CanIssues(off) || v.CanWiki(off) { t.Errorf("%s writes to a disabled feature", v.Name) } } } func TestUsersOnPrivateIsAnError(t *testing.T) { repo := Repo{Visibility: visPrivate, Issues: featUsers, Wiki: featDisabled} if errs := repo.validate("infra/dns"); len(errs) != 1 { t.Fatalf("expected one error, got %v", errs) } repo.Visibility = visPublic if errs := repo.validate("infra/dns"); len(errs) != 0 { t.Fatalf("expected none, got %v", errs) } } // Turning a feature off leaves its repository on disk, and the answer has to // be the same at both doors. func TestDisabledFeatureIsDisabledEverywhere(t *testing.T) { f := testForge(t) addRepo(t, f, "notes", Repo{Visibility: visPublic, Issues: featUsers, Wiki: featUsers}) err := f.store.UpdateRepos("off", f.store.forgeAuthor(), func(m map[string]Repo) error { repo := m["notes"] repo.Issues, repo.Wiki = featDisabled, featDisabled m["notes"] = repo return nil }) if err != nil { t.Fatal(err) } h := f.routes() for _, path := range []string{ "/notes.issues/", "/notes.wiki/", "/notes.issues.git/info/refs?service=git-upload-pack", "/notes.wiki.git/info/refs?service=git-upload-pack", } { if code := get(h, path, nil).Code; code != http.StatusNotFound { t.Errorf("%s answered %d for a disabled feature", path, code) } } }