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/vole.git

Write over SSH with a registered key:

git clone ssh://git@rodent.software:2222/vole.git

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

Add instance-wide roles and refine access UI

5b814e9958090cfd1fa826efb2d375bf613547c1 · Keenan Nemetz · 14d ago · parent 9579c1f9

Add instance-wide roles and refine access UI

Instance roles now live on accounts, apply to public repositories, and the Admin role is the unified instance-admin permission with access to every repository. Repository ACLs can raise an instance role but not lower it.

Adds role assignment to the users UI and CLI, open-registration role config, a reusable role picker, and separates adding a repository account from saving access rules.

server/hook/hook.go +16−4

@@ -16,6 +16,7 @@ import (
16 16 "github.com/rodent-software/vole/core/git"
17 17 "github.com/rodent-software/vole/core/lfs"
18 18 "github.com/rodent-software/vole/forge/access"
19 "github.com/rodent-software/vole/forge/identity"
19 20 "github.com/rodent-software/vole/forge/repo"
20 21 "github.com/rodent-software/vole/internal/format"
21 22 "github.com/rodent-software/vole/metadata"
@@ -73,6 +74,17 @@ func PreReceive(cfg Config) int {
73 74 return 1
74 75 }
75 76 defer acls.Close()
77 // The push gate runs as a separate process, so it opens the roster itself
78 // to learn the actor's instance-wide role. Without this the hook would
79 // enforce only repository entries and silently ignore an invitation that
80 // granted contribute everywhere.
81 ident, err := identity.Open(cfg.UsersDB)
82 if err != nil {
83 reject(cfg.Out, "cannot open the roster: %v", err)
84 return 1
85 }
86 defer ident.Close()
87 acls.InstanceRole = ident.InstanceRole
76 88 // The lock table shares that database, so the push path learns what is held
77 89 // without a second endpoint and without the client's cooperation.
78 90 lockTable, err = lfs.OpenLocks(cfg.UsersDB)
@@ -110,8 +122,8 @@ func PreReceive(cfg Config) int {
110 122 }
111 123
112 124 role := a.RoleFor(actor)
113 if !role.AtLeast(access.Contributor) {
114 reject(cfg.Out, "%s does not have maintain access or contributor access to %s (role: %s)", actor, m.Name, role)
125 if !role.AtLeast(access.Contribute) {
126 reject(cfg.Out, "%s does not have maintain or contribute access to %s (role: %s)", actor, m.Name, role)
115 127 return 1
116 128 }
117 129
@@ -155,7 +167,7 @@ func PreReceive(cfg Config) int {
155 167 // only knows that the account may push something.
156 168 if !role.AtLeast(access.Maintain) && !access.IsContributorRef(actor, c.ref) {
157 169 problems = append(problems, fmt.Sprintf(
158 "%s: contributors may only push branches under %s (role: %s)",
170 "%s: the contribute role may only push branches under %s (role: %s)",
159 171 c.ref, access.ContributorBranchPrefix(actor), role))
160 172 continue
161 173 }
@@ -182,7 +194,7 @@ func PreReceive(cfg Config) int {
182 194 case role.AtLeast(access.Admin):
183 195 auditLine(cfg.AuditPath, fmt.Sprintf("force-push %s %s -> %s by %s in %s",
184 196 c.ref, format.SHA(c.old), format.SHA(c.new), actor, m.Name))
185 case role == access.Contributor && access.IsContributorRef(actor, c.ref):
197 case role == access.Contribute && access.IsContributorRef(actor, c.ref):
186 198 // The namespace is theirs: rebasing or deleting their own branch is
187 199 // not a rewrite of shared history.
188 200 default: