dtPyAppFramework 1.3.2__tar.gz → 1.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. {dtpyappframework-1.3.2/src/dtPyAppFramework.egg-info → dtpyappframework-1.4}/PKG-INFO +1 -1
  2. dtpyappframework-1.4/src/dtPyAppFramework/cloud/__init__.py +40 -0
  3. dtpyappframework-1.4/src/dtPyAppFramework/cloud/aws.py +53 -0
  4. dtpyappframework-1.4/src/dtPyAppFramework/cloud/azure.py +56 -0
  5. dtpyappframework-1.4/src/dtPyAppFramework/cloud/cloud_session.py +17 -0
  6. {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/settings/__init__.py +11 -4
  7. {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/settings/secrets/__init__.py +31 -10
  8. {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/settings/secrets/aws_secret_store.py +10 -29
  9. {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/settings/secrets/azure_secret_store.py +11 -33
  10. {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/settings/secrets/local_secret_store.py +36 -6
  11. {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/settings/secrets/secret_store.py +2 -0
  12. dtpyappframework-1.4/src/dtPyAppFramework/version.dat +1 -0
  13. {dtpyappframework-1.3.2 → dtpyappframework-1.4/src/dtPyAppFramework.egg-info}/PKG-INFO +1 -1
  14. {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework.egg-info/SOURCES.txt +4 -0
  15. dtpyappframework-1.3.2/src/dtPyAppFramework/version.dat +0 -1
  16. {dtpyappframework-1.3.2 → dtpyappframework-1.4}/LICENCE.txt +0 -0
  17. {dtpyappframework-1.3.2 → dtpyappframework-1.4}/MANIFEST.in +0 -0
  18. {dtpyappframework-1.3.2 → dtpyappframework-1.4}/README.md +0 -0
  19. {dtpyappframework-1.3.2 → dtpyappframework-1.4}/pyproject.toml +0 -0
  20. {dtpyappframework-1.3.2 → dtpyappframework-1.4}/requirements.txt +0 -0
  21. {dtpyappframework-1.3.2 → dtpyappframework-1.4}/setup.cfg +0 -0
  22. {dtpyappframework-1.3.2 → dtpyappframework-1.4}/setup.py +0 -0
  23. {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/__init__.py +0 -0
  24. {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/application.py +0 -0
  25. {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/decorators/__init__.py +0 -0
  26. {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/logging/__init__.py +0 -0
  27. {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/logging/default_logging.py +0 -0
  28. {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/misc/__init__.py +0 -0
  29. {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/paths/__init__.py +0 -0
  30. {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/process/__init__.py +0 -0
  31. {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/process/multiprocessing.py +0 -0
  32. {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/resources/__init__.py +0 -0
  33. {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/settings/settings_reader.py +0 -0
  34. {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework.egg-info/dependency_links.txt +0 -0
  35. {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework.egg-info/requires.txt +0 -0
  36. {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dtPyAppFramework
3
- Version: 1.3.2
3
+ Version: 1.4
4
4
  Summary: A Python library for common features in application development.
5
5
  Author-email: Digital-Thought <dev@digital-thought.org>
6
6
  Maintainer-email: Digital-Thought <dev@digital-thought.org>
@@ -0,0 +1,40 @@
1
+ from ..decorators import singleton
2
+ from ..settings import Settings
3
+
4
+ from .aws import AWSCloudSession
5
+ from .azure import AzureCloudSession
6
+
7
+ import logging
8
+
9
+
10
+ @singleton()
11
+ class CloudSessionManager(object):
12
+
13
+ def __init__(self):
14
+ logging.info('Loading Cloud Session Manager')
15
+
16
+ defined_sessions = Settings().get('cloud_sessions', [])
17
+ self.sessions = self.__load_sessions(defined_sessions)
18
+
19
+ @staticmethod
20
+ def __load_sessions(defined_sessions) -> list:
21
+ sessions = []
22
+ for defined_session in defined_sessions:
23
+ session_type = defined_session["session_type"]
24
+ name = defined_session["name"]
25
+ logging.info(f'Loading session "{name}" or type "{session_type}".')
26
+ if session_type == "aws":
27
+ sessions.append(AWSCloudSession(**defined_session))
28
+ elif session_type == "azure":
29
+ sessions.append(AzureCloudSession(**defined_session))
30
+ else:
31
+ logging.error(f'Unrecognised session type "{session_type}" for session "{name}".')
32
+
33
+ return sessions
34
+
35
+ def get_session(self, name):
36
+ for session in self.sessions:
37
+ if session.name == name:
38
+ return session.get_session()
39
+
40
+ logging.error(f'No Cloud Session with the name "{name}" was found in the defined sessions.')
@@ -0,0 +1,53 @@
1
+ from .cloud_session import AbstractCloudSession
2
+ from ..misc import run_cmd
3
+
4
+ import boto3
5
+ import logging
6
+
7
+
8
+ class AWSCloudSession(AbstractCloudSession):
9
+
10
+ def __init__(self, name, session_type, settings):
11
+ super().__init__(name, session_type, settings)
12
+
13
+ self.aws_profile = self.get_setting('aws_profile')
14
+ if not self.aws_profile:
15
+ logging.error('Missing required aws_profile parameter.')
16
+ return
17
+
18
+ self.aws_region = self.get_setting('aws_region')
19
+ if not self.aws_region:
20
+ logging.error('Missing required aws_region parameter.')
21
+ return
22
+
23
+ self.aws_session = self.__initialise_session()
24
+ if self.aws_session is None:
25
+ logging.error(f'An AWS Session could not be established for cloud session name "{self.name}".')
26
+ else:
27
+ self.session_available = True
28
+ logging.error(f'Successfully established AWS Session for cloud session name "{self.name}".')
29
+
30
+ def __initialise_session(self):
31
+ if self.aws_profile == 'key':
32
+ aws_access_key_id = self.get_setting('aws_access_key_id')
33
+ aws_secret_access_key = self.get_setting('aws_secret_access_key')
34
+ if not aws_access_key_id or not aws_secret_access_key:
35
+ logging.error('Missing either aws_access_key_id and aws_secret_access_key parameters.')
36
+ return None
37
+ return boto3.session.Session(region_name=self.aws_region, aws_access_key_id=aws_access_key_id,
38
+ aws_secret_access_key=aws_secret_access_key)
39
+ elif self.aws_profile == 'ec2':
40
+ self.aws_session = boto3.session.Session(region_name=self.aws_region)
41
+ elif self.aws_profile.startswith('sso'):
42
+ aws_sso_profile = self.aws_profile.split(':')[1]
43
+ aws_sso_resp = run_cmd(f'aws sso login --profile {aws_sso_profile}')
44
+ if not aws_sso_resp or "Successfully logged into Start URL" not in aws_sso_resp:
45
+ logging.error(f"Unable to initialise SSO for the AWS profile {aws_sso_profile}.")
46
+ return None
47
+ return boto3.session.Session(region_name=self.aws_region)
48
+ else:
49
+ logging.error(f"Unrecognised AWS Profile type {self.aws_profile}.")
50
+ return None
51
+
52
+ def get_session(self):
53
+ return self.aws_session
@@ -0,0 +1,56 @@
1
+ from .cloud_session import AbstractCloudSession
2
+
3
+ from azure.identity import CertificateCredential, ClientSecretCredential, InteractiveBrowserCredential
4
+ import logging
5
+
6
+
7
+ class AzureCloudSession(AbstractCloudSession):
8
+
9
+ def __init__(self, name, session_type, settings):
10
+ super().__init__(name, session_type, settings)
11
+
12
+ # Get Azure identity type
13
+ self.azure_identity_type = self.get_setting('azure_identity_type')
14
+ if not self.azure_identity_type:
15
+ logging.error('Missing required azure_identity_type parameter.')
16
+ return
17
+
18
+ self.azure_tenant_id = self.get_setting('azure_tenant_id')
19
+ if not self.azure_tenant_id:
20
+ logging.error('Missing required azure_tenant_id parameter.')
21
+ return
22
+
23
+ self.azure_session = self.__initialise_session()
24
+ if self.azure_session is None:
25
+ logging.error(f'An Azure Session could not be established for cloud session name "{self.name}".')
26
+ else:
27
+ self.session_available = True
28
+ logging.error(f'Successfully established Azure Session for cloud session name "{self.name}".')
29
+
30
+ def __initialise_session(self):
31
+ if self.azure_identity_type == 'certificate':
32
+ azure_client_id = self.get_setting('azure_client_id')
33
+ certificate_path = self.get_setting('certificate_path')
34
+ if not azure_client_id or not certificate_path:
35
+ logging.error('Requires both azure_client_id and certificate_path parameters.')
36
+ return None
37
+ certificate_password = self.get_setting('certificate_password')
38
+ return CertificateCredential(tenant_id=self.azure_tenant_id, client_id=azure_client_id,
39
+ certificate_path=certificate_path, password=certificate_password)
40
+ elif self.azure_identity_type == 'key':
41
+ azure_client_id = self.get_setting('azure_client_id')
42
+ client_secret = self.get_setting('client_secret')
43
+ if not azure_client_id or not client_secret:
44
+ logging.error(
45
+ 'Requires both azure_client_id and client_secret parameters.')
46
+ return None
47
+ return ClientSecretCredential(tenant_id=self.azure_tenant_id, client_id=azure_client_id,
48
+ client_secret=client_secret)
49
+ elif self.azure_identity_type == 'interactive_browser':
50
+ return InteractiveBrowserCredential(tenant_id=self.azure_tenant_id)
51
+ else:
52
+ logging.error(f"Unrecognised Azure Identity Type {self.azure_identity_type}.")
53
+ return None
54
+
55
+ def get_session(self):
56
+ return self.azure_session
@@ -0,0 +1,17 @@
1
+ from abc import abstractmethod
2
+
3
+
4
+ class AbstractCloudSession:
5
+
6
+ def __init__(self, name, session_type, settings):
7
+ self.name = name
8
+ self.session_type = session_type
9
+ self.settings = settings
10
+ self.session_available = False
11
+
12
+ def get_setting(self, key):
13
+ return self.settings.get(key, None)
14
+
15
+ @abstractmethod
16
+ def get_session(self):
17
+ raise NotImplementedError
@@ -34,7 +34,8 @@ class Settings(dict):
34
34
 
35
35
  self.settings_readers = []
36
36
  self.persistent_settings_stores = []
37
- self.secret_manager = SecretsManager(application_paths=self.application_paths, application_settings=self)
37
+ self.secret_manager = None
38
+ self.cloud_session_manager = None
38
39
 
39
40
  super().__init__()
40
41
 
@@ -47,6 +48,11 @@ class Settings(dict):
47
48
  self.settings_readers.append(SettingsReader(self.application_paths.usr_data_root_path, 100))
48
49
  self.settings_readers.sort(key=lambda x: x.priority)
49
50
 
51
+ from ..cloud import CloudSessionManager
52
+ self.cloud_session_manager = CloudSessionManager()
53
+ self.secret_manager = SecretsManager(application_paths=self.application_paths, application_settings=self,
54
+ cloud_session_manager=self.cloud_session_manager)
55
+
50
56
  def get_requests_tor_proxy(self) -> dict:
51
57
  """
52
58
  Gets TOR Proxy configuration in a format compatible with Requests.
@@ -168,9 +174,10 @@ class Settings(dict):
168
174
  Returns:
169
175
  Value of the item.
170
176
  """
171
- persistent_value = self.secret_manager.get_secret(key)
172
- if persistent_value:
173
- return persistent_value
177
+ if self.secret_manager is not None:
178
+ persistent_value = self.secret_manager.get_secret(key)
179
+ if persistent_value:
180
+ return persistent_value
174
181
 
175
182
  value = None
176
183
  for reader in self.settings_readers:
@@ -17,7 +17,7 @@ class SecretsManager(object):
17
17
  stores (list): A list to hold different secret stores.
18
18
  """
19
19
 
20
- def __init__(self, application_paths=None, application_settings=None) -> None:
20
+ def __init__(self, application_paths=None, application_settings=None, cloud_session_manager=None) -> None:
21
21
  """
22
22
  Initialize the SecretsManager.
23
23
 
@@ -28,6 +28,7 @@ class SecretsManager(object):
28
28
  super().__init__()
29
29
  self.application_paths = application_paths
30
30
  self.application_settings = application_settings
31
+ self.cloud_session_manager = cloud_session_manager
31
32
  self.stores = []
32
33
  self.store_names = []
33
34
 
@@ -36,7 +37,7 @@ class SecretsManager(object):
36
37
 
37
38
  # Add local user secret store
38
39
  self.stores.append(LocalSecretStore(store_name="User_Local_Store",
39
- store_priority=-1,
40
+ store_priority=-0,
40
41
  root_store_path=self.application_paths.usr_data_root_path,
41
42
  application_settings=self.application_settings,
42
43
  app_short_name=self.application_paths.app_short_name))
@@ -44,7 +45,7 @@ class SecretsManager(object):
44
45
  try:
45
46
  # Add local app secret store (if it exists)
46
47
  self.stores.append(LocalSecretStore(store_name="App_Local_Store",
47
- store_priority=0,
48
+ store_priority=1,
48
49
  root_store_path=self.application_paths.app_data_root_path,
49
50
  application_settings=self.application_settings,
50
51
  app_short_name=self.application_paths.app_short_name))
@@ -54,6 +55,16 @@ class SecretsManager(object):
54
55
  # Sort stores based on priority
55
56
  self._sort_stores()
56
57
 
58
+ def get_local_stores_index(self):
59
+ _index = {"User_Local_Store": {}, "App_Local_Store": {}}
60
+ for key in _index:
61
+ store: LocalSecretStore = self.get_store(key)
62
+ _index[key]['available'] = store.store_available
63
+ _index[key]['index'] = store.get_index()
64
+ _index[key]['read_only'] = store.store_read_only
65
+
66
+ return _index
67
+
57
68
  def _sort_stores(self):
58
69
  self.stores.sort(key=lambda x: x.store_priority)
59
70
  for store in self.stores:
@@ -71,13 +82,16 @@ class SecretsManager(object):
71
82
  self.stores.append(AWSSecretsStore(store_priority=self.application_settings.get(
72
83
  f'secrets_manager.cloud_stores.{store_name}.priority'),
73
84
  store_name=store_name,
74
- application_settings=self.application_settings))
85
+ cloud_session_manager=self.cloud_session_manager,
86
+ application_settings=self.application_settings,
87
+ ))
75
88
 
76
89
  # Add Azure secret store
77
90
  if self.application_settings.get(f'secrets_manager.cloud_stores.{store_name}.store_type') == 'azure':
78
91
  self.stores.append(AzureSecretsStore(store_priority=self.application_settings.get(
79
92
  f'secrets_manager.cloud_stores.{store_name}.priority'),
80
93
  store_name=store_name,
94
+ cloud_session_manager=self.cloud_session_manager,
81
95
  application_settings=self.application_settings))
82
96
 
83
97
  # Sort stores based on priority
@@ -118,12 +132,16 @@ class SecretsManager(object):
118
132
 
119
133
  for store in self.stores:
120
134
  if store_name and store_name == store.store_name:
121
- value = store.get_secret(key, None)
135
+ if store.store_available:
136
+ value = store.get_secret(key, None)
137
+ else:
138
+ logging.error(f'Store {store.store_name} is not available to retrieve secret.')
122
139
  break
123
140
  elif not store_name:
124
- value = store.get_secret(key, None)
125
- if value:
126
- break
141
+ if store.store_available:
142
+ value = store.get_secret(key, None)
143
+ if value:
144
+ break
127
145
 
128
146
  if not value:
129
147
  logging.debug(f'The Secret {key} was not found. Returning default value.')
@@ -144,7 +162,10 @@ class SecretsManager(object):
144
162
  store_name = 'User_Local_Store'
145
163
  for store in self.stores:
146
164
  if store_name == store.store_name:
147
- store.set_secret(key, value)
165
+ if store.store_available and not store.store_read_only:
166
+ store.set_secret(key, value)
167
+ else:
168
+ logging.warning(f'Secrets Store {store.store_name} is either not available or is read only.')
148
169
  break
149
170
 
150
171
  def delete_secret(self, key, store_name='User_Local_Store'):
@@ -159,6 +180,6 @@ class SecretsManager(object):
159
180
  True if the secret is deleted, else False.
160
181
  """
161
182
  for store in self.stores:
162
- if store_name and store_name == store.priority():
183
+ if store_name and store_name == store.name():
163
184
  value = store.delete_secret(key)
164
185
  break
@@ -6,6 +6,7 @@ from ...misc import run_cmd
6
6
  import boto3
7
7
  import logging
8
8
 
9
+
9
10
  class AWSSecretsStore(AbstractSecretStore):
10
11
  """
11
12
  A class representing an AWS Secrets Store for managing secrets.
@@ -20,7 +21,7 @@ class AWSSecretsStore(AbstractSecretStore):
20
21
  aws_secretsmanager (boto3.client): Boto3 client for AWS Secrets Manager.
21
22
  """
22
23
 
23
- def __init__(self, store_priority, store_name, application_settings) -> None:
24
+ def __init__(self, store_priority, store_name, application_settings, cloud_session_manager) -> None:
24
25
  """
25
26
  Initialize the AWSSecretsStore.
26
27
 
@@ -31,43 +32,23 @@ class AWSSecretsStore(AbstractSecretStore):
31
32
  """
32
33
  super().__init__(store_name, "AWS_Secrets_Store", store_priority, application_settings)
33
34
 
35
+ self.cloud_session_manager = cloud_session_manager
36
+
34
37
  # Check if AWS CLI is installed
35
38
  if not which("aws"):
36
39
  raise SecretsStoreException("AWS Secrets Store requires the AWS Command Line Utility to be installed.")
37
40
 
38
- # Get AWS profile
39
- self.aws_profile = self.get_store_setting('aws_profile')
40
- if not self.aws_profile:
41
- raise SecretsStoreException('AWS Secrets Store is missing required aws_profile parameter.')
41
+ self.session_name = self.get_store_setting('session_name')
42
+ if not self.session_name:
43
+ raise SecretsStoreException('AWS Secrets Store is missing required session_name parameter.')
42
44
 
43
- # Get AWS region
44
- self.aws_region = self.get_store_setting('aws_region')
45
- if not self.aws_region:
46
- raise SecretsStoreException('AWS Secrets Store is missing required aws_region parameter.')
45
+ self.aws_session = self.cloud_session_manager.get_session(self.session_name)
47
46
 
48
- self.aws_session = None
47
+ if not self.aws_session:
48
+ raise SecretsStoreException(f'AWS Secrets Store does not have a valid session for session name "{self.session_name}".')
49
49
 
50
50
  self.secret_name = self.get_store_setting('secret_name')
51
51
 
52
- # Initialize AWS session based on profile type
53
- if self.aws_profile == 'key':
54
- aws_access_key_id = self.get_store_setting('aws_access_key_id')
55
- aws_secret_access_key = self.get_store_setting('aws_secret_access_key')
56
- if not aws_access_key_id or not aws_secret_access_key:
57
- raise SecretsStoreException('AWS Secrets Store of type key requires both aws_access_key_id and aws_secret_access_key parameters.')
58
- self.aws_session = boto3.session.Session(region_name=self.aws_region, aws_access_key_id=aws_access_key_id,
59
- aws_secret_access_key=aws_secret_access_key)
60
- elif self.aws_profile == 'ec2':
61
- self.aws_session = boto3.session.Session(region_name=self.aws_region)
62
- elif self.aws_profile.startswith('sso'):
63
- aws_sso_profile = self.aws_profile.split(':')[1]
64
- aws_sso_resp = run_cmd(f'aws sso login --profile {aws_sso_profile}')
65
- if not aws_sso_resp or "Successfully logged into Start URL" not in aws_sso_resp:
66
- raise SecretsStoreException(f"Unable to initialise SSO for the AWS profile {aws_sso_profile}.")
67
- self.aws_session = boto3.session.Session(region_name=self.aws_region)
68
- else:
69
- raise SecretsStoreException(f"Unrecognised AWS Profile type {self.aws_profile}.")
70
-
71
52
  # Initialize AWS Secrets Manager client
72
53
  self.aws_secretsmanager = self.aws_session.client('secretsmanager')
73
54
  try:
@@ -4,6 +4,7 @@ from azure.keyvault.secrets import SecretClient
4
4
  from azure.identity import DefaultAzureCredential, CertificateCredential, ClientSecretCredential, InteractiveBrowserCredential
5
5
  import logging
6
6
 
7
+
7
8
  class AzureSecretsStore(AbstractSecretStore):
8
9
  """
9
10
  A class representing an Azure Secrets Store for managing secrets.
@@ -24,7 +25,7 @@ class AzureSecretsStore(AbstractSecretStore):
24
25
  delete_secret(key): Delete a secret from Azure KeyVault.
25
26
  """
26
27
 
27
- def __init__(self, store_priority, store_name, application_settings) -> None:
28
+ def __init__(self, store_priority, store_name, application_settings, cloud_session_manager) -> None:
28
29
  """
29
30
  Initialize the AzureSecretsStore.
30
31
 
@@ -34,6 +35,7 @@ class AzureSecretsStore(AbstractSecretStore):
34
35
  application_settings (Settings): An instance of Settings providing application settings.
35
36
  """
36
37
  super().__init__(store_name, "Azure_Secrets_Store", store_priority, application_settings)
38
+ self.cloud_session_manager = cloud_session_manager
37
39
 
38
40
  # Get Azure KeyVault URL
39
41
  self.azure_keyvault = self.get_store_setting('azure_keyvault')
@@ -41,38 +43,14 @@ class AzureSecretsStore(AbstractSecretStore):
41
43
  raise SecretsStoreException('Azure KeyVault Store is missing required azure_keyvault parameter.')
42
44
 
43
45
  # Get Azure identity type
44
- self.azure_identity_type = self.get_store_setting('azure_identity_type')
45
- if not self.azure_identity_type:
46
- raise SecretsStoreException('Azure KeyVault Store is missing required azure_identity_type parameter.')
47
-
48
- # Get Azure Tenant ID
49
- self.azure_tenant_id = self.get_store_setting('azure_tenant_id')
50
- if not self.azure_tenant_id:
51
- raise SecretsStoreException('Azure KeyVault Store is missing required azure_tenant_id parameter.')
52
-
53
- credential = None
54
-
55
- # Initialize Azure credential based on identity type
56
- if self.azure_identity_type == 'certificate':
57
- azure_client_id = self.get_store_setting('azure_client_id')
58
- certificate_path = self.get_store_setting('certificate_path')
59
- if not azure_client_id or not certificate_path:
60
- raise SecretsStoreException('Azure KeyVault Store of type key requires both azure_client_id and certificate_path parameters.')
61
- certificate_password = self.get_store_setting('certificate_password')
62
- credential = CertificateCredential(tenant_id=self.azure_tenant_id, client_id=azure_client_id,
63
- certificate_path=certificate_path, password=certificate_password)
64
- elif self.azure_identity_type == 'key':
65
- azure_client_id = self.get_store_setting('azure_client_id')
66
- client_secret = self.get_store_setting('client_secret')
67
- if not azure_client_id or not client_secret:
68
- raise SecretsStoreException(
69
- 'Azure KeyVault Store of type key requires both azure_client_id and client_secret parameters.')
70
- credential = ClientSecretCredential(tenant_id=self.azure_tenant_id, client_id=azure_client_id,
71
- client_secret=client_secret)
72
- elif self.azure_identity_type == 'interactive_browser':
73
- credential = InteractiveBrowserCredential(tenant_id=self.azure_tenant_id)
74
- else:
75
- raise SecretsStoreException(f"Unrecognised Azure Identity Type {self.azure_identity_type}.")
46
+ self.session_name = self.get_store_setting('session_name')
47
+ if not self.session_name:
48
+ raise SecretsStoreException('Azure KeyVault Store is missing required session_name parameter.')
49
+
50
+ credential = self.cloud_session_manager.get_session(self.session_name)
51
+
52
+ if not credential:
53
+ raise SecretsStoreException(f'Azure KeyVault Store does not have a valid session for session name "{self.session_name}".')
76
54
 
77
55
  # Azure KeyVault URI
78
56
  self.kv_uri = f"https://{self.azure_keyvault}.vault.azure.net"
@@ -1,3 +1,4 @@
1
+ import json
1
2
  import os
2
3
  import sys
3
4
  import logging
@@ -53,18 +54,19 @@ class LocalSecretStore(AbstractSecretStore):
53
54
  if password is None:
54
55
  password = self.__guid()
55
56
 
56
- # If the store does not exist, initialize it
57
- if not os.path.exists(self.store_path):
58
- self.store = self.__initialise_secrets_store(password)
59
-
60
57
  try:
58
+ # If the store does not exist, initialize it
59
+ if not os.path.exists(self.store_path):
60
+ self.store = self.__initialise_secrets_store(password)
61
+
61
62
  # Try to load the existing store
62
63
  self.store = pykeystore.KeyStoreEx.load(self.store_path, password)
64
+ self.store_available = True
65
+ self.store_read_only = not self.__is_writeable()
66
+ logging.info(f'Successfully opened Secrets Store: {self.store_path}')
63
67
  except Exception as ex:
64
68
  raise SecretsStoreException(f'Failed to open Secrets Store: {self.store_path}. Error: {str(ex)}')
65
69
 
66
- logging.info(f'Successfully opened Secrets Store: {self.store_path}')
67
-
68
70
  def __initialise_secrets_store(self, password):
69
71
  """
70
72
  Initialize the secrets store if it doesn't exist.
@@ -129,6 +131,7 @@ class LocalSecretStore(AbstractSecretStore):
129
131
  entry = self.store.getPassword(account=key)
130
132
  if not entry or entry == 'NONE':
131
133
  return default_value
134
+
132
135
  return entry
133
136
 
134
137
  def set_secret(self, key, value):
@@ -144,6 +147,9 @@ class LocalSecretStore(AbstractSecretStore):
144
147
 
145
148
  self.store.setPassword(account=key, password=value)
146
149
  self.__save()
150
+ index = self.get_index()
151
+ index.append(key)
152
+ self.__set_index(index)
147
153
 
148
154
  def delete_secret(self, key):
149
155
  """
@@ -154,7 +160,31 @@ class LocalSecretStore(AbstractSecretStore):
154
160
  """
155
161
  entry = self.store.setPassword(account=key, password='NONE')
156
162
  self.__save()
163
+ index = self.get_index()
164
+ index.remove(key)
165
+ self.__set_index(index)
166
+
167
+ def __set_index(self, index: list):
168
+ logging.info(index)
169
+ self.store.setPassword(account=f'{self.store_name}.INDEX', password=json.dumps(index))
170
+ self.__save()
171
+
172
+ def get_index(self) -> list:
173
+ index = self.get_secret(f'{self.store_name}.INDEX', None)
174
+ if index is None:
175
+ self.__set_index([])
176
+ return []
177
+
178
+ return json.loads(index)
157
179
 
158
180
  def __save(self):
159
181
  """Save the changes made to the local secret store."""
160
182
  self.store.save(self.store_path, self.__guid())
183
+
184
+ def __is_writeable(self):
185
+ try:
186
+ self.__save()
187
+ return True
188
+ except Exception as ex:
189
+ logging.warning(str(ex))
190
+ return False
@@ -41,6 +41,8 @@ class AbstractSecretStore(object):
41
41
  self.store_priority = store_priority
42
42
  self.store_type = store_type
43
43
  self.application_settings = application_settings
44
+ self.store_available: bool = False
45
+ self.store_read_only: bool = True
44
46
 
45
47
  def priority(self):
46
48
  """Get the priority of the secret store."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dtPyAppFramework
3
- Version: 1.3.2
3
+ Version: 1.4
4
4
  Summary: A Python library for common features in application development.
5
5
  Author-email: Digital-Thought <dev@digital-thought.org>
6
6
  Maintainer-email: Digital-Thought <dev@digital-thought.org>
@@ -12,6 +12,10 @@ src/dtPyAppFramework.egg-info/SOURCES.txt
12
12
  src/dtPyAppFramework.egg-info/dependency_links.txt
13
13
  src/dtPyAppFramework.egg-info/requires.txt
14
14
  src/dtPyAppFramework.egg-info/top_level.txt
15
+ src/dtPyAppFramework/cloud/__init__.py
16
+ src/dtPyAppFramework/cloud/aws.py
17
+ src/dtPyAppFramework/cloud/azure.py
18
+ src/dtPyAppFramework/cloud/cloud_session.py
15
19
  src/dtPyAppFramework/decorators/__init__.py
16
20
  src/dtPyAppFramework/logging/__init__.py
17
21
  src/dtPyAppFramework/logging/default_logging.py