@@ -9,7 +9,10 @@ use async_trait::async_trait;
99use chrono:: { DateTime , Utc } ;
1010use mas_data_model:: {
1111 Clock , User ,
12- personal:: session:: { PersonalSession , PersonalSessionOwner , SessionState } ,
12+ personal:: {
13+ PersonalAccessToken ,
14+ session:: { PersonalSession , PersonalSessionOwner , SessionState } ,
15+ } ,
1316} ;
1417use mas_storage:: {
1518 Page , Pagination ,
@@ -19,7 +22,7 @@ use mas_storage::{
1922use oauth2_types:: scope:: Scope ;
2023use rand:: RngCore ;
2124use sea_query:: {
22- Condition , Expr , PgFunc , PostgresQueryBuilder , Query , SimpleExpr , enum_def,
25+ Cond , Condition , Expr , PgFunc , PostgresQueryBuilder , Query , SimpleExpr , enum_def,
2326 extension:: postgres:: PgExpr as _,
2427} ;
2528use sea_query_binder:: SqlxBinder as _;
@@ -31,7 +34,7 @@ use crate::{
3134 DatabaseError ,
3235 errors:: DatabaseInconsistencyError ,
3336 filter:: { Filter , StatementExt as _} ,
34- iden:: PersonalSessions ,
37+ iden:: { PersonalAccessTokens , PersonalSessions } ,
3538 pagination:: QueryBuilderExt as _,
3639 tracing:: ExecuteExt as _,
3740} ;
@@ -116,6 +119,73 @@ impl TryFrom<PersonalSessionLookup> for PersonalSession {
116119 }
117120}
118121
122+ #[ derive( sqlx:: FromRow ) ]
123+ #[ enum_def]
124+ struct PersonalSessionAndAccessTokenLookup {
125+ personal_session_id : Uuid ,
126+ owner_user_id : Option < Uuid > ,
127+ owner_oauth2_client_id : Option < Uuid > ,
128+ actor_user_id : Uuid ,
129+ human_name : String ,
130+ scope_list : Vec < String > ,
131+ created_at : DateTime < Utc > ,
132+ revoked_at : Option < DateTime < Utc > > ,
133+ last_active_at : Option < DateTime < Utc > > ,
134+ last_active_ip : Option < IpAddr > ,
135+
136+ // tokens
137+ personal_access_token_id : Option < Uuid > ,
138+ token_created_at : Option < DateTime < Utc > > ,
139+ token_expires_at : Option < DateTime < Utc > > ,
140+ }
141+
142+ impl Node < Ulid > for PersonalSessionAndAccessTokenLookup {
143+ fn cursor ( & self ) -> Ulid {
144+ self . personal_session_id . into ( )
145+ }
146+ }
147+
148+ impl TryFrom < PersonalSessionAndAccessTokenLookup >
149+ for ( PersonalSession , Option < PersonalAccessToken > )
150+ {
151+ type Error = DatabaseInconsistencyError ;
152+
153+ fn try_from ( value : PersonalSessionAndAccessTokenLookup ) -> Result < Self , Self :: Error > {
154+ let session = PersonalSession :: try_from ( PersonalSessionLookup {
155+ personal_session_id : value. personal_session_id ,
156+ owner_user_id : value. owner_user_id ,
157+ owner_oauth2_client_id : value. owner_oauth2_client_id ,
158+ actor_user_id : value. actor_user_id ,
159+ human_name : value. human_name ,
160+ scope_list : value. scope_list ,
161+ created_at : value. created_at ,
162+ revoked_at : value. revoked_at ,
163+ last_active_at : value. last_active_at ,
164+ last_active_ip : value. last_active_ip ,
165+ } ) ?;
166+
167+ let token_opt = if let Some ( id) = value. personal_access_token_id {
168+ let id = Ulid :: from ( id) ;
169+ Some ( PersonalAccessToken {
170+ id,
171+ session_id : session. id ,
172+ // should not be possible
173+ created_at : value. token_created_at . ok_or (
174+ DatabaseInconsistencyError :: on ( "personal_sessions" )
175+ . column ( "created_at" )
176+ . row ( id) ,
177+ ) ?,
178+ expires_at : value. token_expires_at ,
179+ revoked_at : None ,
180+ } )
181+ } else {
182+ None
183+ } ;
184+
185+ Ok ( ( session, token_opt) )
186+ }
187+ }
188+
119189#[ async_trait]
120190impl PersonalSessionRepository for PgPersonalSessionRepository < ' _ > {
121191 type Error = DatabaseError ;
@@ -274,60 +344,90 @@ impl PersonalSessionRepository for PgPersonalSessionRepository<'_> {
274344 & mut self ,
275345 filter : PersonalSessionFilter < ' _ > ,
276346 pagination : Pagination ,
277- ) -> Result < Page < PersonalSession > , Self :: Error > {
347+ ) -> Result < Page < ( PersonalSession , Option < PersonalAccessToken > ) > , Self :: Error > {
278348 let ( sql, arguments) = Query :: select ( )
279349 . expr_as (
280350 Expr :: col ( ( PersonalSessions :: Table , PersonalSessions :: PersonalSessionId ) ) ,
281- PersonalSessionLookupIden :: PersonalSessionId ,
351+ PersonalSessionAndAccessTokenLookupIden :: PersonalSessionId ,
282352 )
283353 . expr_as (
284354 Expr :: col ( ( PersonalSessions :: Table , PersonalSessions :: OwnerUserId ) ) ,
285- PersonalSessionLookupIden :: OwnerUserId ,
355+ PersonalSessionAndAccessTokenLookupIden :: OwnerUserId ,
286356 )
287357 . expr_as (
288358 Expr :: col ( (
289359 PersonalSessions :: Table ,
290360 PersonalSessions :: OwnerOAuth2ClientId ,
291361 ) ) ,
292- PersonalSessionLookupIden :: OwnerOauth2ClientId ,
362+ PersonalSessionAndAccessTokenLookupIden :: OwnerOauth2ClientId ,
293363 )
294364 . expr_as (
295365 Expr :: col ( ( PersonalSessions :: Table , PersonalSessions :: ActorUserId ) ) ,
296- PersonalSessionLookupIden :: ActorUserId ,
366+ PersonalSessionAndAccessTokenLookupIden :: ActorUserId ,
297367 )
298368 . expr_as (
299369 Expr :: col ( ( PersonalSessions :: Table , PersonalSessions :: HumanName ) ) ,
300- PersonalSessionLookupIden :: HumanName ,
370+ PersonalSessionAndAccessTokenLookupIden :: HumanName ,
301371 )
302372 . expr_as (
303373 Expr :: col ( ( PersonalSessions :: Table , PersonalSessions :: ScopeList ) ) ,
304- PersonalSessionLookupIden :: ScopeList ,
374+ PersonalSessionAndAccessTokenLookupIden :: ScopeList ,
305375 )
306376 . expr_as (
307377 Expr :: col ( ( PersonalSessions :: Table , PersonalSessions :: CreatedAt ) ) ,
308- PersonalSessionLookupIden :: CreatedAt ,
378+ PersonalSessionAndAccessTokenLookupIden :: CreatedAt ,
309379 )
310380 . expr_as (
311381 Expr :: col ( ( PersonalSessions :: Table , PersonalSessions :: RevokedAt ) ) ,
312- PersonalSessionLookupIden :: RevokedAt ,
382+ PersonalSessionAndAccessTokenLookupIden :: RevokedAt ,
313383 )
314384 . expr_as (
315385 Expr :: col ( ( PersonalSessions :: Table , PersonalSessions :: LastActiveAt ) ) ,
316- PersonalSessionLookupIden :: LastActiveAt ,
386+ PersonalSessionAndAccessTokenLookupIden :: LastActiveAt ,
317387 )
318388 . expr_as (
319389 Expr :: col ( ( PersonalSessions :: Table , PersonalSessions :: LastActiveIp ) ) ,
320- PersonalSessionLookupIden :: LastActiveIp ,
390+ PersonalSessionAndAccessTokenLookupIden :: LastActiveIp ,
391+ )
392+ . expr_as (
393+ Expr :: col ( (
394+ PersonalAccessTokens :: Table ,
395+ PersonalAccessTokens :: PersonalAccessTokenId ,
396+ ) ) ,
397+ PersonalSessionAndAccessTokenLookupIden :: PersonalAccessTokenId ,
398+ )
399+ . expr_as (
400+ Expr :: col ( ( PersonalAccessTokens :: Table , PersonalAccessTokens :: CreatedAt ) ) ,
401+ PersonalSessionAndAccessTokenLookupIden :: TokenCreatedAt ,
402+ )
403+ . expr_as (
404+ Expr :: col ( ( PersonalAccessTokens :: Table , PersonalAccessTokens :: ExpiresAt ) ) ,
405+ PersonalSessionAndAccessTokenLookupIden :: TokenExpiresAt ,
321406 )
322407 . from ( PersonalSessions :: Table )
408+ . left_join (
409+ PersonalAccessTokens :: Table ,
410+ Cond :: all ( )
411+ . add (
412+ Expr :: col ( ( PersonalSessions :: Table , PersonalSessions :: PersonalSessionId ) )
413+ . eq ( Expr :: col ( (
414+ PersonalAccessTokens :: Table ,
415+ PersonalAccessTokens :: PersonalSessionId ,
416+ ) ) ) ,
417+ )
418+ . add (
419+ Expr :: col ( ( PersonalAccessTokens :: Table , PersonalAccessTokens :: RevokedAt ) )
420+ . is_null ( ) ,
421+ ) ,
422+ )
323423 . apply_filter ( filter)
324424 . generate_pagination (
325425 ( PersonalSessions :: Table , PersonalSessions :: PersonalSessionId ) ,
326426 pagination,
327427 )
328428 . build_sqlx ( PostgresQueryBuilder ) ;
329429
330- let edges: Vec < PersonalSessionLookup > = sqlx:: query_as_with ( & sql, arguments)
430+ let edges: Vec < PersonalSessionAndAccessTokenLookup > = sqlx:: query_as_with ( & sql, arguments)
331431 . traced ( )
332432 . fetch_all ( & mut * self . conn )
333433 . await ?;
@@ -349,6 +449,21 @@ impl PersonalSessionRepository for PgPersonalSessionRepository<'_> {
349449 let ( sql, arguments) = Query :: select ( )
350450 . expr ( Expr :: col ( ( PersonalSessions :: Table , PersonalSessions :: PersonalSessionId ) ) . count ( ) )
351451 . from ( PersonalSessions :: Table )
452+ . left_join (
453+ PersonalAccessTokens :: Table ,
454+ Cond :: all ( )
455+ . add (
456+ Expr :: col ( ( PersonalSessions :: Table , PersonalSessions :: PersonalSessionId ) )
457+ . eq ( Expr :: col ( (
458+ PersonalAccessTokens :: Table ,
459+ PersonalAccessTokens :: PersonalSessionId ,
460+ ) ) ) ,
461+ )
462+ . add (
463+ Expr :: col ( ( PersonalAccessTokens :: Table , PersonalAccessTokens :: RevokedAt ) )
464+ . is_null ( ) ,
465+ ) ,
466+ )
352467 . apply_filter ( filter)
353468 . build_sqlx ( PostgresQueryBuilder ) ;
354469
@@ -419,5 +534,13 @@ impl Filter for PersonalSessionFilter<'_> {
419534 Expr :: col ( ( PersonalSessions :: Table , PersonalSessions :: LastActiveAt ) )
420535 . gt ( last_active_after)
421536 } ) )
537+ . add_option ( self . expires_before ( ) . map ( |expires_before| {
538+ Expr :: col ( ( PersonalAccessTokens :: Table , PersonalAccessTokens :: ExpiresAt ) )
539+ . lt ( expires_before)
540+ } ) )
541+ . add_option ( self . expires_after ( ) . map ( |expires_after| {
542+ Expr :: col ( ( PersonalAccessTokens :: Table , PersonalAccessTokens :: ExpiresAt ) )
543+ . gt ( expires_after)
544+ } ) )
422545 }
423546}
0 commit comments