~bigbes/sr-ht-dolt

ref: 2b781a27e6fd7eaa68ee4e692fe0da7348569daf sr-ht-dolt/core/access.go -rw-r--r-- 3.0 KiB
2b781a27 — Eugene Blikh remoteapi: authz interceptors, remotesrv assembly, credentials service a month 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
package core

// Allowed decides whether caller may perform op on repo, given the caller's
// resolved per-repo ACL grant (aclMode, nil if the caller has no ACL entry).
// It implements the dolt.sr.ht access matrix exactly:
//
//	Caller \ Visibility | PUBLIC        | UNLISTED      | PRIVATE
//	--------------------|---------------|---------------|----------------------
//	anon / any user     | browse, clone | browse, clone | —
//	ACL RO              | browse, clone | browse, clone | browse, clone
//	ACL RW              | + push        | + push        | browse, clone, push
//	owner               | all           | all           | all
//
// Rules:
//   - The owner may do anything (subject to the suspension rule below).
//   - An ACL RO grant permits browse and clone on any visibility, including
//     PRIVATE.
//   - An ACL RW grant additionally permits push (but never admin).
//   - With no ACL and non-owner: PUBLIC and UNLISTED permit browse and clone
//     for everyone (including anonymous); PRIVATE permits nothing.
//   - A suspended caller may read (browse, clone) but never push or admin,
//     regardless of ownership or ACL.
//   - Any operation outside the four known Ops is denied.
//
// Access control fails closed: a nil repo denies everything.
func Allowed(caller *Caller, repo *Repo, aclMode *AccessMode, op Op) bool {
	switch op {
	case OpBrowse, OpCloneRead, OpPush, OpAdmin:
		// known op
	default:
		return false
	}
	if repo == nil {
		return false
	}

	suspended := caller != nil && caller.Suspended
	isWrite := op == OpPush || op == OpAdmin
	if suspended && isWrite {
		return false
	}

	// Owner may do anything (write already gated by the suspension check).
	if caller != nil && caller.UserID == repo.OwnerID {
		return true
	}

	// Explicit ACL grants (only meaningful for authenticated callers).
	if caller != nil && aclMode != nil {
		switch *aclMode {
		case AccessRO:
			return op == OpBrowse || op == OpCloneRead
		case AccessRW:
			// RO reads plus push; never admin.
			return op == OpBrowse || op == OpCloneRead || op == OpPush
		}
	}

	// No ownership, no ACL: fall back to visibility. Only reads are ever
	// granted this way.
	switch repo.Visibility {
	case VisibilityPublic, VisibilityUnlisted:
		return op == OpBrowse || op == OpCloneRead
	default: // VisibilityPrivate and anything unrecognized
		return false
	}
}

// NotFoundForPrivate reports whether a denied request for repo should be
// surfaced as "not found" (404) rather than "forbidden" (403), so that the
// existence of PRIVATE repositories is not leaked. Consult it only after
// Allowed has already returned false for the request.
//
// A PRIVATE repo that the caller cannot even browse is reported as not found;
// PUBLIC/UNLISTED repos, and PRIVATE repos the caller may browse, reveal their
// existence normally (a plain 403). A nil repo is always "not found".
func NotFoundForPrivate(caller *Caller, repo *Repo, aclMode *AccessMode) bool {
	if repo == nil {
		return true
	}
	if repo.Visibility != VisibilityPrivate {
		return false
	}
	return !Allowed(caller, repo, aclMode, OpBrowse)
}