krz/gitbay
A CLI-first git forge.
clone: git clone https://gitbay.org/krz/gitbay.git
main: 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}