cfd79190ee
Adds TOTP (Time-based One-Time Password) two-factor authentication support including: - New TOTP service with secret generation, QR code provisioning, and code verification - New auth endpoints for enrollment, verification, status, and backup code management - New TOTP authentication method type and user methods for TOTP management - Backup codes generation and verification for account recovery - Updated OIDC endpoints with timezone-aware datetime handling and RFC-compliant responses - Added "roles" scope support for OIDC userinfo and ID tokens - New pyotp dependency for TOTP operations - Comprehensive unit tests for TOTP service
74 lines
2.1 KiB
Python
74 lines
2.1 KiB
Python
"""Base model with common fields and functionality."""
|
|
import uuid
|
|
from datetime import datetime, timezone
|
|
from app.extensions import db
|
|
|
|
|
|
class BaseModel(db.Model):
|
|
"""Base model class with common fields."""
|
|
|
|
__abstract__ = True
|
|
|
|
id = db.Column(
|
|
db.String(36),
|
|
primary_key=True,
|
|
default=lambda: str(uuid.uuid4()),
|
|
unique=True,
|
|
nullable=False,
|
|
)
|
|
created_at = db.Column(db.DateTime, nullable=False, default=lambda: datetime.now(timezone.utc))
|
|
updated_at = db.Column(
|
|
db.DateTime, nullable=False, default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc)
|
|
)
|
|
deleted_at = db.Column(db.DateTime, nullable=True)
|
|
|
|
def save(self):
|
|
"""Save the model instance to database."""
|
|
db.session.add(self)
|
|
db.session.commit()
|
|
return self
|
|
|
|
def delete(self, soft=True):
|
|
"""
|
|
Delete the model instance.
|
|
|
|
Args:
|
|
soft: If True, performs soft delete. If False, hard delete.
|
|
"""
|
|
if soft:
|
|
self.deleted_at = datetime.now(timezone.utc)
|
|
db.session.commit()
|
|
else:
|
|
db.session.delete(self)
|
|
db.session.commit()
|
|
|
|
def update(self, **kwargs):
|
|
"""Update model fields."""
|
|
for key, value in kwargs.items():
|
|
if hasattr(self, key):
|
|
setattr(self, key, value)
|
|
self.updated_at = datetime.now(timezone.utc)
|
|
db.session.commit()
|
|
return self
|
|
|
|
def to_dict(self, exclude=None):
|
|
"""
|
|
Convert model to dictionary.
|
|
|
|
Args:
|
|
exclude: List of fields to exclude from output
|
|
|
|
Returns:
|
|
Dictionary representation of the model
|
|
"""
|
|
exclude = exclude or []
|
|
result = {}
|
|
for column in self.__table__.columns:
|
|
if column.name not in exclude:
|
|
value = getattr(self, column.name)
|
|
if isinstance(value, datetime):
|
|
result[column.name] = value.isoformat()
|
|
else:
|
|
result[column.name] = value
|
|
return result
|