fxa_client/internal/oauth/
attached_clients.rs
1pub use super::super::http_client::GetAttachedClientResponse as AttachedClient;
6use super::super::{util, CachedResponse, FirefoxAccount};
7use crate::{Error, Result};
8
9const ATTACHED_CLIENTS_FRESHNESS_THRESHOLD: u64 = 60_000; impl FirefoxAccount {
13 pub fn get_attached_clients(&mut self) -> Result<Vec<AttachedClient>> {
15 if let Some(a) = &self.attached_clients_cache {
16 if util::now() < a.cached_at + ATTACHED_CLIENTS_FRESHNESS_THRESHOLD {
17 return Ok(a.response.clone());
18 }
19 }
20 let session_token = self.get_session_token()?;
21 let response = self
22 .client
23 .get_attached_clients(self.state.config(), &session_token)?;
24
25 self.attached_clients_cache = Some(CachedResponse {
26 response: response.clone(),
27 cached_at: util::now(),
28 etag: "".into(),
29 });
30
31 Ok(response)
32 }
33}
34
35impl TryFrom<AttachedClient> for crate::AttachedClient {
36 type Error = Error;
37 fn try_from(c: AttachedClient) -> Result<Self> {
38 Ok(crate::AttachedClient {
39 client_id: c.client_id,
40 device_id: c.device_id,
41 device_type: c.device_type,
42 is_current_session: c.is_current_session,
43 name: c.name,
44 created_time: c.created_time.map(TryInto::try_into).transpose()?,
45 last_access_time: c.last_access_time.map(TryInto::try_into).transpose()?,
46 scope: c.scope,
47 })
48 }
49}
50
51#[cfg(test)]
52mod tests {
53 use super::*;
54 use crate::internal::{config::Config, http_client::MockFxAClient};
55 use mockall::predicate::always;
56 use mockall::predicate::eq;
57 use std::sync::Arc;
58 use sync15::DeviceType;
59
60 #[test]
61 fn test_get_attached_clients() {
62 let config = Config::stable_dev("12345678", "https://foo.bar");
63 let mut fxa = FirefoxAccount::with_config(config);
64 fxa.set_session_token("session");
65
66 let mut client = MockFxAClient::new();
67 client
68 .expect_get_attached_clients()
69 .with(always(), eq("session"))
70 .times(1)
71 .returning(|_, _| {
72 Ok(vec![AttachedClient {
73 client_id: Some("12345678".into()),
74 session_token_id: None,
75 refresh_token_id: None,
76 device_id: None,
77 device_type: DeviceType::Desktop,
78 is_current_session: true,
79 name: None,
80 created_time: None,
81 last_access_time: None,
82 scope: None,
83 user_agent: "attachedClientsUserAgent".into(),
84 os: None,
85 }])
86 });
87
88 fxa.set_client(Arc::new(client));
89 assert!(fxa.attached_clients_cache.is_none());
90
91 let res = fxa.get_attached_clients();
92
93 assert!(res.is_ok());
94 assert!(fxa.attached_clients_cache.is_some());
95
96 let cached_attached_clients_res = fxa.attached_clients_cache.unwrap();
97 assert!(!cached_attached_clients_res.response.is_empty());
98 assert!(cached_attached_clients_res.cached_at > 0);
99
100 let cached_attached_clients = &cached_attached_clients_res.response[0];
101 assert_eq!(
102 cached_attached_clients.clone().client_id.unwrap(),
103 "12345678".to_string()
104 );
105 }
106
107 #[test]
108 fn test_get_attached_clients_network_errors() {
109 let config = Config::stable_dev("12345678", "https://foo.bar");
110 let mut fxa = FirefoxAccount::with_config(config);
111 fxa.set_session_token("session");
112
113 let mut client = MockFxAClient::new();
114 client
115 .expect_get_attached_clients()
116 .with(always(), eq("session"))
117 .times(1)
118 .returning(|_, _| {
119 Err(Error::RemoteError {
120 code: 500,
121 errno: 101,
122 error: "Did not work!".to_owned(),
123 message: "Did not work!".to_owned(),
124 info: "Did not work!".to_owned(),
125 })
126 });
127
128 fxa.set_client(Arc::new(client));
129 assert!(fxa.attached_clients_cache.is_none());
130
131 let res = fxa.get_attached_clients();
132 assert!(res.is_err());
133 assert!(fxa.attached_clients_cache.is_none());
134 }
135}