Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-msal-extensions for openSUSE:Factory checked in at 2024-02-11 15:45:58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-msal-extensions (Old) and /work/SRC/openSUSE:Factory/.python-msal-extensions.new.1815 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-msal-extensions" Sun Feb 11 15:45:58 2024 rev:7 rq:1145585 version:1.1.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-msal-extensions/python-msal-extensions.changes 2023-05-08 17:24:56.632929313 +0200 +++ /work/SRC/openSUSE:Factory/.python-msal-extensions.new.1815/python-msal-extensions.changes 2024-02-11 15:45:59.515576272 +0100 @@ -1,0 +2,7 @@ +Fri Feb 9 15:46:13 UTC 2024 - John Paul Adrian Glaubitz <adrian.glaub...@suse.com> + +- Update to version 1.1.0 + + Support Python 3.12 by removing dependency on distutils (#120, #123) + + Dropping Python 2.7 (#122) + +------------------------------------------------------------------- Old: ---- msal-extensions-1.0.0.tar.gz New: ---- msal-extensions-1.1.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-msal-extensions.spec ++++++ --- /var/tmp/diff_new_pack.GRW6q0/_old 2024-02-11 15:46:00.083596695 +0100 +++ /var/tmp/diff_new_pack.GRW6q0/_new 2024-02-11 15:46:00.087596838 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-msal-extensions # -# Copyright (c) 2023 SUSE LLC +# Copyright (c) 2024 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -21,7 +21,7 @@ %define skip_python2 1 %endif Name: python-msal-extensions -Version: 1.0.0 +Version: 1.1.0 Release: 0 Summary: Microsoft Authentication Library (MSAL) for Python Extensions License: MIT ++++++ msal-extensions-1.0.0.tar.gz -> msal-extensions-1.1.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/msal-extensions-1.0.0/PKG-INFO new/msal-extensions-1.1.0/PKG-INFO --- old/msal-extensions-1.0.0/PKG-INFO 2022-02-15 00:45:41.537072200 +0100 +++ new/msal-extensions-1.1.0/PKG-INFO 2023-12-09 05:12:27.996491700 +0100 @@ -1,14 +1,26 @@ Metadata-Version: 2.1 Name: msal-extensions -Version: 1.0.0 +Version: 1.1.0 Summary: Microsoft Authentication Library extensions (MSAL EX) provides a persistence API that can save your data on disk, encrypted on Windows, macOS and Linux. Concurrent data access will be coordinated by a file lock mechanism. -License: MIT +License: MIT License Project-URL: Changelog, https://github.com/AzureAD/microsoft-authentication-extensions-for-python/releases -Platform: UNKNOWN Classifier: License :: OSI Approved :: MIT License Classifier: Development Status :: 5 - Production/Stable +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Requires-Python: >=3.7 Description-Content-Type: text/markdown License-File: LICENSE +Requires-Dist: msal<2.0.0,>=0.4.1 +Requires-Dist: portalocker<3,>=1.0; platform_system != "Windows" +Requires-Dist: portalocker<3,>=1.6; platform_system == "Windows" +Requires-Dist: packaging # Microsoft Authentication Extensions for Python @@ -101,6 +113,12 @@ assert json.loads(persistence.load()) == data ``` +## Python version support policy + +Python versions which are 6 months older than their +[end-of-life cycle defined by Python Software Foundation (PSF)](https://devguide.python.org/versions/#versions) +will not receive new feature updates from this library. + ## Community Help and Support @@ -129,5 +147,3 @@ ## We value and adhere to the Microsoft Open Source Code of Conduct This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [openc...@microsoft.com](mailto:openc...@microsoft.com) with any additional questions or comments. - - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/msal-extensions-1.0.0/README.md new/msal-extensions-1.1.0/README.md --- old/msal-extensions-1.0.0/README.md 2022-02-15 00:45:30.000000000 +0100 +++ new/msal-extensions-1.1.0/README.md 2023-12-09 05:12:20.000000000 +0100 @@ -89,6 +89,12 @@ assert json.loads(persistence.load()) == data ``` +## Python version support policy + +Python versions which are 6 months older than their +[end-of-life cycle defined by Python Software Foundation (PSF)](https://devguide.python.org/versions/#versions) +will not receive new feature updates from this library. + ## Community Help and Support diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/msal-extensions-1.0.0/msal_extensions/__init__.py new/msal-extensions-1.1.0/msal_extensions/__init__.py --- old/msal-extensions-1.0.0/msal_extensions/__init__.py 2022-02-15 00:45:30.000000000 +0100 +++ new/msal-extensions-1.1.0/msal_extensions/__init__.py 2023-12-09 05:12:20.000000000 +0100 @@ -1,5 +1,5 @@ """Provides auxiliary functionality to the `msal` package.""" -__version__ = "1.0.0" +__version__ = "1.1.0" from .persistence import ( FilePersistence, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/msal-extensions-1.0.0/msal_extensions/cache_lock.py new/msal-extensions-1.1.0/msal_extensions/cache_lock.py --- old/msal-extensions-1.0.0/msal_extensions/cache_lock.py 2022-02-15 00:45:30.000000000 +0100 +++ new/msal-extensions-1.1.0/msal_extensions/cache_lock.py 2023-12-09 05:12:20.000000000 +0100 @@ -4,9 +4,9 @@ import errno import time import logging -from distutils.version import LooseVersion import portalocker +from packaging.version import Version logger = logging.getLogger(__name__) @@ -21,7 +21,7 @@ self._lockpath = lockfile_path # Support for passing through arguments to the open syscall was added in v1.4.0 open_kwargs = ({'buffering': 0} - if LooseVersion(portalocker.__version__) >= LooseVersion("1.4.0") else {}) + if Version(portalocker.__version__) >= Version("1.4.0") else {}) self._lock = portalocker.Lock( lockfile_path, mode='wb+', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/msal-extensions-1.0.0/msal_extensions/persistence.py new/msal-extensions-1.1.0/msal_extensions/persistence.py --- old/msal-extensions-1.0.0/msal_extensions/persistence.py 2022-02-15 00:45:30.000000000 +0100 +++ new/msal-extensions-1.1.0/msal_extensions/persistence.py 2023-12-09 05:12:20.000000000 +0100 @@ -210,7 +210,7 @@ except OSError as exception: raise PersistenceEncryptionError( err_no=getattr(exception, "winerror", None), # Exists in Python 3 on Windows - message="Encryption failed: {}. Consider disable encryption.".format(exception), + message="Encryption failed: {} Consider disable encryption.".format(exception), ) with os.fdopen(_open(self._location), 'wb+') as handle: handle.write(data) @@ -237,7 +237,7 @@ except OSError as exception: raise PersistenceDecryptionError( err_no=getattr(exception, "winerror", None), # Exists in Python 3 on Windows - message="Decryption failed: {}. " + message="Decryption failed: {} " "App developer may consider this guidance: " "https://github.com/AzureAD/microsoft-authentication-extensions-for-python/wiki/PersistenceDecryptionError" # pylint: disable=line-too-long .format(exception), @@ -342,4 +342,3 @@ # with a FilePersistence to achieve # https://github.com/AzureAD/microsoft-authentication-extensions-for-python/issues/12 # But this idea is not pursued at this time. - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/msal-extensions-1.0.0/msal_extensions/windows.py new/msal-extensions-1.1.0/msal_extensions/windows.py --- old/msal-extensions-1.0.0/msal_extensions/windows.py 2022-02-15 00:45:30.000000000 +0100 +++ new/msal-extensions-1.1.0/msal_extensions/windows.py 2023-12-09 05:12:20.000000000 +0100 @@ -46,7 +46,7 @@ "The computer must be trusted for delegation and " "the current user account must be configured to allow delegation. " "See also https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/enable-computer-and-user-accounts-to-be-trusted-for-delegation", - 13: "The data is invalid", + 13: "The data is invalid.", } # This code is modeled from a StackOverflow question, which can be found here: @@ -91,7 +91,7 @@ _LOCAL_FREE(result.pbData) err_code = _GET_LAST_ERROR() - raise OSError(None, _err_description.get(err_code), None, err_code) + raise OSError(None, _err_description.get(err_code, ''), None, err_code) def unprotect(self, cipher_text): # type: (bytes) -> str @@ -120,4 +120,4 @@ finally: _LOCAL_FREE(result.pbData) err_code = _GET_LAST_ERROR() - raise OSError(None, _err_description.get(err_code), None, err_code) + raise OSError(None, _err_description.get(err_code, ''), None, err_code) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/msal-extensions-1.0.0/msal_extensions.egg-info/PKG-INFO new/msal-extensions-1.1.0/msal_extensions.egg-info/PKG-INFO --- old/msal-extensions-1.0.0/msal_extensions.egg-info/PKG-INFO 2022-02-15 00:45:41.000000000 +0100 +++ new/msal-extensions-1.1.0/msal_extensions.egg-info/PKG-INFO 2023-12-09 05:12:27.000000000 +0100 @@ -1,14 +1,26 @@ Metadata-Version: 2.1 Name: msal-extensions -Version: 1.0.0 +Version: 1.1.0 Summary: Microsoft Authentication Library extensions (MSAL EX) provides a persistence API that can save your data on disk, encrypted on Windows, macOS and Linux. Concurrent data access will be coordinated by a file lock mechanism. -License: MIT +License: MIT License Project-URL: Changelog, https://github.com/AzureAD/microsoft-authentication-extensions-for-python/releases -Platform: UNKNOWN Classifier: License :: OSI Approved :: MIT License Classifier: Development Status :: 5 - Production/Stable +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Requires-Python: >=3.7 Description-Content-Type: text/markdown License-File: LICENSE +Requires-Dist: msal<2.0.0,>=0.4.1 +Requires-Dist: portalocker<3,>=1.0; platform_system != "Windows" +Requires-Dist: portalocker<3,>=1.6; platform_system == "Windows" +Requires-Dist: packaging # Microsoft Authentication Extensions for Python @@ -101,6 +113,12 @@ assert json.loads(persistence.load()) == data ``` +## Python version support policy + +Python versions which are 6 months older than their +[end-of-life cycle defined by Python Software Foundation (PSF)](https://devguide.python.org/versions/#versions) +will not receive new feature updates from this library. + ## Community Help and Support @@ -129,5 +147,3 @@ ## We value and adhere to the Microsoft Open Source Code of Conduct This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [openc...@microsoft.com](mailto:openc...@microsoft.com) with any additional questions or comments. - - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/msal-extensions-1.0.0/msal_extensions.egg-info/SOURCES.txt new/msal-extensions-1.1.0/msal_extensions.egg-info/SOURCES.txt --- old/msal-extensions-1.0.0/msal_extensions.egg-info/SOURCES.txt 2022-02-15 00:45:41.000000000 +0100 +++ new/msal-extensions-1.1.0/msal_extensions.egg-info/SOURCES.txt 2023-12-09 05:12:27.000000000 +0100 @@ -13,4 +13,10 @@ msal_extensions.egg-info/SOURCES.txt msal_extensions.egg-info/dependency_links.txt msal_extensions.egg-info/requires.txt -msal_extensions.egg-info/top_level.txt \ No newline at end of file +msal_extensions.egg-info/top_level.txt +tests/test_agnostic_backend.py +tests/test_cache_lock_file_perf.py +tests/test_crossplatlock.py +tests/test_macos_backend.py +tests/test_persistence.py +tests/test_windows_backend.py \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/msal-extensions-1.0.0/msal_extensions.egg-info/requires.txt new/msal-extensions-1.1.0/msal_extensions.egg-info/requires.txt --- old/msal-extensions-1.0.0/msal_extensions.egg-info/requires.txt 2022-02-15 00:45:41.000000000 +0100 +++ new/msal-extensions-1.1.0/msal_extensions.egg-info/requires.txt 2023-12-09 05:12:27.000000000 +0100 @@ -1,16 +1,8 @@ msal<2.0.0,>=0.4.1 +packaging -[:python_version < "3.0"] -pathlib2 - -[:python_version == "2.7" and platform_system != "Windows"] -portalocker<2,>=1.0 - -[:python_version == "2.7" and platform_system == "Windows"] -portalocker<2,>=1.6 - -[:python_version >= "3.5" and platform_system != "Windows"] +[:platform_system != "Windows"] portalocker<3,>=1.0 -[:python_version >= "3.5" and platform_system == "Windows"] +[:platform_system == "Windows"] portalocker<3,>=1.6 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/msal-extensions-1.0.0/setup.cfg new/msal-extensions-1.1.0/setup.cfg --- old/msal-extensions-1.0.0/setup.cfg 2022-02-15 00:45:41.537072200 +0100 +++ new/msal-extensions-1.1.0/setup.cfg 2023-12-09 05:12:28.000491600 +0100 @@ -1,12 +1,20 @@ [bdist_wheel] -universal = 1 +universal = 0 [metadata] -license = MIT +license = MIT License project_urls = Changelog = https://github.com/AzureAD/microsoft-authentication-extensions-for-python/releases classifiers = License :: OSI Approved :: MIT License Development Status :: 5 - Production/Stable + Programming Language :: Python :: 3 :: Only + Programming Language :: Python :: 3 + 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 + Programming Language :: Python :: 3.12 description = Microsoft Authentication Library extensions (MSAL EX) provides a persistence API that can save your data on disk, encrypted on Windows, macOS and Linux. Concurrent data access will be coordinated by a file lock mechanism. [egg_info] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/msal-extensions-1.0.0/setup.py new/msal-extensions-1.1.0/setup.py --- old/msal-extensions-1.0.0/setup.py 2022-02-15 00:45:30.000000000 +0100 +++ new/msal-extensions-1.1.0/setup.py 2023-12-09 05:12:20.000000000 +0100 @@ -20,22 +20,20 @@ long_description=long_description, long_description_content_type="text/markdown", package_data={'': ['LICENSE']}, + python_requires=">=3.7", install_requires=[ 'msal>=0.4.1,<2.0.0', - # In order to implement these requirements: - # Lowerbound = (1.6 if playform_system == 'Windows' else 1.0) - # Upperbound < (3 if python_version >= '3.5' else 2) - # The following 4 lines use the `and` syntax defined here: - # https://www.python.org/dev/peps/pep-0508/#grammar - "portalocker<3,>=1.0;python_version>='3.5' and platform_system!='Windows'", - "portalocker<2,>=1.0;python_version=='2.7' and platform_system!='Windows'", - "portalocker<3,>=1.6;python_version>='3.5' and platform_system=='Windows'", - "portalocker<2,>=1.6;python_version=='2.7' and platform_system=='Windows'", + "portalocker<3,>=1.0;platform_system!='Windows'", + "portalocker<3,>=1.6;platform_system=='Windows'", - "pathlib2;python_version<'3.0'", ## We choose to NOT define a hard dependency on this. # "pygobject>=3,<4;platform_system=='Linux'", + + # Packaging package uses YY.N versioning so we have no upperbound to pin. + # Neither do we need lowerbound because its `Version` API existed since its first release + # https://github.com/pypa/packaging/blame/14.0/packaging/version.py + 'packaging', ], tests_require=['pytest'], ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/msal-extensions-1.0.0/tests/test_agnostic_backend.py new/msal-extensions-1.1.0/tests/test_agnostic_backend.py --- old/msal-extensions-1.0.0/tests/test_agnostic_backend.py 1970-01-01 01:00:00.000000000 +0100 +++ new/msal-extensions-1.1.0/tests/test_agnostic_backend.py 2023-12-09 05:12:20.000000000 +0100 @@ -0,0 +1,49 @@ +import os +import shutil +import tempfile +import sys + +import msal +import pytest + +from msal_extensions import * + + +@pytest.fixture +def temp_location(): + test_folder = tempfile.mkdtemp(prefix="test_token_cache_roundtrip") + yield os.path.join(test_folder, 'token_cache.bin') + shutil.rmtree(test_folder, ignore_errors=True) + + +def _test_token_cache_roundtrip(cache): + client_id = os.getenv('AZURE_CLIENT_ID') + client_secret = os.getenv('AZURE_CLIENT_SECRET') + if not (client_id and client_secret): + pytest.skip('no credentials present to test TokenCache round-trip with.') + + app = msal.ConfidentialClientApplication( + client_id=client_id, + client_credential=client_secret, + token_cache=cache) + desired_scopes = ['https://graph.microsoft.com/.default'] + token1 = app.acquire_token_for_client(scopes=desired_scopes) + os.utime( # Mock having another process update the cache + cache._persistence.get_location(), None) + token2 = app.acquire_token_silent(scopes=desired_scopes, account=None) + assert token1['access_token'] == token2['access_token'] + +def test_file_token_cache_roundtrip(temp_location): + _test_token_cache_roundtrip(PersistedTokenCache(FilePersistence(temp_location))) + +def test_current_platform_cache_roundtrip_with_persistence_builder(temp_location): + _test_token_cache_roundtrip(PersistedTokenCache(build_encrypted_persistence(temp_location))) + +def test_persisted_token_cache(temp_location): + _test_token_cache_roundtrip(PersistedTokenCache(FilePersistence(temp_location))) + +def test_file_not_found_error_is_not_raised(): + persistence = FilePersistence('non_existing_file') + cache = PersistedTokenCache(persistence) + # An exception raised here will fail the test case as it is supposed to be a NO-OP + cache.find('') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/msal-extensions-1.0.0/tests/test_cache_lock_file_perf.py new/msal-extensions-1.1.0/tests/test_cache_lock_file_perf.py --- old/msal-extensions-1.0.0/tests/test_cache_lock_file_perf.py 1970-01-01 01:00:00.000000000 +0100 +++ new/msal-extensions-1.1.0/tests/test_cache_lock_file_perf.py 2023-12-09 05:12:20.000000000 +0100 @@ -0,0 +1,75 @@ +import multiprocessing +import os +import shutil +import tempfile + +import pytest + +from cache_file_generator import _acquire_lock_and_write_to_cache + + +@pytest.fixture +def temp_location(): + test_folder = tempfile.mkdtemp(prefix="test_persistence_roundtrip") + yield os.path.join(test_folder, 'persistence.bin') + shutil.rmtree(test_folder, ignore_errors=True) + + +def _validate_result_in_cache(cache_location): + with open(cache_location) as handle: + data = handle.read() + prev_process_id = None + count = 0 + for line in data.split("\n"): + if line: + count += 1 + tag, process_id = line.split(" ") + if prev_process_id is not None: + assert process_id == prev_process_id, "Process overlap found" + assert tag == '>', "Process overlap_found" + prev_process_id = None + else: + assert tag == '<', "Opening bracket not found" + prev_process_id = process_id + return count + + +def _run_multiple_processes(no_of_processes, cache_location, sleep_interval): + open(cache_location, "w+") + processes = [] + for i in range(no_of_processes): + process = multiprocessing.Process( + target=_acquire_lock_and_write_to_cache, + args=(cache_location, sleep_interval)) + processes.append(process) + + for process in processes: + process.start() + + for process in processes: + process.join() + + +def test_lock_for_normal_workload(temp_location): + num_of_processes = 4 + sleep_interval = 0.1 + _run_multiple_processes(num_of_processes, temp_location, sleep_interval) + count = _validate_result_in_cache(temp_location) + assert count == num_of_processes * 2, "Should not observe starvation" + + +def test_lock_for_high_workload(temp_location): + num_of_processes = 80 + sleep_interval = 0 + _run_multiple_processes(num_of_processes, temp_location, sleep_interval) + count = _validate_result_in_cache(temp_location) + assert count <= num_of_processes * 2, "Starvation or not, we should not observe garbled payload" + + +def test_lock_for_timeout(temp_location): + num_of_processes = 30 + sleep_interval = 1 + _run_multiple_processes(num_of_processes, temp_location, sleep_interval) + count = _validate_result_in_cache(temp_location) + assert count < num_of_processes * 2, "Should observe starvation" + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/msal-extensions-1.0.0/tests/test_crossplatlock.py new/msal-extensions-1.1.0/tests/test_crossplatlock.py --- old/msal-extensions-1.0.0/tests/test_crossplatlock.py 1970-01-01 01:00:00.000000000 +0100 +++ new/msal-extensions-1.1.0/tests/test_crossplatlock.py 2023-12-09 05:12:20.000000000 +0100 @@ -0,0 +1,18 @@ +import pytest +from msal_extensions.cache_lock import CrossPlatLock + + +def test_ensure_file_deleted(): + lockfile = './test_lock_1.txt' + + try: + FileNotFoundError + except NameError: + FileNotFoundError = IOError + + with CrossPlatLock(lockfile): + pass + + with pytest.raises(FileNotFoundError): + with open(lockfile): + pass diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/msal-extensions-1.0.0/tests/test_macos_backend.py new/msal-extensions-1.1.0/tests/test_macos_backend.py --- old/msal-extensions-1.0.0/tests/test_macos_backend.py 1970-01-01 01:00:00.000000000 +0100 +++ new/msal-extensions-1.1.0/tests/test_macos_backend.py 2023-12-09 05:12:20.000000000 +0100 @@ -0,0 +1,46 @@ +import sys +import os +import shutil +import tempfile +import pytest +import uuid +import msal + +if not sys.platform.startswith('darwin'): + pytest.skip('skipping MacOS-only tests', allow_module_level=True) +else: + from msal_extensions.osx import Keychain + from msal_extensions.token_cache import PersistedTokenCache + from msal_extensions.persistence import KeychainPersistence + + +def test_keychain_roundtrip(): + with Keychain() as subject: + location, account = "msal_extension_test1", "test_account1" + want = uuid.uuid4().hex + subject.set_generic_password(location, account, want) + got = subject.get_generic_password(location, account) + assert got == want + + +def test_osx_token_cache_roundtrip(): + client_id = os.getenv('AZURE_CLIENT_ID') + client_secret = os.getenv('AZURE_CLIENT_SECRET') + if not (client_id and client_secret): + pytest.skip('no credentials present to test PersistedTokenCache round-trip with.') + + test_folder = tempfile.mkdtemp(prefix="msal_extension_test_osx_token_cache_roundtrip") + cache_file = os.path.join(test_folder, 'msal.cache') + try: + subject = PersistedTokenCache(KeychainPersistence(cache_file)) + app = msal.ConfidentialClientApplication( + client_id=client_id, + client_credential=client_secret, + token_cache=subject) + desired_scopes = ['https://graph.microsoft.com/.default'] + token1 = app.acquire_token_for_client(scopes=desired_scopes) + os.utime(cache_file, None) # Mock having another process update the cache. + token2 = app.acquire_token_silent(scopes=desired_scopes, account=None) + assert token1['access_token'] == token2['access_token'] + finally: + shutil.rmtree(test_folder, ignore_errors=True) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/msal-extensions-1.0.0/tests/test_persistence.py new/msal-extensions-1.1.0/tests/test_persistence.py --- old/msal-extensions-1.0.0/tests/test_persistence.py 1970-01-01 01:00:00.000000000 +0100 +++ new/msal-extensions-1.1.0/tests/test_persistence.py 2023-12-09 05:12:20.000000000 +0100 @@ -0,0 +1,96 @@ +import os +import sys +import shutil +import tempfile +import logging + +import pytest + +from msal_extensions.persistence import * + + +def _is_env_var_defined(env_var): + return bool( # (WTF) What-The-Finding: + # The bool(...) is necessary, otherwise skipif(...) would treat "true" as + # string conditions and then raise an undefined "true" exception. + # https://docs.pytest.org/en/latest/historical-notes.html#string-conditions + os.getenv(env_var)) + + +# Note: If you use tox, remember to pass them through via tox.ini +# https://tox.wiki/en/latest/example/basic.html#passing-down-environment-variables +is_running_on_travis_ci = _is_env_var_defined("TRAVIS") +is_running_on_github_ci = _is_env_var_defined("GITHUB_ACTIONS") + +@pytest.fixture +def temp_location(): + test_folder = tempfile.mkdtemp(prefix="test_persistence_roundtrip") + yield os.path.join(test_folder, 'persistence.bin') + shutil.rmtree(test_folder, ignore_errors=True) + +def _test_persistence_roundtrip(persistence): + payload = 'arbitrary content' + persistence.save(payload) + assert persistence.load() == payload + +def _test_nonexistent_persistence(persistence): + with pytest.raises(PersistenceNotFound): + persistence.load() + with pytest.raises(PersistenceNotFound): + persistence.time_last_modified() + +def test_file_persistence(temp_location): + _test_persistence_roundtrip(FilePersistence(temp_location)) + +def test_nonexistent_file_persistence(temp_location): + _test_nonexistent_persistence(FilePersistence(temp_location)) + +@pytest.mark.skipif( + is_running_on_travis_ci or not sys.platform.startswith('win'), + reason="Requires Windows Desktop") +def test_file_persistence_with_data_protection(temp_location): + try: + _test_persistence_roundtrip(FilePersistenceWithDataProtection(temp_location)) + except PersistenceDecryptionError: + if is_running_on_github_ci or is_running_on_travis_ci: + logging.warning("DPAPI tends to fail on Windows VM. Run this on your desktop to double check.") + else: + raise + +@pytest.mark.skipif( + is_running_on_travis_ci or not sys.platform.startswith('win'), + reason="Requires Windows Desktop") +def test_nonexistent_file_persistence_with_data_protection(temp_location): + _test_nonexistent_persistence(FilePersistenceWithDataProtection(temp_location)) + +@pytest.mark.skipif( + not sys.platform.startswith('darwin'), + reason="Requires OSX. Whether running on TRAVIS CI does not seem to matter.") +def test_keychain_persistence(temp_location): + _test_persistence_roundtrip(KeychainPersistence(temp_location)) + +@pytest.mark.skipif( + not sys.platform.startswith('darwin'), + reason="Requires OSX. Whether running on TRAVIS CI does not seem to matter.") +def test_nonexistent_keychain_persistence(temp_location): + random_service_name = random_account_name = str(id(temp_location)) + _test_nonexistent_persistence( + KeychainPersistence(temp_location, random_service_name, random_account_name)) + +@pytest.mark.skipif( + is_running_on_travis_ci or not sys.platform.startswith('linux'), + reason="Requires Linux Desktop. Headless or SSH session won't work.") +def test_libsecret_persistence(temp_location): + _test_persistence_roundtrip(LibsecretPersistence(temp_location)) + +@pytest.mark.skipif( + is_running_on_travis_ci or not sys.platform.startswith('linux'), + reason="Requires Linux Desktop. Headless or SSH session won't work.") +def test_nonexistent_libsecret_persistence(temp_location): + random_schema_name = random_value = str(id(temp_location)) + _test_nonexistent_persistence(LibsecretPersistence( + temp_location, + random_schema_name, + {"my_attr_1": random_value, "my_attr_2": random_value}, + )) + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/msal-extensions-1.0.0/tests/test_windows_backend.py new/msal-extensions-1.1.0/tests/test_windows_backend.py --- old/msal-extensions-1.0.0/tests/test_windows_backend.py 1970-01-01 01:00:00.000000000 +0100 +++ new/msal-extensions-1.1.0/tests/test_windows_backend.py 2023-12-09 05:12:20.000000000 +0100 @@ -0,0 +1,106 @@ +import sys +import os +import errno +import shutil +import tempfile +import pytest +import uuid +import msal + +if not sys.platform.startswith('win'): + pytest.skip('skipping windows-only tests', allow_module_level=True) +else: + from msal_extensions.windows import WindowsDataProtectionAgent + from msal_extensions.token_cache import PersistedTokenCache + from msal_extensions.persistence import FilePersistenceWithDataProtection + + +def test_dpapi_roundtrip_with_entropy(): + subject_without_entropy = WindowsDataProtectionAgent() + subject_with_entropy = WindowsDataProtectionAgent(entropy=uuid.uuid4().hex) + + test_cases = [ + '', + 'lorem ipsum', + 'lorem-ipsum', + '<Python>', + uuid.uuid4().hex, + ] + + try: + for tc in test_cases: + ciphered = subject_with_entropy.protect(tc) + assert ciphered != tc + + got = subject_with_entropy.unprotect(ciphered) + assert got == tc + + ciphered = subject_without_entropy.protect(tc) + assert ciphered != tc + + got = subject_without_entropy.unprotect(ciphered) + assert got == tc + except OSError as exp: + if exp.errno == errno.EIO and os.getenv('TRAVIS_REPO_SLUG'): + pytest.skip('DPAPI tests are known to fail in TravisCI. This effort tracked by ' + 'https://github.com/AzureAD/microsoft-authentication-extentions-for-python' + '/issues/21') + + +def test_read_msal_cache_direct(): + """ + This loads and unprotects an MSAL cache directly, only using the DataProtectionAgent. + """ + localappdata_location = os.getenv('LOCALAPPDATA', os.path.expanduser('~')) + cache_locations = [ + os.path.join(localappdata_location, '.IdentityService', 'msal.cache'), # this is where it's supposed to be + os.path.join(localappdata_location, '.IdentityServices', 'msal.cache'), # There was a miscommunications about whether this was plural or not. + os.path.join(localappdata_location, 'msal.cache'), # The earliest most naive builds used this locations. + ] + + found = False + for loc in cache_locations: + try: + with open(loc, mode='rb') as fh: + contents = fh.read() + found = True + + break + except IOError as exp: + if exp.errno != errno.ENOENT: + raise exp + + if not found: + pytest.skip('could not find the msal.cache file (try logging in using MSAL)') + + subject = WindowsDataProtectionAgent() + raw = subject.unprotect(contents) + assert raw != "" + + cache = msal.SerializableTokenCache() + cache.deserialize(raw) + access_tokens = cache.find(msal.TokenCache.CredentialType.ACCESS_TOKEN) + assert len(access_tokens) > 0 + + +def test_windows_token_cache_roundtrip(): + client_id = os.getenv('AZURE_CLIENT_ID') + client_secret = os.getenv('AZURE_CLIENT_SECRET') + if not (client_id and client_secret): + pytest.skip('no credentials present to test PersistedTokenCache round-trip with.') + + test_folder = tempfile.mkdtemp(prefix="msal_extension_test_windows_token_cache_roundtrip") + cache_file = os.path.join(test_folder, 'msal.cache') + try: + subject = PersistedTokenCache(FilePersistenceWithDataProtection(cache_file)) + app = msal.ConfidentialClientApplication( + client_id=client_id, + client_credential=client_secret, + token_cache=subject) + desired_scopes = ['https://graph.microsoft.com/.default'] + token1 = app.acquire_token_for_client(scopes=desired_scopes) + os.utime(cache_file, None) # Mock having another process update the cache. + token2 = app.acquire_token_silent(scopes=desired_scopes, account=None) + assert token1['access_token'] == token2['access_token'] + finally: + shutil.rmtree(test_folder, ignore_errors=True)