mirror of
https://git.anonymousland.org/anonymousland/synapse.git
synced 2025-08-13 09:05:30 -04:00
Ratelimit 3PID /requestToken API (#9238)
This commit is contained in:
parent
54a6afeee3
commit
4b73488e81
11 changed files with 159 additions and 14 deletions
|
@ -24,7 +24,7 @@ import pkg_resources
|
|||
|
||||
import synapse.rest.admin
|
||||
from synapse.api.constants import LoginType, Membership
|
||||
from synapse.api.errors import Codes
|
||||
from synapse.api.errors import Codes, HttpResponseException
|
||||
from synapse.rest.client.v1 import login, room
|
||||
from synapse.rest.client.v2_alpha import account, register
|
||||
from synapse.rest.synapse.client.password_reset import PasswordResetSubmitTokenResource
|
||||
|
@ -112,6 +112,56 @@ class PasswordResetTestCase(unittest.HomeserverTestCase):
|
|||
# Assert we can't log in with the old password
|
||||
self.attempt_wrong_password_login("kermit", old_password)
|
||||
|
||||
@override_config({"rc_3pid_validation": {"burst_count": 3}})
|
||||
def test_ratelimit_by_email(self):
|
||||
"""Test that we ratelimit /requestToken for the same email.
|
||||
"""
|
||||
old_password = "monkey"
|
||||
new_password = "kangeroo"
|
||||
|
||||
user_id = self.register_user("kermit", old_password)
|
||||
self.login("kermit", old_password)
|
||||
|
||||
email = "test1@example.com"
|
||||
|
||||
# Add a threepid
|
||||
self.get_success(
|
||||
self.store.user_add_threepid(
|
||||
user_id=user_id,
|
||||
medium="email",
|
||||
address=email,
|
||||
validated_at=0,
|
||||
added_at=0,
|
||||
)
|
||||
)
|
||||
|
||||
def reset(ip):
|
||||
client_secret = "foobar"
|
||||
session_id = self._request_token(email, client_secret, ip)
|
||||
|
||||
self.assertEquals(len(self.email_attempts), 1)
|
||||
link = self._get_link_from_email()
|
||||
|
||||
self._validate_token(link)
|
||||
|
||||
self._reset_password(new_password, session_id, client_secret)
|
||||
|
||||
self.email_attempts.clear()
|
||||
|
||||
# We expect to be able to make three requests before getting rate
|
||||
# limited.
|
||||
#
|
||||
# We change IPs to ensure that we're not being ratelimited due to the
|
||||
# same IP
|
||||
reset("127.0.0.1")
|
||||
reset("127.0.0.2")
|
||||
reset("127.0.0.3")
|
||||
|
||||
with self.assertRaises(HttpResponseException) as cm:
|
||||
reset("127.0.0.4")
|
||||
|
||||
self.assertEqual(cm.exception.code, 429)
|
||||
|
||||
def test_basic_password_reset_canonicalise_email(self):
|
||||
"""Test basic password reset flow
|
||||
Request password reset with different spelling
|
||||
|
@ -239,13 +289,18 @@ class PasswordResetTestCase(unittest.HomeserverTestCase):
|
|||
|
||||
self.assertIsNotNone(session_id)
|
||||
|
||||
def _request_token(self, email, client_secret):
|
||||
def _request_token(self, email, client_secret, ip="127.0.0.1"):
|
||||
channel = self.make_request(
|
||||
"POST",
|
||||
b"account/password/email/requestToken",
|
||||
{"client_secret": client_secret, "email": email, "send_attempt": 1},
|
||||
client_ip=ip,
|
||||
)
|
||||
self.assertEquals(200, channel.code, channel.result)
|
||||
|
||||
if channel.code != 200:
|
||||
raise HttpResponseException(
|
||||
channel.code, channel.result["reason"], channel.result["body"],
|
||||
)
|
||||
|
||||
return channel.json_body["sid"]
|
||||
|
||||
|
@ -509,6 +564,21 @@ class ThreepidEmailRestTestCase(unittest.HomeserverTestCase):
|
|||
def test_address_trim(self):
|
||||
self.get_success(self._add_email(" foo@test.bar ", "foo@test.bar"))
|
||||
|
||||
@override_config({"rc_3pid_validation": {"burst_count": 3}})
|
||||
def test_ratelimit_by_ip(self):
|
||||
"""Tests that adding emails is ratelimited by IP
|
||||
"""
|
||||
|
||||
# We expect to be able to set three emails before getting ratelimited.
|
||||
self.get_success(self._add_email("foo1@test.bar", "foo1@test.bar"))
|
||||
self.get_success(self._add_email("foo2@test.bar", "foo2@test.bar"))
|
||||
self.get_success(self._add_email("foo3@test.bar", "foo3@test.bar"))
|
||||
|
||||
with self.assertRaises(HttpResponseException) as cm:
|
||||
self.get_success(self._add_email("foo4@test.bar", "foo4@test.bar"))
|
||||
|
||||
self.assertEqual(cm.exception.code, 429)
|
||||
|
||||
def test_add_email_if_disabled(self):
|
||||
"""Test adding email to profile when doing so is disallowed
|
||||
"""
|
||||
|
@ -777,7 +847,11 @@ class ThreepidEmailRestTestCase(unittest.HomeserverTestCase):
|
|||
body["next_link"] = next_link
|
||||
|
||||
channel = self.make_request("POST", b"account/3pid/email/requestToken", body,)
|
||||
self.assertEquals(expect_code, channel.code, channel.result)
|
||||
|
||||
if channel.code != expect_code:
|
||||
raise HttpResponseException(
|
||||
channel.code, channel.result["reason"], channel.result["body"],
|
||||
)
|
||||
|
||||
return channel.json_body.get("sid")
|
||||
|
||||
|
@ -823,10 +897,12 @@ class ThreepidEmailRestTestCase(unittest.HomeserverTestCase):
|
|||
def _add_email(self, request_email, expected_email):
|
||||
"""Test adding an email to profile
|
||||
"""
|
||||
previous_email_attempts = len(self.email_attempts)
|
||||
|
||||
client_secret = "foobar"
|
||||
session_id = self._request_token(request_email, client_secret)
|
||||
|
||||
self.assertEquals(len(self.email_attempts), 1)
|
||||
self.assertEquals(len(self.email_attempts) - previous_email_attempts, 1)
|
||||
link = self._get_link_from_email()
|
||||
|
||||
self._validate_token(link)
|
||||
|
@ -855,4 +931,6 @@ class ThreepidEmailRestTestCase(unittest.HomeserverTestCase):
|
|||
|
||||
self.assertEqual(200, int(channel.result["code"]), msg=channel.result["body"])
|
||||
self.assertEqual("email", channel.json_body["threepids"][0]["medium"])
|
||||
self.assertEqual(expected_email, channel.json_body["threepids"][0]["address"])
|
||||
|
||||
threepids = {threepid["address"] for threepid in channel.json_body["threepids"]}
|
||||
self.assertIn(expected_email, threepids)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue