mirror of
https://github.com/element-hq/matrix-authentication-service.git
synced 2026-07-02 20:01:38 +00:00
admin-api: filter user list by presence of active OAuth2 session
This commit is contained in:
@@ -86,6 +86,11 @@ pub struct FilterParams {
|
||||
#[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) OAuth 2.0 session, regardless of the client.
|
||||
#[serde(rename = "filter[has-active-oauth2-session]")]
|
||||
has_active_oauth2_session: Option<bool>,
|
||||
|
||||
/// Retrieve users which have (or don't have) at least one active
|
||||
/// (non-finished) compatibility session.
|
||||
#[serde(rename = "filter[has-active-compat-session]")]
|
||||
@@ -116,6 +121,10 @@ impl std::fmt::Display for FilterParams {
|
||||
write!(f, "{sep}filter[active-oauth2-client]={client}")?;
|
||||
sep = '&';
|
||||
}
|
||||
if let Some(has) = self.has_active_oauth2_session {
|
||||
write!(f, "{sep}filter[has-active-oauth2-session]={has}")?;
|
||||
sep = '&';
|
||||
}
|
||||
if let Some(has) = self.has_active_compat_session {
|
||||
write!(f, "{sep}filter[has-active-compat-session]={has}")?;
|
||||
sep = '&';
|
||||
@@ -237,6 +246,11 @@ pub async fn handler(
|
||||
filter.with_active_oauth2_session_for_any_of_clients(¶ms.active_oauth2_client)
|
||||
};
|
||||
|
||||
let filter = match params.has_active_oauth2_session {
|
||||
Some(has) => filter.with_active_oauth2_session(has),
|
||||
None => filter,
|
||||
};
|
||||
|
||||
let filter = match params.has_active_compat_session {
|
||||
Some(has) => filter.with_active_compat_session(has),
|
||||
None => filter,
|
||||
@@ -699,4 +713,119 @@ mod tests {
|
||||
let body: serde_json::Value = response.json();
|
||||
assert_eq!(body["meta"]["count"], 2);
|
||||
}
|
||||
|
||||
/// Test the `filter[has-active-oauth2-session]` filter in both directions.
|
||||
#[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")]
|
||||
async fn test_list_users_filter_has_active_oauth2_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();
|
||||
|
||||
let client = repo
|
||||
.oauth2_client()
|
||||
.add(
|
||||
&mut rng,
|
||||
&state.clock,
|
||||
vec!["https://example.com/redirect".parse().unwrap()],
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
vec![GrantType::AuthorizationCode],
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Alice has an active OAuth2 session.
|
||||
let alice = repo
|
||||
.user()
|
||||
.add(&mut rng, &state.clock, "alice".to_owned())
|
||||
.await
|
||||
.unwrap();
|
||||
let alice_session = repo
|
||||
.browser_session()
|
||||
.add(&mut rng, &state.clock, &alice, None)
|
||||
.await
|
||||
.unwrap();
|
||||
repo.oauth2_session()
|
||||
.add_from_browser_session(
|
||||
&mut rng,
|
||||
&state.clock,
|
||||
&client,
|
||||
&alice_session,
|
||||
Scope::from_iter([OPENID]),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Bob has a finished OAuth2 session.
|
||||
let bob = repo
|
||||
.user()
|
||||
.add(&mut rng, &state.clock, "bob".to_owned())
|
||||
.await
|
||||
.unwrap();
|
||||
let bob_session = repo
|
||||
.browser_session()
|
||||
.add(&mut rng, &state.clock, &bob, None)
|
||||
.await
|
||||
.unwrap();
|
||||
let bob_oauth2 = repo
|
||||
.oauth2_session()
|
||||
.add_from_browser_session(
|
||||
&mut rng,
|
||||
&state.clock,
|
||||
&client,
|
||||
&bob_session,
|
||||
Scope::from_iter([OPENID]),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
repo.oauth2_session()
|
||||
.finish(&state.clock, bob_oauth2)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Carol has no OAuth2 session.
|
||||
repo.user()
|
||||
.add(&mut rng, &state.clock, "carol".to_owned())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
repo.save().await.unwrap();
|
||||
|
||||
// has-active-oauth2-session=true -> 1 (alice)
|
||||
let request =
|
||||
Request::get("/api/admin/v1/users?count=only&filter[has-active-oauth2-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-oauth2-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-oauth2-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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1967,6 +1967,19 @@
|
||||
},
|
||||
"style": "form"
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "filter[has-active-oauth2-session]",
|
||||
"description": "Retrieve users which have (or don't have) at least one active\n (non-finished) OAuth 2.0 session, regardless of the client.",
|
||||
"schema": {
|
||||
"description": "Retrieve users which have (or don't have) at least one active\n (non-finished) OAuth 2.0 session, regardless of the client.",
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"style": "form"
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "filter[has-active-compat-session]",
|
||||
@@ -6197,6 +6210,13 @@
|
||||
},
|
||||
"default": []
|
||||
},
|
||||
"filter[has-active-oauth2-session]": {
|
||||
"description": "Retrieve users which have (or don't have) at least one active\n (non-finished) OAuth 2.0 session, regardless of the client.",
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"filter[has-active-compat-session]": {
|
||||
"description": "Retrieve users which have (or don't have) at least one active\n (non-finished) compatibility session.",
|
||||
"type": [
|
||||
|
||||
Reference in New Issue
Block a user