diff --git a/synapse/rest/admin/users.py b/synapse/rest/admin/users.py
index f7cb9e02cc..ad515bd5a3 100644
--- a/synapse/rest/admin/users.py
+++ b/synapse/rest/admin/users.py
@@ -27,11 +27,13 @@ from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, Union
import attr
+from synapse._pydantic_compat import HAS_PYDANTIC_V2
from synapse.api.constants import Direction, UserTypes
from synapse.api.errors import Codes, NotFoundError, SynapseError
from synapse.http.servlet import (
RestServlet,
assert_params_in_dict,
+ parse_and_validate_json_object_from_request,
parse_boolean,
parse_enum,
parse_integer,
@@ -49,10 +51,17 @@ from synapse.rest.client._base import client_patterns
from synapse.storage.databases.main.registration import ExternalIDReuseException
from synapse.storage.databases.main.stats import UserSortOrder
from synapse.types import JsonDict, JsonMapping, UserID
+from synapse.types.rest import RequestBodyModel
if TYPE_CHECKING:
from synapse.server import HomeServer
+if TYPE_CHECKING or HAS_PYDANTIC_V2:
+ from pydantic.v1 import StrictBool
+else:
+ from pydantic import StrictBool
+
+
logger = logging.getLogger(__name__)
@@ -732,6 +741,36 @@ class DeactivateAccountRestServlet(RestServlet):
return HTTPStatus.OK, {"id_server_unbind_result": id_server_unbind_result}
+class SuspendAccountRestServlet(RestServlet):
+ PATTERNS = admin_patterns("/suspend/(?P<target_user_id>[^/]*)$")
+
+ def __init__(self, hs: "HomeServer"):
+ self.auth = hs.get_auth()
+ self.is_mine = hs.is_mine
+ self.store = hs.get_datastores().main
+
+ class PutBody(RequestBodyModel):
+ suspend: StrictBool
+
+ async def on_PUT(
+ self, request: SynapseRequest, target_user_id: str
+ ) -> Tuple[int, JsonDict]:
+ requester = await self.auth.get_user_by_req(request)
+ await assert_user_is_admin(self.auth, requester)
+
+ if not self.is_mine(UserID.from_string(target_user_id)):
+ raise SynapseError(HTTPStatus.BAD_REQUEST, "Can only suspend local users")
+
+ if not await self.store.get_user_by_id(target_user_id):
+ raise NotFoundError("User not found")
+
+ body = parse_and_validate_json_object_from_request(request, self.PutBody)
+ suspend = body.suspend
+ await self.store.set_user_suspended_status(target_user_id, suspend)
+
+ return HTTPStatus.OK, {f"user_{target_user_id}_suspended": suspend}
+
+
class AccountValidityRenewServlet(RestServlet):
PATTERNS = admin_patterns("/account_validity/validity$")
|