Example #1
0
    def test_spam_checker_shadow_ban(self):
        """A spam checker can choose to shadow-ban a user, which allows registration to succeed."""
        class BanAll:
            def check_registration_for_spam(self, email_threepid, username,
                                            request_info):
                return RegistrationBehaviour.SHADOW_BAN

        # Configure a spam checker that denies all users.
        spam_checker = self.hs.get_spam_checker()
        spam_checker.spam_checkers = [BanAll()]

        user_id = self.get_success(
            self.handler.register_user(localpart="user"))

        # Get an access token.
        token = "testtok"
        self.get_success(
            self.store.add_access_token_to_user(user_id=user_id,
                                                token=token,
                                                device_id=None,
                                                valid_until_ms=None))

        # Ensure the user was marked as shadow-banned.
        request = Mock(args={})
        request.args[b"access_token"] = [token.encode("ascii")]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        auth = Auth(self.hs)
        requester = self.get_success(auth.get_user_by_req(request))

        self.assertTrue(requester.shadow_banned)
Example #2
0
 def build_v1auth(self):
     orf = Auth(self)
     # Matrix spec makes no reference to what HTTP status code is returned,
     # but the V1 API uses 403 where it means 401, and the webclient
     # relies on this behaviour, so V1 gets its own copy of the auth
     # with backwards compat behaviour.
     orf.TOKEN_NOT_FOUND_HTTP_STATUS = 403
     return orf
Example #3
0
    def setUp(self):
        self.state_handler = Mock()
        self.store = Mock()

        self.hs = yield setup_test_homeserver(handlers=None)
        self.hs.get_datastore = Mock(return_value=self.store)
        self.auth = Auth(self.hs)

        self.test_user = "******"
        self.test_token = "_test_token_"
Example #4
0
    def setUp(self):
        self.state_handler = Mock()
        self.store = Mock()

        self.hs = Mock()
        self.hs.get_datastore = Mock(return_value=self.store)
        self.hs.get_state_handler = Mock(return_value=self.state_handler)
        self.auth = Auth(self.hs)

        self.test_user = "******"
        self.test_token = "_test_token_"
Example #5
0
    def setUp(self):
        self.state_handler = Mock()
        self.store = Mock()

        self.hs = yield setup_test_homeserver(handlers=None)
        self.hs.get_datastore = Mock(return_value=self.store)
        self.hs.handlers = TestHandlers(self.hs)
        self.auth = Auth(self.hs)

        self.test_user = "******"
        self.test_token = "_test_token_"

        # this is overridden for the appservice tests
        self.store.get_app_service_by_token = Mock(return_value=None)
Example #6
0
    def setUp(self):
        self.dummy_store = _DummyStore()
        storage_controllers = Mock(main=self.dummy_store, state=self.dummy_store)
        hs = Mock(
            spec_set=[
                "config",
                "get_datastores",
                "get_storage_controllers",
                "get_auth",
                "get_state_handler",
                "get_clock",
                "get_state_resolution_handler",
                "get_account_validity_handler",
                "get_macaroon_generator",
                "hostname",
            ]
        )
        clock = cast(Clock, MockClock())
        hs.config = default_config("tesths", True)
        hs.get_datastores.return_value = Mock(main=self.dummy_store)
        hs.get_state_handler.return_value = None
        hs.get_clock.return_value = clock
        hs.get_macaroon_generator.return_value = MacaroonGenerator(
            clock, "tesths", b"verysecret"
        )
        hs.get_auth.return_value = Auth(hs)
        hs.get_state_resolution_handler = lambda: StateResolutionHandler(hs)
        hs.get_storage_controllers.return_value = storage_controllers

        self.state = StateHandler(hs)
        self.event_id = 0
Example #7
0
    def setUp(self):
        self.store = StateGroupStore()
        storage = Mock(main=self.store, state=self.store)
        hs = Mock(
            spec_set=[
                "config",
                "get_datastore",
                "get_storage",
                "get_auth",
                "get_state_handler",
                "get_clock",
                "get_state_resolution_handler",
                "hostname",
            ]
        )
        hs.config = default_config("tesths", True)
        hs.get_datastore.return_value = self.store
        hs.get_state_handler.return_value = None
        hs.get_clock.return_value = MockClock()
        hs.get_auth.return_value = Auth(hs)
        hs.get_state_resolution_handler = lambda: StateResolutionHandler(hs)
        hs.get_storage.return_value = storage

        self.state = StateHandler(hs)
        self.event_id = 0
Example #8
0
    def setUp(self):
        self.state_handler = Mock()
        self.store = Mock()

        self.hs = yield setup_test_homeserver(handlers=None)
        self.hs.get_datastore = Mock(return_value=self.store)
        self.auth = Auth(self.hs)

        self.test_user = "******"
        self.test_token = "_test_token_"
Example #9
0
    def prepare(self, reactor, clock, hs):
        self.store = Mock()

        hs.get_datastore = Mock(return_value=self.store)
        hs.get_auth_handler().store = self.store
        self.auth = Auth(hs)

        # AuthBlocking reads from the hs' config on initialization. We need to
        # modify its config instead of the hs'
        self.auth_blocking = self.auth._auth_blocking

        self.test_user = "******"
        self.test_token = b"_test_token_"

        # this is overridden for the appservice tests
        self.store.get_app_service_by_token = Mock(return_value=None)

        self.store.insert_client_ip = simple_async_mock(None)
        self.store.is_support_user = simple_async_mock(False)
Example #10
0
    def test_spam_checker_shadow_ban(self):
        """A spam checker can choose to shadow-ban a user, which allows registration to succeed."""
        user_id = self.get_success(self.handler.register_user(localpart="user"))

        # Get an access token.
        token = "testtok"
        self.get_success(
            self.store.add_access_token_to_user(
                user_id=user_id, token=token, device_id=None, valid_until_ms=None
            )
        )

        # Ensure the user was marked as shadow-banned.
        request = Mock(args={})
        request.args[b"access_token"] = [token.encode("ascii")]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        auth = Auth(self.hs)
        requester = self.get_success(auth.get_user_by_req(request))

        self.assertTrue(requester.shadow_banned)
Example #11
0
    def setUp(self):
        self.state_handler = Mock()
        self.store = Mock()

        self.hs = yield setup_test_homeserver(self.addCleanup, handlers=None)
        self.hs.get_datastore = Mock(return_value=self.store)
        self.hs.handlers = TestHandlers(self.hs)
        self.auth = Auth(self.hs)

        # AuthBlocking reads from the hs' config on initialization. We need to
        # modify its config instead of the hs'
        self.auth_blocking = self.auth._auth_blocking

        self.test_user = "******"
        self.test_token = b"_test_token_"

        # this is overridden for the appservice tests
        self.store.get_app_service_by_token = Mock(return_value=None)

        self.store.is_support_user = Mock(return_value=defer.succeed(False))
Example #12
0
    def setUp(self):
        self.state_handler = Mock()
        self.store = Mock()

        self.hs = Mock()
        self.hs.get_datastore = Mock(return_value=self.store)
        self.hs.get_state_handler = Mock(return_value=self.state_handler)
        self.auth = Auth(self.hs)

        self.test_user = "******"
        self.test_token = "_test_token_"
Example #13
0
    def setUp(self):
        self.state_handler = Mock()
        self.store = Mock()

        self.hs = yield setup_test_homeserver(handlers=None)
        self.hs.get_datastore = Mock(return_value=self.store)
        self.hs.handlers = TestHandlers(self.hs)
        self.auth = Auth(self.hs)

        self.test_user = "******"
        self.test_token = "_test_token_"

        # this is overridden for the appservice tests
        self.store.get_app_service_by_token = Mock(return_value=None)
Example #14
0
    def setUp(self):
        self.store = StateGroupStore()
        hs = Mock(spec_set=[
            "get_datastore", "get_auth", "get_state_handler", "get_clock",
            "get_state_resolution_handler",
        ])
        hs.get_datastore.return_value = self.store
        hs.get_state_handler.return_value = None
        hs.get_clock.return_value = MockClock()
        hs.get_auth.return_value = Auth(hs)
        hs.get_state_resolution_handler = lambda: StateResolutionHandler(hs)

        self.state = StateHandler(hs)
        self.event_id = 0
Example #15
0
    def setUp(self):
        self.store = Mock(
            spec_set=[
                "get_state_groups",
                "add_event_hashes",
            ]
        )
        hs = Mock(spec=[
            "get_datastore", "get_auth", "get_state_handler", "get_clock",
        ])
        hs.get_datastore.return_value = self.store
        hs.get_state_handler.return_value = None
        hs.get_auth.return_value = Auth(hs)
        hs.get_clock.return_value = MockClock()

        self.state = StateHandler(hs)
        self.event_id = 0
Example #16
0
    def setUp(self):
        self.store = Mock(
            spec_set=[
                "get_state_groups_ids",
                "add_event_hashes",
                "get_events",
                "get_next_state_group",
                "get_state_group_delta",
            ]
        )
        hs = Mock(spec_set=[
            "get_datastore", "get_auth", "get_state_handler", "get_clock",
        ])
        hs.get_datastore.return_value = self.store
        hs.get_state_handler.return_value = None
        hs.get_clock.return_value = MockClock()
        hs.get_auth.return_value = Auth(hs)

        self.store.get_next_state_group.side_effect = Mock
        self.store.get_state_group_delta.return_value = (None, None)

        self.state = StateHandler(hs)
        self.event_id = 0
Example #17
0
class AuthTestCase(unittest.TestCase):
    @defer.inlineCallbacks
    def setUp(self):
        self.state_handler = Mock()
        self.store = Mock()

        self.hs = yield setup_test_homeserver(self.addCleanup, handlers=None)
        self.hs.get_datastore = Mock(return_value=self.store)
        self.hs.handlers = TestHandlers(self.hs)
        self.auth = Auth(self.hs)

        self.test_user = "******"
        self.test_token = b"_test_token_"

        # this is overridden for the appservice tests
        self.store.get_app_service_by_token = Mock(return_value=None)

    @defer.inlineCallbacks
    def test_get_user_by_req_user_valid_token(self):
        user_info = {"name": self.test_user, "token_id": "ditto", "device_id": "device"}
        self.store.get_user_by_access_token = Mock(return_value=user_info)

        request = Mock(args={})
        request.args[b"access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        requester = yield self.auth.get_user_by_req(request)
        self.assertEquals(requester.user.to_string(), self.test_user)

    def test_get_user_by_req_user_bad_token(self):
        self.store.get_user_by_access_token = Mock(return_value=None)

        request = Mock(args={})
        request.args[b"access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    def test_get_user_by_req_user_missing_token(self):
        user_info = {"name": self.test_user, "token_id": "ditto"}
        self.store.get_user_by_access_token = Mock(return_value=user_info)

        request = Mock(args={})
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    @defer.inlineCallbacks
    def test_get_user_by_req_appservice_valid_token(self):
        app_service = Mock(
            token="foobar", url="a_url", sender=self.test_user, ip_range_whitelist=None
        )
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_access_token = Mock(return_value=None)

        request = Mock(args={})
        request.getClientIP.return_value = "127.0.0.1"
        request.args[b"access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        requester = yield self.auth.get_user_by_req(request)
        self.assertEquals(requester.user.to_string(), self.test_user)

    @defer.inlineCallbacks
    def test_get_user_by_req_appservice_valid_token_good_ip(self):
        from netaddr import IPSet

        app_service = Mock(
            token="foobar",
            url="a_url",
            sender=self.test_user,
            ip_range_whitelist=IPSet(["192.168/16"]),
        )
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_access_token = Mock(return_value=None)

        request = Mock(args={})
        request.getClientIP.return_value = "192.168.10.10"
        request.args[b"access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        requester = yield self.auth.get_user_by_req(request)
        self.assertEquals(requester.user.to_string(), self.test_user)

    def test_get_user_by_req_appservice_valid_token_bad_ip(self):
        from netaddr import IPSet

        app_service = Mock(
            token="foobar",
            url="a_url",
            sender=self.test_user,
            ip_range_whitelist=IPSet(["192.168/16"]),
        )
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_access_token = Mock(return_value=None)

        request = Mock(args={})
        request.getClientIP.return_value = "131.111.8.42"
        request.args[b"access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    def test_get_user_by_req_appservice_bad_token(self):
        self.store.get_app_service_by_token = Mock(return_value=None)
        self.store.get_user_by_access_token = Mock(return_value=None)

        request = Mock(args={})
        request.args[b"access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    def test_get_user_by_req_appservice_missing_token(self):
        app_service = Mock(token="foobar", url="a_url", sender=self.test_user)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_access_token = Mock(return_value=None)

        request = Mock(args={})
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    @defer.inlineCallbacks
    def test_get_user_by_req_appservice_valid_token_valid_user_id(self):
        masquerading_user_id = b"@doppelganger:matrix.org"
        app_service = Mock(
            token="foobar", url="a_url", sender=self.test_user, ip_range_whitelist=None
        )
        app_service.is_interested_in_user = Mock(return_value=True)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_access_token = Mock(return_value=None)

        request = Mock(args={})
        request.getClientIP.return_value = "127.0.0.1"
        request.args[b"access_token"] = [self.test_token]
        request.args[b"user_id"] = [masquerading_user_id]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        requester = yield self.auth.get_user_by_req(request)
        self.assertEquals(
            requester.user.to_string(), masquerading_user_id.decode('utf8')
        )

    def test_get_user_by_req_appservice_valid_token_bad_user_id(self):
        masquerading_user_id = b"@doppelganger:matrix.org"
        app_service = Mock(
            token="foobar", url="a_url", sender=self.test_user, ip_range_whitelist=None
        )
        app_service.is_interested_in_user = Mock(return_value=False)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_access_token = Mock(return_value=None)

        request = Mock(args={})
        request.getClientIP.return_value = "127.0.0.1"
        request.args[b"access_token"] = [self.test_token]
        request.args[b"user_id"] = [masquerading_user_id]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    @defer.inlineCallbacks
    def test_get_user_from_macaroon(self):
        # TODO(danielwh): Remove this mock when we remove the
        # get_user_by_access_token fallback.
        self.store.get_user_by_access_token = Mock(
            return_value={"name": "@baldrick:matrix.org", "device_id": "device"}
        )

        user_id = "@baldrick:matrix.org"
        macaroon = pymacaroons.Macaroon(
            location=self.hs.config.server_name,
            identifier="key",
            key=self.hs.config.macaroon_secret_key,
        )
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")
        macaroon.add_first_party_caveat("user_id = %s" % (user_id,))
        user_info = yield self.auth.get_user_by_access_token(macaroon.serialize())
        user = user_info["user"]
        self.assertEqual(UserID.from_string(user_id), user)

        # TODO: device_id should come from the macaroon, but currently comes
        # from the db.
        self.assertEqual(user_info["device_id"], "device")

    @defer.inlineCallbacks
    def test_get_guest_user_from_macaroon(self):
        self.store.get_user_by_id = Mock(return_value={"is_guest": True})

        user_id = "@baldrick:matrix.org"
        macaroon = pymacaroons.Macaroon(
            location=self.hs.config.server_name,
            identifier="key",
            key=self.hs.config.macaroon_secret_key,
        )
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")
        macaroon.add_first_party_caveat("user_id = %s" % (user_id,))
        macaroon.add_first_party_caveat("guest = true")
        serialized = macaroon.serialize()

        user_info = yield self.auth.get_user_by_access_token(serialized)
        user = user_info["user"]
        is_guest = user_info["is_guest"]
        self.assertEqual(UserID.from_string(user_id), user)
        self.assertTrue(is_guest)
        self.store.get_user_by_id.assert_called_with(user_id)

    @defer.inlineCallbacks
    def test_get_user_from_macaroon_user_db_mismatch(self):
        self.store.get_user_by_access_token = Mock(
            return_value={"name": "@percy:matrix.org"}
        )

        user = "******"
        macaroon = pymacaroons.Macaroon(
            location=self.hs.config.server_name,
            identifier="key",
            key=self.hs.config.macaroon_secret_key,
        )
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")
        macaroon.add_first_party_caveat("user_id = %s" % (user,))
        with self.assertRaises(AuthError) as cm:
            yield self.auth.get_user_by_access_token(macaroon.serialize())
        self.assertEqual(401, cm.exception.code)
        self.assertIn("User mismatch", cm.exception.msg)

    @defer.inlineCallbacks
    def test_get_user_from_macaroon_missing_caveat(self):
        # TODO(danielwh): Remove this mock when we remove the
        # get_user_by_access_token fallback.
        self.store.get_user_by_access_token = Mock(
            return_value={"name": "@baldrick:matrix.org"}
        )

        macaroon = pymacaroons.Macaroon(
            location=self.hs.config.server_name,
            identifier="key",
            key=self.hs.config.macaroon_secret_key,
        )
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")

        with self.assertRaises(AuthError) as cm:
            yield self.auth.get_user_by_access_token(macaroon.serialize())
        self.assertEqual(401, cm.exception.code)
        self.assertIn("No user caveat", cm.exception.msg)

    @defer.inlineCallbacks
    def test_get_user_from_macaroon_wrong_key(self):
        # TODO(danielwh): Remove this mock when we remove the
        # get_user_by_access_token fallback.
        self.store.get_user_by_access_token = Mock(
            return_value={"name": "@baldrick:matrix.org"}
        )

        user = "******"
        macaroon = pymacaroons.Macaroon(
            location=self.hs.config.server_name,
            identifier="key",
            key=self.hs.config.macaroon_secret_key + "wrong",
        )
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")
        macaroon.add_first_party_caveat("user_id = %s" % (user,))

        with self.assertRaises(AuthError) as cm:
            yield self.auth.get_user_by_access_token(macaroon.serialize())
        self.assertEqual(401, cm.exception.code)
        self.assertIn("Invalid macaroon", cm.exception.msg)

    @defer.inlineCallbacks
    def test_get_user_from_macaroon_unknown_caveat(self):
        # TODO(danielwh): Remove this mock when we remove the
        # get_user_by_access_token fallback.
        self.store.get_user_by_access_token = Mock(
            return_value={"name": "@baldrick:matrix.org"}
        )

        user = "******"
        macaroon = pymacaroons.Macaroon(
            location=self.hs.config.server_name,
            identifier="key",
            key=self.hs.config.macaroon_secret_key,
        )
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")
        macaroon.add_first_party_caveat("user_id = %s" % (user,))
        macaroon.add_first_party_caveat("cunning > fox")

        with self.assertRaises(AuthError) as cm:
            yield self.auth.get_user_by_access_token(macaroon.serialize())
        self.assertEqual(401, cm.exception.code)
        self.assertIn("Invalid macaroon", cm.exception.msg)

    @defer.inlineCallbacks
    def test_get_user_from_macaroon_expired(self):
        # TODO(danielwh): Remove this mock when we remove the
        # get_user_by_access_token fallback.
        self.store.get_user_by_access_token = Mock(
            return_value={"name": "@baldrick:matrix.org"}
        )

        self.store.get_user_by_access_token = Mock(
            return_value={"name": "@baldrick:matrix.org"}
        )

        user = "******"
        macaroon = pymacaroons.Macaroon(
            location=self.hs.config.server_name,
            identifier="key",
            key=self.hs.config.macaroon_secret_key,
        )
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")
        macaroon.add_first_party_caveat("user_id = %s" % (user,))
        macaroon.add_first_party_caveat("time < -2000")  # ms

        self.hs.clock.now = 5000  # seconds
        self.hs.config.expire_access_token = True
        # yield self.auth.get_user_by_access_token(macaroon.serialize())
        # TODO(daniel): Turn on the check that we validate expiration, when we
        # validate expiration (and remove the above line, which will start
        # throwing).
        with self.assertRaises(AuthError) as cm:
            yield self.auth.get_user_by_access_token(macaroon.serialize())
        self.assertEqual(401, cm.exception.code)
        self.assertIn("Invalid macaroon", cm.exception.msg)

    @defer.inlineCallbacks
    def test_get_user_from_macaroon_with_valid_duration(self):
        # TODO(danielwh): Remove this mock when we remove the
        # get_user_by_access_token fallback.
        self.store.get_user_by_access_token = Mock(
            return_value={"name": "@baldrick:matrix.org"}
        )

        self.store.get_user_by_access_token = Mock(
            return_value={"name": "@baldrick:matrix.org"}
        )

        user_id = "@baldrick:matrix.org"
        macaroon = pymacaroons.Macaroon(
            location=self.hs.config.server_name,
            identifier="key",
            key=self.hs.config.macaroon_secret_key,
        )
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")
        macaroon.add_first_party_caveat("user_id = %s" % (user_id,))
        macaroon.add_first_party_caveat("time < 900000000")  # ms

        self.hs.clock.now = 5000  # seconds
        self.hs.config.expire_access_token = True

        user_info = yield self.auth.get_user_by_access_token(macaroon.serialize())
        user = user_info["user"]
        self.assertEqual(UserID.from_string(user_id), user)

    @defer.inlineCallbacks
    def test_cannot_use_regular_token_as_guest(self):
        USER_ID = "@percy:matrix.org"
        self.store.add_access_token_to_user = Mock()

        token = yield self.hs.handlers.auth_handler.issue_access_token(
            USER_ID, "DEVICE"
        )
        self.store.add_access_token_to_user.assert_called_with(USER_ID, token, "DEVICE")

        def get_user(tok):
            if token != tok:
                return None
            return {
                "name": USER_ID,
                "is_guest": False,
                "token_id": 1234,
                "device_id": "DEVICE",
            }

        self.store.get_user_by_access_token = get_user
        self.store.get_user_by_id = Mock(return_value={"is_guest": False})

        # check the token works
        request = Mock(args={})
        request.args[b"access_token"] = [token.encode('ascii')]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        requester = yield self.auth.get_user_by_req(request, allow_guest=True)
        self.assertEqual(UserID.from_string(USER_ID), requester.user)
        self.assertFalse(requester.is_guest)

        # add an is_guest caveat
        mac = pymacaroons.Macaroon.deserialize(token)
        mac.add_first_party_caveat("guest = true")
        guest_tok = mac.serialize()

        # the token should *not* work now
        request = Mock(args={})
        request.args[b"access_token"] = [guest_tok.encode('ascii')]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()

        with self.assertRaises(AuthError) as cm:
            yield self.auth.get_user_by_req(request, allow_guest=True)

        self.assertEqual(401, cm.exception.code)
        self.assertEqual("Guest access token used for regular user", cm.exception.msg)

        self.store.get_user_by_id.assert_called_with(USER_ID)

    @defer.inlineCallbacks
    def test_blocking_mau(self):
        self.hs.config.limit_usage_by_mau = False
        self.hs.config.max_mau_value = 50
        lots_of_users = 100
        small_number_of_users = 1

        # Ensure no error thrown
        yield self.auth.check_auth_blocking()

        self.hs.config.limit_usage_by_mau = True

        self.store.get_monthly_active_count = Mock(
            return_value=defer.succeed(lots_of_users)
        )

        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)
        self.assertEquals(e.exception.code, 403)

        # Ensure does not throw an error
        self.store.get_monthly_active_count = Mock(
            return_value=defer.succeed(small_number_of_users)
        )
        yield self.auth.check_auth_blocking()

    @defer.inlineCallbacks
    def test_hs_disabled(self):
        self.hs.config.hs_disabled = True
        self.hs.config.hs_disabled_message = "Reason for being disabled"
        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)
        self.assertEquals(e.exception.code, 403)
Example #18
0
class AuthTestCase(unittest.HomeserverTestCase):
    def prepare(self, reactor, clock, hs):
        self.store = Mock()

        hs.get_datastore = Mock(return_value=self.store)
        hs.get_auth_handler().store = self.store
        self.auth = Auth(hs)

        # AuthBlocking reads from the hs' config on initialization. We need to
        # modify its config instead of the hs'
        self.auth_blocking = self.auth._auth_blocking

        self.test_user = "******"
        self.test_token = b"_test_token_"

        # this is overridden for the appservice tests
        self.store.get_app_service_by_token = Mock(return_value=None)

        self.store.insert_client_ip = simple_async_mock(None)
        self.store.is_support_user = simple_async_mock(False)

    def test_get_user_by_req_user_valid_token(self):
        user_info = TokenLookupResult(user_id=self.test_user,
                                      token_id=5,
                                      device_id="device")
        self.store.get_user_by_access_token = simple_async_mock(user_info)
        self.store.mark_access_token_as_used = simple_async_mock(None)

        request = Mock(args={})
        request.args[b"access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        requester = self.get_success(self.auth.get_user_by_req(request))
        self.assertEquals(requester.user.to_string(), self.test_user)

    def test_get_user_by_req_user_bad_token(self):
        self.store.get_user_by_access_token = simple_async_mock(None)

        request = Mock(args={})
        request.args[b"access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        f = self.get_failure(self.auth.get_user_by_req(request),
                             InvalidClientTokenError).value
        self.assertEqual(f.code, 401)
        self.assertEqual(f.errcode, "M_UNKNOWN_TOKEN")

    def test_get_user_by_req_user_missing_token(self):
        user_info = TokenLookupResult(user_id=self.test_user, token_id=5)
        self.store.get_user_by_access_token = simple_async_mock(user_info)

        request = Mock(args={})
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        f = self.get_failure(self.auth.get_user_by_req(request),
                             MissingClientTokenError).value
        self.assertEqual(f.code, 401)
        self.assertEqual(f.errcode, "M_MISSING_TOKEN")

    def test_get_user_by_req_appservice_valid_token(self):
        app_service = Mock(token="foobar",
                           url="a_url",
                           sender=self.test_user,
                           ip_range_whitelist=None)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_access_token = simple_async_mock(None)

        request = Mock(args={})
        request.getClientIP.return_value = "127.0.0.1"
        request.args[b"access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        requester = self.get_success(self.auth.get_user_by_req(request))
        self.assertEquals(requester.user.to_string(), self.test_user)

    def test_get_user_by_req_appservice_valid_token_good_ip(self):
        from netaddr import IPSet

        app_service = Mock(
            token="foobar",
            url="a_url",
            sender=self.test_user,
            ip_range_whitelist=IPSet(["192.168/16"]),
        )
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_access_token = simple_async_mock(None)

        request = Mock(args={})
        request.getClientIP.return_value = "192.168.10.10"
        request.args[b"access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        requester = self.get_success(self.auth.get_user_by_req(request))
        self.assertEquals(requester.user.to_string(), self.test_user)

    def test_get_user_by_req_appservice_valid_token_bad_ip(self):
        from netaddr import IPSet

        app_service = Mock(
            token="foobar",
            url="a_url",
            sender=self.test_user,
            ip_range_whitelist=IPSet(["192.168/16"]),
        )
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_access_token = simple_async_mock(None)

        request = Mock(args={})
        request.getClientIP.return_value = "131.111.8.42"
        request.args[b"access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        f = self.get_failure(self.auth.get_user_by_req(request),
                             InvalidClientTokenError).value
        self.assertEqual(f.code, 401)
        self.assertEqual(f.errcode, "M_UNKNOWN_TOKEN")

    def test_get_user_by_req_appservice_bad_token(self):
        self.store.get_app_service_by_token = Mock(return_value=None)
        self.store.get_user_by_access_token = simple_async_mock(None)

        request = Mock(args={})
        request.args[b"access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        f = self.get_failure(self.auth.get_user_by_req(request),
                             InvalidClientTokenError).value
        self.assertEqual(f.code, 401)
        self.assertEqual(f.errcode, "M_UNKNOWN_TOKEN")

    def test_get_user_by_req_appservice_missing_token(self):
        app_service = Mock(token="foobar", url="a_url", sender=self.test_user)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_access_token = simple_async_mock(None)

        request = Mock(args={})
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        f = self.get_failure(self.auth.get_user_by_req(request),
                             MissingClientTokenError).value
        self.assertEqual(f.code, 401)
        self.assertEqual(f.errcode, "M_MISSING_TOKEN")

    def test_get_user_by_req_appservice_valid_token_valid_user_id(self):
        masquerading_user_id = b"@doppelganger:matrix.org"
        app_service = Mock(token="foobar",
                           url="a_url",
                           sender=self.test_user,
                           ip_range_whitelist=None)
        app_service.is_interested_in_user = Mock(return_value=True)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        # This just needs to return a truth-y value.
        self.store.get_user_by_id = simple_async_mock({"is_guest": False})
        self.store.get_user_by_access_token = simple_async_mock(None)

        request = Mock(args={})
        request.getClientIP.return_value = "127.0.0.1"
        request.args[b"access_token"] = [self.test_token]
        request.args[b"user_id"] = [masquerading_user_id]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        requester = self.get_success(self.auth.get_user_by_req(request))
        self.assertEquals(requester.user.to_string(),
                          masquerading_user_id.decode("utf8"))

    def test_get_user_by_req_appservice_valid_token_bad_user_id(self):
        masquerading_user_id = b"@doppelganger:matrix.org"
        app_service = Mock(token="foobar",
                           url="a_url",
                           sender=self.test_user,
                           ip_range_whitelist=None)
        app_service.is_interested_in_user = Mock(return_value=False)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_access_token = simple_async_mock(None)

        request = Mock(args={})
        request.getClientIP.return_value = "127.0.0.1"
        request.args[b"access_token"] = [self.test_token]
        request.args[b"user_id"] = [masquerading_user_id]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        self.get_failure(self.auth.get_user_by_req(request), AuthError)

    @override_config(
        {"experimental_features": {
            "msc3202_device_masquerading": True
        }})
    def test_get_user_by_req_appservice_valid_token_valid_device_id(self):
        """
        Tests that when an application service passes the device_id URL parameter
        with the ID of a valid device for the user in question,
        the requester instance tracks that device ID.
        """
        masquerading_user_id = b"@doppelganger:matrix.org"
        masquerading_device_id = b"DOPPELDEVICE"
        app_service = Mock(token="foobar",
                           url="a_url",
                           sender=self.test_user,
                           ip_range_whitelist=None)
        app_service.is_interested_in_user = Mock(return_value=True)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        # This just needs to return a truth-y value.
        self.store.get_user_by_id = simple_async_mock({"is_guest": False})
        self.store.get_user_by_access_token = simple_async_mock(None)
        # This also needs to just return a truth-y value
        self.store.get_device = simple_async_mock({"hidden": False})

        request = Mock(args={})
        request.getClientIP.return_value = "127.0.0.1"
        request.args[b"access_token"] = [self.test_token]
        request.args[b"user_id"] = [masquerading_user_id]
        request.args[b"org.matrix.msc3202.device_id"] = [
            masquerading_device_id
        ]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        requester = self.get_success(self.auth.get_user_by_req(request))
        self.assertEquals(requester.user.to_string(),
                          masquerading_user_id.decode("utf8"))
        self.assertEquals(requester.device_id,
                          masquerading_device_id.decode("utf8"))

    @override_config(
        {"experimental_features": {
            "msc3202_device_masquerading": True
        }})
    def test_get_user_by_req_appservice_valid_token_invalid_device_id(self):
        """
        Tests that when an application service passes the device_id URL parameter
        with an ID that is not a valid device ID for the user in question,
        the request fails with the appropriate error code.
        """
        masquerading_user_id = b"@doppelganger:matrix.org"
        masquerading_device_id = b"NOT_A_REAL_DEVICE_ID"
        app_service = Mock(token="foobar",
                           url="a_url",
                           sender=self.test_user,
                           ip_range_whitelist=None)
        app_service.is_interested_in_user = Mock(return_value=True)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        # This just needs to return a truth-y value.
        self.store.get_user_by_id = simple_async_mock({"is_guest": False})
        self.store.get_user_by_access_token = simple_async_mock(None)
        # This also needs to just return a falsey value
        self.store.get_device = simple_async_mock(None)

        request = Mock(args={})
        request.getClientIP.return_value = "127.0.0.1"
        request.args[b"access_token"] = [self.test_token]
        request.args[b"user_id"] = [masquerading_user_id]
        request.args[b"org.matrix.msc3202.device_id"] = [
            masquerading_device_id
        ]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()

        failure = self.get_failure(self.auth.get_user_by_req(request),
                                   AuthError)
        self.assertEquals(failure.value.code, 400)
        self.assertEquals(failure.value.errcode, Codes.EXCLUSIVE)

    def test_get_user_by_req__puppeted_token__not_tracking_puppeted_mau(self):
        self.store.get_user_by_access_token = simple_async_mock(
            TokenLookupResult(
                user_id="@baldrick:matrix.org",
                device_id="device",
                token_owner="@admin:matrix.org",
            ))
        self.store.insert_client_ip = simple_async_mock(None)
        request = Mock(args={})
        request.getClientIP.return_value = "127.0.0.1"
        request.args[b"access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        self.get_success(self.auth.get_user_by_req(request))
        self.store.insert_client_ip.assert_called_once()

    def test_get_user_by_req__puppeted_token__tracking_puppeted_mau(self):
        self.auth._track_puppeted_user_ips = True
        self.store.get_user_by_access_token = simple_async_mock(
            TokenLookupResult(
                user_id="@baldrick:matrix.org",
                device_id="device",
                token_owner="@admin:matrix.org",
            ))
        self.store.insert_client_ip = simple_async_mock(None)
        request = Mock(args={})
        request.getClientIP.return_value = "127.0.0.1"
        request.args[b"access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        self.get_success(self.auth.get_user_by_req(request))
        self.assertEquals(self.store.insert_client_ip.call_count, 2)

    def test_get_user_from_macaroon(self):
        self.store.get_user_by_access_token = simple_async_mock(
            TokenLookupResult(user_id="@baldrick:matrix.org",
                              device_id="device"))

        user_id = "@baldrick:matrix.org"
        macaroon = pymacaroons.Macaroon(
            location=self.hs.config.server.server_name,
            identifier="key",
            key=self.hs.config.key.macaroon_secret_key,
        )
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")
        macaroon.add_first_party_caveat("user_id = %s" % (user_id, ))
        user_info = self.get_success(
            self.auth.get_user_by_access_token(macaroon.serialize()))
        self.assertEqual(user_id, user_info.user_id)

        # TODO: device_id should come from the macaroon, but currently comes
        # from the db.
        self.assertEqual(user_info.device_id, "device")

    def test_get_guest_user_from_macaroon(self):
        self.store.get_user_by_id = simple_async_mock({"is_guest": True})
        self.store.get_user_by_access_token = simple_async_mock(None)

        user_id = "@baldrick:matrix.org"
        macaroon = pymacaroons.Macaroon(
            location=self.hs.config.server.server_name,
            identifier="key",
            key=self.hs.config.key.macaroon_secret_key,
        )
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")
        macaroon.add_first_party_caveat("user_id = %s" % (user_id, ))
        macaroon.add_first_party_caveat("guest = true")
        serialized = macaroon.serialize()

        user_info = self.get_success(
            self.auth.get_user_by_access_token(serialized))
        self.assertEqual(user_id, user_info.user_id)
        self.assertTrue(user_info.is_guest)
        self.store.get_user_by_id.assert_called_with(user_id)

    def test_blocking_mau(self):
        self.auth_blocking._limit_usage_by_mau = False
        self.auth_blocking._max_mau_value = 50
        lots_of_users = 100
        small_number_of_users = 1

        # Ensure no error thrown
        self.get_success(self.auth.check_auth_blocking())

        self.auth_blocking._limit_usage_by_mau = True

        self.store.get_monthly_active_count = simple_async_mock(lots_of_users)

        e = self.get_failure(self.auth.check_auth_blocking(),
                             ResourceLimitError)
        self.assertEquals(e.value.admin_contact,
                          self.hs.config.server.admin_contact)
        self.assertEquals(e.value.errcode, Codes.RESOURCE_LIMIT_EXCEEDED)
        self.assertEquals(e.value.code, 403)

        # Ensure does not throw an error
        self.store.get_monthly_active_count = simple_async_mock(
            small_number_of_users)
        self.get_success(self.auth.check_auth_blocking())

    def test_blocking_mau__depending_on_user_type(self):
        self.auth_blocking._max_mau_value = 50
        self.auth_blocking._limit_usage_by_mau = True

        self.store.get_monthly_active_count = simple_async_mock(100)
        # Support users allowed
        self.get_success(
            self.auth.check_auth_blocking(user_type=UserTypes.SUPPORT))
        self.store.get_monthly_active_count = simple_async_mock(100)
        # Bots not allowed
        self.get_failure(
            self.auth.check_auth_blocking(user_type=UserTypes.BOT),
            ResourceLimitError)
        self.store.get_monthly_active_count = simple_async_mock(100)
        # Real users not allowed
        self.get_failure(self.auth.check_auth_blocking(), ResourceLimitError)

    def test_blocking_mau__appservice_requester_allowed_when_not_tracking_ips(
            self):
        self.auth_blocking._max_mau_value = 50
        self.auth_blocking._limit_usage_by_mau = True
        self.auth_blocking._track_appservice_user_ips = False

        self.store.get_monthly_active_count = simple_async_mock(100)
        self.store.user_last_seen_monthly_active = simple_async_mock()
        self.store.is_trial_user = simple_async_mock()

        appservice = ApplicationService(
            "abcd",
            self.hs.config.server.server_name,
            id="1234",
            namespaces={
                "users": [{
                    "regex": "@_appservice.*:sender",
                    "exclusive": True
                }]
            },
            sender="@appservice:sender",
        )
        requester = Requester(
            user="******",
            access_token_id=None,
            device_id="FOOBAR",
            is_guest=False,
            shadow_banned=False,
            app_service=appservice,
            authenticated_entity="@appservice:server",
        )
        self.get_success(self.auth.check_auth_blocking(requester=requester))

    def test_blocking_mau__appservice_requester_disallowed_when_tracking_ips(
            self):
        self.auth_blocking._max_mau_value = 50
        self.auth_blocking._limit_usage_by_mau = True
        self.auth_blocking._track_appservice_user_ips = True

        self.store.get_monthly_active_count = simple_async_mock(100)
        self.store.user_last_seen_monthly_active = simple_async_mock()
        self.store.is_trial_user = simple_async_mock()

        appservice = ApplicationService(
            "abcd",
            self.hs.config.server.server_name,
            id="1234",
            namespaces={
                "users": [{
                    "regex": "@_appservice.*:sender",
                    "exclusive": True
                }]
            },
            sender="@appservice:sender",
        )
        requester = Requester(
            user="******",
            access_token_id=None,
            device_id="FOOBAR",
            is_guest=False,
            shadow_banned=False,
            app_service=appservice,
            authenticated_entity="@appservice:server",
        )
        self.get_failure(self.auth.check_auth_blocking(requester=requester),
                         ResourceLimitError)

    def test_reserved_threepid(self):
        self.auth_blocking._limit_usage_by_mau = True
        self.auth_blocking._max_mau_value = 1
        self.store.get_monthly_active_count = simple_async_mock(2)
        threepid = {"medium": "email", "address": "*****@*****.**"}
        unknown_threepid = {
            "medium": "email",
            "address": "*****@*****.**"
        }
        self.auth_blocking._mau_limits_reserved_threepids = [threepid]

        self.get_failure(self.auth.check_auth_blocking(), ResourceLimitError)

        self.get_failure(
            self.auth.check_auth_blocking(threepid=unknown_threepid),
            ResourceLimitError)

        self.get_success(self.auth.check_auth_blocking(threepid=threepid))

    def test_hs_disabled(self):
        self.auth_blocking._hs_disabled = True
        self.auth_blocking._hs_disabled_message = "Reason for being disabled"
        e = self.get_failure(self.auth.check_auth_blocking(),
                             ResourceLimitError)
        self.assertEquals(e.value.admin_contact,
                          self.hs.config.server.admin_contact)
        self.assertEquals(e.value.errcode, Codes.RESOURCE_LIMIT_EXCEEDED)
        self.assertEquals(e.value.code, 403)

    def test_hs_disabled_no_server_notices_user(self):
        """Check that 'hs_disabled_message' works correctly when there is no
        server_notices user.
        """
        # this should be the default, but we had a bug where the test was doing the wrong
        # thing, so let's make it explicit
        self.auth_blocking._server_notices_mxid = None

        self.auth_blocking._hs_disabled = True
        self.auth_blocking._hs_disabled_message = "Reason for being disabled"
        e = self.get_failure(self.auth.check_auth_blocking(),
                             ResourceLimitError)
        self.assertEquals(e.value.admin_contact,
                          self.hs.config.server.admin_contact)
        self.assertEquals(e.value.errcode, Codes.RESOURCE_LIMIT_EXCEEDED)
        self.assertEquals(e.value.code, 403)

    def test_server_notices_mxid_special_cased(self):
        self.auth_blocking._hs_disabled = True
        user = "******"
        self.auth_blocking._server_notices_mxid = user
        self.auth_blocking._hs_disabled_message = "Reason for being disabled"
        self.get_success(self.auth.check_auth_blocking(user))
Example #19
0
class AuthTestCase(unittest.TestCase):

    @defer.inlineCallbacks
    def setUp(self):
        self.state_handler = Mock()
        self.store = Mock()

        self.hs = yield setup_test_homeserver(handlers=None)
        self.hs.get_datastore = Mock(return_value=self.store)
        self.auth = Auth(self.hs)

        self.test_user = "******"
        self.test_token = "_test_token_"

    @defer.inlineCallbacks
    def test_get_user_by_req_user_valid_token(self):
        self.store.get_app_service_by_token = Mock(return_value=None)
        user_info = {
            "name": self.test_user,
            "token_id": "ditto",
            "device_id": "device",
        }
        self.store.get_user_by_access_token = Mock(return_value=user_info)

        request = Mock(args={})
        request.args["access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = Mock(return_value=[""])
        requester = yield self.auth.get_user_by_req(request)
        self.assertEquals(requester.user.to_string(), self.test_user)

    def test_get_user_by_req_user_bad_token(self):
        self.store.get_app_service_by_token = Mock(return_value=None)
        self.store.get_user_by_access_token = Mock(return_value=None)

        request = Mock(args={})
        request.args["access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = Mock(return_value=[""])
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    def test_get_user_by_req_user_missing_token(self):
        self.store.get_app_service_by_token = Mock(return_value=None)
        user_info = {
            "name": self.test_user,
            "token_id": "ditto",
        }
        self.store.get_user_by_access_token = Mock(return_value=user_info)

        request = Mock(args={})
        request.requestHeaders.getRawHeaders = Mock(return_value=[""])
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    @defer.inlineCallbacks
    def test_get_user_by_req_appservice_valid_token(self):
        app_service = Mock(token="foobar", url="a_url", sender=self.test_user)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_access_token = Mock(return_value=None)

        request = Mock(args={})
        request.args["access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = Mock(return_value=[""])
        requester = yield self.auth.get_user_by_req(request)
        self.assertEquals(requester.user.to_string(), self.test_user)

    def test_get_user_by_req_appservice_bad_token(self):
        self.store.get_app_service_by_token = Mock(return_value=None)
        self.store.get_user_by_access_token = Mock(return_value=None)

        request = Mock(args={})
        request.args["access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = Mock(return_value=[""])
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    def test_get_user_by_req_appservice_missing_token(self):
        app_service = Mock(token="foobar", url="a_url", sender=self.test_user)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_access_token = Mock(return_value=None)

        request = Mock(args={})
        request.requestHeaders.getRawHeaders = Mock(return_value=[""])
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    @defer.inlineCallbacks
    def test_get_user_by_req_appservice_valid_token_valid_user_id(self):
        masquerading_user_id = "@doppelganger:matrix.org"
        app_service = Mock(token="foobar", url="a_url", sender=self.test_user)
        app_service.is_interested_in_user = Mock(return_value=True)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_access_token = Mock(return_value=None)

        request = Mock(args={})
        request.args["access_token"] = [self.test_token]
        request.args["user_id"] = [masquerading_user_id]
        request.requestHeaders.getRawHeaders = Mock(return_value=[""])
        requester = yield self.auth.get_user_by_req(request)
        self.assertEquals(requester.user.to_string(), masquerading_user_id)

    def test_get_user_by_req_appservice_valid_token_bad_user_id(self):
        masquerading_user_id = "@doppelganger:matrix.org"
        app_service = Mock(token="foobar", url="a_url", sender=self.test_user)
        app_service.is_interested_in_user = Mock(return_value=False)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_access_token = Mock(return_value=None)

        request = Mock(args={})
        request.args["access_token"] = [self.test_token]
        request.args["user_id"] = [masquerading_user_id]
        request.requestHeaders.getRawHeaders = Mock(return_value=[""])
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    @defer.inlineCallbacks
    def test_get_user_from_macaroon(self):
        # TODO(danielwh): Remove this mock when we remove the
        # get_user_by_access_token fallback.
        self.store.get_user_by_access_token = Mock(
            return_value={
                "name": "@baldrick:matrix.org",
                "device_id": "device",
            }
        )

        user_id = "@baldrick:matrix.org"
        macaroon = pymacaroons.Macaroon(
            location=self.hs.config.server_name,
            identifier="key",
            key=self.hs.config.macaroon_secret_key)
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")
        macaroon.add_first_party_caveat("user_id = %s" % (user_id,))
        user_info = yield self.auth.get_user_from_macaroon(macaroon.serialize())
        user = user_info["user"]
        self.assertEqual(UserID.from_string(user_id), user)

        # TODO: device_id should come from the macaroon, but currently comes
        # from the db.
        self.assertEqual(user_info["device_id"], "device")

    @defer.inlineCallbacks
    def test_get_guest_user_from_macaroon(self):
        user_id = "@baldrick:matrix.org"
        macaroon = pymacaroons.Macaroon(
            location=self.hs.config.server_name,
            identifier="key",
            key=self.hs.config.macaroon_secret_key)
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")
        macaroon.add_first_party_caveat("user_id = %s" % (user_id,))
        macaroon.add_first_party_caveat("guest = true")
        serialized = macaroon.serialize()

        user_info = yield self.auth.get_user_from_macaroon(serialized)
        user = user_info["user"]
        is_guest = user_info["is_guest"]
        self.assertEqual(UserID.from_string(user_id), user)
        self.assertTrue(is_guest)

    @defer.inlineCallbacks
    def test_get_user_from_macaroon_user_db_mismatch(self):
        self.store.get_user_by_access_token = Mock(
            return_value={"name": "@percy:matrix.org"}
        )

        user = "******"
        macaroon = pymacaroons.Macaroon(
            location=self.hs.config.server_name,
            identifier="key",
            key=self.hs.config.macaroon_secret_key)
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")
        macaroon.add_first_party_caveat("user_id = %s" % (user,))
        with self.assertRaises(AuthError) as cm:
            yield self.auth.get_user_from_macaroon(macaroon.serialize())
        self.assertEqual(401, cm.exception.code)
        self.assertIn("User mismatch", cm.exception.msg)

    @defer.inlineCallbacks
    def test_get_user_from_macaroon_missing_caveat(self):
        # TODO(danielwh): Remove this mock when we remove the
        # get_user_by_access_token fallback.
        self.store.get_user_by_access_token = Mock(
            return_value={"name": "@baldrick:matrix.org"}
        )

        macaroon = pymacaroons.Macaroon(
            location=self.hs.config.server_name,
            identifier="key",
            key=self.hs.config.macaroon_secret_key)
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")

        with self.assertRaises(AuthError) as cm:
            yield self.auth.get_user_from_macaroon(macaroon.serialize())
        self.assertEqual(401, cm.exception.code)
        self.assertIn("No user caveat", cm.exception.msg)

    @defer.inlineCallbacks
    def test_get_user_from_macaroon_wrong_key(self):
        # TODO(danielwh): Remove this mock when we remove the
        # get_user_by_access_token fallback.
        self.store.get_user_by_access_token = Mock(
            return_value={"name": "@baldrick:matrix.org"}
        )

        user = "******"
        macaroon = pymacaroons.Macaroon(
            location=self.hs.config.server_name,
            identifier="key",
            key=self.hs.config.macaroon_secret_key + "wrong")
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")
        macaroon.add_first_party_caveat("user_id = %s" % (user,))

        with self.assertRaises(AuthError) as cm:
            yield self.auth.get_user_from_macaroon(macaroon.serialize())
        self.assertEqual(401, cm.exception.code)
        self.assertIn("Invalid macaroon", cm.exception.msg)

    @defer.inlineCallbacks
    def test_get_user_from_macaroon_unknown_caveat(self):
        # TODO(danielwh): Remove this mock when we remove the
        # get_user_by_access_token fallback.
        self.store.get_user_by_access_token = Mock(
            return_value={"name": "@baldrick:matrix.org"}
        )

        user = "******"
        macaroon = pymacaroons.Macaroon(
            location=self.hs.config.server_name,
            identifier="key",
            key=self.hs.config.macaroon_secret_key)
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")
        macaroon.add_first_party_caveat("user_id = %s" % (user,))
        macaroon.add_first_party_caveat("cunning > fox")

        with self.assertRaises(AuthError) as cm:
            yield self.auth.get_user_from_macaroon(macaroon.serialize())
        self.assertEqual(401, cm.exception.code)
        self.assertIn("Invalid macaroon", cm.exception.msg)

    @defer.inlineCallbacks
    def test_get_user_from_macaroon_expired(self):
        # TODO(danielwh): Remove this mock when we remove the
        # get_user_by_access_token fallback.
        self.store.get_user_by_access_token = Mock(
            return_value={"name": "@baldrick:matrix.org"}
        )

        self.store.get_user_by_access_token = Mock(
            return_value={"name": "@baldrick:matrix.org"}
        )

        user = "******"
        macaroon = pymacaroons.Macaroon(
            location=self.hs.config.server_name,
            identifier="key",
            key=self.hs.config.macaroon_secret_key)
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")
        macaroon.add_first_party_caveat("user_id = %s" % (user,))
        macaroon.add_first_party_caveat("time < -2000")  # ms

        self.hs.clock.now = 5000  # seconds
        self.hs.config.expire_access_token = True
        # yield self.auth.get_user_from_macaroon(macaroon.serialize())
        # TODO(daniel): Turn on the check that we validate expiration, when we
        # validate expiration (and remove the above line, which will start
        # throwing).
        with self.assertRaises(AuthError) as cm:
            yield self.auth.get_user_from_macaroon(macaroon.serialize())
        self.assertEqual(401, cm.exception.code)
        self.assertIn("Invalid macaroon", cm.exception.msg)

    @defer.inlineCallbacks
    def test_get_user_from_macaroon_with_valid_duration(self):
        # TODO(danielwh): Remove this mock when we remove the
        # get_user_by_access_token fallback.
        self.store.get_user_by_access_token = Mock(
            return_value={"name": "@baldrick:matrix.org"}
        )

        self.store.get_user_by_access_token = Mock(
            return_value={"name": "@baldrick:matrix.org"}
        )

        user_id = "@baldrick:matrix.org"
        macaroon = pymacaroons.Macaroon(
            location=self.hs.config.server_name,
            identifier="key",
            key=self.hs.config.macaroon_secret_key)
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")
        macaroon.add_first_party_caveat("user_id = %s" % (user_id,))
        macaroon.add_first_party_caveat("time < 900000000")  # ms

        self.hs.clock.now = 5000  # seconds
        self.hs.config.expire_access_token = True

        user_info = yield self.auth.get_user_from_macaroon(macaroon.serialize())
        user = user_info["user"]
        self.assertEqual(UserID.from_string(user_id), user)
Example #20
0
class AuthTestCase(unittest.TestCase):
    @defer.inlineCallbacks
    def setUp(self):
        self.state_handler = Mock()
        self.store = Mock()

        self.hs = yield setup_test_homeserver(self.addCleanup, handlers=None)
        self.hs.get_datastore = Mock(return_value=self.store)
        self.hs.handlers = TestHandlers(self.hs)
        self.auth = Auth(self.hs)

        self.test_user = "******"
        self.test_token = b"_test_token_"

        # this is overridden for the appservice tests
        self.store.get_app_service_by_token = Mock(return_value=None)

        self.store.is_support_user = Mock(return_value=defer.succeed(False))

    @defer.inlineCallbacks
    def test_get_user_by_req_user_valid_token(self):
        user_info = {"name": self.test_user, "token_id": "ditto", "device_id": "device"}
        self.store.get_user_by_access_token = Mock(return_value=user_info)

        request = Mock(args={})
        request.args[b"access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        requester = yield self.auth.get_user_by_req(request)
        self.assertEquals(requester.user.to_string(), self.test_user)

    def test_get_user_by_req_user_bad_token(self):
        self.store.get_user_by_access_token = Mock(return_value=None)

        request = Mock(args={})
        request.args[b"access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    def test_get_user_by_req_user_missing_token(self):
        user_info = {"name": self.test_user, "token_id": "ditto"}
        self.store.get_user_by_access_token = Mock(return_value=user_info)

        request = Mock(args={})
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    @defer.inlineCallbacks
    def test_get_user_by_req_appservice_valid_token(self):
        app_service = Mock(
            token="foobar", url="a_url", sender=self.test_user, ip_range_whitelist=None
        )
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_access_token = Mock(return_value=None)

        request = Mock(args={})
        request.getClientIP.return_value = "127.0.0.1"
        request.args[b"access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        requester = yield self.auth.get_user_by_req(request)
        self.assertEquals(requester.user.to_string(), self.test_user)

    @defer.inlineCallbacks
    def test_get_user_by_req_appservice_valid_token_good_ip(self):
        from netaddr import IPSet

        app_service = Mock(
            token="foobar",
            url="a_url",
            sender=self.test_user,
            ip_range_whitelist=IPSet(["192.168/16"]),
        )
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_access_token = Mock(return_value=None)

        request = Mock(args={})
        request.getClientIP.return_value = "192.168.10.10"
        request.args[b"access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        requester = yield self.auth.get_user_by_req(request)
        self.assertEquals(requester.user.to_string(), self.test_user)

    def test_get_user_by_req_appservice_valid_token_bad_ip(self):
        from netaddr import IPSet

        app_service = Mock(
            token="foobar",
            url="a_url",
            sender=self.test_user,
            ip_range_whitelist=IPSet(["192.168/16"]),
        )
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_access_token = Mock(return_value=None)

        request = Mock(args={})
        request.getClientIP.return_value = "131.111.8.42"
        request.args[b"access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    def test_get_user_by_req_appservice_bad_token(self):
        self.store.get_app_service_by_token = Mock(return_value=None)
        self.store.get_user_by_access_token = Mock(return_value=None)

        request = Mock(args={})
        request.args[b"access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    def test_get_user_by_req_appservice_missing_token(self):
        app_service = Mock(token="foobar", url="a_url", sender=self.test_user)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_access_token = Mock(return_value=None)

        request = Mock(args={})
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    @defer.inlineCallbacks
    def test_get_user_by_req_appservice_valid_token_valid_user_id(self):
        masquerading_user_id = b"@doppelganger:matrix.org"
        app_service = Mock(
            token="foobar", url="a_url", sender=self.test_user, ip_range_whitelist=None
        )
        app_service.is_interested_in_user = Mock(return_value=True)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_access_token = Mock(return_value=None)

        request = Mock(args={})
        request.getClientIP.return_value = "127.0.0.1"
        request.args[b"access_token"] = [self.test_token]
        request.args[b"user_id"] = [masquerading_user_id]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        requester = yield self.auth.get_user_by_req(request)
        self.assertEquals(
            requester.user.to_string(), masquerading_user_id.decode('utf8')
        )

    def test_get_user_by_req_appservice_valid_token_bad_user_id(self):
        masquerading_user_id = b"@doppelganger:matrix.org"
        app_service = Mock(
            token="foobar", url="a_url", sender=self.test_user, ip_range_whitelist=None
        )
        app_service.is_interested_in_user = Mock(return_value=False)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_access_token = Mock(return_value=None)

        request = Mock(args={})
        request.getClientIP.return_value = "127.0.0.1"
        request.args[b"access_token"] = [self.test_token]
        request.args[b"user_id"] = [masquerading_user_id]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    @defer.inlineCallbacks
    def test_get_user_from_macaroon(self):
        self.store.get_user_by_access_token = Mock(
            return_value={"name": "@baldrick:matrix.org", "device_id": "device"}
        )

        user_id = "@baldrick:matrix.org"
        macaroon = pymacaroons.Macaroon(
            location=self.hs.config.server_name,
            identifier="key",
            key=self.hs.config.macaroon_secret_key,
        )
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")
        macaroon.add_first_party_caveat("user_id = %s" % (user_id,))
        user_info = yield self.auth.get_user_by_access_token(macaroon.serialize())
        user = user_info["user"]
        self.assertEqual(UserID.from_string(user_id), user)

        # TODO: device_id should come from the macaroon, but currently comes
        # from the db.
        self.assertEqual(user_info["device_id"], "device")

    @defer.inlineCallbacks
    def test_get_guest_user_from_macaroon(self):
        self.store.get_user_by_id = Mock(return_value={"is_guest": True})
        self.store.get_user_by_access_token = Mock(return_value=None)

        user_id = "@baldrick:matrix.org"
        macaroon = pymacaroons.Macaroon(
            location=self.hs.config.server_name,
            identifier="key",
            key=self.hs.config.macaroon_secret_key,
        )
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")
        macaroon.add_first_party_caveat("user_id = %s" % (user_id,))
        macaroon.add_first_party_caveat("guest = true")
        serialized = macaroon.serialize()

        user_info = yield self.auth.get_user_by_access_token(serialized)
        user = user_info["user"]
        is_guest = user_info["is_guest"]
        self.assertEqual(UserID.from_string(user_id), user)
        self.assertTrue(is_guest)
        self.store.get_user_by_id.assert_called_with(user_id)

    @defer.inlineCallbacks
    def test_cannot_use_regular_token_as_guest(self):
        USER_ID = "@percy:matrix.org"
        self.store.add_access_token_to_user = Mock()

        token = yield self.hs.handlers.auth_handler.issue_access_token(
            USER_ID, "DEVICE"
        )
        self.store.add_access_token_to_user.assert_called_with(USER_ID, token, "DEVICE")

        def get_user(tok):
            if token != tok:
                return None
            return {
                "name": USER_ID,
                "is_guest": False,
                "token_id": 1234,
                "device_id": "DEVICE",
            }

        self.store.get_user_by_access_token = get_user
        self.store.get_user_by_id = Mock(return_value={"is_guest": False})

        # check the token works
        request = Mock(args={})
        request.args[b"access_token"] = [token.encode('ascii')]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        requester = yield self.auth.get_user_by_req(request, allow_guest=True)
        self.assertEqual(UserID.from_string(USER_ID), requester.user)
        self.assertFalse(requester.is_guest)

        # add an is_guest caveat
        mac = pymacaroons.Macaroon.deserialize(token)
        mac.add_first_party_caveat("guest = true")
        guest_tok = mac.serialize()

        # the token should *not* work now
        request = Mock(args={})
        request.args[b"access_token"] = [guest_tok.encode('ascii')]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()

        with self.assertRaises(AuthError) as cm:
            yield self.auth.get_user_by_req(request, allow_guest=True)

        self.assertEqual(401, cm.exception.code)
        self.assertEqual("Guest access token used for regular user", cm.exception.msg)

        self.store.get_user_by_id.assert_called_with(USER_ID)

    @defer.inlineCallbacks
    def test_blocking_mau(self):
        self.hs.config.limit_usage_by_mau = False
        self.hs.config.max_mau_value = 50
        lots_of_users = 100
        small_number_of_users = 1

        # Ensure no error thrown
        yield self.auth.check_auth_blocking()

        self.hs.config.limit_usage_by_mau = True

        self.store.get_monthly_active_count = Mock(
            return_value=defer.succeed(lots_of_users)
        )

        with self.assertRaises(ResourceLimitError) as e:
            yield self.auth.check_auth_blocking()
        self.assertEquals(e.exception.admin_contact, self.hs.config.admin_contact)
        self.assertEquals(e.exception.errcode, Codes.RESOURCE_LIMIT_EXCEEDED)
        self.assertEquals(e.exception.code, 403)

        # Ensure does not throw an error
        self.store.get_monthly_active_count = Mock(
            return_value=defer.succeed(small_number_of_users)
        )
        yield self.auth.check_auth_blocking()

    @defer.inlineCallbacks
    def test_reserved_threepid(self):
        self.hs.config.limit_usage_by_mau = True
        self.hs.config.max_mau_value = 1
        self.store.get_monthly_active_count = lambda: defer.succeed(2)
        threepid = {'medium': 'email', 'address': '*****@*****.**'}
        unknown_threepid = {'medium': 'email', 'address': '*****@*****.**'}
        self.hs.config.mau_limits_reserved_threepids = [threepid]

        yield self.store.register(user_id='user1', token="123", password_hash=None)
        with self.assertRaises(ResourceLimitError):
            yield self.auth.check_auth_blocking()

        with self.assertRaises(ResourceLimitError):
            yield self.auth.check_auth_blocking(threepid=unknown_threepid)

        yield self.auth.check_auth_blocking(threepid=threepid)

    @defer.inlineCallbacks
    def test_hs_disabled(self):
        self.hs.config.hs_disabled = True
        self.hs.config.hs_disabled_message = "Reason for being disabled"
        with self.assertRaises(ResourceLimitError) as e:
            yield self.auth.check_auth_blocking()
        self.assertEquals(e.exception.admin_contact, self.hs.config.admin_contact)
        self.assertEquals(e.exception.errcode, Codes.RESOURCE_LIMIT_EXCEEDED)
        self.assertEquals(e.exception.code, 403)

    @defer.inlineCallbacks
    def test_hs_disabled_no_server_notices_user(self):
        """Check that 'hs_disabled_message' works correctly when there is no
        server_notices user.
        """
        # this should be the default, but we had a bug where the test was doing the wrong
        # thing, so let's make it explicit
        self.hs.config.server_notices_mxid = None

        self.hs.config.hs_disabled = True
        self.hs.config.hs_disabled_message = "Reason for being disabled"
        with self.assertRaises(ResourceLimitError) as e:
            yield self.auth.check_auth_blocking()
        self.assertEquals(e.exception.admin_contact, self.hs.config.admin_contact)
        self.assertEquals(e.exception.errcode, Codes.RESOURCE_LIMIT_EXCEEDED)
        self.assertEquals(e.exception.code, 403)

    @defer.inlineCallbacks
    def test_server_notices_mxid_special_cased(self):
        self.hs.config.hs_disabled = True
        user = "******"
        self.hs.config.server_notices_mxid = user
        self.hs.config.hs_disabled_message = "Reason for being disabled"
        yield self.auth.check_auth_blocking(user)
Example #21
0
        try:
            auth.check(e, auth_events=auth_events)
        except Exception as ex:
            print("Failed:", e.event_id, e.type, e.state_key)
            print("Auth_events:", auth_events)
            print(ex)
            print(json.dumps(e.get_dict(), sort_keys=True, indent=4))
            # raise
        print("Success:", e.event_id, e.type, e.state_key)


if __name__ == "__main__":
    parser = argparse.ArgumentParser()

    parser.add_argument("json",
                        nargs="?",
                        type=argparse.FileType("r"),
                        default=sys.stdin)

    args = parser.parse_args()

    js = json.load(args.json)

    auth = Auth(Mock())
    check_auth(
        auth,
        [FrozenEvent(d) for d in js["auth_chain"]],
        [FrozenEvent(d) for d in js.get("pdus", [])],
    )
Example #22
0
class AuthTestCase(unittest.TestCase):
    def setUp(self):
        self.state_handler = Mock()
        self.store = Mock()

        self.hs = Mock()
        self.hs.get_datastore = Mock(return_value=self.store)
        self.hs.get_state_handler = Mock(return_value=self.state_handler)
        self.auth = Auth(self.hs)

        self.test_user = "******"
        self.test_token = "_test_token_"

    @defer.inlineCallbacks
    def test_get_user_by_req_user_valid_token(self):
        self.store.get_app_service_by_token = Mock(return_value=None)
        user_info = {
            "name": self.test_user,
            "device_id": "nothing",
            "token_id": "ditto",
            "admin": False
        }
        self.store.get_user_by_token = Mock(return_value=user_info)

        request = Mock(args={})
        request.args["access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = Mock(return_value=[""])
        (user, info) = yield self.auth.get_user_by_req(request)
        self.assertEquals(user.to_string(), self.test_user)

    def test_get_user_by_req_user_bad_token(self):
        self.store.get_app_service_by_token = Mock(return_value=None)
        self.store.get_user_by_token = Mock(return_value=None)

        request = Mock(args={})
        request.args["access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = Mock(return_value=[""])
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    def test_get_user_by_req_user_missing_token(self):
        self.store.get_app_service_by_token = Mock(return_value=None)
        user_info = {
            "name": self.test_user,
            "device_id": "nothing",
            "token_id": "ditto",
            "admin": False
        }
        self.store.get_user_by_token = Mock(return_value=user_info)

        request = Mock(args={})
        request.requestHeaders.getRawHeaders = Mock(return_value=[""])
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    @defer.inlineCallbacks
    def test_get_user_by_req_appservice_valid_token(self):
        app_service = Mock(token="foobar", url="a_url", sender=self.test_user)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_token = Mock(return_value=None)

        request = Mock(args={})
        request.args["access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = Mock(return_value=[""])
        (user, info) = yield self.auth.get_user_by_req(request)
        self.assertEquals(user.to_string(), self.test_user)

    def test_get_user_by_req_appservice_bad_token(self):
        self.store.get_app_service_by_token = Mock(return_value=None)
        self.store.get_user_by_token = Mock(return_value=None)

        request = Mock(args={})
        request.args["access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = Mock(return_value=[""])
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    def test_get_user_by_req_appservice_missing_token(self):
        app_service = Mock(token="foobar", url="a_url", sender=self.test_user)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_token = Mock(return_value=None)

        request = Mock(args={})
        request.requestHeaders.getRawHeaders = Mock(return_value=[""])
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    @defer.inlineCallbacks
    def test_get_user_by_req_appservice_valid_token_valid_user_id(self):
        masquerading_user_id = "@doppelganger:matrix.org"
        app_service = Mock(token="foobar", url="a_url", sender=self.test_user)
        app_service.is_interested_in_user = Mock(return_value=True)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_token = Mock(return_value=None)

        request = Mock(args={})
        request.args["access_token"] = [self.test_token]
        request.args["user_id"] = [masquerading_user_id]
        request.requestHeaders.getRawHeaders = Mock(return_value=[""])
        (user, info) = yield self.auth.get_user_by_req(request)
        self.assertEquals(user.to_string(), masquerading_user_id)

    def test_get_user_by_req_appservice_valid_token_bad_user_id(self):
        masquerading_user_id = "@doppelganger:matrix.org"
        app_service = Mock(token="foobar", url="a_url", sender=self.test_user)
        app_service.is_interested_in_user = Mock(return_value=False)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_token = Mock(return_value=None)

        request = Mock(args={})
        request.args["access_token"] = [self.test_token]
        request.args["user_id"] = [masquerading_user_id]
        request.requestHeaders.getRawHeaders = Mock(return_value=[""])
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)
Example #23
0
class AuthTestCase(unittest.TestCase):
    @defer.inlineCallbacks
    def setUp(self):
        self.state_handler = Mock()
        self.store = Mock()

        self.hs = yield setup_test_homeserver(handlers=None)
        self.hs.get_datastore = Mock(return_value=self.store)
        self.hs.handlers = TestHandlers(self.hs)
        self.auth = Auth(self.hs)

        self.test_user = "******"
        self.test_token = "_test_token_"

        # this is overridden for the appservice tests
        self.store.get_app_service_by_token = Mock(return_value=None)

    @defer.inlineCallbacks
    def test_get_user_by_req_user_valid_token(self):
        user_info = {
            "name": self.test_user,
            "token_id": "ditto",
            "device_id": "device",
        }
        self.store.get_user_by_access_token = Mock(return_value=user_info)

        request = Mock(args={})
        request.args["access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        requester = yield self.auth.get_user_by_req(request)
        self.assertEquals(requester.user.to_string(), self.test_user)

    def test_get_user_by_req_user_bad_token(self):
        self.store.get_user_by_access_token = Mock(return_value=None)

        request = Mock(args={})
        request.args["access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    def test_get_user_by_req_user_missing_token(self):
        user_info = {
            "name": self.test_user,
            "token_id": "ditto",
        }
        self.store.get_user_by_access_token = Mock(return_value=user_info)

        request = Mock(args={})
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    @defer.inlineCallbacks
    def test_get_user_by_req_appservice_valid_token(self):
        app_service = Mock(token="foobar", url="a_url", sender=self.test_user)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_access_token = Mock(return_value=None)

        request = Mock(args={})
        request.args["access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        requester = yield self.auth.get_user_by_req(request)
        self.assertEquals(requester.user.to_string(), self.test_user)

    def test_get_user_by_req_appservice_bad_token(self):
        self.store.get_app_service_by_token = Mock(return_value=None)
        self.store.get_user_by_access_token = Mock(return_value=None)

        request = Mock(args={})
        request.args["access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    def test_get_user_by_req_appservice_missing_token(self):
        app_service = Mock(token="foobar", url="a_url", sender=self.test_user)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_access_token = Mock(return_value=None)

        request = Mock(args={})
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    @defer.inlineCallbacks
    def test_get_user_by_req_appservice_valid_token_valid_user_id(self):
        masquerading_user_id = "@doppelganger:matrix.org"
        app_service = Mock(token="foobar", url="a_url", sender=self.test_user)
        app_service.is_interested_in_user = Mock(return_value=True)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_access_token = Mock(return_value=None)

        request = Mock(args={})
        request.args["access_token"] = [self.test_token]
        request.args["user_id"] = [masquerading_user_id]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        requester = yield self.auth.get_user_by_req(request)
        self.assertEquals(requester.user.to_string(), masquerading_user_id)

    def test_get_user_by_req_appservice_valid_token_bad_user_id(self):
        masquerading_user_id = "@doppelganger:matrix.org"
        app_service = Mock(token="foobar", url="a_url", sender=self.test_user)
        app_service.is_interested_in_user = Mock(return_value=False)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_access_token = Mock(return_value=None)

        request = Mock(args={})
        request.args["access_token"] = [self.test_token]
        request.args["user_id"] = [masquerading_user_id]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    @defer.inlineCallbacks
    def test_get_user_from_macaroon(self):
        # TODO(danielwh): Remove this mock when we remove the
        # get_user_by_access_token fallback.
        self.store.get_user_by_access_token = Mock(return_value={
            "name": "@baldrick:matrix.org",
            "device_id": "device",
        })

        user_id = "@baldrick:matrix.org"
        macaroon = pymacaroons.Macaroon(location=self.hs.config.server_name,
                                        identifier="key",
                                        key=self.hs.config.macaroon_secret_key)
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")
        macaroon.add_first_party_caveat("user_id = %s" % (user_id, ))
        user_info = yield self.auth.get_user_by_access_token(
            macaroon.serialize())
        user = user_info["user"]
        self.assertEqual(UserID.from_string(user_id), user)

        # TODO: device_id should come from the macaroon, but currently comes
        # from the db.
        self.assertEqual(user_info["device_id"], "device")

    @defer.inlineCallbacks
    def test_get_guest_user_from_macaroon(self):
        self.store.get_user_by_id = Mock(return_value={
            "is_guest": True,
        })

        user_id = "@baldrick:matrix.org"
        macaroon = pymacaroons.Macaroon(location=self.hs.config.server_name,
                                        identifier="key",
                                        key=self.hs.config.macaroon_secret_key)
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")
        macaroon.add_first_party_caveat("user_id = %s" % (user_id, ))
        macaroon.add_first_party_caveat("guest = true")
        serialized = macaroon.serialize()

        user_info = yield self.auth.get_user_by_access_token(serialized)
        user = user_info["user"]
        is_guest = user_info["is_guest"]
        self.assertEqual(UserID.from_string(user_id), user)
        self.assertTrue(is_guest)
        self.store.get_user_by_id.assert_called_with(user_id)

    @defer.inlineCallbacks
    def test_get_user_from_macaroon_user_db_mismatch(self):
        self.store.get_user_by_access_token = Mock(
            return_value={"name": "@percy:matrix.org"})

        user = "******"
        macaroon = pymacaroons.Macaroon(location=self.hs.config.server_name,
                                        identifier="key",
                                        key=self.hs.config.macaroon_secret_key)
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")
        macaroon.add_first_party_caveat("user_id = %s" % (user, ))
        with self.assertRaises(AuthError) as cm:
            yield self.auth.get_user_by_access_token(macaroon.serialize())
        self.assertEqual(401, cm.exception.code)
        self.assertIn("User mismatch", cm.exception.msg)

    @defer.inlineCallbacks
    def test_get_user_from_macaroon_missing_caveat(self):
        # TODO(danielwh): Remove this mock when we remove the
        # get_user_by_access_token fallback.
        self.store.get_user_by_access_token = Mock(
            return_value={"name": "@baldrick:matrix.org"})

        macaroon = pymacaroons.Macaroon(location=self.hs.config.server_name,
                                        identifier="key",
                                        key=self.hs.config.macaroon_secret_key)
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")

        with self.assertRaises(AuthError) as cm:
            yield self.auth.get_user_by_access_token(macaroon.serialize())
        self.assertEqual(401, cm.exception.code)
        self.assertIn("No user caveat", cm.exception.msg)

    @defer.inlineCallbacks
    def test_get_user_from_macaroon_wrong_key(self):
        # TODO(danielwh): Remove this mock when we remove the
        # get_user_by_access_token fallback.
        self.store.get_user_by_access_token = Mock(
            return_value={"name": "@baldrick:matrix.org"})

        user = "******"
        macaroon = pymacaroons.Macaroon(
            location=self.hs.config.server_name,
            identifier="key",
            key=self.hs.config.macaroon_secret_key + "wrong")
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")
        macaroon.add_first_party_caveat("user_id = %s" % (user, ))

        with self.assertRaises(AuthError) as cm:
            yield self.auth.get_user_by_access_token(macaroon.serialize())
        self.assertEqual(401, cm.exception.code)
        self.assertIn("Invalid macaroon", cm.exception.msg)

    @defer.inlineCallbacks
    def test_get_user_from_macaroon_unknown_caveat(self):
        # TODO(danielwh): Remove this mock when we remove the
        # get_user_by_access_token fallback.
        self.store.get_user_by_access_token = Mock(
            return_value={"name": "@baldrick:matrix.org"})

        user = "******"
        macaroon = pymacaroons.Macaroon(location=self.hs.config.server_name,
                                        identifier="key",
                                        key=self.hs.config.macaroon_secret_key)
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")
        macaroon.add_first_party_caveat("user_id = %s" % (user, ))
        macaroon.add_first_party_caveat("cunning > fox")

        with self.assertRaises(AuthError) as cm:
            yield self.auth.get_user_by_access_token(macaroon.serialize())
        self.assertEqual(401, cm.exception.code)
        self.assertIn("Invalid macaroon", cm.exception.msg)

    @defer.inlineCallbacks
    def test_get_user_from_macaroon_expired(self):
        # TODO(danielwh): Remove this mock when we remove the
        # get_user_by_access_token fallback.
        self.store.get_user_by_access_token = Mock(
            return_value={"name": "@baldrick:matrix.org"})

        self.store.get_user_by_access_token = Mock(
            return_value={"name": "@baldrick:matrix.org"})

        user = "******"
        macaroon = pymacaroons.Macaroon(location=self.hs.config.server_name,
                                        identifier="key",
                                        key=self.hs.config.macaroon_secret_key)
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")
        macaroon.add_first_party_caveat("user_id = %s" % (user, ))
        macaroon.add_first_party_caveat("time < -2000")  # ms

        self.hs.clock.now = 5000  # seconds
        self.hs.config.expire_access_token = True
        # yield self.auth.get_user_by_access_token(macaroon.serialize())
        # TODO(daniel): Turn on the check that we validate expiration, when we
        # validate expiration (and remove the above line, which will start
        # throwing).
        with self.assertRaises(AuthError) as cm:
            yield self.auth.get_user_by_access_token(macaroon.serialize())
        self.assertEqual(401, cm.exception.code)
        self.assertIn("Invalid macaroon", cm.exception.msg)

    @defer.inlineCallbacks
    def test_get_user_from_macaroon_with_valid_duration(self):
        # TODO(danielwh): Remove this mock when we remove the
        # get_user_by_access_token fallback.
        self.store.get_user_by_access_token = Mock(
            return_value={"name": "@baldrick:matrix.org"})

        self.store.get_user_by_access_token = Mock(
            return_value={"name": "@baldrick:matrix.org"})

        user_id = "@baldrick:matrix.org"
        macaroon = pymacaroons.Macaroon(location=self.hs.config.server_name,
                                        identifier="key",
                                        key=self.hs.config.macaroon_secret_key)
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")
        macaroon.add_first_party_caveat("user_id = %s" % (user_id, ))
        macaroon.add_first_party_caveat("time < 900000000")  # ms

        self.hs.clock.now = 5000  # seconds
        self.hs.config.expire_access_token = True

        user_info = yield self.auth.get_user_by_access_token(
            macaroon.serialize())
        user = user_info["user"]
        self.assertEqual(UserID.from_string(user_id), user)

    @defer.inlineCallbacks
    def test_cannot_use_regular_token_as_guest(self):
        USER_ID = "@percy:matrix.org"
        self.store.add_access_token_to_user = Mock()

        token = yield self.hs.handlers.auth_handler.issue_access_token(
            USER_ID, "DEVICE")
        self.store.add_access_token_to_user.assert_called_with(
            USER_ID, token, "DEVICE")

        def get_user(tok):
            if token != tok:
                return None
            return {
                "name": USER_ID,
                "is_guest": False,
                "token_id": 1234,
                "device_id": "DEVICE",
            }

        self.store.get_user_by_access_token = get_user
        self.store.get_user_by_id = Mock(return_value={
            "is_guest": False,
        })

        # check the token works
        request = Mock(args={})
        request.args["access_token"] = [token]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        requester = yield self.auth.get_user_by_req(request, allow_guest=True)
        self.assertEqual(UserID.from_string(USER_ID), requester.user)
        self.assertFalse(requester.is_guest)

        # add an is_guest caveat
        mac = pymacaroons.Macaroon.deserialize(token)
        mac.add_first_party_caveat("guest = true")
        guest_tok = mac.serialize()

        # the token should *not* work now
        request = Mock(args={})
        request.args["access_token"] = [guest_tok]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()

        with self.assertRaises(AuthError) as cm:
            yield self.auth.get_user_by_req(request, allow_guest=True)

        self.assertEqual(401, cm.exception.code)
        self.assertEqual("Guest access token used for regular user",
                         cm.exception.msg)

        self.store.get_user_by_id.assert_called_with(USER_ID)
Example #24
0
class AuthTestCase(unittest.TestCase):
    @defer.inlineCallbacks
    def setUp(self):
        self.state_handler = Mock()
        self.store = Mock()

        self.hs = yield setup_test_homeserver(handlers=None)
        self.hs.get_datastore = Mock(return_value=self.store)
        self.auth = Auth(self.hs)

        self.test_user = "******"
        self.test_token = "_test_token_"

    @defer.inlineCallbacks
    def test_get_user_by_req_user_valid_token(self):
        self.store.get_app_service_by_token = Mock(return_value=None)
        user_info = {
            "name": self.test_user,
            "token_id": "ditto",
        }
        self.store.get_user_by_access_token = Mock(return_value=user_info)

        request = Mock(args={})
        request.args["access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = Mock(return_value=[""])
        requester = yield self.auth.get_user_by_req(request)
        self.assertEquals(requester.user.to_string(), self.test_user)

    def test_get_user_by_req_user_bad_token(self):
        self.store.get_app_service_by_token = Mock(return_value=None)
        self.store.get_user_by_access_token = Mock(return_value=None)

        request = Mock(args={})
        request.args["access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = Mock(return_value=[""])
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    def test_get_user_by_req_user_missing_token(self):
        self.store.get_app_service_by_token = Mock(return_value=None)
        user_info = {
            "name": self.test_user,
            "token_id": "ditto",
        }
        self.store.get_user_by_access_token = Mock(return_value=user_info)

        request = Mock(args={})
        request.requestHeaders.getRawHeaders = Mock(return_value=[""])
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    @defer.inlineCallbacks
    def test_get_user_by_req_appservice_valid_token(self):
        app_service = Mock(token="foobar", url="a_url", sender=self.test_user)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_access_token = Mock(return_value=None)

        request = Mock(args={})
        request.args["access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = Mock(return_value=[""])
        requester = yield self.auth.get_user_by_req(request)
        self.assertEquals(requester.user.to_string(), self.test_user)

    def test_get_user_by_req_appservice_bad_token(self):
        self.store.get_app_service_by_token = Mock(return_value=None)
        self.store.get_user_by_access_token = Mock(return_value=None)

        request = Mock(args={})
        request.args["access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = Mock(return_value=[""])
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    def test_get_user_by_req_appservice_missing_token(self):
        app_service = Mock(token="foobar", url="a_url", sender=self.test_user)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_access_token = Mock(return_value=None)

        request = Mock(args={})
        request.requestHeaders.getRawHeaders = Mock(return_value=[""])
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    @defer.inlineCallbacks
    def test_get_user_by_req_appservice_valid_token_valid_user_id(self):
        masquerading_user_id = "@doppelganger:matrix.org"
        app_service = Mock(token="foobar", url="a_url", sender=self.test_user)
        app_service.is_interested_in_user = Mock(return_value=True)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_access_token = Mock(return_value=None)

        request = Mock(args={})
        request.args["access_token"] = [self.test_token]
        request.args["user_id"] = [masquerading_user_id]
        request.requestHeaders.getRawHeaders = Mock(return_value=[""])
        requester = yield self.auth.get_user_by_req(request)
        self.assertEquals(requester.user.to_string(), masquerading_user_id)

    def test_get_user_by_req_appservice_valid_token_bad_user_id(self):
        masquerading_user_id = "@doppelganger:matrix.org"
        app_service = Mock(token="foobar", url="a_url", sender=self.test_user)
        app_service.is_interested_in_user = Mock(return_value=False)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_access_token = Mock(return_value=None)

        request = Mock(args={})
        request.args["access_token"] = [self.test_token]
        request.args["user_id"] = [masquerading_user_id]
        request.requestHeaders.getRawHeaders = Mock(return_value=[""])
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    @defer.inlineCallbacks
    def test_get_user_from_macaroon(self):
        # TODO(danielwh): Remove this mock when we remove the
        # get_user_by_access_token fallback.
        self.store.get_user_by_access_token = Mock(
            return_value={"name": "@baldrick:matrix.org"})

        user_id = "@baldrick:matrix.org"
        macaroon = pymacaroons.Macaroon(location=self.hs.config.server_name,
                                        identifier="key",
                                        key=self.hs.config.macaroon_secret_key)
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")
        macaroon.add_first_party_caveat("user_id = %s" % (user_id, ))
        user_info = yield self.auth.get_user_from_macaroon(
            macaroon.serialize())
        user = user_info["user"]
        self.assertEqual(UserID.from_string(user_id), user)

    @defer.inlineCallbacks
    def test_get_guest_user_from_macaroon(self):
        user_id = "@baldrick:matrix.org"
        macaroon = pymacaroons.Macaroon(location=self.hs.config.server_name,
                                        identifier="key",
                                        key=self.hs.config.macaroon_secret_key)
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")
        macaroon.add_first_party_caveat("user_id = %s" % (user_id, ))
        macaroon.add_first_party_caveat("guest = true")
        serialized = macaroon.serialize()

        user_info = yield self.auth.get_user_from_macaroon(serialized)
        user = user_info["user"]
        is_guest = user_info["is_guest"]
        self.assertEqual(UserID.from_string(user_id), user)
        self.assertTrue(is_guest)

    @defer.inlineCallbacks
    def test_get_user_from_macaroon_user_db_mismatch(self):
        self.store.get_user_by_access_token = Mock(
            return_value={"name": "@percy:matrix.org"})

        user = "******"
        macaroon = pymacaroons.Macaroon(location=self.hs.config.server_name,
                                        identifier="key",
                                        key=self.hs.config.macaroon_secret_key)
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")
        macaroon.add_first_party_caveat("user_id = %s" % (user, ))
        with self.assertRaises(AuthError) as cm:
            yield self.auth.get_user_from_macaroon(macaroon.serialize())
        self.assertEqual(401, cm.exception.code)
        self.assertIn("User mismatch", cm.exception.msg)

    @defer.inlineCallbacks
    def test_get_user_from_macaroon_missing_caveat(self):
        # TODO(danielwh): Remove this mock when we remove the
        # get_user_by_access_token fallback.
        self.store.get_user_by_access_token = Mock(
            return_value={"name": "@baldrick:matrix.org"})

        macaroon = pymacaroons.Macaroon(location=self.hs.config.server_name,
                                        identifier="key",
                                        key=self.hs.config.macaroon_secret_key)
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")

        with self.assertRaises(AuthError) as cm:
            yield self.auth.get_user_from_macaroon(macaroon.serialize())
        self.assertEqual(401, cm.exception.code)
        self.assertIn("No user caveat", cm.exception.msg)

    @defer.inlineCallbacks
    def test_get_user_from_macaroon_wrong_key(self):
        # TODO(danielwh): Remove this mock when we remove the
        # get_user_by_access_token fallback.
        self.store.get_user_by_access_token = Mock(
            return_value={"name": "@baldrick:matrix.org"})

        user = "******"
        macaroon = pymacaroons.Macaroon(
            location=self.hs.config.server_name,
            identifier="key",
            key=self.hs.config.macaroon_secret_key + "wrong")
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")
        macaroon.add_first_party_caveat("user_id = %s" % (user, ))

        with self.assertRaises(AuthError) as cm:
            yield self.auth.get_user_from_macaroon(macaroon.serialize())
        self.assertEqual(401, cm.exception.code)
        self.assertIn("Invalid macaroon", cm.exception.msg)

    @defer.inlineCallbacks
    def test_get_user_from_macaroon_unknown_caveat(self):
        # TODO(danielwh): Remove this mock when we remove the
        # get_user_by_access_token fallback.
        self.store.get_user_by_access_token = Mock(
            return_value={"name": "@baldrick:matrix.org"})

        user = "******"
        macaroon = pymacaroons.Macaroon(location=self.hs.config.server_name,
                                        identifier="key",
                                        key=self.hs.config.macaroon_secret_key)
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")
        macaroon.add_first_party_caveat("user_id = %s" % (user, ))
        macaroon.add_first_party_caveat("cunning > fox")

        with self.assertRaises(AuthError) as cm:
            yield self.auth.get_user_from_macaroon(macaroon.serialize())
        self.assertEqual(401, cm.exception.code)
        self.assertIn("Invalid macaroon", cm.exception.msg)

    @defer.inlineCallbacks
    def test_get_user_from_macaroon_expired(self):
        # TODO(danielwh): Remove this mock when we remove the
        # get_user_by_access_token fallback.
        self.store.get_user_by_access_token = Mock(
            return_value={"name": "@baldrick:matrix.org"})

        self.store.get_user_by_access_token = Mock(
            return_value={"name": "@baldrick:matrix.org"})

        user = "******"
        macaroon = pymacaroons.Macaroon(location=self.hs.config.server_name,
                                        identifier="key",
                                        key=self.hs.config.macaroon_secret_key)
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")
        macaroon.add_first_party_caveat("user_id = %s" % (user, ))
        macaroon.add_first_party_caveat("time < 1")  # ms

        self.hs.clock.now = 5000  # seconds
        self.hs.config.expire_access_token = True
        # yield self.auth.get_user_from_macaroon(macaroon.serialize())
        # TODO(daniel): Turn on the check that we validate expiration, when we
        # validate expiration (and remove the above line, which will start
        # throwing).
        with self.assertRaises(AuthError) as cm:
            yield self.auth.get_user_from_macaroon(macaroon.serialize())
        self.assertEqual(401, cm.exception.code)
        self.assertIn("Invalid macaroon", cm.exception.msg)
Example #25
0
class AuthTestCase(unittest.HomeserverTestCase):
    def prepare(self, reactor, clock, hs):
        self.store = Mock()

        hs.get_datastore = Mock(return_value=self.store)
        hs.get_auth_handler().store = self.store
        self.auth = Auth(hs)

        # AuthBlocking reads from the hs' config on initialization. We need to
        # modify its config instead of the hs'
        self.auth_blocking = self.auth._auth_blocking

        self.test_user = "******"
        self.test_token = b"_test_token_"

        # this is overridden for the appservice tests
        self.store.get_app_service_by_token = Mock(return_value=None)

        self.store.insert_client_ip = simple_async_mock(None)
        self.store.is_support_user = simple_async_mock(False)

    def test_get_user_by_req_user_valid_token(self):
        user_info = TokenLookupResult(
            user_id=self.test_user, token_id=5, device_id="device"
        )
        self.store.get_user_by_access_token = simple_async_mock(user_info)

        request = Mock(args={})
        request.args[b"access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        requester = self.get_success(self.auth.get_user_by_req(request))
        self.assertEquals(requester.user.to_string(), self.test_user)

    def test_get_user_by_req_user_bad_token(self):
        self.store.get_user_by_access_token = simple_async_mock(None)

        request = Mock(args={})
        request.args[b"access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        f = self.get_failure(
            self.auth.get_user_by_req(request), InvalidClientTokenError
        ).value
        self.assertEqual(f.code, 401)
        self.assertEqual(f.errcode, "M_UNKNOWN_TOKEN")

    def test_get_user_by_req_user_missing_token(self):
        user_info = TokenLookupResult(user_id=self.test_user, token_id=5)
        self.store.get_user_by_access_token = simple_async_mock(user_info)

        request = Mock(args={})
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        f = self.get_failure(
            self.auth.get_user_by_req(request), MissingClientTokenError
        ).value
        self.assertEqual(f.code, 401)
        self.assertEqual(f.errcode, "M_MISSING_TOKEN")

    def test_get_user_by_req_appservice_valid_token(self):
        app_service = Mock(
            token="foobar", url="a_url", sender=self.test_user, ip_range_whitelist=None
        )
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_access_token = simple_async_mock(None)

        request = Mock(args={})
        request.getClientIP.return_value = "127.0.0.1"
        request.args[b"access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        requester = self.get_success(self.auth.get_user_by_req(request))
        self.assertEquals(requester.user.to_string(), self.test_user)

    def test_get_user_by_req_appservice_valid_token_good_ip(self):
        from netaddr import IPSet

        app_service = Mock(
            token="foobar",
            url="a_url",
            sender=self.test_user,
            ip_range_whitelist=IPSet(["192.168/16"]),
        )
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_access_token = simple_async_mock(None)

        request = Mock(args={})
        request.getClientIP.return_value = "192.168.10.10"
        request.args[b"access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        requester = self.get_success(self.auth.get_user_by_req(request))
        self.assertEquals(requester.user.to_string(), self.test_user)

    def test_get_user_by_req_appservice_valid_token_bad_ip(self):
        from netaddr import IPSet

        app_service = Mock(
            token="foobar",
            url="a_url",
            sender=self.test_user,
            ip_range_whitelist=IPSet(["192.168/16"]),
        )
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_access_token = simple_async_mock(None)

        request = Mock(args={})
        request.getClientIP.return_value = "131.111.8.42"
        request.args[b"access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        f = self.get_failure(
            self.auth.get_user_by_req(request), InvalidClientTokenError
        ).value
        self.assertEqual(f.code, 401)
        self.assertEqual(f.errcode, "M_UNKNOWN_TOKEN")

    def test_get_user_by_req_appservice_bad_token(self):
        self.store.get_app_service_by_token = Mock(return_value=None)
        self.store.get_user_by_access_token = simple_async_mock(None)

        request = Mock(args={})
        request.args[b"access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        f = self.get_failure(
            self.auth.get_user_by_req(request), InvalidClientTokenError
        ).value
        self.assertEqual(f.code, 401)
        self.assertEqual(f.errcode, "M_UNKNOWN_TOKEN")

    def test_get_user_by_req_appservice_missing_token(self):
        app_service = Mock(token="foobar", url="a_url", sender=self.test_user)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_access_token = simple_async_mock(None)

        request = Mock(args={})
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        f = self.get_failure(
            self.auth.get_user_by_req(request), MissingClientTokenError
        ).value
        self.assertEqual(f.code, 401)
        self.assertEqual(f.errcode, "M_MISSING_TOKEN")

    def test_get_user_by_req_appservice_valid_token_valid_user_id(self):
        masquerading_user_id = b"@doppelganger:matrix.org"
        app_service = Mock(
            token="foobar", url="a_url", sender=self.test_user, ip_range_whitelist=None
        )
        app_service.is_interested_in_user = Mock(return_value=True)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        # This just needs to return a truth-y value.
        self.store.get_user_by_id = simple_async_mock({"is_guest": False})
        self.store.get_user_by_access_token = simple_async_mock(None)

        request = Mock(args={})
        request.getClientIP.return_value = "127.0.0.1"
        request.args[b"access_token"] = [self.test_token]
        request.args[b"user_id"] = [masquerading_user_id]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        requester = self.get_success(self.auth.get_user_by_req(request))
        self.assertEquals(
            requester.user.to_string(), masquerading_user_id.decode("utf8")
        )

    def test_get_user_by_req_appservice_valid_token_bad_user_id(self):
        masquerading_user_id = b"@doppelganger:matrix.org"
        app_service = Mock(
            token="foobar", url="a_url", sender=self.test_user, ip_range_whitelist=None
        )
        app_service.is_interested_in_user = Mock(return_value=False)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_access_token = simple_async_mock(None)

        request = Mock(args={})
        request.getClientIP.return_value = "127.0.0.1"
        request.args[b"access_token"] = [self.test_token]
        request.args[b"user_id"] = [masquerading_user_id]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        self.get_failure(self.auth.get_user_by_req(request), AuthError)

    def test_get_user_from_macaroon(self):
        self.store.get_user_by_access_token = simple_async_mock(
            TokenLookupResult(user_id="@baldrick:matrix.org", device_id="device")
        )

        user_id = "@baldrick:matrix.org"
        macaroon = pymacaroons.Macaroon(
            location=self.hs.config.server_name,
            identifier="key",
            key=self.hs.config.macaroon_secret_key,
        )
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")
        macaroon.add_first_party_caveat("user_id = %s" % (user_id,))
        user_info = self.get_success(
            self.auth.get_user_by_access_token(macaroon.serialize())
        )
        self.assertEqual(user_id, user_info.user_id)

        # TODO: device_id should come from the macaroon, but currently comes
        # from the db.
        self.assertEqual(user_info.device_id, "device")

    def test_get_guest_user_from_macaroon(self):
        self.store.get_user_by_id = simple_async_mock({"is_guest": True})
        self.store.get_user_by_access_token = simple_async_mock(None)

        user_id = "@baldrick:matrix.org"
        macaroon = pymacaroons.Macaroon(
            location=self.hs.config.server_name,
            identifier="key",
            key=self.hs.config.macaroon_secret_key,
        )
        macaroon.add_first_party_caveat("gen = 1")
        macaroon.add_first_party_caveat("type = access")
        macaroon.add_first_party_caveat("user_id = %s" % (user_id,))
        macaroon.add_first_party_caveat("guest = true")
        serialized = macaroon.serialize()

        user_info = self.get_success(self.auth.get_user_by_access_token(serialized))
        self.assertEqual(user_id, user_info.user_id)
        self.assertTrue(user_info.is_guest)
        self.store.get_user_by_id.assert_called_with(user_id)

    def test_cannot_use_regular_token_as_guest(self):
        USER_ID = "@percy:matrix.org"
        self.store.add_access_token_to_user = simple_async_mock(None)
        self.store.get_device = simple_async_mock(None)

        token = self.get_success(
            self.hs.get_auth_handler().get_access_token_for_user_id(
                USER_ID, "DEVICE", valid_until_ms=None
            )
        )
        self.store.add_access_token_to_user.assert_called_with(
            user_id=USER_ID,
            token=token,
            device_id="DEVICE",
            valid_until_ms=None,
            puppets_user_id=None,
        )

        async def get_user(tok):
            if token != tok:
                return None
            return TokenLookupResult(
                user_id=USER_ID,
                is_guest=False,
                token_id=1234,
                device_id="DEVICE",
            )

        self.store.get_user_by_access_token = get_user
        self.store.get_user_by_id = simple_async_mock({"is_guest": False})

        # check the token works
        request = Mock(args={})
        request.args[b"access_token"] = [token.encode("ascii")]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()
        requester = self.get_success(
            self.auth.get_user_by_req(request, allow_guest=True)
        )
        self.assertEqual(UserID.from_string(USER_ID), requester.user)
        self.assertFalse(requester.is_guest)

        # add an is_guest caveat
        mac = pymacaroons.Macaroon.deserialize(token)
        mac.add_first_party_caveat("guest = true")
        guest_tok = mac.serialize()

        # the token should *not* work now
        request = Mock(args={})
        request.args[b"access_token"] = [guest_tok.encode("ascii")]
        request.requestHeaders.getRawHeaders = mock_getRawHeaders()

        cm = self.get_failure(
            self.auth.get_user_by_req(request, allow_guest=True),
            InvalidClientCredentialsError,
        )

        self.assertEqual(401, cm.value.code)
        self.assertEqual("Guest access token used for regular user", cm.value.msg)

        self.store.get_user_by_id.assert_called_with(USER_ID)

    def test_blocking_mau(self):
        self.auth_blocking._limit_usage_by_mau = False
        self.auth_blocking._max_mau_value = 50
        lots_of_users = 100
        small_number_of_users = 1

        # Ensure no error thrown
        self.get_success(self.auth.check_auth_blocking())

        self.auth_blocking._limit_usage_by_mau = True

        self.store.get_monthly_active_count = simple_async_mock(lots_of_users)

        e = self.get_failure(self.auth.check_auth_blocking(), ResourceLimitError)
        self.assertEquals(e.value.admin_contact, self.hs.config.admin_contact)
        self.assertEquals(e.value.errcode, Codes.RESOURCE_LIMIT_EXCEEDED)
        self.assertEquals(e.value.code, 403)

        # Ensure does not throw an error
        self.store.get_monthly_active_count = simple_async_mock(small_number_of_users)
        self.get_success(self.auth.check_auth_blocking())

    def test_blocking_mau__depending_on_user_type(self):
        self.auth_blocking._max_mau_value = 50
        self.auth_blocking._limit_usage_by_mau = True

        self.store.get_monthly_active_count = simple_async_mock(100)
        # Support users allowed
        self.get_success(self.auth.check_auth_blocking(user_type=UserTypes.SUPPORT))
        self.store.get_monthly_active_count = simple_async_mock(100)
        # Bots not allowed
        self.get_failure(
            self.auth.check_auth_blocking(user_type=UserTypes.BOT), ResourceLimitError
        )
        self.store.get_monthly_active_count = simple_async_mock(100)
        # Real users not allowed
        self.get_failure(self.auth.check_auth_blocking(), ResourceLimitError)

    def test_reserved_threepid(self):
        self.auth_blocking._limit_usage_by_mau = True
        self.auth_blocking._max_mau_value = 1
        self.store.get_monthly_active_count = simple_async_mock(2)
        threepid = {"medium": "email", "address": "*****@*****.**"}
        unknown_threepid = {"medium": "email", "address": "*****@*****.**"}
        self.auth_blocking._mau_limits_reserved_threepids = [threepid]

        self.get_failure(self.auth.check_auth_blocking(), ResourceLimitError)

        self.get_failure(
            self.auth.check_auth_blocking(threepid=unknown_threepid), ResourceLimitError
        )

        self.get_success(self.auth.check_auth_blocking(threepid=threepid))

    def test_hs_disabled(self):
        self.auth_blocking._hs_disabled = True
        self.auth_blocking._hs_disabled_message = "Reason for being disabled"
        e = self.get_failure(self.auth.check_auth_blocking(), ResourceLimitError)
        self.assertEquals(e.value.admin_contact, self.hs.config.admin_contact)
        self.assertEquals(e.value.errcode, Codes.RESOURCE_LIMIT_EXCEEDED)
        self.assertEquals(e.value.code, 403)

    def test_hs_disabled_no_server_notices_user(self):
        """Check that 'hs_disabled_message' works correctly when there is no
        server_notices user.
        """
        # this should be the default, but we had a bug where the test was doing the wrong
        # thing, so let's make it explicit
        self.auth_blocking._server_notices_mxid = None

        self.auth_blocking._hs_disabled = True
        self.auth_blocking._hs_disabled_message = "Reason for being disabled"
        e = self.get_failure(self.auth.check_auth_blocking(), ResourceLimitError)
        self.assertEquals(e.value.admin_contact, self.hs.config.admin_contact)
        self.assertEquals(e.value.errcode, Codes.RESOURCE_LIMIT_EXCEEDED)
        self.assertEquals(e.value.code, 403)

    def test_server_notices_mxid_special_cased(self):
        self.auth_blocking._hs_disabled = True
        user = "******"
        self.auth_blocking._server_notices_mxid = user
        self.auth_blocking._hs_disabled_message = "Reason for being disabled"
        self.get_success(self.auth.check_auth_blocking(user))
Example #26
0
 def get_auth(self) -> Auth:
     return Auth(self)
Example #27
0
 def build_auth(self):
     return Auth(self)
Example #28
0
class AuthTestCase(unittest.TestCase):

    def setUp(self):
        self.state_handler = Mock()
        self.store = Mock()

        self.hs = Mock()
        self.hs.get_datastore = Mock(return_value=self.store)
        self.hs.get_state_handler = Mock(return_value=self.state_handler)
        self.auth = Auth(self.hs)

        self.test_user = "******"
        self.test_token = "_test_token_"

    @defer.inlineCallbacks
    def test_get_user_by_req_user_valid_token(self):
        self.store.get_app_service_by_token = Mock(return_value=None)
        user_info = {
            "name": self.test_user,
            "device_id": "nothing",
            "token_id": "ditto",
            "admin": False
        }
        self.store.get_user_by_token = Mock(return_value=user_info)

        request = Mock(args={})
        request.args["access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = Mock(return_value=[""])
        (user, info) = yield self.auth.get_user_by_req(request)
        self.assertEquals(user.to_string(), self.test_user)

    def test_get_user_by_req_user_bad_token(self):
        self.store.get_app_service_by_token = Mock(return_value=None)
        self.store.get_user_by_token = Mock(return_value=None)

        request = Mock(args={})
        request.args["access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = Mock(return_value=[""])
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    def test_get_user_by_req_user_missing_token(self):
        self.store.get_app_service_by_token = Mock(return_value=None)
        user_info = {
            "name": self.test_user,
            "device_id": "nothing",
            "token_id": "ditto",
            "admin": False
        }
        self.store.get_user_by_token = Mock(return_value=user_info)

        request = Mock(args={})
        request.requestHeaders.getRawHeaders = Mock(return_value=[""])
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    @defer.inlineCallbacks
    def test_get_user_by_req_appservice_valid_token(self):
        app_service = Mock(token="foobar", url="a_url", sender=self.test_user)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_token = Mock(return_value=None)

        request = Mock(args={})
        request.args["access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = Mock(return_value=[""])
        (user, info) = yield self.auth.get_user_by_req(request)
        self.assertEquals(user.to_string(), self.test_user)

    def test_get_user_by_req_appservice_bad_token(self):
        self.store.get_app_service_by_token = Mock(return_value=None)
        self.store.get_user_by_token = Mock(return_value=None)

        request = Mock(args={})
        request.args["access_token"] = [self.test_token]
        request.requestHeaders.getRawHeaders = Mock(return_value=[""])
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    def test_get_user_by_req_appservice_missing_token(self):
        app_service = Mock(token="foobar", url="a_url", sender=self.test_user)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_token = Mock(return_value=None)

        request = Mock(args={})
        request.requestHeaders.getRawHeaders = Mock(return_value=[""])
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)

    @defer.inlineCallbacks
    def test_get_user_by_req_appservice_valid_token_valid_user_id(self):
        masquerading_user_id = "@doppelganger:matrix.org"
        app_service = Mock(token="foobar", url="a_url", sender=self.test_user)
        app_service.is_interested_in_user = Mock(return_value=True)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_token = Mock(return_value=None)

        request = Mock(args={})
        request.args["access_token"] = [self.test_token]
        request.args["user_id"] = [masquerading_user_id]
        request.requestHeaders.getRawHeaders = Mock(return_value=[""])
        (user, info) = yield self.auth.get_user_by_req(request)
        self.assertEquals(user.to_string(), masquerading_user_id)

    def test_get_user_by_req_appservice_valid_token_bad_user_id(self):
        masquerading_user_id = "@doppelganger:matrix.org"
        app_service = Mock(token="foobar", url="a_url", sender=self.test_user)
        app_service.is_interested_in_user = Mock(return_value=False)
        self.store.get_app_service_by_token = Mock(return_value=app_service)
        self.store.get_user_by_token = Mock(return_value=None)

        request = Mock(args={})
        request.args["access_token"] = [self.test_token]
        request.args["user_id"] = [masquerading_user_id]
        request.requestHeaders.getRawHeaders = Mock(return_value=[""])
        d = self.auth.get_user_by_req(request)
        self.failureResultOf(d, AuthError)