aboutsummaryrefslogtreecommitdiff
path: root/perm_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'perm_test.go')
-rw-r--r--perm_test.go132
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)
+ }
+ }
+}