File size: 2,526 Bytes
e5fce98
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
"""
Session model for JWT token management.
"""
from datetime import datetime
from typing import Optional
from uuid import UUID, uuid4

from sqlmodel import Column, DateTime, Field, ForeignKey, SQLModel, Text
from sqlalchemy import text, Index


class Session(SQLModel, table=True):
    """
    Session model for tracking active JWT tokens.

    Attributes:
        id: Unique session identifier (UUID)
        user_id: Associated user ID (foreign key)
        token: JWT token (hashed or partial)
        expires_at: Token expiration timestamp
        created_at: Session creation timestamp
        revoked_at: Optional revocation timestamp
        user_agent: Optional user agent string
        ip_address: Optional IP address
    """

    __tablename__ = 'sessions'

    id: UUID = Field(
        default_factory=uuid4,
        primary_key=True,
        index=True,
        description='Unique session identifier',
    )
    user_id: UUID = Field(
        default=None,
        foreign_key='users.id',
        nullable=False,
        index=True,
        description='Associated user ID',
    )
    token: str = Field(
        max_length=500,
        index=True,
        description='JWT token identifier',
    )
    expires_at: datetime = Field(
        description='Token expiration timestamp',
    )
    created_at: datetime = Field(
        default_factory=datetime.utcnow,
        sa_column=Column(DateTime(), server_default=text('CURRENT_TIMESTAMP')),
        description='Session creation timestamp',
    )
    revoked_at: Optional[datetime] = Field(
        default=None,
        description='Revocation timestamp',
    )
    user_agent: Optional[str] = Field(
        default=None,
        max_length=500,
        description='User agent string',
    )
    ip_address: Optional[str] = Field(
        default=None,
        max_length=45,
        description='IP address (IPv4 or IPv6)',
    )

    # Define indexes
    __table_args__ = (
        Index('idx_sessions_user_expires', 'user_id', 'expires_at'),
        Index('idx_sessions_token', 'token'),
    )

    def __repr__(self) -> str:
        return f'<Session {self.id}>'

    def is_valid(self) -> bool:
        """Check if session is valid (not expired and not revoked)."""
        if self.revoked_at is not None:
            return False
        return datetime.utcnow() < self.expires_at

    def revoke(self) -> None:
        """Revoke the session."""
        self.revoked_at = datetime.utcnow()


# Export for use in other modules
__all__ = ['Session']