Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Blame · server/web/capabilities.go

Line attribution on main · 88 line(s)

c717d4eb
Keenan Nemetz
13d ago
1 package web
2
3 // What a viewer may do in one repository, in the terms the pages use.
4 //
5 // Each function is one rule from forge/access, stated once. The tab that is
6 // shown, the button that is rendered, and the handler that would accept the write
7 // all ask the same question here, so a control cannot be offered to somebody the
8 // writer would refuse.
9 //
10 // The account name passed in is always the one a credential resolved to. Nothing
11 // here takes a name from a form field.
12
d37d301f
Keenan Nemetz
15h ago
13 import "rodent.software/vole/forge/access"
c717d4eb
Keenan Nemetz
13d ago
14
15 // canDiscuss reports whether the account may take part in a repository at all:
16 // file an issue, comment on one, or open a change request. Those are one
17 // capability because discussing is what each of them is.
18 func canDiscuss(acl *access.ACL, name string) bool {
19 return acl != nil && name != "" && acl.CanDiscuss(name)
20 }
21
22 // canOpenChangeRequest reports whether the account may open a change request. It
23 // is separate from canDiscuss because a repository can allow discussion while
24 // only trusting established contributors with requests.
25 func canOpenChangeRequest(acl *access.ACL, name string) bool {
26 return acl != nil && name != "" && acl.CanOpenRequest(name)
27 }
28
29 // canMaintain reports whether the account holds the repository's maintainer
30 // capability: triaging other people's issues, approving change requests,
31 // allowing contributor CI, and managing releases. It says nothing about whose
32 // item is in front of it; that is canTriage's job.
33 func canMaintain(acl *access.ACL, name string) bool {
34 return acl != nil && name != "" && acl.CanTriage(name)
35 }
36
37 // canTriage reports whether the account may manage one issue or change request:
38 // a maintainer may triage any of them, and its author may manage their own.
39 func canTriage(acl *access.ACL, name, author string) bool {
40 return canMaintain(acl, name) || (author != "" && author == name)
41 }
42
43 // canApprove reports whether the account may approve change requests here. It
44 // does not decide whether one request may be approved: approving your own is a
45 // separate rule (see canApproveRequest), because the reader is owed the reason
46 // that applies rather than a general refusal.
47 func canApprove(acl *access.ACL, name string) bool {
48 return canMaintain(acl, name)
49 }
50
51 // canApproveRequest is canApprove plus the self-approval rule. It is what the
52 // approve control is drawn from.
53 func canApproveRequest(acl *access.ACL, name, author string) bool {
54 return canApprove(acl, name) && name != author
55 }
56
38b30251
Keenan Nemetz
13d ago
57 // canMerge reports whether the account may land a change request here. It is the
58 // approve capability: landing is the act of accepting the review, so the accounts
59 // that may approve may merge. The author is not excluded — merging your own request
60 // after somebody else approved it is the point of separating the two — and whether
61 // this approval count is satisfied is the gate's question, not this one.
62 func canMerge(acl *access.ACL, name string) bool {
63 return canMaintain(acl, name)
64 }
65
c717d4eb
Keenan Nemetz
13d ago
66 // contributorOnly reports whether the account is a contributor and nothing more,
67 // so every branch it may push is inside its own contrib/ namespace. The rule
68 // matters to change requests: a contributor must open one from their own branch,
69 // because that is the only branch the pre-receive hook would have let them write.
70 func contributorOnly(acl *access.ACL, name string) bool {
71 return acl != nil && name != "" && acl.RoleFor(name) == access.Contribute
72 }
73
74 // canAdminister reports whether the account holds the repository's admin role.
75 // An instance admin gets this on every repository through EffectiveRole, which is
76 // why the read path asks canRead rather than this.
77 func canAdminister(acl *access.ACL, name string) bool {
78 return acl != nil && name != "" && acl.Can(name, access.Admin)
79 }
80
81 // canModerateComment reports whether the account may edit or delete a comment:
82 // the account that wrote it, or an admin repairing a thread.
83 func canModerateComment(acl *access.ACL, name, author string) bool {
84 if acl == nil || name == "" {
85 return false
86 }
87 return name == author || acl.RoleFor(name).AtLeast(access.Admin)
88 }