8000 Merge branch 'master' into axsuarez/skills-lower-layers · rusty0209/botbuilder-python@75d1b51 · GitHub
[go: up one dir, main page]

Skip to content

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 75d1b51

Browse files
authored
Merge branch 'master' into axsuarez/skills-lower-layers
2 parents 0517074 + e5254b6 commit 75d1b51

9 files changed

+261
-57
lines changed

libraries/botframework-connector/botframework/connector/auth/__init__.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,15 @@
99
# regenerated.
1010
# --------------------------------------------------------------------------
1111
# pylint: disable=missing-docstring
12-
12+
from .government_constants import *
13+
from .channel_provider import *
14+
from .simple_channel_provider import *
1315
from .microsoft_app_credentials import *
16+
from .claims_identity import *
1417
from .jwt_token_validation import *
1518
from .credential_provider import *
1619
from .channel_validation import *
1720
from .emulator_validation import *
1821
from .jwt_token_extractor import *
19-
from .government_constants import *
2022
from .authentication_constants import *
23+
from .authentication_configuration import *
Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
# Copyright (c) Microsoft Corporation. All rights reserved.
22
# Licensed under the MIT License.
33

4-
from typing import List
4+
from typing import Awaitable, Callable, Dict, List
55

66

77
class AuthenticationConfiguration:
8-
def __init__(self, required_endorsements: List[str] = None):
8+
def __init__(
9+
self,
10+
required_endorsements: List[str] = None,
11+
claims_validator: Callable[[List[Dict]], Awaitable] = None,
12+
):
913
self.required_endorsements = required_endorsements or []
14+
self.claims_validator = claims_validator
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
from abc import ABC, abstractmethod
5+
6+
7+
class ChannelProvider(ABC):
8+
"""
9+
ChannelProvider interface. This interface allows Bots to provide their own
10+
implementation for the configuration parameters to connect to a Bot.
11+
Framework channel service.
12+
"""
13+
14+
@abstractmethod
15+
async def get_channel_service(self) -> str:
16+
raise NotImplementedError()
17+
18+
@abstractmethod
19+
def is_government(self) -> bool:
20+
raise NotImplementedError()
21+
22+
@abstractmethod
23+
def is_public_azure(self) -> bool:
24+
raise NotImplementedError()

libraries/botframework-connector/botframework/connector/auth/emulator_validation.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
# Licensed under the MIT License.
33

44
import asyncio
5+
from typing import Union
6+
57
import jwt
68

79
from .jwt_token_extractor import JwtTokenExtractor
@@ -10,6 +12,7 @@
1012
from .credential_provider import CredentialProvider
1113
from .claims_identity import ClaimsIdentity
1214
from .government_constants import GovernmentConstants
15+
from .channel_provider import ChannelProvider
1316

1417

1518
class EmulatorValidation:
@@ -82,7 +85,7 @@ def is_token_from_emulator(auth_header: str) -> bool:
8285
async def authenticate_emulator_token(
8386
auth_header: str,
8487
credentials: CredentialProvider,
85-
channel_service: str,
88+
channel_service_or_provider: Union[str, ChannelProvider],
8689
channel_id: str,
8790
) -> ClaimsIdentity:
8891
""" Validate the incoming Auth Header
@@ -101,12 +104,14 @@ async def authenticate_emulator_token(
101104
# pylint: disable=import-outside-toplevel
102105
from .jwt_token_validation import JwtTokenValidation
103106

107+
if isinstance(channel_service_or_provider, ChannelProvider):
108+
is_gov = channel_service_or_provider.is_government()
109+
else:
110+
is_gov = JwtTokenValidation.is_government(channel_service_or_provider)
111+
104112
open_id_metadata = (
105113
GovernmentConstants.TO_BOT_FROM_EMULATOR_OPEN_ID_METADATA_URL
106-
if (
107-
channel_service is not None
108-
and JwtTokenValidation.is_government(channel_service)
109-
)
114+
if is_gov
110115
else Constants.TO_BOT_FROM_EMULATOR_OPEN_ID_METADATA_URL
111116
)
112117

libraries/botframework-connector/botframework/connector/auth/enterprise_channel_validation.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
# Licensed under the MIT License.
33

44
from abc import ABC
5+
from typing import Union
56

67
from .authentication_configuration import AuthenticationConfiguration
78
from .authentication_constants import AuthenticationConstants
89
from .channel_validation import ChannelValidation
10+
from .channel_provider import ChannelProvider
911
from .claims_identity import ClaimsIdentity
1012
from .credential_provider import CredentialProvider
1113
from .jwt_token_extractor import JwtTokenExtractor
@@ -26,9 +28,13 @@ async def authenticate_channel_token(
2628
auth_header: str,
2729
credentials: CredentialProvider,
2830
channel_id: str,
29-
channel_service: str,
31+
channel_service_or_provider: Union[str, ChannelProvider],
3032
auth_configuration: AuthenticationConfiguration = None,
3133
) -> ClaimsIdentity:
34+
channel_service = channel_service_or_provider
35+
if isinstance(channel_service_or_provider, ChannelProvider):
36+
channel_service = await channel_service_or_provider.get_channel_service()
37+
3238
endpoint = (
3339
ChannelValidation.open_id_metadata_endpoint
3440
if ChannelValidation.open_id_metadata_endpoint
@@ -55,11 +61,15 @@ async def authenticate_channel_token_with_service_url(
5561
credentials: CredentialProvider,
5662
service_url: str,
5763
channel_id: str,
58-
channel_service: str,
64+
channel_service_or_provider: Union[str, ChannelProvider],
5965
auth_configuration: AuthenticationConfiguration = None,
6066
) -> ClaimsIdentity:
6167
identity: ClaimsIdentity = await EnterpriseChannelValidation.authenticate_channel_token(
62-
auth_header, credentials, channel_id, channel_service, auth_configuration
68+
auth_header,
69+
credentials,
70+
channel_id,
71+
channel_service_or_provider,
72+
auth_configuration,
6373
)
6474

6575
service_url_claim: str = identity.get_claim_value(

libraries/botframework-connector/botframework/connector/auth/jwt_token_validation.py

Lines changed: 71 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Copyright (c) Microsoft Corporation. All rights reserved.
22
# Licensed under the MIT License.
3-
from typing import Dict
3+
from typing import Dict, List, Union
44

55
from botbuilder.schema import Activity
66

@@ -15,6 +15,7 @@
1515
from .government_constants import GovernmentConstants
1616
from .government_channel_validation import GovernmentChannelValidation
1717
from .skill_validation import SkillValidation
18+
from .channel_provider import ChannelProvider
1819

1920

2021
class JwtTokenValidation:
@@ -25,7 +26,7 @@ async def authenticate_request(
2526
activity: Activity,
2627
auth_header: str,
2728
credentials: CredentialProvider,
28-
channel_service: str = "",
29+
channel_service_or_provider: Union[str, ChannelProvider] = "",
2930
) -> ClaimsIdentity:
3031
"""Authenticates the request and sets the service url in the set of trusted urls.
3132
:param activity: The incoming Activity from the Bot Framework or the Emulator
@@ -51,7 +52,7 @@ async def authenticate_request(
5152
claims_identity = await JwtTokenValidation.validate_auth_header(
5253
auth_header,
5354
credentials,
54-
channel_service,
55+
channel_service_or_provider,
5556
activity.channel_id,
5657
activity.service_url,
5758
)
@@ -65,71 +66,102 @@ async def authenticate_request(
6566
async def validate_auth_header(
6667
auth_header: str,
6768
credentials: CredentialProvider,
68-
channel_service: str,
69+
channel_service_or_provider: Union[str, ChannelProvider],
6970
channel_id: str,
7071
service_url: str = None,
7172
auth_configuration: AuthenticationConfiguration = None,
7273
) -> ClaimsIdentity:
7374
if not auth_header:
7475
raise ValueError("argument auth_header is null")
7576

76-
if SkillValidation.is_skill_token(auth_header):
77-
return await SkillValidation.authenticate_channel_token(
78-
auth_header,
79-
credentials,
80-
channel_service,
81-
channel_id,
82-
auth_configuration,
83-
)
84-
85-
if EmulatorValidation.is_token_from_emulator(auth_header):
86-
return await EmulatorValidation.authenticate_emulator_token(
87-
auth_header, credentials, channel_service, channel_id
88-
)
89-
90-
# If the channel is Public Azure
91-
if not channel_service:
92-
if service_url:
93-
return await ChannelValidation.authenticate_channel_token_with_service_url(
77+
async def get_claims() -> ClaimsIdentity:
78+
if SkillValidation.is_skill_token(auth_header):
79+
return await SkillValidation.authenticate_channel_token(
9480
auth_header,
9581
credentials,
96-
service_url,
82+
channel_service_or_provider,
9783
channel_id,
9884
auth_configuration,
9985
)
10086

101-
return await ChannelValidation.authenticate_channel_token(
102-
auth_header, credentials, channel_id, auth_configuration
87+
if EmulatorValidation.is_token_from_emulator(auth_header):
88+
return await EmulatorValidation.authenticate_emulator_token(
89+
auth_header, credentials, channel_service_or_provider, channel_id
90+
)
91+
92+
is_public = (
93+
not channel_service_or_provider
94+
or isinstance(channel_service_or_provider, ChannelProvider)
95+
and channel_service_or_provider.is_public_azure()
10396
)
97+
is_gov = (
98+
isinstance(channel_service_or_provider, ChannelProvider)
99+
and channel_service_or_provider.is_public_azure()
100+
or isinstance(channel_service_or_provider, str)
101+
and JwtTokenValidation.is_government(channel_service_or_provider)
102+
)
103+
104+
# If the channel is Public Azure
105+
if is_public:
106+
if service_url:
107+
return await ChannelValidation.authenticate_channel_token_with_service_url(
108+
auth_header,
109+
credentials,
110+
service_url,
111+
channel_id,
112+
auth_configuration,
113+
)
114+
115+
return await ChannelValidation.authenticate_channel_token(
116+
auth_header, credentials, channel_id, auth_configuration
117+
)
104118

105-
if JwtTokenValidation.is_government(channel_service):
119+
if is_gov:
120+
if service_url:
121+
return await GovernmentChannelValidation.authenticate_channel_token_with_service_url(
122+
auth_header,
123+
credentials,
124+
service_url,
125+
channel_id,
126+
auth_configuration,
127+
)
128+
129+
return await GovernmentChannelValidation.authenticate_channel_token(
130+
auth_header, credentials, channel_id, auth_configuration
131+
)
132+
133+
# Otherwise use Enterprise Channel Validation
106134
if service_url:
107-
return await GovernmentChannelValidation.authenticate_channel_token_with_service_url(
135+
return await EnterpriseChannelValidation.authenticate_channel_token_with_service_url(
108136
auth_header,
109137
credentials,
110138
service_url,
111139
channel_id,
140+
channel_service_or_provider,
112141
auth_configuration,
113142
)
114143

115-
return await GovernmentChannelValidation.authenticate_channel_token(
116-
auth_header, credentials, channel_id, auth_configuration
117-
)
118-
119-
# Otherwise use Enterprise Channel Validation
120-
if service_url:
121-
return await EnterpriseChannelValidation.authenticate_channel_token_with_service_url(
144+
return await EnterpriseChannelValidation.authenticate_channel_token(
122145
auth_header,
123146
credentials,
124-
service_url,
125147
channel_id,
126-
channel_service,
148+
channel_service_or_provider,
127149
auth_configuration,
128150
)
129151

130-
return await EnterpriseChannelValidation.authenticate_channel_token(
131-
auth_header, credentials, channel_id, channel_service, auth_configuration
132-
)
152+
claims = await get_claims()
153+
154+
if claims:
155+
await JwtTokenValidation.validate_claims(auth_configuration, claims.claims)
156+
157+
return claims
158+
159+
@staticmethod
160+
async def validate_claims(
161+
auth_config: AuthenticationConfiguration, claims: List[Dict]
162+
):
163+
if auth_config and auth_config.claims_validator:
164+
await auth_config.claims_validator(claims)
133165

134166
@staticmethod
135167
def is_government(channel_service: str) -> bool:
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line n 325D umberDiff line change
@@ -0,0 +1,25 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
from .channel_provider import ChannelProvider
5+
from .government_constants import GovernmentConstants
6+
7+
8+
class SimpleChannelProvider(ChannelProvider):
9+
"""
10+
ChannelProvider interface. This interface allows Bots to provide their own
11+
implementation for the configuration parameters to connect to a Bot.
12+
Framework channel service.
13+
"""
14+
15+
def __init__(self, channel_service: str = None):
16+
self.channel_service = channel_service
17+
18+
async def get_channel_service(self) -> str:
19+
return self.channel_service
20+
21+
def is_government(self) -> bool:
22+
return self.channel_service == GovernmentConstants.CHANNEL_SERVICE
23+
24+
def is_public_azure(self) -> bool:
25+
return not self.channel_service

libraries/botframework-connector/botframework/connector/auth/skill_validation.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# Licensed under the MIT License.
33

44
from datetime import timedelta
5-
from typing import Dict
5+
from typing import Dict, Union
66

77
import jwt
88

@@ -13,6 +13,7 @@
1313
from .government_constants import GovernmentConstants
1414
from .verify_options import VerifyOptions
1515
from .jwt_token_extractor import JwtTokenExtractor
16+
from .channel_provider import ChannelProvider
1617

1718

1819
class SkillValidation:
@@ -88,7 +89,7 @@ def is_skill_claim(claims: Dict[str, object]) -> bool:
8889
async def authenticate_channel_token(
8990
auth_header: str,
9091
credentials: CredentialProvider,
91-
channel_service: str,
92+
channel_service_or_provider: Union[str, ChannelProvider],
9293
channel_id: str,
9394
auth_configuration: AuthenticationConfiguration,
9495
) -> ClaimsIdentity:
@@ -99,9 +100,14 @@ async def authenticate_channel_token(
99100

100101
from .jwt_token_validation import JwtTokenValidation
101102

103+
if isinstance(channel_service_or_provider, ChannelProvider):
104+
is_gov = channel_service_or_provider.is_government()
105+
else:
106+
is_gov = JwtTokenValidation.is_government(channel_service_or_provider)
107+
102108
open_id_metadata_url = (
103109
GovernmentConstants.TO_BOT_FROM_EMULATOR_OPEN_ID_METADATA_URL
104-
if channel_service and JwtTokenValidation.is_government(channel_service)
110+
if is_gov
105111
else AuthenticationConstants.TO_BOT_FROM_EMULATOR_OPEN_ID_METADATA_URL
106112
)
107113

0 commit comments

Comments
 (0)
0