diff --git a/synapse/handlers/directory.py b/synapse/handlers/directory.py
index 91fceda2ac..f76febee8f 100644
--- a/synapse/handlers/directory.py
+++ b/synapse/handlers/directory.py
@@ -19,6 +19,7 @@ from ._base import BaseHandler
from synapse.api.errors import SynapseError, Codes, CodeMessageException
from synapse.api.constants import EventTypes
+from synapse.types import RoomAlias
import logging
@@ -36,18 +37,15 @@ class DirectoryHandler(BaseHandler):
)
@defer.inlineCallbacks
- def create_association(self, user_id, room_alias, room_id, servers=None):
-
- # TODO(erikj): Do auth.
+ def _create_association(self, room_alias, room_id, servers=None):
+ # general association creation for both human users and app services
if not self.hs.is_mine(room_alias):
raise SynapseError(400, "Room alias must be local")
# TODO(erikj): Change this.
# TODO(erikj): Add transactions.
-
# TODO(erikj): Check if there is a current association.
-
if not servers:
servers = yield self.store.get_joined_hosts_for_room(room_id)
@@ -61,22 +59,77 @@ class DirectoryHandler(BaseHandler):
)
@defer.inlineCallbacks
+ def create_association(self, user_id, room_alias, room_id, servers=None):
+ # association creation for human users
+ # TODO(erikj): Do user auth.
+
+ can_create = yield self.can_modify_alias(
+ room_alias,
+ user_id=user_id
+ )
+ if not can_create:
+ raise SynapseError(
+ 400, "This alias is reserved by an application service.",
+ errcode=Codes.EXCLUSIVE
+ )
+ yield self._create_association(room_alias, room_id, servers)
+
+ @defer.inlineCallbacks
+ def create_appservice_association(self, service, room_alias, room_id,
+ servers=None):
+ if not service.is_interested_in_alias(room_alias.to_string()):
+ raise SynapseError(
+ 400, "This application service has not reserved"
+ " this kind of alias.", errcode=Codes.EXCLUSIVE
+ )
+
+ # association creation for app services
+ yield self._create_association(room_alias, room_id, servers)
+
+ @defer.inlineCallbacks
def delete_association(self, user_id, room_alias):
+ # association deletion for human users
+
# TODO Check if server admin
+ can_delete = yield self.can_modify_alias(
+ room_alias,
+ user_id=user_id
+ )
+ if not can_delete:
+ raise SynapseError(
+ 400, "This alias is reserved by an application service.",
+ errcode=Codes.EXCLUSIVE
+ )
+
+ yield self._delete_association(room_alias)
+
+ @defer.inlineCallbacks
+ def delete_appservice_association(self, service, room_alias):
+ if not service.is_interested_in_alias(room_alias.to_string()):
+ raise SynapseError(
+ 400,
+ "This application service has not reserved this kind of alias",
+ errcode=Codes.EXCLUSIVE
+ )
+ yield self._delete_association(room_alias)
+
+ @defer.inlineCallbacks
+ def _delete_association(self, room_alias):
if not self.hs.is_mine(room_alias):
raise SynapseError(400, "Room alias must be local")
- room_id = yield self.store.delete_room_alias(room_alias)
+ yield self.store.delete_room_alias(room_alias)
- if room_id:
- yield self._update_room_alias_events(user_id, room_id)
+ # TODO - Looks like _update_room_alias_event has never been implemented
+ # if room_id:
+ # yield self._update_room_alias_events(user_id, room_id)
@defer.inlineCallbacks
def get_association(self, room_alias):
room_id = None
if self.hs.is_mine(room_alias):
- result = yield self.store.get_association_from_room_alias(
+ result = yield self.get_association_from_room_alias(
room_alias
)
@@ -107,12 +160,21 @@ class DirectoryHandler(BaseHandler):
if not room_id:
raise SynapseError(
404,
- "Room alias %r not found" % (room_alias.to_string(),),
+ "Room alias %s not found" % (room_alias.to_string(),),
Codes.NOT_FOUND
)
extra_servers = yield self.store.get_joined_hosts_for_room(room_id)
- servers = list(set(extra_servers) | set(servers))
+ servers = set(extra_servers) | set(servers)
+
+ # If this server is in the list of servers, return it first.
+ if self.server_name in servers:
+ servers = (
+ [self.server_name]
+ + [s for s in servers if s != self.server_name]
+ )
+ else:
+ servers = list(servers)
defer.returnValue({
"room_id": room_id,
@@ -122,13 +184,13 @@ class DirectoryHandler(BaseHandler):
@defer.inlineCallbacks
def on_directory_query(self, args):
- room_alias = self.hs.parse_roomalias(args["room_alias"])
+ room_alias = RoomAlias.from_string(args["room_alias"])
if not self.hs.is_mine(room_alias):
raise SynapseError(
400, "Room Alias is not hosted on this Home Server"
)
- result = yield self.store.get_association_from_room_alias(
+ result = yield self.get_association_from_room_alias(
room_alias
)
@@ -156,3 +218,37 @@ class DirectoryHandler(BaseHandler):
"sender": user_id,
"content": {"aliases": aliases},
}, ratelimit=False)
+
+ @defer.inlineCallbacks
+ def get_association_from_room_alias(self, room_alias):
+ result = yield self.store.get_association_from_room_alias(
+ room_alias
+ )
+ if not result:
+ # Query AS to see if it exists
+ as_handler = self.hs.get_handlers().appservice_handler
+ result = yield as_handler.query_room_alias_exists(room_alias)
+ defer.returnValue(result)
+
+ @defer.inlineCallbacks
+ def can_modify_alias(self, alias, user_id=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
+ # non-exclusive locks on the alias (or there are no interested services)
+ services = yield self.store.get_app_services()
+ interested_services = [
+ s for s in services if s.is_interested_in_alias(alias.to_string())
+ ]
+
+ for service in interested_services:
+ if user_id == service.sender:
+ # this user IS the app service so they can do whatever they like
+ defer.returnValue(True)
+ return
+ elif service.is_exclusive_alias(alias.to_string()):
+ # another service has an exclusive lock on this alias.
+ defer.returnValue(False)
+ return
+ # either no interested services, or no service with an exclusive lock
+ defer.returnValue(True)
|