Skip to content

Commit 8359f8b

Browse files
authored
When adding or revoking personal sessions, schedule device synchronisation (#5182)
2 parents d8b5e16 + 3d80097 commit 8359f8b

File tree

3 files changed

+55
-4
lines changed

3 files changed

+55
-4
lines changed

crates/data-model/src/personal/session.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use oauth2_types::scope::Scope;
1010
use serde::Serialize;
1111
use ulid::Ulid;
1212

13-
use crate::{Client, InvalidTransitionError, User};
13+
use crate::{Client, Device, InvalidTransitionError, User};
1414

1515
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
1616
pub enum SessionState {
@@ -129,4 +129,13 @@ impl PersonalSession {
129129
self.state = self.state.revoke(revoked_at)?;
130130
Ok(self)
131131
}
132+
133+
/// Returns whether the scope of this session contains a device scope;
134+
/// in other words: whether this session has a device.
135+
#[must_use]
136+
pub fn has_device(&self) -> bool {
137+
self.scope
138+
.iter()
139+
.any(|scope_token| Device::from_scope_token(scope_token).is_some())
140+
}
132141
}

crates/handlers/src/admin/v1/personal_sessions/add.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,16 @@
33
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
44
// Please see LICENSE files in the repository root for full details.
55

6+
use std::sync::Arc;
7+
68
use aide::{NoApi, OperationIo, transform::TransformOperation};
7-
use axum::{Json, response::IntoResponse};
9+
use anyhow::Context;
10+
use axum::{Json, extract::State, response::IntoResponse};
811
use chrono::Duration;
912
use hyper::StatusCode;
1013
use mas_axum_utils::record_error;
11-
use mas_data_model::{BoxRng, TokenType};
14+
use mas_data_model::{BoxRng, Device, TokenType};
15+
use mas_matrix::HomeserverConnection;
1216
use oauth2_types::scope::Scope;
1317
use schemars::JsonSchema;
1418
use serde::Deserialize;
@@ -99,6 +103,7 @@ pub async fn handler(
99103
..
100104
}: CallContext,
101105
NoApi(mut rng): NoApi<BoxRng>,
106+
NoApi(State(homeserver)): NoApi<State<Arc<dyn HomeserverConnection>>>,
102107
Json(params): Json<Request>,
103108
) -> Result<(StatusCode, Json<SingleResponse<PersonalSession>>), RouteError> {
104109
let owner = personal_session_owner_from_caller(&session);
@@ -139,6 +144,28 @@ pub async fn handler(
139144
)
140145
.await?;
141146

147+
// If the session has a device, we should add those to the homeserver now
148+
if session.has_device() {
149+
// Lock the user sync to make sure we don't get into a race condition
150+
repo.user().acquire_lock_for_sync(&actor_user).await?;
151+
152+
for scope in &*session.scope {
153+
if let Some(device) = Device::from_scope_token(scope) {
154+
// NOTE: We haven't relinquished the repo at this point,
155+
// so we are holding a transaction across the homeserver
156+
// operation.
157+
// This is suboptimal, but simpler.
158+
// Given this is an administrative endpoint, this is a tolerable
159+
// compromise for now.
160+
homeserver
161+
.upsert_device(&actor_user.username, device.as_str(), None)
162+
.await
163+
.context("Failed to provision device")
164+
.map_err(|e| RouteError::Internal(e.into()))?;
165+
}
166+
}
167+
}
168+
142169
repo.save().await?;
143170

144171
Ok((

crates/handlers/src/admin/v1/personal_sessions/revoke.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
44
// Please see LICENSE files in the repository root for full details.
55

6-
use aide::{OperationIo, transform::TransformOperation};
6+
use aide::{NoApi, OperationIo, transform::TransformOperation};
77
use axum::{Json, response::IntoResponse};
88
use hyper::StatusCode;
99
use mas_axum_utils::record_error;
10+
use mas_data_model::BoxRng;
11+
use mas_storage::queue::{QueueJobRepositoryExt as _, SyncDevicesJob};
1012
use ulid::Ulid;
1113

1214
use crate::{
@@ -80,6 +82,7 @@ pub async fn handler(
8082
CallContext {
8183
mut repo, clock, ..
8284
}: CallContext,
85+
NoApi(mut rng): NoApi<BoxRng>,
8386
session_id: UlidPathParam,
8487
) -> Result<Json<SingleResponse<PersonalSession>>, RouteError> {
8588
let session_id = *session_id;
@@ -95,6 +98,18 @@ pub async fn handler(
9598

9699
let session = repo.personal_session().revoke(&clock, session).await?;
97100

101+
if session.has_device() {
102+
// If the session has a device, then we are now
103+
// deleting a device and should schedule a device sync to clean up.
104+
repo.queue_job()
105+
.schedule_job(
106+
&mut rng,
107+
&clock,
108+
SyncDevicesJob::new_for_id(session.actor_user_id),
109+
)
110+
.await?;
111+
}
112+
98113
repo.save().await?;
99114

100115
Ok(Json(SingleResponse::new_canonical(

0 commit comments

Comments
 (0)