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.
Identify accounts by their registered name
62576fa8157d431dcf2d360f3ebe481de77bda03 · Keenan Nemetz · 14d ago · parent 642e5994
Identify accounts by their registered name Drop the internal account id and display name. Users, keys, passkeys, invites, sessions, ACL entries, approvals, locks and CI records now all use the registered name as the single identity. The roster migrates in place: old id-keyed tables are rebuilt around the name, and access-rule/lock references are rewritten through the same mapping. Display-name editing and whoami --id are removed.
server/web/web.go +17−18
| @@ -73,7 +73,7 @@ func New(repos *repo.Store, ix *index.Store, ident *identity.Store, rs *runs.Sto | |||
| 73 | 73 | sem: make(chan struct{}, archiveConc)} | |
| 74 | 74 | // The templates are parsed after the Web exists so the roster-backed helper | |
| 75 | 75 | // below can be a method: metadata files carry account names and approvals are | |
| 76 | // keyed by ids, and a page should show the display label for either. | ||
| 76 | // keyed by names, and a page should show the account name for either. | ||
| 77 | 77 | w.Tmpl, err = template.New("vole").Funcs(template.FuncMap{ | |
| 78 | 78 | "since": humanSince, | |
| 79 | 79 | "short": format.SHA, | |
| @@ -107,18 +107,17 @@ func New(repos *repo.Store, ix *index.Store, ident *identity.Store, rs *runs.Sto | |||
| 107 | 107 | return w, nil | |
| 108 | 108 | } | |
| 109 | 109 | ||
| 110 | // who renders the account an id or name refers to as its display label. It is a | ||
| 111 | // presentation helper only: authors on disk stay account names and approvals | ||
| 112 | // stay ids, and nothing is authorized by what this returns. | ||
| 113 | func (w *Web) who(idOrName string) string { | ||
| 114 | if w.Ident == nil || idOrName == "" { | ||
| 115 | return idOrName | ||
| 110 | // who renders an account name for a page. It is a presentation helper only: | ||
| 111 | // nothing is authorized by what this returns. | ||
| 112 | func (w *Web) who(name string) string { | ||
| 113 | if w.Ident == nil || name == "" { | ||
| 114 | return name | ||
| 116 | 115 | } | |
| 117 | a, err := w.Ident.Find(idOrName) | ||
| 116 | a, err := w.Ident.Find(name) | ||
| 118 | 117 | if err != nil { | |
| 119 | return idOrName | ||
| 118 | return name | ||
| 120 | 119 | } | |
| 121 | return a.Label() | ||
| 120 | return a.Name | ||
| 122 | 121 | } | |
| 123 | 122 | ||
| 124 | 123 | // repoNav is the repository header's data: which repository, which tab is | |
| @@ -148,11 +147,11 @@ type repoNavData struct { | |||
| 148 | 147 | // Page is embedded by every view. Exported so html/template can promote its | |
| 149 | 148 | // fields; an unexported embedded type is not reachable from a template. | |
| 150 | 149 | type Page struct { | |
| 151 | Title string | ||
| 152 | Error string | ||
| 153 | LoggedIn bool | ||
| 154 | User string | ||
| 155 | AccountID string | ||
| 150 | Title string | ||
| 151 | Error string | ||
| 152 | LoggedIn bool | ||
| 153 | User string | ||
| 154 | AccountName string | ||
| 156 | 155 | // CSRF is derived from the session token and must be on every state-changing | |
| 157 | 156 | // form or fetch. | |
| 158 | 157 | CSRF string | |
| @@ -178,11 +177,11 @@ func (w *Web) canRead(r *http.Request, m *repo.Meta) bool { | |||
| 178 | 177 | if err != nil { | |
| 179 | 178 | return false | |
| 180 | 179 | } | |
| 181 | accountID := "" | ||
| 180 | name := "" | ||
| 182 | 181 | if p := w.principal(r); p != nil { | |
| 183 | accountID = p.account.ID | ||
| 182 | name = p.account.Name | ||
| 184 | 183 | } | |
| 185 | return a.Can(accountID, access.Read) | ||
| 184 | return a.Can(name, access.Read) | ||
| 186 | 185 | } | |
| 187 | 186 | ||
| 188 | 187 | // htmxRequest reports whether a request came from HTMX. The same handler still | |