~bigbes/sr-ht-dolt

ref: 5c5479c4040cb4ba231cfe09ddae8634879ad5d9 sr-ht-dolt/core/access_test.go -rw-r--r-- 4.9 KiB
5c5479c4 — Eugene Blikh feat(web/beads): ready markers, milestone rollup, transitive dependency tree 29 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package core

import "testing"

const (
	ownerID = 1
	otherID = 2
)

func repoWith(v Visibility) *Repo {
	return &Repo{ID: 10, Name: "db", OwnerID: ownerID, OwnerName: "owner", Path: "/x", Visibility: v}
}

func ptr(m AccessMode) *AccessMode { return &m }

// grant bundles the four operation outcomes for one matrix cell.
type grant struct{ browse, clone, push, admin bool }

func (g grant) want(op Op) bool {
	switch op {
	case OpBrowse:
		return g.browse
	case OpCloneRead:
		return g.clone
	case OpPush:
		return g.push
	case OpAdmin:
		return g.admin
	}
	return false
}

func TestAllowedMatrix(t *testing.T) {
	anon := (*Caller)(nil)
	user := &Caller{UserID: otherID, Username: "user", UserType: UserTypeUser}
	owner := &Caller{UserID: ownerID, Username: "owner", UserType: UserTypeUser}
	suspendedOwner := &Caller{UserID: ownerID, Username: "owner", UserType: UserTypeSuspended, Suspended: true}
	suspendedUser := &Caller{UserID: otherID, Username: "user", UserType: UserTypeSuspended, Suspended: true}

	readOnly := grant{browse: true, clone: true}
	readWrite := grant{browse: true, clone: true, push: true}
	all := grant{browse: true, clone: true, push: true, admin: true}
	none := grant{}

	tests := []struct {
		name   string
		caller *Caller
		repo   *Repo
		acl    *AccessMode
		want   grant
	}{
		// anonymous, no ACL
		{"anon/public", anon, repoWith(VisibilityPublic), nil, readOnly},
		{"anon/unlisted", anon, repoWith(VisibilityUnlisted), nil, readOnly},
		{"anon/private", anon, repoWith(VisibilityPrivate), nil, none},

		// authenticated non-owner, no ACL (same as anonymous)
		{"user/public", user, repoWith(VisibilityPublic), nil, readOnly},
		{"user/unlisted", user, repoWith(VisibilityUnlisted), nil, readOnly},
		{"user/private", user, repoWith(VisibilityPrivate), nil, none},

		// ACL RO grant: browse+clone on every visibility, incl. PRIVATE
		{"acl-ro/public", user, repoWith(VisibilityPublic), ptr(AccessRO), readOnly},
		{"acl-ro/unlisted", user, repoWith(VisibilityUnlisted), ptr(AccessRO), readOnly},
		{"acl-ro/private", user, repoWith(VisibilityPrivate), ptr(AccessRO), readOnly},

		// ACL RW grant: + push, never admin, on every visibility
		{"acl-rw/public", user, repoWith(VisibilityPublic), ptr(AccessRW), readWrite},
		{"acl-rw/unlisted", user, repoWith(VisibilityUnlisted), ptr(AccessRW), readWrite},
		{"acl-rw/private", user, repoWith(VisibilityPrivate), ptr(AccessRW), readWrite},

		// owner: everything on every visibility
		{"owner/public", owner, repoWith(VisibilityPublic), nil, all},
		{"owner/unlisted", owner, repoWith(VisibilityUnlisted), nil, all},
		{"owner/private", owner, repoWith(VisibilityPrivate), nil, all},

		// suspended: reads only, never push/admin
		{"suspended-owner/private", suspendedOwner, repoWith(VisibilityPrivate), nil, readOnly},
		{"suspended-owner/public", suspendedOwner, repoWith(VisibilityPublic), nil, readOnly},
		{"suspended-user-rw/private", suspendedUser, repoWith(VisibilityPrivate), ptr(AccessRW), readOnly},
		{"suspended-user-rw/public", suspendedUser, repoWith(VisibilityPublic), ptr(AccessRW), readOnly},
		{"suspended-user-ro/private", suspendedUser, repoWith(VisibilityPrivate), ptr(AccessRO), readOnly},
	}

	ops := []Op{OpBrowse, OpCloneRead, OpPush, OpAdmin}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			for _, op := range ops {
				got := Allowed(tt.caller, tt.repo, tt.acl, op)
				want := tt.want.want(op)
				if got != want {
					t.Errorf("Allowed(%s, %s) = %v, want %v", tt.name, op, got, want)
				}
			}
		})
	}
}

func TestAllowedUnknownOpDenied(t *testing.T) {
	owner := &Caller{UserID: ownerID}
	if Allowed(owner, repoWith(VisibilityPublic), nil, Op(99)) {
		t.Fatal("unknown op must be denied even for the owner")
	}
}

func TestAllowedNilRepoDenied(t *testing.T) {
	owner := &Caller{UserID: ownerID}
	for _, op := range []Op{OpBrowse, OpCloneRead, OpPush, OpAdmin} {
		if Allowed(owner, nil, nil, op) {
			t.Fatalf("nil repo must deny %s", op)
		}
	}
}

func TestNotFoundForPrivate(t *testing.T) {
	anon := (*Caller)(nil)
	user := &Caller{UserID: otherID}
	owner := &Caller{UserID: ownerID}

	tests := []struct {
		name   string
		caller *Caller
		repo   *Repo
		acl    *AccessMode
		want   bool
	}{
		{"nil-repo", anon, nil, nil, true},
		{"public-anon", anon, repoWith(VisibilityPublic), nil, false},
		{"unlisted-anon", anon, repoWith(VisibilityUnlisted), nil, false},
		{"private-anon-hidden", anon, repoWith(VisibilityPrivate), nil, true},
		{"private-unauthorized-user-hidden", user, repoWith(VisibilityPrivate), nil, true},
		{"private-owner-visible", owner, repoWith(VisibilityPrivate), nil, false},
		{"private-acl-ro-visible", user, repoWith(VisibilityPrivate), ptr(AccessRO), false},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := NotFoundForPrivate(tt.caller, tt.repo, tt.acl); got != tt.want {
				t.Fatalf("NotFoundForPrivate(%s) = %v, want %v", tt.name, got, tt.want)
			}
		})
	}
}