Allow tracking puppeted users for MAU (#11561)

Currently when puppeting another user, the user doing the puppeting is
tracked for client IPs and MAU (if configured).

When tracking MAU is important, it becomes necessary to be possible to
also track the client IPs and MAU of puppeted users. As an example a
client that manages user creation and creation of tokens via the Synapse
admin API, passing those tokens for the client to use.

This PR adds optional configuration to enable tracking of puppeted users
into monthly active users. The default behaviour stays the same.

Signed-off-by: Jason Robinson <jasonr@matrix.org>
This commit is contained in:
Jason Robinson 2022-01-12 18:09:36 +02:00 committed by GitHub
parent 99ba5ae7b7
commit 2560b1b6b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 63 additions and 0 deletions

View File

@ -0,0 +1 @@
Add `track_puppeted_user_ips` config flag to track puppeted user IP addresses. This also includes them in monthly active user counts.

View File

@ -1503,6 +1503,12 @@ room_prejoin_state:
#additional_event_types: #additional_event_types:
# - org.example.custom.event.type # - org.example.custom.event.type
# If enabled, puppeted user IP's 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.
# Implicitly enables MAU tracking for puppeted users.
#track_puppeted_user_ips: false
# A list of application service config files to use # A list of application service config files to use
# #

View File

@ -71,6 +71,7 @@ class Auth:
self._auth_blocking = AuthBlocking(self.hs) self._auth_blocking = AuthBlocking(self.hs)
self._track_appservice_user_ips = hs.config.appservice.track_appservice_user_ips self._track_appservice_user_ips = hs.config.appservice.track_appservice_user_ips
self._track_puppeted_user_ips = hs.config.api.track_puppeted_user_ips
self._macaroon_secret_key = hs.config.key.macaroon_secret_key self._macaroon_secret_key = hs.config.key.macaroon_secret_key
self._force_tracing_for_users = hs.config.tracing.force_tracing_for_users self._force_tracing_for_users = hs.config.tracing.force_tracing_for_users
@ -246,6 +247,18 @@ class Auth:
user_agent=user_agent, user_agent=user_agent,
device_id=device_id, 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._track_puppeted_user_ips
):
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: if is_guest and not allow_guest:
raise AuthError( raise AuthError(

View File

@ -29,6 +29,7 @@ class ApiConfig(Config):
def read_config(self, config: JsonDict, **kwargs): def read_config(self, config: JsonDict, **kwargs):
validate_config(_MAIN_SCHEMA, config, ()) validate_config(_MAIN_SCHEMA, config, ())
self.room_prejoin_state = list(self._get_prejoin_state_types(config)) self.room_prejoin_state = list(self._get_prejoin_state_types(config))
self.track_puppeted_user_ips = config.get("track_puppeted_user_ips", False)
def generate_config_section(cls, **kwargs) -> str: def generate_config_section(cls, **kwargs) -> str:
formatted_default_state_types = "\n".join( formatted_default_state_types = "\n".join(
@ -59,6 +60,12 @@ class ApiConfig(Config):
# #
#additional_event_types: #additional_event_types:
# - org.example.custom.event.type # - org.example.custom.event.type
# If enabled, puppeted user IP's 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.
# Implicitly enables MAU tracking for puppeted users.
#track_puppeted_user_ips: false
""" % { """ % {
"formatted_default_state_types": formatted_default_state_types "formatted_default_state_types": formatted_default_state_types
} }
@ -138,5 +145,8 @@ _MAIN_SCHEMA = {
"properties": { "properties": {
"room_prejoin_state": _ROOM_PREJOIN_STATE_CONFIG_SCHEMA, "room_prejoin_state": _ROOM_PREJOIN_STATE_CONFIG_SCHEMA,
"room_invite_state_types": _ROOM_INVITE_STATE_TYPES_SCHEMA, "room_invite_state_types": _ROOM_INVITE_STATE_TYPES_SCHEMA,
"track_puppeted_user_ips": {
"type": "boolean",
},
}, },
} }

View File

@ -274,6 +274,39 @@ class AuthTestCase(unittest.HomeserverTestCase):
self.assertEquals(failure.value.code, 400) self.assertEquals(failure.value.code, 400)
self.assertEquals(failure.value.errcode, Codes.EXCLUSIVE) self.assertEquals(failure.value.errcode, Codes.EXCLUSIVE)
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._track_puppeted_user_ips = 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): def test_get_user_from_macaroon(self):
self.store.get_user_by_access_token = simple_async_mock( self.store.get_user_by_access_token = simple_async_mock(
TokenLookupResult(user_id="@baldrick:matrix.org", device_id="device") TokenLookupResult(user_id="@baldrick:matrix.org", device_id="device")