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

Move methods involving event authentication to EventAuthHandler. #10268

Merged
merged 9 commits into from
Jul 1, 2021
1 change: 1 addition & 0 deletions changelog.d/10268.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Move event authentication methods from `Auth` to `EventAuthHandler`.
75 changes: 2 additions & 73 deletions synapse/api/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union
from typing import TYPE_CHECKING, Optional, Tuple

import pymacaroons
from netaddr import IPAddress
Expand All @@ -28,34 +28,22 @@
InvalidClientTokenError,
MissingClientTokenError,
)
from synapse.api.room_versions import KNOWN_ROOM_VERSIONS
from synapse.appservice import ApplicationService
from synapse.events import EventBase
from synapse.events.builder import EventBuilder
from synapse.http import get_request_user_agent
from synapse.http.site import SynapseRequest
from synapse.logging import opentracing as opentracing
from synapse.storage.databases.main.registration import TokenLookupResult
from synapse.types import Requester, StateMap, UserID, create_requester
from synapse.util.caches.lrucache import LruCache
from synapse.util.macaroons import get_value_from_macaroon, satisfy_expiry
from synapse.util.metrics import Measure

if TYPE_CHECKING:
from synapse.server import HomeServer

logger = logging.getLogger(__name__)


AuthEventTypes = (
EventTypes.Create,
EventTypes.Member,
EventTypes.PowerLevels,
EventTypes.JoinRules,
EventTypes.RoomHistoryVisibility,
EventTypes.ThirdPartyInvite,
)

# guests always get this device id.
GUEST_DEVICE_ID = "guest_device"

Expand All @@ -66,9 +54,7 @@ class _InvalidMacaroonException(Exception):

class Auth:
"""
FIXME: This class contains a mix of functions for authenticating users
of our client-server API and authenticating events added to room graphs.
The latter should be moved to synapse.handlers.event_auth.EventAuthHandler.
This class contains functions for authenticating users of our client-server API.
"""

def __init__(self, hs: "HomeServer"):
Expand All @@ -90,18 +76,6 @@ def __init__(self, hs: "HomeServer"):
self._macaroon_secret_key = hs.config.macaroon_secret_key
self._force_tracing_for_users = hs.config.tracing.force_tracing_for_users

async def check_from_context(
self, room_version: str, event, context, do_sig_check=True
) -> None:
auth_event_ids = event.auth_event_ids()
auth_events_by_id = await self.store.get_events(auth_event_ids)
auth_events = {(e.type, e.state_key): e for e in auth_events_by_id.values()}

room_version_obj = KNOWN_ROOM_VERSIONS[room_version]
event_auth.check(
room_version_obj, event, auth_events=auth_events, do_sig_check=do_sig_check
)

async def check_user_in_room(
self,
room_id: str,
Expand Down Expand Up @@ -152,13 +126,6 @@ async def check_user_in_room(

raise AuthError(403, "User %s not in room %s" % (user_id, room_id))

async def check_host_in_room(self, room_id: str, host: str) -> bool:
with Measure(self.clock, "check_host_in_room"):
return await self.store.is_host_joined(room_id, host)

def get_public_keys(self, invite_event: EventBase) -> List[Dict[str, Any]]:
return event_auth.get_public_keys(invite_event)

async def get_user_by_req(
self,
request: SynapseRequest,
Expand Down Expand Up @@ -489,44 +456,6 @@ async def is_server_admin(self, user: UserID) -> bool:
"""
return await self.store.is_server_admin(user)

def compute_auth_events(
self,
event: Union[EventBase, EventBuilder],
current_state_ids: StateMap[str],
for_verification: bool = False,
) -> List[str]:
"""Given an event and current state return the list of event IDs used
to auth an event.

If `for_verification` is False then only return auth events that
should be added to the event's `auth_events`.

Returns:
List of event IDs.
"""

if event.type == EventTypes.Create:
return []

# Currently we ignore the `for_verification` flag even though there are
# some situations where we can drop particular auth events when adding
# to the event's `auth_events` (e.g. joins pointing to previous joins
# when room is publicly joinable). Dropping event IDs has the
# advantage that the auth chain for the room grows slower, but we use
# the auth chain in state resolution v2 to order events, which means
# care must be taken if dropping events to ensure that it doesn't
# introduce undesirable "state reset" behaviour.
#
# All of which sounds a bit tricky so we don't bother for now.

auth_ids = []
for etype, state_key in event_auth.auth_types_for_event(event):
auth_ev_id = current_state_ids.get((etype, state_key))
if auth_ev_id:
auth_ids.append(auth_ev_id)

return auth_ids

async def check_can_change_room_list(self, room_id: str, user: UserID) -> bool:
"""Determine whether the user is allowed to edit the room's entry in the
published room list.
Expand Down
12 changes: 7 additions & 5 deletions synapse/events/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
from synapse.util.stringutils import random_string

if TYPE_CHECKING:
from synapse.api.auth import Auth
from synapse.handlers.event_auth import EventAuthHandler
from synapse.server import HomeServer

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -66,7 +66,7 @@ class EventBuilder:
"""

_state: StateHandler
_auth: "Auth"
_event_auth_handler: "EventAuthHandler"
_store: DataStore
_clock: Clock
_hostname: str
Expand Down Expand Up @@ -125,7 +125,9 @@ async def build(
state_ids = await self._state.get_current_state_ids(
self.room_id, prev_event_ids
)
auth_event_ids = self._auth.compute_auth_events(self, state_ids)
auth_event_ids = self._event_auth_handler.compute_auth_events(
self, state_ids
)

format_version = self.room_version.event_format
if format_version == EventFormatVersions.V1:
Expand Down Expand Up @@ -193,7 +195,7 @@ def __init__(self, hs: "HomeServer"):

self.store = hs.get_datastore()
self.state = hs.get_state_handler()
self.auth = hs.get_auth()
self._event_auth_handler = hs.get_event_auth_handler()

def new(self, room_version: str, key_values: dict) -> EventBuilder:
"""Generate an event builder appropriate for the given room version
Expand Down Expand Up @@ -229,7 +231,7 @@ def for_room_version(
return EventBuilder(
store=self.store,
state=self.state,
auth=self.auth,
event_auth_handler=self._event_auth_handler,
clock=self.clock,
hostname=self.hostname,
signing_key=self.signing_key,
Expand Down
6 changes: 3 additions & 3 deletions synapse/federation/federation_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,9 @@ class FederationServer(FederationBase):
def __init__(self, hs: "HomeServer"):
super().__init__(hs)

self.auth = hs.get_auth()
self.handler = hs.get_federation_handler()
self.state = hs.get_state_handler()
self._event_auth_handler = hs.get_event_auth_handler()

self.device_handler = hs.get_device_handler()

Expand Down Expand Up @@ -420,7 +420,7 @@ async def on_room_state_request(
origin_host, _ = parse_server_name(origin)
await self.check_server_matches_acl(origin_host, room_id)

in_room = await self.auth.check_host_in_room(room_id, origin)
in_room = await self._event_auth_handler.check_host_in_room(room_id, origin)
if not in_room:
raise AuthError(403, "Host not in room.")

Expand Down Expand Up @@ -453,7 +453,7 @@ async def on_state_ids_request(
origin_host, _ = parse_server_name(origin)
await self.check_server_matches_acl(origin_host, room_id)

in_room = await self.auth.check_host_in_room(room_id, origin)
in_room = await self._event_auth_handler.check_host_in_room(room_id, origin)
if not in_room:
raise AuthError(403, "Host not in room.")

Expand Down
62 changes: 60 additions & 2 deletions synapse/handlers/event_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,21 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from typing import TYPE_CHECKING, Collection, Optional
from typing import TYPE_CHECKING, Collection, List, Optional, Union

from synapse import event_auth
from synapse.api.constants import (
EventTypes,
JoinRules,
Membership,
RestrictedJoinRuleTypes,
)
from synapse.api.errors import AuthError
from synapse.api.room_versions import RoomVersion
from synapse.api.room_versions import KNOWN_ROOM_VERSIONS, RoomVersion
from synapse.events import EventBase
from synapse.events.builder import EventBuilder
from synapse.types import StateMap
from synapse.util.metrics import Measure

if TYPE_CHECKING:
from synapse.server import HomeServer
Expand All @@ -34,8 +37,63 @@ class EventAuthHandler:
"""

def __init__(self, hs: "HomeServer"):
self._clock = hs.get_clock()
self._store = hs.get_datastore()

async def check_from_context(
self, room_version: str, event, context, do_sig_check=True
) -> None:
auth_event_ids = event.auth_event_ids()
auth_events_by_id = await self._store.get_events(auth_event_ids)
auth_events = {(e.type, e.state_key): e for e in auth_events_by_id.values()}

room_version_obj = KNOWN_ROOM_VERSIONS[room_version]
event_auth.check(
room_version_obj, event, auth_events=auth_events, do_sig_check=do_sig_check
)

def compute_auth_events(
self,
event: Union[EventBase, EventBuilder],
current_state_ids: StateMap[str],
for_verification: bool = False,
) -> List[str]:
"""Given an event and current state return the list of event IDs used
to auth an event.

If `for_verification` is False then only return auth events that
should be added to the event's `auth_events`.

Returns:
List of event IDs.
"""

if event.type == EventTypes.Create:
return []

# Currently we ignore the `for_verification` flag even though there are
# some situations where we can drop particular auth events when adding
# to the event's `auth_events` (e.g. joins pointing to previous joins
# when room is publicly joinable). Dropping event IDs has the
# advantage that the auth chain for the room grows slower, but we use
# the auth chain in state resolution v2 to order events, which means
# care must be taken if dropping events to ensure that it doesn't
# introduce undesirable "state reset" behaviour.
#
# All of which sounds a bit tricky so we don't bother for now.

auth_ids = []
for etype, state_key in event_auth.auth_types_for_event(event):
auth_ev_id = current_state_ids.get((etype, state_key))
if auth_ev_id:
auth_ids.append(auth_ev_id)

return auth_ids

async def check_host_in_room(self, room_id: str, host: str) -> bool:
with Measure(self._clock, "check_host_in_room"):
return await self._store.is_host_joined(room_id, host)

async def check_restricted_join_rules(
self,
state_ids: StateMap[str],
Expand Down
Loading