krz/gitbay

A CLI-first git forge.

clone: git clone https://gitbay.org/krz/gitbay.git

repo-descriptions: internal/policy/access.go · raw

 1package policy
 2
 3import (
 4	"strings"
 5
 6	"gitbay.org/gitbay/internal/store"
 7)
 8
 9// CanRead reports whether user may read repo over an authenticated channel.
10// Public repos are readable by any authenticated user; private repos require
11// ownership or an explicit grant.
12func CanRead(user store.User, repo store.Repo, grant string) bool {
13	if isOwner(user, repo) {
14		return true
15	}
16	if repo.Visibility == "public" {
17		return true
18	}
19	return grant == "read" || grant == "write" || grant == "admin"
20}
21
22// CanWrite reports whether user may push to repo.
23func CanWrite(user store.User, repo store.Repo, grant string) bool {
24	if isOwner(user, repo) {
25		return true
26	}
27	return grant == "write" || grant == "admin"
28}
29
30// CanAdmin reports whether user may change repo settings and access.
31func CanAdmin(user store.User, repo store.Repo, grant string) bool {
32	if isOwner(user, repo) {
33		return true
34	}
35	return grant == "admin"
36}
37
38func isOwner(user store.User, repo store.Repo) bool {
39	return repo.OwnerKind == "user" && repo.OwnerID == user.ID
40}
41
42// ScopeAllowsGit reports whether an SSH key scope permits the requested git
43// transport on repoPath ("owner/name"). write=true for receive-pack.
44func ScopeAllowsGit(scope, repoPath string, write bool) bool {
45	switch scope {
46	case "full", "git":
47		return true
48	}
49	rest, ok := strings.CutPrefix(scope, "deploy:")
50	if !ok {
51		return false
52	}
53	target, mode, ok := strings.Cut(rest, ":")
54	if !ok || target != repoPath {
55		return false
56	}
57	switch mode {
58	case "rw":
59		return true
60	case "ro":
61		return !write
62	}
63	return false
64}
65
66// RefUpdate is one proposed ref change, with git facts computed by the hook
67// process (which can see quarantined objects; the daemon cannot).
68type RefUpdate struct {
69	Ref      string `json:"ref"`
70	Old      string `json:"old"`
71	New      string `json:"new"`
72	IsDelete bool   `json:"is_delete"`
73	IsForce  bool   `json:"is_force"`
74}
75
76// CheckPush applies ref policy for a push by a user with write access
77// already established. It returns a denial message, or "" to allow.
78func CheckPush(repo store.Repo, updates []RefUpdate) string {
79	protected := map[string]bool{}
80	for _, b := range repo.Settings.ProtectedBranches {
81		protected["refs/heads/"+b] = true
82	}
83	for _, u := range updates {
84		if strings.HasPrefix(u.Ref, "refs/merge-requests/") {
85			return "refs/merge-requests/* is server-owned and cannot be pushed"
86		}
87		if protected[u.Ref] {
88			branch := strings.TrimPrefix(u.Ref, "refs/heads/")
89			if u.IsDelete {
90				return "branch " + branch + " is protected: deletion refused"
91			}
92			if u.IsForce {
93				return "branch " + branch + " is protected: force-push refused"
94			}
95		}
96	}
97	return ""
98}