33// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
44// Please see LICENSE files in the repository root for full details.
55
6+ use std:: str:: FromStr as _;
7+
68use aide:: { OperationIo , transform:: TransformOperation } ;
7- use axum:: {
8- Json ,
9- extract:: { Query , rejection:: QueryRejection } ,
10- response:: IntoResponse ,
11- } ;
9+ use axum:: { Json , response:: IntoResponse } ;
10+ use axum_extra:: extract:: { Query , QueryRejection } ;
1211use axum_macros:: FromRequestParts ;
1312use chrono:: { DateTime , Utc } ;
1413use hyper:: StatusCode ;
1514use mas_axum_utils:: record_error;
1615use mas_storage:: personal:: PersonalSessionFilter ;
16+ use oauth2_types:: scope:: { Scope , ScopeToken } ;
1717use schemars:: JsonSchema ;
1818use serde:: Deserialize ;
1919use ulid:: Ulid ;
@@ -64,6 +64,10 @@ pub struct FilterParams {
6464 #[ schemars( with = "Option<crate::admin::schema::Ulid>" ) ]
6565 actor_user : Option < Ulid > ,
6666
67+ /// Retrieve the items with the given scope
68+ #[ serde( default , rename = "filter[scope]" ) ]
69+ scope : Vec < String > ,
70+
6771 /// Filter by session status
6872 #[ serde( rename = "filter[status]" ) ]
6973 status : Option < PersonalSessionStatus > ,
@@ -97,6 +101,10 @@ impl std::fmt::Display for FilterParams {
97101 write ! ( f, "{sep}filter[actor_user]={actor_user}" ) ?;
98102 sep = '&' ;
99103 }
104+ for scope in & self . scope {
105+ write ! ( f, "{sep}filter[scope]={scope}" ) ?;
106+ sep = '&' ;
107+ }
100108 if let Some ( status) = self . status {
101109 write ! ( f, "{sep}filter[status]={status}" ) ?;
102110 sep = '&' ;
@@ -141,6 +149,9 @@ pub enum RouteError {
141149
142150 #[ error( "Invalid filter parameters" ) ]
143151 InvalidFilter ( #[ from] QueryRejection ) ,
152+
153+ #[ error( "Invalid scope {0:?} in filter parameters" ) ]
154+ InvalidScope ( String ) ,
144155}
145156
146157impl_from_error_for_route ! ( mas_storage:: RepositoryError ) ;
@@ -153,7 +164,7 @@ impl IntoResponse for RouteError {
153164 let status = match self {
154165 Self :: Internal ( _) => StatusCode :: INTERNAL_SERVER_ERROR ,
155166 Self :: UserNotFound ( _) | Self :: ClientNotFound ( _) => StatusCode :: NOT_FOUND ,
156- Self :: InvalidFilter ( _) => StatusCode :: BAD_REQUEST ,
167+ Self :: InvalidScope ( _ ) | Self :: InvalidFilter ( _) => StatusCode :: BAD_REQUEST ,
157168 } ;
158169 ( status, sentry_event_id, Json ( error) ) . into_response ( )
159170 }
@@ -259,7 +270,18 @@ pub async fn handler(
259270 None => filter,
260271 } ;
261272
262- // Apply status filter
273+ let scope: Scope = params
274+ . scope
275+ . into_iter ( )
276+ . map ( |s| ScopeToken :: from_str ( & s) . map_err ( |_| RouteError :: InvalidScope ( s) ) )
277+ . collect :: < Result < _ , _ > > ( ) ?;
278+
279+ let filter = if scope. is_empty ( ) {
280+ filter
281+ } else {
282+ filter. with_scope ( & scope)
283+ } ;
284+
263285 let filter = match params. status {
264286 Some ( PersonalSessionStatus :: Active ) => filter. active_only ( ) ,
265287 Some ( PersonalSessionStatus :: Revoked ) => filter. finished_only ( ) ,
@@ -402,7 +424,7 @@ mod tests {
402424 PersonalSessionOwner :: from ( & user) ,
403425 & user,
404426 "Another test session" . to_owned ( ) ,
405- Scope :: from_iter ( [ OPENID ] ) ,
427+ Scope :: from_iter ( [ OPENID , "urn:mas:admin" . parse ( ) . unwrap ( ) ] ) ,
406428 )
407429 . await
408430 . unwrap ( ) ;
@@ -490,7 +512,7 @@ mod tests {
490512 "owner_client_id": null,
491513 "actor_user_id": "01FSHN9AG09FE39KETP6F390F8",
492514 "human_name": "Another test session",
493- "scope": "openid",
515+ "scope": "openid urn:mas:admin ",
494516 "last_active_at": null,
495517 "last_active_ip": null,
496518 "expires_at": "2022-02-01T14:40:00Z"
@@ -533,6 +555,10 @@ mod tests {
533555 & [ "01FSHN9AG0YQYAR04VCYTHJ8SK" , "01FSPT2RG08Y11Y5BM4VZ4CN8K" ] ,
534556 ) ,
535557 ( "filter[expires]=false" , & [ "01FSM7P1G0VBGAMK9D9QMGQ5MY" ] ) ,
558+ (
559+ "filter[scope]=urn:mas:admin" ,
560+ & [ "01FSPT2RG08Y11Y5BM4VZ4CN8K" ] ,
561+ ) ,
536562 ] ;
537563
538564 for ( filter, expected_ids) in filters_and_expected {
0 commit comments