29
29
from synapse import types
30
30
from synapse .api .constants import EventTypes , Membership
31
31
from synapse .api .errors import AuthError , Codes , HttpResponseException , SynapseError
32
+ from synapse .handlers .identity import LookupAlgorithm , create_id_access_token_header
32
33
from synapse .types import RoomID , UserID
33
34
from synapse .util .async_helpers import Linearizer
34
35
from synapse .util .distributor import user_joined_room , user_left_room
36
+ from synapse .util .hash import sha256_and_url_safe_base64
35
37
36
38
from ._base import BaseHandler
37
39
@@ -626,7 +628,7 @@ def lookup_room_alias(self, room_alias):
626
628
servers .remove (room_alias .domain )
627
629
servers .insert (0 , room_alias .domain )
628
630
629
- return ( RoomID .from_string (room_id ), servers )
631
+ return RoomID .from_string (room_id ), servers
630
632
631
633
@defer .inlineCallbacks
632
634
def _get_inviter (self , user_id , room_id ):
@@ -638,7 +640,15 @@ def _get_inviter(self, user_id, room_id):
638
640
639
641
@defer .inlineCallbacks
640
642
def do_3pid_invite (
641
- self , room_id , inviter , medium , address , id_server , requester , txn_id
643
+ self ,
644
+ room_id ,
645
+ inviter ,
646
+ medium ,
647
+ address ,
648
+ id_server ,
649
+ requester ,
650
+ txn_id ,
651
+ id_access_token = None ,
642
652
):
643
653
if self .config .block_non_admin_invites :
644
654
is_requester_admin = yield self .auth .is_server_admin (requester .user )
@@ -661,7 +671,12 @@ def do_3pid_invite(
661
671
Codes .FORBIDDEN ,
662
672
)
663
673
664
- invitee = yield self ._lookup_3pid (id_server , medium , address )
674
+ if not self ._enable_lookup :
675
+ raise SynapseError (
676
+ 403 , "Looking up third-party identifiers is denied from this server"
677
+ )
678
+
679
+ invitee = yield self ._lookup_3pid (id_server , medium , address , id_access_token )
665
680
666
681
if invitee :
667
682
yield self .update_membership (
@@ -673,9 +688,47 @@ def do_3pid_invite(
673
688
)
674
689
675
690
@defer .inlineCallbacks
676
- def _lookup_3pid (self , id_server , medium , address ):
691
+ def _lookup_3pid (self , id_server , medium , address , id_access_token = None ):
677
692
"""Looks up a 3pid in the passed identity server.
678
693
694
+ Args:
695
+ id_server (str): The server name (including port, if required)
696
+ of the identity server to use.
697
+ medium (str): The type of the third party identifier (e.g. "email").
698
+ address (str): The third party identifier (e.g. "[email protected] ").
699
+ id_access_token (str|None): The access token to authenticate to the identity
700
+ server with
701
+
702
+ Returns:
703
+ str|None: the matrix ID of the 3pid, or None if it is not recognized.
704
+ """
705
+ if id_access_token is not None :
706
+ try :
707
+ results = yield self ._lookup_3pid_v2 (
708
+ id_server , id_access_token , medium , address
709
+ )
710
+ return results
711
+
712
+ except Exception as e :
713
+ # Catch HttpResponseExcept for a non-200 response code
714
+ # Check if this identity server does not know about v2 lookups
715
+ if isinstance (e , HttpResponseException ) and e .code == 404 :
716
+ # This is an old identity server that does not yet support v2 lookups
717
+ logger .warning (
718
+ "Attempted v2 lookup on v1 identity server %s. Falling "
719
+ "back to v1" ,
720
+ id_server ,
721
+ )
722
+ else :
723
+ logger .warning ("Error when looking up hashing details: %s" , e )
724
+ return None
725
+
726
+ return (yield self ._lookup_3pid_v1 (id_server , medium , address ))
727
+
728
+ @defer .inlineCallbacks
729
+ def _lookup_3pid_v1 (self , id_server , medium , address ):
730
+ """Looks up a 3pid in the passed identity server using v1 lookup.
731
+
679
732
Args:
680
733
id_server (str): The server name (including port, if required)
681
734
of the identity server to use.
@@ -685,10 +738,6 @@ def _lookup_3pid(self, id_server, medium, address):
685
738
Returns:
686
739
str: the matrix ID of the 3pid, or None if it is not recognized.
687
740
"""
688
- if not self ._enable_lookup :
689
- raise SynapseError (
690
- 403 , "Looking up third-party identifiers is denied from this server"
691
- )
692
741
try :
693
742
data = yield self .simple_http_client .get_json (
694
743
"%s%s/_matrix/identity/api/v1/lookup" % (id_server_scheme , id_server ),
@@ -702,9 +751,116 @@ def _lookup_3pid(self, id_server, medium, address):
702
751
return data ["mxid" ]
703
752
704
753
except IOError as e :
705
- logger .warn ("Error from identity server lookup: %s" % (e ,))
754
+ logger .warning ("Error from v1 identity server lookup: %s" % (e ,))
755
+
756
+ return None
757
+
758
+ @defer .inlineCallbacks
759
+ def _lookup_3pid_v2 (self , id_server , id_access_token , medium , address ):
760
+ """Looks up a 3pid in the passed identity server using v2 lookup.
761
+
762
+ Args:
763
+ id_server (str): The server name (including port, if required)
764
+ of the identity server to use.
765
+ id_access_token (str): The access token to authenticate to the identity server with
766
+ medium (str): The type of the third party identifier (e.g. "email").
767
+ address (str): The third party identifier (e.g. "[email protected] ").
768
+
769
+ Returns:
770
+ Deferred[str|None]: the matrix ID of the 3pid, or None if it is not recognised.
771
+ """
772
+ # Check what hashing details are supported by this identity server
773
+ hash_details = yield self .simple_http_client .get_json (
774
+ "%s%s/_matrix/identity/v2/hash_details" % (id_server_scheme , id_server ),
775
+ {"access_token" : id_access_token },
776
+ )
777
+
778
+ if not isinstance (hash_details , dict ):
779
+ logger .warning (
780
+ "Got non-dict object when checking hash details of %s%s: %s" ,
781
+ id_server_scheme ,
782
+ id_server ,
783
+ hash_details ,
784
+ )
785
+ raise SynapseError (
786
+ 400 ,
787
+ "Non-dict object from %s%s during v2 hash_details request: %s"
788
+ % (id_server_scheme , id_server , hash_details ),
789
+ )
790
+
791
+ # Extract information from hash_details
792
+ supported_lookup_algorithms = hash_details .get ("algorithms" )
793
+ lookup_pepper = hash_details .get ("lookup_pepper" )
794
+ if (
795
+ not supported_lookup_algorithms
796
+ or not isinstance (supported_lookup_algorithms , list )
797
+ or not lookup_pepper
798
+ or not isinstance (lookup_pepper , str )
799
+ ):
800
+ raise SynapseError (
801
+ 400 ,
802
+ "Invalid hash details received from identity server %s%s: %s"
803
+ % (id_server_scheme , id_server , hash_details ),
804
+ )
805
+
806
+ # Check if any of the supported lookup algorithms are present
807
+ if LookupAlgorithm .SHA256 in supported_lookup_algorithms :
808
+ # Perform a hashed lookup
809
+ lookup_algorithm = LookupAlgorithm .SHA256
810
+
811
+ # Hash address, medium and the pepper with sha256
812
+ to_hash = "%s %s %s" % (address , medium , lookup_pepper )
813
+ lookup_value = sha256_and_url_safe_base64 (to_hash )
814
+
815
+ elif LookupAlgorithm .NONE in supported_lookup_algorithms :
816
+ # Perform a non-hashed lookup
817
+ lookup_algorithm = LookupAlgorithm .NONE
818
+
819
+ # Combine together plaintext address and medium
820
+ lookup_value = "%s %s" % (address , medium )
821
+
822
+ else :
823
+ logger .warning (
824
+ "None of the provided lookup algorithms of %s are supported: %s" ,
825
+ id_server ,
826
+ supported_lookup_algorithms ,
827
+ )
828
+ raise SynapseError (
829
+ 400 ,
830
+ "Provided identity server does not support any v2 lookup "
831
+ "algorithms that this homeserver supports." ,
832
+ )
833
+
834
+ # Authenticate with identity server given the access token from the client
835
+ headers = {"Authorization" : create_id_access_token_header (id_access_token )}
836
+
837
+ try :
838
+ lookup_results = yield self .simple_http_client .post_json_get_json (
839
+ "%s%s/_matrix/identity/v2/lookup" % (id_server_scheme , id_server ),
840
+ {
841
+ "addresses" : [lookup_value ],
842
+ "algorithm" : lookup_algorithm ,
843
+ "pepper" : lookup_pepper ,
844
+ },
845
+ headers = headers ,
846
+ )
847
+ except Exception as e :
848
+ logger .warning ("Error when performing a v2 3pid lookup: %s" , e )
849
+ raise SynapseError (
850
+ 500 , "Unknown error occurred during identity server lookup"
851
+ )
852
+
853
+ # Check for a mapping from what we looked up to an MXID
854
+ if "mappings" not in lookup_results or not isinstance (
855
+ lookup_results ["mappings" ], dict
856
+ ):
857
+ logger .warning ("No results from 3pid lookup" )
706
858
return None
707
859
860
+ # Return the MXID if it's available, or None otherwise
861
+ mxid = lookup_results ["mappings" ].get (lookup_value )
862
+ return mxid
863
+
708
864
@defer .inlineCallbacks
709
865
def _verify_any_signature (self , data , server_hostname ):
710
866
if server_hostname not in data ["signatures" ]:
@@ -844,7 +1000,6 @@ def _ask_id_server_for_third_party_invite(
844
1000
display_name (str): A user-friendly name to represent the invited
845
1001
user.
846
1002
"""
847
-
848
1003
is_url = "%s%s/_matrix/identity/api/v1/store-invite" % (
849
1004
id_server_scheme ,
850
1005
id_server ,
@@ -862,7 +1017,6 @@ def _ask_id_server_for_third_party_invite(
862
1017
"sender_display_name" : inviter_display_name ,
863
1018
"sender_avatar_url" : inviter_avatar_url ,
864
1019
}
865
-
866
1020
try :
867
1021
data = yield self .simple_http_client .post_json_get_json (
868
1022
is_url , invite_config
@@ -1049,7 +1203,7 @@ def _remote_reject_invite(self, requester, remote_room_hosts, room_id, target):
1049
1203
# The 'except' clause is very broad, but we need to
1050
1204
# capture everything from DNS failures upwards
1051
1205
#
1052
- logger .warn ("Failed to reject invite: %s" , e )
1206
+ logger .warning ("Failed to reject invite: %s" , e )
1053
1207
1054
1208
yield self .store .locally_reject_invite (target .to_string (), room_id )
1055
1209
return {}
0 commit comments