clear-skies 1.19.22__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.
Files changed (362) hide show
  1. clear_skies-2.0.23.dist-info/METADATA +76 -0
  2. clear_skies-2.0.23.dist-info/RECORD +265 -0
  3. {clear_skies-1.19.22.dist-info → clear_skies-2.0.23.dist-info}/WHEEL +1 -1
  4. clearskies/__init__.py +37 -21
  5. clearskies/action.py +7 -0
  6. clearskies/authentication/__init__.py +9 -38
  7. clearskies/authentication/authentication.py +44 -0
  8. clearskies/authentication/authorization.py +14 -8
  9. clearskies/authentication/authorization_pass_through.py +22 -0
  10. clearskies/authentication/jwks.py +135 -58
  11. clearskies/authentication/public.py +3 -26
  12. clearskies/authentication/secret_bearer.py +515 -44
  13. clearskies/autodoc/formats/oai3_json/__init__.py +2 -2
  14. clearskies/autodoc/formats/oai3_json/oai3_json.py +11 -9
  15. clearskies/autodoc/formats/oai3_json/parameter.py +6 -3
  16. clearskies/autodoc/formats/oai3_json/request.py +7 -5
  17. clearskies/autodoc/formats/oai3_json/response.py +7 -4
  18. clearskies/autodoc/formats/oai3_json/schema/object.py +10 -1
  19. clearskies/autodoc/request/__init__.py +2 -0
  20. clearskies/autodoc/request/header.py +4 -6
  21. clearskies/autodoc/request/json_body.py +4 -6
  22. clearskies/autodoc/request/parameter.py +8 -0
  23. clearskies/autodoc/request/request.py +16 -4
  24. clearskies/autodoc/request/url_parameter.py +4 -6
  25. clearskies/autodoc/request/url_path.py +4 -6
  26. clearskies/autodoc/schema/__init__.py +4 -2
  27. clearskies/autodoc/schema/array.py +5 -6
  28. clearskies/autodoc/schema/boolean.py +4 -10
  29. clearskies/autodoc/schema/date.py +0 -3
  30. clearskies/autodoc/schema/datetime.py +1 -4
  31. clearskies/autodoc/schema/double.py +0 -3
  32. clearskies/autodoc/schema/enum.py +4 -2
  33. clearskies/autodoc/schema/integer.py +4 -9
  34. clearskies/autodoc/schema/long.py +0 -3
  35. clearskies/autodoc/schema/number.py +4 -9
  36. clearskies/autodoc/schema/object.py +5 -7
  37. clearskies/autodoc/schema/password.py +0 -3
  38. clearskies/autodoc/schema/schema.py +11 -0
  39. clearskies/autodoc/schema/string.py +4 -10
  40. clearskies/backends/__init__.py +56 -17
  41. clearskies/backends/api_backend.py +1128 -166
  42. clearskies/backends/backend.py +54 -85
  43. clearskies/backends/cursor_backend.py +246 -191
  44. clearskies/backends/memory_backend.py +514 -208
  45. clearskies/backends/secrets_backend.py +68 -31
  46. clearskies/column.py +1221 -0
  47. clearskies/columns/__init__.py +71 -0
  48. clearskies/columns/audit.py +306 -0
  49. clearskies/columns/belongs_to_id.py +478 -0
  50. clearskies/columns/belongs_to_model.py +129 -0
  51. clearskies/columns/belongs_to_self.py +109 -0
  52. clearskies/columns/boolean.py +110 -0
  53. clearskies/columns/category_tree.py +273 -0
  54. clearskies/columns/category_tree_ancestors.py +51 -0
  55. clearskies/columns/category_tree_children.py +126 -0
  56. clearskies/columns/category_tree_descendants.py +48 -0
  57. clearskies/columns/created.py +92 -0
  58. clearskies/columns/created_by_authorization_data.py +114 -0
  59. clearskies/columns/created_by_header.py +103 -0
  60. clearskies/columns/created_by_ip.py +90 -0
  61. clearskies/columns/created_by_routing_data.py +102 -0
  62. clearskies/columns/created_by_user_agent.py +89 -0
  63. clearskies/columns/date.py +232 -0
  64. clearskies/columns/datetime.py +284 -0
  65. clearskies/columns/email.py +78 -0
  66. clearskies/columns/float.py +149 -0
  67. clearskies/columns/has_many.py +529 -0
  68. clearskies/columns/has_many_self.py +62 -0
  69. clearskies/columns/has_one.py +21 -0
  70. clearskies/columns/integer.py +158 -0
  71. clearskies/columns/json.py +126 -0
  72. clearskies/columns/many_to_many_ids.py +335 -0
  73. clearskies/columns/many_to_many_ids_with_data.py +274 -0
  74. clearskies/columns/many_to_many_models.py +156 -0
  75. clearskies/columns/many_to_many_pivots.py +132 -0
  76. clearskies/columns/phone.py +162 -0
  77. clearskies/columns/select.py +95 -0
  78. clearskies/columns/string.py +102 -0
  79. clearskies/columns/timestamp.py +164 -0
  80. clearskies/columns/updated.py +107 -0
  81. clearskies/columns/uuid.py +83 -0
  82. clearskies/configs/README.md +105 -0
  83. clearskies/configs/__init__.py +170 -0
  84. clearskies/configs/actions.py +43 -0
  85. clearskies/configs/any.py +15 -0
  86. clearskies/configs/any_dict.py +24 -0
  87. clearskies/configs/any_dict_or_callable.py +25 -0
  88. clearskies/configs/authentication.py +23 -0
  89. clearskies/configs/authorization.py +23 -0
  90. clearskies/configs/boolean.py +18 -0
  91. clearskies/configs/boolean_or_callable.py +20 -0
  92. clearskies/configs/callable_config.py +20 -0
  93. clearskies/configs/columns.py +34 -0
  94. clearskies/configs/conditions.py +30 -0
  95. clearskies/configs/config.py +26 -0
  96. clearskies/configs/datetime.py +20 -0
  97. clearskies/configs/datetime_or_callable.py +21 -0
  98. clearskies/configs/email.py +10 -0
  99. clearskies/configs/email_list.py +17 -0
  100. clearskies/configs/email_list_or_callable.py +17 -0
  101. clearskies/configs/email_or_email_list_or_callable.py +59 -0
  102. clearskies/configs/endpoint.py +23 -0
  103. clearskies/configs/endpoint_list.py +29 -0
  104. clearskies/configs/float.py +18 -0
  105. clearskies/configs/float_or_callable.py +20 -0
  106. clearskies/configs/headers.py +28 -0
  107. clearskies/configs/integer.py +18 -0
  108. clearskies/configs/integer_or_callable.py +20 -0
  109. clearskies/configs/joins.py +30 -0
  110. clearskies/configs/list_any_dict.py +32 -0
  111. clearskies/configs/list_any_dict_or_callable.py +33 -0
  112. clearskies/configs/model_class.py +35 -0
  113. clearskies/configs/model_column.py +67 -0
  114. clearskies/configs/model_columns.py +58 -0
  115. clearskies/configs/model_destination_name.py +26 -0
  116. clearskies/configs/model_to_id_column.py +45 -0
  117. clearskies/configs/readable_model_column.py +11 -0
  118. clearskies/configs/readable_model_columns.py +11 -0
  119. clearskies/configs/schema.py +23 -0
  120. clearskies/configs/searchable_model_columns.py +11 -0
  121. clearskies/configs/security_headers.py +39 -0
  122. clearskies/configs/select.py +28 -0
  123. clearskies/configs/select_list.py +49 -0
  124. clearskies/configs/string.py +31 -0
  125. clearskies/configs/string_dict.py +34 -0
  126. clearskies/configs/string_list.py +47 -0
  127. clearskies/configs/string_list_or_callable.py +48 -0
  128. clearskies/configs/string_or_callable.py +18 -0
  129. clearskies/configs/timedelta.py +20 -0
  130. clearskies/configs/timezone.py +20 -0
  131. clearskies/configs/url.py +25 -0
  132. clearskies/configs/validators.py +45 -0
  133. clearskies/configs/writeable_model_column.py +11 -0
  134. clearskies/configs/writeable_model_columns.py +11 -0
  135. clearskies/configurable.py +78 -0
  136. clearskies/contexts/__init__.py +8 -8
  137. clearskies/contexts/cli.py +129 -43
  138. clearskies/contexts/context.py +93 -56
  139. clearskies/contexts/wsgi.py +79 -33
  140. clearskies/contexts/wsgi_ref.py +87 -0
  141. clearskies/cursors/__init__.py +7 -0
  142. clearskies/cursors/cursor.py +166 -0
  143. clearskies/cursors/from_environment/__init__.py +5 -0
  144. clearskies/cursors/from_environment/mysql.py +51 -0
  145. clearskies/cursors/from_environment/postgresql.py +49 -0
  146. clearskies/cursors/from_environment/sqlite.py +35 -0
  147. clearskies/cursors/mysql.py +61 -0
  148. clearskies/cursors/postgresql.py +61 -0
  149. clearskies/cursors/sqlite.py +62 -0
  150. clearskies/decorators.py +33 -0
  151. clearskies/decorators.pyi +10 -0
  152. clearskies/di/__init__.py +11 -7
  153. clearskies/di/additional_config.py +117 -3
  154. clearskies/di/additional_config_auto_import.py +12 -0
  155. clearskies/di/di.py +717 -126
  156. clearskies/di/inject/__init__.py +23 -0
  157. clearskies/di/inject/akeyless_sdk.py +16 -0
  158. clearskies/di/inject/by_class.py +24 -0
  159. clearskies/di/inject/by_name.py +22 -0
  160. clearskies/di/inject/di.py +16 -0
  161. clearskies/di/inject/environment.py +15 -0
  162. clearskies/di/inject/input_output.py +19 -0
  163. clearskies/di/inject/now.py +16 -0
  164. clearskies/di/inject/requests.py +16 -0
  165. clearskies/di/inject/secrets.py +15 -0
  166. clearskies/di/inject/utcnow.py +16 -0
  167. clearskies/di/inject/uuid.py +16 -0
  168. clearskies/di/injectable.py +32 -0
  169. clearskies/di/injectable_properties.py +131 -0
  170. clearskies/end.py +219 -0
  171. clearskies/endpoint.py +1303 -0
  172. clearskies/endpoint_group.py +333 -0
  173. clearskies/endpoints/__init__.py +25 -0
  174. clearskies/endpoints/advanced_search.py +519 -0
  175. clearskies/endpoints/callable.py +382 -0
  176. clearskies/endpoints/create.py +201 -0
  177. clearskies/endpoints/delete.py +133 -0
  178. clearskies/endpoints/get.py +267 -0
  179. clearskies/endpoints/health_check.py +181 -0
  180. clearskies/endpoints/list.py +567 -0
  181. clearskies/endpoints/restful_api.py +417 -0
  182. clearskies/endpoints/schema.py +185 -0
  183. clearskies/endpoints/simple_search.py +279 -0
  184. clearskies/endpoints/update.py +188 -0
  185. clearskies/environment.py +7 -3
  186. clearskies/exceptions/__init__.py +19 -0
  187. clearskies/{handlers/exceptions/input_error.py → exceptions/input_errors.py} +1 -1
  188. clearskies/exceptions/missing_dependency.py +2 -0
  189. clearskies/exceptions/moved_permanently.py +3 -0
  190. clearskies/exceptions/moved_temporarily.py +3 -0
  191. clearskies/functional/__init__.py +2 -2
  192. clearskies/functional/json.py +47 -0
  193. clearskies/functional/routing.py +92 -0
  194. clearskies/functional/string.py +19 -11
  195. clearskies/functional/validations.py +61 -9
  196. clearskies/input_outputs/__init__.py +9 -7
  197. clearskies/input_outputs/cli.py +135 -152
  198. clearskies/input_outputs/exceptions/__init__.py +6 -1
  199. clearskies/input_outputs/headers.py +54 -0
  200. clearskies/input_outputs/input_output.py +77 -123
  201. clearskies/input_outputs/programmatic.py +62 -0
  202. clearskies/input_outputs/wsgi.py +36 -48
  203. clearskies/model.py +1894 -199
  204. clearskies/query/__init__.py +12 -0
  205. clearskies/query/condition.py +228 -0
  206. clearskies/query/join.py +136 -0
  207. clearskies/query/query.py +193 -0
  208. clearskies/query/sort.py +27 -0
  209. clearskies/schema.py +82 -0
  210. clearskies/secrets/__init__.py +4 -31
  211. clearskies/secrets/additional_configs/mysql_connection_dynamic_producer.py +15 -4
  212. clearskies/secrets/additional_configs/mysql_connection_dynamic_producer_via_ssh_cert_bastion.py +11 -5
  213. clearskies/secrets/akeyless.py +421 -155
  214. clearskies/secrets/exceptions/__init__.py +7 -1
  215. clearskies/secrets/exceptions/not_found_error.py +2 -0
  216. clearskies/secrets/exceptions/permissions_error.py +2 -0
  217. clearskies/secrets/secrets.py +12 -11
  218. clearskies/security_header.py +17 -0
  219. clearskies/security_headers/__init__.py +8 -8
  220. clearskies/security_headers/cache_control.py +47 -109
  221. clearskies/security_headers/cors.py +38 -92
  222. clearskies/security_headers/csp.py +76 -150
  223. clearskies/security_headers/hsts.py +14 -15
  224. clearskies/typing.py +11 -0
  225. clearskies/validator.py +36 -0
  226. clearskies/validators/__init__.py +33 -0
  227. clearskies/validators/after_column.py +61 -0
  228. clearskies/validators/before_column.py +15 -0
  229. clearskies/validators/in_the_future.py +29 -0
  230. clearskies/validators/in_the_future_at_least.py +13 -0
  231. clearskies/validators/in_the_future_at_most.py +12 -0
  232. clearskies/validators/in_the_past.py +29 -0
  233. clearskies/validators/in_the_past_at_least.py +12 -0
  234. clearskies/validators/in_the_past_at_most.py +12 -0
  235. clearskies/validators/maximum_length.py +25 -0
  236. clearskies/validators/maximum_value.py +28 -0
  237. clearskies/validators/minimum_length.py +25 -0
  238. clearskies/validators/minimum_value.py +28 -0
  239. clearskies/{input_requirements → validators}/required.py +18 -9
  240. clearskies/validators/timedelta.py +58 -0
  241. clearskies/validators/unique.py +28 -0
  242. clear_skies-1.19.22.dist-info/METADATA +0 -46
  243. clear_skies-1.19.22.dist-info/RECORD +0 -206
  244. clearskies/application.py +0 -29
  245. clearskies/authentication/auth0_jwks.py +0 -118
  246. clearskies/authentication/auth_exception.py +0 -2
  247. clearskies/authentication/jwks_jwcrypto.py +0 -39
  248. clearskies/backends/example_backend.py +0 -43
  249. clearskies/backends/file_backend.py +0 -48
  250. clearskies/backends/json_backend.py +0 -7
  251. clearskies/backends/restful_api_advanced_search_backend.py +0 -138
  252. clearskies/binding_config.py +0 -16
  253. clearskies/column_types/__init__.py +0 -184
  254. clearskies/column_types/audit.py +0 -235
  255. clearskies/column_types/belongs_to.py +0 -250
  256. clearskies/column_types/boolean.py +0 -60
  257. clearskies/column_types/category_tree.py +0 -226
  258. clearskies/column_types/column.py +0 -373
  259. clearskies/column_types/created.py +0 -26
  260. clearskies/column_types/created_by_authorization_data.py +0 -26
  261. clearskies/column_types/created_by_header.py +0 -24
  262. clearskies/column_types/created_by_ip.py +0 -17
  263. clearskies/column_types/created_by_routing_data.py +0 -25
  264. clearskies/column_types/created_by_user_agent.py +0 -17
  265. clearskies/column_types/created_micro.py +0 -26
  266. clearskies/column_types/datetime.py +0 -108
  267. clearskies/column_types/datetime_micro.py +0 -12
  268. clearskies/column_types/email.py +0 -18
  269. clearskies/column_types/float.py +0 -43
  270. clearskies/column_types/has_many.py +0 -139
  271. clearskies/column_types/integer.py +0 -41
  272. clearskies/column_types/json.py +0 -25
  273. clearskies/column_types/many_to_many.py +0 -278
  274. clearskies/column_types/many_to_many_with_data.py +0 -162
  275. clearskies/column_types/select.py +0 -11
  276. clearskies/column_types/string.py +0 -24
  277. clearskies/column_types/updated.py +0 -24
  278. clearskies/column_types/updated_micro.py +0 -24
  279. clearskies/column_types/uuid.py +0 -25
  280. clearskies/columns.py +0 -123
  281. clearskies/condition_parser.py +0 -172
  282. clearskies/contexts/build_context.py +0 -54
  283. clearskies/contexts/convert_to_application.py +0 -190
  284. clearskies/contexts/extract_handler.py +0 -37
  285. clearskies/contexts/test.py +0 -94
  286. clearskies/decorators/__init__.py +0 -39
  287. clearskies/decorators/auth0_jwks.py +0 -22
  288. clearskies/decorators/authorization.py +0 -10
  289. clearskies/decorators/binding_classes.py +0 -9
  290. clearskies/decorators/binding_modules.py +0 -9
  291. clearskies/decorators/bindings.py +0 -9
  292. clearskies/decorators/create.py +0 -10
  293. clearskies/decorators/delete.py +0 -10
  294. clearskies/decorators/docs.py +0 -14
  295. clearskies/decorators/get.py +0 -10
  296. clearskies/decorators/jwks.py +0 -26
  297. clearskies/decorators/merge.py +0 -124
  298. clearskies/decorators/patch.py +0 -10
  299. clearskies/decorators/post.py +0 -10
  300. clearskies/decorators/public.py +0 -11
  301. clearskies/decorators/response_headers.py +0 -10
  302. clearskies/decorators/return_raw_response.py +0 -9
  303. clearskies/decorators/schema.py +0 -10
  304. clearskies/decorators/secret_bearer.py +0 -24
  305. clearskies/decorators/security_headers.py +0 -10
  306. clearskies/di/standard_dependencies.py +0 -140
  307. clearskies/di/test_module/__init__.py +0 -6
  308. clearskies/di/test_module/another_module/__init__.py +0 -2
  309. clearskies/di/test_module/module_class.py +0 -5
  310. clearskies/handlers/__init__.py +0 -41
  311. clearskies/handlers/advanced_search.py +0 -271
  312. clearskies/handlers/base.py +0 -473
  313. clearskies/handlers/callable.py +0 -189
  314. clearskies/handlers/create.py +0 -35
  315. clearskies/handlers/crud_by_method.py +0 -18
  316. clearskies/handlers/database_connector.py +0 -32
  317. clearskies/handlers/delete.py +0 -61
  318. clearskies/handlers/exceptions/__init__.py +0 -5
  319. clearskies/handlers/exceptions/not_found.py +0 -3
  320. clearskies/handlers/get.py +0 -156
  321. clearskies/handlers/health_check.py +0 -59
  322. clearskies/handlers/input_processing.py +0 -79
  323. clearskies/handlers/list.py +0 -530
  324. clearskies/handlers/mygrations.py +0 -82
  325. clearskies/handlers/request_method_routing.py +0 -47
  326. clearskies/handlers/restful_api.py +0 -218
  327. clearskies/handlers/routing.py +0 -62
  328. clearskies/handlers/schema_helper.py +0 -128
  329. clearskies/handlers/simple_routing.py +0 -204
  330. clearskies/handlers/simple_routing_route.py +0 -192
  331. clearskies/handlers/simple_search.py +0 -136
  332. clearskies/handlers/update.py +0 -96
  333. clearskies/handlers/write.py +0 -193
  334. clearskies/input_requirements/__init__.py +0 -68
  335. clearskies/input_requirements/after.py +0 -36
  336. clearskies/input_requirements/before.py +0 -36
  337. clearskies/input_requirements/in_the_future_at_least.py +0 -19
  338. clearskies/input_requirements/in_the_future_at_most.py +0 -19
  339. clearskies/input_requirements/in_the_past_at_least.py +0 -19
  340. clearskies/input_requirements/in_the_past_at_most.py +0 -19
  341. clearskies/input_requirements/maximum_length.py +0 -19
  342. clearskies/input_requirements/minimum_length.py +0 -22
  343. clearskies/input_requirements/requirement.py +0 -25
  344. clearskies/input_requirements/time_delta.py +0 -38
  345. clearskies/input_requirements/unique.py +0 -18
  346. clearskies/mocks/__init__.py +0 -7
  347. clearskies/mocks/input_output.py +0 -124
  348. clearskies/mocks/models.py +0 -142
  349. clearskies/models.py +0 -345
  350. clearskies/security_headers/base.py +0 -12
  351. clearskies/tests/simple_api/models/__init__.py +0 -2
  352. clearskies/tests/simple_api/models/status.py +0 -23
  353. clearskies/tests/simple_api/models/user.py +0 -21
  354. clearskies/tests/simple_api/users_api.py +0 -64
  355. {clear_skies-1.19.22.dist-info → clear_skies-2.0.23.dist-info/licenses}/LICENSE +0 -0
  356. /clearskies/{contexts/bash.py → autodoc/py.typed} +0 -0
  357. /clearskies/{handlers/exceptions → exceptions}/authentication.py +0 -0
  358. /clearskies/{handlers/exceptions → exceptions}/authorization.py +0 -0
  359. /clearskies/{handlers/exceptions → exceptions}/client_error.py +0 -0
  360. /clearskies/{secrets/exceptions → exceptions}/not_found.py +0 -0
  361. /clearskies/{tests/__init__.py → input_outputs/py.typed} +0 -0
  362. /clearskies/{tests/simple_api/__init__.py → py.typed} +0 -0
@@ -1 +1,7 @@
1
- from .not_found import NotFound
1
+ from clearskies.secrets.exceptions.not_found_error import NotFoundError
2
+ from clearskies.secrets.exceptions.permissions_error import PermissionsError
3
+
4
+ __all__ = [
5
+ "NotFoundError",
6
+ "PermissionsError",
7
+ ]
@@ -0,0 +1,2 @@
1
+ class NotFoundError(Exception):
2
+ pass
@@ -0,0 +1,2 @@
1
+ class PermissionsError(Exception):
2
+ pass
@@ -1,38 +1,39 @@
1
+ from __future__ import annotations
2
+
1
3
  from abc import ABC
4
+ from typing import Any
2
5
 
6
+ import clearskies.configurable
7
+ from clearskies.di.injectable_properties import InjectableProperties
3
8
 
4
- class Secrets:
5
- def create(self, path, value):
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
9
 
10
- def get(self, path, silent_if_not_found=False):
10
+ class Secrets(ABC, clearskies.configurable.Configurable, InjectableProperties):
11
+ def create(self, path: str, value: str) -> bool:
11
12
  raise NotImplementedError(
12
13
  "It looks like you tried to use the secret system in clearskies, but didn't specify a secret manager."
13
14
  )
14
15
 
15
- def get_dynamic_secret(self, path):
16
+ def get(self, path: str, silent_if_not_found: bool = False) -> str:
16
17
  raise NotImplementedError(
17
18
  "It looks like you tried to use the secret system in clearskies, but didn't specify a secret manager."
18
19
  )
19
20
 
20
- def list_secrets(self, path):
21
+ def list_secrets(self, path: str) -> list[Any]:
21
22
  raise NotImplementedError(
22
23
  "It looks like you tried to use the secret system in clearskies, but didn't specify a secret manager."
23
24
  )
24
25
 
25
- def update(self, path, value):
26
+ def update(self, path: str, value: Any) -> None:
26
27
  raise NotImplementedError(
27
28
  "It looks like you tried to use the secret system in clearskies, but didn't specify a secret manager."
28
29
  )
29
30
 
30
- def upsert(self, path, value):
31
+ def upsert(self, path: str, value: Any) -> None:
31
32
  raise NotImplementedError(
32
33
  "It looks like you tried to use the secret system in clearskies, but didn't specify a secret manager."
33
34
  )
34
35
 
35
- def list_sub_folders(self, path, value):
36
+ def list_sub_folders(self, path: str) -> list[Any]:
36
37
  raise NotImplementedError(
37
38
  "It looks like you tried to use the secret system in clearskies, but didn't specify a secret manager."
38
39
  )
@@ -0,0 +1,17 @@
1
+ from __future__ import annotations
2
+
3
+ from clearskies import configurable
4
+
5
+
6
+ class SecurityHeader(configurable.Configurable):
7
+ """
8
+ Attach all the various security headers to endpoints.
9
+
10
+ The security header classes can be attached directly to both endpoints and endpoint groups and
11
+ are used to set all the various security headers.
12
+ """
13
+
14
+ is_cors = False
15
+
16
+ def set_headers_for_input_output(self, input_output):
17
+ raise NotImplementedError()
@@ -1,11 +1,11 @@
1
- from .cache_control import cache_control
2
- from .cors import cors
3
- from .csp import csp
4
- from .hsts import hsts
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
- "cache_control",
8
- "cors",
9
- "csp",
10
- "hsts",
7
+ "CacheControl",
8
+ "Cors",
9
+ "Csp",
10
+ "Hsts",
11
11
  ]
@@ -1,76 +1,63 @@
1
- from .base import Base
2
- from ..binding_config import BindingConfig
1
+ from __future__ import annotations
3
2
 
4
- numbers = [
5
- "max_age",
6
- "stale_if_error",
7
- "stale_while_revalidate",
8
- "s_maxage",
9
- ]
10
- bools = [
11
- "immutable",
12
- "must_understand",
13
- "no_cache",
14
- "no_store",
15
- "no_transform",
16
- "private",
17
- "public",
18
- "s_maxage",
19
- ]
3
+ from clearskies import configs, decorators
4
+ from clearskies.security_header import SecurityHeader
20
5
 
21
6
 
22
- class CacheControl(Base):
23
- max_age = None
24
- no_cache = None
25
- no_store = None
26
- no_transform = None
27
- s_maxage = None
28
- must_understand = None
29
- private = None
30
- public = None
31
- immutable = None
32
- stale_while_revalidate = None
33
- stale_if_error = None
7
+ class CacheControl(SecurityHeader):
8
+ max_age = configs.Integer()
9
+ s_maxage = configs.Integer()
10
+ stale_while_revalidate = configs.Integer()
11
+ stale_if_error = configs.Integer()
12
+ immutable = configs.Boolean(default=False)
13
+ must_understand = configs.Boolean(default=False)
14
+ no_cache = configs.Boolean(default=False)
15
+ no_store = configs.Boolean(default=False)
16
+ no_transform = configs.Boolean(default=False)
17
+ private = configs.Boolean(default=False)
18
+ public = configs.Boolean(default=False)
34
19
 
35
- def __init__(self, environment):
36
- super().__init__(environment)
20
+ numbers: list[str] = [
21
+ "max_age",
22
+ "stale_if_error",
23
+ "stale_while_revalidate",
24
+ "s_maxage",
25
+ ]
26
+ bools: list[str] = [
27
+ "immutable",
28
+ "must_understand",
29
+ "no_cache",
30
+ "no_store",
31
+ "no_transform",
32
+ "private",
33
+ "public",
34
+ ]
37
35
 
38
- def configure(
36
+ @decorators.parameters_to_properties
37
+ def __init__(
39
38
  self,
40
- no_cache=None,
41
- no_store=None,
42
- no_transform=None,
43
- max_age=None,
44
- s_maxage=None,
45
- must_revalidate=None,
46
- proxy_revalidate=None,
47
- must_understand=None,
48
- private=None,
49
- public=None,
50
- immutable=None,
51
- stale_while_revalidate=None,
52
- stale_if_error=None,
39
+ max_age: int | None = None,
40
+ s_maxage: int | None = None,
41
+ stale_while_revalidate: int | None = None,
42
+ stale_if_error: int | None = None,
43
+ immutable: bool = False,
44
+ must_understand: bool = False,
45
+ no_cache: bool = False,
46
+ no_store: bool = False,
47
+ no_transform: bool = False,
48
+ private: bool = False,
49
+ public: bool = False,
53
50
  ):
54
- self.max_age = max_age
55
- self.no_cache = no_cache
56
- self.no_store = no_store
57
- self.no_transform = no_transform
58
- self.s_maxage = s_maxage
59
- self.must_understand = must_understand
60
- self.private = private
61
- self.public = public
62
- self.immutable = immutable
63
- self.stale_while_revalidate = stale_while_revalidate
64
- self.stale_if_error = stale_if_error
51
+ self.finalize_and_validate_configuration()
65
52
 
66
53
  def set_headers_for_input_output(self, input_output):
67
54
  parts = []
68
- for variable_name in bools:
55
+ for variable_name in self.bools:
69
56
  value = getattr(self, variable_name)
70
57
  if not value:
71
58
  continue
72
59
  parts.append(variable_name.replace("_", "-"))
73
- for variable_name in numbers:
60
+ for variable_name in self.numbers:
74
61
  value = getattr(self, variable_name)
75
62
  if value is None:
76
63
  continue
@@ -78,53 +65,4 @@ class CacheControl(Base):
78
65
  parts.append(f"{key_name}={value}")
79
66
  if not parts:
80
67
  return
81
- input_output.set_header("cache-control", ", ".join(parts))
82
-
83
-
84
- # Use an explicity param list, even though long, so that Python can provide some input checking directly
85
- def cache_control(
86
- self,
87
- no_cache=None,
88
- no_store=None,
89
- no_transform=None,
90
- max_age=None,
91
- s_maxage=None,
92
- must_revalidate=None,
93
- proxy_revalidate=None,
94
- must_understand=None,
95
- private=None,
96
- public=None,
97
- immutable=None,
98
- stale_while_revalidate=None,
99
- stale_if_error=None,
100
- ):
101
- for variable_name in numbers:
102
- value = locals()[variable_name]
103
- if value is not None and type(value) != int:
104
- actual_type = type(value)
105
- raise ValueError(
106
- f"Invalid configuration value for cache control: {variable_name} should be an integer but instead is '{actual_type}'"
107
- )
108
- for variable_name in bools:
109
- value = locals()[variable_name]
110
- if value is not None and type(value) != bool:
111
- actual_type = type(value)
112
- raise ValueError(
113
- f"Invalid configuration value for cache control: {variable_name} should be True/False but instead is of type '{actual_type}'"
114
- )
115
- return BindingConfig(
116
- CacheControl,
117
- no_cache=no_cache,
118
- no_store=no_store,
119
- no_transform=no_transform,
120
- max_age=max_age,
121
- s_maxage=s_maxage,
122
- must_revalidate=must_revalidate,
123
- proxy_revalidate=proxy_revalidate,
124
- must_understand=must_understand,
125
- private=private,
126
- public=public,
127
- immutable=immutable,
128
- stale_while_revalidate=stale_while_revalidate,
129
- stale_if_error=stale_if_error,
130
- )
68
+ input_output.response_headers.add("cache-control", ", ".join(parts))
@@ -1,105 +1,51 @@
1
- from .base import Base
2
- from ..binding_config import BindingConfig
1
+ from __future__ import annotations
3
2
 
4
- lists = [
5
- "headers",
6
- "expose_headers",
7
- "methods",
8
- ]
3
+ from clearskies import configs, decorators
4
+ from clearskies.security_header import SecurityHeader
9
5
 
10
6
 
11
- class CORS(Base):
12
- origin = None
13
- methods = None
14
- headers = None
15
- max_age = None
16
- credentials = None
17
- expose_headers = None
7
+ class Cors(SecurityHeader):
8
+ origin = configs.String()
9
+ methods = configs.StringList(default=[])
10
+ headers = configs.StringList(default=[])
11
+ max_age = configs.Integer(default=5)
12
+ credentials = configs.Boolean(default=False)
13
+ expose_headers = configs.StringList(default=[])
18
14
  is_cors = True
19
15
 
20
- def __init__(self, environment):
21
- super().__init__(environment)
16
+ @decorators.parameters_to_properties
17
+ def __init__(
18
+ self,
19
+ credentials: bool = False,
20
+ expose_headers: list[str] = [],
21
+ headers: list[str] = [],
22
+ max_age: int = 5,
23
+ methods: list[str] = [],
24
+ origin: str = "",
25
+ ):
26
+ self.finalize_and_validate_configuration()
22
27
 
23
- def configure(self, origin=None, methods=None, headers=None, max_age=None, credentials=None, expose_headers=None):
24
- self.origin = origin
25
- self.max_age = max_age
26
- self.credentials = credentials
27
- self.expose_headers = ", ".join(expose_headers) if type(expose_headers) == list else expose_headers
28
- self.set_methods(methods)
29
- self.set_headers(headers)
28
+ def set_headers(self, headers: list[str]):
29
+ self.headers = headers
30
30
 
31
- def set_headers(self, headers):
32
- if type(headers) == list:
33
- headers = ", ".join(headers)
34
- self.headers = headers if headers is not None else ""
31
+ def add_header(self, header: str):
32
+ self.headers = [*self.headers, header]
35
33
 
36
- def add_header(self, header):
37
- if not self.headers:
38
- self.headers = header
39
- elif header not in self.headers:
40
- self.headers += ", " + header
34
+ def set_methods(self, methods: list[str]):
35
+ self.methods = methods
41
36
 
42
- def set_methods(self, methods):
43
- if type(methods) == list:
44
- methods = ", ".join(methods)
45
- self.methods = methods if methods is not None else ""
46
-
47
- def add_method(self, method):
48
- if not self.methods:
49
- self.methods = method
50
- elif method not in self.methods:
51
- self.methods += ", " + method
37
+ def add_method(self, method: str):
38
+ self.methods = [*self.methods, method]
52
39
 
53
40
  def set_headers_for_input_output(self, input_output):
54
- for key in ["origin", "methods", "headers"]:
55
- if not getattr(self, key):
41
+ for key in ["expose_headers", "methods", "headers"]:
42
+ value = getattr(self, key)
43
+ if not value:
56
44
  continue
57
- input_output.set_header(f"access-control-allow-{key}".replace("_", "-"), getattr(self, key))
45
+ input_output.response_headers.add(f"access-control-allow-{key}".replace("_", "-"), ", ".join(value))
58
46
  if self.credentials:
59
- input_output.set_header("access-control-allow-credentials", "true")
60
- for key in ["max_age", "expose_headers"]:
61
- if not getattr(self, key):
62
- continue
63
- input_output.set_header(f"access-control-{key}".replace("_", "-"), str(getattr(self, key)))
64
-
65
-
66
- def cors(origin=None, methods=None, headers=None, max_age=None, credentials=None, expose_headers=None):
67
- # I didn't auto-pull into kwargs so that the allowed values are clearly defined.
68
- # however, checking and processing will be easier with a dict
69
- kwargs = {
70
- "origin": origin,
71
- "methods": methods,
72
- "headers": headers,
73
- "max_age": max_age,
74
- "credentials": credentials,
75
- "expose_headers": expose_headers,
76
- }
77
- allowed_types = {
78
- "origin": str,
79
- "max_age": int,
80
- "credentials": bool,
81
- }
82
- for key in lists:
83
- value = kwargs[key]
84
- if value is None:
85
- continue
86
- actual_type = type(value)
87
- if actual_type == list:
88
- if not all([type(item) == str for item in value]):
89
- raise ValueError(
90
- f"Invalid configuration value for CORS: {key} should be a list of strings, but another kind of value was found"
91
- )
92
- kwargs[key] = ", ".join(value)
93
- elif actual_type != str:
94
- raise ValueError(
95
- f"Invalid configuration value for CORS: {key} should be a string or list of strings but instead is '{actual_type}'"
96
- )
97
- for key, allowed_type in allowed_types.items():
98
- if kwargs[key] is None:
99
- continue
100
- actual_type = type(kwargs[key])
101
- if actual_type != allowed_type:
102
- raise ValueError(
103
- f"Invalid configuration value for CORS: {key} should be a {allowed_type} but instead is '{actual_type}'"
104
- )
105
- return BindingConfig(CORS, **kwargs)
47
+ input_output.response_headers.add("access-control-allow-credentials", "true")
48
+ if self.max_age:
49
+ input_output.response_headers.add("access-control-max-age", str(self.max_age))
50
+ if self.origin:
51
+ input_output.response_headers.add("access-control-allow-origin", str(self.origin))
@@ -1,169 +1,95 @@
1
- from .base import Base
2
- from ..binding_config import BindingConfig
1
+ from __future__ import annotations
3
2
 
4
- directives = [
5
- "default_src",
6
- "script_src",
7
- "style_src",
8
- "img_src",
9
- "connect_src",
10
- "font_src",
11
- "object_src",
12
- "media_src",
13
- "frame_src",
14
- "sandbox",
15
- "report_uri",
16
- "child_src",
17
- "form_action",
18
- "frame_ancestors",
19
- "plugin_types",
20
- "base_uri",
21
- "report_to",
22
- "worker_src",
23
- "manifest_src",
24
- "prefetch_src",
25
- "navigate_to",
26
- ]
3
+ from clearskies import configs, decorators
4
+ from clearskies.security_header import SecurityHeader
27
5
 
28
6
 
29
- class CSP(Base):
7
+ class Csp(SecurityHeader):
30
8
  header_name = "content-security-policy"
31
- default_src = None
32
- script_src = None
33
- style_src = None
34
- img_src = None
35
- connect_src = None
36
- font_src = None
37
- object_src = None
38
- media_src = None
39
- frame_src = None
40
- sandbox = None
41
- report_uri = None
42
- child_src = None
43
- form_action = None
44
- frame_ancestors = None
45
- plugin_types = None
46
- base_uri = None
47
- report_to = None
48
- worker_src = None
49
- manifest_src = None
50
- prefetch_src = None
51
- navigate_to = None
9
+ default_src = configs.String()
10
+ script_src = configs.String()
11
+ style_src = configs.String()
12
+ img_src = configs.String()
13
+ connect_src = configs.String()
14
+ font_src = configs.String()
15
+ object_src = configs.String()
16
+ media_src = configs.String()
17
+ frame_src = configs.String()
18
+ sandbox = configs.String()
19
+ report_uri = configs.String()
20
+ child_src = configs.String()
21
+ form_action = configs.String()
22
+ frame_ancestors = configs.String()
23
+ plugin_types = configs.String()
24
+ base_uri = configs.String()
25
+ report_to = configs.String()
26
+ worker_src = configs.String()
27
+ manifest_src = configs.String()
28
+ prefetch_src = configs.String()
29
+ navigate_to = configs.String()
52
30
 
53
- def __init__(self, environment):
54
- super().__init__(environment)
31
+ directives = [
32
+ "default_src",
33
+ "script_src",
34
+ "style_src",
35
+ "img_src",
36
+ "connect_src",
37
+ "font_src",
38
+ "object_src",
39
+ "media_src",
40
+ "frame_src",
41
+ "sandbox",
42
+ "report_uri",
43
+ "child_src",
44
+ "form_action",
45
+ "frame_ancestors",
46
+ "plugin_types",
47
+ "base_uri",
48
+ "report_to",
49
+ "worker_src",
50
+ "manifest_src",
51
+ "prefetch_src",
52
+ "navigate_to",
53
+ ]
55
54
 
56
- def configure(
55
+ @decorators.parameters_to_properties
56
+ def __init__(
57
57
  self,
58
- default_src=None,
59
- script_src=None,
60
- style_src=None,
61
- img_src=None,
62
- connect_src=None,
63
- font_src=None,
64
- object_src=None,
65
- media_src=None,
66
- frame_src=None,
67
- sandbox=None,
68
- report_uri=None,
69
- child_src=None,
70
- form_action=None,
71
- frame_ancestors=None,
72
- plugin_types=None,
73
- base_uri=None,
74
- report_to=None,
75
- worker_src=None,
76
- manifest_src=None,
77
- prefetch_src=None,
78
- navigate_to=None,
58
+ default_src: str = "",
59
+ script_src: str = "",
60
+ style_src: str = "",
61
+ img_src: str = "",
62
+ connect_src: str = "",
63
+ font_src: str = "",
64
+ object_src: str = "",
65
+ media_src: str = "",
66
+ frame_src: str = "",
67
+ sandbox: str = "",
68
+ report_uri: str = "",
69
+ child_src: str = "",
70
+ form_action: str = "",
71
+ frame_ancestors: str = "",
72
+ plugin_types: str = "",
73
+ base_uri: str = "",
74
+ report_to: str = "",
75
+ worker_src: str = "",
76
+ manifest_src: str = "",
77
+ prefetch_src: str = "",
78
+ navigate_to: str = "",
79
79
  ):
80
- self.default_src = default_src
81
- self.script_src = script_src
82
- self.style_src = style_src
83
- self.img_src = img_src
84
- self.connect_src = connect_src
85
- self.font_src = font_src
86
- self.object_src = object_src
87
- self.media_src = media_src
88
- self.frame_src = frame_src
89
- self.sandbox = sandbox
90
- self.report_uri = report_uri
91
- self.child_src = child_src
92
- self.form_action = form_action
93
- self.frame_ancestors = frame_ancestors
94
- self.plugin_types = plugin_types
95
- self.base_uri = base_uri
96
- self.report_to = report_to
97
- self.worker_src = worker_src
98
- self.manifest_src = manifest_src
99
- self.prefetch_src = prefetch_src
100
- self.navigate_to = navigate_to
80
+ self.finalize_and_validate_configuration()
101
81
 
102
82
  def set_headers_for_input_output(self, input_output):
103
83
  parts = []
104
- for variable_name in directives:
84
+ for variable_name in self.directives:
105
85
  value = getattr(self, variable_name)
106
86
  if not value:
107
87
  continue
88
+ if value.lower().strip() == "self":
89
+ value = "'self'"
108
90
  header_key_name = variable_name.replace("_", "-")
109
91
  parts.append(f"{header_key_name} {value}")
110
92
  if not parts:
111
93
  return
112
94
  header_value = "; ".join(parts)
113
- input_output.set_header(self.header_name, header_value)
114
-
115
-
116
- def csp(
117
- default_src=None,
118
- script_src=None,
119
- style_src=None,
120
- img_src=None,
121
- connect_src=None,
122
- font_src=None,
123
- object_src=None,
124
- media_src=None,
125
- frame_src=None,
126
- sandbox=None,
127
- report_uri=None,
128
- child_src=None,
129
- form_action=None,
130
- frame_ancestors=None,
131
- plugin_types=None,
132
- base_uri=None,
133
- report_to=None,
134
- worker_src=None,
135
- manifest_src=None,
136
- prefetch_src=None,
137
- navigate_to=None,
138
- ):
139
- for variable_name in directives:
140
- value = locals()[variable_name]
141
- if value is not None and type(value) != str:
142
- actual_type = type(value)
143
- raise ValueError(
144
- f"Invalid configuration value for CSP: {variable_name} should be a string but instead is '{actual_type}'"
145
- )
146
- return BindingConfig(
147
- CSP,
148
- default_src=default_src,
149
- script_src=script_src,
150
- style_src=style_src,
151
- img_src=img_src,
152
- connect_src=connect_src,
153
- font_src=font_src,
154
- object_src=object_src,
155
- media_src=media_src,
156
- frame_src=frame_src,
157
- sandbox=sandbox,
158
- report_uri=report_uri,
159
- child_src=child_src,
160
- form_action=form_action,
161
- frame_ancestors=frame_ancestors,
162
- plugin_types=plugin_types,
163
- base_uri=base_uri,
164
- report_to=report_to,
165
- worker_src=worker_src,
166
- manifest_src=manifest_src,
167
- prefetch_src=prefetch_src,
168
- navigate_to=navigate_to,
169
- )
95
+ input_output.response_headers.add(self.header_name, header_value)