refactor: consolidate user and superadmin sessions into unified model
This commit is contained in:
@@ -8,7 +8,7 @@ from gatehouse_app.extensions import db, bcrypt
|
||||
from gatehouse_app.models.user.user import User
|
||||
from gatehouse_app.models.auth.authentication_method import AuthenticationMethod
|
||||
from gatehouse_app.models.user.session import Session
|
||||
from gatehouse_app.utils.constants import AuthMethodType, SessionStatus, UserStatus, AuditAction
|
||||
from gatehouse_app.utils.constants import AuthMethodType, SessionStatus, SessionType, UserStatus, AuditAction
|
||||
from gatehouse_app.exceptions.auth_exceptions import InvalidCredentialsError, AccountSuspendedError, AccountInactiveError
|
||||
from gatehouse_app.exceptions.validation_exceptions import EmailAlreadyExistsError
|
||||
from gatehouse_app.services.audit_service import AuditService
|
||||
@@ -165,6 +165,8 @@ class AuthService:
|
||||
|
||||
# Create session
|
||||
session = Session(
|
||||
owner_type=SessionType.USER,
|
||||
owner_id=user.id,
|
||||
user_id=user.id,
|
||||
token=token,
|
||||
status=SessionStatus.ACTIVE,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Session service."""
|
||||
from datetime import datetime, timezone
|
||||
from gatehouse_app.models.user.session import Session
|
||||
from gatehouse_app.utils.constants import SessionStatus
|
||||
from gatehouse_app.utils.constants import SessionStatus, SessionType
|
||||
|
||||
|
||||
class SessionService:
|
||||
@@ -28,18 +28,22 @@ class SessionService:
|
||||
).first()
|
||||
|
||||
@staticmethod
|
||||
def get_user_sessions(user_id, active_only=True):
|
||||
"""
|
||||
Get all sessions for a user.
|
||||
def get_owner_sessions(owner_type, owner_id, active_only=True):
|
||||
"""Get all sessions for an owner (user or superadmin).
|
||||
|
||||
Args:
|
||||
user_id: User ID
|
||||
owner_type: SessionType.USER or SessionType.SUPERADMIN
|
||||
owner_id: Owner ID
|
||||
active_only: If True, only return active sessions
|
||||
|
||||
Returns:
|
||||
List of Session instances
|
||||
"""
|
||||
query = Session.query.filter_by(user_id=user_id, deleted_at=None)
|
||||
query = Session.query.filter_by(
|
||||
owner_type=owner_type,
|
||||
owner_id=owner_id,
|
||||
deleted_at=None,
|
||||
)
|
||||
|
||||
if active_only:
|
||||
query = query.filter_by(status=SessionStatus.ACTIVE).filter(
|
||||
@@ -49,18 +53,67 @@ class SessionService:
|
||||
return query.all()
|
||||
|
||||
@staticmethod
|
||||
def revoke_user_sessions(user_id, reason="User logged out from all devices"):
|
||||
def get_user_sessions(user_id, active_only=True):
|
||||
"""Get all sessions for a user.
|
||||
|
||||
Args:
|
||||
user_id: User ID
|
||||
active_only: If True, only return active sessions
|
||||
|
||||
Returns:
|
||||
List of Session instances
|
||||
"""
|
||||
Revoke all active sessions for a user.
|
||||
return SessionService.get_owner_sessions(
|
||||
SessionType.USER, user_id, active_only=active_only
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def get_superadmin_sessions(superadmin_id, active_only=True):
|
||||
"""Get all sessions for a superadmin.
|
||||
|
||||
Args:
|
||||
superadmin_id: Superadmin ID
|
||||
active_only: If True, only return active sessions
|
||||
|
||||
Returns:
|
||||
List of Session instances
|
||||
"""
|
||||
return SessionService.get_owner_sessions(
|
||||
SessionType.SUPERADMIN, superadmin_id, active_only=active_only
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def revoke_owner_sessions(owner_type, owner_id, reason="Logged out from all devices"):
|
||||
"""Revoke all active sessions for an owner.
|
||||
|
||||
Args:
|
||||
owner_type: SessionType.USER or SessionType.SUPERADMIN
|
||||
owner_id: Owner ID
|
||||
reason: Reason for revocation
|
||||
"""
|
||||
sessions = SessionService.get_owner_sessions(owner_type, owner_id, active_only=True)
|
||||
for session in sessions:
|
||||
session.revoke(reason=reason)
|
||||
|
||||
@staticmethod
|
||||
def revoke_user_sessions(user_id, reason="User logged out from all devices"):
|
||||
"""Revoke all active sessions for a user.
|
||||
|
||||
Args:
|
||||
user_id: User ID
|
||||
reason: Reason for revocation
|
||||
"""
|
||||
sessions = SessionService.get_user_sessions(user_id, active_only=True)
|
||||
SessionService.revoke_owner_sessions(SessionType.USER, user_id, reason=reason)
|
||||
|
||||
for session in sessions:
|
||||
session.revoke(reason=reason)
|
||||
@staticmethod
|
||||
def revoke_superadmin_sessions(superadmin_id, reason="Superadmin logged out"):
|
||||
"""Revoke all active sessions for a superadmin.
|
||||
|
||||
Args:
|
||||
superadmin_id: Superadmin ID
|
||||
reason: Reason for revocation
|
||||
"""
|
||||
SessionService.revoke_owner_sessions(SessionType.SUPERADMIN, superadmin_id, reason=reason)
|
||||
|
||||
@staticmethod
|
||||
def cleanup_expired_sessions():
|
||||
|
||||
@@ -6,7 +6,9 @@ from typing import Optional
|
||||
|
||||
from flask import request, current_app
|
||||
from gatehouse_app.extensions import db, bcrypt
|
||||
from gatehouse_app.models.superadmin import Superadmin, SuperadminSession
|
||||
from gatehouse_app.models.superadmin import Superadmin
|
||||
from gatehouse_app.models.user.session import Session
|
||||
from gatehouse_app.utils.constants import SessionType
|
||||
from gatehouse_app.exceptions.auth_exceptions import InvalidCredentialsError
|
||||
|
||||
|
||||
@@ -70,15 +72,17 @@ class SuperadminAuthService:
|
||||
duration_seconds: Session duration in seconds (default 8 hours)
|
||||
|
||||
Returns:
|
||||
SuperadminSession instance
|
||||
Session instance
|
||||
"""
|
||||
# Generate secure token
|
||||
token = secrets.token_urlsafe(32)
|
||||
|
||||
# Create session
|
||||
session = SuperadminSession(
|
||||
superadmin_id=superadmin_id,
|
||||
# Create session using unified model
|
||||
session = Session(
|
||||
owner_type=SessionType.SUPERADMIN,
|
||||
owner_id=superadmin_id,
|
||||
token=token,
|
||||
status="active",
|
||||
expires_at=datetime.now(timezone.utc) + timedelta(seconds=duration_seconds),
|
||||
last_activity_at=datetime.now(timezone.utc),
|
||||
ip_address=request.remote_addr,
|
||||
@@ -97,7 +101,9 @@ class SuperadminAuthService:
|
||||
session_id: Session ID to revoke
|
||||
reason: Optional revocation reason
|
||||
"""
|
||||
session = SuperadminSession.query.get(session_id)
|
||||
session = Session.query.filter_by(
|
||||
id=session_id, owner_type=SessionType.SUPERADMIN
|
||||
).first()
|
||||
if session:
|
||||
session.revoke(reason=reason)
|
||||
logger.info(f"[SuperadminAuth] Session {session_id} revoked: {reason or 'No reason'}")
|
||||
@@ -111,9 +117,11 @@ class SuperadminAuthService:
|
||||
except_token: Optional token to keep (current session)
|
||||
reason: Optional revocation reason
|
||||
"""
|
||||
query = SuperadminSession.query.filter_by(superadmin_id=superadmin_id)
|
||||
query = Session.query.filter_by(
|
||||
owner_type=SessionType.SUPERADMIN, owner_id=superadmin_id
|
||||
)
|
||||
if except_token:
|
||||
query = query.filter(SuperadminSession.token != except_token)
|
||||
query = query.filter(Session.token != except_token)
|
||||
|
||||
sessions = query.all()
|
||||
for session in sessions:
|
||||
|
||||
Reference in New Issue
Block a user