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 2022-04-26 20:15:48 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-msal-extensions (Old) and /work/SRC/openSUSE:Factory/.python-msal-extensions.new.1538 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-msal-extensions" Tue Apr 26 20:15:48 2022 rev:5 rq:972847 version:1.0.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-msal-extensions/python-msal-extensions.changes 2022-01-04 19:37:55.977964862 +0100 +++ /work/SRC/openSUSE:Factory/.python-msal-extensions.new.1538/python-msal-extensions.changes 2022-04-26 20:17:45.336759745 +0200 @@ -1,0 +2,12 @@ +Tue Apr 26 10:44:50 UTC 2022 - John Paul Adrian Glaubitz <adrian.glaub...@suse.com> + +- Update to version 1.0.0 + + New: Add a new platform-independent build_encrypted_persistence() API. (#87, #110) + + Remove: Old TokenCache API which has been deprecated for 2 years. (#110) + + Enhancement: Make all platform-dependent parameters optional (#103) + + Enhancement: Provide PersistenceEncryptError and PersistenceDecryptError, + currently raised when encryption on Windows fails. (#108) + + Enhancement: The data file will be created with 600 permission when + running in Unix-like systems. (#107) + +------------------------------------------------------------------- Old: ---- msal-extensions-0.3.1.tar.gz New: ---- msal-extensions-1.0.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-msal-extensions.spec ++++++ --- /var/tmp/diff_new_pack.thAajI/_old 2022-04-26 20:17:45.756760253 +0200 +++ /var/tmp/diff_new_pack.thAajI/_new 2022-04-26 20:17:45.760760258 +0200 @@ -21,7 +21,7 @@ %define skip_python2 1 %endif Name: python-msal-extensions -Version: 0.3.1 +Version: 1.0.0 Release: 0 Summary: Microsoft Authentication Library (MSAL) for Python Extensions License: MIT ++++++ msal-extensions-0.3.1.tar.gz -> msal-extensions-1.0.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/msal-extensions-0.3.1/PKG-INFO new/msal-extensions-1.0.0/PKG-INFO --- old/msal-extensions-0.3.1/PKG-INFO 2021-12-13 05:46:55.792970000 +0100 +++ new/msal-extensions-1.0.0/PKG-INFO 2022-02-15 00:45:41.537072200 +0100 @@ -1,24 +1,23 @@ Metadata-Version: 2.1 Name: msal-extensions -Version: 0.3.1 -Summary: UNKNOWN -Home-page: UNKNOWN +Version: 1.0.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 Project-URL: Changelog, https://github.com/AzureAD/microsoft-authentication-extensions-for-python/releases Platform: UNKNOWN Classifier: License :: OSI Approved :: MIT License -Classifier: Development Status :: 4 - Beta +Classifier: Development Status :: 5 - Production/Stable Description-Content-Type: text/markdown License-File: LICENSE # Microsoft Authentication Extensions for Python -The Microsoft Authentication Extensions for Python offers secure mechanisms for client applications to perform cross-platform token cache serialization and persistence. It gives additional support to the [Microsoft Authentication Library for Python (MSAL)](https://github.com/AzureAD/microsoft-authentication-library-for-python). +The Microsoft Authentication Extensions for Python offers secure mechanisms for client applications to perform cross-platform token cache serialization and persistence. It gives additional support to the [Microsoft Authentication Library for Python (MSAL)](https://github.com/AzureAD/microsoft-authentication-library-for-python). MSAL Python supports an in-memory cache by default and provides the [SerializableTokenCache](https://msal-python.readthedocs.io/en/latest/#msal.SerializableTokenCache) to perform cache serialization. You can read more about this in the MSAL Python [documentation](https://docs.microsoft.com/en-us/azure/active-directory/develop/msal-python-token-cache-serialization). Developers are required to implement their own cache persistance across multiple platforms and Microsoft Authentication Extensions makes this simpler. -The supported platforms are Windows, Mac and Linux. +The supported platforms are Windows, Mac and Linux. - Windows - [DPAPI](https://docs.microsoft.com/en-us/dotnet/standard/security/how-to-use-data-protection) is used for encryption. - MAC - The MAC KeyChain is used. - Linux - [LibSecret](https://wiki.gnome.org/Projects/Libsecret) is used for encryption. @@ -41,7 +40,9 @@ ## Usage -The Microsoft Authentication Extensions library provides the `PersistedTokenCache` which accepts a platform-dependent persistence instance. This token cache can then be used to instantiate the `PublicClientApplication` in MSAL Python. +### Creating an encrypted token cache file to be used by MSAL + +The Microsoft Authentication Extensions library provides the `PersistedTokenCache` which accepts a platform-dependent persistence instance. This token cache can then be used to instantiate the `PublicClientApplication` in MSAL Python. The token cache includes a file lock, and auto-reload behavior under the hood. @@ -52,24 +53,16 @@ ```python def build_persistence(location, fallback_to_plaintext=False): """Build a suitable persistence instance based your current OS""" - if sys.platform.startswith('win'): - return FilePersistenceWithDataProtection(location) - if sys.platform.startswith('darwin'): - return KeychainPersistence(location, "my_service_name", "my_account_name") - if sys.platform.startswith('linux'): - try: - return LibsecretPersistence( - location, - schema_name="my_schema_name", - attributes={"my_attr1": "foo", "my_attr2": "bar"}, - ) - except: # pylint: disable=bare-except - if not fallback_to_plaintext: - raise - logging.exception("Encryption unavailable. Opting in to plain text.") - return FilePersistence(location) + try: + return build_encrypted_persistence(location) + except: + if not fallback_to_plaintext: + raise + logging.warning("Encryption unavailable. Opting in to plain text.") + return FilePersistence(location) persistence = build_persistence("token_cache.bin") +print("Type of persistence: {}".format(persistence.__class__.__name__)) print("Is this persistence encrypted?", persistence.is_encrypted) cache = PersistedTokenCache(persistence) @@ -79,6 +72,36 @@ app = msal.PublicClientApplication("my_client_id", token_cache=cache) ``` +### Creating an encrypted persistence file to store your own data + +Here is an example of this pattern for multiple platforms (taken from the complete [sample here](https://github.com/AzureAD/microsoft-authentication-extensions-for-python/blob/dev/sample/persistence_sample.py)): + +```python +def build_persistence(location, fallback_to_plaintext=False): + """Build a suitable persistence instance based your current OS""" + try: + return build_encrypted_persistence(location) + except: # pylint: disable=bare-except + if not fallback_to_plaintext: + raise + logging.warning("Encryption unavailable. Opting in to plain text.") + return FilePersistence(location) + +persistence = build_persistence("storage.bin", fallback_to_plaintext=False) +print("Type of persistence: {}".format(persistence.__class__.__name__)) +print("Is this persistence encrypted?", persistence.is_encrypted) + +data = { # It can be anything, here we demonstrate an arbitrary json object + "foo": "hello world", + "bar": "", + "service_principle_1": "blah blah...", + } + +persistence.save(json.dumps(data)) +assert json.loads(persistence.load()) == data +``` + + ## Community Help and Support We leverage Stack Overflow to work with the community on supporting Azure Active Directory and its SDKs, including this one! @@ -107,3 +130,4 @@ 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-0.3.1/README.md new/msal-extensions-1.0.0/README.md --- old/msal-extensions-0.3.1/README.md 2021-12-13 05:46:45.000000000 +0100 +++ new/msal-extensions-1.0.0/README.md 2022-02-15 00:45:30.000000000 +0100 @@ -1,11 +1,11 @@ # Microsoft Authentication Extensions for Python -The Microsoft Authentication Extensions for Python offers secure mechanisms for client applications to perform cross-platform token cache serialization and persistence. It gives additional support to the [Microsoft Authentication Library for Python (MSAL)](https://github.com/AzureAD/microsoft-authentication-library-for-python). +The Microsoft Authentication Extensions for Python offers secure mechanisms for client applications to perform cross-platform token cache serialization and persistence. It gives additional support to the [Microsoft Authentication Library for Python (MSAL)](https://github.com/AzureAD/microsoft-authentication-library-for-python). MSAL Python supports an in-memory cache by default and provides the [SerializableTokenCache](https://msal-python.readthedocs.io/en/latest/#msal.SerializableTokenCache) to perform cache serialization. You can read more about this in the MSAL Python [documentation](https://docs.microsoft.com/en-us/azure/active-directory/develop/msal-python-token-cache-serialization). Developers are required to implement their own cache persistance across multiple platforms and Microsoft Authentication Extensions makes this simpler. -The supported platforms are Windows, Mac and Linux. +The supported platforms are Windows, Mac and Linux. - Windows - [DPAPI](https://docs.microsoft.com/en-us/dotnet/standard/security/how-to-use-data-protection) is used for encryption. - MAC - The MAC KeyChain is used. - Linux - [LibSecret](https://wiki.gnome.org/Projects/Libsecret) is used for encryption. @@ -28,7 +28,9 @@ ## Usage -The Microsoft Authentication Extensions library provides the `PersistedTokenCache` which accepts a platform-dependent persistence instance. This token cache can then be used to instantiate the `PublicClientApplication` in MSAL Python. +### Creating an encrypted token cache file to be used by MSAL + +The Microsoft Authentication Extensions library provides the `PersistedTokenCache` which accepts a platform-dependent persistence instance. This token cache can then be used to instantiate the `PublicClientApplication` in MSAL Python. The token cache includes a file lock, and auto-reload behavior under the hood. @@ -39,24 +41,16 @@ ```python def build_persistence(location, fallback_to_plaintext=False): """Build a suitable persistence instance based your current OS""" - if sys.platform.startswith('win'): - return FilePersistenceWithDataProtection(location) - if sys.platform.startswith('darwin'): - return KeychainPersistence(location, "my_service_name", "my_account_name") - if sys.platform.startswith('linux'): - try: - return LibsecretPersistence( - location, - schema_name="my_schema_name", - attributes={"my_attr1": "foo", "my_attr2": "bar"}, - ) - except: # pylint: disable=bare-except - if not fallback_to_plaintext: - raise - logging.exception("Encryption unavailable. Opting in to plain text.") - return FilePersistence(location) + try: + return build_encrypted_persistence(location) + except: + if not fallback_to_plaintext: + raise + logging.warning("Encryption unavailable. Opting in to plain text.") + return FilePersistence(location) persistence = build_persistence("token_cache.bin") +print("Type of persistence: {}".format(persistence.__class__.__name__)) print("Is this persistence encrypted?", persistence.is_encrypted) cache = PersistedTokenCache(persistence) @@ -66,6 +60,36 @@ app = msal.PublicClientApplication("my_client_id", token_cache=cache) ``` +### Creating an encrypted persistence file to store your own data + +Here is an example of this pattern for multiple platforms (taken from the complete [sample here](https://github.com/AzureAD/microsoft-authentication-extensions-for-python/blob/dev/sample/persistence_sample.py)): + +```python +def build_persistence(location, fallback_to_plaintext=False): + """Build a suitable persistence instance based your current OS""" + try: + return build_encrypted_persistence(location) + except: # pylint: disable=bare-except + if not fallback_to_plaintext: + raise + logging.warning("Encryption unavailable. Opting in to plain text.") + return FilePersistence(location) + +persistence = build_persistence("storage.bin", fallback_to_plaintext=False) +print("Type of persistence: {}".format(persistence.__class__.__name__)) +print("Is this persistence encrypted?", persistence.is_encrypted) + +data = { # It can be anything, here we demonstrate an arbitrary json object + "foo": "hello world", + "bar": "", + "service_principle_1": "blah blah...", + } + +persistence.save(json.dumps(data)) +assert json.loads(persistence.load()) == data +``` + + ## Community Help and Support We leverage Stack Overflow to work with the community on supporting Azure Active Directory and its SDKs, including this one! @@ -92,4 +116,4 @@ ## 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. \ No newline at end of file +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-0.3.1/msal_extensions/__init__.py new/msal-extensions-1.0.0/msal_extensions/__init__.py --- old/msal-extensions-0.3.1/msal_extensions/__init__.py 2021-12-13 05:46:45.000000000 +0100 +++ new/msal-extensions-1.0.0/msal_extensions/__init__.py 2022-02-15 00:45:30.000000000 +0100 @@ -1,10 +1,9 @@ """Provides auxiliary functionality to the `msal` package.""" -__version__ = "0.3.1" - -import sys +__version__ = "1.0.0" from .persistence import ( FilePersistence, + build_encrypted_persistence, FilePersistenceWithDataProtection, KeychainPersistence, LibsecretPersistence, @@ -12,9 +11,3 @@ from .cache_lock import CrossPlatLock from .token_cache import PersistedTokenCache -if sys.platform.startswith('win'): - from .token_cache import WindowsTokenCache as TokenCache -elif sys.platform.startswith('darwin'): - from .token_cache import OSXTokenCache as TokenCache -else: - from .token_cache import FileTokenCache as TokenCache diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/msal-extensions-0.3.1/msal_extensions/persistence.py new/msal-extensions-1.0.0/msal_extensions/persistence.py --- old/msal-extensions-0.3.1/msal_extensions/persistence.py 2021-12-13 05:46:45.000000000 +0100 +++ new/msal-extensions-1.0.0/msal_extensions/persistence.py 2022-02-15 00:45:30.000000000 +0100 @@ -9,6 +9,7 @@ import abc import os import errno +import hashlib import logging import sys try: @@ -50,21 +51,52 @@ else: raise +def _auto_hash(input_string): + return hashlib.sha256(input_string.encode('utf-8')).hexdigest() + # We do not aim to wrap every os-specific exception. -# Here we define only the most common one, -# otherwise caller would need to catch os-specific persistence exceptions. -class PersistenceNotFound(IOError): # Use IOError rather than OSError as base, +# Here we standardize only the most common ones, +# otherwise caller would need to catch os-specific underlying exceptions. +class PersistenceError(IOError): # Use IOError rather than OSError as base, + """The base exception for persistence.""" # because historically an IOError was bubbled up and expected. # https://github.com/AzureAD/microsoft-authentication-extensions-for-python/blob/0.2.2/msal_extensions/token_cache.py#L38 # Now we want to maintain backward compatibility even when using Python 2.x # It makes no difference in Python 3.3+ where IOError is an alias of OSError. + def __init__(self, err_no=None, message=None, location=None): # pylint: disable=useless-super-delegation + super(PersistenceError, self).__init__(err_no, message, location) + + +class PersistenceNotFound(PersistenceError): """This happens when attempting BasePersistence.load() on a non-existent persistence instance""" def __init__(self, err_no=None, message=None, location=None): super(PersistenceNotFound, self).__init__( - err_no or errno.ENOENT, - message or "Persistence not found", - location) + err_no=errno.ENOENT, + message=message or "Persistence not found", + location=location) + +class PersistenceEncryptionError(PersistenceError): + """This could be raised by persistence.save()""" + +class PersistenceDecryptionError(PersistenceError): + """This could be raised by persistence.load()""" + + +def build_encrypted_persistence(location): + """Build a suitable encrypted persistence instance based your current OS. + + If you do not need encryption, then simply use ``FilePersistence`` constructor. + """ + # Does not (yet?) support fallback_to_plaintext flag, + # because the persistence on Windows and macOS do not support built-in trial_run(). + if sys.platform.startswith('win'): + return FilePersistenceWithDataProtection(location) + if sys.platform.startswith('darwin'): + return KeychainPersistence(location) + if sys.platform.startswith('linux'): + return LibsecretPersistence(location) + raise RuntimeError("Unsupported platform: {}".format(sys.platform)) # pylint: disable=consider-using-f-string class BasePersistence(ABC): @@ -101,6 +133,11 @@ raise NotImplementedError +def _open(location): + return os.open(location, os.O_RDWR | os.O_CREAT | os.O_TRUNC, 0o600) + # The 600 seems no-op on NTFS/Windows, and that is fine + + class FilePersistence(BasePersistence): """A generic persistence, storing data in a plain-text file""" @@ -113,7 +150,7 @@ def save(self, content): # type: (str) -> None """Save the content into this persistence""" - with open(self._location, 'w+') as handle: # pylint: disable=unspecified-encoding + with os.fdopen(_open(self._location), 'w+') as handle: handle.write(content) def load(self): @@ -168,8 +205,14 @@ def save(self, content): # type: (str) -> None - data = self._dp_agent.protect(content) - with open(self._location, 'wb+') as handle: + try: + data = self._dp_agent.protect(content) + 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), + ) + with os.fdopen(_open(self._location), 'wb+') as handle: handle.write(data) def load(self): @@ -177,7 +220,6 @@ try: with open(self._location, 'rb') as handle: data = handle.read() - return self._dp_agent.unprotect(data) except EnvironmentError as exp: # EnvironmentError in Py 2.7 works across platform if exp.errno == errno.ENOENT: raise PersistenceNotFound( @@ -190,6 +232,17 @@ "DPAPI error likely caused by file content not previously encrypted. " "App developer should migrate by calling save(plaintext) first.") raise + try: + return self._dp_agent.unprotect(data) + except OSError as exception: + raise PersistenceDecryptionError( + err_no=getattr(exception, "winerror", None), # Exists in Python 3 on Windows + 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), + location=self._location, + ) class KeychainPersistence(BasePersistence): @@ -197,19 +250,18 @@ and protected by native Keychain libraries on OSX""" is_encrypted = True - def __init__(self, signal_location, service_name, account_name): + def __init__(self, signal_location, service_name=None, account_name=None): """Initialization could fail due to unsatisfied dependency. :param signal_location: See :func:`persistence.LibsecretPersistence.__init__` """ - if not (service_name and account_name): # It would hang on OSX - raise ValueError("service_name and account_name are required") from .osx import Keychain, KeychainError # pylint: disable=import-outside-toplevel self._file_persistence = FilePersistence(signal_location) # Favor composition self._Keychain = Keychain # pylint: disable=invalid-name self._KeychainError = KeychainError # pylint: disable=invalid-name - self._service_name = service_name - self._account_name = account_name + default_service_name = "msal-extensions" # This is also our package name + self._service_name = service_name or default_service_name + self._account_name = account_name or _auto_hash(signal_location) def save(self, content): with self._Keychain() as locker: @@ -247,7 +299,7 @@ and protected by native libsecret libraries on Linux""" is_encrypted = True - def __init__(self, signal_location, schema_name, attributes, **kwargs): + def __init__(self, signal_location, schema_name=None, attributes=None, **kwargs): """Initialization could fail due to unsatisfied dependency. :param string signal_location: @@ -262,7 +314,8 @@ from .libsecret import ( # This uncertain import is deferred till runtime LibSecretAgent, trial_run) trial_run() - self._agent = LibSecretAgent(schema_name, attributes, **kwargs) + self._agent = LibSecretAgent( + schema_name or _auto_hash(signal_location), attributes or {}, **kwargs) self._file_persistence = FilePersistence(signal_location) # Favor composition def save(self, content): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/msal-extensions-0.3.1/msal_extensions/token_cache.py new/msal-extensions-1.0.0/msal_extensions/token_cache.py --- old/msal-extensions-0.3.1/msal_extensions/token_cache.py 2021-12-13 05:46:45.000000000 +0100 +++ new/msal-extensions-1.0.0/msal_extensions/token_cache.py 2022-02-15 00:45:30.000000000 +0100 @@ -1,15 +1,12 @@ """Generic functions and types for working with a TokenCache that is not platform specific.""" import os -import warnings import time import logging import msal from .cache_lock import CrossPlatLock -from .persistence import ( - _mkdir_p, PersistenceNotFound, FilePersistence, - FilePersistenceWithDataProtection, KeychainPersistence) +from .persistence import _mkdir_p, PersistenceNotFound logger = logging.getLogger(__name__) @@ -89,35 +86,3 @@ return super(PersistedTokenCache, self).find(credential_type, **kwargs) return [] # Not really reachable here. Just to keep pylint happy. - -class FileTokenCache(PersistedTokenCache): - """A token cache which uses plain text file to store your tokens.""" - def __init__(self, cache_location, **ignored): # pylint: disable=unused-argument - warnings.warn("You are using an unprotected token cache", RuntimeWarning) - warnings.warn("Use PersistedTokenCache(...) instead", DeprecationWarning) - super(FileTokenCache, self).__init__(FilePersistence(cache_location)) - -UnencryptedTokenCache = FileTokenCache # For backward compatibility - - -class WindowsTokenCache(PersistedTokenCache): - """A token cache which uses Windows DPAPI to encrypt your tokens.""" - def __init__( - self, cache_location, entropy='', - **ignored): # pylint: disable=unused-argument - warnings.warn("Use PersistedTokenCache(...) instead", DeprecationWarning) - super(WindowsTokenCache, self).__init__( - FilePersistenceWithDataProtection(cache_location, entropy=entropy)) - - -class OSXTokenCache(PersistedTokenCache): - """A token cache which uses native Keychain libraries to encrypt your tokens.""" - def __init__(self, - cache_location, - service_name='Microsoft.Developer.IdentityService', - account_name='MSALCache', - **ignored): # pylint: disable=unused-argument - warnings.warn("Use PersistedTokenCache(...) instead", DeprecationWarning) - super(OSXTokenCache, self).__init__( - KeychainPersistence(cache_location, service_name, account_name)) - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/msal-extensions-0.3.1/msal_extensions/windows.py new/msal-extensions-1.0.0/msal_extensions/windows.py --- old/msal-extensions-0.3.1/msal_extensions/windows.py 2021-12-13 05:46:45.000000000 +0100 +++ new/msal-extensions-1.0.0/msal_extensions/windows.py 2022-02-15 00:45:30.000000000 +0100 @@ -39,6 +39,15 @@ _MEMCPY(blob_buffer, pb_data, cb_data) return blob_buffer.raw +_err_description = { + # Keys came from real world observation, values came from winerror.h (http://errors (Microsoft internal)) + -2146893813: "Key not valid for use in specified state.", + -2146892987: "The requested operation cannot be completed. " + "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", + } # This code is modeled from a StackOverflow question, which can be found here: # https://stackoverflow.com/questions/463832/using-dpapi-with-python @@ -82,7 +91,7 @@ _LOCAL_FREE(result.pbData) err_code = _GET_LAST_ERROR() - raise OSError(256, '', '', err_code) + raise OSError(None, _err_description.get(err_code), None, err_code) def unprotect(self, cipher_text): # type: (bytes) -> str @@ -111,4 +120,4 @@ finally: _LOCAL_FREE(result.pbData) err_code = _GET_LAST_ERROR() - raise OSError(256, '', '', 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-0.3.1/msal_extensions.egg-info/PKG-INFO new/msal-extensions-1.0.0/msal_extensions.egg-info/PKG-INFO --- old/msal-extensions-0.3.1/msal_extensions.egg-info/PKG-INFO 2021-12-13 05:46:55.000000000 +0100 +++ new/msal-extensions-1.0.0/msal_extensions.egg-info/PKG-INFO 2022-02-15 00:45:41.000000000 +0100 @@ -1,24 +1,23 @@ Metadata-Version: 2.1 Name: msal-extensions -Version: 0.3.1 -Summary: UNKNOWN -Home-page: UNKNOWN +Version: 1.0.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 Project-URL: Changelog, https://github.com/AzureAD/microsoft-authentication-extensions-for-python/releases Platform: UNKNOWN Classifier: License :: OSI Approved :: MIT License -Classifier: Development Status :: 4 - Beta +Classifier: Development Status :: 5 - Production/Stable Description-Content-Type: text/markdown License-File: LICENSE # Microsoft Authentication Extensions for Python -The Microsoft Authentication Extensions for Python offers secure mechanisms for client applications to perform cross-platform token cache serialization and persistence. It gives additional support to the [Microsoft Authentication Library for Python (MSAL)](https://github.com/AzureAD/microsoft-authentication-library-for-python). +The Microsoft Authentication Extensions for Python offers secure mechanisms for client applications to perform cross-platform token cache serialization and persistence. It gives additional support to the [Microsoft Authentication Library for Python (MSAL)](https://github.com/AzureAD/microsoft-authentication-library-for-python). MSAL Python supports an in-memory cache by default and provides the [SerializableTokenCache](https://msal-python.readthedocs.io/en/latest/#msal.SerializableTokenCache) to perform cache serialization. You can read more about this in the MSAL Python [documentation](https://docs.microsoft.com/en-us/azure/active-directory/develop/msal-python-token-cache-serialization). Developers are required to implement their own cache persistance across multiple platforms and Microsoft Authentication Extensions makes this simpler. -The supported platforms are Windows, Mac and Linux. +The supported platforms are Windows, Mac and Linux. - Windows - [DPAPI](https://docs.microsoft.com/en-us/dotnet/standard/security/how-to-use-data-protection) is used for encryption. - MAC - The MAC KeyChain is used. - Linux - [LibSecret](https://wiki.gnome.org/Projects/Libsecret) is used for encryption. @@ -41,7 +40,9 @@ ## Usage -The Microsoft Authentication Extensions library provides the `PersistedTokenCache` which accepts a platform-dependent persistence instance. This token cache can then be used to instantiate the `PublicClientApplication` in MSAL Python. +### Creating an encrypted token cache file to be used by MSAL + +The Microsoft Authentication Extensions library provides the `PersistedTokenCache` which accepts a platform-dependent persistence instance. This token cache can then be used to instantiate the `PublicClientApplication` in MSAL Python. The token cache includes a file lock, and auto-reload behavior under the hood. @@ -52,24 +53,16 @@ ```python def build_persistence(location, fallback_to_plaintext=False): """Build a suitable persistence instance based your current OS""" - if sys.platform.startswith('win'): - return FilePersistenceWithDataProtection(location) - if sys.platform.startswith('darwin'): - return KeychainPersistence(location, "my_service_name", "my_account_name") - if sys.platform.startswith('linux'): - try: - return LibsecretPersistence( - location, - schema_name="my_schema_name", - attributes={"my_attr1": "foo", "my_attr2": "bar"}, - ) - except: # pylint: disable=bare-except - if not fallback_to_plaintext: - raise - logging.exception("Encryption unavailable. Opting in to plain text.") - return FilePersistence(location) + try: + return build_encrypted_persistence(location) + except: + if not fallback_to_plaintext: + raise + logging.warning("Encryption unavailable. Opting in to plain text.") + return FilePersistence(location) persistence = build_persistence("token_cache.bin") +print("Type of persistence: {}".format(persistence.__class__.__name__)) print("Is this persistence encrypted?", persistence.is_encrypted) cache = PersistedTokenCache(persistence) @@ -79,6 +72,36 @@ app = msal.PublicClientApplication("my_client_id", token_cache=cache) ``` +### Creating an encrypted persistence file to store your own data + +Here is an example of this pattern for multiple platforms (taken from the complete [sample here](https://github.com/AzureAD/microsoft-authentication-extensions-for-python/blob/dev/sample/persistence_sample.py)): + +```python +def build_persistence(location, fallback_to_plaintext=False): + """Build a suitable persistence instance based your current OS""" + try: + return build_encrypted_persistence(location) + except: # pylint: disable=bare-except + if not fallback_to_plaintext: + raise + logging.warning("Encryption unavailable. Opting in to plain text.") + return FilePersistence(location) + +persistence = build_persistence("storage.bin", fallback_to_plaintext=False) +print("Type of persistence: {}".format(persistence.__class__.__name__)) +print("Is this persistence encrypted?", persistence.is_encrypted) + +data = { # It can be anything, here we demonstrate an arbitrary json object + "foo": "hello world", + "bar": "", + "service_principle_1": "blah blah...", + } + +persistence.save(json.dumps(data)) +assert json.loads(persistence.load()) == data +``` + + ## Community Help and Support We leverage Stack Overflow to work with the community on supporting Azure Active Directory and its SDKs, including this one! @@ -107,3 +130,4 @@ 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-0.3.1/setup.cfg new/msal-extensions-1.0.0/setup.cfg --- old/msal-extensions-0.3.1/setup.cfg 2021-12-13 05:46:55.792970000 +0100 +++ new/msal-extensions-1.0.0/setup.cfg 2022-02-15 00:45:41.537072200 +0100 @@ -6,7 +6,8 @@ project_urls = Changelog = https://github.com/AzureAD/microsoft-authentication-extensions-for-python/releases classifiers = License :: OSI Approved :: MIT License - Development Status :: 4 - Beta + Development Status :: 5 - Production/Stable +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] tag_build =