diff --git a/synapse/api/auth.py b/synapse/api/auth.py
index 847ff60671..6607d08488 100644
--- a/synapse/api/auth.py
+++ b/synapse/api/auth.py
@@ -14,15 +14,19 @@
# limitations under the License.
"""This module contains classes for authenticating the user."""
+from nacl.exceptions import BadSignatureError
from twisted.internet import defer
from synapse.api.constants import EventTypes, Membership, JoinRules
from synapse.api.errors import AuthError, Codes, SynapseError
+from synapse.types import RoomID, UserID, EventID
from synapse.util.logutils import log_function
-from synapse.types import UserID, EventID
+from synapse.util.thirdpartyinvites import ThirdPartyInvites
+from unpaddedbase64 import decode_base64
import logging
+import nacl.signing
import pymacaroons
logger = logging.getLogger(__name__)
@@ -31,6 +35,7 @@ logger = logging.getLogger(__name__)
AuthEventTypes = (
EventTypes.Create, EventTypes.Member, EventTypes.PowerLevels,
EventTypes.JoinRules, EventTypes.RoomHistoryVisibility,
+ EventTypes.ThirdPartyInvite,
)
@@ -80,6 +85,15 @@ class Auth(object):
"Room %r does not exist" % (event.room_id,)
)
+ creating_domain = RoomID.from_string(event.room_id).domain
+ originating_domain = UserID.from_string(event.sender).domain
+ if creating_domain != originating_domain:
+ if not self.can_federate(event, auth_events):
+ raise AuthError(
+ 403,
+ "This room has been marked as unfederatable."
+ )
+
# FIXME: Temp hack
if event.type == EventTypes.Aliases:
return True
@@ -219,6 +233,11 @@ class Auth(object):
user_id, room_id, repr(member)
))
+ def can_federate(self, event, auth_events):
+ creation_event = auth_events.get((EventTypes.Create, ""))
+
+ return creation_event.content.get("m.federate", True) is True
+
@log_function
def is_membership_change_allowed(self, event, auth_events):
membership = event.content["membership"]
@@ -234,6 +253,15 @@ class Auth(object):
target_user_id = event.state_key
+ creating_domain = RoomID.from_string(event.room_id).domain
+ target_domain = UserID.from_string(target_user_id).domain
+ if creating_domain != target_domain:
+ if not self.can_federate(event, auth_events):
+ raise AuthError(
+ 403,
+ "This room has been marked as unfederatable."
+ )
+
# get info about the caller
key = (EventTypes.Member, event.user_id, )
caller = auth_events.get(key)
@@ -318,7 +346,8 @@ class Auth(object):
pass
elif join_rule == JoinRules.INVITE:
if not caller_in_room and not caller_invited:
- raise AuthError(403, "You are not invited to this room.")
+ if not self._verify_third_party_invite(event, auth_events):
+ raise AuthError(403, "You are not invited to this room.")
else:
# TODO (erikj): may_join list
# TODO (erikj): private rooms
@@ -344,6 +373,31 @@ class Auth(object):
return True
+ def _verify_third_party_invite(self, event, auth_events):
+ for key in ThirdPartyInvites.JOIN_KEYS:
+ if key not in event.content:
+ return False
+ token = event.content["token"]
+ invite_event = auth_events.get(
+ (EventTypes.ThirdPartyInvite, token,)
+ )
+ if not invite_event:
+ return False
+ try:
+ public_key = event.content["public_key"]
+ key_validity_url = event.content["key_validity_url"]
+ if invite_event.content["public_key"] != public_key:
+ return False
+ if invite_event.content["key_validity_url"] != key_validity_url:
+ return False
+ verify_key = nacl.signing.VerifyKey(decode_base64(public_key))
+ encoded_signature = event.content["signature"]
+ signature = decode_base64(encoded_signature)
+ verify_key.verify(token, signature)
+ return True
+ except (KeyError, BadSignatureError,):
+ return False
+
def _get_power_level_event(self, auth_events):
key = (EventTypes.PowerLevels, "", )
return auth_events.get(key)
|