feat: add network-level kill switch endpoint
This commit is contained in:
@@ -561,6 +561,52 @@ def kill_switch(
|
||||
return count
|
||||
|
||||
|
||||
def kill_switch_network(
|
||||
portal_network_id: str,
|
||||
organization_id: str,
|
||||
admin_user_id: str,
|
||||
) -> int:
|
||||
"""Deactivate all active memberships on a network across all users."""
|
||||
requests = NetworkAccessRequest.query.filter(
|
||||
NetworkAccessRequest.portal_network_id == portal_network_id,
|
||||
NetworkAccessRequest.organization_id == organization_id,
|
||||
NetworkAccessRequest.active == True,
|
||||
NetworkAccessRequest.deleted_at.is_(None),
|
||||
).all()
|
||||
count = 0
|
||||
|
||||
for r in requests:
|
||||
_end_active_session(r, reason=ActivationEndReason.KILL_SWITCH)
|
||||
|
||||
device = Device.query.get(r.device_id)
|
||||
network = PortalNetwork.query.get(r.portal_network_id)
|
||||
if device and network:
|
||||
try:
|
||||
zt.deauthorize_member(network.zerotier_network_id, device.node_id,
|
||||
organization_id=r.organization_id)
|
||||
except Exception as exc:
|
||||
logger.warning(f"[kill_switch_network] Could not deauthorize {device.node_id}: {exc}")
|
||||
|
||||
r.active = False
|
||||
if r.status == ApprovalState.APPROVED:
|
||||
r.status = ApprovalState.SUSPENDED
|
||||
r.save()
|
||||
count += 1
|
||||
|
||||
AuditService.log_action(
|
||||
action=AuditAction.ZT_NETWORK_KILL_SWITCH,
|
||||
user_id=admin_user_id,
|
||||
organization_id=organization_id,
|
||||
resource_type="portal_network",
|
||||
resource_id=portal_network_id,
|
||||
metadata={"affected_count": count},
|
||||
description=f"Network kill switch activated: {count} requests deactivated on network {portal_network_id}",
|
||||
success=True,
|
||||
)
|
||||
|
||||
return count
|
||||
|
||||
|
||||
# ── Helpers ────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -799,6 +845,66 @@ def join_network_for_device(
|
||||
return request
|
||||
|
||||
|
||||
def admin_end_session(
|
||||
session_id: str,
|
||||
admin_user_id: str,
|
||||
) -> ActivationSession:
|
||||
"""End a specific activation session (admin only).
|
||||
|
||||
Ends the session, deauthorizes the device in ZeroTier, and marks the
|
||||
associated network access request as inactive. Does NOT change the
|
||||
request's approval status — the user keeps their approval and can
|
||||
re-authenticate without needing re-approval.
|
||||
"""
|
||||
session = ActivationSession.query.filter(
|
||||
ActivationSession.id == session_id,
|
||||
ActivationSession.deleted_at.is_(None),
|
||||
).first()
|
||||
|
||||
if not session:
|
||||
raise ApprovalNotFoundError(f"Session {session_id} not found.")
|
||||
|
||||
if session.ended_at:
|
||||
raise ValidationError("Session already ended.")
|
||||
|
||||
# End the session with ADMIN_ACTION reason
|
||||
_end_session(session, ActivationEndReason.ADMIN_ACTION)
|
||||
|
||||
# Deactivate the associated request
|
||||
if session.network_access_request_id:
|
||||
request = NetworkAccessRequest.query.get(session.network_access_request_id)
|
||||
if request and request.active:
|
||||
request.active = False
|
||||
request.save()
|
||||
|
||||
# Deauthorize in ZeroTier
|
||||
device = Device.query.get(request.device_id)
|
||||
network = PortalNetwork.query.get(request.portal_network_id)
|
||||
if device and network:
|
||||
_deauthorize_in_zerotier(
|
||||
device.node_id,
|
||||
network.zerotier_network_id,
|
||||
organization_id=request.organization_id,
|
||||
)
|
||||
|
||||
AuditService.log_action(
|
||||
action=AuditAction.ZT_SESSION_ENDED,
|
||||
user_id=admin_user_id,
|
||||
organization_id=session.organization_id,
|
||||
resource_type="activation_session",
|
||||
resource_id=session.id,
|
||||
metadata={
|
||||
"target_user_id": session.user_id,
|
||||
"end_reason": ActivationEndReason.ADMIN_ACTION.value,
|
||||
"network_access_request_id": session.network_access_request_id,
|
||||
},
|
||||
description=f"Admin terminated session for user {session.user_id}",
|
||||
success=True,
|
||||
)
|
||||
|
||||
return session
|
||||
|
||||
|
||||
# ── Admin membership management ────────────────────────────────────────────────
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user