def test_ratelimit(self):
        """A simple test with the default values"""
        reactor, clock = get_clock()
        rc_config = build_rc_config()
        ratelimiter = FederationRateLimiter(clock, rc_config)

        with ratelimiter.ratelimit("testhost") as d1:
            # shouldn't block
            self.successResultOf(d1)
Exemplo n.º 2
0
    def __init__(self, homeserver, server_name, server, client):
        """
        Args:
            server_name (str): Local home server host
            server (synapse.protocol.http.HttpServer): the http server to
                register listeners on
            client (synapse.protocol.http.HttpClient): the http client used to
                send requests
        """
        self.keyring = homeserver.get_keyring()
        self.clock = homeserver.get_clock()
        self.server_name = server_name
        self.server = server
        self.client = client
        self.request_handler = None
        self.received_handler = None

        self.ratelimiter = FederationRateLimiter(
            self.clock,
            window_size=homeserver.config.federation_rc_window_size,
            sleep_limit=homeserver.config.federation_rc_sleep_limit,
            sleep_msec=homeserver.config.federation_rc_sleep_delay,
            reject_limit=homeserver.config.federation_rc_reject_limit,
            concurrent_requests=homeserver.config.federation_rc_concurrent,
        )
Exemplo n.º 3
0
    def __init__(self, hs, servlet_groups=None):
        """Initialize the TransportLayerServer

        Will by default register all servlets. For custom behaviour, pass in
        a list of servlet_groups to register.

        Args:
            hs (synapse.server.HomeServer): homeserver
            servlet_groups (list[str], optional): List of servlet groups to register.
                Defaults to ``DEFAULT_SERVLET_GROUPS``.
        """
        self.hs = hs
        self.clock = hs.get_clock()
        self.servlet_groups = servlet_groups

        super(TransportLayerServer, self).__init__(hs, canonical_json=False)

        self.authenticator = Authenticator(hs)
        self.ratelimiter = FederationRateLimiter(
            self.clock,
            window_size=hs.config.federation_rc_window_size,
            sleep_limit=hs.config.federation_rc_sleep_limit,
            sleep_msec=hs.config.federation_rc_sleep_delay,
            reject_limit=hs.config.federation_rc_reject_limit,
            concurrent_requests=hs.config.federation_rc_concurrent,
        )

        self.register_servlets()
Exemplo n.º 4
0
def register_federation_servlets(hs, resource):
    federation_server.register_servlets(
        hs,
        resource=resource,
        authenticator=federation_server.Authenticator(hs),
        ratelimiter=FederationRateLimiter(hs.get_clock(),
                                          config=hs.config.rc_federation),
    )
Exemplo n.º 5
0
    def prepare(self, reactor, clock, homeserver):
        class Authenticator(object):
            def authenticate_request(self, request, content):
                return defer.succeed("otherserver.nottld")

        ratelimiter = FederationRateLimiter(clock, FederationRateLimitConfig())
        server.register_servlets(homeserver, self.resource, Authenticator(),
                                 ratelimiter)
Exemplo n.º 6
0
 def __init__(self, hs: "HomeServer"):
     super().__init__()
     self.hs = hs
     self.registration_handler = hs.get_registration_handler()
     self.ratelimiter = FederationRateLimiter(
         hs.get_clock(),
         FederationRateLimitConfig(
             # Time window of 2s
             window_size=2000,
             # Artificially delay requests if rate > sleep_limit/window_size
             sleep_limit=1,
             # Amount of artificial delay to apply
             sleep_delay=1000,
             # Error with 429 if more than reject_limit requests are queued
             reject_limit=1,
             # Allow 1 request at a time
             concurrent=1,
         ),
     )
Exemplo n.º 7
0
 def __init__(self, hs):
     """
     Args:
         hs (synapse.server.HomeServer): server
     """
     super(UsernameAvailabilityRestServlet, self).__init__()
     self.hs = hs
     self.registration_handler = hs.get_handlers().registration_handler
     self.ratelimiter = FederationRateLimiter(
         hs.get_clock(),
         # Time window of 2s
         window_size=2000,
         # Artificially delay requests if rate > sleep_limit/window_size
         sleep_limit=1,
         # Amount of artificial delay to apply
         sleep_msec=1000,
         # Error with 429 if more than reject_limit requests are queued
         reject_limit=1,
         # Allow 1 request at a time
         concurrent_requests=1,
     )
Exemplo n.º 8
0
    def test_sleep_limit(self):
        """Test what happens when we hit the sleep limit"""
        reactor, clock = get_clock()
        rc_config = build_rc_config(
            {"rc_federation": {"sleep_limit": 2, "sleep_delay": 500}}
        )
        ratelimiter = FederationRateLimiter(clock, rc_config)

        with ratelimiter.ratelimit("testhost") as d1:
            # shouldn't block
            self.successResultOf(d1)

        with ratelimiter.ratelimit("testhost") as d2:
            # nor this
            self.successResultOf(d2)

        with ratelimiter.ratelimit("testhost") as d3:
            # this one should block, though ...
            self.assertNoResult(d3)
            sleep_time = _await_resolution(reactor, d3)
            self.assertAlmostEqual(sleep_time, 500, places=3)
Exemplo n.º 9
0
def register_federation_servlets(hs, resource):
    federation_server.register_servlets(
        hs,
        resource=resource,
        authenticator=federation_server.Authenticator(hs),
        ratelimiter=FederationRateLimiter(
            hs.get_clock(),
            window_size=hs.config.federation_rc_window_size,
            sleep_limit=hs.config.federation_rc_sleep_limit,
            sleep_msec=hs.config.federation_rc_sleep_delay,
            reject_limit=hs.config.federation_rc_reject_limit,
            concurrent_requests=hs.config.federation_rc_concurrent,
        ),
    )
    def test_concurrent_limit(self):
        """Test what happens when we hit the concurrent limit"""
        reactor, clock = get_clock()
        rc_config = build_rc_config({"rc_federation": {"concurrent": 2}})
        ratelimiter = FederationRateLimiter(clock, rc_config)

        with ratelimiter.ratelimit("testhost") as d1:
            # shouldn't block
            self.successResultOf(d1)

            cm2 = ratelimiter.ratelimit("testhost")
            d2 = cm2.__enter__()
            # also shouldn't block
            self.successResultOf(d2)

            cm3 = ratelimiter.ratelimit("testhost")
            d3 = cm3.__enter__()
            # this one should block, though ...
            self.assertNoResult(d3)

            # ... until we complete an earlier request
            cm2.__exit__(None, None, None)
            self.successResultOf(d3)
Exemplo n.º 11
0
    def prepare(self, reactor, clock, homeserver):
        class Authenticator(object):
            def authenticate_request(self, request, content):
                return defer.succeed("otherserver.nottld")

        ratelimiter = FederationRateLimiter(
            clock,
            FederationRateLimitConfig(
                window_size=1,
                sleep_limit=1,
                sleep_msec=1,
                reject_limit=1000,
                concurrent_requests=1000,
            ),
        )
        server.register_servlets(homeserver, self.resource, Authenticator(),
                                 ratelimiter)
Exemplo n.º 12
0
    def __init__(self, hs):
        self.hs = hs
        self.clock = hs.get_clock()

        super(TransportLayerServer, self).__init__(hs, canonical_json=False)

        self.authenticator = Authenticator(hs)
        self.ratelimiter = FederationRateLimiter(
            self.clock,
            window_size=hs.config.federation_rc_window_size,
            sleep_limit=hs.config.federation_rc_sleep_limit,
            sleep_msec=hs.config.federation_rc_sleep_delay,
            reject_limit=hs.config.federation_rc_reject_limit,
            concurrent_requests=hs.config.federation_rc_concurrent,
        )

        self.register_servlets()
Exemplo n.º 13
0
class UsernameAvailabilityRestServlet(RestServlet):
    PATTERNS = client_patterns("/register/available")

    def __init__(self, hs: "HomeServer"):
        super().__init__()
        self.hs = hs
        self.registration_handler = hs.get_registration_handler()
        self.ratelimiter = FederationRateLimiter(
            hs.get_clock(),
            FederationRateLimitConfig(
                # Time window of 2s
                window_size=2000,
                # Artificially delay requests if rate > sleep_limit/window_size
                sleep_limit=1,
                # Amount of artificial delay to apply
                sleep_delay=1000,
                # Error with 429 if more than reject_limit requests are queued
                reject_limit=1,
                # Allow 1 request at a time
                concurrent=1,
            ),
        )

        self.inhibit_user_in_use_error = (
            hs.config.registration.inhibit_user_in_use_error)

    async def on_GET(self, request: Request) -> Tuple[int, JsonDict]:
        if not self.hs.config.registration.enable_registration:
            raise SynapseError(403,
                               "Registration has been disabled",
                               errcode=Codes.FORBIDDEN)

        if self.inhibit_user_in_use_error:
            return 200, {"available": True}

        ip = request.getClientAddress().host
        with self.ratelimiter.ratelimit(ip) as wait_deferred:
            await wait_deferred

            username = parse_string(request, "username", required=True)

            await self.registration_handler.check_username(username)

            return 200, {"available": True}
Exemplo n.º 14
0
class UsernameAvailabilityRestServlet(RestServlet):
    PATTERNS = client_patterns("/register/available")

    def __init__(self, hs):
        """
        Args:
            hs (synapse.server.HomeServer): server
        """
        super(UsernameAvailabilityRestServlet, self).__init__()
        self.hs = hs
        self.registration_handler = hs.get_registration_handler()
        self.ratelimiter = FederationRateLimiter(
            hs.get_clock(),
            FederationRateLimitConfig(
                # Time window of 2s
                window_size=2000,
                # Artificially delay requests if rate > sleep_limit/window_size
                sleep_limit=1,
                # Amount of artificial delay to apply
                sleep_msec=1000,
                # Error with 429 if more than reject_limit requests are queued
                reject_limit=1,
                # Allow 1 request at a time
                concurrent_requests=1,
            ),
        )

    @defer.inlineCallbacks
    def on_GET(self, request):
        if not self.hs.config.enable_registration:
            raise SynapseError(403,
                               "Registration has been disabled",
                               errcode=Codes.FORBIDDEN)

        ip = self.hs.get_ip_from_request(request)
        with self.ratelimiter.ratelimit(ip) as wait_deferred:
            yield wait_deferred

            username = parse_string(request, "username", required=True)

            yield self.registration_handler.check_username(username)

            return 200, {"available": True}
Exemplo n.º 15
0
 def __init__(self, hs):
     """
     Args:
         hs (synapse.server.HomeServer): server
     """
     super(UsernameAvailabilityRestServlet, self).__init__()
     self.hs = hs
     self.registration_handler = hs.get_handlers().registration_handler
     self.ratelimiter = FederationRateLimiter(
         hs.get_clock(),
         # Time window of 2s
         window_size=2000,
         # Artificially delay requests if rate > sleep_limit/window_size
         sleep_limit=1,
         # Amount of artificial delay to apply
         sleep_msec=1000,
         # Error with 429 if more than reject_limit requests are queued
         reject_limit=1,
         # Allow 1 request at a time
         concurrent_requests=1,
     )
Exemplo n.º 16
0
    def __init__(self, hs):
        super().__init__(hs)

        class Authenticator:
            def authenticate_request(self, request, content):
                return succeed("other.example.com")

        authenticator = Authenticator()

        ratelimiter = FederationRateLimiter(
            hs.get_clock(),
            FederationRateLimitConfig(
                window_size=1,
                sleep_limit=1,
                sleep_msec=1,
                reject_limit=1000,
                concurrent_requests=1000,
            ),
        )

        federation_server.register_servlets(hs, self, authenticator, ratelimiter)
Exemplo n.º 17
0
class UsernameAvailabilityRestServlet(RestServlet):
    PATTERNS = client_v2_patterns("/register/available")

    def __init__(self, hs):
        """
        Args:
            hs (synapse.server.HomeServer): server
        """
        super(UsernameAvailabilityRestServlet, self).__init__()
        self.hs = hs
        self.registration_handler = hs.get_registration_handler()
        self.ratelimiter = FederationRateLimiter(
            hs.get_clock(),
            FederationRateLimitConfig(
                # Time window of 2s
                window_size=2000,
                # Artificially delay requests if rate > sleep_limit/window_size
                sleep_limit=1,
                # Amount of artificial delay to apply
                sleep_msec=1000,
                # Error with 429 if more than reject_limit requests are queued
                reject_limit=1,
                # Allow 1 request at a time
                concurrent_requests=1,
            )
        )

    @defer.inlineCallbacks
    def on_GET(self, request):
        ip = self.hs.get_ip_from_request(request)
        with self.ratelimiter.ratelimit(ip) as wait_deferred:
            yield wait_deferred

            username = parse_string(request, "username", required=True)

            yield self.registration_handler.check_username(username)

            defer.returnValue((200, {"available": True}))
Exemplo n.º 18
0
def setup_test_homeserver(name="test", datastore=None, config=None, **kargs):
    """Setup a homeserver suitable for running tests against. Keyword arguments
    are passed to the Homeserver constructor. If no datastore is supplied a
    datastore backed by an in-memory sqlite db will be given to the HS.
    """
    if config is None:
        config = Mock()
        config.signing_key = [MockKey()]
        config.event_cache_size = 1
        config.enable_registration = True
        config.macaroon_secret_key = "not even a little secret"
        config.expire_access_token = False
        config.server_name = name
        config.trusted_third_party_id_servers = []
        config.room_invite_state_types = []
        config.password_providers = []
        config.worker_replication_url = ""
        config.worker_app = None
        config.email_enable_notifs = False
        config.block_non_admin_invites = False

    config.use_frozen_dicts = True
    config.database_config = {"name": "sqlite3"}
    config.ldap_enabled = False

    if "clock" not in kargs:
        kargs["clock"] = MockClock()

    if datastore is None:
        db_pool = SQLiteMemoryDbPool()
        yield db_pool.prepare()
        hs = HomeServer(name,
                        db_pool=db_pool,
                        config=config,
                        version_string="Synapse/tests",
                        database_engine=create_engine(config.database_config),
                        get_db_conn=db_pool.get_db_conn,
                        room_list_handler=object(),
                        tls_server_context_factory=Mock(),
                        **kargs)
        hs.setup()
    else:
        hs = HomeServer(name,
                        db_pool=None,
                        datastore=datastore,
                        config=config,
                        version_string="Synapse/tests",
                        database_engine=create_engine(config.database_config),
                        room_list_handler=object(),
                        tls_server_context_factory=Mock(),
                        **kargs)

    # bcrypt is far too slow to be doing in unit tests
    # Need to let the HS build an auth handler and then mess with it
    # because AuthHandler's constructor requires the HS, so we can't make one
    # beforehand and pass it in to the HS's constructor (chicken / egg)
    hs.get_auth_handler().hash = lambda p: hashlib.md5(p).hexdigest()
    hs.get_auth_handler().validate_hash = lambda p, h: hashlib.md5(
        p).hexdigest() == h

    fed = kargs.get("resource_for_federation", None)
    if fed:
        server.register_servlets(
            hs,
            resource=fed,
            authenticator=server.Authenticator(hs),
            ratelimiter=FederationRateLimiter(
                hs.get_clock(),
                window_size=hs.config.federation_rc_window_size,
                sleep_limit=hs.config.federation_rc_sleep_limit,
                sleep_msec=hs.config.federation_rc_sleep_delay,
                reject_limit=hs.config.federation_rc_reject_limit,
                concurrent_requests=hs.config.federation_rc_concurrent),
        )

    defer.returnValue(hs)
Exemplo n.º 19
0
def setup_test_homeserver(name="test", datastore=None, config=None, **kargs):
    """Setup a homeserver suitable for running tests against. Keyword arguments
    are passed to the Homeserver constructor. If no datastore is supplied a
    datastore backed by an in-memory sqlite db will be given to the HS.
    """
    if config is None:
        config = Mock()
        config.signing_key = [MockKey()]
        config.event_cache_size = 1
        config.enable_registration = True
        config.macaroon_secret_key = "not even a little secret"
        config.expire_access_token = False
        config.server_name = name
        config.trusted_third_party_id_servers = []
        config.room_invite_state_types = []
        config.password_providers = []
        config.worker_replication_url = ""
        config.worker_app = None
        config.email_enable_notifs = False
        config.block_non_admin_invites = False
        config.federation_domain_whitelist = None
        config.user_directory_search_all_users = False

        # disable user directory updates, because they get done in the
        # background, which upsets the test runner.
        config.update_user_directory = False

    config.use_frozen_dicts = True
    config.ldap_enabled = False

    if "clock" not in kargs:
        kargs["clock"] = MockClock()

    if USE_POSTGRES_FOR_TESTS:
        config.database_config = {
            "name": "psycopg2",
            "args": {
                "database": "synapse_test",
                "cp_min": 1,
                "cp_max": 5,
            },
        }
    else:
        config.database_config = {
            "name": "sqlite3",
            "args": {
                "database": ":memory:",
                "cp_min": 1,
                "cp_max": 1,
            },
        }

    db_engine = create_engine(config.database_config)

    # we need to configure the connection pool to run the on_new_connection
    # function, so that we can test code that uses custom sqlite functions
    # (like rank).
    config.database_config["args"]["cp_openfun"] = db_engine.on_new_connection

    if datastore is None:
        hs = HomeServer(name,
                        config=config,
                        db_config=config.database_config,
                        version_string="Synapse/tests",
                        database_engine=db_engine,
                        room_list_handler=object(),
                        tls_server_context_factory=Mock(),
                        **kargs)
        db_conn = hs.get_db_conn()
        # make sure that the database is empty
        if isinstance(db_engine, PostgresEngine):
            cur = db_conn.cursor()
            cur.execute(
                "SELECT tablename FROM pg_tables where schemaname='public'")
            rows = cur.fetchall()
            for r in rows:
                cur.execute("DROP TABLE %s CASCADE" % r[0])
        yield prepare_database(db_conn, db_engine, config)
        hs.setup()
    else:
        hs = HomeServer(name,
                        db_pool=None,
                        datastore=datastore,
                        config=config,
                        version_string="Synapse/tests",
                        database_engine=db_engine,
                        room_list_handler=object(),
                        tls_server_context_factory=Mock(),
                        **kargs)

    # bcrypt is far too slow to be doing in unit tests
    # Need to let the HS build an auth handler and then mess with it
    # because AuthHandler's constructor requires the HS, so we can't make one
    # beforehand and pass it in to the HS's constructor (chicken / egg)
    hs.get_auth_handler().hash = lambda p: hashlib.md5(p).hexdigest()
    hs.get_auth_handler().validate_hash = lambda p, h: hashlib.md5(
        p).hexdigest() == h

    fed = kargs.get("resource_for_federation", None)
    if fed:
        server.register_servlets(
            hs,
            resource=fed,
            authenticator=server.Authenticator(hs),
            ratelimiter=FederationRateLimiter(
                hs.get_clock(),
                window_size=hs.config.federation_rc_window_size,
                sleep_limit=hs.config.federation_rc_sleep_limit,
                sleep_msec=hs.config.federation_rc_sleep_delay,
                reject_limit=hs.config.federation_rc_reject_limit,
                concurrent_requests=hs.config.federation_rc_concurrent),
        )

    defer.returnValue(hs)
Exemplo n.º 20
0
def setup_test_homeserver(name="test", datastore=None, config=None, **kargs):
    """Setup a homeserver suitable for running tests against. Keyword arguments
    are passed to the Homeserver constructor. If no datastore is supplied a
    datastore backed by an in-memory sqlite db will be given to the HS.
    """
    if config is None:
        config = Mock()
        config.signing_key = [MockKey()]
        config.event_cache_size = 1
        config.enable_registration = True
        config.macaroon_secret_key = "not even a little secret"
        config.server_name = "server.under.test"
        config.trusted_third_party_id_servers = []
        config.room_invite_state_types = []

    config.database_config = {"name": "sqlite3"}

    if "clock" not in kargs:
        kargs["clock"] = MockClock()

    if datastore is None:
        db_pool = SQLiteMemoryDbPool()
        yield db_pool.prepare()
        hs = HomeServer(name,
                        db_pool=db_pool,
                        config=config,
                        version_string="Synapse/tests",
                        database_engine=create_engine(config),
                        get_db_conn=db_pool.get_db_conn,
                        **kargs)
        hs.setup()
    else:
        hs = HomeServer(name,
                        db_pool=None,
                        datastore=datastore,
                        config=config,
                        version_string="Synapse/tests",
                        database_engine=create_engine(config),
                        **kargs)

    # bcrypt is far too slow to be doing in unit tests
    def swap_out_hash_for_testing(old_build_handlers):
        def build_handlers():
            handlers = old_build_handlers()
            auth_handler = handlers.auth_handler
            auth_handler.hash = lambda p: hashlib.md5(p).hexdigest()
            auth_handler.validate_hash = lambda p, h: hashlib.md5(p).hexdigest(
            ) == h
            return handlers

        return build_handlers

    hs.build_handlers = swap_out_hash_for_testing(hs.build_handlers)

    fed = kargs.get("resource_for_federation", None)
    if fed:
        server.register_servlets(
            hs,
            resource=fed,
            authenticator=server.Authenticator(hs),
            ratelimiter=FederationRateLimiter(
                hs.get_clock(),
                window_size=hs.config.federation_rc_window_size,
                sleep_limit=hs.config.federation_rc_sleep_limit,
                sleep_msec=hs.config.federation_rc_sleep_delay,
                reject_limit=hs.config.federation_rc_reject_limit,
                concurrent_requests=hs.config.federation_rc_concurrent),
        )

    defer.returnValue(hs)
Exemplo n.º 21
0
def setup_test_homeserver(cleanup_func,
                          name="test",
                          datastore=None,
                          config=None,
                          reactor=None,
                          homeserverToUse=TestHomeServer,
                          **kargs):
    """
    Setup a homeserver suitable for running tests against.  Keyword arguments
    are passed to the Homeserver constructor.

    If no datastore is supplied, one is created and given to the homeserver.

    Args:
        cleanup_func : The function used to register a cleanup routine for
                       after the test.
    """
    if reactor is None:
        from twisted.internet import reactor

    if config is None:
        config = default_config(name)

    config.ldap_enabled = False

    if "clock" not in kargs:
        kargs["clock"] = MockClock()

    if USE_POSTGRES_FOR_TESTS:
        test_db = "synapse_test_%s" % uuid.uuid4().hex

        config.database_config = {
            "name": "psycopg2",
            "args": {
                "database": test_db,
                "cp_min": 1,
                "cp_max": 5
            },
        }
    else:
        config.database_config = {
            "name": "sqlite3",
            "args": {
                "database": ":memory:",
                "cp_min": 1,
                "cp_max": 1
            },
        }

    db_engine = create_engine(config.database_config)

    # Create the database before we actually try and connect to it, based off
    # the template database we generate in setupdb()
    if datastore is None and isinstance(db_engine, PostgresEngine):
        db_conn = db_engine.module.connect(database=POSTGRES_BASE_DB,
                                           user=POSTGRES_USER)
        db_conn.autocommit = True
        cur = db_conn.cursor()
        cur.execute("DROP DATABASE IF EXISTS %s;" % (test_db, ))
        cur.execute("CREATE DATABASE %s WITH TEMPLATE %s;" %
                    (test_db, POSTGRES_BASE_DB))
        cur.close()
        db_conn.close()

    # we need to configure the connection pool to run the on_new_connection
    # function, so that we can test code that uses custom sqlite functions
    # (like rank).
    config.database_config["args"]["cp_openfun"] = db_engine.on_new_connection

    if datastore is None:
        hs = homeserverToUse(name,
                             config=config,
                             db_config=config.database_config,
                             version_string="Synapse/tests",
                             database_engine=db_engine,
                             room_list_handler=object(),
                             tls_server_context_factory=Mock(),
                             tls_client_options_factory=Mock(),
                             reactor=reactor,
                             **kargs)

        # Prepare the DB on SQLite -- PostgreSQL is a copy of an already up to
        # date db
        if not isinstance(db_engine, PostgresEngine):
            db_conn = hs.get_db_conn()
            yield prepare_database(db_conn, db_engine, config)
            db_conn.commit()
            db_conn.close()

        else:
            # We need to do cleanup on PostgreSQL
            def cleanup():
                import psycopg2

                # Close all the db pools
                hs.get_db_pool().close()

                dropped = False

                # Drop the test database
                db_conn = db_engine.module.connect(database=POSTGRES_BASE_DB,
                                                   user=POSTGRES_USER)
                db_conn.autocommit = True
                cur = db_conn.cursor()

                # Try a few times to drop the DB. Some things may hold on to the
                # database for a few more seconds due to flakiness, preventing
                # us from dropping it when the test is over. If we can't drop
                # it, warn and move on.
                for x in range(5):
                    try:
                        cur.execute("DROP DATABASE IF EXISTS %s;" %
                                    (test_db, ))
                        db_conn.commit()
                        dropped = True
                    except psycopg2.OperationalError as e:
                        warnings.warn("Couldn't drop old db: " + str(e),
                                      category=UserWarning)
                        time.sleep(0.5)

                cur.close()
                db_conn.close()

                if not dropped:
                    warnings.warn("Failed to drop old DB.",
                                  category=UserWarning)

            if not LEAVE_DB:
                # Register the cleanup hook
                cleanup_func(cleanup)

        hs.setup()
    else:
        hs = homeserverToUse(name,
                             db_pool=None,
                             datastore=datastore,
                             config=config,
                             version_string="Synapse/tests",
                             database_engine=db_engine,
                             room_list_handler=object(),
                             tls_server_context_factory=Mock(),
                             tls_client_options_factory=Mock(),
                             reactor=reactor,
                             **kargs)

    # bcrypt is far too slow to be doing in unit tests
    # Need to let the HS build an auth handler and then mess with it
    # because AuthHandler's constructor requires the HS, so we can't make one
    # beforehand and pass it in to the HS's constructor (chicken / egg)
    hs.get_auth_handler().hash = lambda p: hashlib.md5(p.encode('utf8')
                                                       ).hexdigest()
    hs.get_auth_handler().validate_hash = (
        lambda p, h: hashlib.md5(p.encode('utf8')).hexdigest() == h)

    fed = kargs.get("resource_for_federation", None)
    if fed:
        server.register_servlets(
            hs,
            resource=fed,
            authenticator=server.Authenticator(hs),
            ratelimiter=FederationRateLimiter(
                hs.get_clock(),
                window_size=hs.config.federation_rc_window_size,
                sleep_limit=hs.config.federation_rc_sleep_limit,
                sleep_msec=hs.config.federation_rc_sleep_delay,
                reject_limit=hs.config.federation_rc_reject_limit,
                concurrent_requests=hs.config.federation_rc_concurrent,
            ),
        )

    defer.returnValue(hs)
Exemplo n.º 22
0
 def get_federation_ratelimiter(self) -> FederationRateLimiter:
     return FederationRateLimiter(self.get_clock(),
                                  config=self.config.rc_federation)
Exemplo n.º 23
0
def setup_test_homeserver(
    cleanup_func,
    name="test",
    datastore=None,
    config=None,
    reactor=None,
    homeserverToUse=TestHomeServer,
    **kargs
):
    """
    Setup a homeserver suitable for running tests against.  Keyword arguments
    are passed to the Homeserver constructor.

    If no datastore is supplied, one is created and given to the homeserver.

    Args:
        cleanup_func : The function used to register a cleanup routine for
                       after the test.
    """
    if reactor is None:
        from twisted.internet import reactor

    if config is None:
        config = Mock()
        config.signing_key = [MockKey()]
        config.event_cache_size = 1
        config.enable_registration = True
        config.macaroon_secret_key = "not even a little secret"
        config.expire_access_token = False
        config.server_name = name
        config.trusted_third_party_id_servers = []
        config.room_invite_state_types = []
        config.password_providers = []
        config.worker_replication_url = ""
        config.worker_app = None
        config.email_enable_notifs = False
        config.block_non_admin_invites = False
        config.federation_domain_whitelist = None
        config.federation_rc_reject_limit = 10
        config.federation_rc_sleep_limit = 10
        config.federation_rc_sleep_delay = 100
        config.federation_rc_concurrent = 10
        config.filter_timeline_limit = 5000
        config.user_directory_search_all_users = False
        config.user_consent_server_notice_content = None
        config.block_events_without_consent_error = None
        config.media_storage_providers = []
        config.auto_join_rooms = []
        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_trial_days = 0
        config.mau_limits_reserved_threepids = []
        config.admin_contact = None
        config.rc_messages_per_second = 10000
        config.rc_message_burst_count = 10000

        # we need a sane default_room_version, otherwise attempts to create rooms will
        # fail.
        config.default_room_version = "1"

        # disable user directory updates, because they get done in the
        # background, which upsets the test runner.
        config.update_user_directory = False

        def is_threepid_reserved(threepid):
            return ServerConfig.is_threepid_reserved(config, threepid)

        config.is_threepid_reserved.side_effect = is_threepid_reserved

    config.use_frozen_dicts = True
    config.ldap_enabled = False

    if "clock" not in kargs:
        kargs["clock"] = MockClock()

    if USE_POSTGRES_FOR_TESTS:
        test_db = "synapse_test_%s" % uuid.uuid4().hex

        config.database_config = {
            "name": "psycopg2",
            "args": {"database": test_db, "cp_min": 1, "cp_max": 5},
        }
    else:
        config.database_config = {
            "name": "sqlite3",
            "args": {"database": ":memory:", "cp_min": 1, "cp_max": 1},
        }

    db_engine = create_engine(config.database_config)

    # Create the database before we actually try and connect to it, based off
    # the template database we generate in setupdb()
    if datastore is None and isinstance(db_engine, PostgresEngine):
        db_conn = db_engine.module.connect(
            database=POSTGRES_BASE_DB, user=POSTGRES_USER
        )
        db_conn.autocommit = True
        cur = db_conn.cursor()
        cur.execute("DROP DATABASE IF EXISTS %s;" % (test_db,))
        cur.execute(
            "CREATE DATABASE %s WITH TEMPLATE %s;" % (test_db, POSTGRES_BASE_DB)
        )
        cur.close()
        db_conn.close()

    # we need to configure the connection pool to run the on_new_connection
    # function, so that we can test code that uses custom sqlite functions
    # (like rank).
    config.database_config["args"]["cp_openfun"] = db_engine.on_new_connection

    if datastore is None:
        hs = homeserverToUse(
            name,
            config=config,
            db_config=config.database_config,
            version_string="Synapse/tests",
            database_engine=db_engine,
            room_list_handler=object(),
            tls_server_context_factory=Mock(),
            tls_client_options_factory=Mock(),
            reactor=reactor,
            **kargs
        )

        # Prepare the DB on SQLite -- PostgreSQL is a copy of an already up to
        # date db
        if not isinstance(db_engine, PostgresEngine):
            db_conn = hs.get_db_conn()
            yield prepare_database(db_conn, db_engine, config)
            db_conn.commit()
            db_conn.close()

        else:
            # We need to do cleanup on PostgreSQL
            def cleanup():
                # Close all the db pools
                hs.get_db_pool().close()

                # Drop the test database
                db_conn = db_engine.module.connect(
                    database=POSTGRES_BASE_DB, user=POSTGRES_USER
                )
                db_conn.autocommit = True
                cur = db_conn.cursor()
                cur.execute("DROP DATABASE IF EXISTS %s;" % (test_db,))
                db_conn.commit()
                cur.close()
                db_conn.close()

            if not LEAVE_DB:
                # Register the cleanup hook
                cleanup_func(cleanup)

        hs.setup()
    else:
        hs = homeserverToUse(
            name,
            db_pool=None,
            datastore=datastore,
            config=config,
            version_string="Synapse/tests",
            database_engine=db_engine,
            room_list_handler=object(),
            tls_server_context_factory=Mock(),
            tls_client_options_factory=Mock(),
            reactor=reactor,
            **kargs
        )

    # bcrypt is far too slow to be doing in unit tests
    # Need to let the HS build an auth handler and then mess with it
    # because AuthHandler's constructor requires the HS, so we can't make one
    # beforehand and pass it in to the HS's constructor (chicken / egg)
    hs.get_auth_handler().hash = lambda p: hashlib.md5(p.encode('utf8')).hexdigest()
    hs.get_auth_handler().validate_hash = (
        lambda p, h: hashlib.md5(p.encode('utf8')).hexdigest() == h
    )

    fed = kargs.get("resource_for_federation", None)
    if fed:
        server.register_servlets(
            hs,
            resource=fed,
            authenticator=server.Authenticator(hs),
            ratelimiter=FederationRateLimiter(
                hs.get_clock(),
                window_size=hs.config.federation_rc_window_size,
                sleep_limit=hs.config.federation_rc_sleep_limit,
                sleep_msec=hs.config.federation_rc_sleep_delay,
                reject_limit=hs.config.federation_rc_reject_limit,
                concurrent_requests=hs.config.federation_rc_concurrent,
            ),
        )

    defer.returnValue(hs)