mirror of
https://github.com/element-hq/matrix-authentication-service.git
synced 2026-10-23 23:54:14 +00:00
admin-api: filter user list by presence of active compat session
Adds `filter[has-active-compat-session]=true|false` to `GET /api/admin/v1/users`. When `true` (resp. `false`), only users with (resp. without) at least one active (non-finished) compatibility session are returned.
This commit is contained in:
3 files changed
+108
-1
No files matched your search
@@ -85,6 +85,11 @@ pub struct FilterParams {
|
||||
#[serde(default, rename = "filter[active-oauth2-client]")]
|
||||
#[schemars(with = "Vec<crate::admin::schema::Ulid>")]
|
||||
active_oauth2_client: Vec<Ulid>,
|
||||
|
||||
/// Retrieve users which have (or don't have) at least one active
|
||||
/// (non-finished) compatibility session.
|
||||
#[serde(rename = "filter[has-active-compat-session]")]
|
||||
has_active_compat_session: Option<bool>,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for FilterParams {
|
||||
@@ -111,6 +116,10 @@ impl std::fmt::Display for FilterParams {
|
||||
write!(f, "{sep}filter[active-oauth2-client]={client}")?;
|
||||
sep = '&';
|
||||
}
|
||||
if let Some(has) = self.has_active_compat_session {
|
||||
write!(f, "{sep}filter[has-active-compat-session]={has}")?;
|
||||
sep = '&';
|
||||
}
|
||||
|
||||
let _ = sep;
|
||||
Ok(())
|
||||
@@ -228,6 +237,11 @@ pub async fn handler(
|
||||
filter.with_active_oauth2_session_for_any_of_clients(¶ms.active_oauth2_client)
|
||||
};
|
||||
|
||||
let filter = match params.has_active_compat_session {
|
||||
Some(has) => filter.with_active_compat_session(has),
|
||||
None => filter,
|
||||
};
|
||||
|
||||
let response = match include_count {
|
||||
IncludeCount::True => {
|
||||
let page = repo.user().list(filter, pagination).await?;
|
||||
@@ -250,8 +264,10 @@ pub async fn handler(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use hyper::{Request, StatusCode};
|
||||
use mas_data_model::Device;
|
||||
use mas_storage::{
|
||||
RepositoryAccess,
|
||||
compat::CompatSessionRepository,
|
||||
oauth2::{OAuth2ClientRepository, OAuth2SessionRepository},
|
||||
};
|
||||
use oauth2_types::{
|
||||
@@ -612,4 +628,75 @@ mod tests {
|
||||
let response = state.request(request).await;
|
||||
response.assert_status(StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
/// Test the `filter[has-active-compat-session]` filter in both
|
||||
/// directions.
|
||||
#[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")]
|
||||
async fn test_list_users_filter_has_active_compat_session(pool: PgPool) {
|
||||
setup();
|
||||
let mut state = TestState::from_pool(pool).await.unwrap();
|
||||
let token = state.token_with_scope("urn:mas:admin").await;
|
||||
let mut rng = state.rng();
|
||||
|
||||
let mut repo = state.repository().await.unwrap();
|
||||
|
||||
// Alice has an active compat session.
|
||||
let alice = repo
|
||||
.user()
|
||||
.add(&mut rng, &state.clock, "alice".to_owned())
|
||||
.await
|
||||
.unwrap();
|
||||
let device = Device::generate(&mut rng);
|
||||
repo.compat_session()
|
||||
.add(&mut rng, &state.clock, &alice, device, None, false, None)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Bob has a finished compat session.
|
||||
let bob = repo
|
||||
.user()
|
||||
.add(&mut rng, &state.clock, "bob".to_owned())
|
||||
.await
|
||||
.unwrap();
|
||||
let device = Device::generate(&mut rng);
|
||||
let bob_session = repo
|
||||
.compat_session()
|
||||
.add(&mut rng, &state.clock, &bob, device, None, false, None)
|
||||
.await
|
||||
.unwrap();
|
||||
repo.compat_session()
|
||||
.finish(&state.clock, bob_session)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Carol has no compat session.
|
||||
repo.user()
|
||||
.add(&mut rng, &state.clock, "carol".to_owned())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
repo.save().await.unwrap();
|
||||
|
||||
// has-active-compat-session=true -> 1 (alice)
|
||||
let request =
|
||||
Request::get("/api/admin/v1/users?count=only&filter[has-active-compat-session]=true")
|
||||
.bearer(&token)
|
||||
.empty();
|
||||
let response = state.request(request).await;
|
||||
response.assert_status(StatusCode::OK);
|
||||
let body: serde_json::Value = response.json();
|
||||
assert_eq!(body["meta"]["count"], 1);
|
||||
|
||||
// has-active-compat-session=false -> 2 (bob + carol). The admin
|
||||
// token is created via client-credentials and does not provision a
|
||||
// user.
|
||||
let request =
|
||||
Request::get("/api/admin/v1/users?count=only&filter[has-active-compat-session]=false")
|
||||
.bearer(&token)
|
||||
.empty();
|
||||
let response = state.request(request).await;
|
||||
response.assert_status(StatusCode::OK);
|
||||
let body: serde_json::Value = response.json();
|
||||
assert_eq!(body["meta"]["count"], 2);
|
||||
}
|
||||
}
|
||||
@@ -841,7 +841,7 @@ async fn test_user_session(pool: PgPool) {
|
||||
|
||||
/// Test [`UserFilter::with_active_oauth2_session_for_any_of_clients`].
|
||||
///
|
||||
/// Sets up three users (A, B, C) with various OAuth2 session states across
|
||||
/// Sets up three users (A, B, C) with various `OAuth2` session states across
|
||||
/// three clients, and asserts that filtering on `[client_x, client_y]`
|
||||
/// returns users that have an ACTIVE session for *any* of those clients
|
||||
/// (OR semantics).
|
||||
|
||||
@@ -1966,6 +1966,19 @@
|
||||
"default": []
|
||||
},
|
||||
"style": "form"
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "filter[has-active-compat-session]",
|
||||
"description": "Retrieve users which have (or don't have) at least one active\n (non-finished) compatibility session.",
|
||||
"schema": {
|
||||
"description": "Retrieve users which have (or don't have) at least one active\n (non-finished) compatibility session.",
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"style": "form"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -6183,6 +6196,13 @@
|
||||
"$ref": "#/components/schemas/ULID"
|
||||
},
|
||||
"default": []
|
||||
},
|
||||
"filter[has-active-compat-session]": {
|
||||
"description": "Retrieve users which have (or don't have) at least one active\n (non-finished) compatibility session.",
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user