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.

Unify account terminology and clarify ACL commands

2e024cc96a391c0959ad2914caeed78b600fcfc1 · Keenan Nemetz · 14d ago · parent ab326d3a

Unify account terminology and clarify ACL commands

Use account consistently in the CLI, help, docs, and remote admin messages. Rename the CLI account command from user to account while retaining the old user spelling as an alias.

Replace positional ACL mutations with explicit verbs such as set-role, protect add, runners remove, and checks enable. Update local and remote admin parsing, hints, docs, and tests.

Tidy repository filter alignment and remove duplicated issue/change counts from page subtitles.

server/ssh/admin.go +209−115

@@ -108,8 +108,9 @@ func splitAdminJSON(argv []string) (jsonOut bool, rest []string, err error) {
108 108 func (s *Server) runAdmin(args []string, actor string, in io.Reader) (any, adminRender, error) {
109 109 resource, action, rest := args[0], args[1], args[2:]
110 110 switch resource {
111 case "user":
112 return s.adminUser(action, rest, actor, in)
111 case "account", "user":
112 // "user" is the historical spelling of this resource.
113 return s.adminAccount(action, rest, actor, in)
113 114 case "repo":
114 115 return s.adminRepo(action, rest, actor)
115 116 case "acl":
@@ -118,17 +119,17 @@ func (s *Server) runAdmin(args []string, actor string, in io.Reader) (any, admin
118 119 return nil, nil, &adminUsageError{msg: fmt.Sprintf("unknown admin resource %q", resource)}
119 120 }
120 121
121 func (s *Server) adminUser(action string, args []string, actor string, in io.Reader) (any, adminRender, error) {
122 func (s *Server) adminAccount(action string, args []string, actor string, in io.Reader) (any, adminRender, error) {
122 123 switch action {
123 124 case "list":
124 125 all := s.cfg.Identity.All()
125 126 data := make([]map[string]any, 0, len(all))
126 127 for _, a := range all {
127 data = append(data, userData(a))
128 data = append(data, accountData(a))
128 129 }
129 130 return data, func(out, _ io.Writer) {
130 131 if len(all) == 0 {
131 fmt.Fprintln(out, "no users")
132 fmt.Fprintln(out, "no accounts")
132 133 return
133 134 }
134 135 for _, a := range all {
@@ -141,13 +142,13 @@ func (s *Server) adminUser(action string, args []string, actor string, in io.Rea
141 142
142 143 case "show":
143 144 if len(args) != 1 {
144 return nil, nil, &adminUsageError{msg: "usage: vole-admin user show <name>"}
145 return nil, nil, &adminUsageError{msg: "usage: vole-admin account show <name>"}
145 146 }
146 147 a, err := s.cfg.Identity.Find(args[0])
147 148 if err != nil {
148 149 return nil, nil, err
149 150 }
150 return userData(a), func(out, _ io.Writer) {
151 return accountData(a), func(out, _ io.Writer) {
151 152 fmt.Fprintf(out, "name: %s\nstatus: %s\nrole: %s\nadmin: %t\ncreated: %s\n",
152 153 a.Name, a.Status, a.Role, a.Admin, a.Created.Format(time.RFC3339))
153 154 for _, k := range a.Keys {
@@ -163,7 +164,7 @@ func (s *Server) adminUser(action string, args []string, actor string, in io.Rea
163 164 return nil, nil, err
164 165 }
165 166 if len(f.rest) != 1 {
166 return nil, nil, &adminUsageError{msg: "usage: vole-admin user create [--no-key] [--role <role>] <name> (public key on stdin unless --no-key)"}
167 return nil, nil, &adminUsageError{msg: "usage: vole-admin account create [--no-key] [--role <role>] <name> (public key on stdin unless --no-key)"}
167 168 }
168 169 role, err := access.ParseRole(f.one("role"))
169 170 if err != nil {
@@ -174,7 +175,7 @@ func (s *Server) adminUser(action string, args []string, actor string, in io.Rea
174 175 if err != nil {
175 176 return nil, nil, err
176 177 }
177 data := map[string]any{"account": userData(a), "invite": s.inviteData(inv)}
178 data := map[string]any{"account": accountData(a), "invite": s.inviteData(inv)}
178 179 return data, func(out, errOut io.Writer) {
179 180 fmt.Fprintln(out, a.Name)
180 181 s.renderInvite(inv, out)
@@ -188,7 +189,7 @@ func (s *Server) adminUser(action string, args []string, actor string, in io.Rea
188 189 if err != nil {
189 190 return nil, nil, err
190 191 }
191 return userData(a), func(out, errOut io.Writer) {
192 return accountData(a), func(out, errOut io.Writer) {
192 193 fmt.Fprintln(out, a.Name)
193 194 if len(a.Keys) > 0 {
194 195 fmt.Fprintf(errOut, "key %s\n", a.Keys[0].Fingerprint)
@@ -197,7 +198,7 @@ func (s *Server) adminUser(action string, args []string, actor string, in io.Rea
197 198
198 199 case "role":
199 200 if len(args) != 2 {
200 return nil, nil, &adminUsageError{msg: "usage: vole-admin user role <name> <none|read|contribute|maintain|admin>"}
201 return nil, nil, &adminUsageError{msg: "usage: vole-admin account role <name> <none|read|contribute|maintain|admin>"}
201 202 }
202 203 role, err := access.ParseRole(args[1])
203 204 if err != nil {
@@ -207,13 +208,13 @@ func (s *Server) adminUser(action string, args []string, actor string, in io.Rea
207 208 if err != nil {
208 209 return nil, nil, err
209 210 }
210 return userData(a), func(out, _ io.Writer) {
211 return accountData(a), func(out, _ io.Writer) {
211 212 fmt.Fprintf(out, "%s: %s\n", a.Name, a.Role)
212 213 }, nil
213 214
214 215 case "invite":
215 216 if len(args) != 1 {
216 return nil, nil, &adminUsageError{msg: "usage: vole-admin user invite <name>"}
217 return nil, nil, &adminUsageError{msg: "usage: vole-admin account invite <name>"}
217 218 }
218 219 inv, err := s.cfg.Identity.Invite(args[0], actor, 0)
219 220 if err != nil {
@@ -225,7 +226,7 @@ func (s *Server) adminUser(action string, args []string, actor string, in io.Rea
225 226
226 227 case "credentials":
227 228 if len(args) != 1 {
228 return nil, nil, &adminUsageError{msg: "usage: vole-admin user credentials <name>"}
229 return nil, nil, &adminUsageError{msg: "usage: vole-admin account credentials <name>"}
229 230 }
230 231 creds, err := s.cfg.Identity.Credentials(args[0])
231 232 if err != nil {
@@ -254,7 +255,7 @@ func (s *Server) adminUser(action string, args []string, actor string, in io.Rea
254 255
255 256 case "rm-credential":
256 257 if len(args) != 2 {
257 return nil, nil, &adminUsageError{msg: "usage: vole-admin user rm-credential <name> <credential-id>"}
258 return nil, nil, &adminUsageError{msg: "usage: vole-admin account rm-credential <name> <credential-id>"}
258 259 }
259 260 id, err := identity.ParseCredentialID(args[1])
260 261 if err != nil {
@@ -269,7 +270,7 @@ func (s *Server) adminUser(action string, args []string, actor string, in io.Rea
269 270
270 271 case "sessions":
271 272 if len(args) != 1 {
272 return nil, nil, &adminUsageError{msg: "usage: vole-admin user sessions <name>"}
273 return nil, nil, &adminUsageError{msg: "usage: vole-admin account sessions <name>"}
273 274 }
274 275 a, err := s.cfg.Identity.Find(args[0])
275 276 if err != nil {
@@ -304,7 +305,7 @@ func (s *Server) adminUser(action string, args []string, actor string, in io.Rea
304 305 }
305 306 if f.has("all") {
306 307 if len(f.rest) != 0 {
307 return nil, nil, &adminUsageError{msg: "usage: vole-admin user revoke-sessions [--all | <name>]"}
308 return nil, nil, &adminUsageError{msg: "usage: vole-admin account revoke-sessions [--all | <name>]"}
308 309 }
309 310 n, err := s.cfg.Instance.Sessions.RevokeAll()
310 311 if err != nil {
@@ -315,7 +316,7 @@ func (s *Server) adminUser(action string, args []string, actor string, in io.Rea
315 316 }, nil
316 317 }
317 318 if len(f.rest) != 1 {
318 return nil, nil, &adminUsageError{msg: "usage: vole-admin user revoke-sessions [--all | <name>]"}
319 return nil, nil, &adminUsageError{msg: "usage: vole-admin account revoke-sessions [--all | <name>]"}
319 320 }
320 321 a, err := s.cfg.Identity.Find(f.rest[0])
321 322 if err != nil {
@@ -325,13 +326,13 @@ func (s *Server) adminUser(action string, args []string, actor string, in io.Rea
325 326 if err != nil {
326 327 return nil, nil, err
327 328 }
328 return map[string]any{"revoked": n, "account": userData(a)}, func(out, _ io.Writer) {
329 return map[string]any{"revoked": n, "account": accountData(a)}, func(out, _ io.Writer) {
329 330 fmt.Fprintf(out, "revoked %d session(s) for %s\n", n, a.Name)
330 331 }, nil
331 332
332 333 case "add-key":
333 334 if len(args) != 1 {
334 return nil, nil, &adminUsageError{msg: "usage: vole-admin user add-key <name>"}
335 return nil, nil, &adminUsageError{msg: "usage: vole-admin account add-key <name>"}
335 336 }
336 337 key, err := readAdminKey(in)
337 338 if err != nil {
@@ -342,14 +343,14 @@ func (s *Server) adminUser(action string, args []string, actor string, in io.Rea
342 343 return nil, nil, err
343 344 }
344 345 a, _ := s.cfg.Identity.Find(args[0])
345 data := map[string]any{"fingerprint": fp, "account": userData(a)}
346 data := map[string]any{"fingerprint": fp, "account": accountData(a)}
346 347 return data, func(out, _ io.Writer) {
347 348 fmt.Fprintf(out, "added key %s for %s\n", fp, a.Name)
348 349 }, nil
349 350
350 351 case "rm-key":
351 352 if len(args) != 2 {
352 return nil, nil, &adminUsageError{msg: "usage: vole-admin user rm-key <name> <fingerprint>"}
353 return nil, nil, &adminUsageError{msg: "usage: vole-admin account rm-key <name> <fingerprint>"}
353 354 }
354 355 if err := s.cfg.Identity.RemoveKey(args[0], args[1], actor); err != nil {
355 356 return nil, nil, err
@@ -363,29 +364,29 @@ func (s *Server) adminUser(action string, args []string, actor string, in io.Rea
363 364
364 365 case "retire", "rm", "delete":
365 366 if len(args) != 1 {
366 return nil, nil, &adminUsageError{msg: "usage: vole-admin user retire <name>"}
367 return nil, nil, &adminUsageError{msg: "usage: vole-admin account retire <name>"}
367 368 }
368 369 a, err := s.cfg.Identity.Retire(args[0], actor)
369 370 if err != nil {
370 371 return nil, nil, err
371 372 }
372 return userData(a), func(out, errOut io.Writer) {
373 return accountData(a), func(out, errOut io.Writer) {
373 374 fmt.Fprintln(out, "retired "+a.Name)
374 375 fmt.Fprintln(errOut, "keys stop working within 5s; the name is never reissued")
375 376 }, nil
376 377
377 378 case "backup":
378 return nil, nil, &adminUsageError{msg: "user backup writes to the server's filesystem; run it locally"}
379 return nil, nil, &adminUsageError{msg: "account backup writes to the server's filesystem; run it locally"}
379 380
380 381 case "grant-admin", "revoke-admin":
381 382 if len(args) != 1 {
382 return nil, nil, &adminUsageError{msg: "usage: vole-admin user " + action + " <name>"}
383 return nil, nil, &adminUsageError{msg: "usage: vole-admin account " + action + " <name>"}
383 384 }
384 385 a, err := s.cfg.Identity.SetAdmin(args[0], action == "grant-admin", actor)
385 386 if err != nil {
386 387 return nil, nil, err
387 388 }
388 return userData(a), func(out, _ io.Writer) {
389 return accountData(a), func(out, _ io.Writer) {
389 390 if action == "grant-admin" {
390 391 fmt.Fprintln(out, "granted instance admin to "+a.Name)
391 392 } else {
@@ -393,7 +394,7 @@ func (s *Server) adminUser(action string, args []string, actor string, in io.Rea
393 394 }
394 395 }, nil
395 396 }
396 return nil, nil, &adminUsageError{msg: fmt.Sprintf("unknown user command %q", action)}
397 return nil, nil, &adminUsageError{msg: fmt.Sprintf("unknown account command %q", action)}
397 398 }
398 399
399 400 func (s *Server) adminRepo(action string, args []string, actor string) (any, adminRender, error) {
@@ -455,7 +456,7 @@ func (s *Server) adminRepo(action string, args []string, actor string) (any, adm
455 456 return nil, nil, err
456 457 }
457 458 if len(f.rest) != 1 || f.one("owner") == "" {
458 return nil, nil, &adminUsageError{msg: "usage: vole-admin repo create --owner <user> <name> [--description <text>]"}
459 return nil, nil, &adminUsageError{msg: "usage: vole-admin repo create --owner <account> <name> [--description <text>]"}
459 460 }
460 461 owner, err := s.cfg.Identity.Find(f.one("owner"))
461 462 if err != nil {
@@ -477,7 +478,7 @@ func (s *Server) adminRepo(action string, args []string, actor string) (any, adm
477 478 }
478 479 return data, func(out, errOut io.Writer) {
479 480 fmt.Fprintf(out, "created %s (id %s)\n", m.Name, m.ID)
480 fmt.Fprintf(errOut, "new repositories are private; publish with: vole admin acl default %s read\n", m.Name)
481 fmt.Fprintf(errOut, "new repositories are private; publish with: vole admin acl set-default-role %s read\n", m.Name)
481 482 }, nil
482 483
483 484 case "rename":
@@ -506,10 +507,78 @@ func (s *Server) adminRepo(action string, args []string, actor string) (any, adm
506 507 }
507 508
508 509 func (s *Server) adminACL(action string, args []string, actor string) (any, adminRender, error) {
509 if len(args) == 0 {
510 return nil, nil, &adminUsageError{msg: "usage: vole-admin acl <show|set|default|protect|approvals|checks|runners> <repo> ..."}
510 var (
511 repoName string
512 mode string
513 accountName string
514 roleName string
515 approvals int
516 values []string
517 )
518 switch action {
519 case "show":
520 if len(args) != 1 {
521 return nil, nil, &adminUsageError{msg: "usage: vole-admin acl show <repo>"}
522 }
523 repoName = args[0]
524
525 case "set-role":
526 if len(args) != 3 {
527 return nil, nil, &adminUsageError{msg: "usage: vole-admin acl set-role <repo> <account> <none|read|contribute|maintain|admin>"}
528 }
529 repoName, accountName, roleName = args[0], args[1], args[2]
530
531 case "set-default-role":
532 if len(args) != 2 {
533 return nil, nil, &adminUsageError{msg: "usage: vole-admin acl set-default-role <repo> <none|read|contribute|maintain|admin>"}
534 }
535 repoName, roleName = args[0], args[1]
536
537 case "set-approvals":
538 if len(args) != 2 {
539 return nil, nil, &adminUsageError{msg: "usage: vole-admin acl set-approvals <repo> <n>"}
540 }
541 repoName = args[0]
542 n, err := strconv.Atoi(args[1])
543 if err != nil || n < 0 {
544 return nil, nil, &adminUsageError{msg: fmt.Sprintf("%q is not a number of approvals", args[1])}
545 }
546 approvals = n
547
548 case "checks":
549 if len(args) != 2 {
550 return nil, nil, &adminUsageError{msg: "usage: vole-admin acl checks enable|disable <repo>"}
551 }
552 mode, repoName = args[0], args[1]
553 if mode != "enable" && mode != "disable" {
554 return nil, nil, &adminUsageError{msg: fmt.Sprintf("checks takes enable or disable, not %q", mode)}
555 }
556
557 case "protect", "runners":
558 if len(args) < 2 {
559 return nil, nil, &adminUsageError{msg: fmt.Sprintf("usage: vole-admin acl %s list|add|remove|set|clear <repo> ...", action)}
560 }
561 mode, repoName, values = args[0], args[1], args[2:]
562 switch mode {
563 case "list", "clear":
564 if len(values) > 0 {
565 return nil, nil, &adminUsageError{msg: fmt.Sprintf("acl %s %s takes only a repository", action, mode)}
566 }
567 case "add", "remove", "set":
568 if len(values) == 0 {
569 if action == "protect" {
570 return nil, nil, &adminUsageError{msg: fmt.Sprintf("acl protect %s needs at least one ref pattern", mode)}
571 }
572 return nil, nil, &adminUsageError{msg: fmt.Sprintf("acl runners %s needs at least one account", mode)}
573 }
574 default:
575 return nil, nil, &adminUsageError{msg: fmt.Sprintf("unknown acl %s command %q", action, mode)}
576 }
577
578 default:
579 return nil, nil, &adminUsageError{msg: "usage: vole-admin acl <show|set-role|set-default-role|set-approvals|checks|protect|runners> <repo> ..."}
511 580 }
512 repoName := args[0]
581
513 582 m, err := s.cfg.Instance.Repos.Open(repoName)
514 583 if err != nil {
515 584 return nil, nil, fmt.Errorf("repository %q not found", repoName)
@@ -530,20 +599,14 @@ func (s *Server) adminACL(action string, args []string, actor string) (any, admi
530 599
531 600 switch action {
532 601 case "show":
533 if len(args) != 1 {
534 return nil, nil, &adminUsageError{msg: "usage: vole-admin acl show <repo>"}
535 }
536 602 return s.aclResult(m, a)
537 603
538 case "set":
539 if len(args) != 3 {
540 return nil, nil, &adminUsageError{msg: "usage: vole-admin acl set <repo> <user> <none|read|contribute|maintain|admin>"}
541 }
542 role, err := access.ParseRole(args[2])
604 case "set-role":
605 role, err := access.ParseRole(roleName)
543 606 if err != nil {
544 607 return nil, nil, err
545 608 }
546 account, err := s.cfg.Identity.Find(args[1])
609 account, err := s.cfg.Identity.Find(accountName)
547 610 if err != nil {
548 611 return nil, nil, err
549 612 }
@@ -552,73 +615,70 @@ func (s *Server) adminACL(action string, args []string, actor string) (any, admi
552 615 }
553 616 return finish()
554 617
555 case "default":
556 if len(args) != 2 {
557 return nil, nil, &adminUsageError{msg: "usage: vole-admin acl default <repo> <none|read|contribute|maintain|admin>"}
558 }
559 role, err := access.ParseRole(args[1])
618 case "set-default-role":
619 role, err := access.ParseRole(roleName)
560 620 if err != nil {
561 621 return nil, nil, err
562 622 }
563 623 a.Default = role.String()
564 624 return finish()
565 625
566 case "protect":
567 if len(args) < 2 {
568 return nil, nil, &adminUsageError{msg: "usage: vole-admin acl protect <repo> <ref-pattern>..."}
569 }
570 a.Protected = append([]string{}, args[1:]...)
626 case "set-approvals":
627 a.ApprovalsRequired = &approvals
571 628 return finish()
572 629
573 case "approvals":
574 if len(args) != 2 {
575 return nil, nil, &adminUsageError{msg: "usage: vole-admin acl approvals <repo> <n>"}
576 }
577 n, err := strconv.Atoi(args[1])
578 if err != nil || n < 0 {
579 return nil, nil, &adminUsageError{msg: fmt.Sprintf("%q is not a number of approvals", args[1])}
580 }
581 a.ApprovalsRequired = &n
630 case "checks":
631 v := mode == "enable"
632 a.ChecksRequired = &v
582 633 return finish()
583 634
584 case "checks":
585 if len(args) != 2 {
586 return nil, nil, &adminUsageError{msg: "usage: vole-admin acl checks <repo> <on|off>"}
587 }
588 switch strings.ToLower(args[1]) {
589 case "on", "true", "yes", "1":
590 v := true
591 a.ChecksRequired = &v
592 case "off", "false", "no", "0":
593 v := false
594 a.ChecksRequired = &v
595 default:
596 return nil, nil, &adminUsageError{msg: fmt.Sprintf("%q is not on or off", args[1])}
635 case "protect":
636 if mode == "list" {
637 return s.aclProtectedResult(m, a)
638 }
639 switch mode {
640 case "clear":
641 a.Protected = nil
642 case "set":
643 a.Protected = append([]string(nil), values...)
644 case "add":
645 for _, pattern := range values {
646 found := false
647 for _, existing := range a.Protected {
648 if existing == pattern {
649 found = true
650 break
651 }
652 }
653 if !found {
654 a.Protected = append(a.Protected, pattern)
655 }
656 }
657 case "remove":
658 next := append([]string(nil), a.Protected...)
659 for _, pattern := range values {
660 kept := make([]string, 0, len(next))
661 found := false
662 for _, existing := range next {
663 if existing == pattern {
664 found = true
665 continue
666 }
667 kept = append(kept, existing)
668 }
669 if !found {
670 return nil, nil, fmt.Errorf("%q is not protected for %s", pattern, m.Name)
671 }
672 next = kept
673 }
674 a.Protected = next
597 675 }
598 676 return finish()
599 677
600 678 case "runners":
601 f, err := parseAdminFlags(args[1:],
602 adminFlagSpec{long: "add", boolean: true},
603 adminFlagSpec{long: "remove", boolean: true},
604 adminFlagSpec{long: "clear", boolean: true})
605 if err != nil {
606 return nil, nil, err
679 if mode == "list" {
680 return s.aclRunnersResult(m, a)
607 681 }
608 names := f.rest
609 edits := 0
610 for _, b := range []string{"add", "remove", "clear"} {
611 if f.has(b) {
612 edits++
613 }
614 }
615 if edits > 1 {
616 return nil, nil, &adminUsageError{msg: "use one of --add, --remove or --clear"}
617 }
618 if len(names) == 0 && !f.has("clear") && !f.has("add") && !f.has("remove") {
619 return s.aclResult(m, a)
620 }
621 var next []string
622 682 resolve := func(name string) (string, error) {
623 683 account, err := s.cfg.Identity.Find(name)
624 684 if err != nil {
@@ -626,24 +686,34 @@ func (s *Server) adminACL(action string, args []string, actor string) (any, admi
626 686 }
627 687 return account.Name, nil
628 688 }
629 switch {
630 case f.has("clear"):
631 if len(names) > 0 {
632 return nil, nil, &adminUsageError{msg: "--clear takes no names"}
633 }
634 case f.has("add"), f.has("remove"):
635 if len(names) == 0 {
636 return nil, nil, &adminUsageError{msg: "--add and --remove need at least one account"}
689 var next []string
690 switch mode {
691 case "clear":
692 // next stays empty
693 case "set":
694 next = make([]string, 0, len(values))
695 for _, name := range values {
696 id, err := resolve(name)
697 if err != nil {
698 return nil, nil, err
699 }
700 next = append(next, id)
637 701 }
702 case "add":
638 703 next = append(next, a.Runners...)
639 for _, name := range names {
704 for _, name := range values {
640 705 id, err := resolve(name)
641 706 if err != nil {
642 707 return nil, nil, err
643 708 }
644 if !f.has("remove") {
645 next = append(next, id)
646 continue
709 next = append(next, id)
710 }
711 case "remove":
712 next = append(next, a.Runners...)
713 for _, name := range values {
714 id, err := resolve(name)
715 if err != nil {
716 return nil, nil, err
647 717 }
648 718 kept, found := next[:0], false
649 719 for _, r := range next {
@@ -658,14 +728,6 @@ func (s *Server) adminACL(action string, args []string, actor string) (any, admi
658 728 }
659 729 next = kept
660 730 }
661 default:
662 for _, name := range names {
663 id, err := resolve(name)
664 if err != nil {
665 return nil, nil, err
666 }
667 next = append(next, id)
668 }
669 731 }
670 732 a.SetRunners(next)
671 733 return finish()
@@ -673,6 +735,38 @@ func (s *Server) adminACL(action string, args []string, actor string) (any, admi
673 735 return nil, nil, &adminUsageError{msg: fmt.Sprintf("unknown acl command %q", action)}
674 736 }
675 737
738 func (s *Server) aclProtectedResult(m *repo.Meta, a *access.ACL) (any, adminRender, error) {
739 protected := append([]string(nil), a.Protected...)
740 data := map[string]any{"repo": m.Name, "protected": protected}
741 return data, func(out, _ io.Writer) {
742 if len(a.Protected) == 0 {
743 fmt.Fprintf(out, "protected: none for %s\n", m.Name)
744 return
745 }
746 fmt.Fprintf(out, "protected: %s\n", strings.Join(a.Protected, ", "))
747 }, nil
748 }
749
750 func (s *Server) aclRunnersResult(m *repo.Meta, a *access.ACL) (any, adminRender, error) {
751 runners := append([]string(nil), a.Runners...)
752 data := map[string]any{"repo": m.Name, "runners": runners}
753 return data, func(out, _ io.Writer) {
754 for _, r := range a.Runners {
755 fmt.Fprintf(out, "runner %s\n", r)
756 }
757 if len(a.Runners) > 0 {
758 fmt.Fprintln(out, "(admins may also report CI results)")
759 return
760 }
761 fmt.Fprintf(out, "runners: none named; only admins may report CI results for %s\n", m.Name)
762 for _, p := range a.Principals() {
763 if a.CanReport(p) {
764 fmt.Fprintf(out, " admin %s\n", p)
765 }
766 }
767 }, nil
768 }
769
676 770 func (s *Server) repoData(m *repo.Meta) (map[string]any, *access.ACL, error) {
677 771 a, err := m.ACL()
678 772 if err != nil {
@@ -770,7 +864,7 @@ func (s *Server) renderInvite(inv *identity.Invite, out io.Writer) {
770 864 fmt.Fprintf(out, "expires: %s\n", inv.Expires.Format(time.RFC3339))
771 865 }
772 866
773 func userData(a *identity.Account) map[string]any {
867 func accountData(a *identity.Account) map[string]any {
774 868 if a == nil {
775 869 return nil
776 870 }