pangea-sdk 3.9.0__py3-none-any.whl → 4.1.0__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.
- pangea/__init__.py +1 -1
- pangea/asyncio/request.py +4 -4
- pangea/asyncio/services/audit.py +30 -11
- pangea/asyncio/services/authn.py +49 -29
- pangea/asyncio/services/authz.py +13 -7
- pangea/asyncio/services/embargo.py +2 -2
- pangea/asyncio/services/file_scan.py +3 -3
- pangea/asyncio/services/intel.py +40 -22
- pangea/asyncio/services/redact.py +5 -3
- pangea/asyncio/services/vault.py +93 -28
- pangea/crypto/rsa.py +47 -0
- pangea/dump_audit.py +1 -1
- pangea/request.py +8 -5
- pangea/response.py +9 -16
- pangea/services/audit/audit.py +43 -20
- pangea/services/audit/models.py +3 -3
- pangea/services/audit/util.py +3 -3
- pangea/services/authn/authn.py +39 -29
- pangea/services/authn/models.py +9 -4
- pangea/services/authz.py +10 -8
- pangea/services/embargo.py +2 -2
- pangea/services/file_scan.py +2 -2
- pangea/services/intel.py +23 -21
- pangea/services/redact.py +3 -3
- pangea/services/vault/models/asymmetric.py +2 -0
- pangea/services/vault/models/common.py +67 -6
- pangea/services/vault/models/symmetric.py +6 -2
- pangea/services/vault/vault.py +89 -28
- pangea/utils.py +4 -16
- pangea/verify_audit.py +270 -83
- {pangea_sdk-3.9.0.dist-info → pangea_sdk-4.1.0.dist-info}/METADATA +10 -9
- pangea_sdk-4.1.0.dist-info/RECORD +47 -0
- {pangea_sdk-3.9.0.dist-info → pangea_sdk-4.1.0.dist-info}/WHEEL +1 -1
- pangea_sdk-3.9.0.dist-info/RECORD +0 -46
pangea/services/vault/vault.py
CHANGED
@@ -31,6 +31,9 @@ from pangea.services.vault.models.common import (
|
|
31
31
|
EncryptStructuredResult,
|
32
32
|
EncryptTransformRequest,
|
33
33
|
EncryptTransformResult,
|
34
|
+
ExportEncryptionAlgorithm,
|
35
|
+
ExportRequest,
|
36
|
+
ExportResult,
|
34
37
|
FolderCreateRequest,
|
35
38
|
FolderCreateResult,
|
36
39
|
GetRequest,
|
@@ -151,7 +154,7 @@ class Vault(ServiceBase):
|
|
151
154
|
input = DeleteRequest(
|
152
155
|
id=id,
|
153
156
|
)
|
154
|
-
return self.request.post("v1/delete", DeleteResult, data=input.
|
157
|
+
return self.request.post("v1/delete", DeleteResult, data=input.model_dump(exclude_none=True))
|
155
158
|
|
156
159
|
# Get endpoint
|
157
160
|
def get(
|
@@ -198,7 +201,7 @@ class Vault(ServiceBase):
|
|
198
201
|
verbose=verbose,
|
199
202
|
version_state=version_state,
|
200
203
|
)
|
201
|
-
return self.request.post("v1/get", GetResult, data=input.
|
204
|
+
return self.request.post("v1/get", GetResult, data=input.model_dump(exclude_none=True))
|
202
205
|
|
203
206
|
# List endpoint
|
204
207
|
def list(
|
@@ -254,7 +257,7 @@ class Vault(ServiceBase):
|
|
254
257
|
)
|
255
258
|
"""
|
256
259
|
input = ListRequest(filter=filter, last=last, order=order, order_by=order_by, size=size)
|
257
|
-
return self.request.post("v1/list", ListResult, data=input.
|
260
|
+
return self.request.post("v1/list", ListResult, data=input.model_dump(exclude_none=True))
|
258
261
|
|
259
262
|
# Update endpoint
|
260
263
|
def update(
|
@@ -335,7 +338,7 @@ class Vault(ServiceBase):
|
|
335
338
|
expiration=expiration,
|
336
339
|
item_state=item_state,
|
337
340
|
)
|
338
|
-
return self.request.post("v1/update", UpdateResult, data=input.
|
341
|
+
return self.request.post("v1/update", UpdateResult, data=input.model_dump(exclude_none=True))
|
339
342
|
|
340
343
|
def secret_store(
|
341
344
|
self,
|
@@ -405,7 +408,7 @@ class Vault(ServiceBase):
|
|
405
408
|
rotation_state=rotation_state,
|
406
409
|
expiration=expiration,
|
407
410
|
)
|
408
|
-
return self.request.post("v1/secret/store", SecretStoreResult, data=input.
|
411
|
+
return self.request.post("v1/secret/store", SecretStoreResult, data=input.model_dump(exclude_none=True))
|
409
412
|
|
410
413
|
def pangea_token_store(
|
411
414
|
self,
|
@@ -475,7 +478,7 @@ class Vault(ServiceBase):
|
|
475
478
|
rotation_state=rotation_state,
|
476
479
|
expiration=expiration,
|
477
480
|
)
|
478
|
-
return self.request.post("v1/secret/store", SecretStoreResult, data=input.
|
481
|
+
return self.request.post("v1/secret/store", SecretStoreResult, data=input.model_dump(exclude_none=True))
|
479
482
|
|
480
483
|
# Rotate endpoint
|
481
484
|
def secret_rotate(
|
@@ -515,7 +518,7 @@ class Vault(ServiceBase):
|
|
515
518
|
)
|
516
519
|
"""
|
517
520
|
input = SecretRotateRequest(id=id, secret=secret, rotation_state=rotation_state)
|
518
|
-
return self.request.post("v1/secret/rotate", SecretRotateResult, data=input.
|
521
|
+
return self.request.post("v1/secret/rotate", SecretRotateResult, data=input.model_dump(exclude_none=True))
|
519
522
|
|
520
523
|
# Rotate endpoint
|
521
524
|
def pangea_token_rotate(self, id: str) -> PangeaResponse[SecretRotateResult]:
|
@@ -543,7 +546,7 @@ class Vault(ServiceBase):
|
|
543
546
|
)
|
544
547
|
"""
|
545
548
|
input = SecretRotateRequest(id=id) # type: ignore[call-arg]
|
546
|
-
return self.request.post("v1/secret/rotate", SecretRotateResult, data=input.
|
549
|
+
return self.request.post("v1/secret/rotate", SecretRotateResult, data=input.model_dump(exclude_none=True))
|
547
550
|
|
548
551
|
def symmetric_generate(
|
549
552
|
self,
|
@@ -556,6 +559,7 @@ class Vault(ServiceBase):
|
|
556
559
|
rotation_frequency: Optional[str] = None,
|
557
560
|
rotation_state: Optional[ItemVersionState] = None,
|
558
561
|
expiration: Optional[datetime.datetime] = None,
|
562
|
+
exportable: Optional[bool] = None,
|
559
563
|
) -> PangeaResponse[SymmetricGenerateResult]:
|
560
564
|
"""
|
561
565
|
Symmetric generate
|
@@ -577,6 +581,7 @@ class Vault(ServiceBase):
|
|
577
581
|
- `deactivated`
|
578
582
|
- `destroyed`
|
579
583
|
expiration (str, optional): Expiration timestamp
|
584
|
+
exportable (bool, optional): Whether the key is exportable or not
|
580
585
|
|
581
586
|
Raises:
|
582
587
|
PangeaAPIException: If an API Error happens
|
@@ -616,11 +621,12 @@ class Vault(ServiceBase):
|
|
616
621
|
rotation_frequency=rotation_frequency,
|
617
622
|
rotation_state=rotation_state,
|
618
623
|
expiration=expiration,
|
624
|
+
exportable=exportable,
|
619
625
|
)
|
620
626
|
return self.request.post(
|
621
627
|
"v1/key/generate",
|
622
628
|
SymmetricGenerateResult,
|
623
|
-
data=input.
|
629
|
+
data=input.model_dump(exclude_none=True),
|
624
630
|
)
|
625
631
|
|
626
632
|
def asymmetric_generate(
|
@@ -634,6 +640,7 @@ class Vault(ServiceBase):
|
|
634
640
|
rotation_frequency: Optional[str] = None,
|
635
641
|
rotation_state: Optional[ItemVersionState] = None,
|
636
642
|
expiration: Optional[datetime.datetime] = None,
|
643
|
+
exportable: Optional[bool] = None,
|
637
644
|
) -> PangeaResponse[AsymmetricGenerateResult]:
|
638
645
|
"""
|
639
646
|
Asymmetric generate
|
@@ -655,6 +662,7 @@ class Vault(ServiceBase):
|
|
655
662
|
- `deactivated`
|
656
663
|
- `destroyed`
|
657
664
|
expiration (str, optional): Expiration timestamp
|
665
|
+
exportable (bool, optional): Whether the key is exportable or not
|
658
666
|
|
659
667
|
Raises:
|
660
668
|
PangeaAPIException: If an API Error happens
|
@@ -694,11 +702,12 @@ class Vault(ServiceBase):
|
|
694
702
|
rotation_frequency=rotation_frequency,
|
695
703
|
rotation_state=rotation_state,
|
696
704
|
expiration=expiration,
|
705
|
+
exportable=exportable,
|
697
706
|
)
|
698
707
|
return self.request.post(
|
699
708
|
"v1/key/generate",
|
700
709
|
AsymmetricGenerateResult,
|
701
|
-
data=input.
|
710
|
+
data=input.model_dump(exclude_none=True),
|
702
711
|
)
|
703
712
|
|
704
713
|
# Store endpoints
|
@@ -715,6 +724,7 @@ class Vault(ServiceBase):
|
|
715
724
|
rotation_frequency: Optional[str] = None,
|
716
725
|
rotation_state: Optional[ItemVersionState] = None,
|
717
726
|
expiration: Optional[datetime.datetime] = None,
|
727
|
+
exportable: Optional[bool] = None,
|
718
728
|
) -> PangeaResponse[AsymmetricStoreResult]:
|
719
729
|
"""
|
720
730
|
Asymmetric store
|
@@ -738,6 +748,7 @@ class Vault(ServiceBase):
|
|
738
748
|
- `deactivated`
|
739
749
|
- `destroyed`
|
740
750
|
expiration (str, optional): Expiration timestamp
|
751
|
+
exportable (bool, optional): Whether the key is exportable or not
|
741
752
|
|
742
753
|
Raises:
|
743
754
|
PangeaAPIException: If an API Error happens
|
@@ -781,8 +792,9 @@ class Vault(ServiceBase):
|
|
781
792
|
rotation_frequency=rotation_frequency,
|
782
793
|
rotation_state=rotation_state,
|
783
794
|
expiration=expiration,
|
795
|
+
exportable=exportable,
|
784
796
|
)
|
785
|
-
return self.request.post("v1/key/store", AsymmetricStoreResult, data=input.
|
797
|
+
return self.request.post("v1/key/store", AsymmetricStoreResult, data=input.model_dump(exclude_none=True))
|
786
798
|
|
787
799
|
def symmetric_store(
|
788
800
|
self,
|
@@ -796,6 +808,7 @@ class Vault(ServiceBase):
|
|
796
808
|
rotation_frequency: Optional[str] = None,
|
797
809
|
rotation_state: Optional[ItemVersionState] = None,
|
798
810
|
expiration: Optional[datetime.datetime] = None,
|
811
|
+
exportable: Optional[bool] = None,
|
799
812
|
) -> PangeaResponse[SymmetricStoreResult]:
|
800
813
|
"""
|
801
814
|
Symmetric store
|
@@ -818,6 +831,7 @@ class Vault(ServiceBase):
|
|
818
831
|
- `deactivated`
|
819
832
|
- `destroyed`
|
820
833
|
expiration (str, optional): Expiration timestamp
|
834
|
+
exportable (bool, optional): Whether the key is exportable or not
|
821
835
|
|
822
836
|
Raises:
|
823
837
|
PangeaAPIException: If an API Error happens
|
@@ -859,8 +873,9 @@ class Vault(ServiceBase):
|
|
859
873
|
rotation_frequency=rotation_frequency,
|
860
874
|
rotation_state=rotation_state,
|
861
875
|
expiration=expiration,
|
876
|
+
exportable=exportable,
|
862
877
|
)
|
863
|
-
return self.request.post("v1/key/store", SymmetricStoreResult, data=input.
|
878
|
+
return self.request.post("v1/key/store", SymmetricStoreResult, data=input.model_dump(exclude_none=True))
|
864
879
|
|
865
880
|
# Rotate endpoint
|
866
881
|
def key_rotate(
|
@@ -913,7 +928,7 @@ class Vault(ServiceBase):
|
|
913
928
|
key=key,
|
914
929
|
rotation_state=rotation_state,
|
915
930
|
)
|
916
|
-
return self.request.post("v1/key/rotate", KeyRotateResult, data=input.
|
931
|
+
return self.request.post("v1/key/rotate", KeyRotateResult, data=input.model_dump(exclude_none=True))
|
917
932
|
|
918
933
|
# Encrypt
|
919
934
|
def encrypt(self, id: str, plain_text: str, version: Optional[int] = None) -> PangeaResponse[EncryptResult]:
|
@@ -944,8 +959,8 @@ class Vault(ServiceBase):
|
|
944
959
|
version=1,
|
945
960
|
)
|
946
961
|
"""
|
947
|
-
input = EncryptRequest(id=id, plain_text=plain_text, version=version)
|
948
|
-
return self.request.post("v1/key/encrypt", EncryptResult, data=input.
|
962
|
+
input = EncryptRequest(id=id, plain_text=plain_text, version=version)
|
963
|
+
return self.request.post("v1/key/encrypt", EncryptResult, data=input.model_dump(exclude_none=True))
|
949
964
|
|
950
965
|
# Decrypt
|
951
966
|
def decrypt(self, id: str, cipher_text: str, version: Optional[int] = None) -> PangeaResponse[DecryptResult]:
|
@@ -976,8 +991,8 @@ class Vault(ServiceBase):
|
|
976
991
|
version=1,
|
977
992
|
)
|
978
993
|
"""
|
979
|
-
input = DecryptRequest(id=id, cipher_text=cipher_text, version=version)
|
980
|
-
return self.request.post("v1/key/decrypt", DecryptResult, data=input.
|
994
|
+
input = DecryptRequest(id=id, cipher_text=cipher_text, version=version)
|
995
|
+
return self.request.post("v1/key/decrypt", DecryptResult, data=input.model_dump(exclude_none=True))
|
981
996
|
|
982
997
|
# Sign
|
983
998
|
def sign(self, id: str, message: str, version: Optional[int] = None) -> PangeaResponse[SignResult]:
|
@@ -1009,7 +1024,7 @@ class Vault(ServiceBase):
|
|
1009
1024
|
)
|
1010
1025
|
"""
|
1011
1026
|
input = SignRequest(id=id, message=message, version=version)
|
1012
|
-
return self.request.post("v1/key/sign", SignResult, data=input.
|
1027
|
+
return self.request.post("v1/key/sign", SignResult, data=input.model_dump(exclude_none=True))
|
1013
1028
|
|
1014
1029
|
# Verify
|
1015
1030
|
def verify(
|
@@ -1050,7 +1065,7 @@ class Vault(ServiceBase):
|
|
1050
1065
|
signature=signature,
|
1051
1066
|
version=version,
|
1052
1067
|
)
|
1053
|
-
return self.request.post("v1/key/verify", VerifyResult, data=input.
|
1068
|
+
return self.request.post("v1/key/verify", VerifyResult, data=input.model_dump(exclude_none=True))
|
1054
1069
|
|
1055
1070
|
def jwt_verify(self, jws: str) -> PangeaResponse[JWTVerifyResult]:
|
1056
1071
|
"""
|
@@ -1077,7 +1092,7 @@ class Vault(ServiceBase):
|
|
1077
1092
|
)
|
1078
1093
|
"""
|
1079
1094
|
input = JWTVerifyRequest(jws=jws)
|
1080
|
-
return self.request.post("v1/key/verify/jwt", JWTVerifyResult, data=input.
|
1095
|
+
return self.request.post("v1/key/verify/jwt", JWTVerifyResult, data=input.model_dump(exclude_none=True))
|
1081
1096
|
|
1082
1097
|
def jwt_sign(self, id: str, payload: str) -> PangeaResponse[JWTSignResult]:
|
1083
1098
|
"""
|
@@ -1106,7 +1121,7 @@ class Vault(ServiceBase):
|
|
1106
1121
|
)
|
1107
1122
|
"""
|
1108
1123
|
input = JWTSignRequest(id=id, payload=payload)
|
1109
|
-
return self.request.post("v1/key/sign/jwt", JWTSignResult, data=input.
|
1124
|
+
return self.request.post("v1/key/sign/jwt", JWTSignResult, data=input.model_dump(exclude_none=True))
|
1110
1125
|
|
1111
1126
|
# Get endpoint
|
1112
1127
|
def jwk_get(self, id: str, version: Optional[str] = None) -> PangeaResponse[JWKGetResult]:
|
@@ -1137,7 +1152,7 @@ class Vault(ServiceBase):
|
|
1137
1152
|
)
|
1138
1153
|
"""
|
1139
1154
|
input = JWKGetRequest(id=id, version=version)
|
1140
|
-
return self.request.post("v1/get/jwk", JWKGetResult, data=input.
|
1155
|
+
return self.request.post("v1/get/jwk", JWKGetResult, data=input.model_dump(exclude_none=True))
|
1141
1156
|
|
1142
1157
|
# State change
|
1143
1158
|
def state_change(
|
@@ -1180,7 +1195,7 @@ class Vault(ServiceBase):
|
|
1180
1195
|
)
|
1181
1196
|
"""
|
1182
1197
|
input = StateChangeRequest(id=id, state=state, version=version, destroy_period=destroy_period)
|
1183
|
-
return self.request.post("v1/state/change", StateChangeResult, data=input.
|
1198
|
+
return self.request.post("v1/state/change", StateChangeResult, data=input.model_dump(exclude_none=True))
|
1184
1199
|
|
1185
1200
|
# Folder create
|
1186
1201
|
def folder_create(
|
@@ -1217,7 +1232,7 @@ class Vault(ServiceBase):
|
|
1217
1232
|
)
|
1218
1233
|
"""
|
1219
1234
|
input = FolderCreateRequest(name=name, folder=folder, metadata=metadata, tags=tags)
|
1220
|
-
return self.request.post("v1/folder/create", FolderCreateResult, data=input.
|
1235
|
+
return self.request.post("v1/folder/create", FolderCreateResult, data=input.model_dump(exclude_none=True))
|
1221
1236
|
|
1222
1237
|
# Encrypt structured
|
1223
1238
|
def encrypt_structured(
|
@@ -1265,7 +1280,7 @@ class Vault(ServiceBase):
|
|
1265
1280
|
return self.request.post(
|
1266
1281
|
"v1/key/encrypt/structured",
|
1267
1282
|
EncryptStructuredResult,
|
1268
|
-
data=input.
|
1283
|
+
data=input.model_dump(exclude_none=True),
|
1269
1284
|
)
|
1270
1285
|
|
1271
1286
|
# Decrypt structured
|
@@ -1314,7 +1329,7 @@ class Vault(ServiceBase):
|
|
1314
1329
|
return self.request.post(
|
1315
1330
|
"v1/key/decrypt/structured",
|
1316
1331
|
EncryptStructuredResult,
|
1317
|
-
data=input.
|
1332
|
+
data=input.model_dump(exclude_none=True),
|
1318
1333
|
)
|
1319
1334
|
|
1320
1335
|
def encrypt_transform(
|
@@ -1364,7 +1379,7 @@ class Vault(ServiceBase):
|
|
1364
1379
|
return self.request.post(
|
1365
1380
|
"v1/key/encrypt/transform",
|
1366
1381
|
EncryptTransformResult,
|
1367
|
-
data=input.
|
1382
|
+
data=input.model_dump(exclude_none=True),
|
1368
1383
|
)
|
1369
1384
|
|
1370
1385
|
def decrypt_transform(
|
@@ -1403,5 +1418,51 @@ class Vault(ServiceBase):
|
|
1403
1418
|
return self.request.post(
|
1404
1419
|
"v1/key/decrypt/transform",
|
1405
1420
|
DecryptTransformResult,
|
1406
|
-
data=input.
|
1421
|
+
data=input.model_dump(exclude_none=True),
|
1422
|
+
)
|
1423
|
+
|
1424
|
+
def export(
|
1425
|
+
self,
|
1426
|
+
id: str,
|
1427
|
+
version: int | None = None,
|
1428
|
+
encryption_key: str | None = None,
|
1429
|
+
encryption_algorithm: ExportEncryptionAlgorithm | None = None,
|
1430
|
+
) -> PangeaResponse[ExportResult]:
|
1431
|
+
"""
|
1432
|
+
Export
|
1433
|
+
|
1434
|
+
Export a symmetric or asymmetric key.
|
1435
|
+
|
1436
|
+
OperationId: vault_post_v1_export
|
1437
|
+
|
1438
|
+
Args:
|
1439
|
+
id: The ID of the item.
|
1440
|
+
version: The item version.
|
1441
|
+
encryption_key: Public key in pem format used to encrypt exported key(s).
|
1442
|
+
encryption_algorithm: The algorithm of the public key.
|
1443
|
+
|
1444
|
+
Raises:
|
1445
|
+
PangeaAPIException: If an API error happens.
|
1446
|
+
|
1447
|
+
Returns:
|
1448
|
+
A `PangeaResponse` where the exported key is returned in the
|
1449
|
+
`response.result` field. Available response fields can be found in
|
1450
|
+
our [API documentation](https://pangea.cloud/docs/api/vault#export).
|
1451
|
+
|
1452
|
+
Examples:
|
1453
|
+
exp_encrypted_resp = self.vault.export(
|
1454
|
+
id=id,
|
1455
|
+
version=1,
|
1456
|
+
encryption_key=rsa_pub_key_pem,
|
1457
|
+
encryption_algorithm=ExportEncryptionAlgorithm.RSA4096_OAEP_SHA512,
|
1458
|
+
)
|
1459
|
+
"""
|
1460
|
+
|
1461
|
+
input: ExportRequest = ExportRequest(
|
1462
|
+
id=id, version=version, encryption_algorithm=encryption_algorithm, encryption_key=encryption_key
|
1463
|
+
)
|
1464
|
+
return self.request.post(
|
1465
|
+
"v1/export",
|
1466
|
+
ExportResult,
|
1467
|
+
data=input.model_dump(exclude_none=True),
|
1407
1468
|
)
|
pangea/utils.py
CHANGED
@@ -3,10 +3,9 @@ import copy
|
|
3
3
|
import datetime
|
4
4
|
import io
|
5
5
|
import json
|
6
|
-
from collections import OrderedDict
|
7
6
|
from hashlib import new, sha1, sha256, sha512
|
8
7
|
|
9
|
-
from google_crc32c import Checksum as CRC32C # type: ignore[import]
|
8
|
+
from google_crc32c import Checksum as CRC32C # type: ignore[import-untyped]
|
10
9
|
from pydantic import BaseModel
|
11
10
|
|
12
11
|
|
@@ -34,20 +33,9 @@ def str2str_b64(data: str, encoding: str = "utf-8") -> str:
|
|
34
33
|
return base64.b64encode(data.encode(encoding)).decode("ascii")
|
35
34
|
|
36
35
|
|
37
|
-
def
|
38
|
-
|
39
|
-
|
40
|
-
else:
|
41
|
-
return data
|
42
|
-
|
43
|
-
|
44
|
-
def dict_order_keys_recursive(data: dict) -> OrderedDict:
|
45
|
-
if isinstance(data, dict):
|
46
|
-
for k, v in data.items():
|
47
|
-
if type(v) is dict:
|
48
|
-
data[k] = dict_order_keys_recursive(v)
|
49
|
-
|
50
|
-
return data # type: ignore[return-value]
|
36
|
+
def str_b64_2bytes(data: str) -> bytes:
|
37
|
+
data += "=" * ((4 - len(data) % 4) % 4) # add padding if needed
|
38
|
+
return base64.urlsafe_b64decode(data)
|
51
39
|
|
52
40
|
|
53
41
|
def canonicalize_nested_json(data: dict) -> dict:
|