upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/key_handling/users.rs
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2024-06-24 09:39:18 +0100
committerDanConwayDev <DanConwayDev@protonmail.com>2024-06-24 09:39:18 +0100
commit173ab188b326fbe78cfba4ab455a74619f4556bb (patch)
tree743a2413c241f7babd4efb336718c510eb743847 /src/key_handling/users.rs
parent681fdd7683363c62251ecd8dabcc1931a18f4840 (diff)
feat(login): store in git config and use cache
replace ngit yaml file config with: * nsec / ncryptsec / npub in git config in nostr.* namespace * sql database cache for metadata and relay events allow different logins to be used for different git repositories by storing login in local git config
Diffstat (limited to 'src/key_handling/users.rs')
-rw-r--r--src/key_handling/users.rs1174
1 files changed, 0 insertions, 1174 deletions
diff --git a/src/key_handling/users.rs b/src/key_handling/users.rs
deleted file mode 100644
index a79a977..0000000
--- a/src/key_handling/users.rs
+++ /dev/null
@@ -1,1174 +0,0 @@
1use std::{str::FromStr, time::SystemTime};
2
3use anyhow::{Context, Result};
4use async_trait::async_trait;
5use nostr::prelude::*;
6use zeroize::Zeroize;
7
8use super::encryption::{EncryptDecrypt, Encryptor};
9#[cfg(not(test))]
10use crate::client::Client;
11#[cfg(test)]
12use crate::client::MockConnect;
13use crate::{
14 cli_interactor::{Interactor, InteractorPrompt, PromptInputParms, PromptPasswordParms},
15 client::Connect,
16 config::{
17 self, ConfigManagement, ConfigManager, UserMetadata, UserRef, UserRelayRef, UserRelays,
18 },
19};
20
21#[derive(Default)]
22pub struct UserManager {
23 config_manager: ConfigManager,
24 interactor: Interactor,
25 encryptor: Encryptor,
26}
27
28#[async_trait]
29pub trait UserManagement {
30 fn add(&self, nsec: &Option<String>, password: &Option<String>) -> Result<nostr::Keys>;
31 async fn get_user(
32 &self,
33 #[cfg(test)] client: &MockConnect,
34 #[cfg(not(test))] client: &Client,
35 public_key: &PublicKey,
36 after: u64,
37 ) -> Result<UserRef>;
38 fn get_user_from_cache(&self, public_key: &PublicKey) -> Result<UserRef>;
39 fn add_user_to_config(
40 &self,
41 public_key: PublicKey,
42 encrypted_secret_key: Option<String>,
43 overwrite: bool,
44 ) -> Result<()>;
45}
46
47#[cfg(test)]
48use duplicate::duplicate_item;
49#[cfg_attr(test, duplicate_item(UserManager; [UserManager]; [self::tests::MockUserManager]))]
50#[async_trait]
51impl UserManagement for UserManager {
52 fn add(&self, nsec: &Option<String>, password: &Option<String>) -> Result<nostr::Keys> {
53 let mut prompt = "login with nsec (or hex private key)";
54 let keys = loop {
55 let pk = match nsec.clone() {
56 Some(nsec) => nsec,
57 None => self
58 .interactor
59 .input(PromptInputParms::default().with_prompt(prompt))
60 .context("failed to get nsec input from interactor")?,
61 };
62 match Keys::from_str(&pk) {
63 Ok(key) => {
64 break key;
65 }
66 Err(e) => {
67 if nsec.is_some() {
68 return Err(e).context(
69 "invalid nsec - supplied parameter could not be converted into a nostr private key",
70 );
71 }
72 prompt = "invalid nsec. try again with nsec (or hex private key)";
73 }
74 }
75 };
76
77 let mut pass = match password.clone() {
78 Some(pass) => pass,
79 None => self
80 .interactor
81 .password(
82 PromptPasswordParms::default()
83 .with_prompt("encrypt with password")
84 .with_confirm(),
85 )
86 .context("failed to get password input from interactor.password")?,
87 };
88
89 let encrypted_secret_key = self
90 .encryptor
91 .encrypt_key(&keys, &pass)
92 .context("failed to encrypt nsec with password.")?;
93 pass.zeroize();
94
95 self.add_user_to_config(keys.public_key(), Some(encrypted_secret_key), true)?;
96
97 Ok(keys)
98 }
99
100 fn add_user_to_config(
101 &self,
102 public_key: PublicKey,
103 encrypted_secret_key: Option<String>,
104 overwrite: bool,
105 ) -> Result<()> {
106 let user_ref = config::UserRef::new(public_key, encrypted_secret_key.unwrap_or_default());
107
108 let mut cfg = self.config_manager.load().context("failed to load application config to find and remove any old versions of the user's encrypted key")?;
109 // don't overwrite unless specified
110 if !overwrite
111 && cfg
112 .users
113 .clone()
114 .into_iter()
115 .any(|r| r.public_key.eq(&public_key))
116 {
117 return Ok(());
118 }
119 // if overwrite remove any duplicate entries for key before adding it to config
120 cfg.users = cfg
121 .users
122 .clone()
123 .into_iter()
124 .filter(|r| !r.public_key.eq(&public_key))
125 .collect();
126 cfg.users.push(user_ref);
127 self.config_manager
128 .save(&cfg)
129 .context("failed to save application configuration with new user details in")
130 }
131
132 fn get_user_from_cache(&self, public_key: &PublicKey) -> Result<UserRef> {
133 let cfg = self
134 .config_manager
135 .load()
136 .context("failed to load application config")?;
137 Ok(cfg
138 .users
139 .iter()
140 .find(|u| u.public_key.eq(public_key))
141 .context(format!("pubkey isn't a current user: {public_key}"))?
142 .clone())
143 }
144 /// get UserRef fetching most recent user relays and metadata infomation
145 /// from
146 #[allow(clippy::too_many_lines)]
147 async fn get_user(
148 &self,
149 #[cfg(test)] client: &MockConnect,
150 #[cfg(not(test))] client: &Client,
151 public_key: &PublicKey,
152 use_cache_unless_checked_more_than_x_secs_ago: u64,
153 ) -> Result<UserRef> {
154 let cfg = self
155 .config_manager
156 .load()
157 .context("failed to load application config")?;
158 let mut user_ref = cfg
159 .users
160 .iter()
161 .find(|u| u.public_key.eq(public_key))
162 .context(format!("pubkey isn't a current user: {public_key}"))?
163 .clone();
164 // return cache if last fetched was within X minutes
165 if !unix_timestamp_after_now_plus_secs(
166 user_ref.last_checked,
167 use_cache_unless_checked_more_than_x_secs_ago,
168 ) {
169 return Ok(user_ref);
170 }
171
172 let mut relays_to_search = if user_ref.relays.write().is_empty() {
173 client.get_fallback_relays().clone()
174 } else {
175 user_ref.relays.write()
176 };
177
178 let mut relays_searched: Vec<String> = vec![];
179
180 loop {
181 for r in &relays_to_search {
182 if !relays_searched.iter().any(|sr| r.eq(sr)) {
183 relays_searched.push(r.clone());
184 }
185 }
186
187 let events: Vec<Event> = match client
188 .get_events(
189 relays_to_search,
190 vec![
191 nostr::Filter::default()
192 .author(*public_key)
193 .since(nostr::Timestamp::from(user_ref.metadata.created_at + 1))
194 .kind(Kind::Metadata),
195 nostr::Filter::default()
196 .author(*public_key)
197 .since(nostr::Timestamp::from(user_ref.relays.created_at + 1))
198 .kind(Kind::RelayList),
199 ],
200 )
201 .await
202 {
203 Ok(events) => events,
204 Err(_) => {
205 return Ok(user_ref.clone());
206 }
207 };
208
209 user_ref.last_checked = SystemTime::now()
210 .duration_since(SystemTime::UNIX_EPOCH)
211 .context("system time should be after the year 1970")?
212 .as_secs();
213
214 if let Some(new_metadata_event) = events
215 .iter()
216 .filter(|e| e.kind.eq(&nostr::Kind::Metadata) && e.pubkey.eq(public_key))
217 .max_by_key(|e| e.created_at)
218 {
219 if new_metadata_event.created_at.as_u64() > user_ref.metadata.created_at {
220 let metadata = nostr::Metadata::from_json(new_metadata_event.content.clone())
221 .context("metadata cannot be found in kind 0 event content")?;
222 user_ref.metadata = UserMetadata {
223 name: if let Some(n) = metadata.name {
224 n
225 } else if let Some(n) = metadata.custom.get("displayName") {
226 // strip quote marks that custom.get() adds
227 let binding = n.to_string();
228 let mut chars = binding.chars();
229 chars.next();
230 chars.next_back();
231 chars.as_str().to_string()
232 } else if let Some(n) = metadata.display_name {
233 n
234 } else {
235 user_ref.metadata.name
236 },
237 created_at: new_metadata_event.created_at.as_u64(),
238 };
239 }
240 };
241
242 if let Some(new_relays_event) = events
243 .iter()
244 .filter(|e| e.kind.eq(&nostr::Kind::RelayList) && e.pubkey.eq(public_key))
245 .max_by_key(|e| e.created_at)
246 {
247 if new_relays_event.created_at.as_u64() > user_ref.relays.created_at {
248 let new_relay_list = UserRelays {
249 relays: new_relays_event
250 .tags
251 .iter()
252 .filter(|t| {
253 t.kind().eq(&nostr::TagKind::SingleLetter(
254 SingleLetterTag::lowercase(Alphabet::R),
255 ))
256 })
257 .map(|t| UserRelayRef {
258 url: t.as_vec()[1].clone(),
259 read: t.as_vec().len() == 2 || t.as_vec()[2].eq("read"),
260 write: t.as_vec().len() == 2 || t.as_vec()[2].eq("write"),
261 })
262 .collect(),
263 created_at: new_relays_event.created_at.as_u64(),
264 };
265 let new_relays: Vec<String> = new_relay_list
266 .write()
267 .iter()
268 .filter(|r| !relays_searched.iter().any(|or| r.eq(&or)))
269 .map(std::clone::Clone::clone)
270 .collect();
271 user_ref.relays = new_relay_list;
272
273 if !new_relays.is_empty() {
274 relays_to_search = new_relays;
275 continue;
276 }
277 }
278 };
279
280 // remove any duplicate entries for key before adding it to config
281 let mut cfg = self.config_manager.load().context("failed to load application config to find and remove any old versions of the user's encrypted key")?;
282 cfg.users = cfg
283 .users
284 .clone()
285 .into_iter()
286 .filter(|r| !r.public_key.eq(public_key))
287 .collect();
288 cfg.users.push(user_ref.clone());
289 self.config_manager
290 .save(&cfg)
291 .context("failed to save application configuration with new user details in")?;
292 break;
293 }
294 Ok(user_ref)
295 }
296}
297
298fn unix_timestamp_after_now_plus_secs(timestamp: u64, secs: u64) -> bool {
299 if let Ok(now) = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
300 now.as_secs() > (timestamp + secs)
301 } else {
302 true
303 }
304}
305
306#[cfg(test)]
307mod tests {
308 use nostr;
309 use test_utils::*;
310
311 use super::*;
312 use crate::{
313 cli_interactor::MockInteractorPrompt,
314 config::{MockConfigManagement, MyConfig, UserRef},
315 key_handling::encryption::MockEncryptDecrypt,
316 };
317
318 #[derive(Default)]
319 pub struct MockUserManager {
320 pub config_manager: MockConfigManagement,
321 pub interactor: MockInteractorPrompt,
322 pub encryptor: MockEncryptDecrypt,
323 }
324
325 mod add {
326 use super::*;
327
328 impl MockUserManager {
329 fn add_return_expected_responses(mut self) -> Self {
330 self.config_manager
331 .expect_load()
332 .returning(|| Ok(MyConfig::default()));
333 self.config_manager.expect_save().returning(|_| Ok(()));
334 self.interactor
335 .expect_input()
336 .returning(|_| Ok(TEST_KEY_1_NSEC.into()));
337 self.interactor
338 .expect_password()
339 .returning(|_| Ok(TEST_PASSWORD.into()));
340 self.encryptor
341 .expect_encrypt_key()
342 .returning(|_, _| Ok(TEST_KEY_1_ENCRYPTED.into()));
343 self
344 }
345 }
346
347 fn reuable_user_isnt_prompted(nsec: &str) {
348 let mut m = MockUserManager::default().add_return_expected_responses();
349 m.interactor = MockInteractorPrompt::default();
350 m.interactor.expect_input().never();
351 m.interactor.expect_password().never();
352 let _ = m.add(&Some(nsec.into()), &Some(TEST_PASSWORD.to_string()));
353 }
354
355 fn reuable_config_isnt_modified(nsec: &str) {
356 let mut m = MockUserManager::default();
357 m.config_manager.expect_save().never();
358 let _ = m.add(&Some(nsec.into()), &Some(TEST_PASSWORD.to_string()));
359 }
360
361 mod when_valid_nsec_and_password_is_passed {
362 use super::*;
363
364 #[test]
365 fn user_isnt_prompted() {
366 reuable_user_isnt_prompted(TEST_KEY_1_NSEC);
367 }
368
369 #[test]
370 fn results_in_correct_keys() {
371 let mut m = MockUserManager::default().add_return_expected_responses();
372 m.interactor = MockInteractorPrompt::default();
373 m.interactor.expect_input().never();
374 m.interactor.expect_password().never();
375 let r = m.add(
376 &Some(TEST_KEY_1_NSEC.into()),
377 &Some(TEST_PASSWORD.to_string()),
378 );
379 assert!(r.is_ok(), "should result in keys");
380 assert!(
381 r.is_ok_and(|k| k
382 .secret_key()
383 .is_ok_and(|k| k.display_secret().to_string().eq(TEST_KEY_1_SK_HEX))),
384 "keys should reflect nsec"
385 );
386 }
387 }
388 mod when_invalid_nsec_is_passed_with_password {
389 use super::*;
390
391 #[test]
392 fn user_isnt_prompted() {
393 reuable_user_isnt_prompted(TEST_INVALID_NSEC);
394 }
395
396 #[test]
397 fn config_isnt_modified() {
398 reuable_config_isnt_modified(TEST_INVALID_NSEC);
399 }
400
401 #[test]
402 fn results_in_an_error() {
403 let m = MockUserManager::default();
404 assert!(
405 m.add(
406 &Some(TEST_INVALID_NSEC.into()),
407 &Some(TEST_PASSWORD.to_string())
408 )
409 .is_err(),
410 "should result in an error"
411 );
412 }
413 }
414 mod when_no_nsec_is_passed {
415 use super::*;
416
417 #[test]
418 fn prompt_for_nsec_and_password() {
419 let mut m = MockUserManager::default().add_return_expected_responses();
420
421 m.interactor = MockInteractorPrompt::new();
422 m.interactor
423 .expect_input()
424 .once()
425 .withf(|p| p.prompt.eq("login with nsec (or hex private key)"))
426 .returning(|_| Ok(TEST_KEY_1_NSEC.into()));
427 m.interactor
428 .expect_password()
429 .once()
430 .withf(|p| p.prompt.eq("encrypt with password"))
431 .returning(|_| Ok(TEST_KEY_1_NSEC.into()));
432
433 let _ = m.add(&None, &None);
434 }
435
436 #[test]
437 fn results_in_correct_keys() {
438 let m = MockUserManager::default().add_return_expected_responses();
439
440 let r = m.add(&None, &None);
441 assert!(r.is_ok(), "should result in keys");
442 assert!(
443 r.is_ok_and(|k| k
444 .secret_key()
445 .is_ok_and(|k| k.display_secret().to_string().eq(TEST_KEY_1_SK_HEX))),
446 "keys should reflect nsec"
447 );
448 }
449
450 #[test]
451 fn stores_encrypted_key_in_config() {
452 let mut m = MockUserManager::default().add_return_expected_responses();
453
454 m.config_manager = MockConfigManagement::new();
455 m.config_manager
456 .expect_load()
457 .returning(|| Ok(MyConfig::default()));
458 m.config_manager
459 .expect_save()
460 .withf(|cfg| {
461 cfg.users.len().eq(&1)
462 && cfg.users[0].encrypted_key.eq(TEST_KEY_1_ENCRYPTED)
463 })
464 .returning(|_| Ok(()));
465
466 let _ = m.add(&None, &None);
467 }
468
469 #[test]
470 fn stored_key_encrypted_with_password() {
471 let mut m = MockUserManager::default().add_return_expected_responses();
472
473 m.encryptor = MockEncryptDecrypt::new();
474 m.encryptor
475 .expect_encrypt_key()
476 .once()
477 .withf(|k, p| {
478 k.eq(&Keys::from_str(TEST_KEY_1_NSEC).unwrap()) && p.eq(TEST_PASSWORD)
479 })
480 .returning(|_, _| Ok(TEST_KEY_1_ENCRYPTED.into()));
481
482 let _ = m.add(&None, &None);
483 }
484
485 mod when_user_key_already_stored {
486 use super::*;
487 use crate::config::UserRef;
488
489 /// key overwritten as password may have changed
490 #[test]
491 fn key_not_saved_as_duplicate_but_encrypted_key_overwritten() {
492 let mut m = MockUserManager::default().add_return_expected_responses();
493
494 m.config_manager = MockConfigManagement::default();
495 m.config_manager.expect_load().returning(|| {
496 Ok(MyConfig {
497 users: vec![UserRef::new(
498 TEST_KEY_1_KEYS.public_key(),
499 TEST_KEY_2_ENCRYPTED.into(),
500 )],
501 ..MyConfig::default()
502 })
503 });
504 m.config_manager
505 .expect_save()
506 .withf(|cfg| {
507 cfg.users.len() == 1
508 && cfg.users[0].encrypted_key.eq(TEST_KEY_1_ENCRYPTED)
509 })
510 .returning(|_| Ok(()));
511
512 let _ = m.add(&None, &None);
513 }
514 }
515
516 mod when_multiple_users_added {
517 use super::*;
518
519 #[test]
520 fn both_user_keys_are_stored() {
521 let mut m = MockUserManager::default().add_return_expected_responses();
522
523 m.config_manager = MockConfigManagement::default();
524 m.config_manager.expect_load().returning(|| {
525 Ok(MyConfig {
526 users: vec![UserRef::new(
527 TEST_KEY_2_KEYS.public_key(),
528 TEST_KEY_2_ENCRYPTED.into(),
529 )],
530 ..MyConfig::default()
531 })
532 });
533 m.config_manager
534 .expect_save()
535 .withf(|cfg| {
536 cfg.users.len() == 2
537 // latest user stored at end of array
538 && cfg.users[1].encrypted_key.eq(TEST_KEY_1_ENCRYPTED)
539 })
540 .returning(|_| Ok(()));
541
542 let _ = m.add(&None, &None);
543 }
544 }
545 }
546 }
547
548 fn now_timestamp() -> u64 {
549 SystemTime::now()
550 .duration_since(SystemTime::UNIX_EPOCH)
551 .unwrap()
552 .as_secs()
553 }
554 fn roughly_now(timestamp: u64) -> bool {
555 let now = now_timestamp();
556 timestamp < now + 100 && timestamp > now - 100
557 }
558
559 mod get_user {
560 use anyhow::anyhow;
561
562 use super::*;
563 use crate::client::MockConnect;
564
565 fn generate_relaylist_event() -> nostr::Event {
566 nostr::event::EventBuilder::new(
567 nostr::Kind::RelayList,
568 "",
569 [
570 nostr::Tag::from_standardized(nostr::TagStandard::RelayMetadata {
571 relay_url: nostr::Url::from_str("wss://fredswrite1.relay/").unwrap(),
572 metadata: Some(RelayMetadata::Write),
573 }),
574 nostr::Tag::from_standardized(nostr::TagStandard::RelayMetadata {
575 relay_url: nostr::Url::from_str("wss://fredsread1.relay/").unwrap(),
576 metadata: Some(RelayMetadata::Read),
577 }),
578 nostr::Tag::from_standardized(nostr::TagStandard::RelayMetadata {
579 relay_url: nostr::Url::from_str("wss://fredsreadwrite.relay/").unwrap(),
580 metadata: None,
581 }),
582 ],
583 )
584 .to_event(&TEST_KEY_1_KEYS)
585 .unwrap()
586 }
587
588 fn generate_relaylist_event_user_2() -> nostr::Event {
589 nostr::event::EventBuilder::new(
590 nostr::Kind::RelayList,
591 "",
592 [
593 nostr::Tag::from_standardized(nostr::TagStandard::RelayMetadata {
594 relay_url: nostr::Url::from_str("wss://carolswrite1.relay/").unwrap(),
595 metadata: Some(RelayMetadata::Write),
596 }),
597 nostr::Tag::from_standardized(nostr::TagStandard::RelayMetadata {
598 relay_url: nostr::Url::from_str("wss://carolsread1.relay/").unwrap(),
599 metadata: Some(RelayMetadata::Read),
600 }),
601 nostr::Tag::from_standardized(nostr::TagStandard::RelayMetadata {
602 relay_url: nostr::Url::from_str("wss://carolsreadwrite.relay/").unwrap(),
603 metadata: None,
604 }),
605 ],
606 )
607 .to_event(&TEST_KEY_2_KEYS)
608 .unwrap()
609 }
610
611 fn fallback_relays() -> Vec<String> {
612 vec!["ws://fallback1".to_string(), "ws://fallback2".to_string()].clone()
613 }
614
615 fn generate_mock_client() -> MockConnect {
616 let mut client = <MockConnect as std::default::Default>::default();
617 client
618 .expect_get_fallback_relays()
619 .return_const(fallback_relays());
620 client
621 }
622
623 fn generate_standard_config() -> MyConfig {
624 MyConfig {
625 users: vec![UserRef {
626 public_key: TEST_KEY_1_KEYS.public_key(),
627 encrypted_key: TEST_KEY_1_ENCRYPTED.to_string(),
628 metadata: UserMetadata {
629 name: "Fred".to_string(),
630 created_at: 10,
631 },
632 relays: UserRelays {
633 relays: vec![
634 UserRelayRef {
635 url: "ws://existingread".to_string(),
636 read: true,
637 write: false,
638 },
639 UserRelayRef {
640 url: "ws://existingreadwrite".to_string(),
641 read: true,
642 write: true,
643 },
644 UserRelayRef {
645 url: "ws://existingwrite".to_string(),
646 read: false,
647 write: true,
648 },
649 ],
650 created_at: 10,
651 },
652 last_checked: now_timestamp() - (60 * 60), // 1h ago
653 }],
654 ..MyConfig::default()
655 }
656 .clone()
657 }
658
659 fn expected_userrelayrefs_write1() -> UserRelayRef {
660 UserRelayRef {
661 url: "wss://fredswrite1.relay/".into(),
662 read: false,
663 write: true,
664 }
665 .clone()
666 }
667
668 fn expected_userrelayrefs_read_write1() -> UserRelayRef {
669 UserRelayRef {
670 url: "wss://fredsreadwrite.relay/".into(),
671 read: true,
672 write: true,
673 }
674 .clone()
675 }
676
677 fn expected_userrelayrefs() -> Vec<UserRelayRef> {
678 vec![
679 expected_userrelayrefs_write1(),
680 UserRelayRef {
681 url: "wss://fredsread1.relay/".into(),
682 read: true,
683 write: false,
684 },
685 expected_userrelayrefs_read_write1(),
686 ]
687 }
688
689 mod when_within_caching_time_window {
690 use super::*;
691
692 #[tokio::test]
693 async fn returns_cached_details_without_checking_relays_or_updaing_config() -> Result<()>
694 {
695 let mut m = MockUserManager::default();
696 let client = generate_mock_client();
697 m.config_manager
698 .expect_load()
699 .returning(|| Ok(generate_standard_config()));
700 let res = m
701 .get_user(
702 &client,
703 &TEST_KEY_1_KEYS.public_key(),
704 24 * 60 * 60, // within 24 hours
705 )
706 .await?;
707 assert_eq!(res.metadata.name, "Fred");
708 assert_eq!(res.relays.relays[0].url, "ws://existingread");
709 Ok(())
710 }
711 }
712
713 mod returns_userref_with_latest_details_from_events_on_relays {
714 use super::*;
715
716 #[tokio::test]
717 async fn name() -> Result<()> {
718 let mut m = MockUserManager::default();
719 let mut client = generate_mock_client();
720 m.config_manager
721 .expect_load()
722 .returning(|| Ok(generate_standard_config()));
723 m.config_manager.expect_save().returning(|_| Ok(()));
724 client
725 .expect_get_events()
726 .returning(|_, _| Ok(vec![generate_test_key_1_metadata_event("fred")]));
727
728 let res = m
729 .get_user(
730 &client,
731 &TEST_KEY_1_KEYS.public_key(),
732 5 * 60, // 5 mins ago
733 )
734 .await?;
735 assert_eq!(res.metadata.name, "fred");
736 Ok(())
737 }
738
739 #[tokio::test]
740 async fn name_ignoring_other_users_events() -> Result<()> {
741 let mut m = MockUserManager::default();
742 let mut client = generate_mock_client();
743 m.config_manager
744 .expect_load()
745 .returning(|| Ok(generate_standard_config()));
746 m.config_manager.expect_save().returning(|_| Ok(()));
747 client.expect_get_events().returning(|_, _| {
748 Ok(vec![
749 generate_test_key_2_metadata_event("carole"),
750 generate_test_key_1_metadata_event_old("fred"),
751 ])
752 });
753
754 let res = m
755 .get_user(
756 &client,
757 &TEST_KEY_1_KEYS.public_key(),
758 5 * 60, // 5 mins ago
759 )
760 .await?;
761 assert_eq!(res.metadata.name, "fred");
762 Ok(())
763 }
764
765 #[tokio::test]
766 async fn relays() -> Result<()> {
767 let mut m = MockUserManager::default();
768 let mut client = generate_mock_client();
769 m.config_manager
770 .expect_load()
771 .returning(|| Ok(generate_standard_config()));
772 m.config_manager.expect_save().returning(|_| Ok(()));
773 client.expect_get_events().returning(|_, _| {
774 Ok(vec![
775 generate_test_key_1_metadata_event("fred"),
776 generate_relaylist_event(),
777 ])
778 });
779
780 let res = m
781 .get_user(
782 &client,
783 &TEST_KEY_1_KEYS.public_key(),
784 5 * 60, // 5 mins ago
785 )
786 .await?;
787 assert_eq!(res.relays.relays, expected_userrelayrefs(),);
788 Ok(())
789 }
790
791 #[tokio::test]
792 async fn relays_ignoring_other_users_events() -> Result<()> {
793 let mut m = MockUserManager::default();
794 let mut client = generate_mock_client();
795 m.config_manager
796 .expect_load()
797 .returning(|| Ok(generate_standard_config()));
798 m.config_manager.expect_save().returning(|_| Ok(()));
799 client.expect_get_events().returning(|_, _| {
800 Ok(vec![
801 make_event_old_or_change_user(
802 generate_relaylist_event(),
803 &TEST_KEY_1_KEYS,
804 10000,
805 ),
806 generate_relaylist_event_user_2(),
807 ])
808 });
809
810 let res = m
811 .get_user(
812 &client,
813 &TEST_KEY_1_KEYS.public_key(),
814 5 * 60, // 5 mins ago
815 )
816 .await?;
817 assert_eq!(res.relays.relays, expected_userrelayrefs(),);
818 Ok(())
819 }
820 }
821
822 mod saves_updates_to_config {
823 use super::*;
824
825 #[tokio::test]
826 async fn saves_name_to_config() -> Result<()> {
827 let mut m = MockUserManager::default();
828 let mut client = generate_mock_client();
829 m.config_manager
830 .expect_load()
831 .returning(|| Ok(generate_standard_config()));
832 m.config_manager
833 .expect_save()
834 .once()
835 .withf(|cfg| cfg.users[0].metadata.name.eq("fred"))
836 .returning(|_| Ok(()));
837 client
838 .expect_get_events()
839 .returning(|_, _| Ok(vec![generate_test_key_1_metadata_event("fred")]));
840
841 let _ = m
842 .get_user(
843 &client,
844 &TEST_KEY_1_KEYS.public_key(),
845 5 * 60, // 5 mins ago
846 )
847 .await?;
848 Ok(())
849 }
850
851 #[tokio::test]
852 async fn updates_metadata_created_at() -> Result<()> {
853 let mut m = MockUserManager::default();
854 let mut client = generate_mock_client();
855 m.config_manager
856 .expect_load()
857 .returning(|| Ok(generate_standard_config()));
858 m.config_manager
859 .expect_save()
860 .once()
861 .withf(|cfg| roughly_now(cfg.users[0].metadata.created_at))
862 .returning(|_| Ok(()));
863 client
864 .expect_get_events()
865 .returning(|_, _| Ok(vec![generate_test_key_1_metadata_event("fred")]));
866
867 let _ = m
868 .get_user(
869 &client,
870 &TEST_KEY_1_KEYS.public_key(),
871 5 * 60, // 5 mins ago
872 )
873 .await?;
874 Ok(())
875 }
876
877 #[tokio::test]
878 async fn saves_relays_to_config() -> Result<()> {
879 let mut m = MockUserManager::default();
880 let mut client = generate_mock_client();
881 m.config_manager
882 .expect_load()
883 .returning(|| Ok(generate_standard_config()));
884 m.config_manager
885 .expect_save()
886 .once()
887 .withf(|cfg| expected_userrelayrefs().eq(&cfg.users[0].relays.relays))
888 .returning(|_| Ok(()));
889 client
890 .expect_get_events()
891 .returning(|_, _| Ok(vec![generate_relaylist_event()]));
892
893 let _ = m
894 .get_user(
895 &client,
896 &TEST_KEY_1_KEYS.public_key(),
897 5 * 60, // 5 mins ago
898 )
899 .await?;
900 Ok(())
901 }
902
903 #[tokio::test]
904 async fn updates_relays_created_at() -> Result<()> {
905 let mut m = MockUserManager::default();
906 let mut client = generate_mock_client();
907 m.config_manager
908 .expect_load()
909 .returning(|| Ok(generate_standard_config()));
910 m.config_manager
911 .expect_save()
912 .once()
913 .withf(|cfg| roughly_now(cfg.users[0].relays.created_at))
914 .returning(|_| Ok(()));
915 client
916 .expect_get_events()
917 .returning(|_, _| Ok(vec![generate_relaylist_event()]));
918
919 let _ = m
920 .get_user(
921 &client,
922 &TEST_KEY_1_KEYS.public_key(),
923 5 * 60, // 5 mins ago
924 )
925 .await?;
926 Ok(())
927 }
928
929 #[tokio::test]
930 async fn when_no_changes_updates_last_updated() -> Result<()> {
931 let mut m = MockUserManager::default();
932 let mut client = generate_mock_client();
933 m.config_manager
934 .expect_load()
935 .returning(|| Ok(generate_standard_config()));
936 m.config_manager
937 .expect_save()
938 .once()
939 .withf(|cfg| roughly_now(cfg.users[0].last_checked))
940 .returning(|_| Ok(()));
941 client.expect_get_events().returning(|_, _| Ok(vec![]));
942
943 let _ = m
944 .get_user(
945 &client,
946 &TEST_KEY_1_KEYS.public_key(),
947 5 * 60, // 5 mins ago
948 )
949 .await?;
950 Ok(())
951 }
952
953 #[tokio::test]
954 async fn when_changes_updates_last_updated() -> Result<()> {
955 let mut m = MockUserManager::default();
956 let mut client = generate_mock_client();
957 m.config_manager
958 .expect_load()
959 .returning(|| Ok(generate_standard_config()));
960 m.config_manager
961 .expect_save()
962 .once()
963 .withf(|cfg| roughly_now(cfg.users[0].last_checked))
964 .returning(|_| Ok(()));
965 client
966 .expect_get_events()
967 .returning(|_, _| Ok(vec![generate_test_key_1_metadata_event("fred")]));
968
969 let _ = m
970 .get_user(
971 &client,
972 &TEST_KEY_1_KEYS.public_key(),
973 5 * 60, // 5 mins ago
974 )
975 .await?;
976 Ok(())
977 }
978 }
979
980 mod fetches_from_correct_relays {
981 use super::*;
982 #[tokio::test]
983 async fn when_userref_write_relays_present_fetches_only_from_them() -> Result<()> {
984 let mut m = MockUserManager::default();
985 let mut client = generate_mock_client();
986 m.config_manager
987 .expect_load()
988 .returning(|| Ok(generate_standard_config()));
989 m.config_manager.expect_save().returning(|_| Ok(()));
990 client
991 .expect_get_events()
992 .once()
993 .withf(move |relays, _filters| {
994 vec![
995 "ws://existingreadwrite".to_string(),
996 "ws://existingwrite".to_string(),
997 ]
998 .eq(relays)
999 })
1000 .returning(|_, _| Ok(vec![]));
1001
1002 let _ = m
1003 .get_user(
1004 &client,
1005 &TEST_KEY_1_KEYS.public_key(),
1006 5 * 60, // 5 mins ago
1007 )
1008 .await?;
1009 Ok(())
1010 }
1011
1012 #[tokio::test]
1013 async fn when_userref_write_relays_not_present_fetches_from_fallback_relays()
1014 -> Result<()> {
1015 let mut m = MockUserManager::default();
1016 let mut client = generate_mock_client();
1017 m.config_manager.expect_load().returning(|| {
1018 Ok(MyConfig {
1019 users: vec![UserRef {
1020 relays: UserRelays {
1021 relays: vec![],
1022 created_at: 0,
1023 },
1024 ..generate_standard_config().users[0].clone()
1025 }],
1026 ..generate_standard_config()
1027 })
1028 });
1029 m.config_manager.expect_save().returning(|_| Ok(()));
1030 client
1031 .expect_get_events()
1032 .once()
1033 .withf(move |relays, _filters| fallback_relays().eq(relays))
1034 .returning(|_, _| Ok(vec![]));
1035
1036 let _ = m
1037 .get_user(
1038 &client,
1039 &TEST_KEY_1_KEYS.public_key(),
1040 5 * 60, // 5 mins ago
1041 )
1042 .await?;
1043 Ok(())
1044 }
1045
1046 mod fetches_from_new_relays_discovered_in_incoming_relay_list {
1047 use super::*;
1048
1049 #[tokio::test]
1050 async fn when_all_relays_in_list_are_new_finds_name() -> Result<()> {
1051 let mut m = MockUserManager::default();
1052 let mut client = generate_mock_client();
1053 m.config_manager.expect_load().returning(|| {
1054 Ok(MyConfig {
1055 users: vec![UserRef {
1056 relays: UserRelays {
1057 relays: vec![],
1058 created_at: 0,
1059 },
1060 ..generate_standard_config().users[0].clone()
1061 }],
1062 ..generate_standard_config()
1063 })
1064 });
1065 m.config_manager.expect_save().returning(|_| Ok(()));
1066 client
1067 .expect_get_events()
1068 .times(2)
1069 .withf(move |relays, _filters| {
1070 fallback_relays().eq(relays)
1071 || UserRelays {
1072 relays: expected_userrelayrefs(),
1073 created_at: 0,
1074 }
1075 .write()
1076 .eq(relays)
1077 })
1078 .returning(|relays, _| {
1079 if fallback_relays().eq(&relays) {
1080 Ok(vec![generate_relaylist_event()])
1081 } else if (UserRelays {
1082 relays: expected_userrelayrefs(),
1083 created_at: 0,
1084 })
1085 .write()
1086 .eq(&relays)
1087 {
1088 Ok(vec![generate_test_key_1_metadata_event("fred")])
1089 } else {
1090 Ok(vec![])
1091 }
1092 });
1093
1094 let res = m
1095 .get_user(
1096 &client,
1097 &TEST_KEY_1_KEYS.public_key(),
1098 5 * 60, // 5 mins ago
1099 )
1100 .await?;
1101 assert_eq!(res.metadata.name, "fred");
1102 Ok(())
1103 }
1104
1105 #[tokio::test]
1106 async fn only_fetches_from_newly_added_relays() -> Result<()> {
1107 let mut m = MockUserManager::default();
1108 let mut client = generate_mock_client();
1109 m.config_manager.expect_load().returning(|| {
1110 Ok(MyConfig {
1111 users: vec![UserRef {
1112 relays: UserRelays {
1113 relays: vec![expected_userrelayrefs_write1()],
1114 created_at: 0,
1115 },
1116 ..generate_standard_config().users[0].clone()
1117 }],
1118 ..generate_standard_config()
1119 })
1120 });
1121 m.config_manager.expect_save().returning(|_| Ok(()));
1122 client
1123 .expect_get_events()
1124 .times(2)
1125 .withf(move |relays, _filters| {
1126 vec![expected_userrelayrefs_write1().url].eq(relays)
1127 || vec![expected_userrelayrefs_read_write1().url].eq(relays)
1128 })
1129 .returning(|relays, _| {
1130 if vec![expected_userrelayrefs_write1().url].eq(&relays) {
1131 Ok(vec![generate_relaylist_event()])
1132 } else if vec![expected_userrelayrefs_read_write1().url].eq(&relays) {
1133 Ok(vec![generate_test_key_1_metadata_event("fred")])
1134 } else {
1135 Ok(vec![])
1136 }
1137 });
1138
1139 let res = m
1140 .get_user(
1141 &client,
1142 &TEST_KEY_1_KEYS.public_key(),
1143 5 * 60, // 5 mins ago
1144 )
1145 .await?;
1146 assert_eq!(res.metadata.name, "fred");
1147 Ok(())
1148 }
1149 }
1150 }
1151
1152 #[tokio::test]
1153 async fn when_failed_to_fetch_events_returns_cached_details() -> Result<()> {
1154 let mut m = MockUserManager::default();
1155 let mut client = generate_mock_client();
1156 m.config_manager
1157 .expect_load()
1158 .returning(|| Ok(generate_standard_config()));
1159 client
1160 .expect_get_events()
1161 .returning(|_, _| Err(anyhow!("test error")));
1162
1163 let res = m
1164 .get_user(
1165 &client,
1166 &TEST_KEY_1_KEYS.public_key(),
1167 5 * 60, // 10 mins ago
1168 )
1169 .await?;
1170 assert_eq!(res.metadata.name, "Fred");
1171 Ok(())
1172 }
1173 }
1174}