@@ -428,7 +428,8 @@ class AccountBackend {
428
428
// try to update old session first
429
429
if (oldSession != null ) {
430
430
final rs = await withRetryTransaction (_db, (tx) async {
431
- final session = await tx.lookupOrNull <UserSession >(oldSession.key);
431
+ final session =
432
+ await tx.userSessions.lookupOrNull (oldSession.sessionId);
432
433
if (session == null ) {
433
434
return null ;
434
435
}
@@ -462,8 +463,7 @@ class AccountBackend {
462
463
throw AuthenticationException .failed ();
463
464
}
464
465
final data = await withRetryTransaction (_db, (tx) async {
465
- final session = await tx.lookupOrNull <UserSession >(
466
- _db.emptyKey.append (UserSession , id: sessionId));
466
+ final session = await tx.userSessions.lookupOrNull (sessionId);
467
467
if (session == null || session.isExpired ()) {
468
468
throw AuthenticationException .failed ('Session has been expired.' );
469
469
}
@@ -570,36 +570,24 @@ class AccountBackend {
570
570
/// Deletes the session entry if it has already expired and
571
571
/// clears the related cache too.
572
572
Future <UserSession ?> lookupValidUserSession (String sessionId) async {
573
- final key = _db.emptyKey.append (UserSession , id: sessionId);
574
- final session = await _db.lookupOrNull <UserSession >(key);
573
+ final session = await _db.userSessions.lookupOrNull (sessionId);
575
574
if (session == null ) {
576
575
return null ;
577
576
}
578
577
if (session.isExpired ()) {
579
- await _db.commit (deletes: [key]);
580
- await cache.userSessionData (sessionId).purge ();
578
+ await _db.userSessions.expire (session.sessionId);
581
579
return null ;
582
580
}
583
581
return session;
584
582
}
585
583
586
- /// Removes the session data from the Datastore and from cache.
587
- Future <void > invalidateUserSession (String sessionId) async {
588
- final key = _db.emptyKey.append (UserSession , id: sessionId);
589
- try {
590
- await _db.commit (deletes: [key]);
591
- } catch (_) {
592
- // ignore if the entity has been already deleted concurrently
584
+ /// Deletes sessions associated with a [userId] or [sessionId] .
585
+ Future <void > deleteUserSessions ({String ? userId, String ? sessionId}) async {
586
+ if (sessionId != null ) {
587
+ await _db.userSessions.expire (sessionId);
593
588
}
594
- await cache.userSessionData (sessionId).purge ();
595
- }
596
-
597
- /// Scans Datastore for all sessions the user has, and invalidates
598
- /// them all (by deleting the Datastore entry and purging the cache).
599
- Future <void > invalidateAllUserSessions (String userId) async {
600
- final query = _db.query <UserSession >()..filter ('userId =' , userId);
601
- await for (final session in query.run ()) {
602
- await invalidateUserSession (session.sessionId);
589
+ if (userId != null ) {
590
+ await _db.userSessions.expireAllForUserId (userId);
603
591
}
604
592
}
605
593
@@ -608,9 +596,8 @@ class AccountBackend {
608
596
final now = clock.now ().toUtc ();
609
597
// account for possible clock skew
610
598
final ts = now.subtract (Duration (minutes: 15 ));
611
- final query = _db.query <UserSession >()..filter ('expires <' , ts);
612
- final count = await _db.deleteWithQuery (query);
613
- _logger.info ('Deleted ${count .deleted } UserSession entries.' );
599
+ final count = await _db.userSessions.expireAllBeforeTimestamp (ts);
600
+ _logger.info ('Deleted $count UserSession entries.' );
614
601
}
615
602
616
603
/// Updates the moderated status of a user.
@@ -644,7 +631,7 @@ class AccountBackend {
644
631
tx.insert (mc);
645
632
}
646
633
});
647
- await _expireAllSessions (userId);
634
+ await _db.userSessions. expireAllForUserId (userId);
648
635
await purgeAccountCache (userId: userId);
649
636
}
650
637
@@ -661,16 +648,6 @@ class AccountBackend {
661
648
}
662
649
return query.run ();
663
650
}
664
-
665
- // expire all sessions of a given user from datastore and cache
666
- Future <void > _expireAllSessions (String userId) async {
667
- final query = _db.query <UserSession >()..filter ('userId =' , userId);
668
- final sessionsToDelete = await query.run ().toList ();
669
- for (final session in sessionsToDelete) {
670
- await _db.commit (deletes: [session.key]);
671
- await cache.userSessionData (session.sessionId).purge ();
672
- }
673
- }
674
651
}
675
652
676
653
/// Purge [cache] entries for given [userId] .
@@ -682,3 +659,63 @@ Future<void> purgeAccountCache({
682
659
cache.publisherPage (userId).purgeAndRepeat (),
683
660
]);
684
661
}
662
+
663
+ /// Low-level, narrowly typed data access methods for [UserSession] entity.
664
+ extension UserSessionDatastoreDBExt on DatastoreDB {
665
+ _UserSessionDataAccess get userSessions => _UserSessionDataAccess (this );
666
+ }
667
+
668
+ extension UserSessionTransactionWrapperExt on TransactionWrapper {
669
+ _UserSessionTransactionDataAcccess get userSessions =>
670
+ _UserSessionTransactionDataAcccess (this );
671
+ }
672
+
673
+ class _UserSessionDataAccess {
674
+ final DatastoreDB _db;
675
+
676
+ _UserSessionDataAccess (this ._db);
677
+
678
+ Future <UserSession ?> lookupOrNull (String sessionId) async {
679
+ final key = _db.emptyKey.append (UserSession , id: sessionId);
680
+ return await _db.lookupOrNull <UserSession >(key);
681
+ }
682
+
683
+ /// Scans Datastore for all sessions the user has, and invalidates
684
+ /// them all (by deleting the Datastore entry and purging the cache).
685
+ Future <void > expireAllForUserId (String userId) async {
686
+ final query = _db.query <UserSession >()..filter ('userId =' , userId);
687
+ final sessionsToDelete = await query.run ().toList ();
688
+ for (final session in sessionsToDelete) {
689
+ await expire (session.sessionId);
690
+ }
691
+ }
692
+
693
+ /// Removes the session data from the Datastore and from cache.
694
+ Future <void > expire (String sessionId) async {
695
+ final key = _db.emptyKey.append (UserSession , id: sessionId);
696
+ try {
697
+ await _db.commit (deletes: [key]);
698
+ } on Exception catch (_) {
699
+ // ignore if the entity has been already deleted concurrently
700
+ }
701
+ await cache.userSessionData (sessionId).purge ();
702
+ }
703
+
704
+ /// Removes the session data that has expiry before [ts] .
705
+ Future <int > expireAllBeforeTimestamp (DateTime ts) async {
706
+ final query = _db.query <UserSession >()..filter ('expires <' , ts);
707
+ final count = await _db.deleteWithQuery (query);
708
+ return count.deleted;
709
+ }
710
+ }
711
+
712
+ class _UserSessionTransactionDataAcccess {
713
+ final TransactionWrapper _tx;
714
+
715
+ _UserSessionTransactionDataAcccess (this ._tx);
716
+
717
+ Future <UserSession ?> lookupOrNull (String sessionId) async {
718
+ final key = _tx.emptyKey.append (UserSession , id: sessionId);
719
+ return await _tx.lookupOrNull <UserSession >(key);
720
+ }
721
+ }
0 commit comments