clear-skies 2.0.4__py3-none-any.whl → 2.0.5__py3-none-any.whl

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.

Potentially problematic release.


This version of clear-skies might be problematic. Click here for more details.

Files changed (253) hide show
  1. clear_skies-2.0.5.dist-info/METADATA +74 -0
  2. clear_skies-2.0.5.dist-info/RECORD +4 -0
  3. {clear_skies-2.0.4.dist-info → clear_skies-2.0.5.dist-info}/WHEEL +1 -1
  4. clear_skies-2.0.4.dist-info/METADATA +0 -36
  5. clear_skies-2.0.4.dist-info/RECORD +0 -251
  6. clearskies/__init__.py +0 -61
  7. clearskies/action.py +0 -7
  8. clearskies/authentication/__init__.py +0 -15
  9. clearskies/authentication/authentication.py +0 -46
  10. clearskies/authentication/authorization.py +0 -16
  11. clearskies/authentication/authorization_pass_through.py +0 -20
  12. clearskies/authentication/jwks.py +0 -163
  13. clearskies/authentication/public.py +0 -5
  14. clearskies/authentication/secret_bearer.py +0 -553
  15. clearskies/autodoc/__init__.py +0 -8
  16. clearskies/autodoc/formats/__init__.py +0 -5
  17. clearskies/autodoc/formats/oai3_json/__init__.py +0 -7
  18. clearskies/autodoc/formats/oai3_json/oai3_json.py +0 -87
  19. clearskies/autodoc/formats/oai3_json/oai3_schema_resolver.py +0 -15
  20. clearskies/autodoc/formats/oai3_json/parameter.py +0 -35
  21. clearskies/autodoc/formats/oai3_json/request.py +0 -68
  22. clearskies/autodoc/formats/oai3_json/response.py +0 -28
  23. clearskies/autodoc/formats/oai3_json/schema/__init__.py +0 -11
  24. clearskies/autodoc/formats/oai3_json/schema/array.py +0 -9
  25. clearskies/autodoc/formats/oai3_json/schema/default.py +0 -13
  26. clearskies/autodoc/formats/oai3_json/schema/enum.py +0 -7
  27. clearskies/autodoc/formats/oai3_json/schema/object.py +0 -35
  28. clearskies/autodoc/formats/oai3_json/test.json +0 -1985
  29. clearskies/autodoc/py.typed +0 -0
  30. clearskies/autodoc/request/__init__.py +0 -15
  31. clearskies/autodoc/request/header.py +0 -6
  32. clearskies/autodoc/request/json_body.py +0 -6
  33. clearskies/autodoc/request/parameter.py +0 -8
  34. clearskies/autodoc/request/request.py +0 -47
  35. clearskies/autodoc/request/url_parameter.py +0 -6
  36. clearskies/autodoc/request/url_path.py +0 -6
  37. clearskies/autodoc/response/__init__.py +0 -5
  38. clearskies/autodoc/response/response.py +0 -9
  39. clearskies/autodoc/schema/__init__.py +0 -31
  40. clearskies/autodoc/schema/array.py +0 -10
  41. clearskies/autodoc/schema/base64.py +0 -8
  42. clearskies/autodoc/schema/boolean.py +0 -5
  43. clearskies/autodoc/schema/date.py +0 -5
  44. clearskies/autodoc/schema/datetime.py +0 -5
  45. clearskies/autodoc/schema/double.py +0 -5
  46. clearskies/autodoc/schema/enum.py +0 -17
  47. clearskies/autodoc/schema/integer.py +0 -6
  48. clearskies/autodoc/schema/long.py +0 -5
  49. clearskies/autodoc/schema/number.py +0 -6
  50. clearskies/autodoc/schema/object.py +0 -13
  51. clearskies/autodoc/schema/password.py +0 -5
  52. clearskies/autodoc/schema/schema.py +0 -11
  53. clearskies/autodoc/schema/string.py +0 -5
  54. clearskies/backends/__init__.py +0 -65
  55. clearskies/backends/api_backend.py +0 -1178
  56. clearskies/backends/backend.py +0 -136
  57. clearskies/backends/cursor_backend.py +0 -335
  58. clearskies/backends/memory_backend.py +0 -797
  59. clearskies/backends/secrets_backend.py +0 -106
  60. clearskies/column.py +0 -1233
  61. clearskies/columns/__init__.py +0 -71
  62. clearskies/columns/audit.py +0 -206
  63. clearskies/columns/belongs_to_id.py +0 -483
  64. clearskies/columns/belongs_to_model.py +0 -132
  65. clearskies/columns/belongs_to_self.py +0 -105
  66. clearskies/columns/boolean.py +0 -113
  67. clearskies/columns/category_tree.py +0 -275
  68. clearskies/columns/category_tree_ancestors.py +0 -51
  69. clearskies/columns/category_tree_children.py +0 -127
  70. clearskies/columns/category_tree_descendants.py +0 -48
  71. clearskies/columns/created.py +0 -95
  72. clearskies/columns/created_by_authorization_data.py +0 -116
  73. clearskies/columns/created_by_header.py +0 -99
  74. clearskies/columns/created_by_ip.py +0 -92
  75. clearskies/columns/created_by_routing_data.py +0 -97
  76. clearskies/columns/created_by_user_agent.py +0 -92
  77. clearskies/columns/date.py +0 -234
  78. clearskies/columns/datetime.py +0 -282
  79. clearskies/columns/email.py +0 -76
  80. clearskies/columns/float.py +0 -153
  81. clearskies/columns/has_many.py +0 -505
  82. clearskies/columns/has_many_self.py +0 -56
  83. clearskies/columns/has_one.py +0 -14
  84. clearskies/columns/integer.py +0 -160
  85. clearskies/columns/json.py +0 -126
  86. clearskies/columns/many_to_many_ids.py +0 -337
  87. clearskies/columns/many_to_many_ids_with_data.py +0 -274
  88. clearskies/columns/many_to_many_models.py +0 -158
  89. clearskies/columns/many_to_many_pivots.py +0 -134
  90. clearskies/columns/phone.py +0 -159
  91. clearskies/columns/select.py +0 -92
  92. clearskies/columns/string.py +0 -102
  93. clearskies/columns/timestamp.py +0 -164
  94. clearskies/columns/updated.py +0 -110
  95. clearskies/columns/uuid.py +0 -86
  96. clearskies/configs/README.md +0 -105
  97. clearskies/configs/__init__.py +0 -162
  98. clearskies/configs/actions.py +0 -43
  99. clearskies/configs/any.py +0 -13
  100. clearskies/configs/any_dict.py +0 -22
  101. clearskies/configs/any_dict_or_callable.py +0 -23
  102. clearskies/configs/authentication.py +0 -23
  103. clearskies/configs/authorization.py +0 -23
  104. clearskies/configs/boolean.py +0 -16
  105. clearskies/configs/boolean_or_callable.py +0 -18
  106. clearskies/configs/callable_config.py +0 -18
  107. clearskies/configs/columns.py +0 -34
  108. clearskies/configs/conditions.py +0 -30
  109. clearskies/configs/config.py +0 -24
  110. clearskies/configs/datetime.py +0 -18
  111. clearskies/configs/datetime_or_callable.py +0 -19
  112. clearskies/configs/endpoint.py +0 -23
  113. clearskies/configs/endpoint_list.py +0 -28
  114. clearskies/configs/float.py +0 -16
  115. clearskies/configs/float_or_callable.py +0 -18
  116. clearskies/configs/integer.py +0 -16
  117. clearskies/configs/integer_or_callable.py +0 -18
  118. clearskies/configs/joins.py +0 -30
  119. clearskies/configs/list_any_dict.py +0 -30
  120. clearskies/configs/list_any_dict_or_callable.py +0 -31
  121. clearskies/configs/model_class.py +0 -35
  122. clearskies/configs/model_column.py +0 -65
  123. clearskies/configs/model_columns.py +0 -56
  124. clearskies/configs/model_destination_name.py +0 -25
  125. clearskies/configs/model_to_id_column.py +0 -43
  126. clearskies/configs/readable_model_column.py +0 -9
  127. clearskies/configs/readable_model_columns.py +0 -9
  128. clearskies/configs/schema.py +0 -23
  129. clearskies/configs/searchable_model_columns.py +0 -9
  130. clearskies/configs/security_headers.py +0 -39
  131. clearskies/configs/select.py +0 -26
  132. clearskies/configs/select_list.py +0 -47
  133. clearskies/configs/string.py +0 -29
  134. clearskies/configs/string_dict.py +0 -32
  135. clearskies/configs/string_list.py +0 -32
  136. clearskies/configs/string_list_or_callable.py +0 -35
  137. clearskies/configs/string_or_callable.py +0 -18
  138. clearskies/configs/timedelta.py +0 -18
  139. clearskies/configs/timezone.py +0 -18
  140. clearskies/configs/url.py +0 -23
  141. clearskies/configs/validators.py +0 -45
  142. clearskies/configs/writeable_model_column.py +0 -9
  143. clearskies/configs/writeable_model_columns.py +0 -9
  144. clearskies/configurable.py +0 -76
  145. clearskies/contexts/__init__.py +0 -11
  146. clearskies/contexts/cli.py +0 -117
  147. clearskies/contexts/context.py +0 -98
  148. clearskies/contexts/wsgi.py +0 -76
  149. clearskies/contexts/wsgi_ref.py +0 -82
  150. clearskies/decorators.py +0 -33
  151. clearskies/di/__init__.py +0 -14
  152. clearskies/di/additional_config.py +0 -130
  153. clearskies/di/additional_config_auto_import.py +0 -17
  154. clearskies/di/di.py +0 -973
  155. clearskies/di/inject/__init__.py +0 -23
  156. clearskies/di/inject/by_class.py +0 -21
  157. clearskies/di/inject/by_name.py +0 -18
  158. clearskies/di/inject/di.py +0 -13
  159. clearskies/di/inject/environment.py +0 -14
  160. clearskies/di/inject/input_output.py +0 -20
  161. clearskies/di/inject/now.py +0 -13
  162. clearskies/di/inject/requests.py +0 -13
  163. clearskies/di/inject/secrets.py +0 -14
  164. clearskies/di/inject/utcnow.py +0 -13
  165. clearskies/di/inject/uuid.py +0 -15
  166. clearskies/di/injectable.py +0 -29
  167. clearskies/di/injectable_properties.py +0 -131
  168. clearskies/di/test_module/__init__.py +0 -6
  169. clearskies/di/test_module/another_module/__init__.py +0 -2
  170. clearskies/di/test_module/module_class.py +0 -5
  171. clearskies/end.py +0 -183
  172. clearskies/endpoint.py +0 -1314
  173. clearskies/endpoint_group.py +0 -338
  174. clearskies/endpoints/__init__.py +0 -25
  175. clearskies/endpoints/advanced_search.py +0 -526
  176. clearskies/endpoints/callable.py +0 -388
  177. clearskies/endpoints/create.py +0 -205
  178. clearskies/endpoints/delete.py +0 -139
  179. clearskies/endpoints/get.py +0 -271
  180. clearskies/endpoints/health_check.py +0 -183
  181. clearskies/endpoints/list.py +0 -574
  182. clearskies/endpoints/restful_api.py +0 -427
  183. clearskies/endpoints/schema.py +0 -189
  184. clearskies/endpoints/simple_search.py +0 -286
  185. clearskies/endpoints/update.py +0 -193
  186. clearskies/environment.py +0 -104
  187. clearskies/exceptions/__init__.py +0 -19
  188. clearskies/exceptions/authentication.py +0 -2
  189. clearskies/exceptions/authorization.py +0 -2
  190. clearskies/exceptions/client_error.py +0 -2
  191. clearskies/exceptions/input_errors.py +0 -4
  192. clearskies/exceptions/missing_dependency.py +0 -2
  193. clearskies/exceptions/moved_permanently.py +0 -3
  194. clearskies/exceptions/moved_temporarily.py +0 -3
  195. clearskies/exceptions/not_found.py +0 -2
  196. clearskies/functional/__init__.py +0 -7
  197. clearskies/functional/routing.py +0 -92
  198. clearskies/functional/string.py +0 -112
  199. clearskies/functional/validations.py +0 -76
  200. clearskies/input_outputs/__init__.py +0 -13
  201. clearskies/input_outputs/cli.py +0 -171
  202. clearskies/input_outputs/exceptions/__init__.py +0 -2
  203. clearskies/input_outputs/exceptions/cli_input_error.py +0 -2
  204. clearskies/input_outputs/exceptions/cli_not_found.py +0 -2
  205. clearskies/input_outputs/headers.py +0 -45
  206. clearskies/input_outputs/input_output.py +0 -138
  207. clearskies/input_outputs/programmatic.py +0 -69
  208. clearskies/input_outputs/py.typed +0 -0
  209. clearskies/input_outputs/wsgi.py +0 -77
  210. clearskies/model.py +0 -1922
  211. clearskies/py.typed +0 -0
  212. clearskies/query/__init__.py +0 -12
  213. clearskies/query/condition.py +0 -223
  214. clearskies/query/join.py +0 -136
  215. clearskies/query/query.py +0 -196
  216. clearskies/query/sort.py +0 -27
  217. clearskies/schema.py +0 -82
  218. clearskies/secrets/__init__.py +0 -6
  219. clearskies/secrets/additional_configs/__init__.py +0 -32
  220. clearskies/secrets/additional_configs/mysql_connection_dynamic_producer.py +0 -61
  221. clearskies/secrets/additional_configs/mysql_connection_dynamic_producer_via_ssh_cert_bastion.py +0 -160
  222. clearskies/secrets/akeyless.py +0 -182
  223. clearskies/secrets/exceptions/__init__.py +0 -1
  224. clearskies/secrets/exceptions/not_found.py +0 -2
  225. clearskies/secrets/secrets.py +0 -38
  226. clearskies/security_header.py +0 -15
  227. clearskies/security_headers/__init__.py +0 -11
  228. clearskies/security_headers/cache_control.py +0 -67
  229. clearskies/security_headers/cors.py +0 -50
  230. clearskies/security_headers/csp.py +0 -94
  231. clearskies/security_headers/hsts.py +0 -22
  232. clearskies/security_headers/x_content_type_options.py +0 -0
  233. clearskies/security_headers/x_frame_options.py +0 -0
  234. clearskies/test_base.py +0 -8
  235. clearskies/typing.py +0 -11
  236. clearskies/validator.py +0 -37
  237. clearskies/validators/__init__.py +0 -33
  238. clearskies/validators/after_column.py +0 -62
  239. clearskies/validators/before_column.py +0 -13
  240. clearskies/validators/in_the_future.py +0 -32
  241. clearskies/validators/in_the_future_at_least.py +0 -11
  242. clearskies/validators/in_the_future_at_most.py +0 -10
  243. clearskies/validators/in_the_past.py +0 -32
  244. clearskies/validators/in_the_past_at_least.py +0 -10
  245. clearskies/validators/in_the_past_at_most.py +0 -10
  246. clearskies/validators/maximum_length.py +0 -26
  247. clearskies/validators/maximum_value.py +0 -29
  248. clearskies/validators/minimum_length.py +0 -26
  249. clearskies/validators/minimum_value.py +0 -29
  250. clearskies/validators/required.py +0 -34
  251. clearskies/validators/timedelta.py +0 -59
  252. clearskies/validators/unique.py +0 -30
  253. {clear_skies-2.0.4.dist-info → clear_skies-2.0.5.dist-info/licenses}/LICENSE +0 -0
@@ -1,32 +0,0 @@
1
- from .mysql_connection_dynamic_producer import MySQLConnectionDynamicProducer
2
- from .mysql_connection_dynamic_producer_via_ssh_cert_bastion import MySQLConnectionDynamicProducerViaSSHCertBastion
3
-
4
-
5
- def mysql_connection_dynamic_producer(producer_name=None, database_host=None, database_name=None):
6
- return MySQLConnectionDynamicProducer(
7
- producer_name=producer_name,
8
- database_host=database_host,
9
- database_name=database_name,
10
- )
11
-
12
-
13
- def mysql_connection_dynamic_producer_via_ssh_cert_bastion(
14
- producer_name=None,
15
- bastion_host=None,
16
- bastion_username=None,
17
- public_key_file_path=None,
18
- cert_issuer_name=None,
19
- database_host=None,
20
- database_name=None,
21
- local_proxy_port=None,
22
- ):
23
- return MySQLConnectionDynamicProducerViaSSHCertBastion(
24
- producer_name=producer_name,
25
- bastion_host=bastion_host,
26
- bastion_username=bastion_username,
27
- cert_issuer_name=cert_issuer_name,
28
- public_key_file_path=public_key_file_path,
29
- database_host=database_host,
30
- database_name=database_name,
31
- local_proxy_port=local_proxy_port,
32
- )
@@ -1,61 +0,0 @@
1
- import clearskies.di
2
-
3
-
4
- class MySQLConnectionDynamicProducer(clearskies.di.additional_config.AdditionalConfig):
5
- _producer_name = None
6
- _database_host = None
7
- _database_name = None
8
-
9
- def __init__(self, producer_name=None, database_host=None, database_name=None):
10
- self._producer_name = producer_name
11
- self._database_host = database_host
12
- self._database_name = database_name
13
-
14
- def provide_connection_details(self, environment, secrets):
15
- if not secrets:
16
- raise ValueError(
17
- "I was asked to connect to a database via an AKeyless dynamic producer, \
18
- but AKeyless itself wasn't configured. \
19
- Try setting the AKeyless auth method via clearskies.secrets.akeyless_[jwt|saml|aws_iam]_auth()"
20
- )
21
-
22
- producer_name = (
23
- self._producer_name
24
- if self._producer_name is not None
25
- else environment.get("akeyless_mysql_dynamic_producer", silent=True)
26
- )
27
- if not producer_name:
28
- raise ValueError(
29
- "I was asked to connect to a database via an AKeyless dynamic producer, \
30
- but I wasn't told the path to the dynamic producer. \
31
- This can be set in an environment variable named 'akeyless_mysql_dynamic_producer'\
32
- or it can be set in the configuration via the producer_name kwarg."
33
- )
34
- database_name = (
35
- self._database_name if self._database_name is not None else environment.get("db_database", silent=True)
36
- )
37
- if not database_name:
38
- raise ValueError(
39
- "I was asked to connect to a database via an AKeyless dynamic producer, \
40
- but I wasn't told the name of the database. \
41
- This can be set in an environment variable named 'db_database' \
42
- or it can be set in the configuration via the database_name kwarg."
43
- )
44
- database_host = (
45
- self._database_host if self._database_host is not None else environment.get("db_host", silent=True)
46
- )
47
- if not database_host:
48
- raise ValueError(
49
- "I was asked to connect to a database via an AKeyless dynamic producer, \
50
- but I wasn't told the host name of the database. \
51
- This can be set in an environment variable named 'db_host' \
52
- or it can be set in the configuration via the database_host kwarg."
53
- )
54
- credentials = secrets.get_dynamic_secret(producer_name)
55
-
56
- return {
57
- "username": credentials["user"],
58
- "password": credentials["password"],
59
- "host": database_host,
60
- "database": database_name,
61
- }
@@ -1,160 +0,0 @@
1
- import os
2
- import socket
3
- import subprocess
4
- import time
5
- from pathlib import Path
6
-
7
- import clearskies.di
8
-
9
-
10
- class MySQLConnectionDynamicProducerViaSSHCertBastion(clearskies.di.additional_config.AdditionalConfig):
11
- _config = None
12
-
13
- def __init__(
14
- self,
15
- producer_name=None,
16
- bastion_host=None,
17
- bastion_username=None,
18
- public_key_file_path=None,
19
- local_proxy_port=None,
20
- cert_issuer_name=None,
21
- database_host=None,
22
- database_name=None,
23
- ):
24
- # not using kwargs because I want the argument list to be explicit
25
- self.config = {
26
- "producer_name": producer_name,
27
- "bastion_host": bastion_host,
28
- "bastion_username": bastion_username,
29
- "public_key_file_path": public_key_file_path,
30
- "local_proxy_port": local_proxy_port,
31
- "cert_issuer_name": cert_issuer_name,
32
- "database_host": database_host,
33
- "database_name": database_name,
34
- }
35
-
36
- def provide_connection_details(self, environment, secrets):
37
- if not secrets:
38
- raise ValueError(
39
- "I was asked to connect to a database via an AKeyless dynamic producer but AKeyless itself wasn't configured. Try setting the AKeyless auth method via clearskies.secrets.akeyless_[jwt|saml|aws_iam]_auth()"
40
- )
41
-
42
- home = str(Path.home())
43
- default_public_key_file_path = f"{home}/.ssh/id_rsa.pub"
44
-
45
- producer_name = self._fetch_config(environment, "producer_name", "akeyless_mysql_dynamic_producer")
46
- bastion_host = self._get_bastion_host(environment)
47
- bastion_username = self._fetch_config(environment, "bastion_username", "akeyless_mysql_bastion_username")
48
- public_key_file_path = self._fetch_config(
49
- environment,
50
- "public_key_file_path",
51
- "akeyless_mysql_bastion_public_key_file_path",
52
- default=default_public_key_file_path,
53
- )
54
- cert_issuer_name = self._fetch_config(
55
- environment, "cert_issuer_name", "akeyless_mysql_bastion_cert_issuer_name"
56
- )
57
- local_proxy_port = self._fetch_config(
58
- environment, "local_proxy_port", "akeyless_mysql_bastion_local_proxy_port", default=8888
59
- )
60
- database_host = self._fetch_config(environment, "database_host", "db_host")
61
- database_name = self._fetch_config(environment, "database_name", "db_database")
62
-
63
- # Create the SSH tunnel (yeah, it's obnoxious)
64
- self._create_tunnel(
65
- secrets,
66
- bastion_host,
67
- bastion_username,
68
- local_proxy_port,
69
- cert_issuer_name,
70
- public_key_file_path,
71
- database_host,
72
- )
73
-
74
- # and now we can fetch credentials
75
- credentials = secrets.get_dynamic_secret(producer_name)
76
-
77
- return {
78
- "username": credentials["user"],
79
- "password": credentials["password"],
80
- "host": "127.0.0.1",
81
- "database": database_name,
82
- "port": local_proxy_port,
83
- }
84
-
85
- def _get_bastion_host(self, environment):
86
- return self._fetch_config(environment, "bastion_host", "akeyless_mysql_bastion_host")
87
-
88
- def _fetch_config(self, environment, config_key_name, environment_key_name, default=None):
89
- if self.config[config_key_name]:
90
- return self.config[config_key_name]
91
- from_environment = environment.get(environment_key_name, silent=True)
92
- if from_environment:
93
- return from_environment
94
- if default is not None:
95
- return default
96
- raise ValueError(
97
- f"I was asked to connect to a database via an AKeyless dynamic producer through an SSH bastion"
98
- "with certificate auth, but I wasn't given a required configuration value: '{config_key_name}'."
99
- "This can be set in the call to "
100
- "`clearskies.backends.akeyless.mysql_connection_dynamic_producer_via_ssh_cert_bastion()` by providing the "
101
- "'{config_key_name}' argument, or by setting an environment variable named '{environment_key_name}'."
102
- )
103
-
104
- def _create_tunnel(
105
- self,
106
- secrets,
107
- bastion_host,
108
- bastion_username,
109
- local_proxy_port,
110
- cert_issuer_name,
111
- public_key_file_path,
112
- database_host,
113
- ):
114
- # first see if the socket is already open, since we don't close it.
115
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
116
- result = sock.connect_ex(("127.0.0.1", local_proxy_port))
117
- if result == 0:
118
- sock.close()
119
- return
120
-
121
- if not os.path.isfile(public_key_file_path):
122
- raise ValueError(
123
- f"I was asked to connect to AKeyless SSH with the public key file in '{public_key_file_path}',"
124
- "but this file does not exist"
125
- )
126
-
127
- ssh_certificate = secrets.get_ssh_certificate(cert_issuer_name, bastion_username, public_key_file_path)
128
-
129
- # We need to write the certificate out to the standard location that SSH expects it so that SSH can find it.
130
- # I haven't found a good library for doing this in Python, so I'm relying on the ssh command
131
- home = str(Path.home())
132
- with open(f"{home}/.ssh/id_rsa-cert.pub", "w") as fp:
133
- fp.write(ssh_certificate)
134
-
135
- # and now we can do this thing.
136
- tunnel_command = [
137
- "ssh",
138
- "-o",
139
- "ConnectTimeout=2",
140
- "-N",
141
- "-L",
142
- f"{local_proxy_port}:{database_host}:3306",
143
- "-p",
144
- "22",
145
- f"{bastion_username}@{bastion_host}",
146
- ]
147
- subprocess.Popen(tunnel_command)
148
- connected = False
149
- attempts = 0
150
- while not connected and attempts < 6:
151
- attempts += 1
152
- time.sleep(0.5)
153
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
154
- result = sock.connect_ex(("127.0.0.1", local_proxy_port))
155
- if result == 0:
156
- connected = True
157
- if not connected:
158
- raise ValueError(
159
- "Failed to open SSH tunnel. The following command was used: \n" + " ".join(tunnel_command)
160
- )
@@ -1,182 +0,0 @@
1
- import datetime
2
- from typing import Any
3
-
4
- import clearskies.configs
5
- from clearskies.di import InjectableProperties, inject
6
-
7
-
8
- class Akeyless(clearskies.Configurable, clearskies.di.InjectableProperties):
9
- requests = clearskies.di.inject.Requests()
10
- environment = clearskies.di.inject.Environment()
11
- akeyless = clearskies.di.inject.ByName("akeyless")
12
-
13
- access_id = clearskies.configs.String(required=True, regexp=r"^p-[\d\w]+$")
14
- access_type = clearskies.configs.Select(["aws_iam", "saml", "jwt"], required=True)
15
- api_host = clearskies.configs.String(default="https://api.akeyless.io")
16
- profile = clearskies.configs.String(regexp=r"^[\d\w\-]+$")
17
-
18
- _token_refresh: datetime.datetime = None # type: ignore
19
- _token: str = ""
20
- _api: Any = None
21
-
22
- def __init__(self, access_id: str, access_type: str, jwt_env_key: str = "", api_host: str = "", profile: str = ""):
23
- self.access_id = access_id
24
- self.access_type = access_type
25
- self.jwt_env_key = jwt_env_key
26
- self.api_host = api_host
27
- self.profile = profile
28
- if self.access_type == "jwt" and not self.jwt_env_key:
29
- raise ValueError("When using the JWT access type for Akeyless you must provide jwt_env_key")
30
-
31
- self.finalize_and_validate_configuration()
32
-
33
- @property
34
- def api(self) -> Any:
35
- if self._api is None:
36
- configuration = self.akeyless.Configuration(host=self.api_host)
37
- self._api = self.akeyless.V2Api(self.akeyless.ApiClient(configuration))
38
- return self._api
39
-
40
- def create(self, path: str, value: Any) -> bool:
41
- res = self.api.create_secret(self.akeyless.CreateSecret(name=path, value=str(value), token=self._get_token()))
42
- return True
43
-
44
- def get(self, path: str, silent_if_not_found: bool = False) -> str:
45
- try:
46
- res = self._api.get_secret_value(self.akeyless.GetSecretValue(names=[path], token=self._get_token()))
47
- except Exception as e:
48
- if e.status == 404: # type: ignore
49
- if silent_if_not_found:
50
- return ""
51
- raise KeyError(f"Secret '{path}' not found")
52
- raise e
53
- return res[path]
54
-
55
- def get_dynamic_secret(self, path: str, args: dict[str, Any] | None = None) -> Any:
56
- kwargs = {
57
- "name": path,
58
- "token": self._get_token(),
59
- }
60
- if args:
61
- kwargs["args"] = args # type: ignore
62
-
63
- return self._api.get_dynamic_secret_value(self.akeyless.GetDynamicSecretValue(**kwargs))
64
-
65
- def get_rotated_secret(self, path: str, args: dict[str, Any] | None = None) -> Any:
66
- kwargs = {
67
- "names": path,
68
- "token": self._get_token(),
69
- }
70
- if args:
71
- kwargs["args"] = args # type: ignore
72
-
73
- res = self._api.get_rotated_secret_value(self.akeyless.GetRotatedSecretValue(**kwargs))
74
- return res
75
-
76
- def list_secrets(self, path: str) -> list[Any]:
77
- res = self._api.list_items(self.akeyless.ListItems(path=path, token=self._get_token()))
78
- if not res.items:
79
- return []
80
-
81
- return [item.item_name for item in res.items]
82
-
83
- def update(self, path: str, value: Any) -> None:
84
- res = self._api.update_secret_val(
85
- self.akeyless.UpdateSecretVal(name=path, value=str(value), token=self._get_token())
86
- )
87
-
88
- def upsert(self, path: str, value: Any) -> None:
89
- try:
90
- self.update(path, value)
91
- except Exception as e:
92
- self.create(path, value)
93
-
94
- def list_sub_folders(self, main_folder: str) -> list[str]:
95
- """Return the list of secrets/sub folders in the given folder."""
96
- items = self._api.list_items(self.akeyless.ListItems(path=main_folder, token=self._get_token()))
97
-
98
- # akeyless will return the absolute path and end in a slash but we only want the folder name
99
- main_folder_string_len = len(main_folder)
100
- return [sub_folder[main_folder_string_len:-1] for sub_folder in items.folders]
101
-
102
- def get_ssh_certificate(self, cert_issuer: str, cert_username: str, path_to_public_file: str) -> Any:
103
- with open(path_to_public_file, "r") as fp:
104
- public_key = fp.read()
105
-
106
- res = self._api.get_ssh_certificate(
107
- self.akeyless.GetSSHCertificate(
108
- cert_username=cert_username,
109
- cert_issuer_name=cert_issuer,
110
- public_key_data=public_key,
111
- token=self._get_token(),
112
- )
113
- )
114
-
115
- return res.data
116
-
117
- def _get_token(self) -> str:
118
- # AKeyless tokens live for an hour
119
- if self._token is not None and (self._token_refresh - datetime.datetime.now()).total_seconds() > 10:
120
- return self._token
121
-
122
- auth_method_name = f"auth_{self.access_type}"
123
- if not hasattr(self, auth_method_name):
124
- raise ValueError(f"Requested Akeyless authentication with unsupported auth method: '{self.access_type}'")
125
-
126
- self._token_refresh = datetime.datetime.now() + datetime.timedelta(hours=0.5)
127
- self._token = getattr(self, auth_method_name)()
128
- return self._token
129
-
130
- def auth_aws_iam(self):
131
- from akeyless_cloud_id import CloudId # type: ignore
132
-
133
- res = self._api.auth(
134
- self.akeyless.Auth(access_id=self.access_id, access_type="aws_iam", cloud_id=CloudId().generate())
135
- )
136
- return res.token
137
-
138
- def auth_saml(self):
139
- import os
140
- from pathlib import Path
141
-
142
- os.system(f"akeyless list-items --profile {self.profile} --path /not/a/real/path > /dev/null 2>&1")
143
- home = str(Path.home())
144
- with open(f"{home}/.akeyless/.tmp_creds/{self.profile}-{self.access_id}", "r") as creds_file:
145
- credentials = creds_file.read()
146
-
147
- # and now we can turn that into a token
148
- response = self.requests.post(
149
- "https://rest.akeyless.io/",
150
- data={
151
- "cmd": "static-creds-auth",
152
- "access-id": self.access_id,
153
- "creds": credentials.strip(),
154
- },
155
- )
156
- return response.json()["token"]
157
-
158
- def auth_jwt(self):
159
- if not self.jwt_env_key:
160
- raise ValueError(
161
- "To use AKeyless JWT Auth, "
162
- "you must specify the name of the ENV key to load the JWT from when configuring AKeyless"
163
- )
164
- res = self._api.auth(
165
- self.akeyless.Auth(access_id=self.access_id, access_type="jwt", jwt=self.environment.get(self.jwt_env_key))
166
- )
167
- return res.token
168
-
169
-
170
- class AkeylessSaml(Akeyless):
171
- def __init__(self, access_id: str, api_host: str = "", profile: str = ""):
172
- return super().__init__(access_id, "saml", api_host=api_host, profile=profile)
173
-
174
-
175
- class AkeylessJwt(Akeyless):
176
- def __init__(self, access_id: str, jwt_env_key: str = "", api_host: str = "", profile: str = ""):
177
- return super().__init__(access_id, "jwt", jwt_env_key=jwt_env_key, api_host=api_host, profile=profile)
178
-
179
-
180
- class AkeylessAwsIam(Akeyless):
181
- def __init__(self, access_id: str, api_host: str = ""):
182
- return super().__init__(access_id, "aws_iam", api_host=api_host)
@@ -1 +0,0 @@
1
- from .not_found import NotFound
@@ -1,2 +0,0 @@
1
- class NotFound(Exception):
2
- pass
@@ -1,38 +0,0 @@
1
- from typing import Any
2
-
3
-
4
- class Secrets:
5
- def create(self, path: str, value: str) -> None:
6
- raise NotImplementedError(
7
- "It looks like you tried to use the secret system in clearskies, but didn't specify a secret manager."
8
- )
9
-
10
- def get(self, path: str, silent_if_not_found: bool = False) -> str:
11
- raise NotImplementedError(
12
- "It looks like you tried to use the secret system in clearskies, but didn't specify a secret manager."
13
- )
14
-
15
- def get_dynamic_secret(self, path: str, args: dict[str, Any] | None = None) -> Any:
16
- raise NotImplementedError(
17
- "It looks like you tried to use the secret system in clearskies, but didn't specify a secret manager."
18
- )
19
-
20
- def list_secrets(self, path: str) -> list[Any]:
21
- raise NotImplementedError(
22
- "It looks like you tried to use the secret system in clearskies, but didn't specify a secret manager."
23
- )
24
-
25
- def update(self, path: str, value: Any) -> None:
26
- raise NotImplementedError(
27
- "It looks like you tried to use the secret system in clearskies, but didn't specify a secret manager."
28
- )
29
-
30
- def upsert(self, path: str, value: Any) -> None:
31
- raise NotImplementedError(
32
- "It looks like you tried to use the secret system in clearskies, but didn't specify a secret manager."
33
- )
34
-
35
- def list_sub_folders(self, path: str) -> list[Any]:
36
- raise NotImplementedError(
37
- "It looks like you tried to use the secret system in clearskies, but didn't specify a secret manager."
38
- )
@@ -1,15 +0,0 @@
1
- from clearskies.configurable import Configurable
2
-
3
-
4
- class SecurityHeader(Configurable):
5
- """
6
- Attach all the various security headers to endpoints.
7
-
8
- The security header classes can be attached directly to both endpoints and endpoint groups and
9
- are used to set all the various security headers.
10
- """
11
-
12
- is_cors = False
13
-
14
- def set_headers_for_input_output(self, input_output):
15
- raise NotImplementedError()
@@ -1,11 +0,0 @@
1
- from clearskies.security_headers.cache_control import CacheControl
2
- from clearskies.security_headers.cors import Cors
3
- from clearskies.security_headers.csp import Csp
4
- from clearskies.security_headers.hsts import Hsts
5
-
6
- __all__ = [
7
- "CacheControl",
8
- "Cors",
9
- "Csp",
10
- "Hsts",
11
- ]
@@ -1,67 +0,0 @@
1
- import clearskies.configs
2
- import clearskies.decorators
3
- from clearskies.security_header import SecurityHeader
4
-
5
-
6
- class CacheControl(SecurityHeader):
7
- max_age = clearskies.configs.Integer()
8
- s_maxage = clearskies.configs.Integer()
9
- stale_while_revalidate = clearskies.configs.Integer()
10
- stale_if_error = clearskies.configs.Integer()
11
- immutable = clearskies.configs.Boolean(default=False)
12
- must_understand = clearskies.configs.Boolean(default=False)
13
- no_cache = clearskies.configs.Boolean(default=False)
14
- no_store = clearskies.configs.Boolean(default=False)
15
- no_transform = clearskies.configs.Boolean(default=False)
16
- private = clearskies.configs.Boolean(default=False)
17
- public = clearskies.configs.Boolean(default=False)
18
-
19
- numbers: list[str] = [
20
- "max_age",
21
- "stale_if_error",
22
- "stale_while_revalidate",
23
- "s_maxage",
24
- ]
25
- bools: list[str] = [
26
- "immutable",
27
- "must_understand",
28
- "no_cache",
29
- "no_store",
30
- "no_transform",
31
- "private",
32
- "public",
33
- ]
34
-
35
- @clearskies.decorators.parameters_to_properties
36
- def __init__(
37
- self,
38
- max_age: int | None = None,
39
- s_maxage: int | None = None,
40
- stale_while_revalidate: int | None = None,
41
- stale_if_error: int | None = None,
42
- immutable: bool = False,
43
- must_understand: bool = False,
44
- no_cache: bool = False,
45
- no_store: bool = False,
46
- no_transform: bool = False,
47
- private: bool = False,
48
- public: bool = False,
49
- ):
50
- self.finalize_and_validate_configuration()
51
-
52
- def set_headers_for_input_output(self, input_output):
53
- parts = []
54
- for variable_name in self.bools:
55
- value = getattr(self, variable_name)
56
- if not value:
57
- continue
58
- parts.append(variable_name.replace("_", "-"))
59
- for variable_name in self.numbers:
60
- value = getattr(self, variable_name)
61
- if value is None:
62
- continue
63
- key_name = variable_name.replace("_", "-")
64
- parts.append(f"{key_name}={value}")
65
- if not parts:
66
- return
67
- input_output.response_headers.add("cache-control", ", ".join(parts))
@@ -1,50 +0,0 @@
1
- import clearskies.configs
2
- import clearskies.decorators
3
- from clearskies.security_header import SecurityHeader
4
-
5
-
6
- class Cors(SecurityHeader):
7
- origin = clearskies.configs.String()
8
- methods = clearskies.configs.StringList(default=[])
9
- headers = clearskies.configs.StringList(default=[])
10
- max_age = clearskies.configs.Integer(default=5)
11
- credentials = clearskies.configs.Boolean(default=False)
12
- expose_headers = clearskies.configs.StringList(default=[])
13
- is_cors = True
14
-
15
- @clearskies.decorators.parameters_to_properties
16
- def __init__(
17
- self,
18
- credentials: bool = False,
19
- expose_headers: list[str] = [],
20
- headers: list[str] = [],
21
- max_age: int = 5,
22
- methods: list[str] = [],
23
- origin: str = "",
24
- ):
25
- self.finalize_and_validate_configuration()
26
-
27
- def set_headers(self, headers: list[str]):
28
- self.headers = headers
29
-
30
- def add_header(self, header: str):
31
- self.headers = [*self.headers, header]
32
-
33
- def set_methods(self, methods: list[str]):
34
- self.methods = methods
35
-
36
- def add_method(self, method: str):
37
- self.methods = [*self.methods, method]
38
-
39
- def set_headers_for_input_output(self, input_output):
40
- for key in ["expose_headers", "methods", "headers"]:
41
- value = getattr(self, key)
42
- if not value:
43
- continue
44
- input_output.response_headers.add(f"access-control-allow-{key}".replace("_", "-"), ", ".join(value))
45
- if self.credentials:
46
- input_output.response_headers.add("access-control-allow-credentials", "true")
47
- if self.max_age:
48
- input_output.response_headers.add("access-control-max-age", str(self.max_age))
49
- if self.origin:
50
- input_output.response_headers.add("access-control-allow-origin", str(self.origin))