Skip to content

Commit a73cb1c

Browse files
committed
UNFINISHED: finish active sessions when replacing a device
1 parent 1f2eccc commit a73cb1c

File tree

5 files changed

+115
-3
lines changed

5 files changed

+115
-3
lines changed

crates/handlers/src/compat/login.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,10 @@ async fn token_login(
468468
.await
469469
.map_err(RouteError::ProvisionDeviceFailed)?;
470470

471+
repo.app_session()
472+
.finish_sessions_to_replace_device(clock, &browser_session.user, &device)
473+
.await?;
474+
471475
let compat_session = repo
472476
.compat_session()
473477
.add(
@@ -563,6 +567,10 @@ async fn user_password_login(
563567
.await
564568
.map_err(RouteError::ProvisionDeviceFailed)?;
565569

570+
repo.app_session()
571+
.finish_sessions_to_replace_device(clock, &user, &device)
572+
.await?;
573+
566574
let session = repo
567575
.compat_session()
568576
.add(&mut rng, clock, &user, device, None, false)

crates/storage-pg/.sqlx/query-373f7eb215b0e515b000a37e55bd055954f697f257de026b74ec408938a52a1a.json

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/storage-pg/.sqlx/query-b74e4d620bed4832a4e8e713a346691f260a7eca4bf494d6fb11c7cf699adaad.json

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/storage-pg/src/app_session.rs

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77
//! A module containing PostgreSQL implementation of repositories for sessions
88
99
use async_trait::async_trait;
10-
use mas_data_model::{CompatSession, CompatSessionState, Device, Session, SessionState, UserAgent};
10+
use mas_data_model::{
11+
CompatSession, CompatSessionState, Device, Session, SessionState, User, UserAgent,
12+
};
1113
use mas_storage::{
12-
Page, Pagination,
14+
Clock, Page, Pagination,
1315
app_session::{AppSession, AppSessionFilter, AppSessionRepository, AppSessionState},
1416
compat::CompatSessionFilter,
1517
oauth2::OAuth2SessionFilter,
@@ -21,6 +23,7 @@ use sea_query::{
2123
use sea_query_binder::SqlxBinder;
2224
use sqlx::PgConnection;
2325
use ulid::Ulid;
26+
use uuid::Uuid;
2427

2528
use crate::{
2629
DatabaseError, ExecuteExt,
@@ -457,6 +460,54 @@ impl AppSessionRepository for PgAppSessionRepository<'_> {
457460
.try_into()
458461
.map_err(DatabaseError::to_invalid_operation)
459462
}
463+
464+
#[tracing::instrument(
465+
name = "db.app_session.finish_sessions_to_replace_device",
466+
fields(
467+
db.query.text,
468+
%user.id,
469+
%device_id = device.as_str()
470+
),
471+
skip_all,
472+
err,
473+
)]
474+
async fn finish_sessions_to_replace_device(
475+
&mut self,
476+
clock: &dyn Clock,
477+
user: &User,
478+
device: &Device,
479+
) -> Result<(), Self::Error> {
480+
// TODO need to invoke this from all the oauth2 login sites
481+
// TODO CREATE A SECOND SPAN FOR THE SECOND QUERY
482+
let finished_at = clock.now();
483+
sqlx::query!(
484+
"
485+
UPDATE compat_sessions SET finished_at = $3 WHERE user_id = $1 AND device_id = $2 AND finished_at IS NULL
486+
",
487+
Uuid::from(user.id),
488+
device.as_str(),
489+
finished_at
490+
)
491+
.traced()
492+
.execute(&mut *self.conn)
493+
.await?;
494+
495+
if let Ok(device_as_scope_token) = device.to_scope_token() {
496+
sqlx::query!(
497+
"
498+
UPDATE oauth2_sessions SET finished_at = $3 WHERE user_id = $1 AND $2 = ANY(scope_list) AND finished_at IS NULL
499+
",
500+
Uuid::from(user.id),
501+
device_as_scope_token.as_str(),
502+
finished_at
503+
)
504+
.traced()
505+
.execute(&mut *self.conn)
506+
.await?;
507+
}
508+
509+
Ok(())
510+
}
460511
}
461512

462513
#[cfg(test)]

crates/storage/src/app_session.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use async_trait::async_trait;
1010
use chrono::{DateTime, Utc};
1111
use mas_data_model::{BrowserSession, CompatSession, Device, Session, User};
1212

13-
use crate::{Page, Pagination, repository_impl};
13+
use crate::{Clock, Page, Pagination, repository_impl};
1414

1515
/// The state of a session
1616
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -188,6 +188,20 @@ pub trait AppSessionRepository: Send + Sync {
188188
///
189189
/// Returns [`Self::Error`] if the underlying repository fails
190190
async fn count(&mut self, filter: AppSessionFilter<'_>) -> Result<usize, Self::Error>;
191+
192+
/// Finishes any application sessions that are using the specified device's
193+
/// ID.
194+
///
195+
/// This is intended for logging in using an existing device ID (i.e.
196+
/// replacing a device).
197+
///
198+
/// Should be called *before* creating a new session for the device.
199+
async fn finish_sessions_to_replace_device(
200+
&mut self,
201+
clock: &dyn Clock,
202+
user: &User,
203+
device: &Device,
204+
) -> Result<(), Self::Error>;
191205
}
192206

193207
repository_impl!(AppSessionRepository:
@@ -198,4 +212,11 @@ repository_impl!(AppSessionRepository:
198212
) -> Result<Page<AppSession>, Self::Error>;
199213

200214
async fn count(&mut self, filter: AppSessionFilter<'_>) -> Result<usize, Self::Error>;
215+
216+
async fn finish_sessions_to_replace_device(
217+
&mut self,
218+
clock: &dyn Clock,
219+
user: &User,
220+
device: &Device,
221+
) -> Result<(), Self::Error>;
201222
);

0 commit comments

Comments
 (0)