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

Allow deleting an alias if the user has sufficient power level #6986

Merged
merged 7 commits into from
Mar 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/6986.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Users with a power level sufficient to modify the canonical alias of a room can now delete room aliases.
9 changes: 2 additions & 7 deletions synapse/api/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,7 @@ def compute_auth_events(

@defer.inlineCallbacks
def check_can_change_room_list(self, room_id: str, user: UserID):
"""Check if the user is allowed to edit the room's entry in the
"""Determine whether the user is allowed to edit the room's entry in the
published room list.

Args:
Expand Down Expand Up @@ -570,12 +570,7 @@ def check_can_change_room_list(self, room_id: str, user: UserID):
)
user_level = event_auth.get_user_power_level(user_id, auth_events)

if user_level < send_level:
raise AuthError(
403,
"This server requires you to be a moderator in the room to"
" edit its room list entry",
)
return user_level >= send_level

@staticmethod
def has_access_token(request):
Expand Down
107 changes: 74 additions & 33 deletions synapse/handlers/directory.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

import logging
import string
from typing import List
from typing import Iterable, List, Optional

from twisted.internet import defer

Expand All @@ -28,6 +28,7 @@
StoreError,
SynapseError,
)
from synapse.appservice import ApplicationService
from synapse.types import Requester, RoomAlias, UserID, get_domain_from_id

from ._base import BaseHandler
Expand Down Expand Up @@ -55,7 +56,13 @@ def __init__(self, hs):
self.spam_checker = hs.get_spam_checker()

@defer.inlineCallbacks
def _create_association(self, room_alias, room_id, servers=None, creator=None):
def _create_association(
self,
room_alias: RoomAlias,
room_id: str,
servers: Optional[Iterable[str]] = None,
creator: Optional[str] = None,
):
# general association creation for both human users and app services

for wchar in string.whitespace:
Expand All @@ -81,17 +88,21 @@ def _create_association(self, room_alias, room_id, servers=None, creator=None):

@defer.inlineCallbacks
def create_association(
self, requester, room_alias, room_id, servers=None, check_membership=True,
self,
requester: Requester,
room_alias: RoomAlias,
room_id: str,
servers: Optional[List[str]] = None,
check_membership: bool = True,
):
"""Attempt to create a new alias

Args:
requester (Requester)
room_alias (RoomAlias)
room_id (str)
servers (list[str]|None): List of servers that others servers
should try and join via
check_membership (bool): Whether to check if the user is in the room
requester
room_alias
room_id
servers: Iterable of servers that others servers should try and join via
check_membership: Whether to check if the user is in the room
before the alias can be set (if the server's config requires it).

Returns:
Expand Down Expand Up @@ -145,15 +156,15 @@ def create_association(
yield self._create_association(room_alias, room_id, servers, creator=user_id)

@defer.inlineCallbacks
def delete_association(self, requester, room_alias):
def delete_association(self, requester: Requester, room_alias: RoomAlias):
"""Remove an alias from the directory

(this is only meant for human users; AS users should call
delete_appservice_association)

Args:
requester (Requester):
room_alias (RoomAlias):
requester
room_alias

Returns:
Deferred[unicode]: room id that the alias used to point to
Expand Down Expand Up @@ -189,16 +200,16 @@ def delete_association(self, requester, room_alias):
room_id = yield self._delete_association(room_alias)

try:
yield self._update_canonical_alias(
requester, requester.user.to_string(), room_id, room_alias
)
yield self._update_canonical_alias(requester, user_id, room_id, room_alias)
except AuthError as e:
logger.info("Failed to update alias events: %s", e)

return room_id

@defer.inlineCallbacks
def delete_appservice_association(self, service, room_alias):
def delete_appservice_association(
self, service: ApplicationService, room_alias: RoomAlias
):
if not service.is_interested_in_alias(room_alias.to_string()):
raise SynapseError(
400,
Expand All @@ -208,7 +219,7 @@ def delete_appservice_association(self, service, room_alias):
yield self._delete_association(room_alias)

@defer.inlineCallbacks
def _delete_association(self, room_alias):
def _delete_association(self, room_alias: RoomAlias):
if not self.hs.is_mine(room_alias):
raise SynapseError(400, "Room alias must be local")

Expand All @@ -217,7 +228,7 @@ def _delete_association(self, room_alias):
return room_id

@defer.inlineCallbacks
def get_association(self, room_alias):
def get_association(self, room_alias: RoomAlias):
room_id = None
if self.hs.is_mine(room_alias):
result = yield self.get_association_from_room_alias(room_alias)
Expand Down Expand Up @@ -282,7 +293,9 @@ def on_directory_query(self, args):
)

@defer.inlineCallbacks
def _update_canonical_alias(self, requester, user_id, room_id, room_alias):
def _update_canonical_alias(
self, requester: Requester, user_id: str, room_id: str, room_alias: RoomAlias
):
"""
Send an updated canonical alias event if the removed alias was set as
the canonical alias or listed in the alt_aliases field.
Expand Down Expand Up @@ -331,15 +344,15 @@ def _update_canonical_alias(self, requester, user_id, room_id, room_alias):
)

@defer.inlineCallbacks
def get_association_from_room_alias(self, room_alias):
def get_association_from_room_alias(self, room_alias: RoomAlias):
result = yield self.store.get_association_from_room_alias(room_alias)
if not result:
# Query AS to see if it exists
as_handler = self.appservice_handler
result = yield as_handler.query_room_alias_exists(room_alias)
return result

def can_modify_alias(self, alias, user_id=None):
def can_modify_alias(self, alias: RoomAlias, user_id: Optional[str] = None):
# Any application service "interested" in an alias they are regexing on
# can modify the alias.
# Users can only modify the alias if ALL the interested services have
Expand All @@ -360,22 +373,42 @@ def can_modify_alias(self, alias, user_id=None):
return defer.succeed(True)

@defer.inlineCallbacks
def _user_can_delete_alias(self, alias, user_id):
def _user_can_delete_alias(self, alias: RoomAlias, user_id: str):
"""Determine whether a user can delete an alias.

One of the following must be true:

1. The user created the alias.
2. The user is a server administrator.
3. The user has a power-level sufficient to send a canonical alias event
for the current room.

"""
creator = yield self.store.get_room_alias_creator(alias.to_string())

if creator is not None and creator == user_id:
return True

is_admin = yield self.auth.is_server_admin(UserID.from_string(user_id))
return is_admin
# Resolve the alias to the corresponding room.
room_mapping = yield self.get_association(alias)
room_id = room_mapping["room_id"]
if not room_id:
return False

res = yield self.auth.check_can_change_room_list(
room_id, UserID.from_string(user_id)
)
return res

@defer.inlineCallbacks
def edit_published_room_list(self, requester, room_id, visibility):
def edit_published_room_list(
self, requester: Requester, room_id: str, visibility: str
):
"""Edit the entry of the room in the published room list.

requester
room_id (str)
visibility (str): "public" or "private"
room_id
visibility: "public" or "private"
"""
user_id = requester.user.to_string()

Expand All @@ -400,7 +433,15 @@ def edit_published_room_list(self, requester, room_id, visibility):
if room is None:
raise SynapseError(400, "Unknown room")

yield self.auth.check_can_change_room_list(room_id, requester.user)
can_change_room_list = yield self.auth.check_can_change_room_list(
room_id, requester.user
)
if not can_change_room_list:
raise AuthError(
403,
"This server requires you to be a moderator in the room to"
" edit its room list entry",
)

making_public = visibility == "public"
if making_public:
Expand All @@ -421,16 +462,16 @@ def edit_published_room_list(self, requester, room_id, visibility):

@defer.inlineCallbacks
def edit_published_appservice_room_list(
self, appservice_id, network_id, room_id, visibility
self, appservice_id: str, network_id: str, room_id: str, visibility: str
):
"""Add or remove a room from the appservice/network specific public
room list.

Args:
appservice_id (str): ID of the appservice that owns the list
network_id (str): The ID of the network the list is associated with
room_id (str)
visibility (str): either "public" or "private"
appservice_id: ID of the appservice that owns the list
network_id: The ID of the network the list is associated with
room_id
visibility: either "public" or "private"
"""
if visibility not in ["public", "private"]:
raise SynapseError(400, "Invalid visibility setting")
Expand Down
Loading