8000 Merge branch 'axsuarez/skills-lower-layers' of https://github.com/Mic… · rusty0209/botbuilder-python@69b8588 · GitHub
[go: up one dir, main page]

Skip to content

Commit 69b8588

Browse files
committed
Merge branch 'axsuarez/skills-lower-layers' of https://github.com/Microsoft/botbuilder-python into axsuarez/skills-lower-layers
2 parents 0b4cf47 + 75d1b51 commit 69b8588

File tree

9 files changed

+261
-57
lines changed

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 numberDiff 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