mirror of
https://github.com/element-hq/synapse.git
synced 2024-11-23 01:55:53 +03:00
Merge pull request #3707 from matrix-org/neilj/limit_exceeded_error
add new error type ResourceLimit
This commit is contained in:
commit
4c22b4047b
9 changed files with 75 additions and 24 deletions
1
changelog.d/3707.misc
Normal file
1
changelog.d/3707.misc
Normal file
|
@ -0,0 +1 @@
|
|||
add new error type ResourceLimit
|
|
@ -25,7 +25,7 @@ from twisted.internet import defer
|
|||
import synapse.types
|
||||
from synapse import event_auth
|
||||
from synapse.api.constants import EventTypes, JoinRules, Membership
|
||||
from synapse.api.errors import AuthError, Codes
|
||||
from synapse.api.errors import AuthError, Codes, ResourceLimitError
|
||||
from synapse.types import UserID
|
||||
from synapse.util.caches import CACHE_SIZE_FACTOR, register_cache
|
||||
from synapse.util.caches.lrucache import LruCache
|
||||
|
@ -784,10 +784,11 @@ class Auth(object):
|
|||
MAU cohort
|
||||
"""
|
||||
if self.hs.config.hs_disabled:
|
||||
raise AuthError(
|
||||
raise ResourceLimitError(
|
||||
403, self.hs.config.hs_disabled_message,
|
||||
errcode=Codes.RESOURCE_LIMIT_EXCEED,
|
||||
admin_uri=self.hs.config.admin_uri,
|
||||
limit_type=self.hs.config.hs_disabled_limit_type
|
||||
)
|
||||
if self.hs.config.limit_usage_by_mau is True:
|
||||
# If the user is already part of the MAU cohort
|
||||
|
@ -798,8 +799,10 @@ class Auth(object):
|
|||
# Else if there is no room in the MAU bucket, bail
|
||||
current_mau = yield self.store.get_monthly_active_count()
|
||||
if current_mau >= self.hs.config.max_mau_value:
|
||||
raise AuthError(
|
||||
raise ResourceLimitError(
|
||||
403, "Monthly Active User Limit Exceeded",
|
||||
|
||||
admin_uri=self.hs.config.admin_uri,
|
||||
errcode=Codes.RESOURCE_LIMIT_EXCEED
|
||||
errcode=Codes.RESOURCE_LIMIT_EXCEED,
|
||||
limit_type="monthly_active_user"
|
||||
)
|
||||
|
|
|
@ -224,15 +224,34 @@ class NotFoundError(SynapseError):
|
|||
|
||||
class AuthError(SynapseError):
|
||||
"""An error raised when there was a problem authorising an event."""
|
||||
def __init__(self, code, msg, errcode=Codes.FORBIDDEN, admin_uri=None):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
if "errcode" not in kwargs:
|
||||
kwargs["errcode"] = Codes.FORBIDDEN
|
||||
super(AuthError, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class ResourceLimitError(SynapseError):
|
||||
"""
|
||||
Any error raised when there is a problem with resource usage.
|
||||
For instance, the monthly active user limit for the server has been exceeded
|
||||
"""
|
||||
def __init__(
|
||||
self, code, msg,
|
||||
errcode=Codes.RESOURCE_LIMIT_EXCEED,
|
||||
admin_uri=None,
|
||||
limit_type=None,
|
||||
):
|
||||
self.admin_uri = admin_uri
|
||||
super(AuthError, self).__init__(code, msg, errcode=errcode)
|
||||
self.limit_type = limit_type
|
||||
super(ResourceLimitError, self).__init__(code, msg, errcode=errcode)
|
||||
|
||||
def error_dict(self):
|
||||
return cs_error(
|
||||
self.msg,
|
||||
self.errcode,
|
||||
admin_uri=self.admin_uri,
|
||||
limit_type=self.limit_type
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -81,6 +81,7 @@ class ServerConfig(Config):
|
|||
# Options to disable HS
|
||||
self.hs_disabled = config.get("hs_disabled", False)
|
||||
self.hs_disabled_message = config.get("hs_disabled_message", "")
|
||||
self.hs_disabled_limit_type = config.get("hs_disabled_limit_type", "")
|
||||
|
||||
# Admin uri to direct users at should their instance become blocked
|
||||
# due to resource constraints
|
||||
|
@ -340,6 +341,32 @@ class ServerConfig(Config):
|
|||
# - port: 9000
|
||||
# bind_addresses: ['::1', '127.0.0.1']
|
||||
# type: manhole
|
||||
|
||||
|
||||
# Homeserver blocking
|
||||
#
|
||||
# How to reach the server admin, used in ResourceLimitError
|
||||
# admin_uri: 'mailto:admin@server.com'
|
||||
#
|
||||
# Global block config
|
||||
#
|
||||
# hs_disabled: False
|
||||
# hs_disabled_message: 'Human readable reason for why the HS is blocked'
|
||||
# hs_disabled_limit_type: 'error code(str), to help clients decode reason'
|
||||
#
|
||||
# Monthly Active User Blocking
|
||||
#
|
||||
# Enables monthly active user checking
|
||||
# limit_usage_by_mau: False
|
||||
# max_mau_value: 50
|
||||
#
|
||||
# Sometimes the server admin will want to ensure certain accounts are
|
||||
# never blocked by mau checking. These accounts are specified here.
|
||||
#
|
||||
# mau_limit_reserved_threepids:
|
||||
# - medium: 'email'
|
||||
# address: 'reserved_user@example.com'
|
||||
|
||||
""" % locals()
|
||||
|
||||
def read_arguments(self, args):
|
||||
|
|
|
@ -21,7 +21,7 @@ from twisted.internet import defer
|
|||
|
||||
import synapse.handlers.auth
|
||||
from synapse.api.auth import Auth
|
||||
from synapse.api.errors import AuthError, Codes
|
||||
from synapse.api.errors import AuthError, Codes, ResourceLimitError
|
||||
from synapse.types import UserID
|
||||
|
||||
from tests import unittest
|
||||
|
@ -455,7 +455,7 @@ class AuthTestCase(unittest.TestCase):
|
|||
return_value=defer.succeed(lots_of_users)
|
||||
)
|
||||
|
||||
with self.assertRaises(AuthError) as e:
|
||||
with self.assertRaises(ResourceLimitError) as e:
|
||||
yield self.auth.check_auth_blocking()
|
||||
self.assertEquals(e.exception.admin_uri, self.hs.config.admin_uri)
|
||||
self.assertEquals(e.exception.errcode, Codes.RESOURCE_LIMIT_EXCEED)
|
||||
|
@ -471,7 +471,7 @@ class AuthTestCase(unittest.TestCase):
|
|||
def test_hs_disabled(self):
|
||||
self.hs.config.hs_disabled = True
|
||||
self.hs.config.hs_disabled_message = "Reason for being disabled"
|
||||
with self.assertRaises(AuthError) as e:
|
||||
with self.assertRaises(ResourceLimitError) as e:
|
||||
yield self.auth.check_auth_blocking()
|
||||
self.assertEquals(e.exception.admin_uri, self.hs.config.admin_uri)
|
||||
self.assertEquals(e.exception.errcode, Codes.RESOURCE_LIMIT_EXCEED)
|
||||
|
|
|
@ -20,7 +20,7 @@ from twisted.internet import defer
|
|||
|
||||
import synapse
|
||||
import synapse.api.errors
|
||||
from synapse.api.errors import AuthError
|
||||
from synapse.api.errors import ResourceLimitError
|
||||
from synapse.handlers.auth import AuthHandler
|
||||
|
||||
from tests import unittest
|
||||
|
@ -130,13 +130,13 @@ class AuthTestCase(unittest.TestCase):
|
|||
return_value=defer.succeed(self.large_number_of_users)
|
||||
)
|
||||
|
||||
with self.assertRaises(AuthError):
|
||||
with self.assertRaises(ResourceLimitError):
|
||||
yield self.auth_handler.get_access_token_for_user_id('user_a')
|
||||
|
||||
self.hs.get_datastore().get_monthly_active_count = Mock(
|
||||
return_value=defer.succeed(self.large_number_of_users)
|
||||
)
|
||||
with self.assertRaises(AuthError):
|
||||
with self.assertRaises(ResourceLimitError):
|
||||
yield self.auth_handler.validate_short_term_login_token_and_get_user_id(
|
||||
self._get_macaroon().serialize()
|
||||
)
|
||||
|
@ -149,13 +149,13 @@ class AuthTestCase(unittest.TestCase):
|
|||
self.hs.get_datastore().get_monthly_active_count = Mock(
|
||||
return_value=defer.succeed(self.hs.config.max_mau_value)
|
||||
)
|
||||
with self.assertRaises(AuthError):
|
||||
with self.assertRaises(ResourceLimitError):
|
||||
yield self.auth_handler.get_access_token_for_user_id('user_a')
|
||||
|
||||
self.hs.get_datastore().get_monthly_active_count = Mock(
|
||||
return_value=defer.succeed(self.hs.config.max_mau_value)
|
||||
)
|
||||
with self.assertRaises(AuthError):
|
||||
with self.assertRaises(ResourceLimitError):
|
||||
yield self.auth_handler.validate_short_term_login_token_and_get_user_id(
|
||||
self._get_macaroon().serialize()
|
||||
)
|
||||
|
|
|
@ -17,7 +17,7 @@ from mock import Mock
|
|||
|
||||
from twisted.internet import defer
|
||||
|
||||
from synapse.api.errors import AuthError
|
||||
from synapse.api.errors import ResourceLimitError
|
||||
from synapse.handlers.register import RegistrationHandler
|
||||
from synapse.types import UserID, create_requester
|
||||
|
||||
|
@ -109,13 +109,13 @@ class RegistrationTestCase(unittest.TestCase):
|
|||
self.store.get_monthly_active_count = Mock(
|
||||
return_value=defer.succeed(self.lots_of_users)
|
||||
)
|
||||
with self.assertRaises(AuthError):
|
||||
with self.assertRaises(ResourceLimitError):
|
||||
yield self.handler.get_or_create_user("requester", 'b', "display_name")
|
||||
|
||||
self.store.get_monthly_active_count = Mock(
|
||||
return_value=defer.succeed(self.hs.config.max_mau_value)
|
||||
)
|
||||
with self.assertRaises(AuthError):
|
||||
with self.assertRaises(ResourceLimitError):
|
||||
yield self.handler.get_or_create_user("requester", 'b', "display_name")
|
||||
|
||||
@defer.inlineCallbacks
|
||||
|
@ -124,13 +124,13 @@ class RegistrationTestCase(unittest.TestCase):
|
|||
self.store.get_monthly_active_count = Mock(
|
||||
return_value=defer.succeed(self.lots_of_users)
|
||||
)
|
||||
with self.assertRaises(AuthError):
|
||||
with self.assertRaises(ResourceLimitError):
|
||||
yield self.handler.register(localpart="local_part")
|
||||
|
||||
self.store.get_monthly_active_count = Mock(
|
||||
return_value=defer.succeed(self.hs.config.max_mau_value)
|
||||
)
|
||||
with self.assertRaises(AuthError):
|
||||
with self.assertRaises(ResourceLimitError):
|
||||
yield self.handler.register(localpart="local_part")
|
||||
|
||||
@defer.inlineCallbacks
|
||||
|
@ -139,11 +139,11 @@ class RegistrationTestCase(unittest.TestCase):
|
|||
self.store.get_monthly_active_count = Mock(
|
||||
return_value=defer.succeed(self.lots_of_users)
|
||||
)
|
||||
with self.assertRaises(AuthError):
|
||||
with self.assertRaises(ResourceLimitError):
|
||||
yield self.handler.register_saml2(localpart="local_part")
|
||||
|
||||
self.store.get_monthly_active_count = Mock(
|
||||
return_value=defer.succeed(self.hs.config.max_mau_value)
|
||||
)
|
||||
with self.assertRaises(AuthError):
|
||||
with self.assertRaises(ResourceLimitError):
|
||||
yield self.handler.register_saml2(localpart="local_part")
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
# limitations under the License.
|
||||
from twisted.internet import defer
|
||||
|
||||
from synapse.api.errors import AuthError, Codes
|
||||
from synapse.api.errors import Codes, ResourceLimitError
|
||||
from synapse.api.filtering import DEFAULT_FILTER_COLLECTION
|
||||
from synapse.handlers.sync import SyncConfig, SyncHandler
|
||||
from synapse.types import UserID
|
||||
|
@ -49,7 +49,7 @@ class SyncTestCase(tests.unittest.TestCase):
|
|||
|
||||
# Test that global lock works
|
||||
self.hs.config.hs_disabled = True
|
||||
with self.assertRaises(AuthError) as e:
|
||||
with self.assertRaises(ResourceLimitError) as e:
|
||||
yield self.sync_handler.wait_for_sync_for_user(sync_config)
|
||||
self.assertEquals(e.exception.errcode, Codes.RESOURCE_LIMIT_EXCEED)
|
||||
|
||||
|
@ -57,7 +57,7 @@ class SyncTestCase(tests.unittest.TestCase):
|
|||
|
||||
sync_config = self._generate_sync_config(user_id2)
|
||||
|
||||
with self.assertRaises(AuthError) as e:
|
||||
with self.assertRaises(ResourceLimitError) as e:
|
||||
yield self.sync_handler.wait_for_sync_for_user(sync_config)
|
||||
self.assertEquals(e.exception.errcode, Codes.RESOURCE_LIMIT_EXCEED)
|
||||
|
||||
|
|
|
@ -137,6 +137,7 @@ def setup_test_homeserver(
|
|||
config.limit_usage_by_mau = False
|
||||
config.hs_disabled = False
|
||||
config.hs_disabled_message = ""
|
||||
config.hs_disabled_limit_type = ""
|
||||
config.max_mau_value = 50
|
||||
config.mau_limits_reserved_threepids = []
|
||||
config.admin_uri = None
|
||||
|
|
Loading…
Reference in a new issue