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

add new error type ResourceLimit #3707

Merged
merged 5 commits into from
Aug 17, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/3707.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
add new error type ResourceLimit
11 changes: 7 additions & 4 deletions synapse/api/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
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
Expand Down Expand Up @@ -784,10 +784,11 @@ def check_auth_blocking(self, user_id=None):
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
Expand All @@ -798,8 +799,10 @@ def check_auth_blocking(self, user_id=None):
# 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"
)
23 changes: 21 additions & 2 deletions synapse/api/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,15 +224,34 @@ def __init__(self, msg="Not found", errcode=Codes.NOT_FOUND):

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
)


Expand Down
27 changes: 27 additions & 0 deletions synapse/config/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ def read_config(self, 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", "")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add some documentation about what these options do. Traditionally, we usually do this buy including them in the default config below (commented out where appropriate)


# Admin uri to direct users at should their instance become blocked
# due to resource constraints
Expand Down Expand Up @@ -340,6 +341,32 @@ def default_config(self, server_name, **kwargs):
# - 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):
Expand Down
6 changes: 3 additions & 3 deletions tests/api/test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

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
Expand Down Expand Up @@ -455,7 +455,7 @@ def test_blocking_mau(self):
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)
Expand All @@ -471,7 +471,7 @@ def test_blocking_mau(self):
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)
Expand Down
10 changes: 5 additions & 5 deletions tests/handlers/test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

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
Expand Down Expand Up @@ -130,13 +130,13 @@ def test_mau_limits_exceeded_large(self):
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()
)
Expand All @@ -149,13 +149,13 @@ def test_mau_limits_parity(self):
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()
)
Expand Down
14 changes: 7 additions & 7 deletions tests/handlers/test_register.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

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

Expand Down Expand Up @@ -109,13 +109,13 @@ def test_get_or_create_user_mau_blocked(self):
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
Expand All @@ -124,13 +124,13 @@ def test_register_mau_blocked(self):
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
Expand All @@ -139,11 +139,11 @@ def test_register_saml2_mau_blocked(self):
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")
6 changes: 3 additions & 3 deletions tests/handlers/test_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -49,15 +49,15 @@ def test_wait_for_sync_for_user_auth_blocking(self):

# 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)

self.hs.config.hs_disabled = False

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)

Expand Down
1 change: 1 addition & 0 deletions tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down