Coverage for gws-app/gws/plugin/auth_session_manager/sqlite/__init__.py: 97%

65 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-08-24 12:46 +0200

1from typing import Optional 

2 

3import gws 

4import gws.base.auth 

5import gws.lib.datetimex as dtx 

6import gws.lib.jsonx 

7import gws.lib.sqlitex 

8 

9gws.ext.new.authSessionManager('sqlite') 

10 

11_CLEANUP_INTERVAL = 600 

12_TOUCH_INTERVAL = 60 

13 

14 

15class Config(gws.base.auth.session_manager.Config): 

16 """Configuration for sqlite sessions""" 

17 

18 path: Optional[str] 

19 """Session storage path.""" 

20 

21 

22class Object(gws.base.auth.session_manager.Object): 

23 dbPath: str 

24 table = 'sessions' 

25 

26 def configure(self): 

27 ver = self.root.specs.version.rpartition('.')[0] 

28 self.dbPath = self.cfg('path', default=f'{gws.c.MISC_DIR}/sessions.{ver}.sqlite') 

29 

30 ## 

31 

32 _cleanupTime = 0 

33 

34 def cleanup(self): 

35 self._db().execute( 

36 f'DELETE FROM {self.table} WHERE updated < :last_time OR created < :max_time', 

37 last_time=gws.u.stime() - self.lifeTime, 

38 max_time=(gws.u.stime() - self.maxLifeTime) if self.maxLifeTime > 0 else 0, 

39 ) 

40 self._cleanupTime = gws.u.stime() 

41 

42 def create(self, method, user, data=None): 

43 if gws.u.stime() > self._cleanupTime + _CLEANUP_INTERVAL: 

44 self.cleanup() 

45 

46 am = self.root.app.authMgr 

47 uid = gws.u.random_string(64) 

48 

49 self._db().insert(self.table, dict( 

50 uid=uid, 

51 method_uid=method.uid, 

52 user_uid=user.uid, 

53 str_user=am.serialize_user(user), 

54 str_data=gws.lib.jsonx.to_string(data or {}), 

55 created=gws.u.stime(), 

56 updated=gws.u.stime(), 

57 )) 

58 

59 return self.get(uid) 

60 

61 def delete(self, sess): 

62 self._db().execute(f'DELETE FROM {self.table} WHERE uid=:uid', uid=sess.uid) 

63 

64 def delete_all(self): 

65 self._db().execute(f'DELETE FROM {self.table}') 

66 

67 def get(self, uid): 

68 rs = self._db().select( 

69 f'SELECT * FROM {self.table} WHERE uid=:uid AND updated >= :last_time AND created >= :max_time', 

70 uid=uid, 

71 last_time=gws.u.stime() - self.lifeTime, 

72 max_time=(gws.u.stime() - self.maxLifeTime) if self.maxLifeTime > 0 else 0, 

73 ) 

74 if len(rs) == 1: 

75 return self._session(rs[0]) 

76 

77 def list_all(self): 

78 return [ 

79 self._session(rec) 

80 for rec in self._db().select(f'SELECT * FROM {self.table}') 

81 ] 

82 

83 def save(self, sess): 

84 if not sess.isChanged: 

85 return 

86 

87 self._db().execute( 

88 f'UPDATE {self.table} SET str_data=:str_data, updated=:updated WHERE uid=:uid', 

89 str_data=gws.lib.jsonx.to_string(sess.data or {}), 

90 updated=gws.u.stime(), 

91 uid=sess.uid 

92 ) 

93 

94 sess.isChanged = False 

95 

96 def touch(self, sess): 

97 if sess.isChanged: 

98 return self.save(sess) 

99 

100 # throttle updates to avoid excessive writes on high-traffic sessions 

101 if gws.u.stime() - dtx.to_timestamp(sess.updated) < _TOUCH_INTERVAL: 

102 return 

103 

104 self._db().execute( 

105 f'UPDATE {self.table} SET updated=:updated WHERE uid=:uid', 

106 updated=gws.u.stime(), 

107 uid=sess.uid 

108 ) 

109 

110 ## 

111 

112 def _session(self, rec): 

113 am = self.root.app.authMgr 

114 r = gws.u.to_dict(rec) 

115 usr = am.unserialize_user(r['str_user']) 

116 if not usr: 

117 gws.log.error(f'invalid user in session {r["uid"]!r}') 

118 self._db().execute(f'DELETE FROM {self.table} WHERE uid=:uid', uid=r['uid']) 

119 usr = am.guestUser 

120 return gws.base.auth.session.Object( 

121 uid=r['uid'], 

122 method=am.get_method(r['method_uid']), 

123 user=usr, 

124 data=gws.lib.jsonx.from_string(r['str_data']), 

125 created=dtx.from_timestamp(r['created']), 

126 updated=dtx.from_timestamp(r['updated']), 

127 ) 

128 

129 ## 

130 

131 _sqlitex: gws.lib.sqlitex.Object 

132 

133 def _db(self): 

134 if getattr(self, '_sqlitex', None) is None: 

135 ddl = f''' 

136 CREATE TABLE IF NOT EXISTS {self.table} ( 

137 uid TEXT NOT NULL PRIMARY KEY, 

138 method_uid TEXT NOT NULL, 

139 user_uid TEXT NOT NULL, 

140 str_user TEXT NOT NULL, 

141 str_data TEXT NOT NULL, 

142 created INTEGER NOT NULL, 

143 updated INTEGER NOT NULL 

144 ) 

145 ''' 

146 self._sqlitex = gws.lib.sqlitex.Object(self.dbPath, ddl) 

147 return self._sqlitex