Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/3531.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
add admin api to send a server notice to one or all users
35 changes: 35 additions & 0 deletions docs/admin_api/server_notices.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Server Notices

The API to send notices is as follows:

```
POST /_matrix/client/r0/admin/send_server_notice/[<user_id>]
```

including an `access_token` of a server admin.

If the user_id is missing, the message is meant to go to all users on the server.

The request body should contain the following:

```json
{
"event": {
"msgtype":"m.text",
"body": "This is my message"
}
}
```
or as shortcut you can also send
```json
{
"event_body": "This is my message"
}
```

## Notes:
1) You have to configure server notices in [homeserver.yaml](../server_notices.md) before
you can use this API
2) This query sends a message to all users (and creates a room for each one if it was not
done before). So please be aware that you will put your server under heavy load if you
have a large number of registered users.
24 changes: 5 additions & 19 deletions docs/server_notices.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
Server Notices
==============
# Server Notices

'Server Notices' are a new feature introduced in Synapse 0.30. They provide a
channel whereby server administrators can send messages to users on the server.
Expand All @@ -11,8 +10,7 @@ they may also find a use for features such as "Message of the day".
This is a feature specific to Synapse, but it uses standard Matrix
communication mechanisms, so should work with any Matrix client.

User experience
---------------
## User experience

When the user is first sent a server notice, they will get an invitation to a
room (typically called 'Server Notices', though this is configurable in
Expand All @@ -29,8 +27,7 @@ levels.
Having joined the room, the user can leave the room if they want. Subsequent
server notices will then cause a new room to be created.

Synapse configuration
---------------------
## Synapse configuration

Server notices come from a specific user id on the server. Server
administrators are free to choose the user id - something like `server` is
Expand Down Expand Up @@ -58,17 +55,6 @@ room which will be created.
`system_mxid_display_name` and `system_mxid_avatar_url` can be used to set the
displayname and avatar of the Server Notices user.

Sending notices
---------------
## Sending notices

As of the current version of synapse, there is no convenient interface for
sending notices (other than the automated ones sent as part of consent
tracking).

In the meantime, it is possible to test this feature using the manhole. Having
gone into the manhole as described in [manhole.md](manhole.md), a notice can be
sent with something like:

```
>>> hs.get_server_notices_manager().send_notice('@user:server.com', {'msgtype':'m.text', 'body':'foo'})
```
To send server notices to users you can use the [admin_api](admin_api/server_notices.md).
71 changes: 67 additions & 4 deletions synapse/rest/client/v1/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,7 @@ class ResetPasswordRestServlet(ClientV1RestServlet):
"""Post request to allow an administrator reset password for a user.
This needs user to have administrator access in Synapse.
Example:
http://localhost:8008/_matrix/client/api/v1/admin/reset_password/
http://localhost:8008/_matrix/client/r0/admin/reset_password/
@user:to_reset_password?access_token=admin_access_token
JsonBodyToSend:
{
Expand Down Expand Up @@ -603,7 +603,7 @@ class GetUsersPaginatedRestServlet(ClientV1RestServlet):
"""Get request to get specific number of users from Synapse.
This needs user to have administrator access in Synapse.
Example:
http://localhost:8008/_matrix/client/api/v1/admin/users_paginate/
http://localhost:8008/_matrix/client/r0/admin/users_paginate/
@admin:user?access_token=admin_access_token&start=0&limit=10
Returns:
200 OK with json object {list[dict[str, Any]], count} or empty object.
Expand Down Expand Up @@ -652,7 +652,7 @@ def on_POST(self, request, target_user_id):
"""Post request to get specific number of users from Synapse..
This needs user to have administrator access in Synapse.
Example:
http://localhost:8008/_matrix/client/api/v1/admin/users_paginate/
http://localhost:8008/_matrix/client/r0/admin/users_paginate/
@admin:user?access_token=admin_access_token
JsonBodyToSend:
{
Expand Down Expand Up @@ -687,7 +687,7 @@ class SearchUsersRestServlet(ClientV1RestServlet):
search term.
This needs user to have administrator access in Synapse.
Example:
http://localhost:8008/_matrix/client/api/v1/admin/search_users/
http://localhost:8008/_matrix/client/r0/admin/search_users/
@admin:user?access_token=admin_access_token&term=alice
Returns:
200 OK with json object {list[dict[str, Any]], count} or empty object.
Expand Down Expand Up @@ -730,6 +730,68 @@ def on_GET(self, request, target_user_id):
defer.returnValue((200, ret))


class ServerNoticeRestServlet(ClientV1RestServlet):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems like it is actually v1

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is that class but it adds the /client/r0/ endpoints as well.

PATTERNS = client_path_patterns("/admin/send_server_notice(/(?P<user_id>[^/]+))?")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm trying to figure out whether there is a difference in behaviour between /admin/send_server_notice/ (with a trailing slash and an empty user_id) and /admin/send_server_notice (with no trailing slash). AFAICT the former will fail, but the docs say it is valid?

I'd suggest that you should not accept /admin/send_server_notice/, and the docs should be updated to clarify.


def __init__(self, hs):
super(ServerNoticeRestServlet, self).__init__(hs)
self._notice_manager = hs.get_server_notices_manager()

@defer.inlineCallbacks
def on_POST(self, request, user_id):
"""
This lets you send a server_notice to one or all users
Example:
http://localhost:8008/_matrix/client/r0/admin/send_server_notice/@user:host
JsonBodyToSend:
{
"event": {'msgtype':'m.text', 'body':'This is my message'}
}
or simply
{
'event_body': 'This is my message'
}
Returns:
200 OK.

"""
body = parse_json_object_from_request(request, allow_empty_body=False)
event = body.get("event", None)
if event is None:
event_body = body.get("event_body", None)
if event_body is None:
raise SynapseError(
http_client.BAD_REQUEST,
"Either 'event' or 'event_body' is required",
Codes.BAD_JSON,
)
event = {
"msgtype": "m.text",
"body": event_body
}
else:
if event.get("msgtype") is None or event.get("body") is None:
raise SynapseError(
http_client.BAD_REQUEST,
"event needs 'msgtype' and 'body' as content",
Codes.BAD_JSON,
)

if user_id is not None:
UserID.from_string(user_id)
requester = yield self.auth.get_user_by_req(request)
is_admin = yield self.auth.is_server_admin(requester.user)

if not is_admin:
raise AuthError(403, "You are not a server admin")

if user_id is not None:
yield self._notice_manager.send_notice(user_id, event)
else:
yield self._notice_manager.send_notice_to_all_users(event)
defer.returnValue((200, {}))


def register_servlets(hs, http_server):
WhoisRestServlet(hs).register(http_server)
PurgeMediaCacheRestServlet(hs).register(http_server)
Expand All @@ -744,3 +806,4 @@ def register_servlets(hs, http_server):
QuarantineMediaInRoom(hs).register(http_server)
ListMediaInRoom(hs).register(http_server)
UserRegisterServlet(hs).register(http_server)
ServerNoticeRestServlet(hs).register(http_server)
31 changes: 31 additions & 0 deletions synapse/server_notices/server_notices_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,37 @@ def is_enabled(self):
"""
return self._config.server_notices_mxid is not None

@defer.inlineCallbacks
def send_notice_to_all_users(self, event_content):
"""Send a notice to all users on the server

Creates the server notices rooms, if none exists.

Args:
event_content (dict): content of event to send

Returns:
Deferred[None]
"""

system_mxid = self._config.server_notices_mxid
requester = create_requester(system_mxid)

user_ids = yield self._store.get_all_local_users()
for user_id in user_ids:
logger.info("Sending server notice to %s", user_id)

room_id = yield self.get_notice_room_for_user(user_id)
yield self._event_creation_handler.create_and_send_nonmember_event(
requester, {
"type": EventTypes.Message,
"room_id": room_id,
"sender": system_mxid,
"content": event_content,
},
ratelimit=False,
)

@defer.inlineCallbacks
def send_notice(self, user_id, event_content):
"""Send a notice to the given user
Expand Down