You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-07-31 09:24:31 +03:00
graphql: allow filtering appsessions on device_id
This commit is contained in:
@ -17,6 +17,7 @@ use async_graphql::{
|
|||||||
Context, Description, Enum, Object, Union, ID,
|
Context, Description, Enum, Object, Union, ID,
|
||||||
};
|
};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
|
use mas_data_model::Device;
|
||||||
use mas_storage::{
|
use mas_storage::{
|
||||||
app_session::AppSessionFilter,
|
app_session::AppSessionFilter,
|
||||||
compat::{CompatSessionFilter, CompatSsoLoginFilter, CompatSsoLoginRepository},
|
compat::{CompatSessionFilter, CompatSsoLoginFilter, CompatSsoLoginRepository},
|
||||||
@ -535,6 +536,9 @@ impl User {
|
|||||||
#[graphql(name = "state", desc = "List only sessions in the given state.")]
|
#[graphql(name = "state", desc = "List only sessions in the given state.")]
|
||||||
state_param: Option<SessionState>,
|
state_param: Option<SessionState>,
|
||||||
|
|
||||||
|
#[graphql(name = "device", desc = "List only sessions for the given device.")]
|
||||||
|
device_param: Option<String>,
|
||||||
|
|
||||||
#[graphql(desc = "Returns the elements in the list that come after the cursor.")]
|
#[graphql(desc = "Returns the elements in the list that come after the cursor.")]
|
||||||
after: Option<String>,
|
after: Option<String>,
|
||||||
#[graphql(desc = "Returns the elements in the list that come before the cursor.")]
|
#[graphql(desc = "Returns the elements in the list that come before the cursor.")]
|
||||||
@ -563,6 +567,8 @@ impl User {
|
|||||||
.transpose()?;
|
.transpose()?;
|
||||||
let pagination = Pagination::try_new(before_id, after_id, first, last)?;
|
let pagination = Pagination::try_new(before_id, after_id, first, last)?;
|
||||||
|
|
||||||
|
let device_param = device_param.map(Device::try_from).transpose()?;
|
||||||
|
|
||||||
let filter = AppSessionFilter::new().for_user(&self.0);
|
let filter = AppSessionFilter::new().for_user(&self.0);
|
||||||
|
|
||||||
let filter = match state_param {
|
let filter = match state_param {
|
||||||
@ -571,6 +577,11 @@ impl User {
|
|||||||
None => filter,
|
None => filter,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let filter = match device_param.as_ref() {
|
||||||
|
Some(device) => filter.for_device(device),
|
||||||
|
None => filter,
|
||||||
|
};
|
||||||
|
|
||||||
let page = repo.app_session().list(filter, pagination).await?;
|
let page = repo.app_session().list(filter, pagination).await?;
|
||||||
|
|
||||||
let count = if ctx.look_ahead().field("totalCount").exists() {
|
let count = if ctx.look_ahead().field("totalCount").exists() {
|
||||||
|
@ -22,7 +22,7 @@ use mas_storage::{
|
|||||||
};
|
};
|
||||||
use oauth2_types::scope::{Scope, ScopeToken};
|
use oauth2_types::scope::{Scope, ScopeToken};
|
||||||
use sea_query::{
|
use sea_query::{
|
||||||
Alias, ColumnRef, CommonTableExpression, Expr, PostgresQueryBuilder, Query, UnionType,
|
Alias, ColumnRef, CommonTableExpression, Expr, PgFunc, PostgresQueryBuilder, Query, UnionType,
|
||||||
};
|
};
|
||||||
use sea_query_binder::SqlxBinder;
|
use sea_query_binder::SqlxBinder;
|
||||||
use sqlx::PgConnection;
|
use sqlx::PgConnection;
|
||||||
@ -269,6 +269,12 @@ impl<'c> AppSessionRepository for PgAppSessionRepository<'c> {
|
|||||||
Expr::col((OAuth2Sessions::Table, OAuth2Sessions::FinishedAt)).is_not_null()
|
Expr::col((OAuth2Sessions::Table, OAuth2Sessions::FinishedAt)).is_not_null()
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
.and_where_option(filter.device().map(|device| {
|
||||||
|
Expr::val(device.to_scope_token().to_string()).eq(PgFunc::any(Expr::col((
|
||||||
|
OAuth2Sessions::Table,
|
||||||
|
OAuth2Sessions::ScopeList,
|
||||||
|
))))
|
||||||
|
}))
|
||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
let compat_session_select = Query::select()
|
let compat_session_select = Query::select()
|
||||||
@ -323,6 +329,9 @@ impl<'c> AppSessionRepository for PgAppSessionRepository<'c> {
|
|||||||
Expr::col((CompatSessions::Table, CompatSessions::FinishedAt)).is_not_null()
|
Expr::col((CompatSessions::Table, CompatSessions::FinishedAt)).is_not_null()
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
.and_where_option(filter.device().map(|device| {
|
||||||
|
Expr::col((CompatSessions::Table, CompatSessions::DeviceId)).eq(device.to_string())
|
||||||
|
}))
|
||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
let common_table_expression = CommonTableExpression::new()
|
let common_table_expression = CommonTableExpression::new()
|
||||||
@ -376,6 +385,12 @@ impl<'c> AppSessionRepository for PgAppSessionRepository<'c> {
|
|||||||
Expr::col((OAuth2Sessions::Table, OAuth2Sessions::FinishedAt)).is_not_null()
|
Expr::col((OAuth2Sessions::Table, OAuth2Sessions::FinishedAt)).is_not_null()
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
.and_where_option(filter.device().map(|device| {
|
||||||
|
Expr::val(device.to_scope_token().to_string()).eq(PgFunc::any(Expr::col((
|
||||||
|
OAuth2Sessions::Table,
|
||||||
|
OAuth2Sessions::ScopeList,
|
||||||
|
))))
|
||||||
|
}))
|
||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
let compat_session_select = Query::select()
|
let compat_session_select = Query::select()
|
||||||
@ -391,6 +406,9 @@ impl<'c> AppSessionRepository for PgAppSessionRepository<'c> {
|
|||||||
Expr::col((CompatSessions::Table, CompatSessions::FinishedAt)).is_not_null()
|
Expr::col((CompatSessions::Table, CompatSessions::FinishedAt)).is_not_null()
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
.and_where_option(filter.device().map(|device| {
|
||||||
|
Expr::col((CompatSessions::Table, CompatSessions::DeviceId)).eq(device.to_string())
|
||||||
|
}))
|
||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
let common_table_expression = CommonTableExpression::new()
|
let common_table_expression = CommonTableExpression::new()
|
||||||
@ -475,7 +493,7 @@ mod tests {
|
|||||||
let device = Device::generate(&mut rng);
|
let device = Device::generate(&mut rng);
|
||||||
let compat_session = repo
|
let compat_session = repo
|
||||||
.compat_session()
|
.compat_session()
|
||||||
.add(&mut rng, &clock, &user, device, false)
|
.add(&mut rng, &clock, &user, device.clone(), false)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -552,7 +570,8 @@ mod tests {
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let scope = Scope::from_iter([OPENID]);
|
let device2 = Device::generate(&mut rng);
|
||||||
|
let scope = Scope::from_iter([OPENID, device2.to_scope_token()]);
|
||||||
|
|
||||||
// We're moving the clock forward by 1 minute between each session to ensure
|
// We're moving the clock forward by 1 minute between each session to ensure
|
||||||
// we're getting consistent ordering in lists.
|
// we're getting consistent ordering in lists.
|
||||||
@ -629,6 +648,25 @@ mod tests {
|
|||||||
AppSession::OAuth2(Box::new(oauth_session.clone()))
|
AppSession::OAuth2(Box::new(oauth_session.clone()))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Query by device
|
||||||
|
let filter = AppSessionFilter::new().for_device(&device);
|
||||||
|
assert_eq!(repo.app_session().count(filter).await.unwrap(), 1);
|
||||||
|
let list = repo.app_session().list(filter, pagination).await.unwrap();
|
||||||
|
assert_eq!(list.edges.len(), 1);
|
||||||
|
assert_eq!(
|
||||||
|
list.edges[0],
|
||||||
|
AppSession::Compat(Box::new(compat_session.clone()))
|
||||||
|
);
|
||||||
|
|
||||||
|
let filter = AppSessionFilter::new().for_device(&device2);
|
||||||
|
assert_eq!(repo.app_session().count(filter).await.unwrap(), 1);
|
||||||
|
let list = repo.app_session().list(filter, pagination).await.unwrap();
|
||||||
|
assert_eq!(list.edges.len(), 1);
|
||||||
|
assert_eq!(
|
||||||
|
list.edges[0],
|
||||||
|
AppSession::OAuth2(Box::new(oauth_session.clone()))
|
||||||
|
);
|
||||||
|
|
||||||
// Create a second user
|
// Create a second user
|
||||||
let user2 = repo
|
let user2 = repo
|
||||||
.user()
|
.user()
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
//! Repositories to interact with all kinds of sessions
|
//! Repositories to interact with all kinds of sessions
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use mas_data_model::{CompatSession, Session, User};
|
use mas_data_model::{CompatSession, Device, Session, User};
|
||||||
|
|
||||||
use crate::{repository_impl, Page, Pagination};
|
use crate::{repository_impl, Page, Pagination};
|
||||||
|
|
||||||
@ -57,6 +57,7 @@ pub enum AppSession {
|
|||||||
pub struct AppSessionFilter<'a> {
|
pub struct AppSessionFilter<'a> {
|
||||||
user: Option<&'a User>,
|
user: Option<&'a User>,
|
||||||
state: Option<AppSessionState>,
|
state: Option<AppSessionState>,
|
||||||
|
device_id: Option<&'a Device>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> AppSessionFilter<'a> {
|
impl<'a> AppSessionFilter<'a> {
|
||||||
@ -79,6 +80,19 @@ impl<'a> AppSessionFilter<'a> {
|
|||||||
self.user
|
self.user
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the device ID filter
|
||||||
|
#[must_use]
|
||||||
|
pub fn for_device(mut self, device_id: &'a Device) -> Self {
|
||||||
|
self.device_id = Some(device_id);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the device ID filter
|
||||||
|
#[must_use]
|
||||||
|
pub fn device(&self) -> Option<&'a Device> {
|
||||||
|
self.device_id
|
||||||
|
}
|
||||||
|
|
||||||
/// Only return active compatibility sessions
|
/// Only return active compatibility sessions
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn active_only(mut self) -> Self {
|
pub fn active_only(mut self) -> Self {
|
||||||
|
@ -1400,6 +1400,10 @@ type User implements Node {
|
|||||||
"""
|
"""
|
||||||
state: SessionState
|
state: SessionState
|
||||||
"""
|
"""
|
||||||
|
List only sessions for the given device.
|
||||||
|
"""
|
||||||
|
device: String
|
||||||
|
"""
|
||||||
Returns the elements in the list that come after the cursor.
|
Returns the elements in the list that come after the cursor.
|
||||||
"""
|
"""
|
||||||
after: String
|
after: String
|
||||||
|
@ -919,6 +919,7 @@ export type User = Node & {
|
|||||||
export type UserAppSessionsArgs = {
|
export type UserAppSessionsArgs = {
|
||||||
after?: InputMaybe<Scalars["String"]["input"]>;
|
after?: InputMaybe<Scalars["String"]["input"]>;
|
||||||
before?: InputMaybe<Scalars["String"]["input"]>;
|
before?: InputMaybe<Scalars["String"]["input"]>;
|
||||||
|
device?: InputMaybe<Scalars["String"]["input"]>;
|
||||||
first?: InputMaybe<Scalars["Int"]["input"]>;
|
first?: InputMaybe<Scalars["Int"]["input"]>;
|
||||||
last?: InputMaybe<Scalars["Int"]["input"]>;
|
last?: InputMaybe<Scalars["Int"]["input"]>;
|
||||||
state?: InputMaybe<SessionState>;
|
state?: InputMaybe<SessionState>;
|
||||||
|
@ -2545,6 +2545,13 @@ export default {
|
|||||||
name: "Any",
|
name: "Any",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "device",
|
||||||
|
type: {
|
||||||
|
kind: "SCALAR",
|
||||||
|
name: "Any",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "first",
|
name: "first",
|
||||||
type: {
|
type: {
|
||||||
|
Reference in New Issue
Block a user