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.

Allow opt-in open registration

f920a9f12c4ff12e4f47497a084f0201d8914181 · Keenan Nemetz · 16d ago · parent 6c84d387

Allow opt-in open registration

Add open_registration, off by default and effective only with an HTTPS public_url. /register creates an account and its first passkey without an administrator; the account row is written only after the WebAuthn ceremony succeeds, so an abandoned registration reserves no name.

New accounts start with no repository-specific ACL entries. Add self-service SSH key management on the Account page so a registered contributor can add the key needed to push to their contributor namespace.

Cover registration enablement, name validation, deferred account creation, and self-service key add/remove in tests, and document the policy.

forge/identity/passkey_test.go +27

@@ -282,3 +282,30 @@ VALUES(?, ?, ?, 1, 1, 9999999999)`, "hash-"+credentialIDString(id), a.ID, creden
282 282 t.Fatalf("removing one passkey removed another's session (count=%d)", n)
283 283 }
284 284 }
285
286 func TestCreatePasskeySelfRegistration(t *testing.T) {
287 t.Parallel()
288 s := newStore(t)
289 id := "01M5JR2JK29KKMAA1T9MDNTPBP"
290 cred := testCredential(t, "first passkey")
291 a, err := s.CreatePasskey("alice", id, cred, "self-registration")
292 if err != nil {
293 t.Fatal(err)
294 }
295 if a.ID != id || a.Name != "alice" || len(a.Keys) != 0 {
296 t.Fatalf("created account = %+v", a)
297 }
298 creds, err := s.Credentials(a.ID)
299 if err != nil || len(creds) != 1 || string(creds[0].ID) != string(cred.ID) {
300 t.Fatalf("credentials = %+v, %v", creds, err)
301 }
302
303 // A second registration for the same name must not leave an account behind.
304 other := "01M5JR2JK29KKMAA1T9MDNTPBQ"
305 if _, err := s.CreatePasskey("alice", other, testCredential(t, "other"), "self-registration"); err == nil {
306 t.Fatal("duplicate name was accepted")
307 }
308 if _, err := s.Find(other); err == nil {
309 t.Fatal("failed registration left an account behind")
310 }
311 }