Made database datetime timezone aware
This commit is contained in:
parent
addfb7c7c4
commit
db96d4b178
|
@ -21,6 +21,7 @@ class CustomJSONEncoder(JSONEncoder):
|
|||
|
||||
if isinstance(o, datetime):
|
||||
return o.isoformat()
|
||||
|
||||
# Check if iterable
|
||||
try:
|
||||
iterable = iter(o)
|
||||
|
|
|
@ -14,7 +14,7 @@ from flaschengeist.system.decorator import login_required
|
|||
from flaschengeist.system.controller import sessionController, userController, messageController
|
||||
from flaschengeist.system.models.session import Session
|
||||
|
||||
session_controller = LocalProxy(lambda: sessionController.SessionController())
|
||||
session_controller: sessionController.SessionController = LocalProxy(lambda: sessionController.SessionController())
|
||||
auth_bp = Blueprint("auth", __name__)
|
||||
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ class SessionController(metaclass=Singleton):
|
|||
access_token = Session.query.filter_by(token=token).one_or_none()
|
||||
if access_token:
|
||||
logger.debug("token found, check if expired or invalid user agent differs")
|
||||
if access_token.expires >= datetime.utcnow() and (
|
||||
if access_token.expires >= datetime.now(timezone.utc) and (
|
||||
access_token.browser == user_agent.browser and access_token.platform == user_agent.platform
|
||||
):
|
||||
if not permissions or access_token.user.has_permissions(permissions):
|
||||
|
@ -71,7 +71,6 @@ class SessionController(metaclass=Singleton):
|
|||
session.refresh()
|
||||
db.session.add(session)
|
||||
db.session.commit()
|
||||
|
||||
logger.debug("access token is {{ {} }}".format(session.token))
|
||||
return session
|
||||
|
||||
|
@ -95,8 +94,7 @@ class SessionController(metaclass=Singleton):
|
|||
def get_users_sessions(self, user):
|
||||
return Session.query.filter(Session._user == user)
|
||||
|
||||
@staticmethod
|
||||
def delete_session(token: Session):
|
||||
def delete_session(self, token: Session):
|
||||
"""Deletes given Session
|
||||
|
||||
Args:
|
||||
|
@ -105,8 +103,7 @@ class SessionController(metaclass=Singleton):
|
|||
db.session.delete(token)
|
||||
db.session.commit()
|
||||
|
||||
@staticmethod
|
||||
def update_session(session):
|
||||
def update_session(self, session):
|
||||
session.refresh()
|
||||
db.session.commit()
|
||||
|
||||
|
@ -117,6 +114,6 @@ class SessionController(metaclass=Singleton):
|
|||
def clear_expired(self):
|
||||
"""Remove expired tokens from database"""
|
||||
logger.debug("Clear expired Sessions")
|
||||
deleted = Session.query.filter(Session.expires < datetime.utcnow()).delete()
|
||||
deleted = Session.query.filter(Session.expires < datetime.now(timezone.utc)).delete()
|
||||
logger.debug("{} sessions have been removed".format(deleted))
|
||||
db.session.commit()
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
import datetime
|
||||
from sqlalchemy.types import DateTime, TypeDecorator
|
||||
|
||||
|
||||
class ModelSerializeMixin:
|
||||
def serialize(self):
|
||||
d = {param: getattr(self, param) for param in self.__class__.__annotations__ if not param.startswith("_")}
|
||||
|
@ -5,3 +9,34 @@ class ModelSerializeMixin:
|
|||
key, value = d.popitem()
|
||||
return value
|
||||
return d
|
||||
|
||||
|
||||
class UtcDateTime(TypeDecorator):
|
||||
"""Almost equivalent to :class:`~sqlalchemy.types.DateTime` with
|
||||
``timezone=True`` option, but it differs from that by:
|
||||
- Never silently take naive :class:`~datetime.datetime`, instead it
|
||||
always raise :exc:`ValueError` unless time zone aware value.
|
||||
- :class:`~datetime.datetime` value's :attr:`~datetime.datetime.tzinfo`
|
||||
is always converted to UTC.
|
||||
- Unlike SQLAlchemy's built-in :class:`~sqlalchemy.types.DateTime`,
|
||||
it never return naive :class:`~datetime.datetime`, but time zone
|
||||
aware value, even with SQLite or MySQL.
|
||||
"""
|
||||
|
||||
impl = DateTime(timezone=True)
|
||||
|
||||
def process_bind_param(self, value, dialect):
|
||||
if value is not None:
|
||||
if not isinstance(value, datetime.datetime):
|
||||
raise TypeError('expected datetime.datetime, not ' +
|
||||
repr(value))
|
||||
elif value.tzinfo is None:
|
||||
raise ValueError('naive datetime is disallowed')
|
||||
return value.astimezone(datetime.timezone.utc)
|
||||
|
||||
def process_result_value(self, value, dialect):
|
||||
if value is not None:
|
||||
if value.tzinfo is not None:
|
||||
value = value.astimezone(datetime.timezone.utc)
|
||||
value = value.replace(tzinfo=datetime.timezone.utc)
|
||||
return value
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
from . import ModelSerializeMixin
|
||||
from . import ModelSerializeMixin, UtcDateTime
|
||||
from .user import User
|
||||
from ..database import db
|
||||
from secrets import compare_digest
|
||||
|
@ -17,7 +17,7 @@ class Session(db.Model, ModelSerializeMixin):
|
|||
"""
|
||||
|
||||
__tablename__ = "session"
|
||||
expires: datetime = db.Column(db.DateTime)
|
||||
expires: datetime = db.Column(UtcDateTime)
|
||||
token: str = db.Column(db.String(32), unique=True)
|
||||
lifetime: int = db.Column(db.Integer)
|
||||
browser: str = db.Column(db.String(30))
|
||||
|
@ -33,8 +33,7 @@ class Session(db.Model, ModelSerializeMixin):
|
|||
Update the Timestamp to the current Time.
|
||||
"""
|
||||
logger.debug("update timestamp from session with token {{ {} }}".format(self))
|
||||
self.expires = datetime.utcnow() + timedelta(seconds=self.lifetime)
|
||||
self.expires.replace(tzinfo=timezone.utc)
|
||||
self.expires = datetime.now(timezone.utc) + timedelta(seconds=self.lifetime)
|
||||
|
||||
def __eq__(self, token):
|
||||
return compare_digest(self.token, token)
|
||||
|
|
Loading…
Reference in New Issue