Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions libraries/botbuilder-core/botbuilder/core/bot_framework_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
TokenStatus,
TokenExchangeRequest,
SignInUrlResponse,
TokenResponse as ConnectorTokenResponse,
)
from botbuilder.schema import (
Activity,
Expand Down Expand Up @@ -509,7 +510,10 @@ async def process_activity_with_identity(
)
if invoke_response is None:
return InvokeResponse(status=int(HTTPStatus.NOT_IMPLEMENTED))
return invoke_response.value
return InvokeResponse(
status=invoke_response.value.status,
body=invoke_response.value.body.serialize(),
)

return None

Expand Down Expand Up @@ -1267,8 +1271,13 @@ async def exchange_token_from_credentials(
exchange_request.token,
)

if isinstance(result, TokenResponse):
return result
if isinstance(result, ConnectorTokenResponse):
return TokenResponse(
channel_id=result.channel_id,
connection_name=result.connection_name,
token=result.token,
expiration=result.expiration,
)
raise TypeError(f"exchange_async returned improper result: {type(result)}")

@staticmethod
Expand Down
154 changes: 154 additions & 0 deletions libraries/botbuilder-core/tests/test_bot_framework_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
BotFrameworkAdapterSettings,
TurnContext,
)
from botbuilder.core.invoke_response import InvokeResponse
from botbuilder.schema import (
Activity,
ActivityTypes,
Expand All @@ -22,6 +23,13 @@
DeliveryModes,
ExpectedReplies,
CallerIdConstants,
SignInConstants,
TokenExchangeInvokeRequest,
TokenExchangeInvokeResponse,
)
from botframework.connector.token_api.models import (
TokenExchangeRequest,
TokenResponse as ConnectorTokenResponse,
)
from botframework.connector.aio import ConnectorClient
from botframework.connector.auth import (
Expand Down Expand Up @@ -189,6 +197,31 @@ async def mock_create_conversation(parameters):

return self.connector_client_mock

async def _create_token_api_client(
self, context: TurnContext, oauth_app_credentials: AppCredentials = None
):
client = await super()._create_token_api_client(context, oauth_app_credentials)

def mock_exchange_async(
user_id, # pylint: disable=unused-argument
connection_name,
channel_id,
uri=None, # pylint: disable=unused-argument
token=None,
custom_headers=None, # pylint: disable=unused-argument
raw=False, # pylint: disable=unused-argument
**operation_config, # pylint: disable=unused-argument
):
return ConnectorTokenResponse(
channel_id=channel_id,
connection_name=connection_name,
token=token,
expiration=None,
)

client.user_token.exchange_async = mock_exchange_async
return client


async def process_activity(
channel_id: str, channel_data_tenant_id: str, conversation_tenant_id: str
Expand Down Expand Up @@ -731,3 +764,124 @@ async def callback(context: TurnContext):
adapter.connector_client_mock.conversations.send_to_conversation.call_count
== 3
)

async def test_process_activity_with_identity_token_exchange_invoke_response(self):
mock_credential_provider = unittest.mock.create_autospec(CredentialProvider)

settings = BotFrameworkAdapterSettings(
app_id="bot_id", credential_provider=mock_credential_provider,
)
adapter = AdapterUnderTest(settings)

identity = ClaimsIdentity(
claims={
AuthenticationConstants.AUDIENCE_CLAIM: "bot_id",
AuthenticationConstants.APP_ID_CLAIM: "bot_id",
AuthenticationConstants.VERSION_CLAIM: "1.0",
},
is_authenticated=True,
)

inbound_activity = Activity(
type=ActivityTypes.invoke,
name=SignInConstants.token_exchange_operation_name,
service_url="http://tempuri.org/whatever",
delivery_mode=DeliveryModes.normal,
conversation=ConversationAccount(id="conversationId"),
value=TokenExchangeInvokeRequest(
id="token_exchange_id",
token="token",
connection_name="connection_name",
),
)

async def callback(context: TurnContext):
activity = Activity(
type=ActivityTypes.invoke_response,
value=InvokeResponse(
status=200,
body=TokenExchangeInvokeResponse(
id=context.activity.value.id,
connection_name=context.activity.value.connection_name,
),
),
)

await context.send_activity(activity)

invoke_response = await adapter.process_activity_with_identity(
inbound_activity, identity, callback,
)

assert invoke_response
assert invoke_response.status == 200
assert invoke_response.body["id"] == inbound_activity.value.id
assert (
invoke_response.body["connectionName"]
== inbound_activity.value.connection_name
)

async def test_exchange_token_from_credentials(self):
mock_credential_provider = unittest.mock.create_autospec(CredentialProvider)

settings = BotFrameworkAdapterSettings(
app_id="bot_id", credential_provider=mock_credential_provider,
)
adapter = AdapterUnderTest(settings)

identity = ClaimsIdentity(
claims={
AuthenticationConstants.AUDIENCE_CLAIM: "bot_id",
AuthenticationConstants.APP_ID_CLAIM: "bot_id",
AuthenticationConstants.VERSION_CLAIM: "1.0",
},
is_authenticated=True,
)

inbound_activity = Activity(
type=ActivityTypes.invoke,
name=SignInConstants.token_exchange_operation_name,
service_url="http://tempuri.org/whatever",
conversation=ConversationAccount(id="conversationId"),
value=TokenExchangeInvokeRequest(
id="token_exchange_id",
token="token",
connection_name="connection_name",
),
)

async def callback(context):
result = await adapter.exchange_token_from_credentials(
turn_context=context,
oauth_app_credentials=None,
connection_name=context.activity.value.connection_name,
exchange_request=TokenExchangeRequest(
token=context.activity.value.token, uri=context.activity.service_url
),
user_id="user_id",
)

activity = Activity(
type=ActivityTypes.invoke_response,
value=InvokeResponse(
status=200,
body=TokenExchangeInvokeResponse(
id=context.activity.value.id,
connection_name=result.connection_name,
),
),
)

await context.send_activity(activity)

invoke_response = await adapter.process_activity_with_identity(
inbound_activity, identity, callback,
)

assert invoke_response
assert invoke_response.status == 200
assert invoke_response.body["id"] == inbound_activity.value.id
assert (
invoke_response.body["connectionName"]
== inbound_activity.value.connection_name
)