"""One-off PROD remediation: force-freeze April Xu (P01108)'s Feishu account and VERIFY the freeze actually took (is_frozen=true). Provider payloads and identifiers are deliberately excluded from stdout. Context: her offboarding froze at 2026-07-24 08:55 CST recorded 'deactivated', but the 11:04 CST directory snapshot shows is_frozen=false — a false-success. Feishu has no per-user session-revoke API, so the freeze is the ONLY lever that actually cuts her access. Terminal DELETE stays deferred (open self-buyback). Run in the prod backend container (creds decrypt only in the prod env): docker exec -i python /app/scripts/ops/freeze_april_p01108.py READ + a single freeze mutation. Idempotent; safe to re-run. """ import asyncio from sqlalchemy import select from sqlalchemy.orm import configure_mappers import app.main # noqa: F401 — register model mappers from app.approval.feishu_client import FeishuApprovalClient from app.core.coordinators import account_lifecycle_coordinator from app.core.database import async_session_factory from app.core.safe_logging import log_fingerprint from app.domains.identity_sync.models import IdentitySyncState, IdentitySyncTarget from app.identity.person_models import IdentityPerson from app.integrations.compat import get_feishu_config EMP_NO = "P01108" async def _get_status(client: FeishuApprovalClient, user_id: str) -> dict: data = await client.request( "GET", f"/contact/v3/users/{user_id}?user_id_type=user_id" ) return ((data or {}).get("user") or {}).get("status") or {} async def main() -> None: configure_mappers() async with async_session_factory() as db: person = ( await db.execute( select(IdentityPerson).where(IdentityPerson.employee_no == EMP_NO) ) ).scalars().first() if person is None: print("!! target person not found") return print( "person_ref=" f"{log_fingerprint(person.id, label='person')} " f"status={person.employment_status}" ) # Resolve her Feishu user_id from the feishu_contact sync state. state = ( await db.execute( select(IdentitySyncState) .join(IdentitySyncTarget, IdentitySyncTarget.id == IdentitySyncState.target_id) .where( IdentitySyncTarget.kind == "feishu_contact", IdentitySyncTarget.enabled.is_(True), IdentitySyncState.person_id == person.id, ) .order_by(IdentitySyncTarget.created_at) ) ).scalars().first() user_id = state.external_id if state else None print( "feishu_user_ref=" f"{log_fingerprint(user_id, label='feishu-user') if user_id else 'missing'}" ) cfg = await get_feishu_config(session=db) client = FeishuApprovalClient( app_id=str(cfg["app_id"]), app_secret=str(cfg["app_secret"]) ) if user_id: before = await _get_status(client, user_id) print("BEFORE is_frozen:", before.get("is_frozen")) # --- The platform freeze path (same one offboarding uses). --- result = await account_lifecycle_coordinator.deactivate_account( db, person, trace_id="ops-freeze-april-p01108", ) print( "deactivate_account result:", { "status": result.get("status"), "external_ref_present": bool(result.get("external_id")), "error_present": bool(result.get("error")), }, ) # --- Direct PATCH; report only whether a response was received. --- if user_id: try: raw = await client.request( "PATCH", f"/contact/v3/users/{user_id}?user_id_type=user_id", json_body={"status": {"is_frozen": True}}, ) print("direct PATCH response_received:", raw is not None) except Exception as e: # noqa: BLE001 print("direct PATCH raised:", type(e).__name__) after = await _get_status(client, user_id) print("AFTER is_frozen:", after.get("is_frozen")) print(">>> FROZEN OK" if after.get("is_frozen") is True else ">>> STILL NOT FROZEN — inspect restricted provider telemetry") if __name__ == "__main__": asyncio.run(main())