diff options
Diffstat (limited to 'perm_test.go')
| -rw-r--r-- | perm_test.go | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/perm_test.go b/perm_test.go new file mode 100644 index 0000000..dc874fe --- /dev/null +++ b/perm_test.go @@ -0,0 +1,132 @@ +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) + } + } +} |