1mod adopt;
2pub mod association;
3
4use std::{
5 iter::once,
6 sync::{Arc, Mutex},
7 time::SystemTime,
8};
9
10use futures::{FutureExt, Stream, StreamExt, TryFutureExt, TryStreamExt};
11use ruma::{OwnedUserId, UserId};
12use serde::{Deserialize, Serialize};
13use tuwunel_core::{
14 Err, Result, at, implement,
15 utils::{
16 MutexMap,
17 stream::{IterStream, ReadyExt, TryExpect},
18 },
19};
20use tuwunel_database::{Cbor, Database, Deserialized, Ignore, Map};
21use url::Url;
22
23pub use self::adopt::Counts;
24use super::{Provider, Providers, UserInfo, unique_id as session_unique_id};
25use crate::SelfServices;
26
27pub struct Sessions {
28 services: SelfServices,
29 association_pending: Mutex<association::Pending>,
30
31 write_locks: MutexMap<String, ()>,
36
37 providers: Arc<Providers>,
38 db: Data,
39}
40
41struct Data {
42 oauthid_session: Arc<Map>,
43 oauthuniqid_oauthid: Arc<Map>,
44 userid_oauthid: Arc<Map>,
45 database: Arc<Database>,
46}
47
48#[derive(Clone, Debug, Default, Deserialize, Serialize)]
54pub struct Session {
55 pub idp_id: Option<String>,
58
59 pub sess_id: Option<SessionId>,
61
62 pub token_type: Option<String>,
64
65 pub access_token: Option<String>,
67
68 pub id_token: Option<String>,
70
71 pub expires_in: Option<u64>,
73
74 pub expires_at: Option<SystemTime>,
76
77 pub refresh_token: Option<String>,
79
80 pub refresh_token_expires_in: Option<u64>,
82
83 pub refresh_token_expires_at: Option<SystemTime>,
85
86 pub scope: Option<String>,
88
89 pub redirect_url: Option<Url>,
91
92 pub code_verifier: Option<String>,
94
95 pub cookie_nonce: Option<String>,
97
98 pub query_nonce: Option<String>,
100
101 pub authorize_expires_at: Option<SystemTime>,
103
104 pub user_id: Option<OwnedUserId>,
106
107 pub user_info: Option<UserInfo>,
109}
110
111pub type SessionId = String;
113
114pub const CODE_VERIFIER_LENGTH: usize = 64;
117
118pub const SESSION_ID_LENGTH: usize = 32;
120
121#[implement(Sessions)]
122pub(super) fn build(args: &crate::Args<'_>, providers: Arc<Providers>) -> Self {
123 Self {
124 services: args.services.clone(),
125 association_pending: Default::default(),
126 write_locks: MutexMap::new(),
127 providers,
128 db: Data {
129 oauthid_session: args.db["oauthid_session"].clone(),
130 oauthuniqid_oauthid: args.db["oauthuniqid_oauthid"].clone(),
131 userid_oauthid: args.db["userid_oauthid"].clone(),
132 database: args.db.clone(),
133 },
134 }
135}
136
137#[implement(Sessions)]
142#[tracing::instrument(level = "debug", skip(self))]
143pub async fn delete(&self, sess_id: &str) {
144 let (session, unique_id, _write_guard) = loop {
145 let Ok(snapshot) = self.get(sess_id).await else {
146 return;
147 };
148
149 let provider = async {
150 let idp_id = snapshot.idp_id.as_deref()?;
151
152 self.providers.get(idp_id).map(Result::ok).await
153 }
154 .await;
155
156 let unique_id = provider
157 .as_ref()
158 .and_then(|provider| session_unique_id((provider, &snapshot)).ok());
159
160 let write_guard = match unique_id.as_deref() {
161 | Some(unique_id) => Some(self.write_locks.lock(unique_id).await),
162 | None => None,
163 };
164
165 let Ok(session) = self.get(sess_id).await else {
166 return;
167 };
168
169 if session.idp_id.as_deref() != snapshot.idp_id.as_deref() {
170 continue;
171 }
172
173 let current_unique_id = provider
174 .as_ref()
175 .and_then(|provider| session_unique_id((provider, &session)).ok());
176
177 if current_unique_id == unique_id {
178 break (session, unique_id, write_guard);
179 }
180 };
181
182 let unique_id = async {
184 let unique_id = unique_id.as_deref()?;
185 let assoc_id = self
186 .get_sess_id_by_unique_id(unique_id)
187 .map(Result::ok)
188 .await?;
189
190 (assoc_id == sess_id).then_some(unique_id)
191 }
192 .await;
193
194 let user_sessions = async {
195 let user_id = session.user_id.as_deref()?;
196 let sess_ids: Vec<_> = self
197 .get_sess_id_by_user(user_id)
198 .ready_filter_map(Result::ok)
199 .ready_filter(|assoc_id| assoc_id != sess_id)
200 .collect()
201 .await;
202
203 Some((user_id, sess_ids))
204 }
205 .await;
206
207 let mut txn = self.db.database.txn();
208
209 if let Some((user_id, sess_ids)) = user_sessions {
210 if !sess_ids.is_empty() {
211 txn.raw_put(&self.db.userid_oauthid, user_id, sess_ids);
212 } else {
213 txn.del_raw(&self.db.userid_oauthid, user_id);
214 }
215 }
216
217 if let Some(unique_id) = unique_id {
218 txn.del_raw(&self.db.oauthuniqid_oauthid, unique_id);
219 }
220
221 txn.del_raw(&self.db.oauthid_session, sess_id);
222 txn.execute();
223}
224
225#[implement(Sessions)]
230#[tracing::instrument(level = "info", skip(self))]
231pub async fn put(&self, session: &Session) {
232 let unique_id = async {
233 let idp_id = session.idp_id.as_ref()?;
234 let provider = self.providers.get(idp_id).map(Result::ok).await?;
235
236 session_unique_id((&provider, session)).ok()
237 }
238 .await;
239
240 let _write_guard = match unique_id.as_deref() {
241 | Some(unique_id) => Some(self.write_locks.lock(unique_id).await),
242 | None => None,
243 };
244
245 self.put_locked(session, unique_id.as_deref())
246 .await;
247}
248
249#[implement(Sessions)]
254#[tracing::instrument(level = "debug", skip_all)]
255pub async fn commit_identity_session<T, F, Fut>(
256 &self,
257 unique_id: &str,
258 build: F,
259) -> Result<(Session, T, Option<SessionId>)>
260where
261 T: Send,
262 F: FnOnce(Option<OwnedUserId>) -> Fut + Send,
263 Fut: Future<Output = Result<(Session, T)>> + Send,
264{
265 let write_guard = self.write_locks.lock(unique_id).await;
266 let existing = match self.get_by_unique_id(unique_id).await {
267 | Ok(session) => Some(session),
268 | Err(e) if e.is_not_found() => None,
269 | Err(e) => return Err(e),
270 };
271
272 let old_sess_id = existing
273 .as_ref()
274 .and_then(|session| session.sess_id.clone());
275
276 let old_user_id = existing.and_then(|session| session.user_id);
277 let (session, value) = build(old_user_id).await?;
278
279 self.put_locked(&session, Some(unique_id)).await;
280 drop(write_guard);
281
282 Ok((session, value, old_sess_id))
283}
284
285#[implement(Sessions)]
286async fn put_locked(&self, session: &Session, unique_id: Option<&str>) {
287 let sess_id = session
288 .sess_id
289 .as_deref()
290 .expect("Missing session.sess_id required for sessions.put()");
291
292 let user_sessions = async {
293 let user_id = session.user_id.as_deref()?;
294 let sess_ids = self
295 .get_sess_id_by_user(user_id)
296 .ready_filter_map(Result::ok)
297 .chain(once(sess_id.to_owned()).stream())
298 .collect::<Vec<_>>()
299 .map(|mut ids| {
300 ids.sort_unstable();
301 ids.dedup();
302 ids
303 })
304 .await;
305
306 Some((user_id, sess_ids))
307 }
308 .await;
309
310 let mut txn = self.db.database.txn();
311
312 txn.raw_put(&self.db.oauthid_session, sess_id, Cbor(session));
313
314 if let Some(unique_id) = unique_id {
315 txn.insert_raw(&self.db.oauthuniqid_oauthid, unique_id, sess_id);
316 }
317
318 if let Some((user_id, sess_ids)) = user_sessions {
319 txn.raw_put(&self.db.userid_oauthid, user_id, sess_ids);
320 }
321
322 txn.execute();
323}
324
325#[implement(Sessions)]
328#[tracing::instrument(level = "debug", skip(self), ret(level = "debug"))]
329pub async fn get_by_unique_id(&self, unique_id: &str) -> Result<Session> {
330 self.get_sess_id_by_unique_id(unique_id)
331 .and_then(async |sess_id| self.get(&sess_id).await)
332 .await
333}
334
335#[implement(Sessions)]
338#[tracing::instrument(level = "debug", skip(self))]
339pub fn get_by_user(&self, user_id: &UserId) -> impl Stream<Item = Result<Session>> + Send {
340 self.get_sess_id_by_user(user_id)
341 .and_then(async |sess_id| self.get(&sess_id).await)
342}
343
344#[implement(Sessions)]
346#[tracing::instrument(level = "debug", skip(self), ret(level = "debug"))]
347pub async fn get(&self, sess_id: &str) -> Result<Session> {
348 self.db
349 .oauthid_session
350 .get(sess_id)
351 .await
352 .deserialized::<Cbor<_>>()
353 .map(at!(0))
354}
355
356#[implement(Sessions)]
358#[tracing::instrument(level = "debug", skip(self))]
359pub fn get_sess_id_by_user(&self, user_id: &UserId) -> impl Stream<Item = Result<String>> + Send {
360 self.db
361 .userid_oauthid
362 .get(user_id)
363 .map(Deserialized::deserialized)
364 .map_ok(Vec::into_iter)
365 .map_ok(IterStream::try_stream)
366 .try_flatten_stream()
367}
368
369#[implement(Sessions)]
371#[tracing::instrument(level = "debug", skip(self), ret(level = "debug"))]
372pub async fn get_sess_id_by_unique_id(&self, unique_id: &str) -> Result<String> {
373 self.db
374 .oauthuniqid_oauthid
375 .get(unique_id)
376 .await
377 .deserialized()
378}
379
380#[implement(Sessions)]
381pub fn users(&self) -> impl Stream<Item = OwnedUserId> + Send {
382 self.db
383 .userid_oauthid
384 .keys()
385 .expect_ok()
386 .map(UserId::to_owned)
387}
388
389#[implement(Sessions)]
390pub fn stream(&self) -> impl Stream<Item = Session> + Send {
391 self.db
392 .oauthid_session
393 .stream()
394 .expect_ok()
395 .map(|(_, session): (Ignore, Cbor<_>)| session.0)
396}
397
398#[implement(Sessions)]
399pub async fn provider(&self, session: &Session) -> Result<Provider> {
400 let Some(idp_id) = session.idp_id.as_deref() else {
401 return Err!(Request(NotFound("No provider for this session")));
402 };
403
404 self.providers.get(idp_id).await
405}