pangea-sdk 1.3.0__py3-none-any.whl → 1.5.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/deep_verify.py +2 -2
- pangea/dump_audit.py +3 -9
- pangea/exceptions.py +40 -2
- pangea/request.py +40 -7
- pangea/response.py +8 -0
- pangea/services/__init__.py +1 -0
- pangea/services/audit/audit.py +28 -7
- pangea/services/audit/models.py +12 -0
- pangea/services/audit/util.py +6 -1
- pangea/services/intel.py +270 -82
- pangea/services/vault/models/asymmetric.py +67 -0
- pangea/services/vault/models/common.py +337 -0
- pangea/services/vault/models/secret.py +24 -0
- pangea/services/vault/models/symmetric.py +61 -0
- pangea/services/vault/vault.py +458 -0
- pangea/{tools_util.py → tools.py} +7 -9
- pangea/utils.py +22 -0
- {pangea_sdk-1.3.0.dist-info → pangea_sdk-1.5.0.dist-info}/METADATA +4 -3
- pangea_sdk-1.5.0.dist-info/RECORD +30 -0
- {pangea_sdk-1.3.0.dist-info → pangea_sdk-1.5.0.dist-info}/WHEEL +1 -1
- pangea_sdk-1.3.0.dist-info/RECORD +0 -24
@@ -0,0 +1,458 @@
|
|
1
|
+
# Copyright 2022 Pangea Cyber Corporation
|
2
|
+
# Author: Pangea Cyber Corporation
|
3
|
+
import datetime
|
4
|
+
from typing import Dict, Optional, Union
|
5
|
+
|
6
|
+
from pangea.response import PangeaResponse
|
7
|
+
from pangea.services.base import ServiceBase
|
8
|
+
from pangea.services.vault.models.asymmetric import (
|
9
|
+
AsymmetricGenerateRequest,
|
10
|
+
AsymmetricGenerateResult,
|
11
|
+
AsymmetricStoreRequest,
|
12
|
+
AsymmetricStoreResult,
|
13
|
+
SignRequest,
|
14
|
+
SignResult,
|
15
|
+
VerifyRequest,
|
16
|
+
VerifyResult,
|
17
|
+
)
|
18
|
+
from pangea.services.vault.models.common import (
|
19
|
+
AsymmetricAlgorithm,
|
20
|
+
DeleteRequest,
|
21
|
+
DeleteResult,
|
22
|
+
EncodedPrivateKey,
|
23
|
+
EncodedPublicKey,
|
24
|
+
EncodedSymmetricKey,
|
25
|
+
GetRequest,
|
26
|
+
GetResult,
|
27
|
+
ItemOrder,
|
28
|
+
ItemOrderBy,
|
29
|
+
ItemState,
|
30
|
+
ItemType,
|
31
|
+
ItemVersionState,
|
32
|
+
JWKGetRequest,
|
33
|
+
JWKGetResult,
|
34
|
+
JWTSignRequest,
|
35
|
+
JWTSignResult,
|
36
|
+
JWTVerifyRequest,
|
37
|
+
JWTVerifyResult,
|
38
|
+
KeyPurpose,
|
39
|
+
KeyRotateRequest,
|
40
|
+
KeyRotateResult,
|
41
|
+
ListRequest,
|
42
|
+
ListResult,
|
43
|
+
Metadata,
|
44
|
+
StateChangeRequest,
|
45
|
+
StateChangeResult,
|
46
|
+
SymmetricAlgorithm,
|
47
|
+
Tags,
|
48
|
+
UpdateRequest,
|
49
|
+
UpdateResult,
|
50
|
+
)
|
51
|
+
from pangea.services.vault.models.secret import (
|
52
|
+
SecretRotateRequest,
|
53
|
+
SecretRotateResult,
|
54
|
+
SecretStoreRequest,
|
55
|
+
SecretStoreResult,
|
56
|
+
)
|
57
|
+
from pangea.services.vault.models.symmetric import (
|
58
|
+
DecryptRequest,
|
59
|
+
DecryptResult,
|
60
|
+
EncryptRequest,
|
61
|
+
EncryptResult,
|
62
|
+
SymmetricGenerateRequest,
|
63
|
+
SymmetricGenerateResult,
|
64
|
+
SymmetricStoreRequest,
|
65
|
+
SymmetricStoreResult,
|
66
|
+
)
|
67
|
+
|
68
|
+
|
69
|
+
class Vault(ServiceBase):
|
70
|
+
"""Vault service client.
|
71
|
+
|
72
|
+
Provides methods to interact with the [Pangea Vault Service](https://pangea.cloud/docs/api/vault).
|
73
|
+
|
74
|
+
The following information is needed:
|
75
|
+
PANGEA_VAULT_TOKEN - service token which can be found on the Pangea User
|
76
|
+
Console at [https://console.pangea.cloud/project/tokens](https://console.pangea.cloud/project/tokens)
|
77
|
+
|
78
|
+
Examples:
|
79
|
+
import os
|
80
|
+
|
81
|
+
# Pangea SDK
|
82
|
+
from pangea.config import PangeaConfig
|
83
|
+
from pangea.services.vault import Vault
|
84
|
+
|
85
|
+
PANGEA_VAULT_TOKEN = os.getenv("PANGEA_VAULT_TOKEN")
|
86
|
+
vault_config = PangeaConfig(domain="pangea.cloud")
|
87
|
+
|
88
|
+
# Setup Pangea Vault service
|
89
|
+
vault = Vault(token=PANGEA_VAULT_TOKEN, config=audit_config)
|
90
|
+
"""
|
91
|
+
|
92
|
+
service_name: str = "vault"
|
93
|
+
version: str = "v1"
|
94
|
+
|
95
|
+
def __init__(
|
96
|
+
self,
|
97
|
+
token,
|
98
|
+
config=None,
|
99
|
+
logger_name="pangea",
|
100
|
+
):
|
101
|
+
super().__init__(token, config, logger_name)
|
102
|
+
|
103
|
+
# Delete endpoint
|
104
|
+
def delete(self, id: str) -> PangeaResponse[DeleteResult]:
|
105
|
+
input = DeleteRequest(
|
106
|
+
id=id,
|
107
|
+
)
|
108
|
+
response = self.request.post("delete", data=input.dict(exclude_none=True))
|
109
|
+
if response.raw_result is not None:
|
110
|
+
response.result = DeleteResult(**response.raw_result)
|
111
|
+
return response
|
112
|
+
|
113
|
+
# Get endpoint
|
114
|
+
def get(
|
115
|
+
self,
|
116
|
+
id: str,
|
117
|
+
version: Optional[Union[str, int]] = None,
|
118
|
+
version_state: Optional[ItemVersionState] = None,
|
119
|
+
verbose: Optional[bool] = None,
|
120
|
+
) -> PangeaResponse[GetResult]:
|
121
|
+
input = GetRequest(
|
122
|
+
id=id,
|
123
|
+
version=version,
|
124
|
+
verbose=verbose,
|
125
|
+
version_state=version_state,
|
126
|
+
)
|
127
|
+
response = self.request.post("get", data=input.dict(exclude_none=True))
|
128
|
+
if response.raw_result is not None:
|
129
|
+
response.result = GetResult(**response.raw_result)
|
130
|
+
return response
|
131
|
+
|
132
|
+
# List endpoint
|
133
|
+
def list(
|
134
|
+
self,
|
135
|
+
filter: Optional[Dict[str, str]] = None,
|
136
|
+
last: Optional[str] = None,
|
137
|
+
order: Optional[ItemOrder] = None,
|
138
|
+
order_by: Optional[ItemOrderBy] = None,
|
139
|
+
size: Optional[int] = None,
|
140
|
+
) -> PangeaResponse[ListResult]:
|
141
|
+
input = ListRequest(filter=filter, last=last, order=order, order_by=order_by, size=size)
|
142
|
+
response = self.request.post("list", data=input.dict(exclude_none=True))
|
143
|
+
|
144
|
+
if response.raw_result is not None:
|
145
|
+
response.result = ListResult(**response.raw_result)
|
146
|
+
return response
|
147
|
+
|
148
|
+
# Update endpoint
|
149
|
+
def update(
|
150
|
+
self,
|
151
|
+
id: str,
|
152
|
+
name: Optional[str] = None,
|
153
|
+
folder: Optional[str] = None,
|
154
|
+
metadata: Optional[Metadata] = None,
|
155
|
+
tags: Optional[Tags] = None,
|
156
|
+
rotation_frequency: Optional[str] = None,
|
157
|
+
rotation_state: Optional[ItemVersionState] = None,
|
158
|
+
rotation_grace_period: Optional[str] = None,
|
159
|
+
expiration: Optional[datetime.datetime] = None,
|
160
|
+
item_state: Optional[ItemState] = None,
|
161
|
+
) -> PangeaResponse[UpdateResult]:
|
162
|
+
input = UpdateRequest(
|
163
|
+
id=id,
|
164
|
+
name=name,
|
165
|
+
folder=folder,
|
166
|
+
metadata=metadata,
|
167
|
+
tags=tags,
|
168
|
+
rotation_frequency=rotation_frequency,
|
169
|
+
rotation_state=rotation_state,
|
170
|
+
rotation_grace_period=rotation_grace_period,
|
171
|
+
expiration=expiration,
|
172
|
+
item_state=item_state,
|
173
|
+
)
|
174
|
+
response = self.request.post("update", data=input.dict(exclude_none=True))
|
175
|
+
if response.raw_result is not None:
|
176
|
+
response.result = UpdateResult(**response.raw_result)
|
177
|
+
return response
|
178
|
+
|
179
|
+
def secret_store(
|
180
|
+
self,
|
181
|
+
secret: str,
|
182
|
+
name: str,
|
183
|
+
folder: Optional[str] = None,
|
184
|
+
metadata: Optional[Metadata] = None,
|
185
|
+
tags: Optional[Tags] = None,
|
186
|
+
rotation_frequency: Optional[str] = None,
|
187
|
+
rotation_state: Optional[ItemVersionState] = None,
|
188
|
+
expiration: Optional[datetime.datetime] = None,
|
189
|
+
) -> PangeaResponse[SecretStoreResult]:
|
190
|
+
input = SecretStoreRequest(
|
191
|
+
type=ItemType.SECRET,
|
192
|
+
secret=secret,
|
193
|
+
name=name,
|
194
|
+
folder=folder,
|
195
|
+
metadata=metadata,
|
196
|
+
tags=tags,
|
197
|
+
rotation_frequency=rotation_frequency,
|
198
|
+
rotation_state=rotation_state,
|
199
|
+
expiration=expiration,
|
200
|
+
)
|
201
|
+
response = self.request.post("secret/store", data=input.dict(exclude_none=True))
|
202
|
+
if response.raw_result is not None:
|
203
|
+
response.result = SecretStoreResult(**response.raw_result)
|
204
|
+
return response
|
205
|
+
|
206
|
+
def pangea_token_store(
|
207
|
+
self,
|
208
|
+
pangea_token: str,
|
209
|
+
name: str,
|
210
|
+
folder: Optional[str] = None,
|
211
|
+
metadata: Optional[Metadata] = None,
|
212
|
+
tags: Optional[Tags] = None,
|
213
|
+
rotation_frequency: Optional[str] = None,
|
214
|
+
rotation_state: Optional[ItemVersionState] = None,
|
215
|
+
expiration: Optional[datetime.datetime] = None,
|
216
|
+
) -> PangeaResponse[SecretStoreResult]:
|
217
|
+
input = SecretStoreRequest(
|
218
|
+
type=ItemType.PANGEA_TOKEN,
|
219
|
+
secret=pangea_token,
|
220
|
+
name=name,
|
221
|
+
folder=folder,
|
222
|
+
metadata=metadata,
|
223
|
+
tags=tags,
|
224
|
+
rotation_frequency=rotation_frequency,
|
225
|
+
rotation_state=rotation_state,
|
226
|
+
expiration=expiration,
|
227
|
+
)
|
228
|
+
response = self.request.post("secret/store", data=input.dict(exclude_none=True))
|
229
|
+
if response.raw_result is not None:
|
230
|
+
response.result = SecretStoreResult(**response.raw_result)
|
231
|
+
return response
|
232
|
+
|
233
|
+
# Rotate endpoint
|
234
|
+
def secret_rotate(
|
235
|
+
self, id: str, secret: str, rotation_state: Optional[ItemVersionState] = None
|
236
|
+
) -> PangeaResponse[SecretRotateResult]:
|
237
|
+
input = SecretRotateRequest(id=id, secret=secret, rotation_state=rotation_state)
|
238
|
+
response = self.request.post("secret/rotate", data=input.dict(exclude_none=True))
|
239
|
+
if response.raw_result is not None:
|
240
|
+
response.result = SecretRotateResult(**response.raw_result)
|
241
|
+
return response
|
242
|
+
|
243
|
+
# Rotate endpoint
|
244
|
+
def pangea_token_rotate(self, id: str) -> PangeaResponse[SecretRotateResult]:
|
245
|
+
input = SecretRotateRequest(id=id)
|
246
|
+
response = self.request.post("secret/rotate", data=input.dict(exclude_none=True))
|
247
|
+
if response.raw_result is not None:
|
248
|
+
response.result = SecretRotateResult(**response.raw_result)
|
249
|
+
return response
|
250
|
+
|
251
|
+
def symmetric_generate(
|
252
|
+
self,
|
253
|
+
algorithm: SymmetricAlgorithm,
|
254
|
+
purpose: KeyPurpose,
|
255
|
+
name: Optional[str] = None,
|
256
|
+
folder: Optional[str] = None,
|
257
|
+
metadata: Optional[Metadata] = None,
|
258
|
+
tags: Optional[Tags] = None,
|
259
|
+
rotation_frequency: Optional[str] = None,
|
260
|
+
rotation_state: Optional[ItemVersionState] = None,
|
261
|
+
expiration: Optional[datetime.datetime] = None,
|
262
|
+
) -> PangeaResponse[SymmetricGenerateResult]:
|
263
|
+
input = SymmetricGenerateRequest(
|
264
|
+
type=ItemType.SYMMETRIC_KEY,
|
265
|
+
algorithm=algorithm,
|
266
|
+
purpose=purpose,
|
267
|
+
name=name,
|
268
|
+
folder=folder,
|
269
|
+
metadata=metadata,
|
270
|
+
tags=tags,
|
271
|
+
rotation_frequency=rotation_frequency,
|
272
|
+
rotation_state=rotation_state,
|
273
|
+
expiration=expiration,
|
274
|
+
)
|
275
|
+
response = self.request.post("key/generate", data=input.dict(exclude_none=True))
|
276
|
+
if response.raw_result is not None:
|
277
|
+
response.result = SymmetricGenerateResult(**response.raw_result)
|
278
|
+
return response
|
279
|
+
|
280
|
+
def asymmetric_generate(
|
281
|
+
self,
|
282
|
+
algorithm: AsymmetricAlgorithm,
|
283
|
+
purpose: KeyPurpose,
|
284
|
+
name: Optional[str] = None,
|
285
|
+
folder: Optional[str] = None,
|
286
|
+
metadata: Optional[Metadata] = None,
|
287
|
+
tags: Optional[Tags] = None,
|
288
|
+
rotation_frequency: Optional[str] = None,
|
289
|
+
rotation_state: Optional[ItemVersionState] = None,
|
290
|
+
expiration: Optional[datetime.datetime] = None,
|
291
|
+
) -> PangeaResponse[AsymmetricGenerateResult]:
|
292
|
+
input = AsymmetricGenerateRequest(
|
293
|
+
type=ItemType.ASYMMETRIC_KEY,
|
294
|
+
algorithm=algorithm,
|
295
|
+
purpose=purpose,
|
296
|
+
name=name,
|
297
|
+
folder=folder,
|
298
|
+
metadata=metadata,
|
299
|
+
tags=tags,
|
300
|
+
rotation_frequency=rotation_frequency,
|
301
|
+
rotation_state=rotation_state,
|
302
|
+
expiration=expiration,
|
303
|
+
)
|
304
|
+
response = self.request.post("key/generate", data=input.dict(exclude_none=True))
|
305
|
+
if response.raw_result is not None:
|
306
|
+
response.result = AsymmetricGenerateResult(**response.raw_result)
|
307
|
+
return response
|
308
|
+
|
309
|
+
# Store endpoints
|
310
|
+
def asymmetric_store(
|
311
|
+
self,
|
312
|
+
private_key: EncodedPrivateKey,
|
313
|
+
public_key: EncodedPublicKey,
|
314
|
+
algorithm: AsymmetricAlgorithm,
|
315
|
+
purpose: KeyPurpose,
|
316
|
+
name: str,
|
317
|
+
folder: Optional[str] = None,
|
318
|
+
metadata: Optional[Metadata] = None,
|
319
|
+
tags: Optional[Tags] = None,
|
320
|
+
rotation_frequency: Optional[str] = None,
|
321
|
+
rotation_state: Optional[ItemVersionState] = None,
|
322
|
+
expiration: Optional[datetime.datetime] = None,
|
323
|
+
) -> PangeaResponse[AsymmetricStoreResult]:
|
324
|
+
input = AsymmetricStoreRequest(
|
325
|
+
type=ItemType.ASYMMETRIC_KEY,
|
326
|
+
algorithm=algorithm,
|
327
|
+
purpose=purpose,
|
328
|
+
public_key=public_key,
|
329
|
+
private_key=private_key,
|
330
|
+
name=name,
|
331
|
+
folder=folder,
|
332
|
+
metadata=metadata,
|
333
|
+
tags=tags,
|
334
|
+
rotation_frequency=rotation_frequency,
|
335
|
+
rotation_state=rotation_state,
|
336
|
+
expiration=expiration,
|
337
|
+
)
|
338
|
+
response = self.request.post("key/store", data=input.dict(exclude_none=True))
|
339
|
+
if response.raw_result is not None:
|
340
|
+
response.result = AsymmetricStoreResult(**response.raw_result)
|
341
|
+
return response
|
342
|
+
|
343
|
+
def symmetric_store(
|
344
|
+
self,
|
345
|
+
key: str,
|
346
|
+
algorithm: SymmetricAlgorithm,
|
347
|
+
purpose: KeyPurpose,
|
348
|
+
name: str,
|
349
|
+
folder: Optional[str] = None,
|
350
|
+
metadata: Optional[Metadata] = None,
|
351
|
+
tags: Optional[Tags] = None,
|
352
|
+
rotation_frequency: Optional[str] = None,
|
353
|
+
rotation_state: Optional[ItemVersionState] = None,
|
354
|
+
expiration: Optional[datetime.datetime] = None,
|
355
|
+
) -> PangeaResponse[SymmetricStoreResult]:
|
356
|
+
input = SymmetricStoreRequest(
|
357
|
+
type=ItemType.SYMMETRIC_KEY,
|
358
|
+
algorithm=algorithm,
|
359
|
+
purpose=purpose,
|
360
|
+
key=key,
|
361
|
+
name=name,
|
362
|
+
folder=folder,
|
363
|
+
metadata=metadata,
|
364
|
+
tags=tags,
|
365
|
+
rotation_frequency=rotation_frequency,
|
366
|
+
rotation_state=rotation_state,
|
367
|
+
expiration=expiration,
|
368
|
+
)
|
369
|
+
response = self.request.post("key/store", data=input.dict(exclude_none=True))
|
370
|
+
if response.raw_result is not None:
|
371
|
+
response.result = SymmetricStoreResult(**response.raw_result)
|
372
|
+
return response
|
373
|
+
|
374
|
+
# Rotate endpoint
|
375
|
+
def key_rotate(
|
376
|
+
self,
|
377
|
+
id: str,
|
378
|
+
rotation_state: ItemVersionState,
|
379
|
+
public_key: Optional[EncodedPublicKey] = None,
|
380
|
+
private_key: Optional[EncodedPrivateKey] = None,
|
381
|
+
key: Optional[EncodedSymmetricKey] = None,
|
382
|
+
) -> PangeaResponse[KeyRotateResult]:
|
383
|
+
input = KeyRotateRequest(
|
384
|
+
id=id, public_key=public_key, private_key=private_key, key=key, rotation_state=rotation_state
|
385
|
+
)
|
386
|
+
response = self.request.post("key/rotate", data=input.dict(exclude_none=True))
|
387
|
+
if response.raw_result is not None:
|
388
|
+
response.result = KeyRotateResult(**response.raw_result)
|
389
|
+
return response
|
390
|
+
|
391
|
+
# Encrypt/Decrypt
|
392
|
+
def encrypt(self, id: str, plain_text: str, version: Optional[int] = None) -> PangeaResponse[EncryptResult]:
|
393
|
+
input = EncryptRequest(id=id, plain_text=plain_text, version=version)
|
394
|
+
response = self.request.post("key/encrypt", data=input.dict(exclude_none=True))
|
395
|
+
if response.raw_result is not None:
|
396
|
+
response.result = EncryptResult(**response.raw_result)
|
397
|
+
return response
|
398
|
+
|
399
|
+
def decrypt(self, id: str, cipher_text: str, version: Optional[int] = None) -> PangeaResponse[DecryptResult]:
|
400
|
+
input = DecryptRequest(id=id, cipher_text=cipher_text, version=version)
|
401
|
+
response = self.request.post("key/decrypt", data=input.dict(exclude_none=True))
|
402
|
+
if response.raw_result is not None:
|
403
|
+
response.result = DecryptResult(**response.raw_result)
|
404
|
+
return response
|
405
|
+
|
406
|
+
# Sign/Verify endpoints
|
407
|
+
def sign(self, id: str, message: str, version: Optional[int] = None) -> PangeaResponse[SignResult]:
|
408
|
+
input = SignRequest(id=id, message=message, version=version)
|
409
|
+
response = self.request.post("key/sign", data=input.dict(exclude_none=True))
|
410
|
+
if response.raw_result is not None:
|
411
|
+
response.result = SignResult(**response.raw_result)
|
412
|
+
return response
|
413
|
+
|
414
|
+
def verify(
|
415
|
+
self, id: str, message: str, signature: str, version: Optional[int] = None
|
416
|
+
) -> PangeaResponse[VerifyResult]:
|
417
|
+
input = VerifyRequest(
|
418
|
+
id=id,
|
419
|
+
message=message,
|
420
|
+
signature=signature,
|
421
|
+
version=version,
|
422
|
+
)
|
423
|
+
response = self.request.post("key/verify", data=input.dict(exclude_none=True))
|
424
|
+
if response.raw_result is not None:
|
425
|
+
response.result = VerifyResult(**response.raw_result)
|
426
|
+
return response
|
427
|
+
|
428
|
+
def jwt_verify(self, jws: str) -> PangeaResponse[JWTVerifyResult]:
|
429
|
+
input = JWTVerifyRequest(jws=jws)
|
430
|
+
response = self.request.post("key/verify/jwt", data=input.dict(exclude_none=True))
|
431
|
+
if response.raw_result is not None:
|
432
|
+
response.result = JWTVerifyResult(**response.raw_result)
|
433
|
+
return response
|
434
|
+
|
435
|
+
def jwt_sign(self, id: str, payload: str) -> PangeaResponse[JWTSignResult]:
|
436
|
+
input = JWTSignRequest(id=id, payload=payload)
|
437
|
+
response = self.request.post("key/sign/jwt", data=input.dict(exclude_none=True))
|
438
|
+
if response.raw_result is not None:
|
439
|
+
response.result = JWTSignResult(**response.raw_result)
|
440
|
+
return response
|
441
|
+
|
442
|
+
# Get endpoint
|
443
|
+
def jwk_get(self, id: str, version: Optional[str] = None) -> PangeaResponse[JWKGetResult]:
|
444
|
+
input = JWKGetRequest(id=id, version=version)
|
445
|
+
response = self.request.post("get/jwk", data=input.dict(exclude_none=True))
|
446
|
+
if response.raw_result is not None:
|
447
|
+
response.result = JWKGetResult(**response.raw_result)
|
448
|
+
return response
|
449
|
+
|
450
|
+
# State change
|
451
|
+
def state_change(
|
452
|
+
self, id: str, state: ItemVersionState, version: Optional[int] = None, destroy_period: Optional[str] = None
|
453
|
+
) -> PangeaResponse[StateChangeResult]:
|
454
|
+
input = StateChangeRequest(id=id, state=state, version=version, destroy_period=destroy_period)
|
455
|
+
response = self.request.post("state/change", data=input.dict(exclude_none=True))
|
456
|
+
if response.raw_result is not None:
|
457
|
+
response.result = StateChangeResult(**response.raw_result)
|
458
|
+
return response
|
@@ -111,15 +111,6 @@ def make_aware_datetime(d: datetime) -> datetime:
|
|
111
111
|
return d
|
112
112
|
|
113
113
|
|
114
|
-
def json_defaults(obj):
|
115
|
-
if obj is None:
|
116
|
-
return obj
|
117
|
-
elif isinstance(obj, (datetime, date)):
|
118
|
-
return obj.isoformat().replace("+00:00", "Z")
|
119
|
-
else:
|
120
|
-
return str(obj)
|
121
|
-
|
122
|
-
|
123
114
|
def filter_deep_none(data: t.Dict) -> t.Dict:
|
124
115
|
return {k: v if not isinstance(v, t.Dict) else filter_deep_none(v) for k, v in data.items() if v is not None}
|
125
116
|
|
@@ -170,7 +161,14 @@ class SequenceFollower:
|
|
170
161
|
return [val for val in range(min_val, max_val) if val not in self.numbers]
|
171
162
|
|
172
163
|
|
164
|
+
loggers = {}
|
165
|
+
|
166
|
+
|
173
167
|
def logger_set_pangea_config(logger_name: str, level=logging.DEBUG):
|
168
|
+
if loggers.get(logger_name, None) is not None:
|
169
|
+
return
|
170
|
+
|
171
|
+
loggers[logger_name] = True
|
174
172
|
logger = logging.getLogger(logger_name)
|
175
173
|
logger.setLevel(level)
|
176
174
|
handler = TimedRotatingFileHandler(
|
pangea/utils.py
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
import base64
|
2
|
+
import datetime
|
3
|
+
|
4
|
+
|
5
|
+
def format_datetime(dt: datetime.datetime) -> str:
|
6
|
+
"""
|
7
|
+
Format a datetime in ISO format, using Z instead of +00:00
|
8
|
+
"""
|
9
|
+
if dt.tzinfo is None:
|
10
|
+
dt = dt.astimezone(datetime.timezone.utc)
|
11
|
+
return dt.isoformat(timespec="milliseconds").replace("+00:00", "Z")
|
12
|
+
|
13
|
+
|
14
|
+
def default_encoder(obj) -> str:
|
15
|
+
if isinstance(obj, datetime.datetime):
|
16
|
+
return format_datetime(obj)
|
17
|
+
else:
|
18
|
+
return str(obj)
|
19
|
+
|
20
|
+
|
21
|
+
def str2str_b64(data: str):
|
22
|
+
return base64.b64encode(data.encode("ascii")).decode("ascii")
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: pangea-sdk
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.5.0
|
4
4
|
Summary: Pangea API SDK
|
5
5
|
License: MIT
|
6
6
|
Keywords: Pangea,SDK,Audit
|
@@ -19,6 +19,7 @@ Requires-Dist: alive-progress (>=2.4.1,<3.0.0)
|
|
19
19
|
Requires-Dist: cryptography (==39.0.1)
|
20
20
|
Requires-Dist: deprecated (>=1.2.13,<2.0.0)
|
21
21
|
Requires-Dist: pydantic (>=1.10.2,<2.0.0)
|
22
|
+
Requires-Dist: pytest (>=7.2.0,<8.0.0)
|
22
23
|
Requires-Dist: python-dateutil (>=2.8.2,<3.0.0)
|
23
24
|
Requires-Dist: requests (>=2.27.1,<3.0.0)
|
24
25
|
Requires-Dist: schema (>=0.7.5,<0.8.0)
|
@@ -27,7 +28,7 @@ Description-Content-Type: text/markdown
|
|
27
28
|
<p>
|
28
29
|
<br />
|
29
30
|
<a href="https://pangea.cloud?utm_source=github&utm_medium=node-sdk" target="_blank" rel="noopener noreferrer">
|
30
|
-
<img src="https://pangea-marketing.s3.us-west-2.amazonaws.com/pangea-color.svg" alt="Pangea Logo" height="40"
|
31
|
+
<img src="https://pangea-marketing.s3.us-west-2.amazonaws.com/pangea-color.svg" alt="Pangea Logo" height="40" />
|
31
32
|
</a>
|
32
33
|
<br />
|
33
34
|
</p>
|
@@ -36,7 +37,7 @@ Description-Content-Type: text/markdown
|
|
36
37
|
<br />
|
37
38
|
|
38
39
|
[](https://pangea.cloud/docs/sdk/python/)
|
39
|
-
[](https://pangea.cloud/join-slack/)
|
40
41
|
|
41
42
|
<br />
|
42
43
|
</p>
|
@@ -0,0 +1,30 @@
|
|
1
|
+
pangea/__init__.py,sha256=Txf0txgiP6f2S5VGkqfM8wxk7WqIpHZw8PSeovMzHjc,146
|
2
|
+
pangea/audit_logger.py,sha256=zCSsq0kvw4Pb_aIBb7APfaYsTqd9dmohYLFld2ir5lc,3778
|
3
|
+
pangea/config.py,sha256=emCT7OnVeU6kwOHmqqO0O4V3RQS01xURpn0D85KobIM,906
|
4
|
+
pangea/deep_verify.py,sha256=O6B84gjRq0YEnhPrRhqt2C_m1N7dkJ5VzXUsPGqFa4M,8741
|
5
|
+
pangea/deprecated.py,sha256=mzOBW98DLHf_Cx-AQ3DO4_GaDkFiyyR9oeEqsG8Nopc,604
|
6
|
+
pangea/dump_audit.py,sha256=Ws0KuZyHoaySsQ2lq9EKK2iw65O8x4zL1Mii0ChDh0k,6511
|
7
|
+
pangea/exceptions.py,sha256=f7Z3fnGqqXeRJ768AEKoyuDFwpzEGZAaI2q2O16sgKQ,4251
|
8
|
+
pangea/request.py,sha256=7lLWWEYHDJ9LW6t4zEQmzIHpIiejZNxc1TToBUXvE40,9795
|
9
|
+
pangea/response.py,sha256=sRJxmqpepZkvPdSVTdC-iowYd_kxDiMeDq2ostp36hs,3625
|
10
|
+
pangea/services/__init__.py,sha256=JD836kMpuPVnZrMpvL9K9YgVz7k_xoCrpTf2jX6Twc0,179
|
11
|
+
pangea/services/audit/audit.py,sha256=hv_73BKyMzLCjroHGwV9cI2CbZ4Pdm-PlLA3fMPG3hI,24064
|
12
|
+
pangea/services/audit/exceptions.py,sha256=CVdaQZCvQKx1n-iIjWz5wnStUGU6cXDwKqe7MoijAXk,451
|
13
|
+
pangea/services/audit/models.py,sha256=65N5sNJm_-kfp4tDZnyx8ce-LWwJLjp5occ1NST6WXM,12093
|
14
|
+
pangea/services/audit/signing.py,sha256=47OaL4xtRFj2ffmjPYRsN2zLk8P7Dhfyu3oXbSc1gbI,4938
|
15
|
+
pangea/services/audit/util.py,sha256=Oi6wEdkZi-vrIKbs88HsOsq0O5Ju-rrUsxQEHKLOazc,8055
|
16
|
+
pangea/services/base.py,sha256=gqGHO-SbDw7ai02LI_5Y5tVFRIY8bh3k4VSyG9OgwC0,1010
|
17
|
+
pangea/services/embargo.py,sha256=ip27GTDsAc_4zJ8AhOKACTz2IHrJla-EkXvfvtcJ0QM,5565
|
18
|
+
pangea/services/intel.py,sha256=yRIhMWh14bwEnhhmkEaB9eTkzzTQzAIncr_sLVrvhco,27450
|
19
|
+
pangea/services/redact.py,sha256=XLvgerEinF0rentDiVt9VKSmM3JlhoYShDoiZx3otfI,6696
|
20
|
+
pangea/services/vault/models/asymmetric.py,sha256=ac2Exc66elXxO-HxBqtvLPQWNI7y_00kb6SVqBPKecA,1450
|
21
|
+
pangea/services/vault/models/common.py,sha256=0HENdBHaU1G0iqs8FROJMlvOEEvpvE5dSZgKhb3USIg,7561
|
22
|
+
pangea/services/vault/models/secret.py,sha256=cLgEj-_BeGkB4-pmSeTkWVyasFbaJwcEltIEcOyf1U8,481
|
23
|
+
pangea/services/vault/models/symmetric.py,sha256=z1bHT5LzgNz7oZ5ay7vFg3WaBZYkbwvPsdlUj2bg52E,1260
|
24
|
+
pangea/services/vault/vault.py,sha256=11gZegxHz1B7v9yu39xkE3ydJY1fHYFzbHDh-8AFH84,16743
|
25
|
+
pangea/tools.py,sha256=EIswiGFOZUhS0_Sc9ZLMsblqml9l5miyd3IyQhIy-Lw,5193
|
26
|
+
pangea/utils.py,sha256=XCrpAJ1ggK8vnkaeqwsz8WNjKz6y8VPZpoUAjVTc6v0,554
|
27
|
+
pangea/verify_audit.py,sha256=I43xhgW-3pZZ_AqcXlMkhXGPDkHVh3l2gpIhlDegh3c,10610
|
28
|
+
pangea_sdk-1.5.0.dist-info/METADATA,sha256=EguOi0K5Q2RyuudEn9N5V2iygpVV1YYXT2J5IjHqpUU,8760
|
29
|
+
pangea_sdk-1.5.0.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
|
30
|
+
pangea_sdk-1.5.0.dist-info/RECORD,,
|
@@ -1,24 +0,0 @@
|
|
1
|
-
pangea/__init__.py,sha256=WkSEQd9mB4TbB7l4z2t2NoGv3gMbMWfDgrfb01YBnDw,146
|
2
|
-
pangea/audit_logger.py,sha256=zCSsq0kvw4Pb_aIBb7APfaYsTqd9dmohYLFld2ir5lc,3778
|
3
|
-
pangea/config.py,sha256=emCT7OnVeU6kwOHmqqO0O4V3RQS01xURpn0D85KobIM,906
|
4
|
-
pangea/deep_verify.py,sha256=uFCy2gcCBo0Khn_PWqFySsDVQ29keouUMAqqfRw8_y8,8738
|
5
|
-
pangea/deprecated.py,sha256=mzOBW98DLHf_Cx-AQ3DO4_GaDkFiyyR9oeEqsG8Nopc,604
|
6
|
-
pangea/dump_audit.py,sha256=gP0JpJxnc902OvpntBzF0bXNoeDQan4ODoAPA3_RKjI,6517
|
7
|
-
pangea/exceptions.py,sha256=1R-E4o_yfunmB98pzetZHt69Bsxb8ucpHUttYoaSufM,3093
|
8
|
-
pangea/request.py,sha256=5xh_BGRVI3rHzVr7mX7nIeFCuZZVGg4DVC0TH9YLzoY,8383
|
9
|
-
pangea/response.py,sha256=RH-Yon6yMYau9tkzvzXUdVvkHhOHg2d16-M9gD55cCo,3335
|
10
|
-
pangea/services/__init__.py,sha256=xGcWHWsRZvTG6q0icrykyOmHnhTuYRZo1KW5_qiZ5BY,148
|
11
|
-
pangea/services/audit/audit.py,sha256=AEVioLUXYaaAh7rgf701_WE6EYg9SgrMQ_252T9LUZI,23552
|
12
|
-
pangea/services/audit/exceptions.py,sha256=CVdaQZCvQKx1n-iIjWz5wnStUGU6cXDwKqe7MoijAXk,451
|
13
|
-
pangea/services/audit/models.py,sha256=jH1rMKGZehs0VQTK_DK2yLvKrLBOeK4yfSwRVzrOWqQ,11871
|
14
|
-
pangea/services/audit/signing.py,sha256=47OaL4xtRFj2ffmjPYRsN2zLk8P7Dhfyu3oXbSc1gbI,4938
|
15
|
-
pangea/services/audit/util.py,sha256=_UEMVpUyXwDqmn2wFwlStNGxEAwZYpDo2EpWi-F5MGM,7931
|
16
|
-
pangea/services/base.py,sha256=gqGHO-SbDw7ai02LI_5Y5tVFRIY8bh3k4VSyG9OgwC0,1010
|
17
|
-
pangea/services/embargo.py,sha256=ip27GTDsAc_4zJ8AhOKACTz2IHrJla-EkXvfvtcJ0QM,5565
|
18
|
-
pangea/services/intel.py,sha256=s0_HCqO1y7__PzdGyKEY187f3nGXheFti72JbgudqLc,22506
|
19
|
-
pangea/services/redact.py,sha256=XLvgerEinF0rentDiVt9VKSmM3JlhoYShDoiZx3otfI,6696
|
20
|
-
pangea/tools_util.py,sha256=DVhugRP-wpu2T-kwTuK0VmaaT1Xp8FnneUarqYlkp5c,5276
|
21
|
-
pangea/verify_audit.py,sha256=I43xhgW-3pZZ_AqcXlMkhXGPDkHVh3l2gpIhlDegh3c,10610
|
22
|
-
pangea_sdk-1.3.0.dist-info/METADATA,sha256=saC_ezQ2Mhip0Fd0jtjd8mbU0xAE7HV27sDfqsbIsN0,8763
|
23
|
-
pangea_sdk-1.3.0.dist-info/WHEEL,sha256=kLuE8m1WYU0Ig0_YEGrXyTtiJvKPpLpDEiChiNyei5Y,88
|
24
|
-
pangea_sdk-1.3.0.dist-info/RECORD,,
|