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

Commit

Permalink
synapse.api.auth.Auth cleanup: make permission-related methods use …
Browse files Browse the repository at this point in the history
…`Requester` instead of the `UserID` (#13024)

Part of #13019

This changes all the permission-related methods to rely on the Requester instead of the UserID. This is a first step towards enabling scoped access tokens at some point, since I expect the Requester to have scope-related informations in it.

It also changes methods which figure out the user/device/appservice out of the access token to return a Requester instead of something else. This avoids having store-related objects in the methods signatures.
  • Loading branch information
sandhose committed Aug 22, 2022
1 parent 94375f7 commit 3dd175b
Show file tree
Hide file tree
Showing 26 changed files with 203 additions and 208 deletions.
1 change: 1 addition & 0 deletions changelog.d/13024.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Refactor methods in `synapse.api.auth.Auth` to use `Requester` objects everywhere instead of user IDs.
202 changes: 95 additions & 107 deletions synapse/api/auth.py

Large diffs are not rendered by default.

17 changes: 11 additions & 6 deletions synapse/handlers/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ async def validate_user_via_ui_auth(
that it isn't stolen by re-authenticating them.
Args:
requester: The user, as given by the access token
requester: The user making the request, according to the access token.
request: The request sent by the client.
Expand Down Expand Up @@ -1435,20 +1435,25 @@ async def delete_access_token(self, access_token: str) -> None:
access_token: access token to be deleted
"""
user_info = await self.auth.get_user_by_access_token(access_token)
token = await self.store.get_user_by_access_token(access_token)
if not token:
# At this point, the token should already have been fetched once by
# the caller, so this should not happen, unless of a race condition
# between two delete requests
raise SynapseError(HTTPStatus.UNAUTHORIZED, "Unrecognised access token")
await self.store.delete_access_token(access_token)

# see if any modules want to know about this
await self.password_auth_provider.on_logged_out(
user_id=user_info.user_id,
device_id=user_info.device_id,
user_id=token.user_id,
device_id=token.device_id,
access_token=access_token,
)

# delete pushers associated with this access token
if user_info.token_id is not None:
if token.token_id is not None:
await self.hs.get_pusherpool().remove_pushers_by_access_token(
user_info.user_id, (user_info.token_id,)
token.user_id, (token.token_id,)
)

async def delete_access_tokens_for_user(
Expand Down
24 changes: 11 additions & 13 deletions synapse/handlers/directory.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
from synapse.appservice import ApplicationService
from synapse.module_api import NOT_SPAM
from synapse.storage.databases.main.directory import RoomAliasMapping
from synapse.types import JsonDict, Requester, RoomAlias, UserID, get_domain_from_id
from synapse.types import JsonDict, Requester, RoomAlias, get_domain_from_id

if TYPE_CHECKING:
from synapse.server import HomeServer
Expand Down Expand Up @@ -133,7 +133,7 @@ async def create_association(
else:
# Server admins are not subject to the same constraints as normal
# users when creating an alias (e.g. being in the room).
is_admin = await self.auth.is_server_admin(requester.user)
is_admin = await self.auth.is_server_admin(requester)

if (self.require_membership and check_membership) and not is_admin:
rooms_for_user = await self.store.get_rooms_for_user(user_id)
Expand Down Expand Up @@ -197,7 +197,7 @@ async def delete_association(
user_id = requester.user.to_string()

try:
can_delete = await self._user_can_delete_alias(room_alias, user_id)
can_delete = await self._user_can_delete_alias(room_alias, requester)
except StoreError as e:
if e.code == 404:
raise NotFoundError("Unknown room alias")
Expand Down Expand Up @@ -400,7 +400,9 @@ def can_modify_alias(self, alias: RoomAlias, user_id: Optional[str] = None) -> b
# either no interested services, or no service with an exclusive lock
return True

async def _user_can_delete_alias(self, alias: RoomAlias, user_id: str) -> bool:
async def _user_can_delete_alias(
self, alias: RoomAlias, requester: Requester
) -> bool:
"""Determine whether a user can delete an alias.
One of the following must be true:
Expand All @@ -413,7 +415,7 @@ async def _user_can_delete_alias(self, alias: RoomAlias, user_id: str) -> bool:
"""
creator = await self.store.get_room_alias_creator(alias.to_string())

if creator == user_id:
if creator == requester.user.to_string():
return True

# Resolve the alias to the corresponding room.
Expand All @@ -422,9 +424,7 @@ async def _user_can_delete_alias(self, alias: RoomAlias, user_id: str) -> bool:
if not room_id:
return False

return await self.auth.check_can_change_room_list(
room_id, UserID.from_string(user_id)
)
return await self.auth.check_can_change_room_list(room_id, requester)

async def edit_published_room_list(
self, requester: Requester, room_id: str, visibility: str
Expand Down Expand Up @@ -463,7 +463,7 @@ async def edit_published_room_list(
raise SynapseError(400, "Unknown room")

can_change_room_list = await self.auth.check_can_change_room_list(
room_id, requester.user
room_id, requester
)
if not can_change_room_list:
raise AuthError(
Expand Down Expand Up @@ -528,10 +528,8 @@ async def get_aliases_for_room(
Get a list of the aliases that currently point to this room on this server
"""
# allow access to server admins and current members of the room
is_admin = await self.auth.is_server_admin(requester.user)
is_admin = await self.auth.is_server_admin(requester)
if not is_admin:
await self.auth.check_user_in_room_or_world_readable(
room_id, requester.user.to_string()
)
await self.auth.check_user_in_room_or_world_readable(room_id, requester)

return await self.store.get_aliases_for_room(room_id)
6 changes: 3 additions & 3 deletions synapse/handlers/initial_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,18 +309,18 @@ async def room_initial_sync(
if blocked:
raise SynapseError(403, "This room has been blocked on this server")

user_id = requester.user.to_string()

(
membership,
member_event_id,
) = await self.auth.check_user_in_room_or_world_readable(
room_id,
user_id,
requester,
allow_departed_users=True,
)
is_peeking = member_event_id is None

user_id = requester.user.to_string()

if membership == Membership.JOIN:
result = await self._room_initial_sync_joined(
user_id, room_id, pagin_config, membership, is_peeking
Expand Down
23 changes: 12 additions & 11 deletions synapse/handlers/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,15 +104,15 @@ def __init__(self, hs: "HomeServer"):

async def get_room_data(
self,
user_id: str,
requester: Requester,
room_id: str,
event_type: str,
state_key: str,
) -> Optional[EventBase]:
"""Get data from a room.
Args:
user_id
requester: The user who did the request.
room_id
event_type
state_key
Expand All @@ -125,7 +125,7 @@ async def get_room_data(
membership,
membership_event_id,
) = await self.auth.check_user_in_room_or_world_readable(
room_id, user_id, allow_departed_users=True
room_id, requester, allow_departed_users=True
)

if membership == Membership.JOIN:
Expand Down Expand Up @@ -161,11 +161,10 @@ async def get_room_data(

async def get_state_events(
self,
user_id: str,
requester: Requester,
room_id: str,
state_filter: Optional[StateFilter] = None,
at_token: Optional[StreamToken] = None,
is_guest: bool = False,
) -> List[dict]:
"""Retrieve all state events for a given room. If the user is
joined to the room then return the current state. If the user has
Expand All @@ -174,14 +173,13 @@ async def get_state_events(
visible.
Args:
user_id: The user requesting state events.
requester: The user requesting state events.
room_id: The room ID to get all state events from.
state_filter: The state filter used to fetch state from the database.
at_token: the stream token of the at which we are requesting
the stats. If the user is not allowed to view the state as of that
stream token, we raise a 403 SynapseError. If None, returns the current
state based on the current_state_events table.
is_guest: whether this user is a guest
Returns:
A list of dicts representing state events. [{}, {}, {}]
Raises:
Expand All @@ -191,6 +189,7 @@ async def get_state_events(
members of this room.
"""
state_filter = state_filter or StateFilter.all()
user_id = requester.user.to_string()

if at_token:
last_event_id = (
Expand Down Expand Up @@ -223,7 +222,7 @@ async def get_state_events(
membership,
membership_event_id,
) = await self.auth.check_user_in_room_or_world_readable(
room_id, user_id, allow_departed_users=True
room_id, requester, allow_departed_users=True
)

if membership == Membership.JOIN:
Expand Down Expand Up @@ -317,12 +316,11 @@ async def get_joined_members(self, requester: Requester, room_id: str) -> dict:
Returns:
A dict of user_id to profile info
"""
user_id = requester.user.to_string()
if not requester.app_service:
# We check AS auth after fetching the room membership, as it
# requires us to pull out all joined members anyway.
membership, _ = await self.auth.check_user_in_room_or_world_readable(
room_id, user_id, allow_departed_users=True
room_id, requester, allow_departed_users=True
)
if membership != Membership.JOIN:
raise SynapseError(
Expand All @@ -340,7 +338,10 @@ async def get_joined_members(self, requester: Requester, room_id: str) -> dict:
# If this is an AS, double check that they are allowed to see the members.
# This can either be because the AS user is in the room or because there
# is a user in the room that the AS is "interested in"
if requester.app_service and user_id not in users_with_profile:
if (
requester.app_service
and requester.user.to_string() not in users_with_profile
):
for uid in users_with_profile:
if requester.app_service.is_interested_in_user(uid):
break
Expand Down
2 changes: 1 addition & 1 deletion synapse/handlers/pagination.py
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,7 @@ async def get_messages(
membership,
member_event_id,
) = await self.auth.check_user_in_room_or_world_readable(
room_id, user_id, allow_departed_users=True
room_id, requester, allow_departed_users=True
)

if pagin_config.direction == "b":
Expand Down
15 changes: 9 additions & 6 deletions synapse/handlers/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,13 @@
JoinRules,
LoginType,
)
from synapse.api.errors import AuthError, Codes, ConsentNotGivenError, SynapseError
from synapse.api.errors import (
AuthError,
Codes,
ConsentNotGivenError,
InvalidClientTokenError,
SynapseError,
)
from synapse.appservice import ApplicationService
from synapse.config.server import is_threepid_reserved
from synapse.http.servlet import assert_params_in_dict
Expand Down Expand Up @@ -180,10 +186,7 @@ async def check_username(
)
if guest_access_token:
user_data = await self.auth.get_user_by_access_token(guest_access_token)
if (
not user_data.is_guest
or UserID.from_string(user_data.user_id).localpart != localpart
):
if not user_data.is_guest or user_data.user.localpart != localpart:
raise AuthError(
403,
"Cannot register taken user ID without valid guest "
Expand Down Expand Up @@ -618,7 +621,7 @@ async def appservice_register(self, user_localpart: str, as_token: str) -> str:
user_id = user.to_string()
service = self.store.get_app_service_by_token(as_token)
if not service:
raise AuthError(403, "Invalid application service token.")
raise InvalidClientTokenError()
if not service.is_interested_in_user(user_id):
raise SynapseError(
400,
Expand Down
2 changes: 1 addition & 1 deletion synapse/handlers/relations.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ async def get_relations(

# TODO Properly handle a user leaving a room.
(_, member_event_id) = await self._auth.check_user_in_room_or_world_readable(
room_id, user_id, allow_departed_users=True
room_id, requester, allow_departed_users=True
)

# This gets the original event and checks that a) the event exists and
Expand Down
4 changes: 2 additions & 2 deletions synapse/handlers/room.py
Original file line number Diff line number Diff line change
Expand Up @@ -721,7 +721,7 @@ async def create_room(
# allow the server notices mxid to create rooms
is_requester_admin = True
else:
is_requester_admin = await self.auth.is_server_admin(requester.user)
is_requester_admin = await self.auth.is_server_admin(requester)

# Let the third party rules modify the room creation config if needed, or abort
# the room creation entirely with an exception.
Expand Down Expand Up @@ -1279,7 +1279,7 @@ async def get_event_context(
"""
user = requester.user
if use_admin_priviledge:
await assert_user_is_admin(self.auth, requester.user)
await assert_user_is_admin(self.auth, requester)

before_limit = math.floor(limit / 2.0)
after_limit = limit - before_limit
Expand Down
10 changes: 5 additions & 5 deletions synapse/handlers/room_member.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ async def _remote_join(
"""Try and join a room that this server is not in
Args:
requester
requester: The user making the request, according to the access token.
remote_room_hosts: List of servers that can be used to join via.
room_id: Room that we are trying to join
user: User who is trying to join
Expand Down Expand Up @@ -744,7 +744,7 @@ async def update_membership_locked(
is_requester_admin = True

else:
is_requester_admin = await self.auth.is_server_admin(requester.user)
is_requester_admin = await self.auth.is_server_admin(requester)

if not is_requester_admin:
if self.config.server.block_non_admin_invites:
Expand Down Expand Up @@ -868,7 +868,7 @@ async def update_membership_locked(
bypass_spam_checker = True

else:
bypass_spam_checker = await self.auth.is_server_admin(requester.user)
bypass_spam_checker = await self.auth.is_server_admin(requester)

inviter = await self._get_inviter(target.to_string(), room_id)
if (
Expand Down Expand Up @@ -1410,7 +1410,7 @@ async def do_3pid_invite(
ShadowBanError if the requester has been shadow-banned.
"""
if self.config.server.block_non_admin_invites:
is_requester_admin = await self.auth.is_server_admin(requester.user)
is_requester_admin = await self.auth.is_server_admin(requester)
if not is_requester_admin:
raise SynapseError(
403, "Invites have been disabled on this server", Codes.FORBIDDEN
Expand Down Expand Up @@ -1693,7 +1693,7 @@ async def _remote_join(
check_complexity
and self.hs.config.server.limit_remote_rooms.admins_can_join
):
check_complexity = not await self.auth.is_server_admin(user)
check_complexity = not await self.store.is_server_admin(user)

if check_complexity:
# Fetch the room complexity
Expand Down
Loading

0 comments on commit 3dd175b

Please sign in to comment.