clear-skies 1.22.31__py3-none-any.whl → 2.0.1__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.
- {clear_skies-1.22.31.dist-info → clear_skies-2.0.1.dist-info}/METADATA +12 -14
- clear_skies-2.0.1.dist-info/RECORD +249 -0
- {clear_skies-1.22.31.dist-info → clear_skies-2.0.1.dist-info}/WHEEL +1 -1
- clearskies/__init__.py +42 -25
- clearskies/action.py +7 -0
- clearskies/authentication/__init__.py +8 -41
- clearskies/authentication/authentication.py +46 -0
- clearskies/authentication/authorization.py +8 -9
- clearskies/authentication/authorization_pass_through.py +11 -9
- clearskies/authentication/jwks.py +133 -58
- clearskies/authentication/public.py +3 -38
- clearskies/authentication/secret_bearer.py +516 -54
- clearskies/autodoc/formats/oai3_json/__init__.py +1 -1
- clearskies/autodoc/formats/oai3_json/oai3_json.py +9 -7
- clearskies/autodoc/formats/oai3_json/parameter.py +6 -3
- clearskies/autodoc/formats/oai3_json/request.py +7 -5
- clearskies/autodoc/formats/oai3_json/response.py +7 -4
- clearskies/autodoc/formats/oai3_json/schema/object.py +4 -1
- clearskies/autodoc/request/__init__.py +2 -0
- clearskies/autodoc/request/header.py +4 -6
- clearskies/autodoc/request/json_body.py +4 -6
- clearskies/autodoc/request/parameter.py +8 -0
- clearskies/autodoc/request/request.py +7 -4
- clearskies/autodoc/request/url_parameter.py +4 -6
- clearskies/autodoc/request/url_path.py +4 -6
- clearskies/autodoc/schema/__init__.py +4 -2
- clearskies/autodoc/schema/array.py +5 -6
- clearskies/autodoc/schema/boolean.py +4 -10
- clearskies/autodoc/schema/date.py +0 -3
- clearskies/autodoc/schema/datetime.py +1 -4
- clearskies/autodoc/schema/double.py +0 -3
- clearskies/autodoc/schema/enum.py +4 -2
- clearskies/autodoc/schema/integer.py +4 -9
- clearskies/autodoc/schema/long.py +0 -3
- clearskies/autodoc/schema/number.py +4 -9
- clearskies/autodoc/schema/object.py +5 -7
- clearskies/autodoc/schema/password.py +0 -3
- clearskies/autodoc/schema/schema.py +11 -0
- clearskies/autodoc/schema/string.py +4 -10
- clearskies/backends/__init__.py +55 -20
- clearskies/backends/api_backend.py +1100 -284
- clearskies/backends/backend.py +53 -84
- clearskies/backends/cursor_backend.py +236 -186
- clearskies/backends/memory_backend.py +519 -226
- clearskies/backends/secrets_backend.py +75 -31
- clearskies/column.py +1229 -0
- clearskies/columns/__init__.py +71 -0
- clearskies/columns/audit.py +205 -0
- clearskies/columns/belongs_to_id.py +483 -0
- clearskies/columns/belongs_to_model.py +128 -0
- clearskies/columns/belongs_to_self.py +105 -0
- clearskies/columns/boolean.py +109 -0
- clearskies/columns/category_tree.py +275 -0
- clearskies/columns/category_tree_ancestors.py +51 -0
- clearskies/columns/category_tree_children.py +127 -0
- clearskies/columns/category_tree_descendants.py +48 -0
- clearskies/columns/created.py +94 -0
- clearskies/columns/created_by_authorization_data.py +116 -0
- clearskies/columns/created_by_header.py +99 -0
- clearskies/columns/created_by_ip.py +92 -0
- clearskies/columns/created_by_routing_data.py +96 -0
- clearskies/columns/created_by_user_agent.py +92 -0
- clearskies/columns/date.py +230 -0
- clearskies/columns/datetime.py +278 -0
- clearskies/columns/email.py +76 -0
- clearskies/columns/float.py +149 -0
- clearskies/columns/has_many.py +505 -0
- clearskies/columns/has_many_self.py +56 -0
- clearskies/columns/has_one.py +14 -0
- clearskies/columns/integer.py +156 -0
- clearskies/columns/json.py +122 -0
- clearskies/columns/many_to_many_ids.py +333 -0
- clearskies/columns/many_to_many_ids_with_data.py +270 -0
- clearskies/columns/many_to_many_models.py +154 -0
- clearskies/columns/many_to_many_pivots.py +133 -0
- clearskies/columns/phone.py +158 -0
- clearskies/columns/select.py +91 -0
- clearskies/columns/string.py +98 -0
- clearskies/columns/timestamp.py +160 -0
- clearskies/columns/updated.py +110 -0
- clearskies/columns/uuid.py +86 -0
- clearskies/configs/README.md +105 -0
- clearskies/configs/__init__.py +162 -0
- clearskies/configs/actions.py +43 -0
- clearskies/configs/any.py +13 -0
- clearskies/configs/any_dict.py +22 -0
- clearskies/configs/any_dict_or_callable.py +23 -0
- clearskies/configs/authentication.py +23 -0
- clearskies/configs/authorization.py +23 -0
- clearskies/configs/boolean.py +16 -0
- clearskies/configs/boolean_or_callable.py +18 -0
- clearskies/configs/callable_config.py +18 -0
- clearskies/configs/columns.py +34 -0
- clearskies/configs/conditions.py +30 -0
- clearskies/configs/config.py +24 -0
- clearskies/configs/datetime.py +18 -0
- clearskies/configs/datetime_or_callable.py +19 -0
- clearskies/configs/endpoint.py +23 -0
- clearskies/configs/endpoint_list.py +28 -0
- clearskies/configs/float.py +16 -0
- clearskies/configs/float_or_callable.py +18 -0
- clearskies/configs/integer.py +16 -0
- clearskies/configs/integer_or_callable.py +18 -0
- clearskies/configs/joins.py +30 -0
- clearskies/configs/list_any_dict.py +30 -0
- clearskies/configs/list_any_dict_or_callable.py +31 -0
- clearskies/configs/model_class.py +35 -0
- clearskies/configs/model_column.py +65 -0
- clearskies/configs/model_columns.py +56 -0
- clearskies/configs/model_destination_name.py +25 -0
- clearskies/configs/model_to_id_column.py +43 -0
- clearskies/configs/readable_model_column.py +9 -0
- clearskies/configs/readable_model_columns.py +9 -0
- clearskies/configs/schema.py +23 -0
- clearskies/configs/searchable_model_columns.py +9 -0
- clearskies/configs/security_headers.py +39 -0
- clearskies/configs/select.py +26 -0
- clearskies/configs/select_list.py +47 -0
- clearskies/configs/string.py +29 -0
- clearskies/configs/string_dict.py +32 -0
- clearskies/configs/string_list.py +32 -0
- clearskies/configs/string_list_or_callable.py +35 -0
- clearskies/configs/string_or_callable.py +18 -0
- clearskies/configs/timedelta.py +18 -0
- clearskies/configs/timezone.py +18 -0
- clearskies/configs/url.py +23 -0
- clearskies/configs/validators.py +45 -0
- clearskies/configs/writeable_model_column.py +9 -0
- clearskies/configs/writeable_model_columns.py +9 -0
- clearskies/configurable.py +76 -0
- clearskies/contexts/__init__.py +8 -8
- clearskies/contexts/cli.py +8 -41
- clearskies/contexts/context.py +91 -56
- clearskies/contexts/wsgi.py +16 -29
- clearskies/contexts/wsgi_ref.py +53 -0
- clearskies/di/__init__.py +10 -7
- clearskies/di/additional_config.py +115 -4
- clearskies/di/additional_config_auto_import.py +12 -0
- clearskies/di/di.py +742 -121
- clearskies/di/inject/__init__.py +23 -0
- clearskies/di/inject/by_class.py +21 -0
- clearskies/di/inject/by_name.py +18 -0
- clearskies/di/inject/di.py +13 -0
- clearskies/di/inject/environment.py +14 -0
- clearskies/di/inject/input_output.py +20 -0
- clearskies/di/inject/now.py +13 -0
- clearskies/di/inject/requests.py +13 -0
- clearskies/di/inject/secrets.py +14 -0
- clearskies/di/inject/utcnow.py +13 -0
- clearskies/di/inject/uuid.py +15 -0
- clearskies/di/injectable.py +29 -0
- clearskies/di/injectable_properties.py +131 -0
- clearskies/end.py +183 -0
- clearskies/endpoint.py +1310 -0
- clearskies/endpoint_group.py +310 -0
- clearskies/endpoints/__init__.py +23 -0
- clearskies/endpoints/advanced_search.py +526 -0
- clearskies/endpoints/callable.py +388 -0
- clearskies/endpoints/create.py +202 -0
- clearskies/endpoints/delete.py +139 -0
- clearskies/endpoints/get.py +275 -0
- clearskies/endpoints/health_check.py +181 -0
- clearskies/endpoints/list.py +573 -0
- clearskies/endpoints/restful_api.py +427 -0
- clearskies/endpoints/simple_search.py +286 -0
- clearskies/endpoints/update.py +190 -0
- clearskies/environment.py +5 -3
- clearskies/exceptions/__init__.py +17 -0
- clearskies/{handlers/exceptions/input_error.py → exceptions/input_errors.py} +1 -1
- clearskies/exceptions/moved_permanently.py +3 -0
- clearskies/exceptions/moved_temporarily.py +3 -0
- clearskies/exceptions/not_found.py +2 -0
- clearskies/functional/__init__.py +2 -2
- clearskies/functional/routing.py +92 -0
- clearskies/functional/string.py +19 -11
- clearskies/functional/validations.py +61 -9
- clearskies/input_outputs/__init__.py +9 -7
- clearskies/input_outputs/cli.py +130 -142
- clearskies/input_outputs/exceptions/__init__.py +1 -1
- clearskies/input_outputs/headers.py +45 -0
- clearskies/input_outputs/input_output.py +91 -122
- clearskies/input_outputs/programmatic.py +69 -0
- clearskies/input_outputs/wsgi.py +23 -38
- clearskies/model.py +984 -183
- clearskies/parameters_to_properties.py +31 -0
- clearskies/query/__init__.py +12 -0
- clearskies/query/condition.py +223 -0
- clearskies/query/join.py +136 -0
- clearskies/query/query.py +196 -0
- clearskies/query/sort.py +27 -0
- clearskies/schema.py +82 -0
- clearskies/secrets/__init__.py +3 -31
- clearskies/secrets/additional_configs/mysql_connection_dynamic_producer.py +15 -4
- clearskies/secrets/additional_configs/mysql_connection_dynamic_producer_via_ssh_cert_bastion.py +11 -5
- clearskies/secrets/akeyless.py +88 -147
- clearskies/secrets/secrets.py +8 -8
- clearskies/security_header.py +15 -0
- clearskies/security_headers/__init__.py +8 -8
- clearskies/security_headers/cache_control.py +47 -110
- clearskies/security_headers/cors.py +40 -95
- clearskies/security_headers/csp.py +76 -151
- clearskies/security_headers/hsts.py +14 -16
- clearskies/test_base.py +8 -0
- clearskies/typing.py +11 -0
- clearskies/validator.py +37 -0
- clearskies/validators/__init__.py +33 -0
- clearskies/validators/after_column.py +62 -0
- clearskies/validators/before_column.py +13 -0
- clearskies/validators/in_the_future.py +32 -0
- clearskies/validators/in_the_future_at_least.py +11 -0
- clearskies/validators/in_the_future_at_most.py +10 -0
- clearskies/validators/in_the_past.py +32 -0
- clearskies/validators/in_the_past_at_least.py +10 -0
- clearskies/validators/in_the_past_at_most.py +10 -0
- clearskies/validators/maximum_length.py +26 -0
- clearskies/validators/maximum_value.py +29 -0
- clearskies/validators/minimum_length.py +26 -0
- clearskies/validators/minimum_value.py +29 -0
- clearskies/validators/required.py +35 -0
- clearskies/validators/timedelta.py +59 -0
- clearskies/validators/unique.py +31 -0
- clear_skies-1.22.31.dist-info/RECORD +0 -214
- clearskies/application.py +0 -29
- clearskies/authentication/auth0_jwks.py +0 -118
- clearskies/authentication/auth_exception.py +0 -2
- clearskies/authentication/jwks_jwcrypto.py +0 -51
- clearskies/backends/api_get_only_backend.py +0 -48
- clearskies/backends/example_backend.py +0 -43
- clearskies/backends/file_backend.py +0 -48
- clearskies/backends/json_backend.py +0 -7
- clearskies/backends/restful_api_advanced_search_backend.py +0 -103
- clearskies/binding_config.py +0 -16
- clearskies/column_types/__init__.py +0 -203
- clearskies/column_types/audit.py +0 -249
- clearskies/column_types/belongs_to.py +0 -271
- clearskies/column_types/boolean.py +0 -60
- clearskies/column_types/category_tree.py +0 -304
- clearskies/column_types/column.py +0 -373
- clearskies/column_types/created.py +0 -26
- clearskies/column_types/created_by_authorization_data.py +0 -26
- clearskies/column_types/created_by_header.py +0 -24
- clearskies/column_types/created_by_ip.py +0 -17
- clearskies/column_types/created_by_routing_data.py +0 -25
- clearskies/column_types/created_by_user_agent.py +0 -17
- clearskies/column_types/created_micro.py +0 -26
- clearskies/column_types/datetime.py +0 -109
- clearskies/column_types/datetime_micro.py +0 -12
- clearskies/column_types/email.py +0 -18
- clearskies/column_types/float.py +0 -43
- clearskies/column_types/has_many.py +0 -179
- clearskies/column_types/has_one.py +0 -60
- clearskies/column_types/integer.py +0 -41
- clearskies/column_types/json.py +0 -25
- clearskies/column_types/many_to_many.py +0 -278
- clearskies/column_types/many_to_many_with_data.py +0 -162
- clearskies/column_types/phone.py +0 -48
- clearskies/column_types/select.py +0 -11
- clearskies/column_types/string.py +0 -24
- clearskies/column_types/timestamp.py +0 -73
- clearskies/column_types/updated.py +0 -26
- clearskies/column_types/updated_micro.py +0 -26
- clearskies/column_types/uuid.py +0 -25
- clearskies/columns.py +0 -123
- clearskies/condition_parser.py +0 -172
- clearskies/contexts/build_context.py +0 -54
- clearskies/contexts/convert_to_application.py +0 -190
- clearskies/contexts/extract_handler.py +0 -37
- clearskies/contexts/test.py +0 -94
- clearskies/decorators/__init__.py +0 -41
- clearskies/decorators/allow_non_json_bodies.py +0 -9
- clearskies/decorators/auth0_jwks.py +0 -22
- clearskies/decorators/authorization.py +0 -10
- clearskies/decorators/binding_classes.py +0 -9
- clearskies/decorators/binding_modules.py +0 -9
- clearskies/decorators/bindings.py +0 -9
- clearskies/decorators/create.py +0 -10
- clearskies/decorators/delete.py +0 -10
- clearskies/decorators/docs.py +0 -14
- clearskies/decorators/get.py +0 -10
- clearskies/decorators/jwks.py +0 -26
- clearskies/decorators/merge.py +0 -124
- clearskies/decorators/patch.py +0 -10
- clearskies/decorators/post.py +0 -10
- clearskies/decorators/public.py +0 -11
- clearskies/decorators/response_headers.py +0 -10
- clearskies/decorators/return_raw_response.py +0 -9
- clearskies/decorators/schema.py +0 -10
- clearskies/decorators/secret_bearer.py +0 -24
- clearskies/decorators/security_headers.py +0 -10
- clearskies/di/standard_dependencies.py +0 -151
- clearskies/handlers/__init__.py +0 -41
- clearskies/handlers/advanced_search.py +0 -271
- clearskies/handlers/base.py +0 -479
- clearskies/handlers/callable.py +0 -192
- clearskies/handlers/create.py +0 -35
- clearskies/handlers/crud_by_method.py +0 -18
- clearskies/handlers/database_connector.py +0 -32
- clearskies/handlers/delete.py +0 -61
- clearskies/handlers/exceptions/__init__.py +0 -5
- clearskies/handlers/exceptions/not_found.py +0 -3
- clearskies/handlers/get.py +0 -156
- clearskies/handlers/health_check.py +0 -59
- clearskies/handlers/input_processing.py +0 -79
- clearskies/handlers/list.py +0 -530
- clearskies/handlers/mygrations.py +0 -82
- clearskies/handlers/request_method_routing.py +0 -47
- clearskies/handlers/restful_api.py +0 -218
- clearskies/handlers/routing.py +0 -62
- clearskies/handlers/schema_helper.py +0 -128
- clearskies/handlers/simple_routing.py +0 -206
- clearskies/handlers/simple_routing_route.py +0 -197
- clearskies/handlers/simple_search.py +0 -136
- clearskies/handlers/update.py +0 -102
- clearskies/handlers/write.py +0 -193
- clearskies/input_requirements/__init__.py +0 -78
- clearskies/input_requirements/after.py +0 -36
- clearskies/input_requirements/before.py +0 -36
- clearskies/input_requirements/in_the_future_at_least.py +0 -19
- clearskies/input_requirements/in_the_future_at_most.py +0 -19
- clearskies/input_requirements/in_the_past_at_least.py +0 -19
- clearskies/input_requirements/in_the_past_at_most.py +0 -19
- clearskies/input_requirements/maximum_length.py +0 -19
- clearskies/input_requirements/maximum_value.py +0 -19
- clearskies/input_requirements/minimum_length.py +0 -22
- clearskies/input_requirements/minimum_value.py +0 -19
- clearskies/input_requirements/required.py +0 -23
- clearskies/input_requirements/requirement.py +0 -25
- clearskies/input_requirements/time_delta.py +0 -38
- clearskies/input_requirements/unique.py +0 -18
- clearskies/mocks/__init__.py +0 -7
- clearskies/mocks/input_output.py +0 -124
- clearskies/mocks/models.py +0 -142
- clearskies/models.py +0 -350
- clearskies/security_headers/base.py +0 -12
- clearskies/tests/simple_api/models/__init__.py +0 -2
- clearskies/tests/simple_api/models/status.py +0 -23
- clearskies/tests/simple_api/models/user.py +0 -21
- clearskies/tests/simple_api/users_api.py +0 -64
- {clear_skies-1.22.31.dist-info → clear_skies-2.0.1.dist-info}/LICENSE +0 -0
- /clearskies/{contexts/bash.py → autodoc/py.typed} +0 -0
- /clearskies/{handlers/exceptions → exceptions}/authentication.py +0 -0
- /clearskies/{handlers/exceptions → exceptions}/authorization.py +0 -0
- /clearskies/{handlers/exceptions → exceptions}/client_error.py +0 -0
- /clearskies/{tests/__init__.py → input_outputs/py.typed} +0 -0
- /clearskies/{tests/simple_api/__init__.py → py.typed} +0 -0
clearskies/schema.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections import OrderedDict
|
|
4
|
+
from typing import TYPE_CHECKING, Self
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from clearskies import Column
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Schema:
|
|
11
|
+
"""
|
|
12
|
+
Define a schema by extending and declaring columns.
|
|
13
|
+
|
|
14
|
+
```python
|
|
15
|
+
from clearskies.schema import Schema
|
|
16
|
+
from clearskies.validators import Required, Unique
|
|
17
|
+
|
|
18
|
+
import clearskies.columns
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Person(Schema):
|
|
22
|
+
id = clearskies.columns.Uuid()
|
|
23
|
+
name = clearskies.columns.String(validators=[Required()])
|
|
24
|
+
date_of_birth = clearskies.columns.Datetime(validators=[Required(), InThePast()])
|
|
25
|
+
email = clearskies.columns.Email()
|
|
26
|
+
```
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
id_column_name: str = ""
|
|
30
|
+
_columns: dict[str, Column] = {}
|
|
31
|
+
|
|
32
|
+
@classmethod
|
|
33
|
+
def destination_name(cls: type[Self]) -> str:
|
|
34
|
+
raise NotImplementedError()
|
|
35
|
+
|
|
36
|
+
def __init__(self):
|
|
37
|
+
self._data = {}
|
|
38
|
+
|
|
39
|
+
@classmethod
|
|
40
|
+
def get_columns(cls: type[Self], overrides={}) -> dict[str, Column]:
|
|
41
|
+
"""
|
|
42
|
+
Return an ordered dictionary with the configuration for the columns.
|
|
43
|
+
|
|
44
|
+
Generally, this method is meant for internal use. It just pulls the column configuration
|
|
45
|
+
information out of class attributes. It doesn't return the fully prepared columns,
|
|
46
|
+
so you probably can't use the return value of this function. For that, see
|
|
47
|
+
`model.columns()`.
|
|
48
|
+
"""
|
|
49
|
+
# no caching if we have overrides
|
|
50
|
+
if cls._columns and not overrides:
|
|
51
|
+
return cls._columns
|
|
52
|
+
|
|
53
|
+
overrides = {**overrides}
|
|
54
|
+
columns: dict[str, Column] = OrderedDict()
|
|
55
|
+
for attribute_name in dir(cls):
|
|
56
|
+
attribute = getattr(cls, attribute_name)
|
|
57
|
+
# use duck typing instead of isinstance to decide which attribute is a column.
|
|
58
|
+
# We have to do this to avoid circular imports.
|
|
59
|
+
if not hasattr(attribute, "from_backend") and not hasattr(attribute, "to_backend"):
|
|
60
|
+
continue
|
|
61
|
+
|
|
62
|
+
if attribute_name in overrides:
|
|
63
|
+
columns[attribute_name] = overrides[attribute_name]
|
|
64
|
+
del overrides[attribute_name]
|
|
65
|
+
columns[attribute_name] = attribute
|
|
66
|
+
|
|
67
|
+
for attribute_name, column in overrides.items():
|
|
68
|
+
columns[attribute_name] = column # type: ignore
|
|
69
|
+
|
|
70
|
+
if not overrides:
|
|
71
|
+
cls._columns = columns
|
|
72
|
+
|
|
73
|
+
# now go through and finalize everything. We have to do this after setting cls._columns, because finalization
|
|
74
|
+
# sometimes depends on fetching the list of columns, so if we do it before caching the answer, we may end up
|
|
75
|
+
# creating circular loops. I don't *think* this will cause painful side-effects, but we'll find out!
|
|
76
|
+
for column_name, column in cls._columns.items():
|
|
77
|
+
column.finalize_configuration(cls, column_name)
|
|
78
|
+
|
|
79
|
+
return columns
|
|
80
|
+
|
|
81
|
+
def __bool__(self):
|
|
82
|
+
return False
|
clearskies/secrets/__init__.py
CHANGED
|
@@ -1,34 +1,6 @@
|
|
|
1
|
-
from .akeyless import AKeyless, AKeylessAdditionalConfig
|
|
2
|
-
from . import
|
|
3
|
-
from ..binding_config import BindingConfig
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def akeyless(*args, **kwargs):
|
|
7
|
-
return BindingConfig(AKeyless, *args, **kwargs)
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def akeyless_aws_iam_auth(access_id=None, api_host=None):
|
|
11
|
-
return AKeylessAdditionalConfig("aws_iam", access_id=access_id, api_host=api_host)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def akeyless_saml_auth(access_id=None, api_host=None, profile=None):
|
|
15
|
-
return AKeylessAdditionalConfig("saml", access_id=access_id, api_host=api_host, profile=profile)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def akeyless_jwt_auth(jwt_env_key, access_id=None, api_host=None):
|
|
19
|
-
return AKeylessAdditionalConfig("jwt", jwt_env_key=jwt_env_key, access_id=access_id, api_host=api_host)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def akeyless_access_key_auth(access_id=None, api_host=None):
|
|
23
|
-
return AKeylessAdditionalConfig("access_key", access_id=access_id, api_host=api_host)
|
|
24
|
-
|
|
1
|
+
# from .akeyless import AKeyless, AKeylessAdditionalConfig
|
|
2
|
+
from clearskies.secrets.secrets import Secrets
|
|
25
3
|
|
|
26
4
|
__all__ = [
|
|
27
|
-
"
|
|
28
|
-
"additional_configs",
|
|
29
|
-
"akeyless",
|
|
30
|
-
"akeyless_aws_iam_auth",
|
|
31
|
-
"akeyless_saml_auth",
|
|
32
|
-
"akeyless_jwt_auth",
|
|
33
|
-
"akeyless_access_key_auth",
|
|
5
|
+
"Secrets",
|
|
34
6
|
]
|
|
@@ -14,7 +14,9 @@ class MySQLConnectionDynamicProducer(clearskies.di.additional_config.AdditionalC
|
|
|
14
14
|
def provide_connection_details(self, environment, secrets):
|
|
15
15
|
if not secrets:
|
|
16
16
|
raise ValueError(
|
|
17
|
-
"I was asked to connect to a database via an AKeyless dynamic producer
|
|
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()"
|
|
18
20
|
)
|
|
19
21
|
|
|
20
22
|
producer_name = (
|
|
@@ -24,21 +26,30 @@ class MySQLConnectionDynamicProducer(clearskies.di.additional_config.AdditionalC
|
|
|
24
26
|
)
|
|
25
27
|
if not producer_name:
|
|
26
28
|
raise ValueError(
|
|
27
|
-
"I was asked to connect to a database via an AKeyless dynamic producer,
|
|
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."
|
|
28
33
|
)
|
|
29
34
|
database_name = (
|
|
30
35
|
self._database_name if self._database_name is not None else environment.get("db_database", silent=True)
|
|
31
36
|
)
|
|
32
37
|
if not database_name:
|
|
33
38
|
raise ValueError(
|
|
34
|
-
"I was asked to connect to a database via an AKeyless dynamic producer,
|
|
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."
|
|
35
43
|
)
|
|
36
44
|
database_host = (
|
|
37
45
|
self._database_host if self._database_host is not None else environment.get("db_host", silent=True)
|
|
38
46
|
)
|
|
39
47
|
if not database_host:
|
|
40
48
|
raise ValueError(
|
|
41
|
-
"I was asked to connect to a database via an AKeyless dynamic producer,
|
|
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."
|
|
42
53
|
)
|
|
43
54
|
credentials = secrets.get_dynamic_secret(producer_name)
|
|
44
55
|
|
clearskies/secrets/additional_configs/mysql_connection_dynamic_producer_via_ssh_cert_bastion.py
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import
|
|
2
|
-
from pathlib import Path
|
|
1
|
+
import os
|
|
3
2
|
import socket
|
|
4
3
|
import subprocess
|
|
5
|
-
import os
|
|
6
4
|
import time
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import clearskies.di
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
class MySQLConnectionDynamicProducerViaSSHCertBastion(clearskies.di.additional_config.AdditionalConfig):
|
|
@@ -93,7 +94,11 @@ class MySQLConnectionDynamicProducerViaSSHCertBastion(clearskies.di.additional_c
|
|
|
93
94
|
if default is not None:
|
|
94
95
|
return default
|
|
95
96
|
raise ValueError(
|
|
96
|
-
f"I was asked to connect to a database via an AKeyless dynamic producer through an SSH bastion
|
|
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}'."
|
|
97
102
|
)
|
|
98
103
|
|
|
99
104
|
def _create_tunnel(
|
|
@@ -115,7 +120,8 @@ class MySQLConnectionDynamicProducerViaSSHCertBastion(clearskies.di.additional_c
|
|
|
115
120
|
|
|
116
121
|
if not os.path.isfile(public_key_file_path):
|
|
117
122
|
raise ValueError(
|
|
118
|
-
f"I was asked to connect to AKeyless SSH with the public key file in '{public_key_file_path}',
|
|
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"
|
|
119
125
|
)
|
|
120
126
|
|
|
121
127
|
ssh_certificate = secrets.get_ssh_certificate(cert_issuer_name, bastion_username, public_key_file_path)
|
clearskies/secrets/akeyless.py
CHANGED
|
@@ -1,170 +1,110 @@
|
|
|
1
1
|
import datetime
|
|
2
|
-
from
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
if
|
|
29
|
-
raise ValueError(
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
+ ". The expected keys are: "
|
|
43
|
-
+ ", ".join(allowed_kwargs)
|
|
44
|
-
)
|
|
45
|
-
self._kwargs = kwargs
|
|
46
|
-
|
|
47
|
-
def provide_secrets(self, requests, environment):
|
|
48
|
-
secrets = AKeyless(requests, environment)
|
|
49
|
-
secrets.configure(access_type=self._auth_method, **self._kwargs)
|
|
50
|
-
return secrets
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
class AKeyless:
|
|
54
|
-
_akeyless = None
|
|
55
|
-
_access_id = None
|
|
56
|
-
_access_type = None
|
|
57
|
-
_api_host = None
|
|
58
|
-
_token_refresh = None
|
|
59
|
-
_token = None
|
|
60
|
-
_environment = None
|
|
61
|
-
_jwt_env_key = None
|
|
62
|
-
_requests = None
|
|
63
|
-
_api = None
|
|
64
|
-
|
|
65
|
-
def __init__(self, requests, environment):
|
|
66
|
-
self._requests = requests
|
|
67
|
-
self._environment = environment
|
|
68
|
-
import akeyless
|
|
69
|
-
|
|
70
|
-
self._akeyless = akeyless
|
|
71
|
-
|
|
72
|
-
def configure(self, access_id=None, access_type=None, jwt_env_key=None, api_host=None, profile=None):
|
|
73
|
-
self._access_id = access_id if access_id is not None else self._environment.get("akeyless_access_id")
|
|
74
|
-
self._access_type = access_type if access_type is not None else self._environment.get("akeyless_access_type")
|
|
75
|
-
self._jwt_env_key = jwt_env_key
|
|
76
|
-
self._api_host = api_host if api_host is not None else self._environment.get("akeyless_api_host", silent=True)
|
|
77
|
-
self._profile = profile if profile is not None else "default"
|
|
78
|
-
|
|
79
|
-
if not self._api_host:
|
|
80
|
-
self._api_host = "https://api.akeyless.io"
|
|
81
|
-
|
|
82
|
-
configuration = self._akeyless.Configuration(host=self._api_host)
|
|
83
|
-
api_client = self._akeyless.ApiClient(configuration)
|
|
84
|
-
self._api = self._akeyless.V2Api(api_client)
|
|
85
|
-
|
|
86
|
-
def create(self, path, value):
|
|
87
|
-
self._configure_guard()
|
|
88
|
-
res = self._api.create_secret(self._akeyless.CreateSecret(name=path, value=str(value), token=self._get_token()))
|
|
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()))
|
|
89
42
|
return True
|
|
90
43
|
|
|
91
|
-
def get(self, path, silent_if_not_found=False):
|
|
92
|
-
self._configure_guard()
|
|
93
|
-
|
|
44
|
+
def get(self, path: str, silent_if_not_found: bool = False) -> str:
|
|
94
45
|
try:
|
|
95
|
-
res = self._api.get_secret_value(self.
|
|
46
|
+
res = self._api.get_secret_value(self.akeyless.GetSecretValue(names=[path], token=self._get_token()))
|
|
96
47
|
except Exception as e:
|
|
97
|
-
if e.status == 404:
|
|
48
|
+
if e.status == 404: # type: ignore
|
|
98
49
|
if silent_if_not_found:
|
|
99
|
-
return
|
|
100
|
-
raise
|
|
50
|
+
return ""
|
|
51
|
+
raise KeyError(f"Secret '{path}' not found")
|
|
101
52
|
raise e
|
|
102
53
|
return res[path]
|
|
103
54
|
|
|
104
|
-
def get_dynamic_secret(self, path, args=None):
|
|
105
|
-
self._configure_guard()
|
|
106
|
-
|
|
55
|
+
def get_dynamic_secret(self, path: str, args: dict[str, Any] | None = None) -> Any:
|
|
107
56
|
kwargs = {
|
|
108
57
|
"name": path,
|
|
109
58
|
"token": self._get_token(),
|
|
110
59
|
}
|
|
111
60
|
if args:
|
|
112
|
-
kwargs["args"] = args
|
|
113
|
-
|
|
114
|
-
res = self._api.get_dynamic_secret_value(self._akeyless.GetDynamicSecretValue(**kwargs))
|
|
115
|
-
return res
|
|
61
|
+
kwargs["args"] = args # type: ignore
|
|
116
62
|
|
|
117
|
-
|
|
118
|
-
self._configure_guard()
|
|
63
|
+
return self._api.get_dynamic_secret_value(self.akeyless.GetDynamicSecretValue(**kwargs))
|
|
119
64
|
|
|
65
|
+
def get_rotated_secret(self, path: str, args: dict[str, Any] | None = None) -> Any:
|
|
120
66
|
kwargs = {
|
|
121
67
|
"names": path,
|
|
122
68
|
"token": self._get_token(),
|
|
123
69
|
}
|
|
124
70
|
if args:
|
|
125
|
-
kwargs["args"] = args
|
|
71
|
+
kwargs["args"] = args # type: ignore
|
|
126
72
|
|
|
127
|
-
res = self._api.get_rotated_secret_value(self.
|
|
73
|
+
res = self._api.get_rotated_secret_value(self.akeyless.GetRotatedSecretValue(**kwargs))
|
|
128
74
|
return res
|
|
129
75
|
|
|
130
|
-
def list_secrets(self, path):
|
|
131
|
-
self.
|
|
132
|
-
res = self._api.list_items(self._akeyless.ListItems(path=path, token=self._get_token()))
|
|
76
|
+
def list_secrets(self, path: str) -> list[Any]:
|
|
77
|
+
res = self._api.list_items(self.akeyless.ListItems(path=path, token=self._get_token()))
|
|
133
78
|
if not res.items:
|
|
134
79
|
return []
|
|
135
80
|
|
|
136
81
|
return [item.item_name for item in res.items]
|
|
137
82
|
|
|
138
|
-
def update(self, path, value):
|
|
139
|
-
self._configure_guard()
|
|
83
|
+
def update(self, path: str, value: Any) -> None:
|
|
140
84
|
res = self._api.update_secret_val(
|
|
141
|
-
self.
|
|
85
|
+
self.akeyless.UpdateSecretVal(name=path, value=str(value), token=self._get_token())
|
|
142
86
|
)
|
|
143
|
-
return True
|
|
144
87
|
|
|
145
|
-
def upsert(self, path, value):
|
|
88
|
+
def upsert(self, path: str, value: Any) -> None:
|
|
146
89
|
try:
|
|
147
|
-
|
|
148
|
-
return True
|
|
90
|
+
self.update(path, value)
|
|
149
91
|
except Exception as e:
|
|
150
|
-
|
|
92
|
+
self.create(path, value)
|
|
151
93
|
|
|
152
94
|
def list_sub_folders(self, main_folder: str) -> list[str]:
|
|
153
95
|
"""Return the list of secrets/sub folders in the given folder."""
|
|
154
|
-
items = self._api.list_items(self.
|
|
96
|
+
items = self._api.list_items(self.akeyless.ListItems(path=main_folder, token=self._get_token()))
|
|
155
97
|
|
|
156
98
|
# akeyless will return the absolute path and end in a slash but we only want the folder name
|
|
157
99
|
main_folder_string_len = len(main_folder)
|
|
158
100
|
return [sub_folder[main_folder_string_len:-1] for sub_folder in items.folders]
|
|
159
101
|
|
|
160
|
-
def get_ssh_certificate(self, cert_issuer, cert_username, path_to_public_file):
|
|
161
|
-
self._configure_guard()
|
|
162
|
-
|
|
102
|
+
def get_ssh_certificate(self, cert_issuer: str, cert_username: str, path_to_public_file: str) -> Any:
|
|
163
103
|
with open(path_to_public_file, "r") as fp:
|
|
164
104
|
public_key = fp.read()
|
|
165
105
|
|
|
166
106
|
res = self._api.get_ssh_certificate(
|
|
167
|
-
self.
|
|
107
|
+
self.akeyless.GetSSHCertificate(
|
|
168
108
|
cert_username=cert_username,
|
|
169
109
|
cert_issuer_name=cert_issuer,
|
|
170
110
|
public_key_data=public_key,
|
|
@@ -174,28 +114,24 @@ class AKeyless:
|
|
|
174
114
|
|
|
175
115
|
return res.data
|
|
176
116
|
|
|
177
|
-
def
|
|
178
|
-
if not self._access_id:
|
|
179
|
-
raise ValueError("Must call configure method before using secrets.AKeyless")
|
|
180
|
-
|
|
181
|
-
def _get_token(self):
|
|
117
|
+
def _get_token(self) -> str:
|
|
182
118
|
# AKeyless tokens live for an hour
|
|
183
119
|
if self._token is not None and (self._token_refresh - datetime.datetime.now()).total_seconds() > 10:
|
|
184
120
|
return self._token
|
|
185
121
|
|
|
186
|
-
auth_method_name = f"auth_{self.
|
|
122
|
+
auth_method_name = f"auth_{self.access_type}"
|
|
187
123
|
if not hasattr(self, auth_method_name):
|
|
188
|
-
raise ValueError(f"Requested
|
|
124
|
+
raise ValueError(f"Requested Akeyless authentication with unsupported auth method: '{self.access_type}'")
|
|
189
125
|
|
|
190
126
|
self._token_refresh = datetime.datetime.now() + datetime.timedelta(hours=0.5)
|
|
191
127
|
self._token = getattr(self, auth_method_name)()
|
|
192
128
|
return self._token
|
|
193
129
|
|
|
194
130
|
def auth_aws_iam(self):
|
|
195
|
-
from akeyless_cloud_id import CloudId
|
|
131
|
+
from akeyless_cloud_id import CloudId # type: ignore
|
|
196
132
|
|
|
197
133
|
res = self._api.auth(
|
|
198
|
-
self.
|
|
134
|
+
self.akeyless.Auth(access_id=self.access_id, access_type="aws_iam", cloud_id=CloudId().generate())
|
|
199
135
|
)
|
|
200
136
|
return res.token
|
|
201
137
|
|
|
@@ -203,39 +139,44 @@ class AKeyless:
|
|
|
203
139
|
import os
|
|
204
140
|
from pathlib import Path
|
|
205
141
|
|
|
206
|
-
os.system(f"akeyless list-items --profile {self.
|
|
142
|
+
os.system(f"akeyless list-items --profile {self.profile} --path /not/a/real/path > /dev/null 2>&1")
|
|
207
143
|
home = str(Path.home())
|
|
208
|
-
with open(f"{home}/.akeyless/.tmp_creds/{self.
|
|
144
|
+
with open(f"{home}/.akeyless/.tmp_creds/{self.profile}-{self.access_id}", "r") as creds_file:
|
|
209
145
|
credentials = creds_file.read()
|
|
210
146
|
|
|
211
147
|
# and now we can turn that into a token
|
|
212
|
-
response = self.
|
|
148
|
+
response = self.requests.post(
|
|
213
149
|
"https://rest.akeyless.io/",
|
|
214
150
|
data={
|
|
215
151
|
"cmd": "static-creds-auth",
|
|
216
|
-
"access-id": self.
|
|
152
|
+
"access-id": self.access_id,
|
|
217
153
|
"creds": credentials.strip(),
|
|
218
154
|
},
|
|
219
155
|
)
|
|
220
156
|
return response.json()["token"]
|
|
221
157
|
|
|
222
158
|
def auth_jwt(self):
|
|
223
|
-
if not self.
|
|
159
|
+
if not self.jwt_env_key:
|
|
224
160
|
raise ValueError(
|
|
225
|
-
"To use AKeyless JWT Auth,
|
|
161
|
+
"To use AKeyless JWT Auth, "
|
|
162
|
+
"you must specify the name of the ENV key to load the JWT from when configuring AKeyless"
|
|
226
163
|
)
|
|
227
164
|
res = self._api.auth(
|
|
228
|
-
self.
|
|
229
|
-
access_id=self._access_id, access_type="jwt", jwt=self._environment.get(self._jwt_env_key)
|
|
230
|
-
)
|
|
165
|
+
self.akeyless.Auth(access_id=self.access_id, access_type="jwt", jwt=self.environment.get(self.jwt_env_key))
|
|
231
166
|
)
|
|
232
167
|
return res.token
|
|
233
168
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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)
|
clearskies/secrets/secrets.py
CHANGED
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
from
|
|
1
|
+
from typing import Any
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
class Secrets:
|
|
5
|
-
def create(self, path, value):
|
|
5
|
+
def create(self, path: str, value: str) -> None:
|
|
6
6
|
raise NotImplementedError(
|
|
7
7
|
"It looks like you tried to use the secret system in clearskies, but didn't specify a secret manager."
|
|
8
8
|
)
|
|
9
9
|
|
|
10
|
-
def get(self, path, silent_if_not_found=False):
|
|
10
|
+
def get(self, path: str, silent_if_not_found: bool = False) -> str:
|
|
11
11
|
raise NotImplementedError(
|
|
12
12
|
"It looks like you tried to use the secret system in clearskies, but didn't specify a secret manager."
|
|
13
13
|
)
|
|
14
14
|
|
|
15
|
-
def get_dynamic_secret(self, path):
|
|
15
|
+
def get_dynamic_secret(self, path: str, args: dict[str, Any] | None = None) -> Any:
|
|
16
16
|
raise NotImplementedError(
|
|
17
17
|
"It looks like you tried to use the secret system in clearskies, but didn't specify a secret manager."
|
|
18
18
|
)
|
|
19
19
|
|
|
20
|
-
def list_secrets(self, path):
|
|
20
|
+
def list_secrets(self, path: str) -> list[Any]:
|
|
21
21
|
raise NotImplementedError(
|
|
22
22
|
"It looks like you tried to use the secret system in clearskies, but didn't specify a secret manager."
|
|
23
23
|
)
|
|
24
24
|
|
|
25
|
-
def update(self, path, value):
|
|
25
|
+
def update(self, path: str, value: Any) -> None:
|
|
26
26
|
raise NotImplementedError(
|
|
27
27
|
"It looks like you tried to use the secret system in clearskies, but didn't specify a secret manager."
|
|
28
28
|
)
|
|
29
29
|
|
|
30
|
-
def upsert(self, path, value):
|
|
30
|
+
def upsert(self, path: str, value: Any) -> None:
|
|
31
31
|
raise NotImplementedError(
|
|
32
32
|
"It looks like you tried to use the secret system in clearskies, but didn't specify a secret manager."
|
|
33
33
|
)
|
|
34
34
|
|
|
35
|
-
def list_sub_folders(self, path
|
|
35
|
+
def list_sub_folders(self, path: str) -> list[Any]:
|
|
36
36
|
raise NotImplementedError(
|
|
37
37
|
"It looks like you tried to use the secret system in clearskies, but didn't specify a secret manager."
|
|
38
38
|
)
|
|
@@ -0,0 +1,15 @@
|
|
|
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 +1,11 @@
|
|
|
1
|
-
from .cache_control import
|
|
2
|
-
from .cors import
|
|
3
|
-
from .csp import
|
|
4
|
-
from .hsts import
|
|
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
5
|
|
|
6
6
|
__all__ = [
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
7
|
+
"CacheControl",
|
|
8
|
+
"Cors",
|
|
9
|
+
"Csp",
|
|
10
|
+
"Hsts",
|
|
11
11
|
]
|