mas_handlers/graphql/query/
session.rsuse async_graphql::{Context, ID, Object, Union};
use mas_data_model::Device;
use mas_storage::{
Pagination, RepositoryAccess,
compat::{CompatSessionFilter, CompatSessionRepository},
oauth2::OAuth2SessionFilter,
};
use oauth2_types::scope::Scope;
use crate::graphql::{
UserId,
model::{CompatSession, NodeType, OAuth2Session},
state::ContextExt,
};
#[derive(Default)]
pub struct SessionQuery;
#[derive(Union)]
enum Session {
CompatSession(Box<CompatSession>),
OAuth2Session(Box<OAuth2Session>),
}
#[Object]
impl SessionQuery {
async fn session(
&self,
ctx: &Context<'_>,
user_id: ID,
device_id: String,
) -> Result<Option<Session>, async_graphql::Error> {
let user_id = NodeType::User.extract_ulid(&user_id)?;
let requester = ctx.requester();
if !requester.is_owner_or_admin(&UserId(user_id)) {
return Ok(None);
}
let device = Device::from(device_id);
let state = ctx.state();
let mut repo = state.repository().await?;
let Some(user) = repo.user().lookup(user_id).await? else {
return Ok(None);
};
let filter = CompatSessionFilter::new()
.for_user(&user)
.active_only()
.for_device(&device);
let pagination = Pagination::last(1);
let compat_sessions = repo.compat_session().list(filter, pagination).await?;
if compat_sessions.has_previous_page {
tracing::warn!(
"Found more than one active session with device {device} for user {user_id}"
);
}
if let Some((compat_session, sso_login)) = compat_sessions.edges.into_iter().next() {
repo.cancel().await?;
return Ok(Some(Session::CompatSession(Box::new(
CompatSession::new(compat_session).with_loaded_sso_login(sso_login),
))));
}
let Ok(scope_token) = device.to_scope_token() else {
repo.cancel().await?;
return Ok(None);
};
let scope = Scope::from_iter([scope_token]);
let filter = OAuth2SessionFilter::new()
.for_user(&user)
.active_only()
.with_scope(&scope);
let sessions = repo.oauth2_session().list(filter, pagination).await?;
if sessions.has_previous_page {
tracing::warn!(
"Found more than one active session with device {device} for user {user_id}"
);
}
if let Some(session) = sessions.edges.into_iter().next() {
repo.cancel().await?;
return Ok(Some(Session::OAuth2Session(Box::new(OAuth2Session(
session,
)))));
}
repo.cancel().await?;
Ok(None)
}
}