@kattebak/openapi-generator-ts 1.0.0

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 (290) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +189 -0
  3. package/dist/cli/commands/generate.d.ts +23 -0
  4. package/dist/cli/commands/generate.d.ts.map +1 -0
  5. package/dist/cli/commands/generate.js +116 -0
  6. package/dist/cli/commands/generate.js.map +1 -0
  7. package/dist/cli/commands/list.d.ts +2 -0
  8. package/dist/cli/commands/list.d.ts.map +1 -0
  9. package/dist/cli/commands/list.js +26 -0
  10. package/dist/cli/commands/list.js.map +1 -0
  11. package/dist/cli/commands/validate.d.ts +6 -0
  12. package/dist/cli/commands/validate.d.ts.map +1 -0
  13. package/dist/cli/commands/validate.js +80 -0
  14. package/dist/cli/commands/validate.js.map +1 -0
  15. package/dist/cli/convert-template.d.ts +31 -0
  16. package/dist/cli/convert-template.d.ts.map +1 -0
  17. package/dist/cli/convert-template.js +204 -0
  18. package/dist/cli/convert-template.js.map +1 -0
  19. package/dist/cli/convert-template.test.d.ts +2 -0
  20. package/dist/cli/convert-template.test.d.ts.map +1 -0
  21. package/dist/cli/convert-template.test.js +74 -0
  22. package/dist/cli/convert-template.test.js.map +1 -0
  23. package/dist/cli/index.d.ts +3 -0
  24. package/dist/cli/index.d.ts.map +1 -0
  25. package/dist/cli/index.js +246 -0
  26. package/dist/cli/index.js.map +1 -0
  27. package/dist/core/config.d.ts +252 -0
  28. package/dist/core/config.d.ts.map +1 -0
  29. package/dist/core/config.js +31 -0
  30. package/dist/core/config.js.map +1 -0
  31. package/dist/core/generator.d.ts +99 -0
  32. package/dist/core/generator.d.ts.map +1 -0
  33. package/dist/core/generator.js +492 -0
  34. package/dist/core/generator.js.map +1 -0
  35. package/dist/core/index.d.ts +4 -0
  36. package/dist/core/index.d.ts.map +1 -0
  37. package/dist/core/index.js +4 -0
  38. package/dist/core/index.js.map +1 -0
  39. package/dist/core/types.d.ts +47 -0
  40. package/dist/core/types.d.ts.map +1 -0
  41. package/dist/core/types.js +5 -0
  42. package/dist/core/types.js.map +1 -0
  43. package/dist/generators/generators.test.d.ts +6 -0
  44. package/dist/generators/generators.test.d.ts.map +1 -0
  45. package/dist/generators/generators.test.js +247 -0
  46. package/dist/generators/generators.test.js.map +1 -0
  47. package/dist/generators/go.d.ts +10 -0
  48. package/dist/generators/go.d.ts.map +1 -0
  49. package/dist/generators/go.js +327 -0
  50. package/dist/generators/go.js.map +1 -0
  51. package/dist/generators/index.d.ts +29 -0
  52. package/dist/generators/index.d.ts.map +1 -0
  53. package/dist/generators/index.js +58 -0
  54. package/dist/generators/index.js.map +1 -0
  55. package/dist/generators/php.d.ts +14 -0
  56. package/dist/generators/php.d.ts.map +1 -0
  57. package/dist/generators/php.js +241 -0
  58. package/dist/generators/php.js.map +1 -0
  59. package/dist/generators/python.d.ts +10 -0
  60. package/dist/generators/python.d.ts.map +1 -0
  61. package/dist/generators/python.js +273 -0
  62. package/dist/generators/python.js.map +1 -0
  63. package/dist/generators/typescript-fetch.d.ts +14 -0
  64. package/dist/generators/typescript-fetch.d.ts.map +1 -0
  65. package/dist/generators/typescript-fetch.js +217 -0
  66. package/dist/generators/typescript-fetch.js.map +1 -0
  67. package/dist/index.d.ts +14 -0
  68. package/dist/index.d.ts.map +1 -0
  69. package/dist/index.js +16 -0
  70. package/dist/index.js.map +1 -0
  71. package/dist/models/codegen-model.d.ts +123 -0
  72. package/dist/models/codegen-model.d.ts.map +1 -0
  73. package/dist/models/codegen-model.js +64 -0
  74. package/dist/models/codegen-model.js.map +1 -0
  75. package/dist/models/codegen-model.test.d.ts +6 -0
  76. package/dist/models/codegen-model.test.d.ts.map +1 -0
  77. package/dist/models/codegen-model.test.js +143 -0
  78. package/dist/models/codegen-model.test.js.map +1 -0
  79. package/dist/models/codegen-operation.d.ts +95 -0
  80. package/dist/models/codegen-operation.d.ts.map +1 -0
  81. package/dist/models/codegen-operation.js +54 -0
  82. package/dist/models/codegen-operation.js.map +1 -0
  83. package/dist/models/codegen-parameter.d.ts +86 -0
  84. package/dist/models/codegen-parameter.d.ts.map +1 -0
  85. package/dist/models/codegen-parameter.js +47 -0
  86. package/dist/models/codegen-parameter.js.map +1 -0
  87. package/dist/models/codegen-property.d.ts +95 -0
  88. package/dist/models/codegen-property.d.ts.map +1 -0
  89. package/dist/models/codegen-property.js +47 -0
  90. package/dist/models/codegen-property.js.map +1 -0
  91. package/dist/models/codegen-response.d.ts +64 -0
  92. package/dist/models/codegen-response.d.ts.map +1 -0
  93. package/dist/models/codegen-response.js +42 -0
  94. package/dist/models/codegen-response.js.map +1 -0
  95. package/dist/models/codegen-security.d.ts +34 -0
  96. package/dist/models/codegen-security.d.ts.map +1 -0
  97. package/dist/models/codegen-security.js +18 -0
  98. package/dist/models/codegen-security.js.map +1 -0
  99. package/dist/models/index.d.ts +8 -0
  100. package/dist/models/index.d.ts.map +1 -0
  101. package/dist/models/index.js +7 -0
  102. package/dist/models/index.js.map +1 -0
  103. package/dist/parser/index.d.ts +4 -0
  104. package/dist/parser/index.d.ts.map +1 -0
  105. package/dist/parser/index.js +4 -0
  106. package/dist/parser/index.js.map +1 -0
  107. package/dist/parser/openapi-parser.d.ts +71 -0
  108. package/dist/parser/openapi-parser.d.ts.map +1 -0
  109. package/dist/parser/openapi-parser.js +195 -0
  110. package/dist/parser/openapi-parser.js.map +1 -0
  111. package/dist/parser/openapi-parser.test.d.ts +6 -0
  112. package/dist/parser/openapi-parser.test.d.ts.map +1 -0
  113. package/dist/parser/openapi-parser.test.js +238 -0
  114. package/dist/parser/openapi-parser.test.js.map +1 -0
  115. package/dist/parser/operation-transformer.d.ts +95 -0
  116. package/dist/parser/operation-transformer.d.ts.map +1 -0
  117. package/dist/parser/operation-transformer.js +634 -0
  118. package/dist/parser/operation-transformer.js.map +1 -0
  119. package/dist/parser/schema-transformer.d.ts +134 -0
  120. package/dist/parser/schema-transformer.d.ts.map +1 -0
  121. package/dist/parser/schema-transformer.js +557 -0
  122. package/dist/parser/schema-transformer.js.map +1 -0
  123. package/dist/template/engine-adapter.d.ts +55 -0
  124. package/dist/template/engine-adapter.d.ts.map +1 -0
  125. package/dist/template/engine-adapter.js +178 -0
  126. package/dist/template/engine-adapter.js.map +1 -0
  127. package/dist/template/engine-adapter.test.d.ts +6 -0
  128. package/dist/template/engine-adapter.test.d.ts.map +1 -0
  129. package/dist/template/engine-adapter.test.js +186 -0
  130. package/dist/template/engine-adapter.test.js.map +1 -0
  131. package/dist/template/index.d.ts +5 -0
  132. package/dist/template/index.d.ts.map +1 -0
  133. package/dist/template/index.js +5 -0
  134. package/dist/template/index.js.map +1 -0
  135. package/dist/template/lambdas/indent-lambdas.d.ts +40 -0
  136. package/dist/template/lambdas/indent-lambdas.d.ts.map +1 -0
  137. package/dist/template/lambdas/indent-lambdas.js +98 -0
  138. package/dist/template/lambdas/indent-lambdas.js.map +1 -0
  139. package/dist/template/lambdas/index.d.ts +44 -0
  140. package/dist/template/lambdas/index.d.ts.map +1 -0
  141. package/dist/template/lambdas/index.js +79 -0
  142. package/dist/template/lambdas/index.js.map +1 -0
  143. package/dist/template/lambdas/string-lambdas.d.ts +78 -0
  144. package/dist/template/lambdas/string-lambdas.d.ts.map +1 -0
  145. package/dist/template/lambdas/string-lambdas.js +148 -0
  146. package/dist/template/lambdas/string-lambdas.js.map +1 -0
  147. package/dist/template/lambdas/string-lambdas.test.d.ts +6 -0
  148. package/dist/template/lambdas/string-lambdas.test.d.ts.map +1 -0
  149. package/dist/template/lambdas/string-lambdas.test.js +158 -0
  150. package/dist/template/lambdas/string-lambdas.test.js.map +1 -0
  151. package/dist/template/template-locator.d.ts +72 -0
  152. package/dist/template/template-locator.d.ts.map +1 -0
  153. package/dist/template/template-locator.js +173 -0
  154. package/dist/template/template-locator.js.map +1 -0
  155. package/dist/template/template-manager.d.ts +65 -0
  156. package/dist/template/template-manager.d.ts.map +1 -0
  157. package/dist/template/template-manager.js +185 -0
  158. package/dist/template/template-manager.js.map +1 -0
  159. package/package.json +67 -0
  160. package/templates/go/.travis.yml +8 -0
  161. package/templates/go/README.mustache +236 -0
  162. package/templates/go/api.mustache +452 -0
  163. package/templates/go/api_doc.mustache +92 -0
  164. package/templates/go/api_test.mustache +59 -0
  165. package/templates/go/client.mustache +761 -0
  166. package/templates/go/configuration.mustache +332 -0
  167. package/templates/go/git_push.sh.mustache +57 -0
  168. package/templates/go/gitignore.mustache +24 -0
  169. package/templates/go/go.mod.mustache +16 -0
  170. package/templates/go/go.sum.mustache +19 -0
  171. package/templates/go/model.mustache +21 -0
  172. package/templates/go/model_anyof.mustache +93 -0
  173. package/templates/go/model_doc.mustache +97 -0
  174. package/templates/go/model_enum.mustache +101 -0
  175. package/templates/go/model_oneof.mustache +160 -0
  176. package/templates/go/model_simple.mustache +572 -0
  177. package/templates/go/nullable_model.mustache +35 -0
  178. package/templates/go/openapi.mustache +1 -0
  179. package/templates/go/partial_header.mustache +18 -0
  180. package/templates/go/response.mustache +38 -0
  181. package/templates/go/signing.mustache +453 -0
  182. package/templates/go/utils.mustache +352 -0
  183. package/templates/php/.php-cs-fixer.dist.php +29 -0
  184. package/templates/php/.travis.yml +8 -0
  185. package/templates/php/ApiException.mustache +111 -0
  186. package/templates/php/Configuration.mustache +606 -0
  187. package/templates/php/FormDataProcessor.mustache +238 -0
  188. package/templates/php/HeaderSelector.mustache +265 -0
  189. package/templates/php/ModelInterface.mustache +103 -0
  190. package/templates/php/ObjectSerializer.mustache +591 -0
  191. package/templates/php/README.mustache +151 -0
  192. package/templates/php/api.mustache +891 -0
  193. package/templates/php/api_doc.mustache +105 -0
  194. package/templates/php/api_test.mustache +80 -0
  195. package/templates/php/composer.mustache +44 -0
  196. package/templates/php/git_push.sh.mustache +57 -0
  197. package/templates/php/gitignore +15 -0
  198. package/templates/php/libraries/psr-18/ApiException.mustache +114 -0
  199. package/templates/php/libraries/psr-18/DebugPlugin.mustache +93 -0
  200. package/templates/php/libraries/psr-18/README.mustache +161 -0
  201. package/templates/php/libraries/psr-18/api.mustache +833 -0
  202. package/templates/php/libraries/psr-18/api_doc.mustache +79 -0
  203. package/templates/php/libraries/psr-18/composer.mustache +56 -0
  204. package/templates/php/model.mustache +47 -0
  205. package/templates/php/model_doc.mustache +10 -0
  206. package/templates/php/model_enum.mustache +33 -0
  207. package/templates/php/model_generic.mustache +565 -0
  208. package/templates/php/model_test.mustache +88 -0
  209. package/templates/php/partial_header.mustache +18 -0
  210. package/templates/php/php_doc_auth_partial.mustache +23 -0
  211. package/templates/php/phpunit.xml.mustache +18 -0
  212. package/templates/python/README.mustache +60 -0
  213. package/templates/python/README_onlypackage.mustache +50 -0
  214. package/templates/python/__init__.mustache +1 -0
  215. package/templates/python/__init__api.mustache +19 -0
  216. package/templates/python/__init__model.mustache +22 -0
  217. package/templates/python/__init__package.mustache +49 -0
  218. package/templates/python/api.mustache +244 -0
  219. package/templates/python/api_client.mustache +822 -0
  220. package/templates/python/api_doc.mustache +81 -0
  221. package/templates/python/api_doc_example.mustache +38 -0
  222. package/templates/python/api_response.mustache +21 -0
  223. package/templates/python/api_test.mustache +48 -0
  224. package/templates/python/asyncio/rest.mustache +209 -0
  225. package/templates/python/common_README.mustache +85 -0
  226. package/templates/python/configuration.mustache +806 -0
  227. package/templates/python/exceptions.mustache +210 -0
  228. package/templates/python/exports_api.mustache +3 -0
  229. package/templates/python/exports_model.mustache +3 -0
  230. package/templates/python/exports_package.mustache +20 -0
  231. package/templates/python/git_push.sh.mustache +57 -0
  232. package/templates/python/github-workflow.mustache +35 -0
  233. package/templates/python/gitignore.mustache +66 -0
  234. package/templates/python/gitlab-ci.mustache +31 -0
  235. package/templates/python/httpx/rest.mustache +190 -0
  236. package/templates/python/model.mustache +16 -0
  237. package/templates/python/model_anyof.mustache +182 -0
  238. package/templates/python/model_doc.mustache +40 -0
  239. package/templates/python/model_enum.mustache +36 -0
  240. package/templates/python/model_generic.mustache +403 -0
  241. package/templates/python/model_oneof.mustache +209 -0
  242. package/templates/python/model_test.mustache +60 -0
  243. package/templates/python/partial_api.mustache +52 -0
  244. package/templates/python/partial_api_args.mustache +18 -0
  245. package/templates/python/partial_header.mustache +19 -0
  246. package/templates/python/py.typed.mustache +1 -0
  247. package/templates/python/pyproject.mustache +172 -0
  248. package/templates/python/python_doc_auth_partial.mustache +108 -0
  249. package/templates/python/requirements.mustache +23 -0
  250. package/templates/python/rest.mustache +254 -0
  251. package/templates/python/setup.mustache +60 -0
  252. package/templates/python/setup_cfg.mustache +2 -0
  253. package/templates/python/signing.mustache +422 -0
  254. package/templates/python/test-requirements.mustache +6 -0
  255. package/templates/python/tornado/rest.mustache +148 -0
  256. package/templates/python/tox.mustache +9 -0
  257. package/templates/python/travis.mustache +17 -0
  258. package/templates/typescript-fetch/ApiEntitiesRecord.mustache +26 -0
  259. package/templates/typescript-fetch/ApiEntitiesReducer.mustache +21 -0
  260. package/templates/typescript-fetch/ApiEntitiesSelectors.mustache +5 -0
  261. package/templates/typescript-fetch/README.mustache +127 -0
  262. package/templates/typescript-fetch/allSagas.mustache +19 -0
  263. package/templates/typescript-fetch/api_doc.mustache +63 -0
  264. package/templates/typescript-fetch/api_example.mustache +44 -0
  265. package/templates/typescript-fetch/apis.index.mustache +16 -0
  266. package/templates/typescript-fetch/apis.mustache +483 -0
  267. package/templates/typescript-fetch/apisAssignQueryParam.mustache +12 -0
  268. package/templates/typescript-fetch/gitignore +4 -0
  269. package/templates/typescript-fetch/index.mustache +17 -0
  270. package/templates/typescript-fetch/licenseInfo.mustache +11 -0
  271. package/templates/typescript-fetch/modelEnum.mustache +28 -0
  272. package/templates/typescript-fetch/modelEnumInterfaces.mustache +37 -0
  273. package/templates/typescript-fetch/modelGeneric.mustache +261 -0
  274. package/templates/typescript-fetch/modelGenericInterfaces.mustache +50 -0
  275. package/templates/typescript-fetch/modelOneOf.mustache +255 -0
  276. package/templates/typescript-fetch/modelOneOfInterfaces.mustache +6 -0
  277. package/templates/typescript-fetch/model_doc.mustache +41 -0
  278. package/templates/typescript-fetch/models.index.mustache +32 -0
  279. package/templates/typescript-fetch/models.mustache +24 -0
  280. package/templates/typescript-fetch/npmignore.mustache +1 -0
  281. package/templates/typescript-fetch/package.mustache +43 -0
  282. package/templates/typescript-fetch/recordGeneric.mustache +295 -0
  283. package/templates/typescript-fetch/records.mustache +17 -0
  284. package/templates/typescript-fetch/runtime.mustache +424 -0
  285. package/templates/typescript-fetch/runtimeSagasAndRecords.mustache +120 -0
  286. package/templates/typescript-fetch/sagaApiManager.mustache +28 -0
  287. package/templates/typescript-fetch/sagas.mustache +245 -0
  288. package/templates/typescript-fetch/sourceLibraryIndex.mustache +1 -0
  289. package/templates/typescript-fetch/tsconfig.esm.mustache +7 -0
  290. package/templates/typescript-fetch/tsconfig.mustache +25 -0
@@ -0,0 +1,422 @@
1
+ {{>partial_header}}
2
+
3
+ from base64 import b64encode
4
+ from Crypto.IO import PEM, PKCS8
5
+ from Crypto.Hash import SHA256, SHA512
6
+ from Crypto.Hash.SHA512 import SHA512Hash
7
+ from Crypto.Hash.SHA256 import SHA256Hash
8
+ from Crypto.PublicKey import RSA, ECC
9
+ from Crypto.Signature import PKCS1_v1_5, pss, DSS
10
+ from datetime import timedelta
11
+ from email.utils import formatdate
12
+ import os
13
+ import re
14
+ from time import time
15
+ from typing import List, Optional, Union
16
+ from urllib.parse import urlencode, urlparse
17
+
18
+ # The constants below define a subset of HTTP headers that can be included in the
19
+ # HTTP signature scheme. Additional headers may be included in the signature.
20
+
21
+ # The '(request-target)' header is a calculated field that includes the HTTP verb,
22
+ # the URL path and the URL query.
23
+ HEADER_REQUEST_TARGET = '(request-target)'
24
+ # The time when the HTTP signature was generated.
25
+ HEADER_CREATED = '(created)'
26
+ # The time when the HTTP signature expires. The API server should reject HTTP requests
27
+ # that have expired.
28
+ HEADER_EXPIRES = '(expires)'
29
+ # The 'Host' header.
30
+ HEADER_HOST = 'Host'
31
+ # The 'Date' header.
32
+ HEADER_DATE = 'Date'
33
+ # When the 'Digest' header is included in the HTTP signature, the client automatically
34
+ # computes the digest of the HTTP request body, per RFC 3230.
35
+ HEADER_DIGEST = 'Digest'
36
+ # The 'Authorization' header is automatically generated by the client. It includes
37
+ # the list of signed headers and a base64-encoded signature.
38
+ HEADER_AUTHORIZATION = 'Authorization'
39
+
40
+ # The constants below define the cryptographic schemes for the HTTP signature scheme.
41
+ SCHEME_HS2019 = 'hs2019'
42
+ SCHEME_RSA_SHA256 = 'rsa-sha256'
43
+ SCHEME_RSA_SHA512 = 'rsa-sha512'
44
+
45
+ # The constants below define the signature algorithms that can be used for the HTTP
46
+ # signature scheme.
47
+ ALGORITHM_RSASSA_PSS = 'RSASSA-PSS'
48
+ ALGORITHM_RSASSA_PKCS1v15 = 'RSASSA-PKCS1-v1_5'
49
+
50
+ ALGORITHM_ECDSA_MODE_FIPS_186_3 = 'fips-186-3'
51
+ ALGORITHM_ECDSA_MODE_DETERMINISTIC_RFC6979 = 'deterministic-rfc6979'
52
+ ALGORITHM_ECDSA_KEY_SIGNING_ALGORITHMS = {
53
+ ALGORITHM_ECDSA_MODE_FIPS_186_3,
54
+ ALGORITHM_ECDSA_MODE_DETERMINISTIC_RFC6979
55
+ }
56
+
57
+ # The cryptographic hash algorithm for the message signature.
58
+ HASH_SHA256 = 'sha256'
59
+ HASH_SHA512 = 'sha512'
60
+
61
+
62
+ class HttpSigningConfiguration:
63
+ """The configuration parameters for the HTTP signature security scheme.
64
+
65
+ The HTTP signature security scheme is used to sign HTTP requests with a private key
66
+ which is in possession of the API client.
67
+
68
+ An ``Authorization`` header is calculated by creating a hash of select headers,
69
+ and optionally the body of the HTTP request, then signing the hash value using
70
+ a private key. The ``Authorization`` header is added to outbound HTTP requests.
71
+
72
+ :param key_id: A string value specifying the identifier of the cryptographic key,
73
+ when signing HTTP requests.
74
+ :param signing_scheme: A string value specifying the signature scheme, when
75
+ signing HTTP requests.
76
+ Supported value are: ``hs2019``, ``rsa-sha256``, ``rsa-sha512``.
77
+ Avoid using ``rsa-sha256``, ``rsa-sha512`` as they are deprecated. These values are
78
+ available for server-side applications that only support the older
79
+ HTTP signature algorithms.
80
+ :param private_key_path: A string value specifying the path of the file containing
81
+ a private key. The private key is used to sign HTTP requests.
82
+ :param private_key_passphrase: A string value specifying the passphrase to decrypt
83
+ the private key.
84
+ :param signed_headers: A list of strings. Each value is the name of a HTTP header
85
+ that must be included in the HTTP signature calculation.
86
+ The two special signature headers ``(request-target)`` and ``(created)`` SHOULD be
87
+ included in SignedHeaders.
88
+ The ``(created)`` header expresses when the signature was created.
89
+ The ``(request-target)`` header is a concatenation of the lowercased :method, an
90
+ ASCII space, and the :path pseudo-headers.
91
+ When signed_headers is not specified, the client defaults to a single value,
92
+ ``(created)``, in the list of HTTP headers.
93
+ When SignedHeaders contains the 'Digest' value, the client performs the
94
+ following operations:
95
+ 1. Calculate a digest of request body, as specified in `RFC3230,
96
+ section 4.[3].2<https://datatracker.ietf.org/doc/html/rfc3230#section-4.[3].2>`_.
97
+ 2. Set the ``Digest`` header in the request body.
98
+ 3. Include the ``Digest`` header and value in the HTTP signature.
99
+ :param signing_algorithm: A string value specifying the signature algorithm, when
100
+ signing HTTP requests.
101
+ Supported values are:
102
+ 1. For RSA keys: RSASSA-PSS, RSASSA-PKCS1-v1_5.
103
+ 2. For ECDSA keys: fips-186-3, deterministic-rfc6979.
104
+ If None, the signing algorithm is inferred from the private key.
105
+ The default signing algorithm for RSA keys is RSASSA-PSS.
106
+ The default signing algorithm for ECDSA keys is fips-186-3.
107
+ :param hash_algorithm: The hash algorithm for the signature. Supported values are
108
+ sha256 and sha512.
109
+ If the signing_scheme is rsa-sha256, the hash algorithm must be set
110
+ to None or sha256.
111
+ If the signing_scheme is rsa-sha512, the hash algorithm must be set
112
+ to None or sha512.
113
+ :param signature_max_validity: The signature max validity, expressed as
114
+ a datetime.timedelta value. It must be a positive value.
115
+ """
116
+ def __init__(self,
117
+ key_id: str,
118
+ signing_scheme: str,
119
+ private_key_path: str,
120
+ private_key_passphrase: Union[None, str]=None,
121
+ signed_headers: Optional[List[str]]=None,
122
+ signing_algorithm: Optional[str]=None,
123
+ hash_algorithm: Optional[str]=None,
124
+ signature_max_validity: Optional[timedelta]=None,
125
+ ) -> None:
126
+ self.key_id = key_id
127
+ if signing_scheme not in {SCHEME_HS2019, SCHEME_RSA_SHA256, SCHEME_RSA_SHA512}:
128
+ raise Exception("Unsupported security scheme: {0}".format(signing_scheme))
129
+ self.signing_scheme = signing_scheme
130
+ if not os.path.exists(private_key_path):
131
+ raise Exception("Private key file does not exist")
132
+ self.private_key_path = private_key_path
133
+ self.private_key_passphrase = private_key_passphrase
134
+ self.signing_algorithm = signing_algorithm
135
+ self.hash_algorithm = hash_algorithm
136
+ if signing_scheme == SCHEME_RSA_SHA256:
137
+ if self.hash_algorithm is None:
138
+ self.hash_algorithm = HASH_SHA256
139
+ elif self.hash_algorithm != HASH_SHA256:
140
+ raise Exception("Hash algorithm must be sha256 when security scheme is %s" %
141
+ SCHEME_RSA_SHA256)
142
+ elif signing_scheme == SCHEME_RSA_SHA512:
143
+ if self.hash_algorithm is None:
144
+ self.hash_algorithm = HASH_SHA512
145
+ elif self.hash_algorithm != HASH_SHA512:
146
+ raise Exception("Hash algorithm must be sha512 when security scheme is %s" %
147
+ SCHEME_RSA_SHA512)
148
+ elif signing_scheme == SCHEME_HS2019:
149
+ if self.hash_algorithm is None:
150
+ self.hash_algorithm = HASH_SHA256
151
+ elif self.hash_algorithm not in {HASH_SHA256, HASH_SHA512}:
152
+ raise Exception("Invalid hash algorithm")
153
+ if signature_max_validity is not None and signature_max_validity.total_seconds() < 0:
154
+ raise Exception("The signature max validity must be a positive value")
155
+ self.signature_max_validity = signature_max_validity
156
+ # If the user has not provided any signed_headers, the default must be set to '(created)',
157
+ # as specified in the 'HTTP signature' standard.
158
+ if signed_headers is None or len(signed_headers) == 0:
159
+ signed_headers = [HEADER_CREATED]
160
+ if self.signature_max_validity is None and HEADER_EXPIRES in signed_headers:
161
+ raise Exception(
162
+ "Signature max validity must be set when "
163
+ "'(expires)' signature parameter is specified")
164
+ if len(signed_headers) != len(set(signed_headers)):
165
+ raise Exception("Cannot have duplicates in the signed_headers parameter")
166
+ if HEADER_AUTHORIZATION in signed_headers:
167
+ raise Exception("'Authorization' header cannot be included in signed headers")
168
+ self.signed_headers = signed_headers
169
+ self.private_key: Optional[Union[ECC.EccKey, RSA.RsaKey]] = None
170
+ """The private key used to sign HTTP requests.
171
+ Initialized when the PEM-encoded private key is loaded from a file.
172
+ """
173
+ self.host: Optional[str] = None
174
+ """The host name, optionally followed by a colon and TCP port number.
175
+ """
176
+ self._load_private_key()
177
+
178
+ def get_http_signature_headers(self, resource_path, method, headers, body, query_params):
179
+ """Create a cryptographic message signature for the HTTP request and add the signed headers.
180
+
181
+ :param resource_path : A string representation of the HTTP request resource path.
182
+ :param method: A string representation of the HTTP request method, e.g. GET, POST.
183
+ :param headers: A dict containing the HTTP request headers.
184
+ :param body: The object representing the HTTP request body.
185
+ :param query_params: A string representing the HTTP request query parameters.
186
+ :return: A dict of HTTP headers that must be added to the outbound HTTP request.
187
+ """
188
+ if method is None:
189
+ raise Exception("HTTP method must be set")
190
+ if resource_path is None:
191
+ raise Exception("Resource path must be set")
192
+
193
+ signed_headers_list, request_headers_dict = self._get_signed_header_info(
194
+ resource_path, method, headers, body, query_params)
195
+
196
+ header_items = [
197
+ "{0}: {1}".format(key.lower(), value) for key, value in signed_headers_list]
198
+ string_to_sign = "\n".join(header_items)
199
+
200
+ digest, digest_prefix = self._get_message_digest(string_to_sign.encode())
201
+ b64_signed_msg = self._sign_digest(digest)
202
+
203
+ request_headers_dict[HEADER_AUTHORIZATION] = self._get_authorization_header(
204
+ signed_headers_list, b64_signed_msg)
205
+
206
+ return request_headers_dict
207
+
208
+ def get_public_key(self):
209
+ """Returns the public key object associated with the private key.
210
+ """
211
+ pubkey: Optional[Union[ECC.EccKey, RSA.RsaKey]] = None
212
+ if isinstance(self.private_key, RSA.RsaKey):
213
+ pubkey = self.private_key.publickey()
214
+ elif isinstance(self.private_key, ECC.EccKey):
215
+ pubkey = self.private_key.public_key()
216
+ return pubkey
217
+
218
+ def _load_private_key(self):
219
+ """Load the private key used to sign HTTP requests.
220
+ The private key is used to sign HTTP requests as defined in
221
+ https://datatracker.ietf.org/doc/draft-cavage-http-signatures/.
222
+ """
223
+ if self.private_key is not None:
224
+ return
225
+ with open(self.private_key_path, 'r') as f:
226
+ pem_data = f.read()
227
+ # Verify PEM Pre-Encapsulation Boundary
228
+ r = re.compile(r"\s*-----BEGIN (.*)-----\s+")
229
+ m = r.match(pem_data)
230
+ if not m:
231
+ raise ValueError("Not a valid PEM pre boundary")
232
+ pem_header = m.group(1)
233
+ if pem_header == 'RSA PRIVATE KEY':
234
+ self.private_key = RSA.importKey(pem_data, self.private_key_passphrase)
235
+ elif pem_header == 'EC PRIVATE KEY':
236
+ self.private_key = ECC.import_key(pem_data, self.private_key_passphrase)
237
+ elif pem_header in {'PRIVATE KEY', 'ENCRYPTED PRIVATE KEY'}:
238
+ # Key is in PKCS8 format, which is capable of holding many different
239
+ # types of private keys, not just EC keys.
240
+ if self.private_key_passphrase is not None:
241
+ passphrase = self.private_key_passphrase.encode("utf-8")
242
+ else:
243
+ passphrase = None
244
+ (key_binary, pem_header, is_encrypted) = PEM.decode(pem_data, passphrase)
245
+ (oid, privkey, params) = \
246
+ PKCS8.unwrap(key_binary, passphrase=self.private_key_passphrase)
247
+ if oid == '1.[2].840.[10045].2.1':
248
+ self.private_key = ECC.import_key(pem_data, self.private_key_passphrase)
249
+ else:
250
+ raise Exception("Unsupported key: {0}. OID: {1}".format(pem_header, oid))
251
+ else:
252
+ raise Exception("Unsupported key: {0}".format(pem_header))
253
+ # Validate the specified signature algorithm is compatible with the private key.
254
+ if self.signing_algorithm is not None:
255
+ supported_algs = None
256
+ if isinstance(self.private_key, RSA.RsaKey):
257
+ supported_algs = {ALGORITHM_RSASSA_PSS, ALGORITHM_RSASSA_PKCS1v15}
258
+ elif isinstance(self.private_key, ECC.EccKey):
259
+ supported_algs = ALGORITHM_ECDSA_KEY_SIGNING_ALGORITHMS
260
+ if supported_algs is not None and self.signing_algorithm not in supported_algs:
261
+ raise Exception(
262
+ "Signing algorithm {0} is not compatible with private key".format(
263
+ self.signing_algorithm))
264
+
265
+ def _get_signed_header_info(self, resource_path, method, headers, body, query_params):
266
+ """Build the HTTP headers (name, value) that need to be included in
267
+ the HTTP signature scheme.
268
+
269
+ :param resource_path : A string representation of the HTTP request resource path.
270
+ :param method: A string representation of the HTTP request method, e.g. GET, POST.
271
+ :param headers: A dict containing the HTTP request headers.
272
+ :param body: The object (e.g. a dict) representing the HTTP request body.
273
+ :param query_params: A string representing the HTTP request query parameters.
274
+ :return: A tuple containing two dict objects:
275
+ The first dict contains the HTTP headers that are used to calculate
276
+ the HTTP signature.
277
+ The second dict contains the HTTP headers that must be added to
278
+ the outbound HTTP request.
279
+ """
280
+
281
+ if body is None:
282
+ body = ''
283
+ else:
284
+ body = body.to_json()
285
+
286
+ # Build the '(request-target)' HTTP signature parameter.
287
+ target_host = urlparse(self.host).netloc
288
+ target_path = urlparse(self.host).path
289
+ request_target = method.lower() + " " + target_path + resource_path
290
+ if query_params:
291
+ request_target += "?" + urlencode(query_params)
292
+
293
+ # Get UNIX time, e.g. seconds since epoch, not including leap seconds.
294
+ now = time()
295
+ # Format date per RFC 7231 section-7.[1].1.[2]. An example is:
296
+ # Date: Wed, 21 Oct 2015 07:28:00 GMT
297
+ cdate = formatdate(timeval=now, localtime=False, usegmt=True)
298
+ # The '(created)' value MUST be a Unix timestamp integer value.
299
+ # Subsecond precision is not supported.
300
+ created = int(now)
301
+ if self.signature_max_validity is not None:
302
+ expires = now + self.signature_max_validity.total_seconds()
303
+
304
+ signed_headers_list = []
305
+ request_headers_dict = {}
306
+ for hdr_key in self.signed_headers:
307
+ hdr_key = hdr_key.lower()
308
+ if hdr_key == HEADER_REQUEST_TARGET:
309
+ value = request_target
310
+ elif hdr_key == HEADER_CREATED:
311
+ value = '{0}'.format(created)
312
+ elif hdr_key == HEADER_EXPIRES:
313
+ value = '{0}'.format(expires)
314
+ elif hdr_key == HEADER_DATE.lower():
315
+ value = cdate
316
+ request_headers_dict[HEADER_DATE] = '{0}'.format(cdate)
317
+ elif hdr_key == HEADER_DIGEST.lower():
318
+ request_body = body.encode()
319
+ body_digest, digest_prefix = self._get_message_digest(request_body)
320
+ b64_body_digest = b64encode(body_digest.digest())
321
+ value = digest_prefix + b64_body_digest.decode('ascii')
322
+ request_headers_dict[HEADER_DIGEST] = '{0}{1}'.format(
323
+ digest_prefix, b64_body_digest.decode('ascii'))
324
+ elif hdr_key == HEADER_HOST.lower():
325
+ if isinstance(target_host, bytes):
326
+ value = target_host.decode('ascii')
327
+ else:
328
+ value = target_host
329
+ request_headers_dict[HEADER_HOST] = value
330
+ else:
331
+ value = next((v for k, v in headers.items() if k.lower() == hdr_key), None)
332
+ if value is None:
333
+ raise Exception(
334
+ "Cannot sign HTTP request. "
335
+ "Request does not contain the '{0}' header".format(hdr_key))
336
+ signed_headers_list.append((hdr_key, value))
337
+
338
+ return signed_headers_list, request_headers_dict
339
+
340
+ def _get_message_digest(self, data):
341
+ """Calculates and returns a cryptographic digest of a specified HTTP request.
342
+
343
+ :param data: The string representation of the date to be hashed with a cryptographic hash.
344
+ :return: A tuple of (digest, prefix).
345
+ The digest is a hashing object that contains the cryptographic digest of
346
+ the HTTP request.
347
+ The prefix is a string that identifies the cryptographic hash. It is used
348
+ to generate the 'Digest' header as specified in RFC 3230.
349
+ """
350
+
351
+ digest: Union[SHA256Hash, SHA512Hash]
352
+
353
+ if self.hash_algorithm == HASH_SHA512:
354
+ digest = SHA512.new()
355
+ prefix = 'SHA-512='
356
+ elif self.hash_algorithm == HASH_SHA256:
357
+ digest = SHA256.new()
358
+ prefix = 'SHA-256='
359
+ else:
360
+ raise Exception("Unsupported hash algorithm: {0}".format(self.hash_algorithm))
361
+ digest.update(data)
362
+ return digest, prefix
363
+
364
+ def _sign_digest(self, digest):
365
+ """Signs a message digest with a private key specified in the signing_info.
366
+
367
+ :param digest: A hashing object that contains the cryptographic digest of the HTTP request.
368
+ :return: A base-64 string representing the cryptographic signature of the input digest.
369
+ """
370
+ sig_alg = self.signing_algorithm
371
+ if isinstance(self.private_key, RSA.RsaKey):
372
+ if sig_alg is None or sig_alg == ALGORITHM_RSASSA_PSS:
373
+ # RSASSA-PSS in Section 8.1 of RFC8017.
374
+ signature = pss.new(self.private_key).sign(digest)
375
+ elif sig_alg == ALGORITHM_RSASSA_PKCS1v15:
376
+ # RSASSA-PKCS1-v1_5 in Section 8.2 of RFC8017.
377
+ signature = PKCS1_v1_5.new(self.private_key).sign(digest)
378
+ else:
379
+ raise Exception("Unsupported signature algorithm: {0}".format(sig_alg))
380
+ elif isinstance(self.private_key, ECC.EccKey):
381
+ if sig_alg is None:
382
+ sig_alg = ALGORITHM_ECDSA_MODE_FIPS_186_3
383
+ if sig_alg in ALGORITHM_ECDSA_KEY_SIGNING_ALGORITHMS:
384
+ # draft-ietf-httpbis-message-signatures-00 does not specify the ECDSA encoding.
385
+ # Issue: https://github.com/w3c-ccg/http-signatures/issues/107
386
+ signature = DSS.new(key=self.private_key, mode=sig_alg,
387
+ encoding='der').sign(digest)
388
+ else:
389
+ raise Exception("Unsupported signature algorithm: {0}".format(sig_alg))
390
+ else:
391
+ raise Exception("Unsupported private key: {0}".format(type(self.private_key)))
392
+ return b64encode(signature)
393
+
394
+ def _get_authorization_header(self, signed_headers, signed_msg):
395
+ """Calculates and returns the value of the 'Authorization' header when signing HTTP requests.
396
+
397
+ :param signed_headers : A list of tuples. Each value is the name of a HTTP header that
398
+ must be included in the HTTP signature calculation.
399
+ :param signed_msg: A base-64 encoded string representation of the signature.
400
+ :return: The string value of the 'Authorization' header, representing the signature
401
+ of the HTTP request.
402
+ """
403
+ created_ts = None
404
+ expires_ts = None
405
+ for k, v in signed_headers:
406
+ if k == HEADER_CREATED:
407
+ created_ts = v
408
+ elif k == HEADER_EXPIRES:
409
+ expires_ts = v
410
+ lower_keys = [k.lower() for k, v in signed_headers]
411
+ headers_value = " ".join(lower_keys)
412
+
413
+ auth_str = "Signature keyId=\"{0}\",algorithm=\"{1}\",".format(
414
+ self.key_id, self.signing_scheme)
415
+ if created_ts is not None:
416
+ auth_str = auth_str + "created={0},".format(created_ts)
417
+ if expires_ts is not None:
418
+ auth_str = auth_str + "expires={0},".format(expires_ts)
419
+ auth_str = auth_str + "headers=\"{0}\",signature=\"{1}\"".format(
420
+ headers_value, signed_msg.decode('ascii'))
421
+
422
+ return auth_str
@@ -0,0 +1,6 @@
1
+ pytest >= 7.[2].1
2
+ pytest-cov >= 2.[8].1
3
+ tox >= 3.[9].0
4
+ flake8 >= 4.[0].0
5
+ types-python-dateutil >= 2.[8].19.14
6
+ mypy >= 1.5
@@ -0,0 +1,148 @@
1
+ # coding: utf-8
2
+
3
+ {{>partial_header}}
4
+
5
+
6
+ import io
7
+ import json
8
+ import re
9
+
10
+ from urllib.parse import urlencode
11
+ import tornado
12
+ import tornado.gen
13
+ from tornado import httpclient
14
+ from urllib3.filepost import encode_multipart_formdata
15
+
16
+ from {{packageName}}.exceptions import ApiException, ApiValueError
17
+
18
+ RESTResponseType = httpclient.HTTPResponse
19
+
20
+ class RESTResponse(io.IOBase):
21
+
22
+ def __init__(self, resp) -> None:
23
+ self.response = resp
24
+ self.status = resp.code
25
+ self.reason = resp.reason
26
+ self.data = None
27
+
28
+ def read(self):
29
+ if self.data is None:
30
+ self.data = self.response.body
31
+ return self.data
32
+
33
+ @property
34
+ def headers(self):
35
+ """Returns a CIMultiDictProxy of response headers."""
36
+ return self.response.headers
37
+
38
+ def getheaders(self):
39
+ """Returns a CIMultiDictProxy of the response headers; use ``headers`` instead."""
40
+ return self.response.headers
41
+
42
+ def getheader(self, name, default=None):
43
+ """Returns a given response header; use ``headers`` instead."""
44
+ return self.response.headers.get(name, default)
45
+
46
+
47
+ class RESTClientObject:
48
+
49
+ def __init__(self, configuration) -> None:
50
+
51
+ self.ca_certs = configuration.ssl_ca_cert
52
+ self.client_key = configuration.key_file
53
+ self.client_cert = configuration.cert_file
54
+
55
+ self.proxy_port = self.proxy_host = None
56
+
57
+ # https pool manager
58
+ if configuration.proxy:
59
+ self.proxy_port = 80
60
+ self.proxy_host = configuration.proxy
61
+
62
+ self.pool_manager = httpclient.AsyncHTTPClient()
63
+
64
+ @tornado.gen.coroutine
65
+ def request(
66
+ self,
67
+ method,
68
+ url,
69
+ headers=None,
70
+ body=None,
71
+ post_params=None,
72
+ _request_timeout=None
73
+ ):
74
+ """Execute Request
75
+
76
+ :param method: http request method
77
+ :param url: http request url
78
+ :param headers: http request headers
79
+ :param body: request json body, for `application/json`
80
+ :param post_params: request post parameters,
81
+ `application/x-www-form-urlencoded`
82
+ and `multipart/form-data`
83
+ :param _request_timeout: timeout setting for this request. If one
84
+ number provided, it will be total request
85
+ timeout. It can also be a pair (tuple) of
86
+ (connection, read) timeouts.
87
+ """
88
+ method = method.upper()
89
+ assert method in [
90
+ 'GET',
91
+ 'HEAD',
92
+ 'DELETE',
93
+ 'POST',
94
+ 'PUT',
95
+ 'PATCH',
96
+ 'OPTIONS'
97
+ ]
98
+
99
+ if post_params and body:
100
+ raise ApiValueError(
101
+ "body parameter cannot be used with post_params parameter."
102
+ )
103
+
104
+ request = httpclient.HTTPRequest(url)
105
+ request.allow_nonstandard_methods = True
106
+ request.ca_certs = self.ca_certs
107
+ request.client_key = self.client_key
108
+ request.client_cert = self.client_cert
109
+ request.proxy_host = self.proxy_host
110
+ request.proxy_port = self.proxy_port
111
+ request.method = method
112
+ if headers:
113
+ request.headers = headers
114
+ if 'Content-Type' not in headers:
115
+ request.headers['Content-Type'] = 'application/json'
116
+ request.request_timeout = _request_timeout or 5 * 60
117
+
118
+ post_params = post_params or {}
119
+
120
+ # For `POST`, `PUT`, `PATCH`, `OPTIONS`, `DELETE`
121
+ if method in ['POST', 'PUT', 'PATCH', 'OPTIONS', 'DELETE']:
122
+ if re.search('json', headers['Content-Type'], re.IGNORECASE):
123
+ if body:
124
+ body = json.dumps(body)
125
+ request.body = body
126
+ elif headers['Content-Type'] == 'application/x-www-form-urlencoded':
127
+ request.body = urlencode(post_params)
128
+ elif headers['Content-Type'] == 'multipart/form-data':
129
+ multipart = encode_multipart_formdata(post_params)
130
+ request.body, headers['Content-Type'] = multipart
131
+ # Pass a `bytes` parameter directly in the body to support
132
+ # other content types than Json when `body` argument is provided
133
+ # in serialized form
134
+ elif isinstance(body, bytes):
135
+ request.body = body
136
+ else:
137
+ # Cannot generate the request from given parameters
138
+ msg = """Cannot prepare a request message for provided
139
+ arguments. Please check that your arguments match
140
+ declared content type."""
141
+ raise ApiException(status=0, reason=msg)
142
+
143
+ r = yield self.pool_manager.fetch(request, raise_error=False)
144
+
145
+
146
+ r = RESTResponse(r)
147
+
148
+ raise tornado.gen.Return(r)
@@ -0,0 +1,9 @@
1
+ [tox]
2
+ envlist = py3
3
+
4
+ [testenv]
5
+ deps=-r{toxinidir}/requirements.txt
6
+ -r{toxinidir}/test-requirements.txt
7
+
8
+ commands=
9
+ pytest --cov={{{packageName}}}
@@ -0,0 +1,17 @@
1
+ # ref: https://docs.travis-ci.com/user/languages/python
2
+ language: python
3
+ python:
4
+ - "3.9"
5
+ - "3.10"
6
+ - "3.11"
7
+ - "3.12"
8
+ - "3.13"
9
+ # uncomment the following if needed
10
+ #- "3.13-dev" # 3.13 development branch
11
+ #- "nightly" # nightly build
12
+ # command to install dependencies
13
+ install:
14
+ - "pip install -r requirements.txt"
15
+ - "pip install -r test-requirements.txt"
16
+ # command to run tests
17
+ script: pytest --cov={{{packageName}}}
@@ -0,0 +1,26 @@
1
+ import {Map, Record, RecordOf} from 'immutable';
2
+
3
+ import {
4
+ {{#models}}
5
+ {{#model}}
6
+ {{#isEntity}}
7
+ {{classname}}RecordEntity,
8
+ {{/isEntity}}
9
+ {{/model}}
10
+ {{/models}}
11
+ } from "./models/index{{importFileExtension}}"
12
+
13
+ export const ApiEntitiesRecordProps = {
14
+ recType: "ApiEntitiesRecord" as "ApiEntitiesRecord",
15
+ {{#models}}
16
+ {{#model}}
17
+ {{#isEntity}}
18
+ {{#camelcase}}{{classname}}{{/camelcase}}: ({{classname}}RecordEntity(), Map<string, {{classname}}RecordEntity>()),
19
+ {{/isEntity}}
20
+ {{/model}}
21
+ {{/models}}
22
+ };
23
+
24
+ export type ApiEntitiesRecordPropsType = typeof ApiEntitiesRecordProps;
25
+ export const ApiEntitiesRecord = Record(ApiEntitiesRecordProps, ApiEntitiesRecordProps.recType);
26
+ export type ApiEntitiesRecord = RecordOf<ApiEntitiesRecordPropsType>;
@@ -0,0 +1,21 @@
1
+ import {ApiEntitiesRecord} from "./ApiEntitiesRecord{{importFileExtension}}";
2
+ import {ReducerBuilder} from "redux-ts-simple";
3
+ import {normalizedEntities} from "./runtimeSagasAndRecords{{importFileExtension}}";
4
+
5
+ export const ApiEntitiesReducer = new ReducerBuilder(ApiEntitiesRecord())
6
+ .on(normalizedEntities, (state, action): ApiEntitiesRecord => {
7
+ const {entities} = action.payload;
8
+ return state.withMutations(mutableState => {
9
+ for (const entityKey in entities) {
10
+ const entityMap = entities[entityKey];
11
+ const currentEntityMap = mutableState.get(entityKey as any);
12
+ if (currentEntityMap) {
13
+ let mergedEntityMap = currentEntityMap.mergeDeep(entityMap);
14
+ if (!mergedEntityMap.equals(currentEntityMap)) {
15
+ mutableState.set(entityKey as any, mergedEntityMap);
16
+ }
17
+ }
18
+ }
19
+ });
20
+ })
21
+ .build();
@@ -0,0 +1,5 @@
1
+ export let getApiEntitiesState: (state: any) => any = (state: any) => state.app.apiEntities;
2
+
3
+ export function setApiEntitiesStateGetter(getter: (state: any) => any) { // Use this to customize the location where you have placed your ApiEntitiesRecord in your project.
4
+ getApiEntitiesState = getter;
5
+ }