diff --git a/changelog.d/11561.feature b/changelog.d/11561.feature new file mode 100644 index 000000000000..6b81a8ed6176 --- /dev/null +++ b/changelog.d/11561.feature @@ -0,0 +1 @@ +Add config flag `mau_track_puppeted_users` to allow tracking puppeted users in terms of monthly active users. diff --git a/docs/sample_config.yaml b/docs/sample_config.yaml index 6696ed5d1ef9..c3152c2b84a4 100644 --- a/docs/sample_config.yaml +++ b/docs/sample_config.yaml @@ -439,6 +439,11 @@ manhole_settings: # - medium: 'email' # address: 'reserved_user@example.com' +# If enabled, puppeted users can also be tracked. By default when +# puppeting another user, the user who has created the access token +# for puppeting is tracked. If this is enabled, both requests are tracked. +#mau_track_puppeted_users: false + # Used by phonehome stats to group together related servers. #server_context: context diff --git a/synapse/api/auth.py b/synapse/api/auth.py index 44883c6663ff..af5e63e339f7 100644 --- a/synapse/api/auth.py +++ b/synapse/api/auth.py @@ -73,6 +73,7 @@ def __init__(self, hs: "HomeServer"): self._track_appservice_user_ips = hs.config.appservice.track_appservice_user_ips self._macaroon_secret_key = hs.config.key.macaroon_secret_key self._force_tracing_for_users = hs.config.tracing.force_tracing_for_users + self._mau_track_puppeted_users = hs.config.server.mau_track_puppeted_users async def check_user_in_room( self, @@ -208,6 +209,18 @@ async def get_user_by_req( user_agent=user_agent, device_id=device_id, ) + # Track also the puppeted user client IP if enabled and the user is puppeting + if ( + user_info.user_id != user_info.token_owner + and self._mau_track_puppeted_users + ): + await self.store.insert_client_ip( + user_id=user_info.user_id, + access_token=access_token, + ip=ip_addr, + user_agent=user_agent, + device_id=device_id, + ) if is_guest and not allow_guest: raise AuthError( diff --git a/synapse/config/server.py b/synapse/config/server.py index 1de2dea9b024..254a2c121093 100644 --- a/synapse/config/server.py +++ b/synapse/config/server.py @@ -418,6 +418,7 @@ def read_config(self, config, **kwargs): self.mau_trial_days = config.get("mau_trial_days", 0) self.mau_limit_alerting = config.get("mau_limit_alerting", True) + self.mau_track_puppeted_users = config.get("mau_track_puppeted_users", False) # How long to keep redacted events in the database in unredacted form # before redacting them. @@ -1128,6 +1129,11 @@ def generate_config_section( # - medium: 'email' # address: 'reserved_user@example.com' + # If enabled, puppeted users can also be tracked. By default when + # puppeting another user, the user who has created the access token + # for puppeting is tracked. If this is enabled, both requests are tracked. + #mau_track_puppeted_users: false + # Used by phonehome stats to group together related servers. #server_context: context diff --git a/tests/api/test_auth.py b/tests/api/test_auth.py index 3aa9ba3c43ac..c603eb004fab 100644 --- a/tests/api/test_auth.py +++ b/tests/api/test_auth.py @@ -210,6 +210,39 @@ def test_get_user_by_req_appservice_valid_token_bad_user_id(self): request.requestHeaders.getRawHeaders = mock_getRawHeaders() self.get_failure(self.auth.get_user_by_req(request), AuthError) + def test_get_user_by_req__puppeted_token__not_tracking_puppeted_mau(self): + self.store.get_user_by_access_token = simple_async_mock( + TokenLookupResult( + user_id="@baldrick:matrix.org", + device_id="device", + token_owner="@admin:matrix.org", + ) + ) + self.store.insert_client_ip = simple_async_mock(None) + request = Mock(args={}) + request.getClientIP.return_value = "127.0.0.1" + request.args[b"access_token"] = [self.test_token] + request.requestHeaders.getRawHeaders = mock_getRawHeaders() + self.get_success(self.auth.get_user_by_req(request)) + self.store.insert_client_ip.assert_called_once() + + def test_get_user_by_req__puppeted_token__tracking_puppeted_mau(self): + self.auth._mau_track_puppeted_users = True + self.store.get_user_by_access_token = simple_async_mock( + TokenLookupResult( + user_id="@baldrick:matrix.org", + device_id="device", + token_owner="@admin:matrix.org", + ) + ) + self.store.insert_client_ip = simple_async_mock(None) + request = Mock(args={}) + request.getClientIP.return_value = "127.0.0.1" + request.args[b"access_token"] = [self.test_token] + request.requestHeaders.getRawHeaders = mock_getRawHeaders() + self.get_success(self.auth.get_user_by_req(request)) + self.assertEquals(self.store.insert_client_ip.call_count, 2) + def test_get_user_from_macaroon(self): self.store.get_user_by_access_token = simple_async_mock( TokenLookupResult(user_id="@baldrick:matrix.org", device_id="device")