Skip to content

Commit f566705

Browse files
bugfix: assert we always pass the create event to get_user_power_level (element-hq#18545)
The create event is required if there is no PL event, in which case the creator gets PL100. ### Pull Request Checklist <!-- Please read https://element-hq.github.io/synapse/latest/development/contributing_guide.html before submitting your pull request --> * [x] Pull request is based on the develop branch * [x] Pull request includes a [changelog file](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#changelog). The entry should: - Be a short description of your change which makes sense to users. "Fixed a bug that prevented receiving messages from other servers." instead of "Moved X method from `EventStore` to `EventWorkerStore`.". - Use markdown where necessary, mostly for `code blocks`. - End with either a period (.) or an exclamation mark (!). - Start with a capital letter. - Feel free to credit yourself, by adding a sentence "Contributed by @github_username." or "Contributed by [Your Name]." to the end of the entry. * [x] [Code style](https://element-hq.github.io/synapse/latest/code_style.html) is correct (run the [linters](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#run-the-linters)) --------- Co-authored-by: Andrew Morgan <[email protected]>
1 parent db8a8d3 commit f566705

File tree

6 files changed

+66
-31
lines changed

6 files changed

+66
-31
lines changed

changelog.d/18545.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix an issue where Synapse could calculate the wrong power level for the creator of the room if there was no power levels event.

synapse/api/auth/base.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@
3737
from synapse.http import get_request_user_agent
3838
from synapse.http.site import SynapseRequest
3939
from synapse.logging.opentracing import trace
40+
from synapse.state import CREATE_KEY, POWER_KEY
4041
from synapse.types import Requester, create_requester
42+
from synapse.types.state import StateFilter
4143
from synapse.util.cancellation import cancellable
4244

4345
if TYPE_CHECKING:
@@ -216,18 +218,20 @@ async def check_can_change_room_list(
216218
# by checking if they would (theoretically) be able to change the
217219
# m.room.canonical_alias events
218220

219-
power_level_event = (
220-
await self._storage_controllers.state.get_current_state_event(
221-
room_id, EventTypes.PowerLevels, ""
222-
)
221+
auth_events = await self._storage_controllers.state.get_current_state(
222+
room_id,
223+
StateFilter.from_types(
224+
[
225+
POWER_KEY,
226+
CREATE_KEY,
227+
]
228+
),
223229
)
224230

225-
auth_events = {}
226-
if power_level_event:
227-
auth_events[(EventTypes.PowerLevels, "")] = power_level_event
228-
229231
send_level = event_auth.get_send_level(
230-
EventTypes.CanonicalAlias, "", power_level_event
232+
EventTypes.CanonicalAlias,
233+
"",
234+
auth_events.get(POWER_KEY),
231235
)
232236
user_level = event_auth.get_user_power_level(
233237
requester.user.to_string(), auth_events

synapse/event_auth.py

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
RoomVersion,
6565
RoomVersions,
6666
)
67+
from synapse.state import CREATE_KEY
6768
from synapse.storage.databases.main.events_worker import EventRedactBehaviour
6869
from synapse.types import (
6970
MutableStateMap,
@@ -308,6 +309,13 @@ def check_state_dependent_auth_rules(
308309

309310
auth_dict = {(e.type, e.state_key): e for e in auth_events}
310311

312+
# Later code relies on there being a create event e.g _can_federate, _is_membership_change_allowed
313+
# so produce a more intelligible error if we don't have one.
314+
if auth_dict.get(CREATE_KEY) is None:
315+
raise AuthError(
316+
403, f"Event {event.event_id} is missing a create event in auth_events."
317+
)
318+
311319
# additional check for m.federate
312320
creating_domain = get_domain_from_id(event.room_id)
313321
originating_domain = get_domain_from_id(event.sender)
@@ -1010,11 +1018,16 @@ def get_user_power_level(user_id: str, auth_events: StateMap["EventBase"]) -> in
10101018
user_id: user's id to look up in power_levels
10111019
auth_events:
10121020
state in force at this point in the room (or rather, a subset of
1013-
it including at least the create event and power levels event.
1021+
it including at least the create event, and possibly a power levels event).
10141022
10151023
Returns:
10161024
the user's power level in this room.
10171025
"""
1026+
create_event = auth_events.get(CREATE_KEY)
1027+
assert create_event is not None, (
1028+
"A create event in the auth events chain is required to calculate user power level correctly,"
1029+
" but was not found. This indicates a bug"
1030+
)
10181031
power_level_event = get_power_level_event(auth_events)
10191032
if power_level_event:
10201033
level = power_level_event.content.get("users", {}).get(user_id)
@@ -1028,18 +1041,12 @@ def get_user_power_level(user_id: str, auth_events: StateMap["EventBase"]) -> in
10281041
else:
10291042
# if there is no power levels event, the creator gets 100 and everyone
10301043
# else gets 0.
1031-
1032-
# some things which call this don't pass the create event: hack around
1033-
# that.
1034-
key = (EventTypes.Create, "")
1035-
create_event = auth_events.get(key)
1036-
if create_event is not None:
1037-
if create_event.room_version.implicit_room_creator:
1038-
creator = create_event.sender
1039-
else:
1040-
creator = create_event.content[EventContentFields.ROOM_CREATOR]
1041-
if creator == user_id:
1042-
return 100
1044+
if create_event.room_version.implicit_room_creator:
1045+
creator = create_event.sender
1046+
else:
1047+
creator = create_event.content[EventContentFields.ROOM_CREATOR]
1048+
if creator == user_id:
1049+
return 100
10431050
return 0
10441051

10451052

synapse/push/bulk_push_rule_evaluator.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
from synapse.events import EventBase, relation_from_event
5151
from synapse.events.snapshot import EventContext
5252
from synapse.logging.context import make_deferred_yieldable, run_in_background
53-
from synapse.state import POWER_KEY
53+
from synapse.state import CREATE_KEY, POWER_KEY
5454
from synapse.storage.databases.main.roommember import EventIdMembership
5555
from synapse.storage.invite_rule import InviteRule
5656
from synapse.storage.roommember import ProfileInfo
@@ -246,6 +246,7 @@ async def _get_power_levels_and_sender_level(
246246
StateFilter.from_types(event_types)
247247
)
248248
pl_event_id = prev_state_ids.get(POWER_KEY)
249+
create_event_id = prev_state_ids.get(CREATE_KEY)
249250

250251
# fastpath: if there's a power level event, that's all we need, and
251252
# not having a power level event is an extreme edge case
@@ -268,6 +269,26 @@ async def _get_power_levels_and_sender_level(
268269
if auth_event:
269270
auth_events_dict[auth_event_id] = auth_event
270271
auth_events = {(e.type, e.state_key): e for e in auth_events_dict.values()}
272+
if auth_events.get(CREATE_KEY) is None:
273+
# if the event being checked is the create event, use its own permissions
274+
if event.type == EventTypes.Create and event.get_state_key() == "":
275+
auth_events[CREATE_KEY] = event
276+
else:
277+
auth_events[
278+
CREATE_KEY
279+
] = await self.store.get_create_event_for_room(event.room_id)
280+
281+
# if we are evaluating the create event, then use itself to determine power levels.
282+
if event.type == EventTypes.Create and event.get_state_key() == "":
283+
auth_events[CREATE_KEY] = event
284+
else:
285+
# if we aren't processing the create event, create_event_id should always be set
286+
assert create_event_id is not None
287+
create_event = event_id_to_event.get(create_event_id)
288+
if create_event:
289+
auth_events[CREATE_KEY] = create_event
290+
else:
291+
auth_events[CREATE_KEY] = await self.store.get_event(create_event_id)
271292

272293
sender_level = get_user_power_level(event.sender, auth_events)
273294

synapse/rest/client/room.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
from synapse.metrics.background_process_metrics import run_as_background_process
6565
from synapse.rest.client._base import client_patterns
6666
from synapse.rest.client.transactions import HttpTransactionCache
67+
from synapse.state import CREATE_KEY, POWER_KEY
6768
from synapse.streams.config import PaginationConfig
6869
from synapse.types import JsonDict, Requester, StreamToken, ThirdPartyInstanceID, UserID
6970
from synapse.types.state import StateFilter
@@ -924,16 +925,16 @@ async def on_GET(
924925
if include_unredacted_content and not await self.auth.is_server_admin(
925926
requester
926927
):
927-
power_level_event = (
928-
await self._storage_controllers.state.get_current_state_event(
929-
room_id, EventTypes.PowerLevels, ""
930-
)
928+
auth_events = await self._storage_controllers.state.get_current_state(
929+
room_id,
930+
StateFilter.from_types(
931+
[
932+
POWER_KEY,
933+
CREATE_KEY,
934+
]
935+
),
931936
)
932937

933-
auth_events = {}
934-
if power_level_event:
935-
auth_events[(EventTypes.PowerLevels, "")] = power_level_event
936-
937938
redact_level = event_auth.get_named_level(auth_events, "redact", 50)
938939
user_level = event_auth.get_user_power_level(
939940
requester.user.to_string(), auth_events

synapse/state/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383

8484
_NEXT_STATE_ID = 1
8585

86+
CREATE_KEY = (EventTypes.Create, "")
8687
POWER_KEY = (EventTypes.PowerLevels, "")
8788

8889

0 commit comments

Comments
 (0)