-
Notifications
You must be signed in to change notification settings - Fork 3
/
backend.go
155 lines (128 loc) · 3.19 KB
/
backend.go
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
143
144
145
146
147
148
149
150
151
152
153
154
155
package git
import (
"encoding/base64"
"fmt"
"log/slog"
"strings"
"github.com/charmbracelet/ssh"
"github.com/jmoiron/sqlx"
gossh "golang.org/x/crypto/ssh"
)
type Backend struct {
Logger *slog.Logger
DB *sqlx.DB
Cfg *GitCfg
}
var ErrRepoNoNamespace = fmt.Errorf("repo must be namespaced by username")
// Repo Namespace.
func (be *Backend) CreateRepoNs(userName, repoName string) string {
if be.Cfg.CreateRepo == "admin" {
return repoName
}
return fmt.Sprintf("%s/%s", userName, repoName)
}
func (be *Backend) ValidateRepoNs(repoNs string) error {
_, repoID := be.SplitRepoNs(repoNs)
if strings.Contains(repoID, "/") {
return fmt.Errorf("repo can only contain a single forward-slash")
}
return nil
}
func (be *Backend) SplitRepoNs(repoNs string) (string, string) {
results := strings.SplitN(repoNs, "/", 2)
if len(results) == 1 {
return "", results[0]
}
return results[0], results[1]
}
func (be *Backend) CanCreateRepo(repo *Repo, requester *User) error {
pubkey, err := be.PubkeyToPublicKey(requester.Pubkey)
if err != nil {
return err
}
isAdmin := be.IsAdmin(pubkey)
if isAdmin {
return nil
}
// can create repo is a misnomer since we are saying it's ok to create
// a repo even though one already exists. this is a hack since this function
// is used exclusively inside pr creation flow.
if repo != nil {
return nil
}
if be.Cfg.CreateRepo == "user" {
return nil
}
// new repo with cfg indicating only admins can create prs/repos
return fmt.Errorf("you are not authorized to create repo")
}
func (be *Backend) Pubkey(pk ssh.PublicKey) string {
return be.KeyForKeyText(pk)
}
func (be *Backend) KeyForFingerprint(pk ssh.PublicKey) string {
return gossh.FingerprintSHA256(pk)
}
func (be *Backend) PubkeyToPublicKey(pubkey string) (ssh.PublicKey, error) {
kk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(pubkey))
return kk, err
}
func (be *Backend) KeyForKeyText(pk ssh.PublicKey) string {
kb := base64.StdEncoding.EncodeToString(pk.Marshal())
return fmt.Sprintf("%s %s", pk.Type(), kb)
}
func (be *Backend) KeysEqual(pka, pkb string) bool {
return pka == pkb
}
func (be *Backend) IsAdmin(pk ssh.PublicKey) bool {
for _, apk := range be.Cfg.Admins {
if ssh.KeysEqual(pk, apk) {
return true
}
}
return false
}
func (be *Backend) IsPrOwner(pka, pkb int64) bool {
return pka == pkb
}
type PrAcl struct {
CanModify bool
CanReview bool
CanDelete bool
}
func (be *Backend) GetPatchRequestAcl(repo *Repo, prq *PatchRequest, requester *User) *PrAcl {
acl := &PrAcl{}
if requester == nil {
return acl
}
pubkey, err := be.PubkeyToPublicKey(requester.Pubkey)
if err != nil {
return acl
}
isAdmin := be.IsAdmin(pubkey)
// admin can do it all
if isAdmin {
acl.CanModify = true
acl.CanReview = true
acl.CanDelete = true
return acl
}
// repo owner can do it all
if repo.UserID == requester.ID {
acl.CanModify = true
acl.CanReview = true
acl.CanDelete = true
return acl
}
// pr creator have special priv
if be.IsPrOwner(prq.UserID, requester.ID) {
acl.CanModify = true
acl.CanReview = false
acl.CanDelete = true
return acl
}
// otherwise no perms
acl.CanModify = false
acl.CanReview = false
acl.CanDelete = false
return acl
}