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.
- {dtpyappframework-1.3.2/src/dtPyAppFramework.egg-info → dtpyappframework-1.4}/PKG-INFO +1 -1
- dtpyappframework-1.4/src/dtPyAppFramework/cloud/__init__.py +40 -0
- dtpyappframework-1.4/src/dtPyAppFramework/cloud/aws.py +53 -0
- dtpyappframework-1.4/src/dtPyAppFramework/cloud/azure.py +56 -0
- dtpyappframework-1.4/src/dtPyAppFramework/cloud/cloud_session.py +17 -0
- {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/settings/__init__.py +11 -4
- {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/settings/secrets/__init__.py +31 -10
- {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/settings/secrets/aws_secret_store.py +10 -29
- {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/settings/secrets/azure_secret_store.py +11 -33
- {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/settings/secrets/local_secret_store.py +36 -6
- {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/settings/secrets/secret_store.py +2 -0
- dtpyappframework-1.4/src/dtPyAppFramework/version.dat +1 -0
- {dtpyappframework-1.3.2 → dtpyappframework-1.4/src/dtPyAppFramework.egg-info}/PKG-INFO +1 -1
- {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework.egg-info/SOURCES.txt +4 -0
- dtpyappframework-1.3.2/src/dtPyAppFramework/version.dat +0 -1
- {dtpyappframework-1.3.2 → dtpyappframework-1.4}/LICENCE.txt +0 -0
- {dtpyappframework-1.3.2 → dtpyappframework-1.4}/MANIFEST.in +0 -0
- {dtpyappframework-1.3.2 → dtpyappframework-1.4}/README.md +0 -0
- {dtpyappframework-1.3.2 → dtpyappframework-1.4}/pyproject.toml +0 -0
- {dtpyappframework-1.3.2 → dtpyappframework-1.4}/requirements.txt +0 -0
- {dtpyappframework-1.3.2 → dtpyappframework-1.4}/setup.cfg +0 -0
- {dtpyappframework-1.3.2 → dtpyappframework-1.4}/setup.py +0 -0
- {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/__init__.py +0 -0
- {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/application.py +0 -0
- {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/decorators/__init__.py +0 -0
- {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/logging/__init__.py +0 -0
- {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/logging/default_logging.py +0 -0
- {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/misc/__init__.py +0 -0
- {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/paths/__init__.py +0 -0
- {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/process/__init__.py +0 -0
- {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/process/multiprocessing.py +0 -0
- {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/resources/__init__.py +0 -0
- {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/settings/settings_reader.py +0 -0
- {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework.egg-info/dependency_links.txt +0 -0
- {dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework.egg-info/requires.txt +0 -0
- {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
|
+
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 =
|
|
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
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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:
|
{dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/settings/secrets/__init__.py
RENAMED
|
@@ -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=-
|
|
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=
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
45
|
-
if not self.
|
|
46
|
-
raise SecretsStoreException('Azure KeyVault Store is missing required
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if not
|
|
51
|
-
raise SecretsStoreException('Azure KeyVault Store
|
|
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."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
1.4
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: dtPyAppFramework
|
|
3
|
-
Version: 1.
|
|
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
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
1.3.2
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/logging/default_logging.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/process/multiprocessing.py
RENAMED
|
File without changes
|
|
File without changes
|
{dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework/settings/settings_reader.py
RENAMED
|
File without changes
|
{dtpyappframework-1.3.2 → dtpyappframework-1.4}/src/dtPyAppFramework.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|