From e0b163cfb6408113a290a122a6576567a6e82664 Mon Sep 17 00:00:00 2001 From: skorn95 Date: Thu, 7 Dec 2023 12:48:19 -0500 Subject: [PATCH 1/3] Full support for GCC High Sharepoint Environments --- .../runtime/auth/authentication_context.py | 3 ++- .../auth/providers/acs_token_provider.py | 20 +++++++++----- office365/sharepoint/client_context.py | 26 +++++++++++-------- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/office365/runtime/auth/authentication_context.py b/office365/runtime/auth/authentication_context.py index f0222593..3e8e44a0 100644 --- a/office365/runtime/auth/authentication_context.py +++ b/office365/runtime/auth/authentication_context.py @@ -173,8 +173,9 @@ def with_credentials(self, credentials, **kwargs): :param UserCredential or ClientCredential credentials: """ if isinstance(credentials, ClientCredential): + environment = kwargs.get("environment") provider = ACSTokenProvider( - self.url, credentials.clientId, credentials.clientSecret + self.url, credentials.clientId, credentials.clientSecret, environment ) elif isinstance(credentials, UserCredential): allow_ntlm = kwargs.get("allow_ntlm", False) diff --git a/office365/runtime/auth/providers/acs_token_provider.py b/office365/runtime/auth/providers/acs_token_provider.py index 1be5fd2f..a712e85e 100644 --- a/office365/runtime/auth/providers/acs_token_provider.py +++ b/office365/runtime/auth/providers/acs_token_provider.py @@ -10,13 +10,15 @@ class ACSTokenProvider(AuthenticationProvider, office365.logger.LoggerContext): - def __init__(self, url, client_id, client_secret): + def __init__(self, url, client_id, client_secret, environment): """ Provider to acquire the access token from a Microsoft Azure Access Control Service (ACS) :param str client_id: The OAuth client id of the calling application. :param str client_secret: Secret string that the application uses to prove its identity when requesting a token :param str url: SharePoint web or site url + :param str environment: The Office 365 Cloud Environment endpoint used for authentication + defaults to 'commercial'. """ self.url = url self.redirect_url = None @@ -25,6 +27,7 @@ def __init__(self, url, client_id, client_secret): self._client_id = client_id self._client_secret = client_secret self._cached_token = None + self._environment = environment def authenticate_request(self, request): # type: (RequestOptions) -> None @@ -62,7 +65,7 @@ def _get_app_only_access_token(self, target_host, target_realm): self.SharePointPrincipal, target_host, target_realm ) principal_id = self.get_formatted_principal(self._client_id, None, target_realm) - sts_url = self.get_security_token_service_url(target_realm) + sts_url = self.get_security_token_service_url(target_realm, environment=self._environment) oauth2_request = { "grant_type": "client_credentials", "client_id": principal_id, @@ -101,10 +104,15 @@ def get_formatted_principal(principal_name, host_name, realm): return "{0}@{1}".format(principal_name, realm) @staticmethod - def get_security_token_service_url(realm): - return "https://accounts.accesscontrol.windows.net/{0}/tokens/OAuth/2".format( - realm - ) + def get_security_token_service_url(realm, environment): + if environment == "GCCH": + return "https://login.microsoftonline.us/{0}/tokens/OAuth/2".format( + realm + ) + else: + return "https://accounts.accesscontrol.windows.net/{0}/tokens/OAuth/2".format( + realm + ) def _get_authorization_header(self): return "Bearer {0}".format(self._cached_token.accessToken) diff --git a/office365/sharepoint/client_context.py b/office365/sharepoint/client_context.py index b3debb22..10951264 100644 --- a/office365/sharepoint/client_context.py +++ b/office365/sharepoint/client_context.py @@ -81,7 +81,7 @@ def with_client_certificate( thumbprint, cert_path=None, private_key=None, - scopes=None, + scopes=None ): # type: (str, str, str, Optional[str], Optional[str], Optional[List[str]]) -> Self """ @@ -93,7 +93,6 @@ def with_client_certificate( :param str thumbprint: Hex encoded thumbprint of the certificate. :param str client_id: The OAuth client id of the calling application. :param list[str] or None scopes: Scopes requested to access a protected API (a resource) - """ self.authentication_context.with_client_certificate( tenant, client_id, thumbprint, cert_path, private_key, scopes @@ -139,15 +138,15 @@ def with_access_token(self, token_func): def with_user_credentials( self, username, password, allow_ntlm=False, browser_mode=False, environment='commercial' ): - # type: (str, str, bool, bool, str) -> Self + # type: (str, str, bool, bool, Optional[str]) -> Self """ Initializes a client to acquire a token via user credentials. :param str username: Typically, a UPN in the form of an email address :param str password: The password :param bool allow_ntlm: Flag indicates whether NTLM scheme is enabled. Disabled by default :param bool browser_mode: - :param str environment: The Office 365 Cloud Environment endpoint used for authentication. - By default, this will be set to commercial ('commercial', 'GCCH') + :param str environment: The Office 365 Cloud Environment endpoint used for authentication + defaults to 'commercial'. """ self.authentication_context.with_credentials( UserCredential(username, password), @@ -157,8 +156,8 @@ def with_user_credentials( ) return self - def with_client_credentials(self, client_id, client_secret): - # type: (str, str) -> Self + def with_client_credentials(self, client_id, client_secret, environment='commercial'): + # type: (str, str, Optional[str]) -> Self """ Initializes a client to acquire a token via client credentials (SharePoint App-Only) @@ -167,19 +166,24 @@ def with_client_credentials(self, client_id, client_secret): :param str client_id: The OAuth client id of the calling application :param str client_secret: Secret string that the application uses to prove its identity when requesting a token + :param str environment: The Office 365 Cloud Environment endpoint used for authentication + defaults to 'commercial'. """ self.authentication_context.with_credentials( - ClientCredential(client_id, client_secret) + ClientCredential(client_id, client_secret), + environment=environment ) return self - def with_credentials(self, credentials): - # type: (UserCredential|ClientCredential) -> Self + def with_credentials(self, credentials, environment='commercial'): + # type: (UserCredential|ClientCredential, Optional[str]) -> Self """ Initializes a client to acquire a token via user or client credentials :type credentials: UserCredential or ClientCredential + :param str environment: The Office 365 Cloud Environment endpoint used for authentication + defaults to 'commercial'. """ - self.authentication_context.with_credentials(credentials) + self.authentication_context.with_credentials(credentials, environment=environment) return self def execute_batch(self, items_per_batch=100, success_callback=None): From 460876470a3580179af2f6164d367378a2cca3b5 Mon Sep 17 00:00:00 2001 From: skorn95 Date: Thu, 7 Dec 2023 12:48:45 -0500 Subject: [PATCH 2/3] Updated documentation for GCC High Sharepoint Support --- README.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f8e8a7af..b06bebae 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,6 @@ pip install Office365-REST-Python-Client ### Note > - >Alternatively the _latest_ version could be directly installed via GitHub: >``` >pip install git+https://github.com/vgrem/Office365-REST-Python-Client.git @@ -175,6 +174,20 @@ The list of examples: Refer [examples section](examples/sharepoint) for another scenarios +### Support for non-standard SharePoint Online Environments + + Support for non-standard SharePoint Environments is currently being implemented. Currently supported: + - GCC High + + To enable authentication to GCC High endpoints, add the `environment='GCCH'` parameter when calling the + `ClientContext class` with `.with_user_credentials`, `.with_client_credentials`, or `.with_credentials` + + Example: + ```python + client_credentials = ClientCredential('{client_id}','{client_secret}') + ctx = ClientContext('{url}').with_credentials(client_credentials, environment='GCCH') + ``` + # Working with Outlook API The list of supported APIs: From 185ba127e1111462611852fb141fbe8d28cced11 Mon Sep 17 00:00:00 2001 From: skorn95 Date: Thu, 7 Dec 2023 13:16:55 -0500 Subject: [PATCH 3/3] Resolving no value for argument when calling ACSTokenProvider class outside ClientContext --- office365/runtime/auth/providers/acs_token_provider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/office365/runtime/auth/providers/acs_token_provider.py b/office365/runtime/auth/providers/acs_token_provider.py index a712e85e..ce17361d 100644 --- a/office365/runtime/auth/providers/acs_token_provider.py +++ b/office365/runtime/auth/providers/acs_token_provider.py @@ -10,7 +10,7 @@ class ACSTokenProvider(AuthenticationProvider, office365.logger.LoggerContext): - def __init__(self, url, client_id, client_secret, environment): + def __init__(self, url, client_id, client_secret, environment='commercial'): """ Provider to acquire the access token from a Microsoft Azure Access Control Service (ACS)