Remove the 'password_hash' from the Users Admin API endpoint response dictionary (#11576)
This commit is contained in:
parent
904bb04409
commit
18862f20b5
|
@ -0,0 +1 @@
|
||||||
|
Remove the `"password_hash"` field from the response dictionaries of the [Users Admin API](https://matrix-org.github.io/synapse/latest/admin_api/user_admin_api.html).
|
|
@ -15,9 +15,10 @@ server admin: [Admin API](../usage/administration/admin_api)
|
||||||
|
|
||||||
It returns a JSON body like the following:
|
It returns a JSON body like the following:
|
||||||
|
|
||||||
```json
|
```jsonc
|
||||||
{
|
{
|
||||||
"displayname": "User",
|
"name": "@user:example.com",
|
||||||
|
"displayname": "User", // can be null if not set
|
||||||
"threepids": [
|
"threepids": [
|
||||||
{
|
{
|
||||||
"medium": "email",
|
"medium": "email",
|
||||||
|
@ -32,11 +33,11 @@ It returns a JSON body like the following:
|
||||||
"validated_at": 1586458409743
|
"validated_at": 1586458409743
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"avatar_url": "<avatar_url>",
|
"avatar_url": "<avatar_url>", // can be null if not set
|
||||||
|
"is_guest": 0,
|
||||||
"admin": 0,
|
"admin": 0,
|
||||||
"deactivated": 0,
|
"deactivated": 0,
|
||||||
"shadow_banned": 0,
|
"shadow_banned": 0,
|
||||||
"password_hash": "$2b$12$p9B4GkqYdRTPGD",
|
|
||||||
"creation_ts": 1560432506,
|
"creation_ts": 1560432506,
|
||||||
"appservice_id": null,
|
"appservice_id": null,
|
||||||
"consent_server_notice_sent": null,
|
"consent_server_notice_sent": null,
|
||||||
|
|
|
@ -55,8 +55,33 @@ class AdminHandler:
|
||||||
|
|
||||||
async def get_user(self, user: UserID) -> Optional[JsonDict]:
|
async def get_user(self, user: UserID) -> Optional[JsonDict]:
|
||||||
"""Function to get user details"""
|
"""Function to get user details"""
|
||||||
ret = await self.store.get_user_by_id(user.to_string())
|
user_info_dict = await self.store.get_user_by_id(user.to_string())
|
||||||
if ret:
|
if user_info_dict is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Restrict returned information to a known set of fields. This prevents additional
|
||||||
|
# fields added to get_user_by_id from modifying Synapse's external API surface.
|
||||||
|
user_info_to_return = {
|
||||||
|
"name",
|
||||||
|
"admin",
|
||||||
|
"deactivated",
|
||||||
|
"shadow_banned",
|
||||||
|
"creation_ts",
|
||||||
|
"appservice_id",
|
||||||
|
"consent_server_notice_sent",
|
||||||
|
"consent_version",
|
||||||
|
"user_type",
|
||||||
|
"is_guest",
|
||||||
|
}
|
||||||
|
|
||||||
|
# Restrict returned keys to a known set.
|
||||||
|
user_info_dict = {
|
||||||
|
key: value
|
||||||
|
for key, value in user_info_dict.items()
|
||||||
|
if key in user_info_to_return
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add additional user metadata
|
||||||
profile = await self.store.get_profileinfo(user.localpart)
|
profile = await self.store.get_profileinfo(user.localpart)
|
||||||
threepids = await self.store.user_get_threepids(user.to_string())
|
threepids = await self.store.user_get_threepids(user.to_string())
|
||||||
external_ids = [
|
external_ids = [
|
||||||
|
@ -65,11 +90,12 @@ class AdminHandler:
|
||||||
user.to_string()
|
user.to_string()
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
ret["displayname"] = profile.display_name
|
user_info_dict["displayname"] = profile.display_name
|
||||||
ret["avatar_url"] = profile.avatar_url
|
user_info_dict["avatar_url"] = profile.avatar_url
|
||||||
ret["threepids"] = threepids
|
user_info_dict["threepids"] = threepids
|
||||||
ret["external_ids"] = external_ids
|
user_info_dict["external_ids"] = external_ids
|
||||||
return ret
|
|
||||||
|
return user_info_dict
|
||||||
|
|
||||||
async def export_user_data(self, user_id: str, writer: "ExfiltrationWriter") -> Any:
|
async def export_user_data(self, user_id: str, writer: "ExfiltrationWriter") -> Any:
|
||||||
"""Write all data we have on the user to the given writer.
|
"""Write all data we have on the user to the given writer.
|
||||||
|
|
|
@ -173,12 +173,11 @@ class UserRestServletV2(RestServlet):
|
||||||
if not self.hs.is_mine(target_user):
|
if not self.hs.is_mine(target_user):
|
||||||
raise SynapseError(HTTPStatus.BAD_REQUEST, "Can only look up local users")
|
raise SynapseError(HTTPStatus.BAD_REQUEST, "Can only look up local users")
|
||||||
|
|
||||||
ret = await self.admin_handler.get_user(target_user)
|
user_info_dict = await self.admin_handler.get_user(target_user)
|
||||||
|
if not user_info_dict:
|
||||||
if not ret:
|
|
||||||
raise NotFoundError("User not found")
|
raise NotFoundError("User not found")
|
||||||
|
|
||||||
return HTTPStatus.OK, ret
|
return HTTPStatus.OK, user_info_dict
|
||||||
|
|
||||||
async def on_PUT(
|
async def on_PUT(
|
||||||
self, request: SynapseRequest, user_id: str
|
self, request: SynapseRequest, user_id: str
|
||||||
|
@ -399,10 +398,10 @@ class UserRestServletV2(RestServlet):
|
||||||
target_user, requester, body["avatar_url"], True
|
target_user, requester, body["avatar_url"], True
|
||||||
)
|
)
|
||||||
|
|
||||||
user = await self.admin_handler.get_user(target_user)
|
user_info_dict = await self.admin_handler.get_user(target_user)
|
||||||
assert user is not None
|
assert user_info_dict is not None
|
||||||
|
|
||||||
return 201, user
|
return HTTPStatus.CREATED, user_info_dict
|
||||||
|
|
||||||
|
|
||||||
class UserRegisterServlet(RestServlet):
|
class UserRegisterServlet(RestServlet):
|
||||||
|
|
|
@ -1181,6 +1181,7 @@ class UserRestTestCase(unittest.HomeserverTestCase):
|
||||||
self.other_user, device_id=None, valid_until_ms=None
|
self.other_user, device_id=None, valid_until_ms=None
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
self.url_prefix = "/_synapse/admin/v2/users/%s"
|
self.url_prefix = "/_synapse/admin/v2/users/%s"
|
||||||
self.url_other_user = self.url_prefix % self.other_user
|
self.url_other_user = self.url_prefix % self.other_user
|
||||||
|
|
||||||
|
@ -1188,7 +1189,7 @@ class UserRestTestCase(unittest.HomeserverTestCase):
|
||||||
"""
|
"""
|
||||||
If the user is not a server admin, an error is returned.
|
If the user is not a server admin, an error is returned.
|
||||||
"""
|
"""
|
||||||
url = "/_synapse/admin/v2/users/@bob:test"
|
url = self.url_prefix % "@bob:test"
|
||||||
|
|
||||||
channel = self.make_request(
|
channel = self.make_request(
|
||||||
"GET",
|
"GET",
|
||||||
|
@ -1216,7 +1217,7 @@ class UserRestTestCase(unittest.HomeserverTestCase):
|
||||||
|
|
||||||
channel = self.make_request(
|
channel = self.make_request(
|
||||||
"GET",
|
"GET",
|
||||||
"/_synapse/admin/v2/users/@unknown_person:test",
|
self.url_prefix % "@unknown_person:test",
|
||||||
access_token=self.admin_user_tok,
|
access_token=self.admin_user_tok,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1337,7 +1338,7 @@ class UserRestTestCase(unittest.HomeserverTestCase):
|
||||||
"""
|
"""
|
||||||
Check that a new admin user is created successfully.
|
Check that a new admin user is created successfully.
|
||||||
"""
|
"""
|
||||||
url = "/_synapse/admin/v2/users/@bob:test"
|
url = self.url_prefix % "@bob:test"
|
||||||
|
|
||||||
# Create user (server admin)
|
# Create user (server admin)
|
||||||
body = {
|
body = {
|
||||||
|
@ -1386,7 +1387,7 @@ class UserRestTestCase(unittest.HomeserverTestCase):
|
||||||
"""
|
"""
|
||||||
Check that a new regular user is created successfully.
|
Check that a new regular user is created successfully.
|
||||||
"""
|
"""
|
||||||
url = "/_synapse/admin/v2/users/@bob:test"
|
url = self.url_prefix % "@bob:test"
|
||||||
|
|
||||||
# Create user
|
# Create user
|
||||||
body = {
|
body = {
|
||||||
|
@ -1478,7 +1479,7 @@ class UserRestTestCase(unittest.HomeserverTestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Register new user with admin API
|
# Register new user with admin API
|
||||||
url = "/_synapse/admin/v2/users/@bob:test"
|
url = self.url_prefix % "@bob:test"
|
||||||
|
|
||||||
# Create user
|
# Create user
|
||||||
channel = self.make_request(
|
channel = self.make_request(
|
||||||
|
@ -1515,7 +1516,7 @@ class UserRestTestCase(unittest.HomeserverTestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Register new user with admin API
|
# Register new user with admin API
|
||||||
url = "/_synapse/admin/v2/users/@bob:test"
|
url = self.url_prefix % "@bob:test"
|
||||||
|
|
||||||
# Create user
|
# Create user
|
||||||
channel = self.make_request(
|
channel = self.make_request(
|
||||||
|
@ -1545,7 +1546,7 @@ class UserRestTestCase(unittest.HomeserverTestCase):
|
||||||
Check that a new regular user is created successfully and
|
Check that a new regular user is created successfully and
|
||||||
got an email pusher.
|
got an email pusher.
|
||||||
"""
|
"""
|
||||||
url = "/_synapse/admin/v2/users/@bob:test"
|
url = self.url_prefix % "@bob:test"
|
||||||
|
|
||||||
# Create user
|
# Create user
|
||||||
body = {
|
body = {
|
||||||
|
@ -1588,7 +1589,7 @@ class UserRestTestCase(unittest.HomeserverTestCase):
|
||||||
Check that a new regular user is created successfully and
|
Check that a new regular user is created successfully and
|
||||||
got not an email pusher.
|
got not an email pusher.
|
||||||
"""
|
"""
|
||||||
url = "/_synapse/admin/v2/users/@bob:test"
|
url = self.url_prefix % "@bob:test"
|
||||||
|
|
||||||
# Create user
|
# Create user
|
||||||
body = {
|
body = {
|
||||||
|
@ -2085,10 +2086,13 @@ class UserRestTestCase(unittest.HomeserverTestCase):
|
||||||
self.assertEqual(HTTPStatus.OK, channel.code, msg=channel.json_body)
|
self.assertEqual(HTTPStatus.OK, channel.code, msg=channel.json_body)
|
||||||
self.assertEqual("@user:test", channel.json_body["name"])
|
self.assertEqual("@user:test", channel.json_body["name"])
|
||||||
self.assertTrue(channel.json_body["deactivated"])
|
self.assertTrue(channel.json_body["deactivated"])
|
||||||
self.assertIsNone(channel.json_body["password_hash"])
|
|
||||||
self.assertEqual(0, len(channel.json_body["threepids"]))
|
self.assertEqual(0, len(channel.json_body["threepids"]))
|
||||||
self.assertEqual("mxc://servername/mediaid", channel.json_body["avatar_url"])
|
self.assertEqual("mxc://servername/mediaid", channel.json_body["avatar_url"])
|
||||||
self.assertEqual("User", channel.json_body["displayname"])
|
self.assertEqual("User", channel.json_body["displayname"])
|
||||||
|
|
||||||
|
# This key was removed intentionally. Ensure it is not accidentally re-included.
|
||||||
|
self.assertNotIn("password_hash", channel.json_body)
|
||||||
|
|
||||||
# the user is deactivated, the threepid will be deleted
|
# the user is deactivated, the threepid will be deleted
|
||||||
|
|
||||||
# Get user
|
# Get user
|
||||||
|
@ -2101,11 +2105,13 @@ class UserRestTestCase(unittest.HomeserverTestCase):
|
||||||
self.assertEqual(HTTPStatus.OK, channel.code, msg=channel.json_body)
|
self.assertEqual(HTTPStatus.OK, channel.code, msg=channel.json_body)
|
||||||
self.assertEqual("@user:test", channel.json_body["name"])
|
self.assertEqual("@user:test", channel.json_body["name"])
|
||||||
self.assertTrue(channel.json_body["deactivated"])
|
self.assertTrue(channel.json_body["deactivated"])
|
||||||
self.assertIsNone(channel.json_body["password_hash"])
|
|
||||||
self.assertEqual(0, len(channel.json_body["threepids"]))
|
self.assertEqual(0, len(channel.json_body["threepids"]))
|
||||||
self.assertEqual("mxc://servername/mediaid", channel.json_body["avatar_url"])
|
self.assertEqual("mxc://servername/mediaid", channel.json_body["avatar_url"])
|
||||||
self.assertEqual("User", channel.json_body["displayname"])
|
self.assertEqual("User", channel.json_body["displayname"])
|
||||||
|
|
||||||
|
# This key was removed intentionally. Ensure it is not accidentally re-included.
|
||||||
|
self.assertNotIn("password_hash", channel.json_body)
|
||||||
|
|
||||||
@override_config({"user_directory": {"enabled": True, "search_all_users": True}})
|
@override_config({"user_directory": {"enabled": True, "search_all_users": True}})
|
||||||
def test_change_name_deactivate_user_user_directory(self):
|
def test_change_name_deactivate_user_user_directory(self):
|
||||||
"""
|
"""
|
||||||
|
@ -2177,9 +2183,11 @@ class UserRestTestCase(unittest.HomeserverTestCase):
|
||||||
self.assertEqual(HTTPStatus.OK, channel.code, msg=channel.json_body)
|
self.assertEqual(HTTPStatus.OK, channel.code, msg=channel.json_body)
|
||||||
self.assertEqual("@user:test", channel.json_body["name"])
|
self.assertEqual("@user:test", channel.json_body["name"])
|
||||||
self.assertFalse(channel.json_body["deactivated"])
|
self.assertFalse(channel.json_body["deactivated"])
|
||||||
self.assertIsNotNone(channel.json_body["password_hash"])
|
|
||||||
self._is_erased("@user:test", False)
|
self._is_erased("@user:test", False)
|
||||||
|
|
||||||
|
# This key was removed intentionally. Ensure it is not accidentally re-included.
|
||||||
|
self.assertNotIn("password_hash", channel.json_body)
|
||||||
|
|
||||||
@override_config({"password_config": {"localdb_enabled": False}})
|
@override_config({"password_config": {"localdb_enabled": False}})
|
||||||
def test_reactivate_user_localdb_disabled(self):
|
def test_reactivate_user_localdb_disabled(self):
|
||||||
"""
|
"""
|
||||||
|
@ -2209,9 +2217,11 @@ class UserRestTestCase(unittest.HomeserverTestCase):
|
||||||
self.assertEqual(HTTPStatus.OK, channel.code, msg=channel.json_body)
|
self.assertEqual(HTTPStatus.OK, channel.code, msg=channel.json_body)
|
||||||
self.assertEqual("@user:test", channel.json_body["name"])
|
self.assertEqual("@user:test", channel.json_body["name"])
|
||||||
self.assertFalse(channel.json_body["deactivated"])
|
self.assertFalse(channel.json_body["deactivated"])
|
||||||
self.assertIsNone(channel.json_body["password_hash"])
|
|
||||||
self._is_erased("@user:test", False)
|
self._is_erased("@user:test", False)
|
||||||
|
|
||||||
|
# This key was removed intentionally. Ensure it is not accidentally re-included.
|
||||||
|
self.assertNotIn("password_hash", channel.json_body)
|
||||||
|
|
||||||
@override_config({"password_config": {"enabled": False}})
|
@override_config({"password_config": {"enabled": False}})
|
||||||
def test_reactivate_user_password_disabled(self):
|
def test_reactivate_user_password_disabled(self):
|
||||||
"""
|
"""
|
||||||
|
@ -2241,9 +2251,11 @@ class UserRestTestCase(unittest.HomeserverTestCase):
|
||||||
self.assertEqual(HTTPStatus.OK, channel.code, msg=channel.json_body)
|
self.assertEqual(HTTPStatus.OK, channel.code, msg=channel.json_body)
|
||||||
self.assertEqual("@user:test", channel.json_body["name"])
|
self.assertEqual("@user:test", channel.json_body["name"])
|
||||||
self.assertFalse(channel.json_body["deactivated"])
|
self.assertFalse(channel.json_body["deactivated"])
|
||||||
self.assertIsNone(channel.json_body["password_hash"])
|
|
||||||
self._is_erased("@user:test", False)
|
self._is_erased("@user:test", False)
|
||||||
|
|
||||||
|
# This key was removed intentionally. Ensure it is not accidentally re-included.
|
||||||
|
self.assertNotIn("password_hash", channel.json_body)
|
||||||
|
|
||||||
def test_set_user_as_admin(self):
|
def test_set_user_as_admin(self):
|
||||||
"""
|
"""
|
||||||
Test setting the admin flag on a user.
|
Test setting the admin flag on a user.
|
||||||
|
@ -2328,7 +2340,7 @@ class UserRestTestCase(unittest.HomeserverTestCase):
|
||||||
Ensure an account can't accidentally be deactivated by using a str value
|
Ensure an account can't accidentally be deactivated by using a str value
|
||||||
for the deactivated body parameter
|
for the deactivated body parameter
|
||||||
"""
|
"""
|
||||||
url = "/_synapse/admin/v2/users/@bob:test"
|
url = self.url_prefix % "@bob:test"
|
||||||
|
|
||||||
# Create user
|
# Create user
|
||||||
channel = self.make_request(
|
channel = self.make_request(
|
||||||
|
@ -2392,18 +2404,20 @@ class UserRestTestCase(unittest.HomeserverTestCase):
|
||||||
# Deactivate the user.
|
# Deactivate the user.
|
||||||
channel = self.make_request(
|
channel = self.make_request(
|
||||||
"PUT",
|
"PUT",
|
||||||
"/_synapse/admin/v2/users/%s" % urllib.parse.quote(user_id),
|
self.url_prefix % urllib.parse.quote(user_id),
|
||||||
access_token=self.admin_user_tok,
|
access_token=self.admin_user_tok,
|
||||||
content={"deactivated": True},
|
content={"deactivated": True},
|
||||||
)
|
)
|
||||||
self.assertEqual(HTTPStatus.OK, channel.code, msg=channel.json_body)
|
self.assertEqual(HTTPStatus.OK, channel.code, msg=channel.json_body)
|
||||||
self.assertTrue(channel.json_body["deactivated"])
|
self.assertTrue(channel.json_body["deactivated"])
|
||||||
self.assertIsNone(channel.json_body["password_hash"])
|
|
||||||
self._is_erased(user_id, False)
|
self._is_erased(user_id, False)
|
||||||
d = self.store.mark_user_erased(user_id)
|
d = self.store.mark_user_erased(user_id)
|
||||||
self.assertIsNone(self.get_success(d))
|
self.assertIsNone(self.get_success(d))
|
||||||
self._is_erased(user_id, True)
|
self._is_erased(user_id, True)
|
||||||
|
|
||||||
|
# This key was removed intentionally. Ensure it is not accidentally re-included.
|
||||||
|
self.assertNotIn("password_hash", channel.json_body)
|
||||||
|
|
||||||
def _check_fields(self, content: JsonDict):
|
def _check_fields(self, content: JsonDict):
|
||||||
"""Checks that the expected user attributes are present in content
|
"""Checks that the expected user attributes are present in content
|
||||||
|
|
||||||
|
@ -2416,13 +2430,15 @@ class UserRestTestCase(unittest.HomeserverTestCase):
|
||||||
self.assertIn("admin", content)
|
self.assertIn("admin", content)
|
||||||
self.assertIn("deactivated", content)
|
self.assertIn("deactivated", content)
|
||||||
self.assertIn("shadow_banned", content)
|
self.assertIn("shadow_banned", content)
|
||||||
self.assertIn("password_hash", content)
|
|
||||||
self.assertIn("creation_ts", content)
|
self.assertIn("creation_ts", content)
|
||||||
self.assertIn("appservice_id", content)
|
self.assertIn("appservice_id", content)
|
||||||
self.assertIn("consent_server_notice_sent", content)
|
self.assertIn("consent_server_notice_sent", content)
|
||||||
self.assertIn("consent_version", content)
|
self.assertIn("consent_version", content)
|
||||||
self.assertIn("external_ids", content)
|
self.assertIn("external_ids", content)
|
||||||
|
|
||||||
|
# This key was removed intentionally. Ensure it is not accidentally re-included.
|
||||||
|
self.assertNotIn("password_hash", content)
|
||||||
|
|
||||||
|
|
||||||
class UserMembershipRestTestCase(unittest.HomeserverTestCase):
|
class UserMembershipRestTestCase(unittest.HomeserverTestCase):
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue