forked from MirrorHub/synapse
Merge pull request #6790 from matrix-org/rav/msc2260.1
MSC2260: change the default power level for m.room.aliases events
This commit is contained in:
commit
281551f720
5 changed files with 112 additions and 18 deletions
1
changelog.d/6790.feature
Normal file
1
changelog.d/6790.feature
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Implement updated authorization rules for aliases events, from [MSC2260](https://github.com/matrix-org/matrix-doc/pull/2260).
|
|
@ -12,8 +12,9 @@
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
import collections
|
||||||
import re
|
import re
|
||||||
|
from typing import Mapping, Union
|
||||||
|
|
||||||
from six import string_types
|
from six import string_types
|
||||||
|
|
||||||
|
@ -422,3 +423,37 @@ class EventClientSerializer(object):
|
||||||
return yieldable_gather_results(
|
return yieldable_gather_results(
|
||||||
self.serialize_event, events, time_now=time_now, **kwargs
|
self.serialize_event, events, time_now=time_now, **kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def copy_power_levels_contents(
|
||||||
|
old_power_levels: Mapping[str, Union[int, Mapping[str, int]]]
|
||||||
|
):
|
||||||
|
"""Copy the content of a power_levels event, unfreezing frozendicts along the way
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
TypeError if the input does not look like a valid power levels event content
|
||||||
|
"""
|
||||||
|
if not isinstance(old_power_levels, collections.Mapping):
|
||||||
|
raise TypeError("Not a valid power-levels content: %r" % (old_power_levels,))
|
||||||
|
|
||||||
|
power_levels = {}
|
||||||
|
for k, v in old_power_levels.items():
|
||||||
|
|
||||||
|
if isinstance(v, int):
|
||||||
|
power_levels[k] = v
|
||||||
|
continue
|
||||||
|
|
||||||
|
if isinstance(v, collections.Mapping):
|
||||||
|
power_levels[k] = h = {}
|
||||||
|
for k1, v1 in v.items():
|
||||||
|
# we should only have one level of nesting
|
||||||
|
if not isinstance(v1, int):
|
||||||
|
raise TypeError(
|
||||||
|
"Invalid power_levels value for %s.%s: %r" % (k, k1, v1)
|
||||||
|
)
|
||||||
|
h[k1] = v1
|
||||||
|
continue
|
||||||
|
|
||||||
|
raise TypeError("Invalid power_levels value for %s: %r" % (k, v))
|
||||||
|
|
||||||
|
return power_levels
|
||||||
|
|
|
@ -151,7 +151,12 @@ class DirectoryHandler(BaseHandler):
|
||||||
|
|
||||||
yield self._create_association(room_alias, room_id, servers, creator=user_id)
|
yield self._create_association(room_alias, room_id, servers, creator=user_id)
|
||||||
if send_event:
|
if send_event:
|
||||||
|
try:
|
||||||
yield self.send_room_alias_update_event(requester, room_id)
|
yield self.send_room_alias_update_event(requester, room_id)
|
||||||
|
except AuthError as e:
|
||||||
|
# sending the aliases event may fail due to the user not having
|
||||||
|
# permission in the room; this is permitted.
|
||||||
|
logger.info("Skipping updating aliases event due to auth error %s", e)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def delete_association(self, requester, room_alias, send_event=True):
|
def delete_association(self, requester, room_alias, send_event=True):
|
||||||
|
|
|
@ -30,6 +30,7 @@ from twisted.internet import defer
|
||||||
from synapse.api.constants import EventTypes, JoinRules, RoomCreationPreset
|
from synapse.api.constants import EventTypes, JoinRules, RoomCreationPreset
|
||||||
from synapse.api.errors import AuthError, Codes, NotFoundError, StoreError, SynapseError
|
from synapse.api.errors import AuthError, Codes, NotFoundError, StoreError, SynapseError
|
||||||
from synapse.api.room_versions import KNOWN_ROOM_VERSIONS, RoomVersion
|
from synapse.api.room_versions import KNOWN_ROOM_VERSIONS, RoomVersion
|
||||||
|
from synapse.events.utils import copy_power_levels_contents
|
||||||
from synapse.http.endpoint import parse_and_validate_server_name
|
from synapse.http.endpoint import parse_and_validate_server_name
|
||||||
from synapse.storage.state import StateFilter
|
from synapse.storage.state import StateFilter
|
||||||
from synapse.types import (
|
from synapse.types import (
|
||||||
|
@ -286,7 +287,16 @@ class RoomCreationHandler(BaseHandler):
|
||||||
except AuthError as e:
|
except AuthError as e:
|
||||||
logger.warning("Unable to update PLs in old room: %s", e)
|
logger.warning("Unable to update PLs in old room: %s", e)
|
||||||
|
|
||||||
logger.info("Setting correct PLs in new room to %s", old_room_pl_state.content)
|
new_pl_content = copy_power_levels_contents(old_room_pl_state.content)
|
||||||
|
|
||||||
|
# pre-msc2260 rooms may not have the right setting for aliases. If no other
|
||||||
|
# value is set, set it now.
|
||||||
|
events_default = new_pl_content.get("events_default", 0)
|
||||||
|
new_pl_content.setdefault("events", {}).setdefault(
|
||||||
|
EventTypes.Aliases, events_default
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info("Setting correct PLs in new room to %s", new_pl_content)
|
||||||
yield self.event_creation_handler.create_and_send_nonmember_event(
|
yield self.event_creation_handler.create_and_send_nonmember_event(
|
||||||
requester,
|
requester,
|
||||||
{
|
{
|
||||||
|
@ -294,7 +304,7 @@ class RoomCreationHandler(BaseHandler):
|
||||||
"state_key": "",
|
"state_key": "",
|
||||||
"room_id": new_room_id,
|
"room_id": new_room_id,
|
||||||
"sender": requester.user.to_string(),
|
"sender": requester.user.to_string(),
|
||||||
"content": old_room_pl_state.content,
|
"content": new_pl_content,
|
||||||
},
|
},
|
||||||
ratelimit=False,
|
ratelimit=False,
|
||||||
)
|
)
|
||||||
|
@ -367,6 +377,15 @@ class RoomCreationHandler(BaseHandler):
|
||||||
if old_event:
|
if old_event:
|
||||||
initial_state[k] = old_event.content
|
initial_state[k] = old_event.content
|
||||||
|
|
||||||
|
# deep-copy the power-levels event before we start modifying it
|
||||||
|
# note that if frozen_dicts are enabled, `power_levels` will be a frozen
|
||||||
|
# dict so we can't just copy.deepcopy it.
|
||||||
|
initial_state[
|
||||||
|
(EventTypes.PowerLevels, "")
|
||||||
|
] = power_levels = copy_power_levels_contents(
|
||||||
|
initial_state[(EventTypes.PowerLevels, "")]
|
||||||
|
)
|
||||||
|
|
||||||
# Resolve the minimum power level required to send any state event
|
# Resolve the minimum power level required to send any state event
|
||||||
# We will give the upgrading user this power level temporarily (if necessary) such that
|
# We will give the upgrading user this power level temporarily (if necessary) such that
|
||||||
# they are able to copy all of the state events over, then revert them back to their
|
# they are able to copy all of the state events over, then revert them back to their
|
||||||
|
@ -375,8 +394,6 @@ class RoomCreationHandler(BaseHandler):
|
||||||
# Copy over user power levels now as this will not be possible with >100PL users once
|
# Copy over user power levels now as this will not be possible with >100PL users once
|
||||||
# the room has been created
|
# the room has been created
|
||||||
|
|
||||||
power_levels = initial_state[(EventTypes.PowerLevels, "")]
|
|
||||||
|
|
||||||
# Calculate the minimum power level needed to clone the room
|
# Calculate the minimum power level needed to clone the room
|
||||||
event_power_levels = power_levels.get("events", {})
|
event_power_levels = power_levels.get("events", {})
|
||||||
state_default = power_levels.get("state_default", 0)
|
state_default = power_levels.get("state_default", 0)
|
||||||
|
@ -386,16 +403,7 @@ class RoomCreationHandler(BaseHandler):
|
||||||
# Raise the requester's power level in the new room if necessary
|
# Raise the requester's power level in the new room if necessary
|
||||||
current_power_level = power_levels["users"][user_id]
|
current_power_level = power_levels["users"][user_id]
|
||||||
if current_power_level < needed_power_level:
|
if current_power_level < needed_power_level:
|
||||||
# make sure we copy the event content rather than overwriting it.
|
power_levels["users"][user_id] = needed_power_level
|
||||||
# note that if frozen_dicts are enabled, `power_levels` will be a frozen
|
|
||||||
# dict so we can't just copy.deepcopy it.
|
|
||||||
|
|
||||||
new_power_levels = {k: v for k, v in power_levels.items() if k != "users"}
|
|
||||||
new_power_levels["users"] = {
|
|
||||||
k: v for k, v in power_levels.get("users", {}).items() if k != user_id
|
|
||||||
}
|
|
||||||
new_power_levels["users"][user_id] = needed_power_level
|
|
||||||
initial_state[(EventTypes.PowerLevels, "")] = new_power_levels
|
|
||||||
|
|
||||||
yield self._send_events_for_new_room(
|
yield self._send_events_for_new_room(
|
||||||
requester,
|
requester,
|
||||||
|
@ -813,6 +821,10 @@ class RoomCreationHandler(BaseHandler):
|
||||||
EventTypes.RoomHistoryVisibility: 100,
|
EventTypes.RoomHistoryVisibility: 100,
|
||||||
EventTypes.CanonicalAlias: 50,
|
EventTypes.CanonicalAlias: 50,
|
||||||
EventTypes.RoomAvatar: 50,
|
EventTypes.RoomAvatar: 50,
|
||||||
|
# MSC2260: Allow everybody to send alias events by default
|
||||||
|
# This will be reudundant on pre-MSC2260 rooms, since the
|
||||||
|
# aliases event is special-cased.
|
||||||
|
EventTypes.Aliases: 0,
|
||||||
},
|
},
|
||||||
"events_default": 0,
|
"events_default": 0,
|
||||||
"state_default": 50,
|
"state_default": 50,
|
||||||
|
|
|
@ -15,9 +15,14 @@
|
||||||
|
|
||||||
|
|
||||||
from synapse.events import FrozenEvent
|
from synapse.events import FrozenEvent
|
||||||
from synapse.events.utils import prune_event, serialize_event
|
from synapse.events.utils import (
|
||||||
|
copy_power_levels_contents,
|
||||||
|
prune_event,
|
||||||
|
serialize_event,
|
||||||
|
)
|
||||||
|
from synapse.util.frozenutils import freeze
|
||||||
|
|
||||||
from .. import unittest
|
from tests import unittest
|
||||||
|
|
||||||
|
|
||||||
def MockEvent(**kwargs):
|
def MockEvent(**kwargs):
|
||||||
|
@ -241,3 +246,39 @@ class SerializeEventTestCase(unittest.TestCase):
|
||||||
self.serialize(
|
self.serialize(
|
||||||
MockEvent(room_id="!foo:bar", content={"foo": "bar"}), ["room_id", 4]
|
MockEvent(room_id="!foo:bar", content={"foo": "bar"}), ["room_id", 4]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CopyPowerLevelsContentTestCase(unittest.TestCase):
|
||||||
|
def setUp(self) -> None:
|
||||||
|
self.test_content = {
|
||||||
|
"ban": 50,
|
||||||
|
"events": {"m.room.name": 100, "m.room.power_levels": 100},
|
||||||
|
"events_default": 0,
|
||||||
|
"invite": 50,
|
||||||
|
"kick": 50,
|
||||||
|
"notifications": {"room": 20},
|
||||||
|
"redact": 50,
|
||||||
|
"state_default": 50,
|
||||||
|
"users": {"@example:localhost": 100},
|
||||||
|
"users_default": 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
def _test(self, input):
|
||||||
|
a = copy_power_levels_contents(input)
|
||||||
|
|
||||||
|
self.assertEqual(a["ban"], 50)
|
||||||
|
self.assertEqual(a["events"]["m.room.name"], 100)
|
||||||
|
|
||||||
|
# make sure that changing the copy changes the copy and not the orig
|
||||||
|
a["ban"] = 10
|
||||||
|
a["events"]["m.room.power_levels"] = 20
|
||||||
|
|
||||||
|
self.assertEqual(input["ban"], 50)
|
||||||
|
self.assertEqual(input["events"]["m.room.power_levels"], 100)
|
||||||
|
|
||||||
|
def test_unfrozen(self):
|
||||||
|
self._test(self.test_content)
|
||||||
|
|
||||||
|
def test_frozen(self):
|
||||||
|
input = freeze(self.test_content)
|
||||||
|
self._test(input)
|
||||||
|
|
Loading…
Reference in a new issue