Example #1
0
    def make_homeserver(self, reactor, clock):
        self.hs = self.setup_test_homeserver()

        self.service = ApplicationService(
            id="unique_identifier",
            token="some_token",
            hostname="example.com",
            sender="@asbot:example.com",
            namespaces={
                ApplicationService.NS_USERS: [{
                    "regex": r"@as_user.*",
                    "exclusive": False
                }],
                ApplicationService.NS_ROOMS: [],
                ApplicationService.NS_ALIASES: [],
            },
        )
        self.another_service = ApplicationService(
            id="another__identifier",
            token="another_token",
            hostname="example.com",
            sender="@as2bot:example.com",
            namespaces={
                ApplicationService.NS_USERS: [{
                    "regex": r"@as2_user.*",
                    "exclusive": False
                }],
                ApplicationService.NS_ROOMS: [],
                ApplicationService.NS_ALIASES: [],
            },
        )

        self.hs.get_datastore().services_cache.append(self.service)
        self.hs.get_datastore().services_cache.append(self.another_service)
        return self.hs
Example #2
0
    async def _do_appservice_login(self, login_submission: JsonDict,
                                   appservice: ApplicationService):
        identifier = login_submission.get("identifier")
        logger.info("Got appservice login request with identifier: %r",
                    identifier)

        if not isinstance(identifier, dict):
            raise SynapseError(400, "Invalid identifier in login submission",
                               Codes.INVALID_PARAM)

        # this login flow only supports identifiers of type "m.id.user".
        if identifier.get("type") != "m.id.user":
            raise SynapseError(400, "Unknown login identifier type",
                               Codes.INVALID_PARAM)

        user = identifier.get("user")
        if not isinstance(user, str):
            raise SynapseError(400, "Invalid user in identifier",
                               Codes.INVALID_PARAM)

        if user.startswith("@"):
            qualified_user_id = user
        else:
            qualified_user_id = UserID(user, self.hs.hostname).to_string()

        if not appservice.is_interested_in_user(qualified_user_id):
            raise LoginError(403,
                             "Invalid access_token",
                             errcode=Codes.FORBIDDEN)

        return await self._complete_login(
            qualified_user_id,
            login_submission,
            ratelimit=appservice.is_rate_limited())
Example #3
0
 def setUp(self):
     self.service = ApplicationService(url="some_url",
                                       token="some_token",
                                       namespaces={
                                           ApplicationService.NS_USERS: [],
                                           ApplicationService.NS_ROOMS: [],
                                           ApplicationService.NS_ALIASES:
                                           []
                                       })
     self.event = Mock(type="m.something",
                       room_id="!foo:bar",
                       sender="@someone:somewhere")
Example #4
0
    def setUp(self):
        self.service = ApplicationService(
            id="unique_identifier",
            sender="@as:test",
            url="some_url",
            token="some_token",
            hostname="matrix.org",  # only used by get_groups_for_user
        )
        self.event = Mock(type="m.something",
                          room_id="!foo:bar",
                          sender="@someone:somewhere")

        self.store = Mock()
Example #5
0
    def test_allowed_appservice_ratelimited_via_can_requester_do_action(self):
        appservice = ApplicationService(
            None,
            "example.com",
            id="foo",
            rate_limited=True,
            sender="@as:example.com",
        )
        as_requester = create_requester("@user:example.com",
                                        app_service=appservice)

        limiter = Ratelimiter(clock=None, rate_hz=0.1, burst_count=1)
        allowed, time_allowed = limiter.can_requester_do_action(as_requester,
                                                                _time_now_s=0)
        self.assertTrue(allowed)
        self.assertEquals(10.0, time_allowed)

        allowed, time_allowed = limiter.can_requester_do_action(as_requester,
                                                                _time_now_s=5)
        self.assertFalse(allowed)
        self.assertEquals(10.0, time_allowed)

        allowed, time_allowed = limiter.can_requester_do_action(as_requester,
                                                                _time_now_s=10)
        self.assertTrue(allowed)
        self.assertEquals(20.0, time_allowed)
Example #6
0
    def test_GET_whoami_appservices(self):
        user_id = "@as:test"
        as_token = "i_am_an_app_service"

        appservice = ApplicationService(
            as_token,
            self.hs.config.server.server_name,
            id="1234",
            namespaces={"users": [{
                "regex": user_id,
                "exclusive": True
            }]},
            sender=user_id,
        )
        self.hs.get_datastore().services_cache.append(appservice)

        whoami = self._whoami(as_token)
        self.assertEqual(
            whoami,
            {
                "user_id": user_id,
                # Unstable until MSC3069 enters spec
                "org.matrix.msc3069.is_guest": False,
            },
        )
        self.assertFalse(hasattr(whoami, "device_id"))
Example #7
0
    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)
Example #8
0
    def test_POST_appservice_registration_valid(self):
        user_id = "@as_user_kermit:test"
        as_token = "i_am_an_app_service"

        appservice = ApplicationService(
            as_token,
            self.hs.config.server.server_name,
            id="1234",
            namespaces={
                "users": [{
                    "regex": r"@as_user.*",
                    "exclusive": True
                }]
            },
            sender="@as:test",
        )

        self.hs.get_datastore().services_cache.append(appservice)
        request_data = json.dumps({
            "username": "******",
            "type": APP_SERVICE_REGISTRATION_TYPE
        })

        channel = self.make_request(
            b"POST", self.url + b"?access_token=i_am_an_app_service",
            request_data)

        self.assertEquals(channel.result["code"], b"200", channel.result)
        det_data = {"user_id": user_id, "home_server": self.hs.hostname}
        self.assertDictContainsSubset(det_data, channel.json_body)
Example #9
0
    def setUp(self):
        self.service = ApplicationService(
            id="unique_identifier",
            url="some_url",
            token="some_token",
            hostname="matrix.org",  # only used by get_groups_for_user
            namespaces={
                ApplicationService.NS_USERS: [],
                ApplicationService.NS_ROOMS: [],
                ApplicationService.NS_ALIASES: []
            })
        self.event = Mock(type="m.something",
                          room_id="!foo:bar",
                          sender="@someone:somewhere")

        self.store = Mock()
Example #10
0
    async def validate_appservice_can_control_user_id(
            self, app_service: ApplicationService, user_id: str):
        """Validates that the app service is allowed to control
        the given user.

        Args:
            app_service: The app service that controls the user
            user_id: The author MXID that the app service is controlling

        Raises:
            AuthError: If the application service is not allowed to control the user
                (user namespace regex does not match, wrong homeserver, etc)
                or if the user has not been registered yet.
        """

        # It's ok if the app service is trying to use the sender from their registration
        if app_service.sender == user_id:
            pass
        # Check to make sure the app service is allowed to control the user
        elif not app_service.is_interested_in_user(user_id):
            raise AuthError(
                403,
                "Application service cannot masquerade as this user (%s)." %
                user_id,
            )
        # Check to make sure the user is already registered on the homeserver
        elif not (await self.store.get_user_by_id(user_id)):
            raise AuthError(
                403, "Application service has not registered this user (%s)" %
                user_id)
Example #11
0
    def test_allowed_appservice_via_can_requester_do_action(self):
        appservice = ApplicationService(
            None,
            "example.com",
            id="foo",
            rate_limited=False,
            sender="@as:example.com",
        )
        as_requester = create_requester("@user:example.com", app_service=appservice)

        limiter = Ratelimiter(
            store=self.hs.get_datastore(), clock=None, rate_hz=0.1, burst_count=1
        )
        allowed, time_allowed = self.get_success_or_raise(
            limiter.can_do_action(as_requester, _time_now_s=0)
        )
        self.assertTrue(allowed)
        self.assertEquals(-1, time_allowed)

        allowed, time_allowed = self.get_success_or_raise(
            limiter.can_do_action(as_requester, _time_now_s=5)
        )
        self.assertTrue(allowed)
        self.assertEquals(-1, time_allowed)

        allowed, time_allowed = self.get_success_or_raise(
            limiter.can_do_action(as_requester, _time_now_s=10)
        )
        self.assertTrue(allowed)
        self.assertEquals(-1, time_allowed)
Example #12
0
    def test_update_and_retrieval_of_service(self):
        url = "https://matrix.org/appservices/foobar"
        hs_token = "hstok"
        user_regex = ["@foobar_.*:matrix.org"]
        alias_regex = ["#foobar_.*:matrix.org"]
        room_regex = []
        service = ApplicationService(url=url,
                                     hs_token=hs_token,
                                     token=self.as_token,
                                     namespaces={
                                         ApplicationService.NS_USERS:
                                         user_regex,
                                         ApplicationService.NS_ALIASES:
                                         alias_regex,
                                         ApplicationService.NS_ROOMS:
                                         room_regex
                                     })
        yield self.store.update_app_service(service)

        stored_service = yield self.store.get_app_service_by_token(
            self.as_token)
        self.assertEquals(stored_service.token, self.as_token)
        self.assertEquals(stored_service.url, url)
        self.assertEquals(
            stored_service.namespaces[ApplicationService.NS_ALIASES],
            alias_regex)
        self.assertEquals(
            stored_service.namespaces[ApplicationService.NS_ROOMS], room_regex)
        self.assertEquals(
            stored_service.namespaces[ApplicationService.NS_USERS], user_regex)
Example #13
0
    def _register_application_service(
        self,
        namespaces: Optional[Dict[str, Iterable[Dict]]] = None,
    ) -> ApplicationService:
        """
        Register a new application service, with the given namespaces of interest.

        Args:
            namespaces: A dictionary containing any user, room or alias namespaces that
                the application service is interested in.

        Returns:
            The registered application service.
        """
        # Create an application service
        appservice = ApplicationService(
            token=random_string(10),
            id=random_string(10),
            sender="@as:example.com",
            rate_limited=False,
            namespaces=namespaces,
            supports_ephemeral=True,
        )

        # Register the application service
        self._services.append(appservice)

        return appservice
Example #14
0
    def make_homeserver(self, reactor: MemoryReactor,
                        clock: Clock) -> HomeServer:
        config = self.default_config()
        config["update_user_directory"] = True

        self.appservice = ApplicationService(
            token="i_am_an_app_service",
            id="1234",
            namespaces={
                "users": [{
                    "regex": r"@as_user.*",
                    "exclusive": True
                }]
            },
            # Note: this user does not match the regex above, so that tests
            # can distinguish the sender from the AS user.
            sender="@as_main:test",
        )

        mock_load_appservices = Mock(return_value=[self.appservice])
        with patch(
                "synapse.storage.databases.main.appservice.load_appservices",
                mock_load_appservices,
        ):
            hs = self.setup_test_homeserver(config=config)
        return hs
Example #15
0
    def setUp(self):
        self.service = ApplicationService(
            id="unique_identifier",
            sender="@as:test",
            url="some_url",
            token="some_token",
        )
        self.event = Mock(
            event_id="$abc:xyz",
            type="m.something",
            room_id="!foo:bar",
            sender="@someone:somewhere",
        )

        self.store = Mock()
        self.store.get_aliases_for_room = simple_async_mock([])
        self.store.get_users_in_room = simple_async_mock([])
Example #16
0
 def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer):
     self.api = hs.get_application_service_api()
     self.service = ApplicationService(
         id="unique_identifier",
         sender="@as:test",
         url=URL,
         token="unused",
         hs_token=TOKEN,
     )
Example #17
0
    def _parse_services_dict(self, results):
        # SQL results in the form:
        # [
        #   {
        #     'regex': "something",
        #     'url': "something",
        #     'namespace': enum,
        #     'as_id': 0,
        #     'token': "something",
        #     'hs_token': "otherthing",
        #     'id': 0
        #   }
        # ]
        services = {}
        for res in results:
            as_token = res["token"]
            if as_token is None:
                continue
            if as_token not in services:
                # add the service
                services[as_token] = {
                    "id": res["id"],
                    "url": res["url"],
                    "token": as_token,
                    "hs_token": res["hs_token"],
                    "sender": res["sender"],
                    "namespaces": {
                        ApplicationService.NS_USERS: [],
                        ApplicationService.NS_ALIASES: [],
                        ApplicationService.NS_ROOMS: []
                    }
                }
            # add the namespace regex if one exists
            ns_int = res["namespace"]
            if ns_int is None:
                continue
            try:
                services[as_token]["namespaces"][
                    ApplicationService.NS_LIST[ns_int]].append(
                    json.loads(res["regex"])
                )
            except IndexError:
                logger.error("Bad namespace enum '%s'. %s", ns_int, res)
            except JSONDecodeError:
                logger.error("Bad regex object '%s'", res["regex"])

        service_list = []
        for service in services.values():
            service_list.append(ApplicationService(
                token=service["token"],
                url=service["url"],
                namespaces=service["namespaces"],
                hs_token=service["hs_token"],
                sender=service["sender"],
                id=service["id"]
            ))
        return service_list
Example #18
0
 async def delete_appservice_association(self, service: ApplicationService,
                                         room_alias: RoomAlias) -> None:
     if not service.is_interested_in_alias(room_alias.to_string()):
         raise SynapseError(
             400,
             "This application service has not reserved this kind of alias",
             errcode=Codes.EXCLUSIVE,
         )
     await self._delete_association(room_alias)
Example #19
0
    def _populate_cache(self):
        """Populates the ApplicationServiceCache from the database."""
        sql = ("SELECT * FROM application_services LEFT JOIN "
               "application_services_regex ON application_services.id = "
               "application_services_regex.as_id")
        # SQL results in the form:
        # [
        #   {
        #     'regex': "something",
        #     'url': "something",
        #     'namespace': enum,
        #     'as_id': 0,
        #     'token': "something",
        #     'hs_token': "otherthing",
        #     'id': 0
        #   }
        # ]
        services = {}
        results = yield self._execute_and_decode(sql)
        for res in results:
            as_token = res["token"]
            if as_token not in services:
                # add the service
                services[as_token] = {
                    "url": res["url"],
                    "token": as_token,
                    "hs_token": res["hs_token"],
                    "sender": res["sender"],
                    "namespaces": {
                        ApplicationService.NS_USERS: [],
                        ApplicationService.NS_ALIASES: [],
                        ApplicationService.NS_ROOMS: []
                    }
                }
            # add the namespace regex if one exists
            ns_int = res["namespace"]
            if ns_int is None:
                continue
            try:
                services[as_token]["namespaces"][
                    ApplicationService.NS_LIST[ns_int]].append(res["regex"])
            except IndexError:
                logger.error("Bad namespace enum '%s'. %s", ns_int, res)

        # TODO get last successful txn id f.e. service
        for service in services.values():
            logger.info("Found application service: %s", service)
            self.cache.services.append(
                ApplicationService(token=service["token"],
                                   url=service["url"],
                                   namespaces=service["namespaces"],
                                   hs_token=service["hs_token"],
                                   sender=service["sender"]))
Example #20
0
 def setUp(self):
     self.service = ApplicationService(
         url="some_url",
         token="some_token",
         namespaces={
             ApplicationService.NS_USERS: [],
             ApplicationService.NS_ROOMS: [],
             ApplicationService.NS_ALIASES: []
         }
     )
     self.event = Mock(
         type="m.something", room_id="!foo:bar", sender="@someone:somewhere"
     )
Example #21
0
    def make_homeserver(self, reactor: MemoryReactor, clock: Clock) -> HomeServer:
        self.appservice = ApplicationService(
            token="i_am_an_app_service",
            id="1234",
            namespaces={"users": [{"regex": r"@as_user.*", "exclusive": True}]},
            sender="@as:test",
        )

        mock_load_appservices = Mock(return_value=[self.appservice])
        with patch(
            "synapse.storage.databases.main.appservice.load_appservices",
            mock_load_appservices,
        ):
            hs = super().make_homeserver(reactor, clock)
        return hs
Example #22
0
    def setUp(self):
        self.service = ApplicationService(
            id="unique_identifier",
            url="some_url",
            token="some_token",
            hostname="matrix.org",  # only used by get_groups_for_user
            namespaces={
                ApplicationService.NS_USERS: [],
                ApplicationService.NS_ROOMS: [],
                ApplicationService.NS_ALIASES: []
            }
        )
        self.event = Mock(
            type="m.something", room_id="!foo:bar", sender="@someone:somewhere"
        )

        self.store = Mock()
Example #23
0
    async def _do_appservice_login(self, login_submission: JsonDict,
                                   appservice: ApplicationService):
        logger.info(
            "Got appservice login request with identifier: %r",
            login_submission.get("identifier"),
        )

        identifier = convert_client_dict_legacy_fields_to_identifier(
            login_submission)
        qualified_user_id = self._get_qualified_user_id(identifier)

        if not appservice.is_interested_in_user(qualified_user_id):
            raise LoginError(403,
                             "Invalid access_token",
                             errcode=Codes.FORBIDDEN)

        return await self._complete_login(qualified_user_id, login_submission)
Example #24
0
    def _load_appservice(self, as_info):
        required_string_fields = [
            "url", "as_token", "hs_token", "sender_localpart"
        ]
        for field in required_string_fields:
            if not isinstance(as_info.get(field), basestring):
                raise KeyError("Required string field: '%s'", field)

        localpart = as_info["sender_localpart"]
        if urllib.quote(localpart) != localpart:
            raise ValueError(
                "sender_localpart needs characters which are not URL encoded."
            )
        user = UserID(localpart, self.hostname)
        user_id = user.to_string()

        # namespace checks
        if not isinstance(as_info.get("namespaces"), dict):
            raise KeyError("Requires 'namespaces' object.")
        for ns in ApplicationService.NS_LIST:
            # specific namespaces are optional
            if ns in as_info["namespaces"]:
                # expect a list of dicts with exclusive and regex keys
                for regex_obj in as_info["namespaces"][ns]:
                    if not isinstance(regex_obj, dict):
                        raise ValueError(
                            "Expected namespace entry in %s to be an object,"
                            " but got %s", ns, regex_obj
                        )
                    if not isinstance(regex_obj.get("regex"), basestring):
                        raise ValueError(
                            "Missing/bad type 'regex' key in %s", regex_obj
                        )
                    if not isinstance(regex_obj.get("exclusive"), bool):
                        raise ValueError(
                            "Missing/bad type 'exclusive' key in %s", regex_obj
                        )
        return ApplicationService(
            token=as_info["as_token"],
            url=as_info["url"],
            namespaces=as_info["namespaces"],
            hs_token=as_info["hs_token"],
            sender=user_id,
            id=as_info["as_token"]  # the token is the only unique thing here
        )
Example #25
0
    def test_POST_appservice_registration_no_type(self) -> None:
        as_token = "i_am_an_app_service"

        appservice = ApplicationService(
            as_token,
            id="1234",
            namespaces={"users": [{"regex": r"@as_user.*", "exclusive": True}]},
            sender="@as:test",
        )

        self.hs.get_datastores().main.services_cache.append(appservice)
        request_data = json.dumps({"username": "******"})

        channel = self.make_request(
            b"POST", self.url + b"?access_token=i_am_an_app_service", request_data
        )

        self.assertEqual(channel.result["code"], b"400", channel.result)
Example #26
0
    def test_GET_whoami_appservices(self):
        user_id = "@as:test"
        as_token = "i_am_an_app_service"

        appservice = ApplicationService(
            as_token,
            self.hs.config.server_name,
            id="1234",
            namespaces={"users": [{
                "regex": user_id,
                "exclusive": True
            }]},
            sender=user_id,
        )
        self.hs.get_datastore().services_cache.append(appservice)

        whoami = self.whoami(as_token)
        self.assertEqual(whoami, {"user_id": user_id})
        self.assertFalse(hasattr(whoami, "device_id"))
Example #27
0
    async def _is_appservice_interested_in_device_lists_of_user(
        self,
        appservice: ApplicationService,
        user_id: str,
    ) -> bool:
        """
        Returns whether a given application service is interested in the device list
        updates of a given user.

        The application service is interested in the user's device list updates if any
        of the following are true:
            * The user is the appservice's sender localpart user.
            * The user is in the appservice's user namespace.
            * At least one member of one room that the user is a part of is in the
              appservice's user namespace.
            * The appservice is explicitly (via room ID or alias) interested in at
              least one room that the user is in.

        Args:
            appservice: The application service to gauge interest of.
            user_id: The ID of the user whose device list interest is in question.

        Returns:
            True if the application service is interested in the user's device lists, False
            otherwise.
        """
        # This method checks against both the sender localpart user as well as if the
        # user is in the appservice's user namespace.
        if appservice.is_interested_in_user(user_id):
            return True

        # Determine whether any of the rooms the user is in justifies sending this
        # device list update to the application service.
        room_ids = await self.store.get_rooms_for_user(user_id)
        for room_id in room_ids:
            # This method covers checking room members for appservice interest as well as
            # room ID and alias checks.
            if await appservice.is_interested_in_room(room_id, self.store):
                return True

        return False
Example #28
0
    def test_as_ignores_mau(self):
        """Test that application services can still create users when the MAU
        limit has been reached. This only works when application service
        user ip tracking is disabled.
        """

        # Create and sync so that the MAU counts get updated
        token1 = self.create_user("kermit1")
        self.do_sync_for_user(token1)
        token2 = self.create_user("kermit2")
        self.do_sync_for_user(token2)

        # check we're testing what we think we are: there should be two active users
        self.assertEqual(
            self.get_success(self.store.get_monthly_active_count()), 2)

        # We've created and activated two users, we shouldn't be able to
        # register new users
        with self.assertRaises(SynapseError) as cm:
            self.create_user("kermit3")

        e = cm.exception
        self.assertEqual(e.code, 403)
        self.assertEqual(e.errcode, Codes.RESOURCE_LIMIT_EXCEEDED)

        # Cheekily add an application service that we use to register a new user
        # with.
        as_token = "foobartoken"
        self.store.services_cache.append(
            ApplicationService(
                token=as_token,
                hostname=self.hs.hostname,
                id="SomeASID",
                sender="@as_sender:test",
                namespaces={"users": [{
                    "regex": "@as_*",
                    "exclusive": True
                }]},
            ))

        self.create_user("as_kermit4", token=as_token, appservice=True)
Example #29
0
    def prepare(self, reactor: MemoryReactor, clock: Clock,
                hs: HomeServer) -> None:
        # Mock the ApplicationServiceScheduler's _TransactionController's send method so that
        # we can track what's going out
        self.send_mock = simple_async_mock()
        hs.get_application_service_handler(
        ).scheduler.txn_ctrl.send = self.send_mock  # type: ignore[assignment]  # We assign to a method.

        # Define an application service for the tests
        self._service_token = "VERYSECRET"
        self._service = ApplicationService(
            self._service_token,
            "as1",
            "@as.sender:test",
            namespaces={
                "users": [
                    {
                        "regex": "@_as_.*:test",
                        "exclusive": True
                    },
                    {
                        "regex": "@as.sender:test",
                        "exclusive": True
                    },
                ]
            },
            msc3202_transaction_extensions=True,
        )
        self.hs.get_datastores().main.services_cache = [self._service]

        # Register some appservice users
        self._sender_user, self._sender_device = self.register_appservice_user(
            "as.sender", self._service_token)
        self._namespaced_user, self._namespaced_device = self.register_appservice_user(
            "_as_user1", self._service_token)

        # Register a real user as well.
        self._real_user = self.register_user("real.user", "meow")
        self._real_user_token = self.login("real.user", "meow")
Example #30
0
    def test_deleting_alias_via_directory_appservice(self) -> None:
        user_id = "@as:test"
        as_token = "i_am_an_app_service"

        appservice = ApplicationService(
            as_token,
            id="1234",
            namespaces={"aliases": [{
                "regex": "#asns-*",
                "exclusive": True
            }]},
            sender=user_id,
        )
        self.hs.get_datastores().main.services_cache.append(appservice)

        # Add an alias for the room, as the appservice
        alias = RoomAlias(f"asns-{random_string(5)}",
                          self.hs.hostname).to_string()
        data = {"room_id": self.room_id}
        request_data = json.dumps(data)

        channel = self.make_request(
            "PUT",
            f"/_matrix/client/r0/directory/room/{alias}",
            request_data,
            access_token=as_token,
        )
        self.assertEqual(channel.code, HTTPStatus.OK, channel.result)

        # Then try to remove the alias, as the appservice
        channel = self.make_request(
            "DELETE",
            f"/_matrix/client/r0/directory/room/{alias}",
            access_token=as_token,
        )
        self.assertEqual(channel.code, HTTPStatus.OK, channel.result)
Example #31
0
def _load_appservice(hostname: str, as_info: JsonDict,
                     config_filename: str) -> ApplicationService:
    required_string_fields = ["id", "as_token", "hs_token", "sender_localpart"]
    for field in required_string_fields:
        if not isinstance(as_info.get(field), str):
            raise KeyError("Required string field: '%s' (%s)" %
                           (field, config_filename))

    # 'url' must either be a string or explicitly null, not missing
    # to avoid accidentally turning off push for ASes.
    if not isinstance(as_info.get("url"), str) and as_info.get("url",
                                                               "") is not None:
        raise KeyError("Required string field or explicit null: 'url' (%s)" %
                       (config_filename, ))

    localpart = as_info["sender_localpart"]
    if urlparse.quote(localpart) != localpart:
        raise ValueError(
            "sender_localpart needs characters which are not URL encoded.")
    user = UserID(localpart, hostname)
    user_id = user.to_string()

    # Rate limiting for users of this AS is on by default (excludes sender)
    rate_limited = as_info.get("rate_limited")
    if not isinstance(rate_limited, bool):
        rate_limited = True

    # namespace checks
    if not isinstance(as_info.get("namespaces"), dict):
        raise KeyError("Requires 'namespaces' object.")
    for ns in ApplicationService.NS_LIST:
        # specific namespaces are optional
        if ns in as_info["namespaces"]:
            # expect a list of dicts with exclusive and regex keys
            for regex_obj in as_info["namespaces"][ns]:
                if not isinstance(regex_obj, dict):
                    raise ValueError(
                        "Expected namespace entry in %s to be an object, but got %s",
                        ns,
                        regex_obj,
                    )
                if not isinstance(regex_obj.get("regex"), str):
                    raise ValueError("Missing/bad type 'regex' key in %s",
                                     regex_obj)
                if not isinstance(regex_obj.get("exclusive"), bool):
                    raise ValueError("Missing/bad type 'exclusive' key in %s",
                                     regex_obj)
    # protocols check
    protocols = as_info.get("protocols")
    if protocols:
        if not isinstance(protocols, list):
            raise KeyError("Optional 'protocols' must be a list if present.")
        for p in protocols:
            if not isinstance(p, str):
                raise KeyError("Bad value for 'protocols' item")

    if as_info["url"] is None:
        logger.info(
            "(%s) Explicitly empty 'url' provided. This application service"
            " will not receive events or queries.",
            config_filename,
        )

    ip_range_whitelist = None
    if as_info.get("ip_range_whitelist"):
        ip_range_whitelist = IPSet(as_info.get("ip_range_whitelist"))

    supports_ephemeral = as_info.get("de.sorunome.msc2409.push_ephemeral",
                                     False)

    # Opt-in flag for the MSC3202-specific transactional behaviour.
    # When enabled, appservice transactions contain the following information:
    #  - device One-Time Key counts
    #  - device unused fallback key usage states
    #  - device list changes
    msc3202_transaction_extensions = as_info.get("org.matrix.msc3202", False)
    if not isinstance(msc3202_transaction_extensions, bool):
        raise ValueError(
            "The `org.matrix.msc3202` option should be true or false if specified."
        )

    return ApplicationService(
        token=as_info["as_token"],
        url=as_info["url"],
        namespaces=as_info["namespaces"],
        hs_token=as_info["hs_token"],
        sender=user_id,
        id=as_info["id"],
        protocols=protocols,
        rate_limited=rate_limited,
        ip_range_whitelist=ip_range_whitelist,
        supports_ephemeral=supports_ephemeral,
        msc3202_transaction_extensions=msc3202_transaction_extensions,
    )
Example #32
0
class ApplicationServiceTestCase(unittest.TestCase):
    def setUp(self):
        self.service = ApplicationService(id="unique_identifier",
                                          url="some_url",
                                          token="some_token",
                                          namespaces={
                                              ApplicationService.NS_USERS: [],
                                              ApplicationService.NS_ROOMS: [],
                                              ApplicationService.NS_ALIASES:
                                              []
                                          })
        self.event = Mock(type="m.something",
                          room_id="!foo:bar",
                          sender="@someone:somewhere")

    def test_regex_user_id_prefix_match(self):
        self.service.namespaces[ApplicationService.NS_USERS].append(
            _regex("@irc_.*"))
        self.event.sender = "@irc_foobar:matrix.org"
        self.assertTrue(self.service.is_interested(self.event))

    def test_regex_user_id_prefix_no_match(self):
        self.service.namespaces[ApplicationService.NS_USERS].append(
            _regex("@irc_.*"))
        self.event.sender = "@someone_else:matrix.org"
        self.assertFalse(self.service.is_interested(self.event))

    def test_regex_room_member_is_checked(self):
        self.service.namespaces[ApplicationService.NS_USERS].append(
            _regex("@irc_.*"))
        self.event.sender = "@someone_else:matrix.org"
        self.event.type = "m.room.member"
        self.event.state_key = "@irc_foobar:matrix.org"
        self.assertTrue(self.service.is_interested(self.event))

    def test_regex_room_id_match(self):
        self.service.namespaces[ApplicationService.NS_ROOMS].append(
            _regex("!some_prefix.*some_suffix:matrix.org"))
        self.event.room_id = "!some_prefixs0m3th1nGsome_suffix:matrix.org"
        self.assertTrue(self.service.is_interested(self.event))

    def test_regex_room_id_no_match(self):
        self.service.namespaces[ApplicationService.NS_ROOMS].append(
            _regex("!some_prefix.*some_suffix:matrix.org"))
        self.event.room_id = "!XqBunHwQIXUiqCaoxq:matrix.org"
        self.assertFalse(self.service.is_interested(self.event))

    def test_regex_alias_match(self):
        self.service.namespaces[ApplicationService.NS_ALIASES].append(
            _regex("#irc_.*:matrix.org"))
        self.assertTrue(
            self.service.is_interested(self.event,
                                       aliases_for_event=[
                                           "#irc_foobar:matrix.org",
                                           "#athing:matrix.org"
                                       ]))

    def test_non_exclusive_alias(self):
        self.service.namespaces[ApplicationService.NS_ALIASES].append(
            _regex("#irc_.*:matrix.org", exclusive=False))
        self.assertFalse(
            self.service.is_exclusive_alias("#irc_foobar:matrix.org"))

    def test_non_exclusive_room(self):
        self.service.namespaces[ApplicationService.NS_ROOMS].append(
            _regex("!irc_.*:matrix.org", exclusive=False))
        self.assertFalse(
            self.service.is_exclusive_room("!irc_foobar:matrix.org"))

    def test_non_exclusive_user(self):
        self.service.namespaces[ApplicationService.NS_USERS].append(
            _regex("@irc_.*:matrix.org", exclusive=False))
        self.assertFalse(
            self.service.is_exclusive_user("@irc_foobar:matrix.org"))

    def test_exclusive_alias(self):
        self.service.namespaces[ApplicationService.NS_ALIASES].append(
            _regex("#irc_.*:matrix.org", exclusive=True))
        self.assertTrue(
            self.service.is_exclusive_alias("#irc_foobar:matrix.org"))

    def test_exclusive_user(self):
        self.service.namespaces[ApplicationService.NS_USERS].append(
            _regex("@irc_.*:matrix.org", exclusive=True))
        self.assertTrue(
            self.service.is_exclusive_user("@irc_foobar:matrix.org"))

    def test_exclusive_room(self):
        self.service.namespaces[ApplicationService.NS_ROOMS].append(
            _regex("!irc_.*:matrix.org", exclusive=True))
        self.assertTrue(
            self.service.is_exclusive_room("!irc_foobar:matrix.org"))

    def test_regex_alias_no_match(self):
        self.service.namespaces[ApplicationService.NS_ALIASES].append(
            _regex("#irc_.*:matrix.org"))
        self.assertFalse(
            self.service.is_interested(self.event,
                                       aliases_for_event=[
                                           "#xmpp_foobar:matrix.org",
                                           "#athing:matrix.org"
                                       ]))

    def test_regex_multiple_matches(self):
        self.service.namespaces[ApplicationService.NS_ALIASES].append(
            _regex("#irc_.*:matrix.org"))
        self.service.namespaces[ApplicationService.NS_USERS].append(
            _regex("@irc_.*"))
        self.event.sender = "@irc_foobar:matrix.org"
        self.assertTrue(
            self.service.is_interested(
                self.event, aliases_for_event=["#irc_barfoo:matrix.org"]))

    def test_restrict_to_rooms(self):
        self.service.namespaces[ApplicationService.NS_ROOMS].append(
            _regex("!flibble_.*:matrix.org"))
        self.service.namespaces[ApplicationService.NS_USERS].append(
            _regex("@irc_.*"))
        self.event.sender = "@irc_foobar:matrix.org"
        self.event.room_id = "!wibblewoo:matrix.org"
        self.assertFalse(
            self.service.is_interested(
                self.event, restrict_to=ApplicationService.NS_ROOMS))

    def test_restrict_to_aliases(self):
        self.service.namespaces[ApplicationService.NS_ALIASES].append(
            _regex("#xmpp_.*:matrix.org"))
        self.service.namespaces[ApplicationService.NS_USERS].append(
            _regex("@irc_.*"))
        self.event.sender = "@irc_foobar:matrix.org"
        self.assertFalse(
            self.service.is_interested(
                self.event,
                restrict_to=ApplicationService.NS_ALIASES,
                aliases_for_event=["#irc_barfoo:matrix.org"]))

    def test_restrict_to_senders(self):
        self.service.namespaces[ApplicationService.NS_ALIASES].append(
            _regex("#xmpp_.*:matrix.org"))
        self.service.namespaces[ApplicationService.NS_USERS].append(
            _regex("@irc_.*"))
        self.event.sender = "@xmpp_foobar:matrix.org"
        self.assertFalse(
            self.service.is_interested(
                self.event,
                restrict_to=ApplicationService.NS_USERS,
                aliases_for_event=["#xmpp_barfoo:matrix.org"]))

    def test_interested_in_self(self):
        # make sure invites get through
        self.service.sender = "@appservice:name"
        self.service.namespaces[ApplicationService.NS_USERS].append(
            _regex("@irc_.*"))
        self.event.type = "m.room.member"
        self.event.content = {"membership": "invite"}
        self.event.state_key = self.service.sender
        self.assertTrue(self.service.is_interested(self.event))

    def test_member_list_match(self):
        self.service.namespaces[ApplicationService.NS_USERS].append(
            _regex("@irc_.*"))
        join_list = [
            "@alice:here",
            "@irc_fo:here",  # AS user
            "@bob:here",
        ]

        self.event.sender = "@xmpp_foobar:matrix.org"
        self.assertTrue(
            self.service.is_interested(event=self.event,
                                       member_list=join_list))
Example #33
0
class ApplicationServiceTestCase(unittest.TestCase):

    def setUp(self):
        self.service = ApplicationService(
            id="unique_identifier",
            url="some_url",
            token="some_token",
            namespaces={
                ApplicationService.NS_USERS: [],
                ApplicationService.NS_ROOMS: [],
                ApplicationService.NS_ALIASES: []
            }
        )
        self.event = Mock(
            type="m.something", room_id="!foo:bar", sender="@someone:somewhere"
        )

    def test_regex_user_id_prefix_match(self):
        self.service.namespaces[ApplicationService.NS_USERS].append(
            _regex("@irc_.*")
        )
        self.event.sender = "@irc_foobar:matrix.org"
        self.assertTrue(self.service.is_interested(self.event))

    def test_regex_user_id_prefix_no_match(self):
        self.service.namespaces[ApplicationService.NS_USERS].append(
            _regex("@irc_.*")
        )
        self.event.sender = "@someone_else:matrix.org"
        self.assertFalse(self.service.is_interested(self.event))

    def test_regex_room_member_is_checked(self):
        self.service.namespaces[ApplicationService.NS_USERS].append(
            _regex("@irc_.*")
        )
        self.event.sender = "@someone_else:matrix.org"
        self.event.type = "m.room.member"
        self.event.state_key = "@irc_foobar:matrix.org"
        self.assertTrue(self.service.is_interested(self.event))

    def test_regex_room_id_match(self):
        self.service.namespaces[ApplicationService.NS_ROOMS].append(
            _regex("!some_prefix.*some_suffix:matrix.org")
        )
        self.event.room_id = "!some_prefixs0m3th1nGsome_suffix:matrix.org"
        self.assertTrue(self.service.is_interested(self.event))

    def test_regex_room_id_no_match(self):
        self.service.namespaces[ApplicationService.NS_ROOMS].append(
            _regex("!some_prefix.*some_suffix:matrix.org")
        )
        self.event.room_id = "!XqBunHwQIXUiqCaoxq:matrix.org"
        self.assertFalse(self.service.is_interested(self.event))

    def test_regex_alias_match(self):
        self.service.namespaces[ApplicationService.NS_ALIASES].append(
            _regex("#irc_.*:matrix.org")
        )
        self.assertTrue(self.service.is_interested(
            self.event,
            aliases_for_event=["#irc_foobar:matrix.org", "#athing:matrix.org"]
        ))

    def test_non_exclusive_alias(self):
        self.service.namespaces[ApplicationService.NS_ALIASES].append(
            _regex("#irc_.*:matrix.org", exclusive=False)
        )
        self.assertFalse(self.service.is_exclusive_alias(
            "#irc_foobar:matrix.org"
        ))

    def test_non_exclusive_room(self):
        self.service.namespaces[ApplicationService.NS_ROOMS].append(
            _regex("!irc_.*:matrix.org", exclusive=False)
        )
        self.assertFalse(self.service.is_exclusive_room(
            "!irc_foobar:matrix.org"
        ))

    def test_non_exclusive_user(self):
        self.service.namespaces[ApplicationService.NS_USERS].append(
            _regex("@irc_.*:matrix.org", exclusive=False)
        )
        self.assertFalse(self.service.is_exclusive_user(
            "@irc_foobar:matrix.org"
        ))

    def test_exclusive_alias(self):
        self.service.namespaces[ApplicationService.NS_ALIASES].append(
            _regex("#irc_.*:matrix.org", exclusive=True)
        )
        self.assertTrue(self.service.is_exclusive_alias(
            "#irc_foobar:matrix.org"
        ))

    def test_exclusive_user(self):
        self.service.namespaces[ApplicationService.NS_USERS].append(
            _regex("@irc_.*:matrix.org", exclusive=True)
        )
        self.assertTrue(self.service.is_exclusive_user(
            "@irc_foobar:matrix.org"
        ))

    def test_exclusive_room(self):
        self.service.namespaces[ApplicationService.NS_ROOMS].append(
            _regex("!irc_.*:matrix.org", exclusive=True)
        )
        self.assertTrue(self.service.is_exclusive_room(
            "!irc_foobar:matrix.org"
        ))

    def test_regex_alias_no_match(self):
        self.service.namespaces[ApplicationService.NS_ALIASES].append(
            _regex("#irc_.*:matrix.org")
        )
        self.assertFalse(self.service.is_interested(
            self.event,
            aliases_for_event=["#xmpp_foobar:matrix.org", "#athing:matrix.org"]
        ))

    def test_regex_multiple_matches(self):
        self.service.namespaces[ApplicationService.NS_ALIASES].append(
            _regex("#irc_.*:matrix.org")
        )
        self.service.namespaces[ApplicationService.NS_USERS].append(
            _regex("@irc_.*")
        )
        self.event.sender = "@irc_foobar:matrix.org"
        self.assertTrue(self.service.is_interested(
            self.event,
            aliases_for_event=["#irc_barfoo:matrix.org"]
        ))

    def test_restrict_to_rooms(self):
        self.service.namespaces[ApplicationService.NS_ROOMS].append(
            _regex("!flibble_.*:matrix.org")
        )
        self.service.namespaces[ApplicationService.NS_USERS].append(
            _regex("@irc_.*")
        )
        self.event.sender = "@irc_foobar:matrix.org"
        self.event.room_id = "!wibblewoo:matrix.org"
        self.assertFalse(self.service.is_interested(
            self.event,
            restrict_to=ApplicationService.NS_ROOMS
        ))

    def test_restrict_to_aliases(self):
        self.service.namespaces[ApplicationService.NS_ALIASES].append(
            _regex("#xmpp_.*:matrix.org")
        )
        self.service.namespaces[ApplicationService.NS_USERS].append(
            _regex("@irc_.*")
        )
        self.event.sender = "@irc_foobar:matrix.org"
        self.assertFalse(self.service.is_interested(
            self.event,
            restrict_to=ApplicationService.NS_ALIASES,
            aliases_for_event=["#irc_barfoo:matrix.org"]
        ))

    def test_restrict_to_senders(self):
        self.service.namespaces[ApplicationService.NS_ALIASES].append(
            _regex("#xmpp_.*:matrix.org")
        )
        self.service.namespaces[ApplicationService.NS_USERS].append(
            _regex("@irc_.*")
        )
        self.event.sender = "@xmpp_foobar:matrix.org"
        self.assertFalse(self.service.is_interested(
            self.event,
            restrict_to=ApplicationService.NS_USERS,
            aliases_for_event=["#xmpp_barfoo:matrix.org"]
        ))

    def test_interested_in_self(self):
        # make sure invites get through
        self.service.sender = "@appservice:name"
        self.service.namespaces[ApplicationService.NS_USERS].append(
            _regex("@irc_.*")
        )
        self.event.type = "m.room.member"
        self.event.content = {
            "membership": "invite"
        }
        self.event.state_key = self.service.sender
        self.assertTrue(self.service.is_interested(self.event))

    def test_member_list_match(self):
        self.service.namespaces[ApplicationService.NS_USERS].append(
            _regex("@irc_.*")
        )
        join_list = [
            "@alice:here",
            "@irc_fo:here",  # AS user
            "@bob:here",
        ]

        self.event.sender = "@xmpp_foobar:matrix.org"
        self.assertTrue(self.service.is_interested(
            event=self.event,
            member_list=join_list
        ))