feat: add network-level kill switch endpoint

This commit is contained in:
2026-05-30 06:32:26 +00:00
parent fed72f8bcd
commit 2aad17f5e0
8 changed files with 460 additions and 1 deletions
+58
View File
@@ -117,6 +117,10 @@ class KillSwitchSchema(Schema):
network_ids = fields.List(fields.Str(), allow_none=True)
class NetworkKillSwitchSchema(Schema):
reason = fields.Str(allow_none=True, validate=validate.Length(max=500))
# ── Networks ──────────────────────────────────────────────────────────────────
@@ -971,6 +975,36 @@ def admin_list_sessions(org_id):
)
@api_v1_bp.route("/organizations/<org_id>/admin/sessions/<session_id>/end", methods=["POST"])
@login_required
@require_admin
@full_access_required
def admin_end_session(org_id, session_id):
"""End a specific activation session (admin only).
Terminates the active session for any user, deauthorizes the device
in ZeroTier, and marks the membership as inactive. The user retains
their approval and can re-authenticate without re-approval.
"""
org, err = _org_check(org_id)
if err:
return err
try:
session = network_access_service.admin_end_session(
session_id=session_id,
admin_user_id=g.current_user.id,
)
return api_response(
data={"session": _session_to_dict(session, include_user=True)},
message="Session ended successfully by admin",
)
except ApprovalNotFoundError as e:
return api_response(success=False, message=str(e), status=404, error_type="NOT_FOUND")
except AppValidationError as e:
return api_response(success=False, message=str(e.message), status=400, error_type=e.error_type)
# ── Kill Switch ───────────────────────────────────────────────────────────────
@@ -1005,6 +1039,30 @@ def trigger_kill_switch(org_id):
return api_response(success=False, message=str(e.message), status=400, error_type=e.error_type)
@api_v1_bp.route("/organizations/<org_id>/networks/<network_id>/kill-switch", methods=["POST"])
@login_required
@require_admin
@full_access_required
def trigger_network_kill_switch(org_id, network_id):
"""Deactivate all active memberships on a network (admin only)."""
org, err = _org_check(org_id)
if err:
return err
schema = NetworkKillSwitchSchema()
data = schema.load(request.json or {})
count = network_access_service.kill_switch_network(
portal_network_id=network_id,
organization_id=org_id,
admin_user_id=g.current_user.id,
)
return api_response(
data={"affected_count": count},
message="Network kill switch triggered successfully",
)
# ── Admin / ZeroTier Controller ───────────────────────────────────────────────
@api_v1_bp.route("/organizations/<org_id>/admin/memberships", methods=["GET"])