Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Add displayname to Shared-Secret Registration for admins #8722

Merged
Merged
Show file tree
Hide file tree
Changes from 2 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/8722.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add `displayname` to Shared-Secret Registration for admins.
Copy link
Member

Choose a reason for hiding this comment

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

I think this is a feature! 🙂

4 changes: 3 additions & 1 deletion docs/admin_api/register_api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,16 @@ To fetch the nonce, you need to request one from the API::

Once you have the nonce, you can make a ``POST`` to the same URL with a JSON
body containing the nonce, username, password, whether they are an admin
(optional, False by default), and a HMAC digest of the content.
(optional, False by default), and a HMAC digest of the content. Also you can
set the displayname (optional, ``username`` by default).

As an example::

> POST /_synapse/admin/v1/register
> {
"nonce": "thisisanonce",
"username": "pepper_roni",
"displayname": "Pepper Roni",
"password": "pizza",
"admin": true,
"mac": "mac_digest_here"
Expand Down
2 changes: 2 additions & 0 deletions synapse/rest/admin/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,7 @@ async def on_POST(self, request):

admin = body.get("admin", None)
user_type = body.get("user_type", None)
displayname = body.get("displayname", None)

if user_type is not None and user_type not in UserTypes.ALL_USER_TYPES:
raise SynapseError(400, "Invalid user type")
Expand Down Expand Up @@ -448,6 +449,7 @@ async def on_POST(self, request):
password_hash=password_hash,
admin=bool(admin),
user_type=user_type,
default_display_name=displayname,
by_admin=True,
)

Expand Down
121 changes: 119 additions & 2 deletions tests/rest/admin/test_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
import synapse.rest.admin
from synapse.api.constants import UserTypes
from synapse.api.errors import Codes, HttpResponseException, ResourceLimitError
from synapse.rest.client.v1 import login, room
from synapse.rest.client.v1 import login, profile, room
from synapse.rest.client.v2_alpha import sync

from tests import unittest
Expand All @@ -34,7 +34,10 @@

class UserRegisterTestCase(unittest.HomeserverTestCase):

servlets = [synapse.rest.admin.register_servlets_for_client_rest_resource]
servlets = [
synapse.rest.admin.register_servlets_for_client_rest_resource,
profile.register_servlets,
]

def make_homeserver(self, reactor, clock):

Expand Down Expand Up @@ -325,6 +328,120 @@ def nonce():
self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"])
self.assertEqual("Invalid user type", channel.json_body["error"])

def test_displayname(self):
"""
Test that displayname of new user is set
"""

# set no displayname
request, channel = self.make_request("GET", self.url)
self.render(request)
nonce = channel.json_body["nonce"]

want_mac = hmac.new(key=b"shared", digestmod=hashlib.sha1)
want_mac.update(nonce.encode("ascii") + b"\x00bob1\x00abc123\x00notadmin")
want_mac = want_mac.hexdigest()

body = json.dumps(
{"nonce": nonce, "username": "bob1", "password": "abc123", "mac": want_mac}
)
request, channel = self.make_request("POST", self.url, body.encode("utf8"))
self.render(request)

self.assertEqual(200, int(channel.result["code"]), msg=channel.result["body"])
self.assertEqual("@bob1:test", channel.json_body["user_id"])

request, channel = self.make_request("GET", "/profile/@bob1:test/displayname")
self.render(request)
self.assertEqual(200, int(channel.result["code"]), msg=channel.result["body"])
self.assertEqual("bob1", channel.json_body["displayname"])

# displayname is None
request, channel = self.make_request("GET", self.url)
self.render(request)
nonce = channel.json_body["nonce"]

want_mac = hmac.new(key=b"shared", digestmod=hashlib.sha1)
want_mac.update(nonce.encode("ascii") + b"\x00bob2\x00abc123\x00notadmin")
want_mac = want_mac.hexdigest()

body = json.dumps(
{
"nonce": nonce,
"username": "bob2",
"displayname": None,
"password": "abc123",
"mac": want_mac,
}
)
request, channel = self.make_request("POST", self.url, body.encode("utf8"))
self.render(request)

self.assertEqual(200, int(channel.result["code"]), msg=channel.result["body"])
self.assertEqual("@bob2:test", channel.json_body["user_id"])

request, channel = self.make_request("GET", "/profile/@bob2:test/displayname")
self.render(request)
self.assertEqual(200, int(channel.result["code"]), msg=channel.result["body"])
self.assertEqual("bob2", channel.json_body["displayname"])

# displayname is empty
request, channel = self.make_request("GET", self.url)
self.render(request)
nonce = channel.json_body["nonce"]

want_mac = hmac.new(key=b"shared", digestmod=hashlib.sha1)
want_mac.update(nonce.encode("ascii") + b"\x00bob3\x00abc123\x00notadmin")
want_mac = want_mac.hexdigest()

body = json.dumps(
{
"nonce": nonce,
"username": "bob3",
"displayname": "",
"password": "abc123",
"mac": want_mac,
}
)
request, channel = self.make_request("POST", self.url, body.encode("utf8"))
self.render(request)

self.assertEqual(200, int(channel.result["code"]), msg=channel.result["body"])
self.assertEqual("@bob3:test", channel.json_body["user_id"])

request, channel = self.make_request("GET", "/profile/@bob3:test/displayname")
self.render(request)
self.assertEqual(404, int(channel.result["code"]), msg=channel.result["body"])

# set displayname
request, channel = self.make_request("GET", self.url)
self.render(request)
nonce = channel.json_body["nonce"]

want_mac = hmac.new(key=b"shared", digestmod=hashlib.sha1)
want_mac.update(nonce.encode("ascii") + b"\x00bob4\x00abc123\x00notadmin")
want_mac = want_mac.hexdigest()

body = json.dumps(
{
"nonce": nonce,
"username": "bob4",
"displayname": "Bob's Name",
"password": "abc123",
"mac": want_mac,
}
)
request, channel = self.make_request("POST", self.url, body.encode("utf8"))
self.render(request)

self.assertEqual(200, int(channel.result["code"]), msg=channel.result["body"])
self.assertEqual("@bob4:test", channel.json_body["user_id"])

request, channel = self.make_request("GET", "/profile/@bob4:test/displayname")
self.render(request)
self.assertEqual(200, int(channel.result["code"]), msg=channel.result["body"])
self.assertEqual("Bob's Name", channel.json_body["displayname"])

@override_config(
{"limit_usage_by_mau": True, "max_mau_value": 2, "mau_trial_days": 0}
)
Expand Down
19 changes: 13 additions & 6 deletions tests/unittest.py
Original file line number Diff line number Diff line change
Expand Up @@ -546,18 +546,24 @@ def get_success_or_raise(self, d, by=0.0):

return result

def register_user(self, username, password, admin=False):
def register_user(
self,
username: str,
password: str,
admin: Optional[bool] = False,
displayname: Optional[str] = None,
) -> str:
"""
Register a user. Requires the Admin API be registered.

Args:
username (bytes/unicode): The user part of the new user.
password (bytes/unicode): The password of the new user.
admin (bool): Whether the user should be created as an admin
or not.
username: The user part of the new user.
password: The password of the new user.
admin: Whether the user should be created as an admin or not.
displayname: The displayname of the new user.

Returns:
The MXID of the new user (unicode).
The MXID of the new user.
"""
self.hs.config.registration_shared_secret = "shared"

Expand All @@ -581,6 +587,7 @@ def register_user(self, username, password, admin=False):
{
"nonce": nonce,
"username": username,
"displayname": displayname,
"password": password,
"admin": admin,
"mac": want_mac,
Expand Down