eleven / ocho Public

Clone
git clone https://amadan.net/eleven/ocho

Plain git — no account needed to clone.

Download

Download this file

1package main
2
3import (
4 "encoding/json"
5 "net/http"
6 "strings"
7 "testing"
8)
9
10// The Go derivation must match crypto.js deriveInvite exactly, or a link minted
11// by add-instance would be unclaimable in the browser. The expected values are a
12// vector computed by the real crypto.js (node, WebCrypto) for T = bytes 0x00..0x0f.
13func TestDeriveInvitePartsMatchesCryptoJS(t *testing.T) {
14 id, claimSecret, claimHash, err := deriveInviteParts("AAECAwQFBgcICQoLDA0ODw")
15 if err != nil {
16 t.Fatalf("derive: %v", err)
17 }
18 if id != "5ReoOHe_g9MBI9A925thvq4BM7NNXPgx3tOzReMa6A4" {
19 t.Fatalf("id mismatch: %s", id)
20 }
21 if claimSecret != "Kog7DRX7AZVY-PF5H-8FjH7Oc6BOPacaZjLLRzJ9Q_g" {
22 t.Fatalf("claimSecret mismatch: %s", claimSecret)
23 }
24 if claimHash != "79b97fffe2f562bc4a5c6e6ab76147447aff96fd553fc4b1cd6cb83a537c0caa" {
25 t.Fatalf("claimHash mismatch: %s", claimHash)
26 }
27}
28
29// mintSetupInvite against a real (test) instance produces a link whose fragment
30// secret derives to a claimable, admin-granting invite — the whole add-instance →
31// browser handshake in one test.
32func TestMintSetupInviteEndToEnd(t *testing.T) {
33 ts, srv := newTestServer(t)
34
35 link, err := mintSetupInvite(http.DefaultClient, ts.URL, "https://canary.example", "admintok")
36 if err != nil {
37 t.Fatalf("mint: %v", err)
38 }
39 prefix := "https://canary.example/join/setup#"
40 if !strings.HasPrefix(link, prefix) {
41 t.Fatalf("link shape: %s", link)
42 }
43 tB64 := strings.TrimPrefix(link, prefix)
44
45 // The browser side: derive from the fragment, inspect, claim.
46 id, claimSecret, _, err := deriveInviteParts(tB64)
47 if err != nil {
48 t.Fatalf("derive: %v", err)
49 }
50 var info struct {
51 OK bool `json:"ok"`
52 GrantsAdmin bool `json:"grants_admin"`
53 }
54 resp, _ := http.Get(ts.URL + "/api/key-invite/" + id)
55 json.NewDecoder(resp.Body).Decode(&info)
56 if !info.OK || !info.GrantsAdmin {
57 t.Fatalf("inspect: %+v", info)
58 }
59 // An admin-granting link must not be claimable forever: minting sets a
60 // real expiry (a stale link in scrollback/logs shouldn't mint an admin).
61 ki, found, _ := srv.db.getKeyInvite(id)
62 if !found {
63 t.Fatalf("invite not found")
64 }
65 if ki.Expired {
66 t.Fatalf("fresh setup invite should not be expired")
67 }
68 var expiresAt int64
69 srv.db.sql.QueryRow("SELECT COALESCE(expires_at, 0) FROM key_invites WHERE id = ?", id).Scan(&expiresAt)
70 if expiresAt == 0 {
71 t.Fatalf("setup invite has no expiry — a stale admin-granting link would live forever")
72 }
73
74 tok, ok, err := srv.db.claimKeyInvite(id, "", "", claimSecret)
75 if err != nil || !ok || tok == "" {
76 t.Fatalf("claim: ok=%v err=%v", ok, err)
77 }
78 uid, _ := srv.db.deviceTokenUser(tok)
79 if srv.db.userRole(uid) != "admin" {
80 t.Fatalf("claimer should be admin")
81 }
82}
83
84// A rejected mint (wrong token) surfaces the HTTP failure rather than printing a
85// dead link.
86func TestMintSetupInviteAuthFailure(t *testing.T) {
87 ts, _ := newTestServer(t)
88 if _, err := mintSetupInvite(http.DefaultClient, ts.URL, ts.URL, "wrong"); err == nil {
89 t.Fatalf("want error on bad admin token")
90 }
91}
92