예제 #1
0
    async def exchange_token_from_credentials(
        self,
        turn_context: TurnContext,
        oauth_app_credentials: AppCredentials,
        connection_name: str,
        user_id: str,
        exchange_request: TokenExchangeRequest,
    ) -> TokenResponse:
        exchangeable_value = exchange_request.token or exchange_request.uri

        key = ExchangeableToken(
            channel_id=turn_context.activity.channel_id,
            connection_name=connection_name,
            exchangeable_item=exchangeable_value,
            user_id=user_id,
        )

        token_exchange_response = self.exchangeable_tokens.get(key.to_key())
        if token_exchange_response:
            return TokenResponse(
                channel_id=key.channel_id,
                connection_name=key.connection_name,
                token=token_exchange_response.token,
                expiration=None,
            )

        return None
    async def exchange_token_from_credentials(
        self,
        turn_context: TurnContext,
        oauth_app_credentials: AppCredentials,
        connection_name: str,
        user_id: str,
        exchange_request: TokenExchangeRequest,
    ) -> TokenResponse:
        exchangeable_value = exchange_request.token or exchange_request.uri

        key = ExchangeableToken(
            channel_id=turn_context.activity.channel_id,
            connection_name=connection_name,
            exchangeable_item=exchangeable_value,
            user_id=user_id,
        )

        token_exchange_response = self.exchangeable_tokens.get(key.to_key())
        if token_exchange_response:
            if token_exchange_response.token == TestAdapter.__EXCEPTION_EXPECTED:
                raise Exception("Exception occurred during exchanging tokens")

            return TokenResponse(
                channel_id=key.channel_id,
                connection_name=key.connection_name,
                token=token_exchange_response.token,
                expiration=None,
            )

        return None
        async def inspector(activity: Activity, description: str = None):  # pylint: disable=unused-argument

            self.assertTrue(len(activity.attachments) == 1)
            self.assertTrue(
                activity.attachments[0].content_type
                == CardFactory.content_types.oauth_card
            )

            # send a mock EventActivity back to the bot with the token
            adapter.add_user_token(
                connection_name, activity.channel_id, activity.recipient.id, token
            )

            event_activity = create_reply(activity)
            event_activity.type = ActivityTypes.event
            event_activity.from_property, event_activity.recipient = (
                event_activity.recipient,
                event_activity.from_property,
            )
            event_activity.name = "tokens/response"
            event_activity.value = TokenResponse(
                connection_name=connection_name, token=token
            )

            context = TurnContext(adapter, event_activity)
            await callback_handler(context)
예제 #4
0
    async def get_user_token(self, context: TurnContext, connection_name: str, magic_code: str = None) -> TokenResponse:
        key = UserToken()
        key.channel_id = context.activity.channel_id
        key.connection_name = connection_name
        key.user_id = context.activity.from_property.id

        if magic_code:
            magic_code_record = list(filter(lambda x: key.equals_key(x.key), self._magic_codes))
            if magic_code_record and len(magic_code_record) > 0 and magic_code_record[0].magic_code == magic_code:
                # Move the token to long term dictionary.
                self.add_user_token(connection_name, key.channel_id, key.user_id, magic_code_record[0].key.token)

                # Remove from the magic code list.
                idx = self._magic_codes.index(magic_code_record[0])
                self._magic_codes = [self._magic_codes.pop(idx)]

        match = list(filter(lambda x: key.equals_key(x), self._user_tokens))

        if match and len(match) > 0:
            return TokenResponse(
                connection_name=match[0].connection_name,
                token=match[0].token,
                expiration=None
            )
        else:
            # Not found.
            return None
예제 #5
0
    async def get_user_token(
            self,
            context: TurnContext,
            connection_name: str,
            magic_code: str = None,
            oauth_app_credentials: AppCredentials = None,  # pylint: disable=unused-argument
    ) -> TokenResponse:
        key = UserToken()
        key.channel_id = context.activity.channel_id
        key.connection_name = connection_name
        key.user_id = context.activity.from_property.id

        if magic_code:
            magic_code_record = list(
                filter(lambda x: key.equals_key(x.key), self._magic_codes))
            if magic_code_record and magic_code_record[
                    0].magic_code == magic_code:
                # Move the token to long term dictionary.
                self.add_user_token(
                    connection_name,
                    key.channel_id,
                    key.user_id,
                    magic_code_record[0].key.token,
                )

                # Remove from the magic code list.
                idx = self._magic_codes.index(magic_code_record[0])
                self._magic_codes = [self._magic_codes.pop(idx)]

        match = [token for token in self._user_tokens if key.equals_key(token)]

        if match:
            return TokenResponse(
                connection_name=match[0].connection_name,
                token=match[0].token,
                expiration=None,
            )
        # Not found.
        return None
예제 #6
0
    async def _recognize_token(
            self, dialog_context: DialogContext) -> PromptRecognizerResult:
        context = dialog_context.context
        token = None
        if OAuthPrompt._is_token_response_event(context):
            token = context.activity.value

            # fixup the turnContext's state context if this was received from a skill host caller
            state: CallerInfo = dialog_context.active_dialog.state[
                OAuthPrompt.PERSISTED_CALLER]
            if state:
                # set the ServiceUrl to the skill host's Url
                dialog_context.context.activity.service_url = state.caller_service_url

                # recreate a ConnectorClient and set it in TurnState so replies use the correct one
                if not isinstance(context.adapter, ConnectorClientBuilder):
                    raise TypeError(
                        "OAuthPrompt: IConnectorClientProvider interface not implemented by the current adapter"
                    )

                connector_client_builder: ConnectorClientBuilder = context.adapter
                claims_identity = context.turn_state.get(
                    BotAdapter.BOT_IDENTITY_KEY)
                connector_client = await connector_client_builder.create_connector_client(
                    dialog_context.context.activity.service_url,
                    claims_identity,
                    state.scope,
                )

                context.turn_state[
                    BotAdapter.BOT_CONNECTOR_CLIENT_KEY] = connector_client

        elif OAuthPrompt._is_teams_verification_invoke(context):
            code = context.activity.value["state"]
            try:
                token = await self.get_user_token(context, code)
                if token is not None:
                    await context.send_activity(
                        Activity(
                            type="invokeResponse",
                            value=InvokeResponse(int(HTTPStatus.OK)),
                        ))
                else:
                    await context.send_activity(
                        Activity(
                            type="invokeResponse",
                            value=InvokeResponse(int(HTTPStatus.NOT_FOUND)),
                        ))
            except Exception:
                await context.send_activity(
                    Activity(
                        type="invokeResponse",
                        value=InvokeResponse(
                            int(HTTPStatus.INTERNAL_SERVER_ERROR)),
                    ))
        elif self._is_token_exchange_request_invoke(context):
            if isinstance(context.activity.value, dict):
                context.activity.value = TokenExchangeInvokeRequest(
                ).from_dict(context.activity.value)

            if not (context.activity.value and self._is_token_exchange_request(
                    context.activity.value)):
                # Received activity is not a token exchange request.
                await context.send_activity(
                    self._get_token_exchange_invoke_response(
                        int(HTTPStatus.BAD_REQUEST),
                        "The bot received an InvokeActivity that is missing a TokenExchangeInvokeRequest value."
                        " This is required to be sent with the InvokeActivity.",
                    ))
            elif (context.activity.value.connection_name !=
                  self._settings.connection_name):
                # Connection name on activity does not match that of setting.
                await context.send_activity(
                    self._get_token_exchange_invoke_response(
                        int(HTTPStatus.BAD_REQUEST),
                        "The bot received an InvokeActivity with a TokenExchangeInvokeRequest containing a"
                        " ConnectionName that does not match the ConnectionName expected by the bots active"
                        " OAuthPrompt. Ensure these names match when sending the InvokeActivityInvalid"
                        " ConnectionName in the TokenExchangeInvokeRequest",
                    ))
            elif not getattr(context.adapter, "exchange_token"):
                # Token Exchange not supported in the adapter.
                await context.send_activity(
                    self._get_token_exchange_invoke_response(
                        int(HTTPStatus.BAD_GATEWAY),
                        "The bot's BotAdapter does not support token exchange operations."
                        " Ensure the bot's Adapter supports the ExtendedUserTokenProvider interface.",
                    ))

                raise AttributeError(
                    "OAuthPrompt._recognize_token(): not supported by the current adapter."
                )
            else:
                # No errors. Proceed with token exchange.
                extended_user_token_provider: ExtendedUserTokenProvider = context.adapter

                token_exchange_response = None
                try:
                    token_exchange_response = await extended_user_token_provider.exchange_token_from_credentials(
                        context,
                        self._settings.oath_app_credentials,
                        self._settings.connection_name,
                        context.activity.from_property.id,
                        TokenExchangeRequest(
                            token=context.activity.value.token),
                    )
                except:
                    # Ignore Exceptions
                    # If token exchange failed for any reason, tokenExchangeResponse above stays null, and
                    # hence we send back a failure invoke response to the caller.
                    pass

                if not token_exchange_response or not token_exchange_response.token:
                    await context.send_activity(
                        self._get_token_exchange_invoke_response(
                            int(HTTPStatus.PRECONDITION_FAILED),
                            "The bot is unable to exchange token. Proceed with regular login.",
                        ))
                else:
                    await context.send_activity(
                        self._get_token_exchange_invoke_response(
                            int(HTTPStatus.OK), None,
                            context.activity.value.id))
                    token = TokenResponse(
                        channel_id=token_exchange_response.channel_id,
                        connection_name=token_exchange_response.
                        connection_name,
                        token=token_exchange_response.token,
                        expiration=None,
                    )
        elif context.activity.type == ActivityTypes.message and context.activity.text:
            match = re.match(r"(?<!\d)\d{6}(?!\d)", context.activity.text)
            if match:
                token = await self.get_user_token(context, match[0])

        return (PromptRecognizerResult(True, token)
                if token is not None else PromptRecognizerResult())
예제 #7
0
    async def _recognize_token(self,
                               context: TurnContext) -> PromptRecognizerResult:
        token = None
        if OAuthPrompt._is_token_response_event(context):
            token = context.activity.value
        elif OAuthPrompt._is_teams_verification_invoke(context):
            code = context.activity.value["state"]
            try:
                token = await self.get_user_token(context, code)
                if token is not None:
                    await context.send_activity(
                        Activity(
                            type="invokeResponse",
                            value=InvokeResponse(int(HTTPStatus.OK)),
                        ))
                else:
                    await context.send_activity(
                        Activity(
                            type="invokeResponse",
                            value=InvokeResponse(int(HTTPStatus.NOT_FOUND)),
                        ))
            except Exception:
                await context.send_activity(
                    Activity(
                        type="invokeResponse",
                        value=InvokeResponse(
                            int(HTTPStatus.INTERNAL_SERVER_ERROR)),
                    ))
        elif self._is_token_exchange_request_invoke(context):
            if isinstance(context.activity.value, dict):
                context.activity.value = TokenExchangeInvokeRequest(
                ).from_dict(context.activity.value)

            if not (context.activity.value and self._is_token_exchange_request(
                    context.activity.value)):
                # Received activity is not a token exchange request.
                await context.send_activity(
                    self._get_token_exchange_invoke_response(
                        int(HTTPStatus.BAD_REQUEST),
                        "The bot received an InvokeActivity that is missing a TokenExchangeInvokeRequest value."
                        " This is required to be sent with the InvokeActivity.",
                    ))
            elif (context.activity.value.connection_name !=
                  self._settings.connection_name):
                # Connection name on activity does not match that of setting.
                await context.send_activity(
                    self._get_token_exchange_invoke_response(
                        int(HTTPStatus.BAD_REQUEST),
                        "The bot received an InvokeActivity with a TokenExchangeInvokeRequest containing a"
                        " ConnectionName that does not match the ConnectionName expected by the bots active"
                        " OAuthPrompt. Ensure these names match when sending the InvokeActivityInvalid"
                        " ConnectionName in the TokenExchangeInvokeRequest",
                    ))
            elif not getattr(context.adapter, "exchange_token"):
                # Token Exchange not supported in the adapter.
                await context.send_activity(
                    self._get_token_exchange_invoke_response(
                        int(HTTPStatus.BAD_GATEWAY),
                        "The bot's BotAdapter does not support token exchange operations."
                        " Ensure the bot's Adapter supports the ITokenExchangeProvider interface.",
                    ))

                raise AttributeError(
                    "OAuthPrompt.recognize(): not supported by the current adapter."
                )
            else:
                # No errors. Proceed with token exchange.
                extended_user_token_provider: ExtendedUserTokenProvider = context.adapter
                token_exchange_response = await extended_user_token_provider.exchange_token_from_credentials(
                    context,
                    self._settings.oath_app_credentials,
                    self._settings.connection_name,
                    context.activity.from_property.id,
                    TokenExchangeRequest(token=context.activity.value.token),
                )

                if not token_exchange_response or not token_exchange_response.token:
                    await context.send_activity(
                        self._get_token_exchange_invoke_response(
                            int(HTTPStatus.CONFLICT),
                            "The bot is unable to exchange token. Proceed with regular login.",
                        ))
                else:
                    await context.send_activity(
                        self._get_token_exchange_invoke_response(
                            int(HTTPStatus.OK), None,
                            context.activity.value.id))
                    token = TokenResponse(
                        channel_id=token_exchange_response.channel_id,
                        connection_name=token_exchange_response.
                        connection_name,
                        token=token_exchange_response.token,
                        expiration=None,
                    )
        elif context.activity.type == ActivityTypes.message and context.activity.text:
            match = re.match(r"(?<!\d)\d{6}(?!\d)", context.activity.text)
            if match:
                token = await self.get_user_token(context, match[0])

        return (PromptRecognizerResult(True, token)
                if token is not None else PromptRecognizerResult())