Coverage for nexios\session\base.py: 61%
126 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-05-21 20:31 +0100
« prev ^ index » next coverage.py v7.8.0, created at 2025-05-21 20:31 +0100
1from typing import Dict, Any, Iterable, Union
2from nexios.config import get_config, MakeConfig
3from datetime import datetime, timedelta, timezone
4import secrets
5import typing
8class BaseSessionInterface:
10 modified = False
12 accessed = False
14 deleted = False
16 _session_cache: Dict[str, Any] = {}
18 def __init__(self, session_key: str) -> None:
20 config: MakeConfig = get_config()
21 self.session_key = session_key
22 if not config.secret_key:
23 return
24 self.config = config
25 self.session_config = config.session
27 def __getitem__(self, key: str) -> Any:
28 self.accessed = True
29 return self._session_cache[key]
31 def __setitem__(self, key: str, value: Any) -> None:
32 self.modified = True
33 self.accessed = True
34 self._session_cache[key] = value
36 def __delitem__(self, key: str) -> None:
37 self.modified = True
38 self.deleted = True
39 del self._session_cache[key]
41 def __iter__(self) -> Iterable[str]:
42 self.accessed = True
43 return iter(self._session_cache)
45 def __len__(self) -> int:
46 self.accessed = True
47 return len(self._session_cache)
49 def __contains__(self, key: str) -> bool:
50 self.accessed = True
51 return key in self._session_cache
53 def set_session(self, key: str, value: str):
55 self.modified = True
56 self.accessed = True
58 self._session_cache[key] = value
60 def get_session(self, key: str):
61 self.accessed = True
63 return self._session_cache.get(key, None)
65 def get_all(self):
66 self.accessed = True
67 return self._session_cache.items()
69 def delete_session(self, key: str):
70 self.modified = True
71 self.deleted = True
72 if key in self._session_cache:
73 del self._session_cache[key]
75 def keys(self):
76 return self._session_cache.keys()
78 def values(self):
79 return self._session_cache.values()
81 def is_empty(self):
82 return self._session_cache.items().__len__() == 0
84 async def save(self):
86 raise NotImplemented
88 def get_cookie_name(self) -> str:
89 """The name of the session cookie. Uses``app.config.SESSION_COOKIE_NAME``."""
90 if not self.session_config:
91 return "session_id"
92 return self.session_config.session_cookie_name or "session_id"
94 def get_cookie_domain(self) -> typing.Optional[str]:
95 """Returns the domain for which the cookie is valid. Uses `config.SESSION_COOKIE_DOMAIN`."""
96 if not self.session_config:
97 return None
98 return self.session_config.session_cookie_domain
100 def get_cookie_path(self) -> Union[str, None]:
101 """Returns the path for which the cookie is valid. Uses `config.SESSION_COOKIE_PATH`."""
102 if not self.session_config:
103 return None
104 return self.session_config.session_cookie_path
106 def get_cookie_httponly(self) -> typing.Optional[bool]:
107 """Returns whether the session cookie should be HTTPOnly. Uses `session_config.session_cookie_httponly`."""
108 if not self.session_config:
109 return None
110 return self.session_config.session_cookie_httponly
112 def get_cookie_secure(self) -> typing.Optional[bool]:
113 """Returns whether the session cookie should be secure. Uses `session_config.session_cookie_secure`."""
114 if not self.session_config:
115 return None
116 return self.session_config.session_cookie_secure
118 def get_cookie_samesite(self) -> typing.Optional[str]:
119 """Returns the SameSite attribute for the cookie. Uses `session_config.session_cookie_samesite`."""
120 if not self.session_config:
121 return None
122 return self.session_config.session_cookie_samesite
124 def get_cookie_partitioned(self) -> typing.Optional[bool]:
125 """Returns whether the cookie should be partitioned. Uses `session_config.session_cookie_partitioned`."""
126 if not self.session_config:
127 return None
128 return self.session_config.session_cookie_partitioned
130 def get_expiration_time(self) -> typing.Optional[datetime]:
131 """Returns the expiration time for the session if one is set."""
132 if not self.session_config:
133 # No config - use default 7 day expiration
134 if not hasattr(self, "_expiration_time"):
135 self._expiration_time = datetime.now(timezone.utc) + timedelta(days=7)
136 return self._expiration_time
138 if not self.session_config.session_permanent:
139 # Non-permanent session - calculate expiration if not set
140 if not hasattr(self, "_expiration_time"):
141 expiration_seconds = (
142 self.session_config.session_expiration_time or 86400
143 )
144 self._expiration_time = datetime.now(timezone.utc) + timedelta(
145 seconds=expiration_seconds
146 )
147 return self._expiration_time
149 return None
151 @property
152 def should_set_cookie(self) -> bool:
153 """Determines if the cookie should be set. Depends on `config.SESSION_REFRESH_EACH_REQUEST`."""
155 if not self.session_config:
157 return self.modified
158 return self.modified or (
159 self.session_config.session_permanent
160 and self.session_config.session_refresh_each_request
161 )
163 def has_expired(self) -> bool:
164 """Returns True if the session has expired."""
165 expiration_time = self.get_expiration_time()
166 if expiration_time and datetime.now(timezone.utc) > expiration_time: # type: ignore
167 return True
168 return False
170 def get_session_key(self) -> str:
171 """Returns the session key."""
172 if self.session_key:
173 return self.session_key
174 return secrets.token_hex(32)
176 def clear(self):
177 self._session_cache = {}
179 def get(self, key: str):
180 return self._session_cache.get(key)
182 def set_expiration_time(self, expiration: datetime) -> None:
183 """Set the expiration time for the session."""
184 self._expiration_time = expiration
186 def __str__(self) -> str:
187 return f"<Sesion { self._session_cache} >"
189 def __del__(self) -> None:
190 self.clear()
191 self.session_key = None
192 self.config = None
193 self.session_config = None