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.
@@ -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.dict(exclude_none=True))
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.dict(exclude_none=True))
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.dict(exclude_none=True))
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.dict(exclude_none=True))
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.dict(exclude_none=True))
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.dict(exclude_none=True))
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.dict(exclude_none=True))
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.dict(exclude_none=True))
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.dict(exclude_none=True),
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.dict(exclude_none=True),
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.dict(exclude_none=True))
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.dict(exclude_none=True))
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.dict(exclude_none=True))
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) # type: ignore[call-arg]
948
- return self.request.post("v1/key/encrypt", EncryptResult, data=input.dict(exclude_none=True))
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) # type: ignore[call-arg]
980
- return self.request.post("v1/key/decrypt", DecryptResult, data=input.dict(exclude_none=True))
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.dict(exclude_none=True))
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.dict(exclude_none=True))
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.dict(exclude_none=True))
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.dict(exclude_none=True))
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.dict(exclude_none=True))
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.dict(exclude_none=True))
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.dict(exclude_none=True))
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.dict(exclude_none=True),
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.dict(exclude_none=True),
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.dict(exclude_none=True),
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.dict(exclude_none=True),
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 dict_order_keys(data: dict) -> OrderedDict:
38
- if isinstance(data, dict):
39
- return OrderedDict(sorted(data.items()))
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: