Files
gatehouse-api/gatehouse_app/models/zerotier/activation_session.py
T

108 lines
3.4 KiB
Python

"""Activation session model — temporary activation window for a device membership."""
from gatehouse_app.extensions import db
from gatehouse_app.models.base import BaseModel
from gatehouse_app.utils.constants import ActivationEndReason
class ActivationSession(BaseModel):
"""A temporary activation window for an already-approved device membership.
Created when a user re-authenticates in the portal and wants to activate
an approved device on a network. Has a fixed lifetime (e.g. 8 hours).
When it expires the membership is de-authorized in ZeroTier but the
underlying approval record is untouched.
Attributes:
organization_id: FK to the organization
user_id: FK to the user who owns the session
network_access_request_id: FK to the related network access request
authenticated_at: When the user re-authenticated to start this session
expires_at: When the activation window ends
ended_at: When the session was explicitly ended (null if still active)
end_reason: Why the session ended (expired, logout, kill_switch, etc.)
created_by: FK to the user who triggered activation (usually same as user_id)
"""
__tablename__ = "activation_sessions"
organization_id = db.Column(
db.String(36),
db.ForeignKey("organizations.id"),
nullable=False,
index=True,
)
user_id = db.Column(
db.String(36),
db.ForeignKey("users.id"),
nullable=False,
index=True,
)
network_access_request_id = db.Column(
db.String(36),
db.ForeignKey("network_access_requests.id"),
nullable=False,
index=True,
)
authenticated_at = db.Column(
db.DateTime,
nullable=False,
)
expires_at = db.Column(
db.DateTime,
nullable=False,
)
ended_at = db.Column(db.DateTime, nullable=True)
end_reason = db.Column(
db.Enum(ActivationEndReason, name="activation_end_reason", values_callable=lambda x: [e.value for e in x]),
nullable=True,
)
created_by = db.Column(
db.String(36),
db.ForeignKey("users.id"),
nullable=False,
)
# Relationships
organization = db.relationship("Organization", backref="activation_sessions")
user = db.relationship(
"User",
foreign_keys=[user_id],
backref="activation_sessions",
)
created_by_user = db.relationship(
"User",
foreign_keys=[created_by],
backref="created_activation_sessions",
)
access_request = db.relationship(
"NetworkAccessRequest",
back_populates="activation_sessions",
)
def __repr__(self):
return (
f"<ActivationSession request={self.network_access_request_id} "
f"expires={self.expires_at}>"
)
@property
def is_expired(self) -> bool:
from datetime import datetime, timezone
now = datetime.now(timezone.utc)
exp = self.expires_at
if exp.tzinfo is None:
exp = exp.replace(tzinfo=timezone.utc)
return now > exp
@property
def is_active(self) -> bool:
return self.ended_at is None and not self.is_expired
def to_dict(self, exclude=None):
exclude = exclude or []
data = super().to_dict(exclude=exclude)
data["is_expired"] = self.is_expired
data["is_active"] = self.is_active
return data