mas_handlers/graphql/query/
session.rs1use async_graphql::{Context, ID, Object, Union};
8use mas_data_model::Device;
9use mas_storage::{
10 Pagination, RepositoryAccess,
11 compat::{CompatSessionFilter, CompatSessionRepository},
12 oauth2::OAuth2SessionFilter,
13};
14use oauth2_types::scope::Scope;
15
16use crate::graphql::{
17 UserId,
18 model::{CompatSession, NodeType, OAuth2Session},
19 state::ContextExt,
20};
21
22#[derive(Default)]
23pub struct SessionQuery;
24
25#[derive(Union)]
27enum Session {
28 CompatSession(Box<CompatSession>),
29 OAuth2Session(Box<OAuth2Session>),
30}
31
32#[Object]
33impl SessionQuery {
34 async fn session(
36 &self,
37 ctx: &Context<'_>,
38 user_id: ID,
39 device_id: String,
40 ) -> Result<Option<Session>, async_graphql::Error> {
41 let user_id = NodeType::User.extract_ulid(&user_id)?;
42 let requester = ctx.requester();
43 if !requester.is_owner_or_admin(&UserId(user_id)) {
44 return Ok(None);
45 }
46
47 let device = Device::from(device_id);
48 let state = ctx.state();
49 let mut repo = state.repository().await?;
50
51 let Some(user) = repo.user().lookup(user_id).await? else {
53 return Ok(None);
54 };
55
56 let filter = CompatSessionFilter::new()
58 .for_user(&user)
59 .active_only()
60 .for_device(&device);
61 let pagination = Pagination::last(1);
63 let compat_sessions = repo.compat_session().list(filter, pagination).await?;
64
65 if compat_sessions.has_previous_page {
66 tracing::warn!(
68 "Found more than one active session with device {device} for user {user_id}"
69 );
70 }
71
72 if let Some((compat_session, sso_login)) = compat_sessions.edges.into_iter().next() {
73 repo.cancel().await?;
74
75 return Ok(Some(Session::CompatSession(Box::new(
76 CompatSession::new(compat_session).with_loaded_sso_login(sso_login),
77 ))));
78 }
79
80 let Ok(scope_token) = device.to_scope_token() else {
85 repo.cancel().await?;
86
87 return Ok(None);
88 };
89 let scope = Scope::from_iter([scope_token]);
90 let filter = OAuth2SessionFilter::new()
91 .for_user(&user)
92 .active_only()
93 .with_scope(&scope);
94 let sessions = repo.oauth2_session().list(filter, pagination).await?;
95
96 if sessions.has_previous_page {
99 tracing::warn!(
101 "Found more than one active session with device {device} for user {user_id}"
102 );
103 }
104
105 if let Some(session) = sessions.edges.into_iter().next() {
106 repo.cancel().await?;
107 return Ok(Some(Session::OAuth2Session(Box::new(OAuth2Session(
108 session,
109 )))));
110 }
111 repo.cancel().await?;
112
113 Ok(None)
114 }
115}