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.
forge/session/session_test.go +23−23
| @@ -52,7 +52,7 @@ func (f *fixture) account(t *testing.T, name string) *identity.Account { | |||
| 52 | 52 | if err != nil { | |
| 53 | 53 | t.Fatal(err) | |
| 54 | 54 | } | |
| 55 | a, err := f.ident.Create(name, "", strings.TrimSpace(string(ssh.MarshalAuthorizedKey(key))), "test") | ||
| 55 | a, err := f.ident.Create(name, strings.TrimSpace(string(ssh.MarshalAuthorizedKey(key))), "test") | ||
| 56 | 56 | if err != nil { | |
| 57 | 57 | t.Fatal(err) | |
| 58 | 58 | } | |
| @@ -70,7 +70,7 @@ func (f *fixture) credential(t *testing.T, a *identity.Account) identity.Credent | |||
| 70 | 70 | t.Fatal(err) | |
| 71 | 71 | } | |
| 72 | 72 | cred := identity.Credential{ID: id, PublicKey: pub, Label: "laptop"} | |
| 73 | if err := f.ident.AddCredential(a.ID, cred, "test"); err != nil { | ||
| 73 | if err := f.ident.AddCredential(a.Name, cred, "test"); err != nil { | ||
| 74 | 74 | t.Fatal(err) | |
| 75 | 75 | } | |
| 76 | 76 | return cred | |
| @@ -80,7 +80,7 @@ func TestCreateLookupAndRevoke(t *testing.T) { | |||
| 80 | 80 | t.Parallel() | |
| 81 | 81 | f := newFixture(t) | |
| 82 | 82 | a := f.account(t, "alice") | |
| 83 | sess, err := f.store.Create(a.ID, nil, "test-agent") | ||
| 83 | sess, err := f.store.Create(a.Name, nil, "test-agent") | ||
| 84 | 84 | if err != nil { | |
| 85 | 85 | t.Fatal(err) | |
| 86 | 86 | } | |
| @@ -99,7 +99,7 @@ func TestCreateLookupAndRevoke(t *testing.T) { | |||
| 99 | 99 | if err != nil { | |
| 100 | 100 | t.Fatal(err) | |
| 101 | 101 | } | |
| 102 | if got.AccountID != a.ID || got.UserAgent != "test-agent" || got.Token != "" { | ||
| 102 | if got.Name != a.Name || got.UserAgent != "test-agent" || got.Token != "" { | ||
| 103 | 103 | t.Fatalf("lookup = %+v", got) | |
| 104 | 104 | } | |
| 105 | 105 | if _, err := f.store.Lookup("not-a-token"); !errors.Is(err, ErrNoSession) { | |
| @@ -122,7 +122,7 @@ func TestIdleExpiryAndSlidingUse(t *testing.T) { | |||
| 122 | 122 | f.store.Idle = time.Hour | |
| 123 | 123 | f.store.Absolute = 24 * time.Hour | |
| 124 | 124 | a := f.account(t, "alice") | |
| 125 | sess, err := f.store.Create(a.ID, nil, "agent") | ||
| 125 | sess, err := f.store.Create(a.Name, nil, "agent") | ||
| 126 | 126 | if err != nil { | |
| 127 | 127 | t.Fatal(err) | |
| 128 | 128 | } | |
| @@ -147,7 +147,7 @@ func TestAbsoluteExpiry(t *testing.T) { | |||
| 147 | 147 | f.store.Idle = 100 * time.Hour | |
| 148 | 148 | f.store.Absolute = 2 * time.Hour | |
| 149 | 149 | a := f.account(t, "alice") | |
| 150 | sess, err := f.store.Create(a.ID, nil, "agent") | ||
| 150 | sess, err := f.store.Create(a.Name, nil, "agent") | ||
| 151 | 151 | if err != nil { | |
| 152 | 152 | t.Fatal(err) | |
| 153 | 153 | } | |
| @@ -161,7 +161,7 @@ func TestLastSeenIsWrittenCoarsely(t *testing.T) { | |||
| 161 | 161 | t.Parallel() | |
| 162 | 162 | f := newFixture(t) | |
| 163 | 163 | a := f.account(t, "alice") | |
| 164 | sess, err := f.store.Create(a.ID, nil, "agent") | ||
| 164 | sess, err := f.store.Create(a.Name, nil, "agent") | ||
| 165 | 165 | if err != nil { | |
| 166 | 166 | t.Fatal(err) | |
| 167 | 167 | } | |
| @@ -171,7 +171,7 @@ func TestLastSeenIsWrittenCoarsely(t *testing.T) { | |||
| 171 | 171 | t.Fatal(err) | |
| 172 | 172 | } | |
| 173 | 173 | var seen int64 | |
| 174 | if err := f.store.db.QueryRow(`SELECT last_seen FROM sessions WHERE account_id=?`, a.ID).Scan(&seen); err != nil { | ||
| 174 | if err := f.store.db.QueryRow(`SELECT last_seen FROM sessions WHERE user_name=?`, a.Name).Scan(&seen); err != nil { | ||
| 175 | 175 | t.Fatal(err) | |
| 176 | 176 | } | |
| 177 | 177 | if seen != f.now.Unix() || seen == first { | |
| @@ -183,7 +183,7 @@ func TestLastSeenIsWrittenCoarsely(t *testing.T) { | |||
| 183 | 183 | t.Fatal(err) | |
| 184 | 184 | } | |
| 185 | 185 | var again int64 | |
| 186 | if err := f.store.db.QueryRow(`SELECT last_seen FROM sessions WHERE account_id=?`, a.ID).Scan(&again); err != nil { | ||
| 186 | if err := f.store.db.QueryRow(`SELECT last_seen FROM sessions WHERE user_name=?`, a.Name).Scan(&again); err != nil { | ||
| 187 | 187 | t.Fatal(err) | |
| 188 | 188 | } | |
| 189 | 189 | if again != seen { | |
| @@ -196,11 +196,11 @@ func TestRevokeAccountAndCredential(t *testing.T) { | |||
| 196 | 196 | f := newFixture(t) | |
| 197 | 197 | a := f.account(t, "alice") | |
| 198 | 198 | cred := f.credential(t, a) | |
| 199 | byCredential, err := f.store.Create(a.ID, cred.ID, "laptop") | ||
| 199 | byCredential, err := f.store.Create(a.Name, cred.ID, "laptop") | ||
| 200 | 200 | if err != nil { | |
| 201 | 201 | t.Fatal(err) | |
| 202 | 202 | } | |
| 203 | byCookie, err := f.store.Create(a.ID, nil, "other") | ||
| 203 | byCookie, err := f.store.Create(a.Name, nil, "other") | ||
| 204 | 204 | if err != nil { | |
| 205 | 205 | t.Fatal(err) | |
| 206 | 206 | } | |
| @@ -217,7 +217,7 @@ func TestRevokeAccountAndCredential(t *testing.T) { | |||
| 217 | 217 | if _, err := f.store.Lookup(byCookie.Token); err != nil { | |
| 218 | 218 | t.Fatalf("unrelated session was revoked: %v", err) | |
| 219 | 219 | } | |
| 220 | n, err = f.store.RevokeAccount(a.ID) | ||
| 220 | n, err = f.store.RevokeAccount(a.Name) | ||
| 221 | 221 | if err != nil { | |
| 222 | 222 | t.Fatal(err) | |
| 223 | 223 | } | |
| @@ -234,27 +234,27 @@ func TestRetireAndCredentialRemovalEndSessions(t *testing.T) { | |||
| 234 | 234 | f := newFixture(t) | |
| 235 | 235 | a := f.account(t, "alice") | |
| 236 | 236 | cred := f.credential(t, a) | |
| 237 | sess, err := f.store.Create(a.ID, cred.ID, "laptop") | ||
| 237 | sess, err := f.store.Create(a.Name, cred.ID, "laptop") | ||
| 238 | 238 | if err != nil { | |
| 239 | 239 | t.Fatal(err) | |
| 240 | 240 | } | |
| 241 | if err := f.ident.RemoveCredential(a.ID, cred.ID, "test"); err != nil { | ||
| 241 | if err := f.ident.RemoveCredential(a.Name, cred.ID, "test"); err != nil { | ||
| 242 | 242 | t.Fatal(err) | |
| 243 | 243 | } | |
| 244 | 244 | if _, err := f.store.Lookup(sess.Token); !errors.Is(err, ErrNoSession) { | |
| 245 | 245 | t.Fatal("removing the passkey left its session alive") | |
| 246 | 246 | } | |
| 247 | sess, err = f.store.Create(a.ID, nil, "cookie") | ||
| 247 | sess, err = f.store.Create(a.Name, nil, "cookie") | ||
| 248 | 248 | if err != nil { | |
| 249 | 249 | t.Fatal(err) | |
| 250 | 250 | } | |
| 251 | if _, err := f.ident.Retire(a.ID, "test"); err != nil { | ||
| 251 | if _, err := f.ident.Retire(a.Name, "test"); err != nil { | ||
| 252 | 252 | t.Fatal(err) | |
| 253 | 253 | } | |
| 254 | 254 | if _, err := f.store.Lookup(sess.Token); !errors.Is(err, ErrNoSession) { | |
| 255 | 255 | t.Fatal("retiring the account left its session alive") | |
| 256 | 256 | } | |
| 257 | if _, err := f.store.Create(a.ID, nil, "retired"); !errors.Is(err, ErrNoAccount) { | ||
| 257 | if _, err := f.store.Create(a.Name, nil, "retired"); !errors.Is(err, ErrNoAccount) { | ||
| 258 | 258 | t.Fatalf("created a session for a retired account: %v", err) | |
| 259 | 259 | } | |
| 260 | 260 | } | |
| @@ -263,14 +263,14 @@ func TestListShowsLiveSessionsWithoutTokens(t *testing.T) { | |||
| 263 | 263 | t.Parallel() | |
| 264 | 264 | f := newFixture(t) | |
| 265 | 265 | a := f.account(t, "alice") | |
| 266 | if _, err := f.store.Create(a.ID, nil, "one"); err != nil { | ||
| 266 | if _, err := f.store.Create(a.Name, nil, "one"); err != nil { | ||
| 267 | 267 | t.Fatal(err) | |
| 268 | 268 | } | |
| 269 | 269 | f.now = f.now.Add(time.Minute) | |
| 270 | if _, err := f.store.Create(a.ID, nil, "two"); err != nil { | ||
| 270 | if _, err := f.store.Create(a.Name, nil, "two"); err != nil { | ||
| 271 | 271 | t.Fatal(err) | |
| 272 | 272 | } | |
| 273 | list, err := f.store.List(a.ID) | ||
| 273 | list, err := f.store.List(a.Name) | ||
| 274 | 274 | if err != nil { | |
| 275 | 275 | t.Fatal(err) | |
| 276 | 276 | } | |
| @@ -290,11 +290,11 @@ func TestRevokeAll(t *testing.T) { | |||
| 290 | 290 | f := newFixture(t) | |
| 291 | 291 | alice := f.account(t, "alice") | |
| 292 | 292 | bob := f.account(t, "bob") | |
| 293 | a, err := f.store.Create(alice.ID, nil, "one") | ||
| 293 | a, err := f.store.Create(alice.Name, nil, "one") | ||
| 294 | 294 | if err != nil { | |
| 295 | 295 | t.Fatal(err) | |
| 296 | 296 | } | |
| 297 | b, err := f.store.Create(bob.ID, nil, "two") | ||
| 297 | b, err := f.store.Create(bob.Name, nil, "two") | ||
| 298 | 298 | if err != nil { | |
| 299 | 299 | t.Fatal(err) | |
| 300 | 300 | } | |
| @@ -318,7 +318,7 @@ func TestCreateRejectsAnotherAccountsPasskey(t *testing.T) { | |||
| 318 | 318 | alice := f.account(t, "alice") | |
| 319 | 319 | bob := f.account(t, "bob") | |
| 320 | 320 | cred := f.credential(t, alice) | |
| 321 | if _, err := f.store.Create(bob.ID, cred.ID, "wrong"); !errors.Is(err, ErrCredentialMismatch) { | ||
| 321 | if _, err := f.store.Create(bob.Name, cred.ID, "wrong"); !errors.Is(err, ErrCredentialMismatch) { | ||
| 322 | 322 | t.Fatalf("created a session for somebody else's passkey: %v", err) | |
| 323 | 323 | } | |
| 324 | 324 | } | |