Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-msal for openSUSE:Factory checked in at 2023-08-14 22:36:11 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-msal (Old) and /work/SRC/openSUSE:Factory/.python-msal.new.11712 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-msal" Mon Aug 14 22:36:11 2023 rev:15 rq:1103887 version:1.24.0~b1 Changes: -------- --- /work/SRC/openSUSE:Factory/python-msal/python-msal.changes 2023-04-19 17:44:48.524832059 +0200 +++ /work/SRC/openSUSE:Factory/.python-msal.new.11712/python-msal.changes 2023-08-14 22:36:28.504689044 +0200 @@ -1,0 +2,11 @@ +Mon Aug 14 13:29:06 UTC 2023 - John Paul Adrian Glaubitz <adrian.glaub...@suse.com> + +- Update to version 1.24.0b1 + + Experimental: Surface MSAL telemetry as a long opaque string (#575). + This behavior is useful if your app has your own telemetry mechanism + and wants to also collect MSAL's telemetry. +- from version 1.23.0 + + acquire_token_for_client() will automatically look up tokens from cache (#577) +- Override upstream version with 1.24.0~b1 + +------------------------------------------------------------------- Old: ---- msal-1.22.0.tar.gz New: ---- msal-1.24.0b1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-msal.spec ++++++ --- /var/tmp/diff_new_pack.P2EZln/_old 2023-08-14 22:36:29.192693418 +0200 +++ /var/tmp/diff_new_pack.P2EZln/_new 2023-08-14 22:36:29.196693443 +0200 @@ -16,18 +16,20 @@ # +%define realversion 1.24.0b1 + %{?!python_module:%define python_module() python-%{**} python3-%{**}} %if 0%{?suse_version} >= 1500 %define skip_python2 1 %endif Name: python-msal -Version: 1.22.0 +Version: 1.24.0~b1 Release: 0 Summary: Microsoft Authentication Library (MSAL) for Python License: MIT Group: Development/Languages/Python URL: https://github.com/AzureAD/microsoft-authentication-library-for-python -Source: https://files.pythonhosted.org/packages/source/m/msal/msal-%{version}.tar.gz +Source: https://files.pythonhosted.org/packages/source/m/msal/msal-%{realversion}.tar.gz BuildRequires: %{python_module devel} BuildRequires: %{python_module setuptools} BuildRequires: fdupes @@ -49,7 +51,7 @@ standard OAuth2 and OpenID Connect. %prep -%setup -q -n msal-%{version} +%setup -q -n msal-%{realversion} %build %python_build ++++++ msal-1.22.0.tar.gz -> msal-1.24.0b1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/msal-1.22.0/PKG-INFO new/msal-1.24.0b1/PKG-INFO --- old/msal-1.22.0/PKG-INFO 2023-04-17 18:56:23.226273800 +0200 +++ new/msal-1.24.0b1/PKG-INFO 2023-07-24 11:38:52.279546500 +0200 @@ -1,14 +1,14 @@ Metadata-Version: 2.1 Name: msal -Version: 1.22.0 -Summary: The Microsoft Authentication Library (MSAL) for Python library enables your app to access the Microsoft Cloud by supporting authentication of users with Microsoft Azure Active Directory accounts (AAD) and Microsoft Accounts (MSA) using industry standard OAuth2 and OpenID Connect. +Version: 1.24.0b1 +Summary: The Microsoft Authentication Library (MSAL) for Python library Home-page: https://github.com/AzureAD/microsoft-authentication-library-for-python Author: Microsoft Corporation Author-email: nuget...@microsoft.com License: MIT Project-URL: Changelog, https://github.com/AzureAD/microsoft-authentication-library-for-python/releases Project-URL: Documentation, https://msal-python.readthedocs.io/ -Project-URL: Questions, https://stackoverflow.com/questions/tagged/msal+python +Project-URL: Questions, https://stackoverflow.com/questions/tagged/azure-ad-msal+python Project-URL: Feature/Bug Tracker, https://github.com/AzureAD/microsoft-authentication-library-for-python/issues Classifier: Development Status :: 5 - Production/Stable Classifier: Programming Language :: Python @@ -24,6 +24,7 @@ Classifier: Programming Language :: Python :: 3.11 Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent +Requires-Python: >=2.7 Description-Content-Type: text/markdown Provides-Extra: broker License-File: LICENSE diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/msal-1.22.0/msal/application.py new/msal-1.24.0b1/msal/application.py --- old/msal-1.22.0/msal/application.py 2023-04-17 18:56:13.000000000 +0200 +++ new/msal-1.24.0b1/msal/application.py 2023-07-24 11:38:42.000000000 +0200 @@ -25,7 +25,7 @@ # The __init__.py will import this. Not the other way around. -__version__ = "1.22.0" # When releasing, also check and bump our dependencies's versions if needed +__version__ = "1.24.0b1" # When releasing, also check and bump our dependencies's versions if needed logger = logging.getLogger(__name__) _AUTHORITY_TYPE_CLOUDSHELL = "CLOUDSHELL" @@ -65,8 +65,19 @@ return raw +def _pii_less_home_account_id(home_account_id): + parts = home_account_id.split(".") # It could contain one or two parts + parts[0] = "********" + return ".".join(parts) + + def _clean_up(result): if isinstance(result, dict): + if "_msalruntime_telemetry" in result or "_msal_python_telemetry" in result: + result["msal_telemetry"] = json.dumps({ # Telemetry as an opaque string + "msalruntime_telemetry": result.get("_msalruntime_telemetry"), + "msal_python_telemetry": result.get("_msal_python_telemetry"), + }, separators=(",", ":")) return { k: result[k] for k in result if k != "refresh_in" # MSAL handled refresh_in, customers need not @@ -150,6 +161,9 @@ class ClientApplication(object): + """You do not usually directly use this class. Use its subclasses instead: + :class:`PublicClientApplication` and :class:`ConfidentialClientApplication`. + """ ACQUIRE_TOKEN_SILENT_ID = "84" ACQUIRE_TOKEN_BY_REFRESH_TOKEN = "85" ACQUIRE_TOKEN_BY_USERNAME_PASSWORD_ID = "301" @@ -313,7 +327,7 @@ to keep their traffic remain inside that region. As of 2021 May, regional service is only available for - ``acquire_token_for_client()`` sent by any of the following scenarios:: + ``acquire_token_for_client()`` sent by any of the following scenarios: 1. An app powered by a capable MSAL (MSAL Python 1.12+ will be provisioned) @@ -444,6 +458,8 @@ New in version 1.19.0. :param boolean allow_broker: + This parameter is NOT applicable to :class:`ConfidentialClientApplication`. + A broker is a component installed on your device. Broker implicitly gives your device an identity. By using a broker, your device becomes a factor that can satisfy MFA (Multi-factor authentication). @@ -756,9 +772,9 @@ Can be one of "consumers" or "organizations" or your tenant domain "contoso.com". If included, it will skip the email-based discovery process that user goes through on the sign-in page, leading to a slightly more streamlined user experience. - More information on possible values - `here <https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow#request-an-authorization-code>`_ and - `here <https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-oapx/86fb452d-e34a-494e-ac61-e526e263b6d8>`_. + More information on possible values available in + `Auth Code Flow doc <https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow#request-an-authorization-code>`_ and + `domain_hint doc <https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-oapx/86fb452d-e34a-494e-ac61-e526e263b6d8>`_. :param int max_age: OPTIONAL. Maximum Authentication Age. @@ -796,7 +812,7 @@ "...": "...", // Everything else are reserved and internal } - The caller is expected to:: + The caller is expected to: 1. somehow store this content, typically inside the current session, 2. guide the end user (i.e. resource owner) to visit that auth_uri, @@ -860,9 +876,9 @@ Can be one of "consumers" or "organizations" or your tenant domain "contoso.com". If included, it will skip the email-based discovery process that user goes through on the sign-in page, leading to a slightly more streamlined user experience. - More information on possible values - `here <https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow#request-an-authorization-code>`_ and - `here <https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-oapx/86fb452d-e34a-494e-ac61-e526e263b6d8>`_. + More information on possible values available in + `Auth Code Flow doc <https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow#request-an-authorization-code>`_ and + `domain_hint doc <https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-oapx/86fb452d-e34a-494e-ac61-e526e263b6d8>`_. :param claims_challenge: The claims_challenge parameter requests specific claims requested by the resource provider in the form of a claims_challenge directive in the www-authenticate header to be @@ -955,7 +971,7 @@ self._validate_ssh_cert_input_data(kwargs.get("data", {})) telemetry_context = self._build_telemetry_context( self.ACQUIRE_TOKEN_BY_AUTHORIZATION_CODE_ID) - response =_clean_up(self.client.obtain_token_by_auth_code_flow( + response = _clean_up(self.client.obtain_token_by_auth_code_flow( auth_code_flow, auth_response, scope=self._decorate_scope(scopes) if scopes else None, @@ -1198,32 +1214,24 @@ **kwargs): """Acquire an access token for given account, without user interaction. - It is done either by finding a valid access token from cache, - or by finding a valid refresh token from cache and then automatically - use it to redeem a new access token. - + It has same parameters as the :func:`~acquire_token_silent_with_error`. + The difference is the behavior of the return value. This method will combine the cache empty and refresh error into one return value, `None`. If your app does not care about the exact token refresh error during token cache look-up, then this method is easier and recommended. - Internally, this method calls :func:`~acquire_token_silent_with_error`. - - :param claims_challenge: - The claims_challenge parameter requests specific claims requested by the resource provider - in the form of a claims_challenge directive in the www-authenticate header to be - returned from the UserInfo Endpoint and/or in the ID Token and/or Access Token. - It is a string of a JSON object which contains lists of claims being requested from these locations. - :return: - A dict containing no "error" key, and typically contains an "access_token" key, if cache lookup succeeded. - None when cache lookup does not yield a token. """ - result = self.acquire_token_silent_with_error( + if not account: + return None # A backward-compatible NO-OP to drop the account=None usage + result = _clean_up(self._acquire_token_silent_with_error( scopes, account, authority=authority, force_refresh=force_refresh, - claims_challenge=claims_challenge, **kwargs) + claims_challenge=claims_challenge, **kwargs)) return result if result and "error" not in result else None def acquire_token_silent_with_error( @@ -1247,9 +1255,10 @@ :param list[str] scopes: (Required) Scopes requested to access a protected API (a resource). - :param account: - one of the account object returned by :func:`~get_accounts`, - or use None when you want to find an access token for this client. + :param account: (Required) + One of the account object returned by :func:`~get_accounts`. + Starting from MSAL Python 1.23, + a ``None`` input will become a NO-OP and always return ``None``. :param force_refresh: If True, it will skip Access Token look-up, and try to find a Refresh Token to obtain a new Access Token. @@ -1265,6 +1274,20 @@ - None when there is simply no token in the cache. - A dict containing an "error" key, when token refresh failed. """ + if not account: + return None # A backward-compatible NO-OP to drop the account=None usage + return _clean_up(self._acquire_token_silent_with_error( + scopes, account, authority=authority, force_refresh=force_refresh, + claims_challenge=claims_challenge, **kwargs)) + + def _acquire_token_silent_with_error( + self, + scopes, # type: List[str] + account, # type: Optional[Account] + authority=None, # See get_authorization_request_url() + force_refresh=False, # type: Optional[boolean] + claims_challenge=None, + **kwargs): assert isinstance(scopes, list), "Invalid parameter type" self._validate_ssh_cert_input_data(kwargs.get("data", {})) correlation_id = msal.telemetry._get_new_correlation_id() @@ -1324,7 +1347,11 @@ force_refresh=False, # type: Optional[boolean] claims_challenge=None, correlation_id=None, + http_exceptions=None, **kwargs): + # This internal method has two calling patterns: + # it accepts a non-empty account to find token for a user, + # and accepts account=None to find a token for the current app. access_token_from_cache = None if not (force_refresh or claims_challenge): # Bypass AT when desired or using claims query={ @@ -1361,6 +1388,10 @@ else: refresh_reason = msal.telemetry.FORCE_REFRESH # TODO: It could also mean claims_challenge assert refresh_reason, "It should have been established at this point" + if not http_exceptions: # It can be a tuple of exceptions + # The exact HTTP exceptions are transportation-layer dependent + from requests.exceptions import RequestException # Lazy load + http_exceptions = (RequestException,) try: data = kwargs.get("data", {}) if account and account.get("authority_type") == _AUTHORITY_TYPE_CLOUDSHELL: @@ -1380,14 +1411,19 @@ if response: # The broker provided a decisive outcome, so we use it return self._process_broker_response(response, scopes, data) - result = _clean_up(self._acquire_token_silent_by_finding_rt_belongs_to_me_or_my_family( - authority, self._decorate_scope(scopes), account, - refresh_reason=refresh_reason, claims_challenge=claims_challenge, - correlation_id=correlation_id, - **kwargs)) + if account: + result = self._acquire_token_silent_by_finding_rt_belongs_to_me_or_my_family( + authority, self._decorate_scope(scopes), account, + refresh_reason=refresh_reason, claims_challenge=claims_challenge, + correlation_id=correlation_id, + **kwargs) + else: # The caller is acquire_token_for_client() + result = self._acquire_token_for_client( + scopes, refresh_reason, claims_challenge=claims_challenge, + **kwargs) if (result and "error" not in result) or (not access_token_from_cache): return result - except: # The exact HTTP exception is transportation-layer dependent + except http_exceptions: # Typically network error. Potential AAD outage? if not access_token_from_cache: # It means there is no fall back option raise # We choose to bubble up the exception @@ -1458,7 +1494,10 @@ self.token_cache.CredentialType.REFRESH_TOKEN, # target=scopes, # AAD RTs are scope-independent query=query) - logger.debug("Found %d RTs matching %s", len(matches), query) + logger.debug("Found %d RTs matching %s", len(matches), { + k: _pii_less_home_account_id(v) if k == "home_account_id" and v else v + for k, v in query.items() + }) response = None # A distinguishable value to mean cache is empty if not matches: # Then exit early to avoid expensive operations @@ -1671,6 +1710,9 @@ CONSOLE_WINDOW_HANDLE = object() def __init__(self, client_id, client_credential=None, **kwargs): + """Same as :func:`ClientApplication.__init__`, + except that ``client_credential`` parameter shall remain ``None``. + """ if client_credential is not None: raise ValueError("Public Client should not possess credentials") super(PublicClientApplication, self).__init__( @@ -1711,9 +1753,9 @@ Can be one of "consumers" or "organizations" or your tenant domain "contoso.com". If included, it will skip the email-based discovery process that user goes through on the sign-in page, leading to a slightly more streamlined user experience. - More information on possible values - `here <https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow#request-an-authorization-code>`_ and - `here <https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-oapx/86fb452d-e34a-494e-ac61-e526e263b6d8>`_. + More information on possible values available in + `Auth Code Flow doc <https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow#request-an-authorization-code>`_ and + `domain_hint doc <https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-oapx/86fb452d-e34a-494e-ac61-e526e263b6d8>`_. :param claims_challenge: The claims_challenge parameter requests specific claims requested by the resource provider @@ -1983,10 +2025,16 @@ class ConfidentialClientApplication(ClientApplication): # server-side web app + """Same as :func:`ClientApplication.__init__`, + except that ``allow_broker`` parameter shall remain ``None``. + """ def acquire_token_for_client(self, scopes, claims_challenge=None, **kwargs): """Acquires token for the current confidential client, not for an end user. + Since MSAL Python 1.23, it will automatically look for token from cache, + and only send request to Identity Provider when cache misses. + :param list[str] scopes: (Required) Scopes requested to access a protected API (a resource). :param claims_challenge: @@ -2000,7 +2048,20 @@ - A successful response would contain "access_token" key, - an error response would contain "error" and usually "error_description". """ - # TBD: force_refresh behavior + if kwargs.get("force_refresh"): + raise ValueError( # We choose to disallow force_refresh + "Historically, this method does not support force_refresh behavior. " + ) + return _clean_up(self._acquire_token_silent_with_error( + scopes, None, claims_challenge=claims_challenge, **kwargs)) + + def _acquire_token_for_client( + self, + scopes, + refresh_reason, + claims_challenge=None, + **kwargs + ): if self.authority.tenant.lower() in ["common", "organizations"]: warnings.warn( "Using /common or /organizations authority " @@ -2008,16 +2069,16 @@ "Please use a specific tenant instead.", DeprecationWarning) self._validate_ssh_cert_input_data(kwargs.get("data", {})) telemetry_context = self._build_telemetry_context( - self.ACQUIRE_TOKEN_FOR_CLIENT_ID) + self.ACQUIRE_TOKEN_FOR_CLIENT_ID, refresh_reason=refresh_reason) client = self._regional_client or self.client - response = _clean_up(client.obtain_token_for_client( + response = client.obtain_token_for_client( scope=scopes, # This grant flow requires no scope decoration headers=telemetry_context.generate_headers(), data=dict( kwargs.pop("data", {}), claims=_merge_claims_challenge_and_capabilities( self._client_capabilities, claims_challenge)), - **kwargs)) + **kwargs) telemetry_context.update_telemetry(response) return response diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/msal-1.22.0/msal/broker.py new/msal-1.24.0b1/msal/broker.py --- old/msal-1.22.0/msal/broker.py 2023-04-17 18:56:13.000000000 +0200 +++ new/msal-1.24.0b1/msal/broker.py 2023-07-24 11:38:42.000000000 +0200 @@ -23,8 +23,7 @@ except (ImportError, AttributeError): # AttributeError happens when a prior pymsalruntime uninstallation somehow leaved an empty folder behind # PyMsalRuntime currently supports these Windows versions, listed in this MSFT internal link # https://github.com/AzureAD/microsoft-authentication-library-for-cpp/pull/2406/files - raise ImportError( # TODO: Remove or adjust this line right before merging this PR - 'You need to install dependency by: pip install "msal[broker]>=1.20,<2"') + raise ImportError('You need to install dependency by: pip install "msal[broker]>=1.20,<2"') # It could throw RuntimeError when running on ancient versions of Windows @@ -84,9 +83,11 @@ def _convert_result(result, client_id, expected_token_type=None): # Mimic an on-the-wire response from AAD + telemetry = result.get_telemetry_data() + telemetry.pop("wam_telemetry", None) # In pymsalruntime 0.13, it contains PII "account_id" error = result.get_error() if error: - return _convert_error(error, client_id) + return dict(_convert_error(error, client_id), _msalruntime_telemetry=telemetry) id_token_claims = json.loads(result.get_id_token()) if result.get_id_token() else {} account = result.get_account() assert account, "Account is expected to be always available" @@ -107,7 +108,7 @@ granted_scopes = result.get_granted_scopes() # New in pymsalruntime 0.3.x if granted_scopes: return_value["scope"] = " ".join(granted_scopes) # Mimic the on-the-wire data format - return return_value + return dict(return_value, _msalruntime_telemetry=telemetry) def _get_new_correlation_id(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/msal-1.22.0/msal/oauth2cli/http.py new/msal-1.24.0b1/msal/oauth2cli/http.py --- old/msal-1.22.0/msal/oauth2cli/http.py 2023-04-17 18:56:13.000000000 +0200 +++ new/msal-1.24.0b1/msal/oauth2cli/http.py 2023-07-24 11:38:42.000000000 +0200 @@ -58,6 +58,11 @@ # but a `text` would be more generic, # when downstream packages would potentially access some XML endpoints. + headers = {} # Duplicated headers are expected to be combined into one header + # with its value as a comma-separated string. + # https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.2 + # Popular HTTP libraries model it as a case-insensitive dict. + def raise_for_status(self): """Raise an exception when http response status contains error""" raise NotImplementedError("Your implementation should provide this") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/msal-1.22.0/msal/token_cache.py new/msal-1.24.0b1/msal/token_cache.py --- old/msal-1.22.0/msal/token_cache.py 2023-04-17 18:56:13.000000000 +0200 +++ new/msal-1.24.0b1/msal/token_cache.py 2023-07-24 11:38:42.000000000 +0200 @@ -102,7 +102,6 @@ ] def add(self, event, now=None): - # type: (dict) -> None """Handle a token obtaining event, and add tokens into cache.""" def make_clean_copy(dictionary, sensitive_fields): # Masks sensitive info return { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/msal-1.22.0/msal.egg-info/PKG-INFO new/msal-1.24.0b1/msal.egg-info/PKG-INFO --- old/msal-1.22.0/msal.egg-info/PKG-INFO 2023-04-17 18:56:23.000000000 +0200 +++ new/msal-1.24.0b1/msal.egg-info/PKG-INFO 2023-07-24 11:38:52.000000000 +0200 @@ -1,14 +1,14 @@ Metadata-Version: 2.1 Name: msal -Version: 1.22.0 -Summary: The Microsoft Authentication Library (MSAL) for Python library enables your app to access the Microsoft Cloud by supporting authentication of users with Microsoft Azure Active Directory accounts (AAD) and Microsoft Accounts (MSA) using industry standard OAuth2 and OpenID Connect. +Version: 1.24.0b1 +Summary: The Microsoft Authentication Library (MSAL) for Python library Home-page: https://github.com/AzureAD/microsoft-authentication-library-for-python Author: Microsoft Corporation Author-email: nuget...@microsoft.com License: MIT Project-URL: Changelog, https://github.com/AzureAD/microsoft-authentication-library-for-python/releases Project-URL: Documentation, https://msal-python.readthedocs.io/ -Project-URL: Questions, https://stackoverflow.com/questions/tagged/msal+python +Project-URL: Questions, https://stackoverflow.com/questions/tagged/azure-ad-msal+python Project-URL: Feature/Bug Tracker, https://github.com/AzureAD/microsoft-authentication-library-for-python/issues Classifier: Development Status :: 5 - Production/Stable Classifier: Programming Language :: Python @@ -24,6 +24,7 @@ Classifier: Programming Language :: Python :: 3.11 Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent +Requires-Python: >=2.7 Description-Content-Type: text/markdown Provides-Extra: broker License-File: LICENSE diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/msal-1.22.0/msal.egg-info/requires.txt new/msal-1.24.0b1/msal.egg-info/requires.txt --- old/msal-1.22.0/msal.egg-info/requires.txt 2023-04-17 18:56:23.000000000 +0200 +++ new/msal-1.24.0b1/msal.egg-info/requires.txt 2023-07-24 11:38:52.000000000 +0200 @@ -1,6 +1,6 @@ requests<3,>=2.0.0 PyJWT[crypto]<3,>=1.0.0 -cryptography<43,>=0.6 +cryptography<44,>=0.6 [:python_version < "3.3"] mock diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/msal-1.22.0/setup.cfg new/msal-1.24.0b1/setup.cfg --- old/msal-1.22.0/setup.cfg 2023-04-17 18:56:23.226273800 +0200 +++ new/msal-1.24.0b1/setup.cfg 2023-07-24 11:38:52.279546500 +0200 @@ -2,12 +2,62 @@ universal = 1 [metadata] +name = msal +version = attr: msal.__version__ +description = + The Microsoft Authentication Library (MSAL) for Python library + enables your app to access the Microsoft Cloud + by supporting authentication of users with + Microsoft Azure Active Directory accounts (AAD) and Microsoft Accounts (MSA) + using industry standard OAuth2 and OpenID Connect. +long_description = file: README.md +long_description_content_type = text/markdown +license = MIT +author = Microsoft Corporation +author_email = nuget...@microsoft.com +url = https://github.com/AzureAD/microsoft-authentication-library-for-python +classifiers = + Development Status :: 5 - Production/Stable + Programming Language :: Python + Programming Language :: Python :: 2 + Programming Language :: Python :: 2.7 + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.5 + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 + License :: OSI Approved :: MIT License + Operating System :: OS Independent project_urls = Changelog = https://github.com/AzureAD/microsoft-authentication-library-for-python/releases Documentation = https://msal-python.readthedocs.io/ - Questions = https://stackoverflow.com/questions/tagged/msal+python + Questions = https://stackoverflow.com/questions/tagged/azure-ad-msal+python Feature/Bug Tracker = https://github.com/AzureAD/microsoft-authentication-library-for-python/issues +[options] +include_package_data = False # We used to ship LICENSE, but our __init__.py already mentions MIT +packages = find: +python_requires = >=2.7 +install_requires = + requests>=2.0.0,<3 + + PyJWT[crypto]>=1.0.0,<3 + + cryptography>=0.6,<44 + + mock; python_version<'3.3' + +[options.extras_require] +broker = + pymsalruntime>=0.13.2,<0.14; python_version>='3.6' and platform_system=='Windows' + +[options.packages.find] +exclude = + tests + [egg_info] tag_build = tag_date = 0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/msal-1.22.0/setup.py new/msal-1.24.0b1/setup.py --- old/msal-1.22.0/setup.py 2023-04-17 18:56:13.000000000 +0200 +++ new/msal-1.24.0b1/setup.py 2023-07-24 11:38:42.000000000 +0200 @@ -1,101 +1,3 @@ -#!/usr/bin/env python -#------------------------------------------------------------------------------ -# -# Copyright (c) Microsoft Corporation. -# All rights reserved. -# -# This code is licensed under the MIT License. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files(the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions : -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -#------------------------------------------------------------------------------ - -from setuptools import setup, find_packages -import re, io - -# setup.py shall not import main package -__version__ = re.search( - r'__version__\s*=\s*[\'"]([^\'"]*)[\'"]', # It excludes inline comment too - io.open('msal/application.py', encoding='utf_8_sig').read() - ).group(1) - -long_description = open('README.md').read() - -setup( - name='msal', - version=__version__, - description=' '.join( - """The Microsoft Authentication Library (MSAL) for Python library - enables your app to access the Microsoft Cloud - by supporting authentication of users with - Microsoft Azure Active Directory accounts (AAD) and Microsoft Accounts (MSA) - using industry standard OAuth2 and OpenID Connect.""".split()), - long_description=long_description, - long_description_content_type="text/markdown", - license='MIT', - author='Microsoft Corporation', - author_email='nuget...@microsoft.com', - url='https://github.com/AzureAD/microsoft-authentication-library-for-python', - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Programming Language :: Python :: 3.11', - 'License :: OSI Approved :: MIT License', - 'Operating System :: OS Independent', - ], - packages=find_packages(exclude=["tests"]), - package_data={'': ['LICENSE']}, # Do not use data_files=[...], - # which would cause the LICENSE being copied to /usr/local, - # and tend to fail because of insufficient permission. - # See https://stackoverflow.com/a/14211600/728675 for more detail - install_requires=[ - 'requests>=2.0.0,<3', - 'PyJWT[crypto]>=1.0.0,<3', # MSAL does not use jwt.decode(), therefore is insusceptible to CVE-2022-29217 so no need to bump to PyJWT 2.4+ - - 'cryptography>=0.6,<43', - # load_pem_private_key() is available since 0.6 - # https://github.com/pyca/cryptography/blob/master/CHANGELOG.rst#06---2014-09-29 - # - # And we will use the cryptography (X+3).0.0 as the upper bound, - # based on their latest deprecation policy - # https://cryptography.io/en/latest/api-stability/#deprecation - - "mock;python_version<'3.3'", - ], - extras_require={ # It does not seem to work if being defined inside setup.cfg - "broker": [ - # The broker is defined as optional dependency, - # so that downstream apps can opt in. The opt-in is needed, partially because - # most existing MSAL Python apps do not have the redirect_uri needed by broker. - # MSAL Python uses a subset of API from PyMsalRuntime 0.11.2+, - # but we still bump the lower bound to 0.13.2+ for its important bugfix (https://github.com/AzureAD/microsoft-authentication-library-for-cpp/pull/3244) - "pymsalruntime>=0.13.2,<0.14;python_version>='3.6' and platform_system=='Windows'", - ], - }, -) +from setuptools import setup +setup() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/msal-1.22.0/tests/test_application.py new/msal-1.24.0b1/tests/test_application.py --- old/msal-1.22.0/tests/test_application.py 2023-04-17 18:56:13.000000000 +0200 +++ new/msal-1.24.0b1/tests/test_application.py 2023-07-24 11:38:42.000000000 +0200 @@ -382,8 +382,8 @@ old_at = "old AT" self.populate_cache(access_token=old_at, expires_in=3599, refresh_in=-1) def mock_post(url, headers=None, *args, **kwargs): - self.assertEqual("4|84,2|", (headers or {}).get(CLIENT_CURRENT_TELEMETRY)) - return MinimalResponse(status_code=400, text=json.dumps({"error": error})) + self.assertEqual("4|84,4|", (headers or {}).get(CLIENT_CURRENT_TELEMETRY)) + return MinimalResponse(status_code=400, text=json.dumps({"error": "foo"})) result = self.app.acquire_token_silent(['s1'], self.account, post=mock_post) self.assertEqual(old_at, result.get("access_token")) @@ -549,12 +549,31 @@ authority="https://login.microsoftonline.com/common") def test_acquire_token_for_client(self): - at = "this is an access token" def mock_post(url, headers=None, *args, **kwargs): - self.assertEqual("4|730,0|", (headers or {}).get(CLIENT_CURRENT_TELEMETRY)) - return MinimalResponse(status_code=200, text=json.dumps({"access_token": at})) + self.assertEqual("4|730,2|", (headers or {}).get(CLIENT_CURRENT_TELEMETRY)) + return MinimalResponse(status_code=200, text=json.dumps({ + "access_token": "AT 1", + "expires_in": 0, + })) result = self.app.acquire_token_for_client(["scope"], post=mock_post) - self.assertEqual(at, result.get("access_token")) + self.assertEqual("AT 1", result.get("access_token"), "Shall get a new token") + + def mock_post(url, headers=None, *args, **kwargs): + self.assertEqual("4|730,3|", (headers or {}).get(CLIENT_CURRENT_TELEMETRY)) + return MinimalResponse(status_code=200, text=json.dumps({ + "access_token": "AT 2", + "expires_in": 3600, + "refresh_in": -100, # A hack to make sure it will attempt refresh + })) + result = self.app.acquire_token_for_client(["scope"], post=mock_post) + self.assertEqual("AT 2", result.get("access_token"), "Shall get a new token") + + def mock_post(url, headers=None, *args, **kwargs): + # 1/0 # TODO: Make sure this was called + self.assertEqual("4|730,4|", (headers or {}).get(CLIENT_CURRENT_TELEMETRY)) + return MinimalResponse(status_code=400, text=json.dumps({"error": "foo"})) + result = self.app.acquire_token_for_client(["scope"], post=mock_post) + self.assertEqual("AT 2", result.get("access_token"), "Shall get aging token") def test_acquire_token_on_behalf_of(self): at = "this is an access token" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/msal-1.22.0/tests/test_e2e.py new/msal-1.24.0b1/tests/test_e2e.py --- old/msal-1.22.0/tests/test_e2e.py 2023-04-17 18:56:13.000000000 +0200 +++ new/msal-1.24.0b1/tests/test_e2e.py 2023-07-24 11:38:42.000000000 +0200 @@ -146,17 +146,15 @@ json.dumps(self.app.token_cache._cache, indent=4), json.dumps(result_from_wire.get("id_token_claims"), indent=4), ) - # Going to test acquire_token_silent(...) to locate an AT from cache - result_from_cache = self.app.acquire_token_silent(scope, account=None) + self.assertIsNone( + self.app.acquire_token_silent(scope, account=None), + "acquire_token_silent(..., account=None) shall always return None") + # Going to test acquire_token_for_client(...) to locate an AT from cache + result_from_cache = self.app.acquire_token_for_client(scope) self.assertIsNotNone(result_from_cache) self.assertEqual( result_from_wire['access_token'], result_from_cache['access_token'], "We should get a cached AT") - self.app.acquire_token_silent( - # Result will typically be None, because client credential grant returns no RT. - # But we care more on this call should succeed without exception. - scope, account=None, - force_refresh=True) # Mimic the AT already expires @classmethod def _build_app(cls, @@ -925,10 +923,16 @@ client_secret=self.get_lab_user_secret( self.app_config["clientSecret"].split("=")[-1]), authority=self.app_config["authority"], - scope=["{}/.default".format(self.app_config["appId"])], # App permission + #scope=["{}/.default".format(self.app_config["appId"])], # AADSTS500207: The account type can't be used for the resource you're trying to access. + #scope=["api://{}/.default".format(self.app_config["appId"])], # AADSTS500011: The resource principal named api://ced781e7-bdb0-4c99-855c-d3bacddea88a was not found in the tenant named MSIDLABCIAM2. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You might have sent your authentication request to the wrong tenant. + scope=self.app_config["scopes"], # It shall ends with "/.default" ) def test_ciam_acquire_token_by_ropc(self): + """CIAM does not officially support ROPC, especially not for external emails. + + We keep this test case for now, because the test data will use a local email. + """ # Somehow, this would only work after creating a secret for the test app # and enabling "Allow public client flows". # Otherwise it would hit AADSTS7000218. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/msal-1.22.0/tests/test_throttled_http_client.py new/msal-1.24.0b1/tests/test_throttled_http_client.py --- old/msal-1.22.0/tests/test_throttled_http_client.py 2023-04-17 18:56:13.000000000 +0200 +++ new/msal-1.24.0b1/tests/test_throttled_http_client.py 2023-07-24 11:38:42.000000000 +0200 @@ -11,19 +11,13 @@ logging.basicConfig(level=logging.DEBUG) -class DummyHttpResponse(MinimalResponse): - def __init__(self, headers=None, **kwargs): - self.headers = {} if headers is None else headers - super(DummyHttpResponse, self).__init__(**kwargs) - - class DummyHttpClient(object): def __init__(self, status_code=None, response_headers=None): self._status_code = status_code self._response_headers = response_headers def _build_dummy_response(self): - return DummyHttpResponse( + return MinimalResponse( status_code=self._status_code, headers=self._response_headers, text=random(), # So that we'd know whether a new response is received