Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/django/contrib/sessions/backends/base.py: 42%

180 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1import logging 

2import string 

3from datetime import datetime, timedelta 

4 

5from django.conf import settings 

6from django.core import signing 

7from django.utils import timezone 

8from django.utils.crypto import get_random_string 

9from django.utils.module_loading import import_string 

10 

11# session_key should not be case sensitive because some backends can store it 

12# on case insensitive file systems. 

13VALID_KEY_CHARS = string.ascii_lowercase + string.digits 

14 

15 

16class CreateError(Exception): 

17 """ 

18 Used internally as a consistent exception type to catch from save (see the 

19 docstring for SessionBase.save() for details). 

20 """ 

21 

22 pass 

23 

24 

25class UpdateError(Exception): 

26 """ 

27 Occurs if Django tries to update a session that was deleted. 

28 """ 

29 

30 pass 

31 

32 

33class SessionBase: 

34 """ 

35 Base class for all Session classes. 

36 """ 

37 

38 TEST_COOKIE_NAME = "testcookie" 

39 TEST_COOKIE_VALUE = "worked" 

40 

41 __not_given = object() 

42 

43 def __init__(self, session_key=None): 

44 self._session_key = session_key 

45 self.accessed = False 

46 self.modified = False 

47 self.serializer = import_string(settings.SESSION_SERIALIZER) 

48 

49 def __contains__(self, key): 

50 return key in self._session 

51 

52 def __getitem__(self, key): 

53 return self._session[key] 

54 

55 def __setitem__(self, key, value): 

56 self._session[key] = value 

57 self.modified = True 

58 

59 def __delitem__(self, key): 

60 del self._session[key] 

61 self.modified = True 

62 

63 @property 

64 def key_salt(self): 

65 return "django.contrib.sessions." + self.__class__.__qualname__ 

66 

67 def get(self, key, default=None): 

68 return self._session.get(key, default) 

69 

70 def pop(self, key, default=__not_given): 

71 self.modified = self.modified or key in self._session 

72 args = () if default is self.__not_given else (default,) 

73 return self._session.pop(key, *args) 

74 

75 def setdefault(self, key, value): 

76 if key in self._session: 

77 return self._session[key] 

78 else: 

79 self.modified = True 

80 self._session[key] = value 

81 return value 

82 

83 def set_test_cookie(self): 

84 self[self.TEST_COOKIE_NAME] = self.TEST_COOKIE_VALUE 

85 

86 def test_cookie_worked(self): 

87 return self.get(self.TEST_COOKIE_NAME) == self.TEST_COOKIE_VALUE 

88 

89 def delete_test_cookie(self): 

90 del self[self.TEST_COOKIE_NAME] 

91 

92 def encode(self, session_dict): 

93 "Return the given session dictionary serialized and encoded as a string." 

94 return signing.dumps( 

95 session_dict, 

96 salt=self.key_salt, 

97 serializer=self.serializer, 

98 compress=True, 

99 ) 

100 

101 def decode(self, session_data): 

102 try: 

103 return signing.loads( 

104 session_data, salt=self.key_salt, serializer=self.serializer 

105 ) 

106 except signing.BadSignature: 

107 logger = logging.getLogger("django.security.SuspiciousSession") 

108 logger.warning("Session data corrupted") 

109 except Exception: 

110 # ValueError, unpickling exceptions. If any of these happen, just 

111 # return an empty dictionary (an empty session). 

112 pass 

113 return {} 

114 

115 def update(self, dict_): 

116 self._session.update(dict_) 

117 self.modified = True 

118 

119 def has_key(self, key): 

120 return key in self._session 

121 

122 def keys(self): 

123 return self._session.keys() 

124 

125 def values(self): 

126 return self._session.values() 

127 

128 def items(self): 

129 return self._session.items() 

130 

131 def clear(self): 

132 # To avoid unnecessary persistent storage accesses, we set up the 

133 # internals directly (loading data wastes time, since we are going to 

134 # set it to an empty dict anyway). 

135 self._session_cache = {} 

136 self.accessed = True 

137 self.modified = True 

138 

139 def is_empty(self): 

140 "Return True when there is no session_key and the session is empty." 

141 try: 

142 return not self._session_key and not self._session_cache 

143 except AttributeError: 

144 return True 

145 

146 def _get_new_session_key(self): 

147 "Return session key that isn't being used." 

148 while True: 

149 session_key = get_random_string(32, VALID_KEY_CHARS) 

150 if not self.exists(session_key): 

151 return session_key 

152 

153 def _get_or_create_session_key(self): 

154 if self._session_key is None: 

155 self._session_key = self._get_new_session_key() 

156 return self._session_key 

157 

158 def _validate_session_key(self, key): 

159 """ 

160 Key must be truthy and at least 8 characters long. 8 characters is an 

161 arbitrary lower bound for some minimal key security. 

162 """ 

163 return key and len(key) >= 8 

164 

165 def _get_session_key(self): 

166 return self.__session_key 

167 

168 def _set_session_key(self, value): 

169 """ 

170 Validate session key on assignment. Invalid values will set to None. 

171 """ 

172 if self._validate_session_key(value): 172 ↛ 173line 172 didn't jump to line 173, because the condition on line 172 was never true

173 self.__session_key = value 

174 else: 

175 self.__session_key = None 

176 

177 session_key = property(_get_session_key) 

178 _session_key = property(_get_session_key, _set_session_key) 

179 

180 def _get_session(self, no_load=False): 

181 """ 

182 Lazily load session from storage (unless "no_load" is True, when only 

183 an empty dict is stored) and store it in the current instance. 

184 """ 

185 self.accessed = True 

186 try: 

187 return self._session_cache 

188 except AttributeError: 

189 if self.session_key is None or no_load: 189 ↛ 192line 189 didn't jump to line 192, because the condition on line 189 was never false

190 self._session_cache = {} 

191 else: 

192 self._session_cache = self.load() 

193 return self._session_cache 

194 

195 _session = property(_get_session) 

196 

197 def get_session_cookie_age(self): 

198 return settings.SESSION_COOKIE_AGE 

199 

200 def get_expiry_age(self, **kwargs): 

201 """Get the number of seconds until the session expires. 

202 

203 Optionally, this function accepts `modification` and `expiry` keyword 

204 arguments specifying the modification and expiry of the session. 

205 """ 

206 try: 

207 modification = kwargs["modification"] 

208 except KeyError: 

209 modification = timezone.now() 

210 # Make the difference between "expiry=None passed in kwargs" and 

211 # "expiry not passed in kwargs", in order to guarantee not to trigger 

212 # self.load() when expiry is provided. 

213 try: 

214 expiry = kwargs["expiry"] 

215 except KeyError: 

216 expiry = self.get("_session_expiry") 

217 

218 if not expiry: # Checks both None and 0 cases 

219 return self.get_session_cookie_age() 

220 if not isinstance(expiry, datetime): 

221 return expiry 

222 delta = expiry - modification 

223 return delta.days * 86400 + delta.seconds 

224 

225 def get_expiry_date(self, **kwargs): 

226 """Get session the expiry date (as a datetime object). 

227 

228 Optionally, this function accepts `modification` and `expiry` keyword 

229 arguments specifying the modification and expiry of the session. 

230 """ 

231 try: 

232 modification = kwargs["modification"] 

233 except KeyError: 

234 modification = timezone.now() 

235 # Same comment as in get_expiry_age 

236 try: 

237 expiry = kwargs["expiry"] 

238 except KeyError: 

239 expiry = self.get("_session_expiry") 

240 

241 if isinstance(expiry, datetime): 

242 return expiry 

243 expiry = expiry or self.get_session_cookie_age() 

244 return modification + timedelta(seconds=expiry) 

245 

246 def set_expiry(self, value): 

247 """ 

248 Set a custom expiration for the session. ``value`` can be an integer, 

249 a Python ``datetime`` or ``timedelta`` object or ``None``. 

250 

251 If ``value`` is an integer, the session will expire after that many 

252 seconds of inactivity. If set to ``0`` then the session will expire on 

253 browser close. 

254 

255 If ``value`` is a ``datetime`` or ``timedelta`` object, the session 

256 will expire at that specific future time. 

257 

258 If ``value`` is ``None``, the session uses the global session expiry 

259 policy. 

260 """ 

261 if value is None: 

262 # Remove any custom expiration for this session. 

263 try: 

264 del self["_session_expiry"] 

265 except KeyError: 

266 pass 

267 return 

268 if isinstance(value, timedelta): 

269 value = timezone.now() + value 

270 self["_session_expiry"] = value 

271 

272 def get_expire_at_browser_close(self): 

273 """ 

274 Return ``True`` if the session is set to expire when the browser 

275 closes, and ``False`` if there's an expiry date. Use 

276 ``get_expiry_date()`` or ``get_expiry_age()`` to find the actual expiry 

277 date/age, if there is one. 

278 """ 

279 if self.get("_session_expiry") is None: 

280 return settings.SESSION_EXPIRE_AT_BROWSER_CLOSE 

281 return self.get("_session_expiry") == 0 

282 

283 def flush(self): 

284 """ 

285 Remove the current session data from the database and regenerate the 

286 key. 

287 """ 

288 self.clear() 

289 self.delete() 

290 self._session_key = None 

291 

292 def cycle_key(self): 

293 """ 

294 Create a new session key, while retaining the current session data. 

295 """ 

296 data = self._session 

297 key = self.session_key 

298 self.create() 

299 self._session_cache = data 

300 if key: 

301 self.delete(key) 

302 

303 # Methods that child classes must implement. 

304 

305 def exists(self, session_key): 

306 """ 

307 Return True if the given session_key already exists. 

308 """ 

309 raise NotImplementedError( 

310 "subclasses of SessionBase must provide an exists() method" 

311 ) 

312 

313 def create(self): 

314 """ 

315 Create a new session instance. Guaranteed to create a new object with 

316 a unique key and will have saved the result once (with empty data) 

317 before the method returns. 

318 """ 

319 raise NotImplementedError( 

320 "subclasses of SessionBase must provide a create() method" 

321 ) 

322 

323 def save(self, must_create=False): 

324 """ 

325 Save the session data. If 'must_create' is True, create a new session 

326 object (or raise CreateError). Otherwise, only update an existing 

327 object and don't create one (raise UpdateError if needed). 

328 """ 

329 raise NotImplementedError( 

330 "subclasses of SessionBase must provide a save() method" 

331 ) 

332 

333 def delete(self, session_key=None): 

334 """ 

335 Delete the session data under this key. If the key is None, use the 

336 current session key value. 

337 """ 

338 raise NotImplementedError( 

339 "subclasses of SessionBase must provide a delete() method" 

340 ) 

341 

342 def load(self): 

343 """ 

344 Load the session data and return a dictionary. 

345 """ 

346 raise NotImplementedError( 

347 "subclasses of SessionBase must provide a load() method" 

348 ) 

349 

350 @classmethod 

351 def clear_expired(cls): 

352 """ 

353 Remove expired sessions from the session store. 

354 

355 If this operation isn't possible on a given backend, it should raise 

356 NotImplementedError. If it isn't necessary, because the backend has 

357 a built-in expiration mechanism, it should be a no-op. 

358 """ 

359 raise NotImplementedError("This backend does not support clear_expired().")