Skip to content

Commit dab640a

Browse files
committed
Allow filtering sessions by client kind (dynamic or static)
1 parent 935400d commit dab640a

File tree

5 files changed

+119
-1
lines changed

5 files changed

+119
-1
lines changed

crates/handlers/src/admin/v1/oauth2_sessions/list.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,22 @@ impl std::fmt::Display for OAuth2SessionStatus {
4646
}
4747
}
4848

49+
#[derive(Deserialize, JsonSchema, Clone, Copy)]
50+
#[serde(rename_all = "snake_case")]
51+
enum OAuth2ClientKind {
52+
Dynamic,
53+
Static,
54+
}
55+
56+
impl std::fmt::Display for OAuth2ClientKind {
57+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58+
match self {
59+
Self::Dynamic => write!(f, "dynamic"),
60+
Self::Static => write!(f, "static"),
61+
}
62+
}
63+
}
64+
4965
#[derive(FromRequestParts, Deserialize, JsonSchema, OperationIo)]
5066
#[serde(rename = "OAuth2SessionFilter")]
5167
#[aide(input_with = "Query<FilterParams>")]
@@ -61,6 +77,10 @@ pub struct FilterParams {
6177
#[schemars(with = "Option<crate::admin::schema::Ulid>")]
6278
client: Option<Ulid>,
6379

80+
/// Retrieve the items only for a specific client kind
81+
#[serde(rename = "filter[client-kind]")]
82+
client_kind: Option<OAuth2ClientKind>,
83+
6484
/// Retrieve the items started from the given browser session
6585
#[serde(rename = "filter[user-session]")]
6686
#[schemars(with = "Option<crate::admin::schema::Ulid>")]
@@ -95,6 +115,11 @@ impl std::fmt::Display for FilterParams {
95115
sep = '&';
96116
}
97117

118+
if let Some(client_kind) = self.client_kind {
119+
write!(f, "{sep}filter[client-kind]={client_kind}")?;
120+
sep = '&';
121+
}
122+
98123
if let Some(user_session) = self.user_session {
99124
write!(f, "{sep}filter[user-session]={user_session}")?;
100125
sep = '&';
@@ -232,6 +257,12 @@ pub async fn handler(
232257
None => filter,
233258
};
234259

260+
let filter = match params.client_kind {
261+
Some(OAuth2ClientKind::Dynamic) => filter.only_dynamic_clients(),
262+
Some(OAuth2ClientKind::Static) => filter.only_static_clients(),
263+
None => filter,
264+
};
265+
235266
let user_session = if let Some(user_session_id) = params.user_session {
236267
let user_session = repo
237268
.browser_session()

crates/storage-pg/src/iden.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,15 @@ pub enum OAuth2Sessions {
8383
LastActiveIp,
8484
}
8585

86+
#[derive(sea_query::Iden)]
87+
#[iden = "oauth2_clients"]
88+
pub enum OAuth2Clients {
89+
Table,
90+
#[iden = "oauth2_client_id"]
91+
OAuth2ClientId,
92+
IsStatic,
93+
}
94+
8695
#[derive(sea_query::Iden)]
8796
#[iden = "upstream_oauth_providers"]
8897
pub enum UpstreamOAuthProviders {

crates/storage-pg/src/oauth2/session.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use uuid::Uuid;
2323

2424
use crate::{
2525
filter::{Filter, StatementExt},
26-
iden::OAuth2Sessions,
26+
iden::{OAuth2Clients, OAuth2Sessions},
2727
pagination::QueryBuilderExt,
2828
tracing::ExecuteExt,
2929
DatabaseError, DatabaseInconsistencyError,
@@ -104,6 +104,26 @@ impl Filter for OAuth2SessionFilter<'_> {
104104
Expr::col((OAuth2Sessions::Table, OAuth2Sessions::OAuth2ClientId))
105105
.eq(Uuid::from(client.id))
106106
}))
107+
.add_option(self.client_kind().map(|client_kind| {
108+
// This builds either a:
109+
// `WHERE oauth2_client_id = ANY(...)`
110+
// or a `WHERE oauth2_client_id <> ALL(...)`
111+
let static_clients = Query::select()
112+
.expr(Expr::col((
113+
OAuth2Clients::Table,
114+
OAuth2Clients::OAuth2ClientId,
115+
)))
116+
.and_where(Expr::col((OAuth2Clients::Table, OAuth2Clients::IsStatic)).into())
117+
.from(OAuth2Clients::Table)
118+
.take();
119+
if client_kind.is_static() {
120+
Expr::col((OAuth2Sessions::Table, OAuth2Sessions::OAuth2ClientId))
121+
.eq(Expr::any(static_clients))
122+
} else {
123+
Expr::col((OAuth2Sessions::Table, OAuth2Sessions::OAuth2ClientId))
124+
.ne(Expr::all(static_clients))
125+
}
126+
}))
107127
.add_option(self.device().map(|device| {
108128
Expr::val(device.to_scope_token().to_string()).eq(PgFunc::any(Expr::col((
109129
OAuth2Sessions::Table,

crates/storage/src/oauth2/session.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,18 @@ impl OAuth2SessionState {
3131
}
3232
}
3333

34+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
35+
pub enum ClientKind {
36+
Static,
37+
Dynamic,
38+
}
39+
40+
impl ClientKind {
41+
pub fn is_static(self) -> bool {
42+
matches!(self, Self::Static)
43+
}
44+
}
45+
3446
/// Filter parameters for listing OAuth 2.0 sessions
3547
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
3648
pub struct OAuth2SessionFilter<'a> {
@@ -39,6 +51,7 @@ pub struct OAuth2SessionFilter<'a> {
3951
browser_session: Option<&'a BrowserSession>,
4052
device: Option<&'a Device>,
4153
client: Option<&'a Client>,
54+
client_kind: Option<ClientKind>,
4255
state: Option<OAuth2SessionState>,
4356
scope: Option<&'a Scope>,
4457
last_active_before: Option<DateTime<Utc>>,
@@ -119,6 +132,28 @@ impl<'a> OAuth2SessionFilter<'a> {
119132
self.client
120133
}
121134

135+
/// List only static clients
136+
#[must_use]
137+
pub fn only_static_clients(mut self) -> Self {
138+
self.client_kind = Some(ClientKind::Static);
139+
self
140+
}
141+
142+
/// List only dynamic clients
143+
#[must_use]
144+
pub fn only_dynamic_clients(mut self) -> Self {
145+
self.client_kind = Some(ClientKind::Dynamic);
146+
self
147+
}
148+
149+
/// Get the client kind filter
150+
///
151+
/// Returns [`None`] if no client kind filter was set
152+
#[must_use]
153+
pub fn client_kind(&self) -> Option<ClientKind> {
154+
self.client_kind
155+
}
156+
122157
/// Only return sessions with a last active time before the given time
123158
#[must_use]
124159
pub fn with_last_active_before(mut self, last_active_before: DateTime<Utc>) -> Self {

docs/api/spec.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,17 @@
357357
},
358358
"style": "form"
359359
},
360+
{
361+
"in": "query",
362+
"name": "filter[client-kind]",
363+
"description": "Retrieve the items only for a specific client kind",
364+
"schema": {
365+
"description": "Retrieve the items only for a specific client kind",
366+
"$ref": "#/components/schemas/OAuth2ClientKind",
367+
"nullable": true
368+
},
369+
"style": "form"
370+
},
360371
{
361372
"in": "query",
362373
"name": "filter[user-session]",
@@ -2347,6 +2358,11 @@
23472358
"$ref": "#/components/schemas/ULID",
23482359
"nullable": true
23492360
},
2361+
"filter[client-kind]": {
2362+
"description": "Retrieve the items only for a specific client kind",
2363+
"$ref": "#/components/schemas/OAuth2ClientKind",
2364+
"nullable": true
2365+
},
23502366
"filter[user-session]": {
23512367
"description": "Retrieve the items started from the given browser session",
23522368
"$ref": "#/components/schemas/ULID",
@@ -2367,6 +2383,13 @@
23672383
}
23682384
}
23692385
},
2386+
"OAuth2ClientKind": {
2387+
"type": "string",
2388+
"enum": [
2389+
"dynamic",
2390+
"static"
2391+
]
2392+
},
23702393
"OAuth2SessionStatus": {
23712394
"type": "string",
23722395
"enum": [

0 commit comments

Comments
 (0)