Skip to content

Commit

Permalink
refactor: Sub-class Session from db.Entity to behave dict-compl…
Browse files Browse the repository at this point in the history
…iant (#1153)

Resolves #1147, replaces #1140.

---------

Co-authored-by: Jan Max Meyer <jmm@phorward.de>
Co-authored-by: Sven Eberth <mail@sveneberth.de>
  • Loading branch information
3 people authored May 28, 2024
1 parent acf4b13 commit 56f110b
Showing 1 changed file with 41 additions and 52 deletions.
93 changes: 41 additions & 52 deletions src/viur/core/session.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import datetime
import hmac
import logging
import time
from viur.core.tasks import DeleteEntitiesIter
Expand All @@ -24,8 +23,10 @@
It returns None instead of raising an Exception if the key is not found.
"""

_SENTINEL: t.Final[object] = object()

class Session:

class Session(db.Entity):
"""
Store Sessions inside the datastore.
The behaviour of this module can be customized in the following ways:
Expand Down Expand Up @@ -56,7 +57,6 @@ def __init__(self):
self.changed = False
self.cookie_key = None
self.static_security_key = None
self.session = db.Entity()

def load(self, req):
"""
Expand All @@ -73,7 +73,10 @@ def load(self, req):
return False

self.cookie_key = cookie_key
self.session = data["data"]

super().clear()
super().update(data["data"])

self.static_security_key = data.get("static_security_key") or data.get("staticSecurityKey")

if data["lastseen"] < time.time() - 5 * 60: # Refresh every 5 Minutes
Expand Down Expand Up @@ -105,7 +108,7 @@ def save(self, req):

dbSession = db.Entity(db.Key(self.kindName, self.cookie_key))

dbSession["data"] = db.fixUnindexableProperties(self.session)
dbSession["data"] = db.fixUnindexableProperties(self)
dbSession["static_security_key"] = self.static_security_key
dbSession["lastseen"] = time.time()
dbSession["user"] = str(user_key) # allow filtering for users
Expand All @@ -126,53 +129,14 @@ def save(self, req):
("Set-Cookie", f"{self.cookie_name}={self.cookie_key};{';'.join([f for f in flags if f])}")
)

def __contains__(self, key: str) -> bool:
"""
Returns True if the given *key* is set in the current session.
"""
return key in self.session

def __delitem__(self, key: str) -> None:
"""
Removes a *key* from the session.
This key must exist.
"""
del self.session[key]
self.changed = True

def __getitem__(self, key) -> t.Any:
"""
Returns the value stored under the given *key*.
The key must exist.
"""
return self.session[key]

def __ior__(self, other: dict):
"""
Merges the contents of a dict into the session.
"""
self.session |= other
return self

def get(self, key: str, default: t.Any = None) -> t.Any:
"""
Returns the value stored under the given key.
:param key: Key to retrieve from the session variables.
:param default: Default value to return when key does not exist.
"""
return self.session.get(key, default)

def __setitem__(self, key: str, item: t.Any):
"""
Stores a new value under the given key.
If that key exists before, its value is
overwritten.
"""
self.session[key] = item
super().__setitem__(key, item)
self.changed = True

def markChanged(self) -> None:
Expand All @@ -199,20 +163,45 @@ def reset(self) -> None:

self.cookie_key = utils.string.random(42)
self.static_security_key = utils.string.random(13)
self.session.clear()
self.clear()
self.changed = True

def __delitem__(self, key: str) -> None:
"""
Removes a *key* from the session.
This key must exist.
"""
del self[key]
self.changed = True

def items(self) -> 'dict_items':
def __ior__(self, other: dict) -> t.Self:
"""
Returns all items in the current session.
Merges the contents of a dict into the session.
"""
return self.session.items()
super().__ior__(other)
self.changed = True
return self

def setdefault(self, key: str, value: t.Any) -> t.Any:
def update(self, other: dict) -> None:
"""
Set a default value in the current session
Merges the contents of a dict into the session.
"""
return self.session.setdefault(key, value)
self |= other

def pop(self, key: str, default=_SENTINEL) -> t.Any:
"""
Delete a specified key from the session.
If key is in the session, remove it and return its value, else return default.
If default is not given and key is not in the session, a KeyError is raised.
"""
if key in self or default is _SENTINEL:
value = super().pop(key)
self.changed = True

return value

return default


@tasks.CallDeferred
Expand Down

0 comments on commit 56f110b

Please sign in to comment.