Skip to content

Commit d0696ae

Browse files
committed
Add build_encrypted_persistence() factory
1 parent 289a94f commit d0696ae

File tree

6 files changed

+44
-53
lines changed

6 files changed

+44
-53
lines changed

msal_extensions/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
from .persistence import (
55
FilePersistence,
6+
build_encrypted_persistence,
67
FilePersistenceWithDataProtection,
78
KeychainPersistence,
89
LibsecretPersistence,

msal_extensions/persistence.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,22 @@ class PersistenceDecryptionError(PersistenceError):
8383
"""This could be raised by persistence.load()"""
8484

8585

86+
def build_encrypted_persistence(location):
87+
"""Build a suitable encrypted persistence instance based your current OS.
88+
89+
If you do not need encryption, then simply use ``FilePersistence`` constructor.
90+
"""
91+
# Does not (yet?) support fallback_to_plaintext flag,
92+
# because the persistence on Windows and macOS do not support built-in trial_run().
93+
if sys.platform.startswith('win'):
94+
return FilePersistenceWithDataProtection(location)
95+
if sys.platform.startswith('darwin'):
96+
return KeychainPersistence(location)
97+
if sys.platform.startswith('linux'):
98+
return LibsecretPersistence(location)
99+
raise RuntimeError("Unsupported platform: {}".format(sys.platform)) # pylint: disable=consider-using-f-string
100+
101+
86102
class BasePersistence(ABC):
87103
"""An abstract persistence defining the common interface of this family"""
88104

msal_extensions/token_cache.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
"""Generic functions and types for working with a TokenCache that is not platform specific."""
22
import os
3-
import warnings
43
import time
54
import logging
65

76
import msal
87

98
from .cache_lock import CrossPlatLock
10-
from .persistence import (
11-
_mkdir_p, PersistenceNotFound, FilePersistence,
12-
FilePersistenceWithDataProtection, KeychainPersistence)
9+
from .persistence import _mkdir_p, PersistenceNotFound
1310

1411

1512
logger = logging.getLogger(__name__)

sample/persistence_sample.py

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,21 @@
1-
import sys
21
import logging
32
import json
43

5-
from msal_extensions import *
4+
from msal_extensions import build_encrypted_persistence, FilePersistence, CrossPlatLock
65

76

87
def build_persistence(location, fallback_to_plaintext=False):
98
"""Build a suitable persistence instance based your current OS"""
10-
if sys.platform.startswith('win'):
11-
return FilePersistenceWithDataProtection(location)
12-
if sys.platform.startswith('darwin'):
13-
return KeychainPersistence(location)
14-
if sys.platform.startswith('linux'):
15-
try:
16-
return LibsecretPersistence(
17-
# By using same location as the fall back option below,
18-
# this would override the unencrypted data stored by the
19-
# fall back option. It is probably OK, or even desirable
20-
# (in order to aggressively wipe out plain-text persisted data),
21-
# unless there would frequently be a desktop session and
22-
# a remote ssh session being active simultaneously.
23-
location,
24-
)
25-
except: # pylint: disable=bare-except
26-
if not fallback_to_plaintext:
27-
raise
28-
logging.warning("Encryption unavailable. Opting in to plain text.")
29-
return FilePersistence(location)
9+
# Note: This sample stores both encrypted persistence and plaintext persistence
10+
# into same location, therefore their data would likely override with each other.
11+
try:
12+
return build_encrypted_persistence(location)
13+
except: # pylint: disable=bare-except
14+
# Known issue: Currently, only Linux
15+
if not fallback_to_plaintext:
16+
raise
17+
logging.warning("Encryption unavailable. Opting in to plain text.")
18+
return FilePersistence(location)
3019

3120
persistence = build_persistence("storage.bin", fallback_to_plaintext=False)
3221
print("Type of persistence: {}".format(persistence.__class__.__name__))

sample/token_cache_sample.py

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,21 @@
22
import logging
33
import json
44

5-
from msal_extensions import *
5+
from msal_extensions import build_encrypted_persistence, FilePersistence
66

77

88
def build_persistence(location, fallback_to_plaintext=False):
99
"""Build a suitable persistence instance based your current OS"""
10-
if sys.platform.startswith('win'):
11-
return FilePersistenceWithDataProtection(location)
12-
if sys.platform.startswith('darwin'):
13-
return KeychainPersistence(location)
14-
if sys.platform.startswith('linux'):
15-
try:
16-
return LibsecretPersistence(
17-
# By using same location as the fall back option below,
18-
# this would override the unencrypted data stored by the
19-
# fall back option. It is probably OK, or even desirable
20-
# (in order to aggressively wipe out plain-text persisted data),
21-
# unless there would frequently be a desktop session and
22-
# a remote ssh session being active simultaneously.
23-
location,
24-
)
25-
except: # pylint: disable=bare-except
26-
if not fallback_to_plaintext:
27-
raise
28-
logging.exception("Encryption unavailable. Opting in to plain text.")
29-
return FilePersistence(location)
10+
# Note: This sample stores both encrypted persistence and plaintext persistence
11+
# into same location, therefore their data would likely override with each other.
12+
try:
13+
return build_encrypted_persistence(location)
14+
except: # pylint: disable=bare-except
15+
# Known issue: Currently, only Linux
16+
if not fallback_to_plaintext:
17+
raise
18+
logging.warning("Encryption unavailable. Opting in to plain text.")
19+
return FilePersistence(location)
3020

3121
persistence = build_persistence("token_cache.bin")
3222
print("Type of persistence: {}".format(persistence.__class__.__name__))

tests/test_agnostic_backend.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,16 @@ def _test_token_cache_roundtrip(cache):
3434
assert token1['access_token'] == token2['access_token']
3535

3636
def test_file_token_cache_roundtrip(temp_location):
37-
from msal_extensions.token_cache import FileTokenCache
38-
_test_token_cache_roundtrip(FileTokenCache(temp_location))
37+
_test_token_cache_roundtrip(PersistedTokenCache(FilePersistence(temp_location)))
3938

40-
def test_current_platform_cache_roundtrip_with_alias_class(temp_location):
41-
from msal_extensions import TokenCache
42-
_test_token_cache_roundtrip(TokenCache(temp_location))
39+
def test_current_platform_cache_roundtrip_with_persistence_builder(temp_location):
40+
_test_token_cache_roundtrip(PersistedTokenCache(build_encrypted_persistence(temp_location)))
4341

4442
def test_persisted_token_cache(temp_location):
4543
_test_token_cache_roundtrip(PersistedTokenCache(FilePersistence(temp_location)))
4644

4745
def test_file_not_found_error_is_not_raised():
4846
persistence = FilePersistence('non_existing_file')
49-
cache = PersistedTokenCache(persistence=persistence)
47+
cache = PersistedTokenCache(persistence)
5048
# An exception raised here will fail the test case as it is supposed to be a NO-OP
5149
cache.find('')

0 commit comments

Comments
 (0)