summaryrefslogtreecommitdiffstats
path: root/tests/storage.rs
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-10-29 22:22:27 +0100
committersalmonik <salmonik@tuta.io>2026-10-29 22:22:27 +0100
commitf30b1a519771ac49e2b03640e742fdb0a246bd81 (patch)
treeb9a78039ba55a364e73df5dc174320dc8222f5c3 /tests/storage.rs
parentformatting (diff)
downloadzagan-bet-f30b1a519771ac49e2b03640e742fdb0a246bd81.tar.gz
zagan-bet-f30b1a519771ac49e2b03640e742fdb0a246bd81.zip
v2.4.0: key stored credentials by certificate fingerprint
Diffstat (limited to 'tests/storage.rs')
-rw-r--r--tests/storage.rs113
1 files changed, 108 insertions, 5 deletions
diff --git a/tests/storage.rs b/tests/storage.rs
index b6e1369..19936c2 100644
--- a/tests/storage.rs
+++ b/tests/storage.rs
@@ -20,21 +20,124 @@ fn client_credentials_roundtrip() {
20 let (_, conn) = tmp_db("creds"); 20 let (_, conn) = tmp_db("creds");
21 client_database::init_db(&conn); 21 client_database::init_db(&conn);
22 22
23 assert_eq!(client_database::get_credentials(&conn, "h1"), None); 23 let identity = "5a15c712230831cb";
24 client_database::save_credentials(&conn, "h1", "alice", "pw").unwrap(); 24 assert_eq!(client_database::get_credentials(&conn, identity), None);
25 client_database::save_credentials(&conn, identity, "alice", "pw").unwrap();
25 assert_eq!( 26 assert_eq!(
26 client_database::get_credentials(&conn, "h1"), 27 client_database::get_credentials(&conn, identity),
27 Some(("alice".to_string(), "pw".to_string())) 28 Some(("alice".to_string(), "pw".to_string()))
28 ); 29 );
29 // Overwrite works. 30 // Overwrite works.
30 client_database::save_credentials(&conn, "h1", "alice", "pw2").unwrap(); 31 client_database::save_credentials(&conn, identity, "alice", "pw2").unwrap();
31 assert_eq!( 32 assert_eq!(
32 client_database::get_credentials(&conn, "h1"), 33 client_database::get_credentials(&conn, identity),
33 Some(("alice".to_string(), "pw2".to_string())) 34 Some(("alice".to_string(), "pw2".to_string()))
34 ); 35 );
35} 36}
36 37
37#[test] 38#[test]
39fn credentials_belong_to_the_certificate_not_the_address() {
40 let (_, conn) = tmp_db("creds-identity");
41 client_database::init_db(&conn);
42
43 // Two addresses for one server, one address for another.
44 client_database::save_host(&conn, "10.0.0.100:11525");
45 client_database::save_host(&conn, "127.0.0.1:11525");
46 client_database::save_host(&conn, "other.example:11525");
47 client_database::save_fingerprint(&conn, "10.0.0.100:11525", "server-a").unwrap();
48 client_database::save_fingerprint(&conn, "127.0.0.1:11525", "server-a").unwrap();
49 client_database::save_fingerprint(&conn, "other.example:11525", "server-b").unwrap();
50
51 client_database::save_credentials(&conn, "server-a", "alice", "pw").unwrap();
52
53 assert_eq!(
54 client_database::get_credentials(&conn, "server-a"),
55 Some(("alice".to_string(), "pw".to_string())),
56 "one login covers every address with the same key"
57 );
58 assert_eq!(
59 client_database::get_credentials(&conn, "server-b"),
60 None,
61 "a different key must not inherit the credentials"
62 );
63}
64
65#[test]
66fn forgetting_one_address_keeps_the_shared_credentials() {
67 let (_, conn) = tmp_db("creds-forget");
68 client_database::init_db(&conn);
69
70 client_database::save_host(&conn, "10.0.0.100:11525");
71 client_database::save_host(&conn, "127.0.0.1:11525");
72 client_database::save_fingerprint(&conn, "10.0.0.100:11525", "server-a").unwrap();
73 client_database::save_fingerprint(&conn, "127.0.0.1:11525", "server-a").unwrap();
74 client_database::save_credentials(&conn, "server-a", "alice", "pw").unwrap();
75
76 let outcome = client_database::forget_host(&conn, "10.0.0.100:11525").unwrap();
77 assert!(!outcome.credentials_removed);
78 assert_eq!(outcome.siblings, vec!["127.0.0.1:11525".to_string()]);
79 assert_eq!(
80 client_database::get_credentials(&conn, "server-a"),
81 Some(("alice".to_string(), "pw".to_string())),
82 "the server is still reachable, so the login is still useful"
83 );
84
85 let outcome = client_database::forget_host(&conn, "127.0.0.1:11525").unwrap();
86 assert!(outcome.credentials_removed);
87 assert!(outcome.siblings.is_empty());
88 assert_eq!(client_database::get_credentials(&conn, "server-a"), None);
89}
90
91#[test]
92fn legacy_host_keyed_credentials_are_rekeyed_by_fingerprint() {
93 let (_, conn) = tmp_db("creds-migrate");
94 conn.execute_batch(
95 "CREATE TABLE hosts (
96 host TEXT PRIMARY KEY,
97 last_used DATETIME DEFAULT CURRENT_TIMESTAMP,
98 fingerprint TEXT
99 );
100 CREATE TABLE credentials (
101 host TEXT PRIMARY KEY,
102 username TEXT NOT NULL,
103 password TEXT NOT NULL
104 );
105 INSERT INTO hosts (host, last_used, fingerprint)
106 VALUES ('a:1', '2020-01-01 00:00:00', 'server-a');
107 INSERT INTO hosts (host, last_used, fingerprint)
108 VALUES ('b:2', '2021-01-01 00:00:00', 'server-a');
109 INSERT INTO hosts (host, last_used, fingerprint)
110 VALUES ('c:3', '2022-01-01 00:00:00', NULL);
111 INSERT INTO credentials VALUES ('a:1', 'alice', 'old');
112 INSERT INTO credentials VALUES ('b:2', 'alice', 'recent');
113 INSERT INTO credentials VALUES ('c:3', 'carol', 'untrusted');",
114 )
115 .unwrap();
116
117 client_database::init_db(&conn);
118
119 assert_eq!(
120 client_database::get_credentials(&conn, "server-a"),
121 Some(("alice".to_string(), "recent".to_string())),
122 "the most recently used address wins when legacy rows disagree"
123 );
124 assert_eq!(
125 client_database::get_credentials(&conn, "carol"),
126 None,
127 "a host with no trusted key cannot be re-keyed"
128 );
129
130 // The old table is gone: the column set is the fingerprint schema.
131 let mut statement = conn.prepare("PRAGMA table_info(credentials)").unwrap();
132 let columns: Vec<String> = statement
133 .query_map([], |row| row.get(1))
134 .unwrap()
135 .filter_map(|row| row.ok())
136 .collect();
137 assert_eq!(columns, vec!["fingerprint", "username", "password"]);
138}
139
140#[test]
38fn client_last_host_tracking() { 141fn client_last_host_tracking() {
39 let (_, conn) = tmp_db("hosts"); 142 let (_, conn) = tmp_db("hosts");
40 client_database::init_db(&conn); 143 client_database::init_db(&conn);