ctfy.sdk.admin_resources.users
client.admin.users — user roster + role mutation (admin).
1"""``client.admin.users`` — user roster + role mutation (admin).""" 2 3from __future__ import annotations 4 5import builtins 6 7from ctfy.sdk._helpers import _raise_for_status 8from ctfy.sdk.base import BaseHttpClient 9from ctfy.server.models import AdminUserInfo 10 11 12class AdminUsersResource: 13 """Admin-or-better user listing, lookup and promotion.""" 14 15 def __init__(self, http: BaseHttpClient) -> None: 16 self._http = http 17 18 def list( 19 self, 20 offset: int = 0, 21 limit: int = 50, 22 *, 23 q: str = "", 24 role: str = "", 25 sort: str = "", 26 dir: str = "", 27 ) -> builtins.list[AdminUserInfo]: 28 """Users known to the platform, with role + promotion audit. 29 30 ``q`` (matches display name / email / id), ``role``, ``sort`` 31 (``name`` / ``email`` / ``created`` / ``last_active`` / 32 ``solves`` / ``attempts``) and ``dir`` (``asc`` / ``desc``) are 33 applied **server-side**, before the page is sliced. 34 """ 35 params: dict[str, str | int] = {"offset": offset, "limit": limit} 36 params.update( 37 {k: v for k, v in (("q", q), ("role", role), ("sort", sort), ("dir", dir)) if v} 38 ) 39 resp = self._http.request("GET", "/admin/users", params=params) 40 _raise_for_status(resp) 41 return [AdminUserInfo.model_validate(u) for u in resp.json()["items"]] 42 43 def get(self, user_id: str) -> AdminUserInfo: 44 """Admin-or-better: single-user lookup with the audit fields 45 (role, promoted_by, promoted_at, current team summary). Skips 46 the list scan :meth:`list` would otherwise force.""" 47 resp = self._http.request("GET", f"/admin/users/{user_id}") 48 _raise_for_status(resp) 49 return AdminUserInfo.model_validate(resp.json()) 50 51 def set_role(self, user_id: str, role: str) -> AdminUserInfo: 52 """Promote / demote between ``"user"`` and ``"admin"``. 53 54 Requires **super-admin**. ``super_admin`` role is env-anchored and 55 cannot be changed via API; trying returns 403. 56 """ 57 resp = self._http.request( 58 "PATCH", 59 f"/admin/users/{user_id}/role", 60 json={"role": role}, 61 ) 62 _raise_for_status(resp) 63 return AdminUserInfo.model_validate(resp.json())
class
AdminUsersResource:
13class AdminUsersResource: 14 """Admin-or-better user listing, lookup and promotion.""" 15 16 def __init__(self, http: BaseHttpClient) -> None: 17 self._http = http 18 19 def list( 20 self, 21 offset: int = 0, 22 limit: int = 50, 23 *, 24 q: str = "", 25 role: str = "", 26 sort: str = "", 27 dir: str = "", 28 ) -> builtins.list[AdminUserInfo]: 29 """Users known to the platform, with role + promotion audit. 30 31 ``q`` (matches display name / email / id), ``role``, ``sort`` 32 (``name`` / ``email`` / ``created`` / ``last_active`` / 33 ``solves`` / ``attempts``) and ``dir`` (``asc`` / ``desc``) are 34 applied **server-side**, before the page is sliced. 35 """ 36 params: dict[str, str | int] = {"offset": offset, "limit": limit} 37 params.update( 38 {k: v for k, v in (("q", q), ("role", role), ("sort", sort), ("dir", dir)) if v} 39 ) 40 resp = self._http.request("GET", "/admin/users", params=params) 41 _raise_for_status(resp) 42 return [AdminUserInfo.model_validate(u) for u in resp.json()["items"]] 43 44 def get(self, user_id: str) -> AdminUserInfo: 45 """Admin-or-better: single-user lookup with the audit fields 46 (role, promoted_by, promoted_at, current team summary). Skips 47 the list scan :meth:`list` would otherwise force.""" 48 resp = self._http.request("GET", f"/admin/users/{user_id}") 49 _raise_for_status(resp) 50 return AdminUserInfo.model_validate(resp.json()) 51 52 def set_role(self, user_id: str, role: str) -> AdminUserInfo: 53 """Promote / demote between ``"user"`` and ``"admin"``. 54 55 Requires **super-admin**. ``super_admin`` role is env-anchored and 56 cannot be changed via API; trying returns 403. 57 """ 58 resp = self._http.request( 59 "PATCH", 60 f"/admin/users/{user_id}/role", 61 json={"role": role}, 62 ) 63 _raise_for_status(resp) 64 return AdminUserInfo.model_validate(resp.json())
Admin-or-better user listing, lookup and promotion.
def
list( self, offset: int = 0, limit: int = 50, *, q: str = '', role: str = '', sort: str = '', dir: str = '') -> list[ctfy.server.models.AdminUserInfo]:
19 def list( 20 self, 21 offset: int = 0, 22 limit: int = 50, 23 *, 24 q: str = "", 25 role: str = "", 26 sort: str = "", 27 dir: str = "", 28 ) -> builtins.list[AdminUserInfo]: 29 """Users known to the platform, with role + promotion audit. 30 31 ``q`` (matches display name / email / id), ``role``, ``sort`` 32 (``name`` / ``email`` / ``created`` / ``last_active`` / 33 ``solves`` / ``attempts``) and ``dir`` (``asc`` / ``desc``) are 34 applied **server-side**, before the page is sliced. 35 """ 36 params: dict[str, str | int] = {"offset": offset, "limit": limit} 37 params.update( 38 {k: v for k, v in (("q", q), ("role", role), ("sort", sort), ("dir", dir)) if v} 39 ) 40 resp = self._http.request("GET", "/admin/users", params=params) 41 _raise_for_status(resp) 42 return [AdminUserInfo.model_validate(u) for u in resp.json()["items"]]
Users known to the platform, with role + promotion audit.
q (matches display name / email / id), role, sort
(name / email / created / last_active /
solves / attempts) and dir (asc / desc) are
applied server-side, before the page is sliced.
44 def get(self, user_id: str) -> AdminUserInfo: 45 """Admin-or-better: single-user lookup with the audit fields 46 (role, promoted_by, promoted_at, current team summary). Skips 47 the list scan :meth:`list` would otherwise force.""" 48 resp = self._http.request("GET", f"/admin/users/{user_id}") 49 _raise_for_status(resp) 50 return AdminUserInfo.model_validate(resp.json())
Admin-or-better: single-user lookup with the audit fields
(role, promoted_by, promoted_at, current team summary). Skips
the list scan list() would otherwise force.
52 def set_role(self, user_id: str, role: str) -> AdminUserInfo: 53 """Promote / demote between ``"user"`` and ``"admin"``. 54 55 Requires **super-admin**. ``super_admin`` role is env-anchored and 56 cannot be changed via API; trying returns 403. 57 """ 58 resp = self._http.request( 59 "PATCH", 60 f"/admin/users/{user_id}/role", 61 json={"role": role}, 62 ) 63 _raise_for_status(resp) 64 return AdminUserInfo.model_validate(resp.json())
Promote / demote between "user" and "admin".
Requires super-admin. super_admin role is env-anchored and
cannot be changed via API; trying returns 403.