eleven / messenger Public

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

Plain git — no account needed to clone.

Download

Download this file

1package main
2
3import (
4 "strconv"
5 "testing"
6 "time"
7)
8
9// Ban = remove + the key stays out: the banned public key can't re-register
10// through any later invite, the member's queued scheduled messages die with
11// them, and the act is audited.
12func TestBan(t *testing.T) {
13 ts, srv := newTestServer(t)
14
15 tokAdm, uidAdm, _ := joinedMember(t, srv, "Root")
16 if err := srv.db.setUserRole(uidAdm, "admin"); err != nil {
17 t.Fatal(err)
18 }
19 tokBad, uidBad, tidBad := joinedMember(t, srv, "Spammer")
20 if err := srv.db.setUserPublicKey(uidBad, "PUBKEY-SPAMMER", ""); err != nil {
21 t.Fatal(err)
22 }
23 // A queued future message from the spammer.
24 due := time.Now().Unix()
25 if code, _ := modPost(t, ts.URL+"/api/schedule",
26 `{"token":"`+tokBad+`","thread_id":"`+tidBad+`","deliver_at":`+strconv.FormatInt(due, 10)+`,"blob":"later"}`); code != 200 {
27 t.Fatal("schedule should work pre-ban")
28 }
29
30 if code, _ := modAdminPost(t, ts.URL+"/api/admin/users/"+uidBad+"/ban", tokAdm, ""); code != 200 {
31 t.Fatal("ban should succeed")
32 }
33 if srv.db.userExists(uidBad) {
34 t.Fatal("banned member should be removed")
35 }
36 if due, _ := srv.db.dueScheduledMessages(1<<60, 10); len(due) != 0 {
37 t.Fatalf("banned member's scheduled messages should die, still %d", len(due))
38 }
39 if !srv.db.keyBanned("PUBKEY-SPAMMER") {
40 t.Fatal("the key should be on the ban list")
41 }
42
43 // The same key can't re-register through a fresh invite: a new claimer
44 // (keyless — pubkeys publish AFTER claim in the real flow) is refused the
45 // banned key but accepted with a fresh one. joinedMember pre-registers a
46 // key, so build the keyless joiner by hand.
47 uidNew, err := srv.db.createUser("Reborn")
48 if err != nil {
49 t.Fatal(err)
50 }
51 if err := srv.db.createKeyInvite("inv-reborn", uidNew, "wrap", testClaimHash, 0); err != nil {
52 t.Fatal(err)
53 }
54 tokNew, ok, err := srv.db.claimKeyInvite("inv-reborn", "Reborn", "", testClaimSecret)
55 if err != nil || !ok {
56 t.Fatalf("claim: ok=%v err=%v", ok, err)
57 }
58 code, _ := modPost(t, ts.URL+"/api/pubkey", `{"token":"`+tokNew+`","public_key":"PUBKEY-SPAMMER"}`)
59 if code != 403 {
60 t.Fatalf("banned key re-registering should be 403, got %d", code)
61 }
62 // A different key is fine — a ban stops the key, not the invite path.
63 if code, _ := modPost(t, ts.URL+"/api/pubkey", `{"token":"`+tokNew+`","public_key":"PUBKEY-FRESH"}`); code != 200 {
64 t.Fatalf("a fresh key should register, got %d", code)
65 }
66
67 // Audited.
68 entries, err := srv.db.listAudit(0, 10)
69 if err != nil {
70 t.Fatal(err)
71 }
72 found := false
73 for _, e := range entries {
74 if e.Action == "member.banned" && e.SubjectID == uidBad && e.ActorID == uidAdm {
75 found = true
76 }
77 }
78 if !found {
79 t.Fatalf("ban should be audited, got %v", entries)
80 }
81}
82
83// The content-blind brakes: messages flood-stop (REST path), DM opens are
84// capped tighter for day-one members, and invite minting has a ceiling.
85// Admins are exempt from all three.
86func TestModerationBrakes(t *testing.T) {
87 ts, srv := newTestServer(t)
88
89 tokA, uidA, tid := joinedMember(t, srv, "Chatty")
90 // Messages: the burst is 30; the 31st in the same instant bounces.
91 sent := 0
92 for i := 0; i < 40; i++ {
93 code, _ := modPost(t, ts.URL+"/api/messages?token="+tokA, `{"thread_id":"`+tid+`","blob":"m"}`)
94 if code == 200 {
95 sent++
96 } else if code == 429 {
97 break
98 } else {
99 t.Fatalf("unexpected %d", code)
100 }
101 }
102 if sent != 30 {
103 t.Fatalf("the message brake should admit the burst of 30, admitted %d", sent)
104 }
105
106 // DM opens: a brand-new member (created just now) hits the day-one cap of 3.
107 opened := 0
108 for i := 0; i < 5; i++ {
109 _, uidT, _ := joinedMember(t, srv, "T"+strconv.Itoa(i))
110 code, _ := modPost(t, ts.URL+"/api/threads", dmBody(tokA, uidA, uidT))
111 if code == 200 {
112 opened++
113 } else if code == 429 {
114 break
115 } else {
116 t.Fatalf("unexpected %d on dm %d", code, i)
117 }
118 }
119 if opened != 3 {
120 t.Fatalf("a day-one member should open at most 3 DMs, opened %d", opened)
121 }
122
123 // Admins are exempt from the message brake.
124 tokAdm, uidAdm, tidAdm := joinedMember(t, srv, "Root")
125 if err := srv.db.setUserRole(uidAdm, "admin"); err != nil {
126 t.Fatal(err)
127 }
128 for i := 0; i < 35; i++ {
129 if code, _ := modPost(t, ts.URL+"/api/messages?token="+tokAdm, `{"thread_id":"`+tidAdm+`","blob":"m"}`); code != 200 {
130 t.Fatalf("admin message %d should pass, got %d", i, code)
131 }
132 }
133}
134