Exemple #1
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())
Exemple #2
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)
Exemple #3
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)
Exemple #4
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
Exemple #5
0
    async def _get_to_device_messages(
        self,
        service: ApplicationService,
        new_token: int,
        users: Collection[Union[str, UserID]],
    ) -> List[JsonDict]:
        """
        Given an application service, determine which events it should receive
        from those between the last-recorded to-device message stream token for this
        appservice and the given stream token.

        Args:
            service: The application service to check for which events it should receive.
            new_token: The latest to-device event stream token.
            users: The users to be notified for the new to-device messages
                (ie, the recipients of the messages).

        Returns:
            A list of JSON dictionaries containing data derived from the to-device events
                that should be sent to the given application service.
        """
        # Get the stream token that this application service has processed up until
        from_key = await self.store.get_type_stream_id_for_appservice(
            service, "to_device")

        # Filter out users that this appservice is not interested in
        users_appservice_is_interested_in: List[str] = []
        for user in users:
            # FIXME: We should do this farther up the call stack. We currently repeat
            #  this operation in _handle_presence.
            if isinstance(user, UserID):
                user = user.to_string()

            if service.is_interested_in_user(user):
                users_appservice_is_interested_in.append(user)

        if not users_appservice_is_interested_in:
            # Return early if the AS was not interested in any of these users
            return []

        # Retrieve the to-device messages for each user
        recipient_device_to_messages = await self.store.get_messages_for_user_devices(
            users_appservice_is_interested_in,
            from_key,
            new_token,
        )

        # According to MSC2409, we'll need to add 'to_user_id' and 'to_device_id' fields
        # to the event JSON so that the application service will know which user/device
        # combination this messages was intended for.
        #
        # So we mangle this dict into a flat list of to-device messages with the relevant
        # user ID and device ID embedded inside each message dict.
        message_payload: List[JsonDict] = []
        for (
                user_id,
                device_id,
        ), messages in recipient_device_to_messages.items():
            for message_json in messages:
                # Remove 'message_id' from the to-device message, as it's an internal ID
                message_json.pop("message_id", None)

                message_payload.append({
                    "to_user_id": user_id,
                    "to_device_id": device_id,
                    **message_json,
                })

        return message_payload