diff --git a/changelog.d/9203.feature b/changelog.d/9203.feature new file mode 100644 index 000000000000..36b66a47a854 --- /dev/null +++ b/changelog.d/9203.feature @@ -0,0 +1 @@ +Add some configuration settings to make users' profile data more private. diff --git a/docs/sample_config.yaml b/docs/sample_config.yaml index 6d265d29729c..499c154182e6 100644 --- a/docs/sample_config.yaml +++ b/docs/sample_config.yaml @@ -105,6 +105,14 @@ pid_file: DATADIR/homeserver.pid # #limit_profile_requests_to_users_who_share_rooms: true +# Uncomment to prevent a user's profile data from being retrieved and +# displayed in a room until they have joined it. By default, a user's +# profile data is included in an invite event, regardless of the values +# of the above two settings, and whether or not the users share a server. +# Defaults to 'true'. +# +#include_profile_data_on_invite: false + # If set to 'true', removes the need for authentication to access the server's # public rooms directory through the client API, meaning that anyone can # query the room directory. Defaults to 'false'. @@ -699,6 +707,12 @@ acme: # - matrix.org # - example.com +# Uncomment to disable profile lookup over federation. By default, the +# Federation API allows other homeservers to obtain profile data of any user +# on this homeserver. Defaults to 'true'. +# +#allow_profile_lookup_over_federation: false + ## Caching ## diff --git a/synapse/config/federation.py b/synapse/config/federation.py index 9f3c57e6a1d4..55e4db54425d 100644 --- a/synapse/config/federation.py +++ b/synapse/config/federation.py @@ -41,6 +41,10 @@ def read_config(self, config, **kwargs): ) self.federation_metrics_domains = set(federation_metrics_domains) + self.allow_profile_lookup_over_federation = config.get( + "allow_profile_lookup_over_federation", True + ) + def generate_config_section(self, config_dir_path, server_name, **kwargs): return """\ ## Federation ## @@ -66,6 +70,12 @@ def generate_config_section(self, config_dir_path, server_name, **kwargs): #federation_metrics_domains: # - matrix.org # - example.com + + # Uncomment to disable profile lookup over federation. By default, the + # Federation API allows other homeservers to obtain profile data of any user + # on this homeserver. Defaults to 'true'. + # + #allow_profile_lookup_over_federation: false """ diff --git a/synapse/config/server.py b/synapse/config/server.py index 47a037017332..a38976ce61bd 100644 --- a/synapse/config/server.py +++ b/synapse/config/server.py @@ -192,6 +192,12 @@ def read_config(self, config, **kwargs): "limit_profile_requests_to_users_who_share_rooms", False, ) + # Whether to retrieve and display profile data for a user when they + # are invited to a room + self.include_profile_data_on_invite = config.get( + "include_profile_data_on_invite", True + ) + if "restrict_public_rooms_to_local_users" in config and ( "allow_public_rooms_without_auth" in config or "allow_public_rooms_over_federation" in config @@ -779,6 +785,14 @@ def generate_config_section( # #limit_profile_requests_to_users_who_share_rooms: true + # Uncomment to prevent a user's profile data from being retrieved and + # displayed in a room until they have joined it. By default, a user's + # profile data is included in an invite event, regardless of the values + # of the above two settings, and whether or not the users share a server. + # Defaults to 'true'. + # + #include_profile_data_on_invite: false + # If set to 'true', removes the need for authentication to access the server's # public rooms directory through the client API, meaning that anyone can # query the room directory. Defaults to 'false'. diff --git a/synapse/federation/transport/server.py b/synapse/federation/transport/server.py index 95c64510a927..083a42633503 100644 --- a/synapse/federation/transport/server.py +++ b/synapse/federation/transport/server.py @@ -480,10 +480,9 @@ class FederationQueryServlet(BaseFederationServlet): # This is when we receive a server-server Query async def on_GET(self, origin, content, query, query_type): - return await self.handler.on_query_request( - query_type, - {k.decode("utf8"): v[0].decode("utf-8") for k, v in query.items()}, - ) + args = {k.decode("utf8"): v[0].decode("utf-8") for k, v in query.items()} + args["origin"] = origin + return await self.handler.on_query_request(query_type, args) class FederationMakeJoinServlet(BaseFederationServlet): diff --git a/synapse/handlers/message.py b/synapse/handlers/message.py index a15336bf00a8..4716b4dba4db 100644 --- a/synapse/handlers/message.py +++ b/synapse/handlers/message.py @@ -381,6 +381,12 @@ def __init__(self, hs: "HomeServer"): self.room_invite_state_types = self.hs.config.room_invite_state_types + self.membership_types_to_include_profile_data_in = ( + {Membership.JOIN, Membership.INVITE} + if self.hs.config.include_profile_data_on_invite + else {Membership.JOIN} + ) + self.send_event = ReplicationSendEventRestServlet.make_client(hs) # This is only used to get at ratelimit function, and maybe_kick_guest_users @@ -494,7 +500,7 @@ async def create_event( membership = builder.content.get("membership", None) target = UserID.from_string(builder.state_key) - if membership in {Membership.JOIN, Membership.INVITE}: + if membership in self.membership_types_to_include_profile_data_in: # If event doesn't include a display name, add one. profile = self.profile_handler content = builder.content diff --git a/synapse/handlers/profile.py b/synapse/handlers/profile.py index c02b95103199..3d7b652009a5 100644 --- a/synapse/handlers/profile.py +++ b/synapse/handlers/profile.py @@ -309,6 +309,16 @@ async def set_avatar_url( await self._update_join_states(requester, target_user) async def on_profile_query(self, args: JsonDict) -> JsonDict: + """Handles federation profile query requests. + """ + + if not self.hs.config.allow_profile_lookup_over_federation: + raise SynapseError( + 403, + "Profile lookup over federation is disabled on this homeserver", + Codes.FORBIDDEN, + ) + user = UserID.from_string(args["user_id"]) if not self.hs.is_mine(user): raise SynapseError(400, "User is not hosted on this homeserver") diff --git a/synapse/replication/http/federation.py b/synapse/replication/http/federation.py index 7a0dbb5b1ada..8af53b4f2815 100644 --- a/synapse/replication/http/federation.py +++ b/synapse/replication/http/federation.py @@ -213,8 +213,9 @@ async def _handle_request(self, request, query_type): content = parse_json_object_from_request(request) args = content["args"] + args["origin"] = content["origin"] - logger.info("Got %r query", query_type) + logger.info("Got %r query from %s", query_type, args["origin"]) result = await self.registry.on_query(query_type, args) diff --git a/tests/handlers/test_profile.py b/tests/handlers/test_profile.py index 022943a10a8e..192255702acf 100644 --- a/tests/handlers/test_profile.py +++ b/tests/handlers/test_profile.py @@ -184,7 +184,11 @@ def test_incoming_fed_query(self): response = yield defer.ensureDeferred( self.query_handlers["profile"]( - {"user_id": "@caroline:test", "field": "displayname"} + { + "user_id": "@caroline:test", + "field": "displayname", + "origin": "servername.tld", + } ) )