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.
Merge change request 01M70WES5ZZDJ6RP226WB11BBZ: Let people rename and remove their own passkeys
keenan · 24m ago
4b8c9e1d
| 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 | |
| 13 | import "rodent.software/vole/forge/access" |
| 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 | |
| 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 | |
| 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 | } |
| 89 |