Source code for user.models.tan

from datetime import timedelta
from uuid import uuid4

from onegov.core.orm import Base
from onegov.core.orm.mixins import ContentMixin, TimestampMixin
from onegov.core.orm.types import UUID
from onegov.core.orm.types import UTCDateTime
from sqlalchemy import Column, Index, Text
from sqlalchemy.ext.hybrid import hybrid_method


from typing import TYPE_CHECKING
if TYPE_CHECKING:
    import uuid
    from datetime import datetime
    from sqlalchemy.sql import ColumnElement


[docs] DEFAULT_EXPIRES_AFTER = timedelta(hours=1)
[docs] class TAN(Base, TimestampMixin, ContentMixin): """ A single use TAN for temporarily elevating access or to serve as a second authentication factor through e.g. a mobile phone number. """
[docs] __tablename__ = 'tans'
[docs] __table_args__ = ( # TimestampMixin by default does not generate an index for # the created column, so we do it here instead Index('ix_tans_created', 'created'), )
[docs] id: 'Column[uuid.UUID]' = Column( UUID, # type: ignore[arg-type] primary_key=True, default=uuid4 )
[docs] hashed_tan: 'Column[str]' = Column( Text, index=True, nullable=False )
[docs] scope: 'Column[str]' = Column(Text, index=True, nullable=False)
[docs] client: 'Column[str]' = Column(Text, nullable=False)
[docs] expired: 'Column[datetime | None]' = Column(UTCDateTime, index=True)
@hybrid_method
[docs] def is_active( self, expires_after: timedelta | None = DEFAULT_EXPIRES_AFTER ) -> bool: now = self.timestamp() if self.expired and self.expired <= now: return False if expires_after is not None: if now >= (self.created + expires_after): return False return True
@is_active.expression # type:ignore[no-redef] def is_active( cls, expires_after: timedelta | None = DEFAULT_EXPIRES_AFTER ) -> 'ColumnElement[bool]': now = cls.timestamp() expr = (cls.expired > now) | cls.expired.is_(None) if expires_after is not None: expr &= cls.created >= (now - expires_after) return expr
[docs] def expire(self) -> None: if self.expired: raise ValueError('TAN already expired') self.expired = self.timestamp()