clear-skies 1.22.10__py3-none-any.whl → 2.0.23__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.
- clear_skies-2.0.23.dist-info/METADATA +76 -0
- clear_skies-2.0.23.dist-info/RECORD +265 -0
- {clear_skies-1.22.10.dist-info → clear_skies-2.0.23.dist-info}/WHEEL +1 -1
- clearskies/__init__.py +37 -21
- clearskies/action.py +7 -0
- clearskies/authentication/__init__.py +8 -39
- clearskies/authentication/authentication.py +44 -0
- clearskies/authentication/authorization.py +14 -8
- clearskies/authentication/authorization_pass_through.py +14 -10
- clearskies/authentication/jwks.py +135 -58
- clearskies/authentication/public.py +3 -26
- clearskies/authentication/secret_bearer.py +515 -44
- clearskies/autodoc/formats/oai3_json/__init__.py +2 -2
- clearskies/autodoc/formats/oai3_json/oai3_json.py +11 -9
- 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 +10 -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 +16 -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 +1118 -280
- clearskies/backends/backend.py +54 -85
- clearskies/backends/cursor_backend.py +246 -191
- clearskies/backends/memory_backend.py +514 -208
- clearskies/backends/secrets_backend.py +68 -31
- clearskies/column.py +1221 -0
- clearskies/columns/__init__.py +71 -0
- clearskies/columns/audit.py +306 -0
- clearskies/columns/belongs_to_id.py +478 -0
- clearskies/columns/belongs_to_model.py +129 -0
- clearskies/columns/belongs_to_self.py +109 -0
- clearskies/columns/boolean.py +110 -0
- clearskies/columns/category_tree.py +273 -0
- clearskies/columns/category_tree_ancestors.py +51 -0
- clearskies/columns/category_tree_children.py +126 -0
- clearskies/columns/category_tree_descendants.py +48 -0
- clearskies/columns/created.py +92 -0
- clearskies/columns/created_by_authorization_data.py +114 -0
- clearskies/columns/created_by_header.py +103 -0
- clearskies/columns/created_by_ip.py +90 -0
- clearskies/columns/created_by_routing_data.py +102 -0
- clearskies/columns/created_by_user_agent.py +89 -0
- clearskies/columns/date.py +232 -0
- clearskies/columns/datetime.py +284 -0
- clearskies/columns/email.py +78 -0
- clearskies/columns/float.py +149 -0
- clearskies/columns/has_many.py +529 -0
- clearskies/columns/has_many_self.py +62 -0
- clearskies/columns/has_one.py +21 -0
- clearskies/columns/integer.py +158 -0
- clearskies/columns/json.py +126 -0
- clearskies/columns/many_to_many_ids.py +335 -0
- clearskies/columns/many_to_many_ids_with_data.py +274 -0
- clearskies/columns/many_to_many_models.py +156 -0
- clearskies/columns/many_to_many_pivots.py +132 -0
- clearskies/columns/phone.py +162 -0
- clearskies/columns/select.py +95 -0
- clearskies/columns/string.py +102 -0
- clearskies/columns/timestamp.py +164 -0
- clearskies/columns/updated.py +107 -0
- clearskies/columns/uuid.py +83 -0
- clearskies/configs/README.md +105 -0
- clearskies/configs/__init__.py +170 -0
- clearskies/configs/actions.py +43 -0
- clearskies/configs/any.py +15 -0
- clearskies/configs/any_dict.py +24 -0
- clearskies/configs/any_dict_or_callable.py +25 -0
- clearskies/configs/authentication.py +23 -0
- clearskies/configs/authorization.py +23 -0
- clearskies/configs/boolean.py +18 -0
- clearskies/configs/boolean_or_callable.py +20 -0
- clearskies/configs/callable_config.py +20 -0
- clearskies/configs/columns.py +34 -0
- clearskies/configs/conditions.py +30 -0
- clearskies/configs/config.py +26 -0
- clearskies/configs/datetime.py +20 -0
- clearskies/configs/datetime_or_callable.py +21 -0
- clearskies/configs/email.py +10 -0
- clearskies/configs/email_list.py +17 -0
- clearskies/configs/email_list_or_callable.py +17 -0
- clearskies/configs/email_or_email_list_or_callable.py +59 -0
- clearskies/configs/endpoint.py +23 -0
- clearskies/configs/endpoint_list.py +29 -0
- clearskies/configs/float.py +18 -0
- clearskies/configs/float_or_callable.py +20 -0
- clearskies/configs/headers.py +28 -0
- clearskies/configs/integer.py +18 -0
- clearskies/configs/integer_or_callable.py +20 -0
- clearskies/configs/joins.py +30 -0
- clearskies/configs/list_any_dict.py +32 -0
- clearskies/configs/list_any_dict_or_callable.py +33 -0
- clearskies/configs/model_class.py +35 -0
- clearskies/configs/model_column.py +67 -0
- clearskies/configs/model_columns.py +58 -0
- clearskies/configs/model_destination_name.py +26 -0
- clearskies/configs/model_to_id_column.py +45 -0
- clearskies/configs/readable_model_column.py +11 -0
- clearskies/configs/readable_model_columns.py +11 -0
- clearskies/configs/schema.py +23 -0
- clearskies/configs/searchable_model_columns.py +11 -0
- clearskies/configs/security_headers.py +39 -0
- clearskies/configs/select.py +28 -0
- clearskies/configs/select_list.py +49 -0
- clearskies/configs/string.py +31 -0
- clearskies/configs/string_dict.py +34 -0
- clearskies/configs/string_list.py +47 -0
- clearskies/configs/string_list_or_callable.py +48 -0
- clearskies/configs/string_or_callable.py +18 -0
- clearskies/configs/timedelta.py +20 -0
- clearskies/configs/timezone.py +20 -0
- clearskies/configs/url.py +25 -0
- clearskies/configs/validators.py +45 -0
- clearskies/configs/writeable_model_column.py +11 -0
- clearskies/configs/writeable_model_columns.py +11 -0
- clearskies/configurable.py +78 -0
- clearskies/contexts/__init__.py +8 -8
- clearskies/contexts/cli.py +129 -43
- clearskies/contexts/context.py +93 -56
- clearskies/contexts/wsgi.py +79 -33
- clearskies/contexts/wsgi_ref.py +87 -0
- clearskies/cursors/__init__.py +7 -0
- clearskies/cursors/cursor.py +166 -0
- clearskies/cursors/from_environment/__init__.py +5 -0
- clearskies/cursors/from_environment/mysql.py +51 -0
- clearskies/cursors/from_environment/postgresql.py +49 -0
- clearskies/cursors/from_environment/sqlite.py +35 -0
- clearskies/cursors/mysql.py +61 -0
- clearskies/cursors/postgresql.py +61 -0
- clearskies/cursors/sqlite.py +62 -0
- clearskies/decorators.py +33 -0
- clearskies/decorators.pyi +10 -0
- clearskies/di/__init__.py +11 -7
- clearskies/di/additional_config.py +115 -4
- clearskies/di/additional_config_auto_import.py +12 -0
- clearskies/di/di.py +714 -125
- clearskies/di/inject/__init__.py +23 -0
- clearskies/di/inject/akeyless_sdk.py +16 -0
- clearskies/di/inject/by_class.py +24 -0
- clearskies/di/inject/by_name.py +22 -0
- clearskies/di/inject/di.py +16 -0
- clearskies/di/inject/environment.py +15 -0
- clearskies/di/inject/input_output.py +19 -0
- clearskies/di/inject/now.py +16 -0
- clearskies/di/inject/requests.py +16 -0
- clearskies/di/inject/secrets.py +15 -0
- clearskies/di/inject/utcnow.py +16 -0
- clearskies/di/inject/uuid.py +16 -0
- clearskies/di/injectable.py +32 -0
- clearskies/di/injectable_properties.py +131 -0
- clearskies/end.py +219 -0
- clearskies/endpoint.py +1303 -0
- clearskies/endpoint_group.py +333 -0
- clearskies/endpoints/__init__.py +25 -0
- clearskies/endpoints/advanced_search.py +519 -0
- clearskies/endpoints/callable.py +382 -0
- clearskies/endpoints/create.py +201 -0
- clearskies/endpoints/delete.py +133 -0
- clearskies/endpoints/get.py +267 -0
- clearskies/endpoints/health_check.py +181 -0
- clearskies/endpoints/list.py +567 -0
- clearskies/endpoints/restful_api.py +417 -0
- clearskies/endpoints/schema.py +185 -0
- clearskies/endpoints/simple_search.py +279 -0
- clearskies/endpoints/update.py +188 -0
- clearskies/environment.py +7 -3
- clearskies/exceptions/__init__.py +19 -0
- clearskies/{handlers/exceptions/input_error.py → exceptions/input_errors.py} +1 -1
- clearskies/exceptions/missing_dependency.py +2 -0
- clearskies/exceptions/moved_permanently.py +3 -0
- clearskies/exceptions/moved_temporarily.py +3 -0
- clearskies/functional/__init__.py +2 -2
- clearskies/functional/json.py +47 -0
- 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 +135 -160
- clearskies/input_outputs/exceptions/__init__.py +6 -1
- clearskies/input_outputs/headers.py +54 -0
- clearskies/input_outputs/input_output.py +77 -123
- clearskies/input_outputs/programmatic.py +62 -0
- clearskies/input_outputs/wsgi.py +36 -48
- clearskies/model.py +1874 -193
- clearskies/query/__init__.py +12 -0
- clearskies/query/condition.py +228 -0
- clearskies/query/join.py +136 -0
- clearskies/query/query.py +193 -0
- clearskies/query/sort.py +27 -0
- clearskies/schema.py +82 -0
- clearskies/secrets/__init__.py +4 -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 +421 -155
- clearskies/secrets/exceptions/__init__.py +7 -1
- clearskies/secrets/exceptions/not_found_error.py +2 -0
- clearskies/secrets/exceptions/permissions_error.py +2 -0
- clearskies/secrets/secrets.py +12 -11
- clearskies/security_header.py +17 -0
- clearskies/security_headers/__init__.py +8 -8
- clearskies/security_headers/cache_control.py +47 -109
- clearskies/security_headers/cors.py +38 -92
- clearskies/security_headers/csp.py +76 -150
- clearskies/security_headers/hsts.py +14 -15
- clearskies/typing.py +11 -0
- clearskies/validator.py +36 -0
- clearskies/validators/__init__.py +33 -0
- clearskies/validators/after_column.py +61 -0
- clearskies/validators/before_column.py +15 -0
- clearskies/validators/in_the_future.py +29 -0
- clearskies/validators/in_the_future_at_least.py +13 -0
- clearskies/validators/in_the_future_at_most.py +12 -0
- clearskies/validators/in_the_past.py +29 -0
- clearskies/validators/in_the_past_at_least.py +12 -0
- clearskies/validators/in_the_past_at_most.py +12 -0
- clearskies/validators/maximum_length.py +25 -0
- clearskies/validators/maximum_value.py +28 -0
- clearskies/validators/minimum_length.py +25 -0
- clearskies/validators/minimum_value.py +28 -0
- clearskies/{input_requirements → validators}/required.py +18 -9
- clearskies/validators/timedelta.py +58 -0
- clearskies/validators/unique.py +28 -0
- clear_skies-1.22.10.dist-info/METADATA +0 -47
- clear_skies-1.22.10.dist-info/RECORD +0 -213
- 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 -13
- 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 -58
- 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 -39
- 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/di/test_module/__init__.py +0 -6
- clearskies/di/test_module/another_module/__init__.py +0 -2
- clearskies/di/test_module/module_class.py +0 -5
- clearskies/handlers/__init__.py +0 -41
- clearskies/handlers/advanced_search.py +0 -271
- clearskies/handlers/base.py +0 -479
- clearskies/handlers/callable.py +0 -191
- 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 -192
- clearskies/handlers/simple_search.py +0 -136
- clearskies/handlers/update.py +0 -96
- 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/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.10.dist-info → clear_skies-2.0.23.dist-info/licenses}/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/{secrets/exceptions → exceptions}/not_found.py +0 -0
- /clearskies/{tests/__init__.py → input_outputs/py.typed} +0 -0
- /clearskies/{tests/simple_api/__init__.py → py.typed} +0 -0
|
@@ -1,88 +1,165 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
3
|
import datetime
|
|
4
|
+
import json
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
|
+
|
|
7
|
+
from clearskies import configs, decorators, di
|
|
8
|
+
from clearskies.authentication.authentication import Authentication
|
|
9
|
+
from clearskies.exceptions import ClientError
|
|
10
|
+
from clearskies.security_headers.cors import Cors
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from clearskies.input_outputs.input_output import InputOutput
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Jwks(Authentication, di.InjectableProperties):
|
|
17
|
+
"""Validate a JWT against a JWKS (JSON Web Key Set)."""
|
|
18
|
+
|
|
19
|
+
"""
|
|
20
|
+
The URL of the JWKS
|
|
21
|
+
"""
|
|
22
|
+
jwks_url = configs.String(required=True)
|
|
23
|
+
|
|
24
|
+
"""
|
|
25
|
+
The audience to accept JWTs for.
|
|
26
|
+
"""
|
|
27
|
+
audience = configs.StringList(default=[])
|
|
28
|
+
|
|
29
|
+
"""
|
|
30
|
+
The expected issuer of the JWTs.
|
|
31
|
+
"""
|
|
32
|
+
issuer = configs.String(default="")
|
|
33
|
+
|
|
34
|
+
"""
|
|
35
|
+
The allowed algorithms
|
|
36
|
+
"""
|
|
37
|
+
algorithms = configs.StringList(default=["RS256"])
|
|
38
|
+
|
|
39
|
+
"""
|
|
40
|
+
The number of seconds for which the JWKS URL contents can be cached
|
|
41
|
+
"""
|
|
42
|
+
jwks_cache_time = configs.Integer(default=86400)
|
|
43
|
+
|
|
44
|
+
"""
|
|
45
|
+
The Authorization URL (used in the auto-generated documentation)
|
|
46
|
+
"""
|
|
47
|
+
authorization_url = configs.String()
|
|
48
|
+
|
|
49
|
+
"""
|
|
50
|
+
The name of the security scheme in the auto-generated documentation.
|
|
51
|
+
"""
|
|
52
|
+
documentation_security_name = configs.String(default="jwt")
|
|
53
|
+
|
|
54
|
+
"""
|
|
55
|
+
The environment helper.
|
|
56
|
+
"""
|
|
57
|
+
environment = di.inject.Environment()
|
|
4
58
|
|
|
59
|
+
"""
|
|
60
|
+
The requests object.
|
|
61
|
+
"""
|
|
62
|
+
requests = di.inject.Requests()
|
|
5
63
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
_authorization_url = None
|
|
64
|
+
"""
|
|
65
|
+
The JoseJwt library
|
|
66
|
+
"""
|
|
67
|
+
jose_jwt = di.inject.ByName("jose_jwt")
|
|
11
68
|
|
|
12
|
-
|
|
13
|
-
|
|
69
|
+
"""
|
|
70
|
+
The current time
|
|
71
|
+
"""
|
|
72
|
+
now = di.inject.Now()
|
|
14
73
|
|
|
15
|
-
|
|
74
|
+
"""
|
|
75
|
+
Local cache of the JWKS
|
|
76
|
+
"""
|
|
77
|
+
_jwks = None
|
|
78
|
+
|
|
79
|
+
"""
|
|
80
|
+
The time when the JWKS was last fetched
|
|
81
|
+
"""
|
|
82
|
+
_jwks_fetched: datetime.datetime
|
|
83
|
+
|
|
84
|
+
@decorators.parameters_to_properties
|
|
85
|
+
def __init__(
|
|
16
86
|
self,
|
|
17
|
-
jwks_url
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
authorization_url=
|
|
23
|
-
|
|
87
|
+
jwks_url: str,
|
|
88
|
+
audience: str = "",
|
|
89
|
+
issuer: str = "",
|
|
90
|
+
algorithms: list[str] = ["RS256"],
|
|
91
|
+
jwks_cache_time: int = 86400,
|
|
92
|
+
authorization_url: str = "",
|
|
93
|
+
documentation_security_name: str = "jwt",
|
|
24
94
|
):
|
|
25
|
-
self.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if not self._jwks_url:
|
|
30
|
-
raise ValueError("Must provide 'jwks_url' when using JWKS authentication")
|
|
31
|
-
self._algorithms = ["RS256"] if algorithms is None else algorithms
|
|
32
|
-
self._documentation_security_name = documentation_security_name
|
|
33
|
-
self._authorization_url = authorization_url if authorization_url else ""
|
|
34
|
-
|
|
35
|
-
def authenticate(self, input_output):
|
|
36
|
-
auth_header = input_output.get_request_header("authorization", True)
|
|
95
|
+
self.finalize_and_validate_configuration()
|
|
96
|
+
|
|
97
|
+
def authenticate(self, input_output: InputOutput) -> bool:
|
|
98
|
+
auth_header = input_output.request_headers.get("authorization", None)
|
|
37
99
|
if not auth_header:
|
|
38
100
|
raise ClientError("Missing 'Authorization' header in request")
|
|
39
101
|
if auth_header[:7].lower() != "bearer ":
|
|
40
102
|
raise ClientError("Missing 'Bearer ' prefix in authorization header")
|
|
41
103
|
self.validate_jwt(auth_header[7:])
|
|
42
|
-
input_output.
|
|
104
|
+
input_output.authorization_data = self.jwt_claims
|
|
43
105
|
return True
|
|
44
106
|
|
|
45
107
|
def validate_jwt(self, raw_jwt):
|
|
46
108
|
try:
|
|
47
|
-
|
|
48
|
-
|
|
109
|
+
from jwcrypto import jwk, jwt # type: ignore
|
|
110
|
+
from jwcrypto.common import JWException # type: ignore
|
|
111
|
+
except:
|
|
112
|
+
raise ValueError(
|
|
113
|
+
"The JWKS authentication method requires the jwcrypto libraries to be installed. These are optional dependencies of clearskies, so to include them do a `pip install 'clear-skies[jwcrypto]'`"
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
keys = jwk.JWKSet()
|
|
117
|
+
keys.import_keyset(json.dumps(self._get_jwks()))
|
|
118
|
+
|
|
119
|
+
client_jwt = jwt.JWT()
|
|
120
|
+
try:
|
|
121
|
+
client_jwt.deserialize(raw_jwt)
|
|
122
|
+
except Exception as e:
|
|
49
123
|
raise ClientError(str(e))
|
|
50
|
-
jwks = self._get_jwks()
|
|
51
|
-
# find a matching key in the JWKS for the key in the JWT
|
|
52
|
-
rsa_key = next((key for key in jwks["keys"] if key["kid"] == unverified_header["kid"]), False)
|
|
53
|
-
if not rsa_key:
|
|
54
|
-
raise ClientError("No matching keys found")
|
|
55
124
|
|
|
56
125
|
try:
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
126
|
+
client_jwt.validate(keys)
|
|
127
|
+
self.jwt_claims = json.loads(client_jwt.claims)
|
|
128
|
+
except JWException as e:
|
|
129
|
+
raise ClientError(str(e))
|
|
130
|
+
|
|
131
|
+
if self.issuer and self.jwt_claims.get("iss") != self.issuer:
|
|
132
|
+
raise ClientError("Issuer does not match")
|
|
133
|
+
|
|
134
|
+
if self.audience:
|
|
135
|
+
jwt_audience = self.jwt_claims.get("aud")
|
|
136
|
+
if not jwt_audience:
|
|
137
|
+
raise ClientError("Audience required, but missing in JWT")
|
|
138
|
+
has_match = False
|
|
139
|
+
for audience in jwt_audience:
|
|
140
|
+
if audience == self.audience:
|
|
141
|
+
has_match = True
|
|
142
|
+
if not has_match:
|
|
143
|
+
raise ClientError("Audience does not match")
|
|
144
|
+
|
|
70
145
|
return True
|
|
71
146
|
|
|
72
147
|
def _get_jwks(self):
|
|
73
|
-
now
|
|
74
|
-
|
|
75
|
-
self.
|
|
76
|
-
self._jwks_fetched = now
|
|
148
|
+
if self._jwks is None or ((self.now - self._jwks_fetched).total_seconds() > self.jwks_cache_time):
|
|
149
|
+
self._jwks = self.requests.get(self.jwks_url).json()
|
|
150
|
+
self._jwks_fetched = self.now
|
|
77
151
|
|
|
78
152
|
return self._jwks
|
|
79
153
|
|
|
80
|
-
def documentation_security_scheme(self):
|
|
154
|
+
def documentation_security_scheme(self) -> dict[str, Any]:
|
|
81
155
|
return {
|
|
82
156
|
"type": "oauth2",
|
|
83
157
|
"description": "JWT based authentication",
|
|
84
|
-
"flows": {"implicit": {"authorizationUrl": self.
|
|
158
|
+
"flows": {"implicit": {"authorizationUrl": self.authorization_url, "scopes": {}}},
|
|
85
159
|
}
|
|
86
160
|
|
|
87
|
-
def documentation_security_scheme_name(self):
|
|
88
|
-
return self.
|
|
161
|
+
def documentation_security_scheme_name(self) -> str:
|
|
162
|
+
return self.documentation_security_name
|
|
163
|
+
|
|
164
|
+
def set_headers_for_cors(self, cors: Cors):
|
|
165
|
+
cors.add_header("Authorization")
|
|
@@ -1,28 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
is_public = True
|
|
3
|
-
can_authorize = False
|
|
4
|
-
has_dynamic_credentials = False
|
|
1
|
+
from .authentication import Authentication
|
|
5
2
|
|
|
6
|
-
def headers(self, retry_auth=False):
|
|
7
|
-
return {}
|
|
8
3
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def authenticate(self, input_output):
|
|
13
|
-
return True
|
|
14
|
-
|
|
15
|
-
def authorize(self, authorization):
|
|
16
|
-
raise ValueError("Public endpoints do not support authorization")
|
|
17
|
-
|
|
18
|
-
def set_headers_for_cors(self, cors):
|
|
19
|
-
pass
|
|
20
|
-
|
|
21
|
-
def documentation_request_parameters(self):
|
|
22
|
-
return []
|
|
23
|
-
|
|
24
|
-
def documentation_security_scheme(self):
|
|
25
|
-
return {}
|
|
26
|
-
|
|
27
|
-
def documentation_security_scheme_name(self):
|
|
28
|
-
return ""
|
|
4
|
+
class Public(Authentication):
|
|
5
|
+
pass
|