Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit a7a9139

Browse files
committed
Merge remote-tracking branch 'origin/erikj/as_mau_block' into develop
2 parents 70586aa + 14eab1b commit a7a9139

File tree

7 files changed

+86
-9
lines changed

7 files changed

+86
-9
lines changed

changelog.d/8962.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix bug where application services couldn't register new ghost users if the server had reached its MAU limit.

synapse/api/auth_blocking.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ def __init__(self, hs):
3636
self._limit_usage_by_mau = hs.config.limit_usage_by_mau
3737
self._mau_limits_reserved_threepids = hs.config.mau_limits_reserved_threepids
3838
self._server_name = hs.hostname
39+
self._track_appservice_user_ips = hs.config.appservice.track_appservice_user_ips
3940

4041
async def check_auth_blocking(
4142
self,
@@ -76,6 +77,12 @@ async def check_auth_blocking(
7677
# We never block the server from doing actions on behalf of
7778
# users.
7879
return
80+
elif requester.app_service and not self._track_appservice_user_ips:
81+
# If we're authenticated as an appservice then we only block
82+
# auth if `track_appservice_user_ips` is set, as that option
83+
# implicitly means that application services are part of MAU
84+
# limits.
85+
return
7986

8087
# Never fail an auth check for the server notices users or support user
8188
# This can be a problem where event creation is prohibited due to blocking

synapse/handlers/auth.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,7 @@ async def get_access_token_for_user_id(
738738
device_id: Optional[str],
739739
valid_until_ms: Optional[int],
740740
puppets_user_id: Optional[str] = None,
741+
is_appservice_ghost: bool = False,
741742
) -> str:
742743
"""
743744
Creates a new access token for the user with the given user ID.
@@ -754,6 +755,7 @@ async def get_access_token_for_user_id(
754755
we should always have a device ID)
755756
valid_until_ms: when the token is valid until. None for
756757
no expiry.
758+
is_appservice_ghost: Whether the user is an application ghost user
757759
Returns:
758760
The access token for the user's session.
759761
Raises:
@@ -774,7 +776,11 @@ async def get_access_token_for_user_id(
774776
"Logging in user %s on device %s%s", user_id, device_id, fmt_expiry
775777
)
776778

777-
await self.auth.check_auth_blocking(user_id)
779+
if (
780+
not is_appservice_ghost
781+
or self.hs.config.appservice.track_appservice_user_ips
782+
):
783+
await self.auth.check_auth_blocking(user_id)
778784

779785
access_token = self.macaroon_gen.generate_access_token(user_id)
780786
await self.store.add_access_token_to_user(

synapse/handlers/register.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,7 @@ async def register_device(
630630
device_id: Optional[str],
631631
initial_display_name: Optional[str],
632632
is_guest: bool = False,
633+
is_appservice_ghost: bool = False,
633634
) -> Tuple[str, str]:
634635
"""Register a device for a user and generate an access token.
635636
@@ -651,6 +652,7 @@ async def register_device(
651652
device_id=device_id,
652653
initial_display_name=initial_display_name,
653654
is_guest=is_guest,
655+
is_appservice_ghost=is_appservice_ghost,
654656
)
655657
return r["device_id"], r["access_token"]
656658

@@ -672,7 +674,10 @@ async def register_device(
672674
)
673675
else:
674676
access_token = await self._auth_handler.get_access_token_for_user_id(
675-
user_id, device_id=registered_device_id, valid_until_ms=valid_until_ms
677+
user_id,
678+
device_id=registered_device_id,
679+
valid_until_ms=valid_until_ms,
680+
is_appservice_ghost=is_appservice_ghost,
676681
)
677682

678683
return (registered_device_id, access_token)

synapse/replication/http/login.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ def __init__(self, hs):
3636
self.registration_handler = hs.get_registration_handler()
3737

3838
@staticmethod
39-
async def _serialize_payload(user_id, device_id, initial_display_name, is_guest):
39+
async def _serialize_payload(
40+
user_id, device_id, initial_display_name, is_guest, is_appservice_ghost
41+
):
4042
"""
4143
Args:
4244
device_id (str|None): Device ID to use, if None a new one is
@@ -48,6 +50,7 @@ async def _serialize_payload(user_id, device_id, initial_display_name, is_guest)
4850
"device_id": device_id,
4951
"initial_display_name": initial_display_name,
5052
"is_guest": is_guest,
53+
"is_appservice_ghost": is_appservice_ghost,
5154
}
5255

5356
async def _handle_request(self, request, user_id):
@@ -56,9 +59,14 @@ async def _handle_request(self, request, user_id):
5659
device_id = content["device_id"]
5760
initial_display_name = content["initial_display_name"]
5861
is_guest = content["is_guest"]
62+
is_appservice_ghost = content["is_appservice_ghost"]
5963

6064
device_id, access_token = await self.registration_handler.register_device(
61-
user_id, device_id, initial_display_name, is_guest
65+
user_id,
66+
device_id,
67+
initial_display_name,
68+
is_guest,
69+
is_appservice_ghost=is_appservice_ghost,
6270
)
6371

6472
return 200, {"device_id": device_id, "access_token": access_token}

synapse/rest/client/v2_alpha/register.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -655,9 +655,13 @@ async def _do_appservice_registration(self, username, as_token, body):
655655
user_id = await self.registration_handler.appservice_register(
656656
username, as_token
657657
)
658-
return await self._create_registration_details(user_id, body)
658+
return await self._create_registration_details(
659+
user_id, body, is_appservice_ghost=True,
660+
)
659661

660-
async def _create_registration_details(self, user_id, params):
662+
async def _create_registration_details(
663+
self, user_id, params, is_appservice_ghost=False
664+
):
661665
"""Complete registration of newly-registered user
662666
663667
Allocates device_id if one was not given; also creates access_token.
@@ -674,7 +678,11 @@ async def _create_registration_details(self, user_id, params):
674678
device_id = params.get("device_id")
675679
initial_display_name = params.get("initial_device_display_name")
676680
device_id, access_token = await self.registration_handler.register_device(
677-
user_id, device_id, initial_display_name, is_guest=False
681+
user_id,
682+
device_id,
683+
initial_display_name,
684+
is_guest=False,
685+
is_appservice_ghost=is_appservice_ghost,
678686
)
679687

680688
result.update({"access_token": access_token, "device_id": device_id})

tests/test_mau.py

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
from synapse.api.constants import LoginType
2121
from synapse.api.errors import Codes, HttpResponseException, SynapseError
22+
from synapse.appservice import ApplicationService
2223
from synapse.rest.client.v2_alpha import register, sync
2324

2425
from tests import unittest
@@ -75,6 +76,45 @@ def test_simple_deny_mau(self):
7576
self.assertEqual(e.code, 403)
7677
self.assertEqual(e.errcode, Codes.RESOURCE_LIMIT_EXCEEDED)
7778

79+
def test_as_ignores_mau(self):
80+
"""Test that application services can still create users when the MAU
81+
limit has been reached. This only works when application service
82+
user ip tracking is disabled.
83+
"""
84+
85+
# Create and sync so that the MAU counts get updated
86+
token1 = self.create_user("kermit1")
87+
self.do_sync_for_user(token1)
88+
token2 = self.create_user("kermit2")
89+
self.do_sync_for_user(token2)
90+
91+
# check we're testing what we think we are: there should be two active users
92+
self.assertEqual(self.get_success(self.store.get_monthly_active_count()), 2)
93+
94+
# We've created and activated two users, we shouldn't be able to
95+
# register new users
96+
with self.assertRaises(SynapseError) as cm:
97+
self.create_user("kermit3")
98+
99+
e = cm.exception
100+
self.assertEqual(e.code, 403)
101+
self.assertEqual(e.errcode, Codes.RESOURCE_LIMIT_EXCEEDED)
102+
103+
# Cheekily add an application service that we use to register a new user
104+
# with.
105+
as_token = "foobartoken"
106+
self.store.services_cache.append(
107+
ApplicationService(
108+
token=as_token,
109+
hostname=self.hs.hostname,
110+
id="SomeASID",
111+
sender="@as_sender:test",
112+
namespaces={"users": [{"regex": "@as_*", "exclusive": True}]},
113+
)
114+
)
115+
116+
self.create_user("as_kermit4", token=as_token)
117+
78118
def test_allowed_after_a_month_mau(self):
79119
# Create and sync so that the MAU counts get updated
80120
token1 = self.create_user("kermit1")
@@ -192,7 +232,7 @@ def test_tracked_but_not_limited(self):
192232
self.reactor.advance(100)
193233
self.assertEqual(2, self.successResultOf(count))
194234

195-
def create_user(self, localpart):
235+
def create_user(self, localpart, token=None):
196236
request_data = json.dumps(
197237
{
198238
"username": localpart,
@@ -201,7 +241,9 @@ def create_user(self, localpart):
201241
}
202242
)
203243

204-
channel = self.make_request("POST", "/register", request_data)
244+
channel = self.make_request(
245+
"POST", "/register", request_data, access_token=token,
246+
)
205247

206248
if channel.code != 200:
207249
raise HttpResponseException(

0 commit comments

Comments
 (0)