py2docfx 0.1.9.dev1917798__py3-none-any.whl → 0.1.9.dev1926139__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.
- py2docfx/__main__.py +77 -22
- py2docfx/convert_prepare/environment.py +34 -13
- py2docfx/convert_prepare/generate_document.py +12 -5
- py2docfx/convert_prepare/get_source.py +5 -1
- py2docfx/convert_prepare/git.py +24 -20
- py2docfx/convert_prepare/package_info.py +15 -9
- py2docfx/convert_prepare/pip_utils.py +29 -8
- py2docfx/convert_prepare/post_process/merge_toc.py +5 -1
- py2docfx/convert_prepare/sphinx_caller.py +31 -14
- py2docfx/convert_prepare/tests/test_generate_document.py +2 -0
- py2docfx/convert_prepare/tests/test_sphinx_caller.py +2 -0
- py2docfx/convert_prepare/tests/utils.py +11 -0
- py2docfx/docfx_yaml/build_finished.py +11 -3
- py2docfx/docfx_yaml/convert_class.py +4 -2
- py2docfx/docfx_yaml/convert_enum.py +4 -2
- py2docfx/docfx_yaml/convert_module.py +4 -2
- py2docfx/docfx_yaml/convert_package.py +4 -2
- py2docfx/docfx_yaml/logger.py +68 -0
- py2docfx/docfx_yaml/process_doctree.py +6 -4
- py2docfx/docfx_yaml/translator.py +5 -7
- py2docfx/docfx_yaml/writer.py +10 -4
- py2docfx/docfx_yaml/yaml_builder.py +0 -1
- py2docfx/venv/basevenv/Lib/site-packages/setuptools/build_meta.py +2 -2
- py2docfx/venv/basevenv/Lib/site-packages/setuptools/command/bdist_egg.py +1 -1
- py2docfx/venv/basevenv/Lib/site-packages/setuptools/command/bdist_wheel.py +25 -39
- py2docfx/venv/basevenv/Lib/site-packages/setuptools/command/build_ext.py +2 -2
- py2docfx/venv/basevenv/Lib/site-packages/setuptools/command/build_py.py +9 -14
- py2docfx/venv/basevenv/Lib/site-packages/setuptools/command/easy_install.py +2 -2
- py2docfx/venv/basevenv/Lib/site-packages/setuptools/command/editable_wheel.py +2 -2
- py2docfx/venv/basevenv/Lib/site-packages/setuptools/command/egg_info.py +3 -2
- py2docfx/venv/basevenv/Lib/site-packages/setuptools/command/install_egg_info.py +2 -2
- py2docfx/venv/basevenv/Lib/site-packages/setuptools/command/saveopts.py +2 -2
- py2docfx/venv/basevenv/Lib/site-packages/setuptools/command/sdist.py +2 -2
- py2docfx/venv/basevenv/Lib/site-packages/setuptools/command/setopt.py +1 -1
- py2docfx/venv/basevenv/Lib/site-packages/setuptools/config/pyprojecttoml.py +0 -13
- py2docfx/venv/basevenv/Lib/site-packages/setuptools/dist.py +3 -2
- py2docfx/venv/basevenv/Lib/site-packages/setuptools/monkey.py +3 -3
- py2docfx/venv/basevenv/Lib/site-packages/setuptools/msvc.py +11 -11
- py2docfx/venv/basevenv/Lib/site-packages/setuptools/tests/config/test_pyprojecttoml.py +0 -7
- py2docfx/venv/basevenv/Lib/site-packages/setuptools/tests/test_core_metadata.py +168 -72
- py2docfx/venv/basevenv/Lib/site-packages/setuptools/unicode_utils.py +3 -3
- py2docfx/venv/basevenv/Lib/site-packages/wheel/__init__.py +1 -1
- py2docfx/venv/basevenv/Lib/site-packages/wheel/cli/convert.py +1 -2
- py2docfx/venv/venv1/Lib/site-packages/jwt/__init__.py +3 -2
- py2docfx/venv/venv1/Lib/site-packages/jwt/algorithms.py +31 -16
- py2docfx/venv/venv1/Lib/site-packages/jwt/api_jws.py +19 -8
- py2docfx/venv/venv1/Lib/site-packages/jwt/api_jwt.py +75 -19
- py2docfx/venv/venv1/Lib/site-packages/jwt/exceptions.py +8 -0
- py2docfx/venv/venv1/Lib/site-packages/jwt/help.py +4 -1
- py2docfx/venv/venv1/Lib/site-packages/jwt/jwks_client.py +4 -2
- py2docfx/venv/venv1/Lib/site-packages/jwt/utils.py +7 -10
- py2docfx/venv/venv1/Lib/site-packages/msal/application.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/msal/managed_identity.py +5 -3
- py2docfx/venv/venv1/Lib/site-packages/setuptools/build_meta.py +2 -2
- py2docfx/venv/venv1/Lib/site-packages/setuptools/command/bdist_egg.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/setuptools/command/bdist_wheel.py +25 -39
- py2docfx/venv/venv1/Lib/site-packages/setuptools/command/build_ext.py +2 -2
- py2docfx/venv/venv1/Lib/site-packages/setuptools/command/build_py.py +9 -14
- py2docfx/venv/venv1/Lib/site-packages/setuptools/command/easy_install.py +2 -2
- py2docfx/venv/venv1/Lib/site-packages/setuptools/command/editable_wheel.py +2 -2
- py2docfx/venv/venv1/Lib/site-packages/setuptools/command/egg_info.py +3 -2
- py2docfx/venv/venv1/Lib/site-packages/setuptools/command/install_egg_info.py +2 -2
- py2docfx/venv/venv1/Lib/site-packages/setuptools/command/saveopts.py +2 -2
- py2docfx/venv/venv1/Lib/site-packages/setuptools/command/sdist.py +2 -2
- py2docfx/venv/venv1/Lib/site-packages/setuptools/command/setopt.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/setuptools/config/pyprojecttoml.py +0 -13
- py2docfx/venv/venv1/Lib/site-packages/setuptools/dist.py +3 -2
- py2docfx/venv/venv1/Lib/site-packages/setuptools/monkey.py +3 -3
- py2docfx/venv/venv1/Lib/site-packages/setuptools/msvc.py +11 -11
- py2docfx/venv/venv1/Lib/site-packages/setuptools/tests/config/test_pyprojecttoml.py +0 -7
- py2docfx/venv/venv1/Lib/site-packages/setuptools/tests/test_core_metadata.py +168 -72
- py2docfx/venv/venv1/Lib/site-packages/setuptools/unicode_utils.py +3 -3
- py2docfx/venv/venv1/Lib/site-packages/wheel/__init__.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/wheel/cli/convert.py +1 -2
- {py2docfx-0.1.9.dev1917798.dist-info → py2docfx-0.1.9.dev1926139.dist-info}/METADATA +1 -1
- {py2docfx-0.1.9.dev1917798.dist-info → py2docfx-0.1.9.dev1926139.dist-info}/RECORD +79 -77
- {py2docfx-0.1.9.dev1917798.dist-info → py2docfx-0.1.9.dev1926139.dist-info}/WHEEL +1 -1
- /py2docfx/convert_prepare/conf_templates/{master_doc.rst_t → root_doc.rst_t} +0 -0
- {py2docfx-0.1.9.dev1917798.dist-info → py2docfx-0.1.9.dev1926139.dist-info}/top_level.txt +0 -0
@@ -122,8 +122,8 @@ class EggFileSource(ConvertSource):
|
|
122
122
|
self.version = match.group("ver")
|
123
123
|
if pyver := match.group("pyver"):
|
124
124
|
self.pyver = pyver.replace(".", "")
|
125
|
-
self.abi = self.pyver.replace("py", "cp")
|
126
125
|
if arch := match.group("arch"):
|
126
|
+
self.abi = self.pyver.replace("py", "cp")
|
127
127
|
self.platform = normalize(arch)
|
128
128
|
|
129
129
|
self.metadata = Message()
|
@@ -225,7 +225,6 @@ class WininstFileSource(ConvertSource):
|
|
225
225
|
self.platform = normalize(match.group("platform"))
|
226
226
|
if pyver := match.group("pyver"):
|
227
227
|
self.pyver = pyver.replace(".", "")
|
228
|
-
self.abi = pyver.replace("py", "cp")
|
229
228
|
|
230
229
|
# Look for an .egg-info directory and any .pyd files for more precise info
|
231
230
|
egg_info_found = pyd_found = False
|
@@ -6,7 +6,7 @@ from .api_jws import (
|
|
6
6
|
register_algorithm,
|
7
7
|
unregister_algorithm,
|
8
8
|
)
|
9
|
-
from .api_jwt import PyJWT, decode, encode
|
9
|
+
from .api_jwt import PyJWT, decode, decode_complete, encode
|
10
10
|
from .exceptions import (
|
11
11
|
DecodeError,
|
12
12
|
ExpiredSignatureError,
|
@@ -27,7 +27,7 @@ from .exceptions import (
|
|
27
27
|
)
|
28
28
|
from .jwks_client import PyJWKClient
|
29
29
|
|
30
|
-
__version__ = "2.
|
30
|
+
__version__ = "2.10.0"
|
31
31
|
|
32
32
|
__title__ = "PyJWT"
|
33
33
|
__description__ = "JSON Web Token implementation in Python"
|
@@ -49,6 +49,7 @@ __all__ = [
|
|
49
49
|
"PyJWK",
|
50
50
|
"PyJWKSet",
|
51
51
|
"decode",
|
52
|
+
"decode_complete",
|
52
53
|
"encode",
|
53
54
|
"get_unverified_header",
|
54
55
|
"register_algorithm",
|
@@ -297,7 +297,7 @@ class HMACAlgorithm(Algorithm):
|
|
297
297
|
else:
|
298
298
|
raise ValueError
|
299
299
|
except ValueError:
|
300
|
-
raise InvalidKeyError("Key is not valid JSON")
|
300
|
+
raise InvalidKeyError("Key is not valid JSON") from None
|
301
301
|
|
302
302
|
if obj.get("kty") != "oct":
|
303
303
|
raise InvalidKeyError("Not an HMAC key")
|
@@ -346,7 +346,9 @@ if has_crypto:
|
|
346
346
|
try:
|
347
347
|
return cast(RSAPublicKey, load_pem_public_key(key_bytes))
|
348
348
|
except (ValueError, UnsupportedAlgorithm):
|
349
|
-
raise InvalidKeyError(
|
349
|
+
raise InvalidKeyError(
|
350
|
+
"Could not parse the provided public key."
|
351
|
+
) from None
|
350
352
|
|
351
353
|
@overload
|
352
354
|
@staticmethod
|
@@ -409,10 +411,10 @@ if has_crypto:
|
|
409
411
|
else:
|
410
412
|
raise ValueError
|
411
413
|
except ValueError:
|
412
|
-
raise InvalidKeyError("Key is not valid JSON")
|
414
|
+
raise InvalidKeyError("Key is not valid JSON") from None
|
413
415
|
|
414
416
|
if obj.get("kty") != "RSA":
|
415
|
-
raise InvalidKeyError("Not an RSA key")
|
417
|
+
raise InvalidKeyError("Not an RSA key") from None
|
416
418
|
|
417
419
|
if "d" in obj and "e" in obj and "n" in obj:
|
418
420
|
# Private key
|
@@ -428,7 +430,7 @@ if has_crypto:
|
|
428
430
|
if any_props_found and not all(props_found):
|
429
431
|
raise InvalidKeyError(
|
430
432
|
"RSA key must include all parameters if any are present besides d"
|
431
|
-
)
|
433
|
+
) from None
|
432
434
|
|
433
435
|
public_numbers = RSAPublicNumbers(
|
434
436
|
from_base64url_uint(obj["e"]),
|
@@ -520,7 +522,7 @@ if has_crypto:
|
|
520
522
|
):
|
521
523
|
raise InvalidKeyError(
|
522
524
|
"Expecting a EllipticCurvePrivateKey/EllipticCurvePublicKey. Wrong key provided for ECDSA algorithms"
|
523
|
-
)
|
525
|
+
) from None
|
524
526
|
|
525
527
|
return crypto_key
|
526
528
|
|
@@ -581,13 +583,20 @@ if has_crypto:
|
|
581
583
|
obj: dict[str, Any] = {
|
582
584
|
"kty": "EC",
|
583
585
|
"crv": crv,
|
584
|
-
"x": to_base64url_uint(
|
585
|
-
|
586
|
+
"x": to_base64url_uint(
|
587
|
+
public_numbers.x,
|
588
|
+
bit_length=key_obj.curve.key_size,
|
589
|
+
).decode(),
|
590
|
+
"y": to_base64url_uint(
|
591
|
+
public_numbers.y,
|
592
|
+
bit_length=key_obj.curve.key_size,
|
593
|
+
).decode(),
|
586
594
|
}
|
587
595
|
|
588
596
|
if isinstance(key_obj, EllipticCurvePrivateKey):
|
589
597
|
obj["d"] = to_base64url_uint(
|
590
|
-
key_obj.private_numbers().private_value
|
598
|
+
key_obj.private_numbers().private_value,
|
599
|
+
bit_length=key_obj.curve.key_size,
|
591
600
|
).decode()
|
592
601
|
|
593
602
|
if as_dict:
|
@@ -605,13 +614,13 @@ if has_crypto:
|
|
605
614
|
else:
|
606
615
|
raise ValueError
|
607
616
|
except ValueError:
|
608
|
-
raise InvalidKeyError("Key is not valid JSON")
|
617
|
+
raise InvalidKeyError("Key is not valid JSON") from None
|
609
618
|
|
610
619
|
if obj.get("kty") != "EC":
|
611
|
-
raise InvalidKeyError("Not an Elliptic curve key")
|
620
|
+
raise InvalidKeyError("Not an Elliptic curve key") from None
|
612
621
|
|
613
622
|
if "x" not in obj or "y" not in obj:
|
614
|
-
raise InvalidKeyError("Not an Elliptic curve key")
|
623
|
+
raise InvalidKeyError("Not an Elliptic curve key") from None
|
615
624
|
|
616
625
|
x = base64url_decode(obj.get("x"))
|
617
626
|
y = base64url_decode(obj.get("y"))
|
@@ -623,17 +632,23 @@ if has_crypto:
|
|
623
632
|
if len(x) == len(y) == 32:
|
624
633
|
curve_obj = SECP256R1()
|
625
634
|
else:
|
626
|
-
raise InvalidKeyError(
|
635
|
+
raise InvalidKeyError(
|
636
|
+
"Coords should be 32 bytes for curve P-256"
|
637
|
+
) from None
|
627
638
|
elif curve == "P-384":
|
628
639
|
if len(x) == len(y) == 48:
|
629
640
|
curve_obj = SECP384R1()
|
630
641
|
else:
|
631
|
-
raise InvalidKeyError(
|
642
|
+
raise InvalidKeyError(
|
643
|
+
"Coords should be 48 bytes for curve P-384"
|
644
|
+
) from None
|
632
645
|
elif curve == "P-521":
|
633
646
|
if len(x) == len(y) == 66:
|
634
647
|
curve_obj = SECP521R1()
|
635
648
|
else:
|
636
|
-
raise InvalidKeyError(
|
649
|
+
raise InvalidKeyError(
|
650
|
+
"Coords should be 66 bytes for curve P-521"
|
651
|
+
) from None
|
637
652
|
elif curve == "secp256k1":
|
638
653
|
if len(x) == len(y) == 32:
|
639
654
|
curve_obj = SECP256K1()
|
@@ -834,7 +849,7 @@ if has_crypto:
|
|
834
849
|
else:
|
835
850
|
raise ValueError
|
836
851
|
except ValueError:
|
837
|
-
raise InvalidKeyError("Key is not valid JSON")
|
852
|
+
raise InvalidKeyError("Key is not valid JSON") from None
|
838
853
|
|
839
854
|
if obj.get("kty") != "OKP":
|
840
855
|
raise InvalidKeyError("Not an Octet Key Pair")
|
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
3
3
|
import binascii
|
4
4
|
import json
|
5
5
|
import warnings
|
6
|
+
from collections.abc import Sequence
|
6
7
|
from typing import TYPE_CHECKING, Any
|
7
8
|
|
8
9
|
from .algorithms import (
|
@@ -30,7 +31,7 @@ class PyJWS:
|
|
30
31
|
|
31
32
|
def __init__(
|
32
33
|
self,
|
33
|
-
algorithms:
|
34
|
+
algorithms: Sequence[str] | None = None,
|
34
35
|
options: dict[str, Any] | None = None,
|
35
36
|
) -> None:
|
36
37
|
self._algorithms = get_default_algorithms()
|
@@ -104,8 +105,8 @@ class PyJWS:
|
|
104
105
|
def encode(
|
105
106
|
self,
|
106
107
|
payload: bytes,
|
107
|
-
key: AllowedPrivateKeys | str | bytes,
|
108
|
-
algorithm: str | None =
|
108
|
+
key: AllowedPrivateKeys | PyJWK | str | bytes,
|
109
|
+
algorithm: str | None = None,
|
109
110
|
headers: dict[str, Any] | None = None,
|
110
111
|
json_encoder: type[json.JSONEncoder] | None = None,
|
111
112
|
is_payload_detached: bool = False,
|
@@ -114,7 +115,13 @@ class PyJWS:
|
|
114
115
|
segments = []
|
115
116
|
|
116
117
|
# declare a new var to narrow the type for type checkers
|
117
|
-
|
118
|
+
if algorithm is None:
|
119
|
+
if isinstance(key, PyJWK):
|
120
|
+
algorithm_ = key.algorithm_name
|
121
|
+
else:
|
122
|
+
algorithm_ = "HS256"
|
123
|
+
else:
|
124
|
+
algorithm_ = algorithm
|
118
125
|
|
119
126
|
# Prefer headers values if present to function parameters.
|
120
127
|
if headers:
|
@@ -158,6 +165,8 @@ class PyJWS:
|
|
158
165
|
signing_input = b".".join(segments)
|
159
166
|
|
160
167
|
alg_obj = self.get_algorithm_by_name(algorithm_)
|
168
|
+
if isinstance(key, PyJWK):
|
169
|
+
key = key.key
|
161
170
|
key = alg_obj.prepare_key(key)
|
162
171
|
signature = alg_obj.sign(signing_input, key)
|
163
172
|
|
@@ -174,7 +183,7 @@ class PyJWS:
|
|
174
183
|
self,
|
175
184
|
jwt: str | bytes,
|
176
185
|
key: AllowedPublicKeys | PyJWK | str | bytes = "",
|
177
|
-
algorithms:
|
186
|
+
algorithms: Sequence[str] | None = None,
|
178
187
|
options: dict[str, Any] | None = None,
|
179
188
|
detached_payload: bytes | None = None,
|
180
189
|
**kwargs,
|
@@ -185,6 +194,7 @@ class PyJWS:
|
|
185
194
|
"and will be removed in pyjwt version 3. "
|
186
195
|
f"Unsupported kwargs: {tuple(kwargs.keys())}",
|
187
196
|
RemovedInPyjwt3Warning,
|
197
|
+
stacklevel=2,
|
188
198
|
)
|
189
199
|
if options is None:
|
190
200
|
options = {}
|
@@ -219,7 +229,7 @@ class PyJWS:
|
|
219
229
|
self,
|
220
230
|
jwt: str | bytes,
|
221
231
|
key: AllowedPublicKeys | PyJWK | str | bytes = "",
|
222
|
-
algorithms:
|
232
|
+
algorithms: Sequence[str] | None = None,
|
223
233
|
options: dict[str, Any] | None = None,
|
224
234
|
detached_payload: bytes | None = None,
|
225
235
|
**kwargs,
|
@@ -230,6 +240,7 @@ class PyJWS:
|
|
230
240
|
"and will be removed in pyjwt version 3. "
|
231
241
|
f"Unsupported kwargs: {tuple(kwargs.keys())}",
|
232
242
|
RemovedInPyjwt3Warning,
|
243
|
+
stacklevel=2,
|
233
244
|
)
|
234
245
|
decoded = self.decode_complete(
|
235
246
|
jwt, key, algorithms, options, detached_payload=detached_payload
|
@@ -291,14 +302,14 @@ class PyJWS:
|
|
291
302
|
header: dict[str, Any],
|
292
303
|
signature: bytes,
|
293
304
|
key: AllowedPublicKeys | PyJWK | str | bytes = "",
|
294
|
-
algorithms:
|
305
|
+
algorithms: Sequence[str] | None = None,
|
295
306
|
) -> None:
|
296
307
|
if algorithms is None and isinstance(key, PyJWK):
|
297
308
|
algorithms = [key.algorithm_name]
|
298
309
|
try:
|
299
310
|
alg = header["alg"]
|
300
311
|
except KeyError:
|
301
|
-
raise InvalidAlgorithmError("Algorithm not specified")
|
312
|
+
raise InvalidAlgorithmError("Algorithm not specified") from None
|
302
313
|
|
303
314
|
if not alg or (algorithms is not None and alg not in algorithms):
|
304
315
|
raise InvalidAlgorithmError("The specified alg value is not allowed")
|
@@ -3,9 +3,9 @@ from __future__ import annotations
|
|
3
3
|
import json
|
4
4
|
import warnings
|
5
5
|
from calendar import timegm
|
6
|
-
from collections.abc import Iterable
|
6
|
+
from collections.abc import Iterable, Sequence
|
7
7
|
from datetime import datetime, timedelta, timezone
|
8
|
-
from typing import TYPE_CHECKING, Any
|
8
|
+
from typing import TYPE_CHECKING, Any
|
9
9
|
|
10
10
|
from . import api_jws
|
11
11
|
from .exceptions import (
|
@@ -15,6 +15,8 @@ from .exceptions import (
|
|
15
15
|
InvalidAudienceError,
|
16
16
|
InvalidIssuedAtError,
|
17
17
|
InvalidIssuerError,
|
18
|
+
InvalidJTIError,
|
19
|
+
InvalidSubjectError,
|
18
20
|
MissingRequiredClaimError,
|
19
21
|
)
|
20
22
|
from .warnings import RemovedInPyjwt3Warning
|
@@ -39,14 +41,16 @@ class PyJWT:
|
|
39
41
|
"verify_iat": True,
|
40
42
|
"verify_aud": True,
|
41
43
|
"verify_iss": True,
|
44
|
+
"verify_sub": True,
|
45
|
+
"verify_jti": True,
|
42
46
|
"require": [],
|
43
47
|
}
|
44
48
|
|
45
49
|
def encode(
|
46
50
|
self,
|
47
51
|
payload: dict[str, Any],
|
48
|
-
key: AllowedPrivateKeys | str | bytes,
|
49
|
-
algorithm: str | None =
|
52
|
+
key: AllowedPrivateKeys | PyJWK | str | bytes,
|
53
|
+
algorithm: str | None = None,
|
50
54
|
headers: dict[str, Any] | None = None,
|
51
55
|
json_encoder: type[json.JSONEncoder] | None = None,
|
52
56
|
sort_headers: bool = True,
|
@@ -102,7 +106,7 @@ class PyJWT:
|
|
102
106
|
self,
|
103
107
|
jwt: str | bytes,
|
104
108
|
key: AllowedPublicKeys | PyJWK | str | bytes = "",
|
105
|
-
algorithms:
|
109
|
+
algorithms: Sequence[str] | None = None,
|
106
110
|
options: dict[str, Any] | None = None,
|
107
111
|
# deprecated arg, remove in pyjwt3
|
108
112
|
verify: bool | None = None,
|
@@ -111,7 +115,8 @@ class PyJWT:
|
|
111
115
|
# passthrough arguments to _validate_claims
|
112
116
|
# consider putting in options
|
113
117
|
audience: str | Iterable[str] | None = None,
|
114
|
-
issuer: str |
|
118
|
+
issuer: str | Sequence[str] | None = None,
|
119
|
+
subject: str | None = None,
|
115
120
|
leeway: float | timedelta = 0,
|
116
121
|
# kwargs
|
117
122
|
**kwargs: Any,
|
@@ -122,6 +127,7 @@ class PyJWT:
|
|
122
127
|
"and will be removed in pyjwt version 3. "
|
123
128
|
f"Unsupported kwargs: {tuple(kwargs.keys())}",
|
124
129
|
RemovedInPyjwt3Warning,
|
130
|
+
stacklevel=2,
|
125
131
|
)
|
126
132
|
options = dict(options or {}) # shallow-copy or initialize an empty dict
|
127
133
|
options.setdefault("verify_signature", True)
|
@@ -135,6 +141,7 @@ class PyJWT:
|
|
135
141
|
"The equivalent is setting `verify_signature` to False in the `options` dictionary. "
|
136
142
|
"This invocation has a mismatch between the kwarg and the option entry.",
|
137
143
|
category=DeprecationWarning,
|
144
|
+
stacklevel=2,
|
138
145
|
)
|
139
146
|
|
140
147
|
if not options["verify_signature"]:
|
@@ -143,11 +150,8 @@ class PyJWT:
|
|
143
150
|
options.setdefault("verify_iat", False)
|
144
151
|
options.setdefault("verify_aud", False)
|
145
152
|
options.setdefault("verify_iss", False)
|
146
|
-
|
147
|
-
|
148
|
-
raise DecodeError(
|
149
|
-
'It is required that you pass in a value for the "algorithms" argument when calling decode().'
|
150
|
-
)
|
153
|
+
options.setdefault("verify_sub", False)
|
154
|
+
options.setdefault("verify_jti", False)
|
151
155
|
|
152
156
|
decoded = api_jws.decode_complete(
|
153
157
|
jwt,
|
@@ -161,7 +165,12 @@ class PyJWT:
|
|
161
165
|
|
162
166
|
merged_options = {**self.options, **options}
|
163
167
|
self._validate_claims(
|
164
|
-
payload,
|
168
|
+
payload,
|
169
|
+
merged_options,
|
170
|
+
audience=audience,
|
171
|
+
issuer=issuer,
|
172
|
+
leeway=leeway,
|
173
|
+
subject=subject,
|
165
174
|
)
|
166
175
|
|
167
176
|
decoded["payload"] = payload
|
@@ -178,7 +187,7 @@ class PyJWT:
|
|
178
187
|
try:
|
179
188
|
payload = json.loads(decoded["payload"])
|
180
189
|
except ValueError as e:
|
181
|
-
raise DecodeError(f"Invalid payload string: {e}")
|
190
|
+
raise DecodeError(f"Invalid payload string: {e}") from e
|
182
191
|
if not isinstance(payload, dict):
|
183
192
|
raise DecodeError("Invalid payload string: must be a json object")
|
184
193
|
return payload
|
@@ -187,7 +196,7 @@ class PyJWT:
|
|
187
196
|
self,
|
188
197
|
jwt: str | bytes,
|
189
198
|
key: AllowedPublicKeys | PyJWK | str | bytes = "",
|
190
|
-
algorithms:
|
199
|
+
algorithms: Sequence[str] | None = None,
|
191
200
|
options: dict[str, Any] | None = None,
|
192
201
|
# deprecated arg, remove in pyjwt3
|
193
202
|
verify: bool | None = None,
|
@@ -196,7 +205,8 @@ class PyJWT:
|
|
196
205
|
# passthrough arguments to _validate_claims
|
197
206
|
# consider putting in options
|
198
207
|
audience: str | Iterable[str] | None = None,
|
199
|
-
|
208
|
+
subject: str | None = None,
|
209
|
+
issuer: str | Sequence[str] | None = None,
|
200
210
|
leeway: float | timedelta = 0,
|
201
211
|
# kwargs
|
202
212
|
**kwargs: Any,
|
@@ -207,6 +217,7 @@ class PyJWT:
|
|
207
217
|
"and will be removed in pyjwt version 3. "
|
208
218
|
f"Unsupported kwargs: {tuple(kwargs.keys())}",
|
209
219
|
RemovedInPyjwt3Warning,
|
220
|
+
stacklevel=2,
|
210
221
|
)
|
211
222
|
decoded = self.decode_complete(
|
212
223
|
jwt,
|
@@ -216,6 +227,7 @@ class PyJWT:
|
|
216
227
|
verify=verify,
|
217
228
|
detached_payload=detached_payload,
|
218
229
|
audience=audience,
|
230
|
+
subject=subject,
|
219
231
|
issuer=issuer,
|
220
232
|
leeway=leeway,
|
221
233
|
)
|
@@ -227,6 +239,7 @@ class PyJWT:
|
|
227
239
|
options: dict[str, Any],
|
228
240
|
audience=None,
|
229
241
|
issuer=None,
|
242
|
+
subject: str | None = None,
|
230
243
|
leeway: float | timedelta = 0,
|
231
244
|
) -> None:
|
232
245
|
if isinstance(leeway, timedelta):
|
@@ -256,6 +269,12 @@ class PyJWT:
|
|
256
269
|
payload, audience, strict=options.get("strict_aud", False)
|
257
270
|
)
|
258
271
|
|
272
|
+
if options["verify_sub"]:
|
273
|
+
self._validate_sub(payload, subject)
|
274
|
+
|
275
|
+
if options["verify_jti"]:
|
276
|
+
self._validate_jti(payload)
|
277
|
+
|
259
278
|
def _validate_required_claims(
|
260
279
|
self,
|
261
280
|
payload: dict[str, Any],
|
@@ -265,6 +284,39 @@ class PyJWT:
|
|
265
284
|
if payload.get(claim) is None:
|
266
285
|
raise MissingRequiredClaimError(claim)
|
267
286
|
|
287
|
+
def _validate_sub(self, payload: dict[str, Any], subject=None) -> None:
|
288
|
+
"""
|
289
|
+
Checks whether "sub" if in the payload is valid ot not.
|
290
|
+
This is an Optional claim
|
291
|
+
|
292
|
+
:param payload(dict): The payload which needs to be validated
|
293
|
+
:param subject(str): The subject of the token
|
294
|
+
"""
|
295
|
+
|
296
|
+
if "sub" not in payload:
|
297
|
+
return
|
298
|
+
|
299
|
+
if not isinstance(payload["sub"], str):
|
300
|
+
raise InvalidSubjectError("Subject must be a string")
|
301
|
+
|
302
|
+
if subject is not None:
|
303
|
+
if payload.get("sub") != subject:
|
304
|
+
raise InvalidSubjectError("Invalid subject")
|
305
|
+
|
306
|
+
def _validate_jti(self, payload: dict[str, Any]) -> None:
|
307
|
+
"""
|
308
|
+
Checks whether "jti" if in the payload is valid ot not
|
309
|
+
This is an Optional claim
|
310
|
+
|
311
|
+
:param payload(dict): The payload which needs to be validated
|
312
|
+
"""
|
313
|
+
|
314
|
+
if "jti" not in payload:
|
315
|
+
return
|
316
|
+
|
317
|
+
if not isinstance(payload.get("jti"), str):
|
318
|
+
raise InvalidJTIError("JWT ID must be a string")
|
319
|
+
|
268
320
|
def _validate_iat(
|
269
321
|
self,
|
270
322
|
payload: dict[str, Any],
|
@@ -274,7 +326,9 @@ class PyJWT:
|
|
274
326
|
try:
|
275
327
|
iat = int(payload["iat"])
|
276
328
|
except ValueError:
|
277
|
-
raise InvalidIssuedAtError(
|
329
|
+
raise InvalidIssuedAtError(
|
330
|
+
"Issued At claim (iat) must be an integer."
|
331
|
+
) from None
|
278
332
|
if iat > (now + leeway):
|
279
333
|
raise ImmatureSignatureError("The token is not yet valid (iat)")
|
280
334
|
|
@@ -287,7 +341,7 @@ class PyJWT:
|
|
287
341
|
try:
|
288
342
|
nbf = int(payload["nbf"])
|
289
343
|
except ValueError:
|
290
|
-
raise DecodeError("Not Before claim (nbf) must be an integer.")
|
344
|
+
raise DecodeError("Not Before claim (nbf) must be an integer.") from None
|
291
345
|
|
292
346
|
if nbf > (now + leeway):
|
293
347
|
raise ImmatureSignatureError("The token is not yet valid (nbf)")
|
@@ -301,7 +355,9 @@ class PyJWT:
|
|
301
355
|
try:
|
302
356
|
exp = int(payload["exp"])
|
303
357
|
except ValueError:
|
304
|
-
raise DecodeError(
|
358
|
+
raise DecodeError(
|
359
|
+
"Expiration Time claim (exp) must be an integer."
|
360
|
+
) from None
|
305
361
|
|
306
362
|
if exp <= (now - leeway):
|
307
363
|
raise ExpiredSignatureError("Signature has expired")
|
@@ -363,7 +419,7 @@ class PyJWT:
|
|
363
419
|
if "iss" not in payload:
|
364
420
|
raise MissingRequiredClaimError("iss")
|
365
421
|
|
366
|
-
if isinstance(issuer,
|
422
|
+
if isinstance(issuer, Sequence):
|
367
423
|
if payload["iss"] not in issuer:
|
368
424
|
raise InvalidIssuerError("Invalid issuer")
|
369
425
|
else:
|
@@ -39,7 +39,10 @@ def info() -> Dict[str, Dict[str, str]]:
|
|
39
39
|
)
|
40
40
|
if pypy_version_info.releaselevel != "final":
|
41
41
|
implementation_version = "".join(
|
42
|
-
[
|
42
|
+
[
|
43
|
+
implementation_version,
|
44
|
+
pypy_version_info.releaselevel,
|
45
|
+
]
|
43
46
|
)
|
44
47
|
else:
|
45
48
|
implementation_version = "Unknown"
|
@@ -45,7 +45,9 @@ class PyJWKClient:
|
|
45
45
|
if cache_keys:
|
46
46
|
# Cache signing keys
|
47
47
|
# Ignore mypy (https://github.com/python/mypy/issues/2427)
|
48
|
-
self.get_signing_key = lru_cache(maxsize=max_cached_keys)(
|
48
|
+
self.get_signing_key = lru_cache(maxsize=max_cached_keys)(
|
49
|
+
self.get_signing_key
|
50
|
+
) # type: ignore
|
49
51
|
|
50
52
|
def fetch_data(self) -> Any:
|
51
53
|
jwk_set: Any = None
|
@@ -58,7 +60,7 @@ class PyJWKClient:
|
|
58
60
|
except (URLError, TimeoutError) as e:
|
59
61
|
raise PyJWKClientConnectionError(
|
60
62
|
f'Fail to fetch data from the url, err: "{e}"'
|
61
|
-
)
|
63
|
+
) from e
|
62
64
|
else:
|
63
65
|
return jwk_set
|
64
66
|
finally:
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import base64
|
2
2
|
import binascii
|
3
3
|
import re
|
4
|
-
from typing import Union
|
4
|
+
from typing import Optional, Union
|
5
5
|
|
6
6
|
try:
|
7
7
|
from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurve
|
@@ -37,11 +37,11 @@ def base64url_encode(input: bytes) -> bytes:
|
|
37
37
|
return base64.urlsafe_b64encode(input).replace(b"=", b"")
|
38
38
|
|
39
39
|
|
40
|
-
def to_base64url_uint(val: int) -> bytes:
|
40
|
+
def to_base64url_uint(val: int, *, bit_length: Optional[int] = None) -> bytes:
|
41
41
|
if val < 0:
|
42
42
|
raise ValueError("Must be a positive integer")
|
43
43
|
|
44
|
-
int_bytes = bytes_from_int(val)
|
44
|
+
int_bytes = bytes_from_int(val, bit_length=bit_length)
|
45
45
|
|
46
46
|
if len(int_bytes) == 0:
|
47
47
|
int_bytes = b"\x00"
|
@@ -63,13 +63,10 @@ def bytes_to_number(string: bytes) -> int:
|
|
63
63
|
return int(binascii.b2a_hex(string), 16)
|
64
64
|
|
65
65
|
|
66
|
-
def bytes_from_int(val: int) -> bytes:
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
while remaining != 0:
|
71
|
-
remaining >>= 8
|
72
|
-
byte_length += 1
|
66
|
+
def bytes_from_int(val: int, *, bit_length: Optional[int] = None) -> bytes:
|
67
|
+
if bit_length is None:
|
68
|
+
bit_length = val.bit_length()
|
69
|
+
byte_length = (bit_length + 7) // 8
|
73
70
|
|
74
71
|
return val.to_bytes(byte_length, "big", signed=False)
|
75
72
|
|
@@ -21,7 +21,7 @@ from .cloudshell import _is_running_in_cloud_shell
|
|
21
21
|
|
22
22
|
|
23
23
|
# The __init__.py will import this. Not the other way around.
|
24
|
-
__version__ = "1.31.
|
24
|
+
__version__ = "1.31.1" # When releasing, also check and bump our dependencies's versions if needed
|
25
25
|
|
26
26
|
logger = logging.getLogger(__name__)
|
27
27
|
_AUTHORITY_TYPE_CLOUDSHELL = "CLOUDSHELL"
|
@@ -346,10 +346,12 @@ def _scope_to_resource(scope): # This is an experimental reasonable-effort appr
|
|
346
346
|
def _get_arc_endpoint():
|
347
347
|
if "IDENTITY_ENDPOINT" in os.environ and "IMDS_ENDPOINT" in os.environ:
|
348
348
|
return os.environ["IDENTITY_ENDPOINT"]
|
349
|
-
if ( # Defined in https://
|
350
|
-
sys.platform == "linux" and os.path.exists("/
|
349
|
+
if ( # Defined in https://eng.ms/docs/cloud-ai-platform/azure-core/azure-management-and-platforms/control-plane-bburns/hybrid-resource-provider/azure-arc-for-servers/specs/extension_authoring
|
350
|
+
sys.platform == "linux" and os.path.exists("/opt/azcmagent/bin/himds")
|
351
351
|
or sys.platform == "win32" and os.path.exists(os.path.expandvars(
|
352
|
-
|
352
|
+
# Avoid Windows-only "%EnvVar%" syntax so that tests can be run on Linux
|
353
|
+
r"${ProgramFiles}\AzureConnectedMachineAgent\himds.exe"
|
354
|
+
))
|
353
355
|
):
|
354
356
|
return "http://localhost:40342/metadata/identity/oauth2/token"
|
355
357
|
|
@@ -91,11 +91,11 @@ class Distribution(setuptools.dist.Distribution):
|
|
91
91
|
for the duration of this context.
|
92
92
|
"""
|
93
93
|
orig = distutils.core.Distribution
|
94
|
-
distutils.core.Distribution = cls
|
94
|
+
distutils.core.Distribution = cls # type: ignore[misc] # monkeypatching
|
95
95
|
try:
|
96
96
|
yield
|
97
97
|
finally:
|
98
|
-
distutils.core.Distribution = orig
|
98
|
+
distutils.core.Distribution = orig # type: ignore[misc] # monkeypatching
|
99
99
|
|
100
100
|
|
101
101
|
@contextlib.contextmanager
|
@@ -277,7 +277,7 @@ class bdist_egg(Command):
|
|
277
277
|
log.warn("zip_safe flag not set; analyzing archive contents...")
|
278
278
|
return analyze_egg(self.bdist_dir, self.stubs)
|
279
279
|
|
280
|
-
def gen_header(self) ->
|
280
|
+
def gen_header(self) -> Literal["w"]:
|
281
281
|
return 'w'
|
282
282
|
|
283
283
|
def copy_metadata_to(self, target_dir) -> None:
|