pangea-sdk 3.7.0__py3-none-any.whl → 3.7.1__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 +117 -25
- pangea/asyncio/services/audit.py +97 -13
- pangea/asyncio/services/authn.py +15 -2
- pangea/asyncio/services/base.py +9 -6
- pangea/asyncio/services/file_scan.py +3 -4
- pangea/asyncio/services/intel.py +1 -2
- pangea/asyncio/services/redact.py +1 -2
- pangea/asyncio/services/vault.py +7 -8
- pangea/config.py +10 -18
- pangea/dump_audit.py +1 -0
- pangea/exceptions.py +8 -0
- pangea/request.py +153 -66
- pangea/response.py +39 -4
- pangea/services/audit/audit.py +98 -44
- pangea/services/audit/exceptions.py +1 -2
- pangea/services/audit/models.py +46 -21
- pangea/services/audit/signing.py +1 -0
- pangea/services/audit/util.py +1 -0
- pangea/services/authn/authn.py +18 -0
- pangea/services/authn/models.py +9 -9
- pangea/services/base.py +12 -9
- pangea/services/embargo.py +1 -2
- pangea/services/file_scan.py +3 -4
- pangea/services/intel.py +1 -2
- pangea/services/redact.py +1 -2
- pangea/services/vault/vault.py +6 -6
- pangea/utils.py +0 -1
- {pangea_sdk-3.7.0.dist-info → pangea_sdk-3.7.1.dist-info}/METADATA +24 -2
- pangea_sdk-3.7.1.dist-info/RECORD +44 -0
- pangea_sdk-3.7.0.dist-info/RECORD +0 -44
- {pangea_sdk-3.7.0.dist-info → pangea_sdk-3.7.1.dist-info}/WHEEL +0 -0
pangea/services/audit/audit.py
CHANGED
@@ -2,12 +2,15 @@
|
|
2
2
|
# Author: Pangea Cyber Corporation
|
3
3
|
import datetime
|
4
4
|
import json
|
5
|
-
from typing import Any, Dict, List, Optional, Union
|
5
|
+
from typing import Any, Dict, List, Optional, Sequence, Set, Tuple, Union
|
6
6
|
|
7
7
|
import pangea.exceptions as pexc
|
8
8
|
from pangea.response import PangeaResponse
|
9
9
|
from pangea.services.audit.exceptions import AuditException, EventCorruption
|
10
10
|
from pangea.services.audit.models import (
|
11
|
+
DownloadFormat,
|
12
|
+
DownloadRequest,
|
13
|
+
DownloadResult,
|
11
14
|
Event,
|
12
15
|
EventEnvelope,
|
13
16
|
EventVerification,
|
@@ -50,7 +53,7 @@ class AuditBase:
|
|
50
53
|
def __init__(
|
51
54
|
self, private_key_file: str = "", public_key_info: Dict[str, str] = {}, tenant_id: Optional[str] = None
|
52
55
|
):
|
53
|
-
self.pub_roots: Dict[int,
|
56
|
+
self.pub_roots: Dict[int, PublishedRoot] = {}
|
54
57
|
self.buffer_data: Optional[str] = None
|
55
58
|
self.signer: Optional[Signer] = Signer(private_key_file) if private_key_file else None
|
56
59
|
self.public_key_info = public_key_info
|
@@ -182,8 +185,6 @@ class AuditBase:
|
|
182
185
|
unpublished_root = response.result.unpublished_root # type: ignore[union-attr]
|
183
186
|
|
184
187
|
if verify_consistency:
|
185
|
-
self.update_published_roots(response.result) # type: ignore[arg-type]
|
186
|
-
|
187
188
|
for search_event in response.result.events: # type: ignore[union-attr]
|
188
189
|
# verify membership proofs
|
189
190
|
if self.can_verify_membership_proof(search_event):
|
@@ -201,22 +202,9 @@ class AuditBase:
|
|
201
202
|
|
202
203
|
return response
|
203
204
|
|
204
|
-
def
|
205
|
-
"""Fetches series of published root hashes from Arweave
|
206
|
-
|
207
|
-
This is used for subsequent calls to verify_consistency_proof(). Root hashes
|
208
|
-
are published on [Arweave](https://arweave.net).
|
209
|
-
|
210
|
-
Args:
|
211
|
-
result (SearchOutput): PangeaResponse object from previous call to audit.search()
|
212
|
-
|
213
|
-
Raises:
|
214
|
-
AuditException: If an audit based api exception happens
|
215
|
-
PangeaAPIException: If an API Error happens
|
216
|
-
"""
|
217
|
-
|
205
|
+
def _get_tree_sizes_and_roots(self, result: SearchResultOutput) -> Tuple[Set[int], Dict[int, PublishedRoot]]:
|
218
206
|
if not result.root:
|
219
|
-
return
|
207
|
+
return set(), {}
|
220
208
|
|
221
209
|
tree_sizes = set()
|
222
210
|
for search_event in result.events:
|
@@ -230,22 +218,11 @@ class AuditBase:
|
|
230
218
|
tree_sizes.difference_update(self.pub_roots.keys())
|
231
219
|
|
232
220
|
if tree_sizes:
|
233
|
-
arweave_roots = get_arweave_published_roots(result.root.tree_name, list(tree_sizes))
|
221
|
+
arweave_roots = get_arweave_published_roots(result.root.tree_name, list(tree_sizes))
|
234
222
|
else:
|
235
223
|
arweave_roots = {}
|
236
224
|
|
237
|
-
|
238
|
-
for tree_size in tree_sizes:
|
239
|
-
pub_root = None
|
240
|
-
if tree_size in arweave_roots:
|
241
|
-
pub_root = PublishedRoot(**arweave_roots[tree_size].dict(exclude_none=True))
|
242
|
-
pub_root.source = RootSource.ARWEAVE
|
243
|
-
elif self.allow_server_roots:
|
244
|
-
resp = self.root(tree_size=tree_size) # type: ignore[attr-defined]
|
245
|
-
if resp.success:
|
246
|
-
pub_root = PublishedRoot(**resp.result.data.dict(exclude_none=True))
|
247
|
-
pub_root.source = RootSource.PANGEA
|
248
|
-
self.pub_roots[tree_size] = pub_root # type: ignore[assignment]
|
225
|
+
return tree_sizes, arweave_roots
|
249
226
|
|
250
227
|
def can_verify_membership_proof(self, event: SearchEvent) -> bool:
|
251
228
|
"""
|
@@ -303,7 +280,7 @@ class AuditBase:
|
|
303
280
|
"""
|
304
281
|
return event.published and event.leaf_index is not None and event.leaf_index >= 0 # type: ignore[return-value]
|
305
282
|
|
306
|
-
def verify_consistency_proof(self, pub_roots: Dict[int,
|
283
|
+
def verify_consistency_proof(self, pub_roots: Dict[int, PublishedRoot], event: SearchEvent) -> bool:
|
307
284
|
"""
|
308
285
|
Verify consistency proof
|
309
286
|
|
@@ -329,7 +306,7 @@ class AuditBase:
|
|
329
306
|
return False
|
330
307
|
|
331
308
|
if not self.allow_server_roots and (
|
332
|
-
curr_root.source != RootSource.ARWEAVE or prev_root.source != RootSource.ARWEAVE
|
309
|
+
curr_root.source != RootSource.ARWEAVE or prev_root.source != RootSource.ARWEAVE
|
333
310
|
):
|
334
311
|
return False
|
335
312
|
|
@@ -518,8 +495,8 @@ class Audit(ServiceBase, AuditBase):
|
|
518
495
|
"""
|
519
496
|
|
520
497
|
input = self._get_log_request(event, sign_local=sign_local, verify=verify, verbose=verbose)
|
521
|
-
response = self.request.post("v1/log", LogResult, data=input.dict(exclude_none=True))
|
522
|
-
if response.success:
|
498
|
+
response: PangeaResponse[LogResult] = self.request.post("v1/log", LogResult, data=input.dict(exclude_none=True))
|
499
|
+
if response.success and response.result is not None:
|
523
500
|
self._process_log_result(response.result, verify=verify)
|
524
501
|
return response
|
525
502
|
|
@@ -557,9 +534,11 @@ class Audit(ServiceBase, AuditBase):
|
|
557
534
|
"""
|
558
535
|
|
559
536
|
input = self._get_log_request(events, sign_local=sign_local, verify=False, verbose=verbose)
|
560
|
-
response = self.request.post(
|
537
|
+
response: PangeaResponse[LogBulkResult] = self.request.post(
|
538
|
+
"v2/log", LogBulkResult, data=input.dict(exclude_none=True)
|
539
|
+
)
|
561
540
|
|
562
|
-
if response.success:
|
541
|
+
if response.success and response.result is not None:
|
563
542
|
for result in response.result.results:
|
564
543
|
self._process_log_result(result, verify=True)
|
565
544
|
return response
|
@@ -599,13 +578,13 @@ class Audit(ServiceBase, AuditBase):
|
|
599
578
|
|
600
579
|
try:
|
601
580
|
# Calling to v2 methods will return always a 202.
|
602
|
-
response = self.request.post(
|
581
|
+
response: PangeaResponse[LogBulkResult] = self.request.post(
|
603
582
|
"v2/log_async", LogBulkResult, data=input.dict(exclude_none=True), poll_result=False
|
604
583
|
)
|
605
584
|
except pexc.AcceptedRequestException as e:
|
606
585
|
return e.response
|
607
586
|
|
608
|
-
if response.success:
|
587
|
+
if response.success and response.result is not None:
|
609
588
|
for result in response.result.results:
|
610
589
|
self._process_log_result(result, verify=True)
|
611
590
|
return response
|
@@ -619,7 +598,7 @@ class Audit(ServiceBase, AuditBase):
|
|
619
598
|
end: Optional[Union[datetime.datetime, str]] = None,
|
620
599
|
limit: Optional[int] = None,
|
621
600
|
max_results: Optional[int] = None,
|
622
|
-
search_restriction: Optional[
|
601
|
+
search_restriction: Optional[Dict[str, Sequence[str]]] = None,
|
623
602
|
verbose: Optional[bool] = None,
|
624
603
|
verify_consistency: bool = False,
|
625
604
|
verify_events: bool = True,
|
@@ -648,7 +627,7 @@ class Audit(ServiceBase, AuditBase):
|
|
648
627
|
end (datetime, optional): An RFC-3339 formatted timestamp, or relative time adjustment from the current time.
|
649
628
|
limit (int, optional): Optional[int] = None,
|
650
629
|
max_results (int, optional): Maximum number of results to return.
|
651
|
-
search_restriction (
|
630
|
+
search_restriction (Dict[str, Sequence[str]], optional): A list of keys to restrict the search results to. Useful for partitioning data available to the query string.
|
652
631
|
verbose (bool, optional): If true, response include root and membership and consistency proofs.
|
653
632
|
verify_consistency (bool): True to verify logs consistency
|
654
633
|
verify_events (bool): True to verify hash events and signatures
|
@@ -687,7 +666,11 @@ class Audit(ServiceBase, AuditBase):
|
|
687
666
|
verbose=verbose,
|
688
667
|
)
|
689
668
|
|
690
|
-
response = self.request.post(
|
669
|
+
response: PangeaResponse[SearchOutput] = self.request.post(
|
670
|
+
"v1/search", SearchOutput, data=input.dict(exclude_none=True)
|
671
|
+
)
|
672
|
+
if verify_consistency and response.result is not None:
|
673
|
+
self.update_published_roots(response.result)
|
691
674
|
return self.handle_search_response(response, verify_consistency, verify_events)
|
692
675
|
|
693
676
|
def results(
|
@@ -695,6 +678,7 @@ class Audit(ServiceBase, AuditBase):
|
|
695
678
|
id: str,
|
696
679
|
limit: Optional[int] = 20,
|
697
680
|
offset: Optional[int] = 0,
|
681
|
+
assert_search_restriction: Optional[Dict[str, Sequence[str]]] = None,
|
698
682
|
verify_consistency: bool = False,
|
699
683
|
verify_events: bool = True,
|
700
684
|
) -> PangeaResponse[SearchResultOutput]:
|
@@ -709,6 +693,7 @@ class Audit(ServiceBase, AuditBase):
|
|
709
693
|
id (string): the id of a search action, found in `response.result.id`
|
710
694
|
limit (integer, optional): the maximum number of results to return, default is 20
|
711
695
|
offset (integer, optional): the position of the first result to return, default is 0
|
696
|
+
assert_search_restriction (Dict[str, Sequence[str]], optional): Assert the requested search results were queried with the exact same search restrictions, to ensure the results comply to the expected restrictions.
|
712
697
|
verify_consistency (bool): True to verify logs consistency
|
713
698
|
verify_events (bool): True to verify hash events and signatures
|
714
699
|
Raises:
|
@@ -733,8 +718,13 @@ class Audit(ServiceBase, AuditBase):
|
|
733
718
|
id=id,
|
734
719
|
limit=limit,
|
735
720
|
offset=offset,
|
721
|
+
assert_search_restriction=assert_search_restriction,
|
722
|
+
)
|
723
|
+
response: PangeaResponse[SearchResultOutput] = self.request.post(
|
724
|
+
"v1/results", SearchResultOutput, data=input.dict(exclude_none=True)
|
736
725
|
)
|
737
|
-
|
726
|
+
if verify_consistency and response.result is not None:
|
727
|
+
self.update_published_roots(response.result)
|
738
728
|
return self.handle_results_response(response, verify_consistency, verify_events)
|
739
729
|
|
740
730
|
def root(self, tree_size: Optional[int] = None) -> PangeaResponse[RootResult]:
|
@@ -760,3 +750,67 @@ class Audit(ServiceBase, AuditBase):
|
|
760
750
|
"""
|
761
751
|
input = RootRequest(tree_size=tree_size)
|
762
752
|
return self.request.post("v1/root", RootResult, data=input.dict(exclude_none=True))
|
753
|
+
|
754
|
+
def download_results(
|
755
|
+
self, result_id: str, format: Optional[DownloadFormat] = None
|
756
|
+
) -> PangeaResponse[DownloadResult]:
|
757
|
+
"""
|
758
|
+
Download search results
|
759
|
+
|
760
|
+
Get all search results as a compressed (gzip) CSV file.
|
761
|
+
|
762
|
+
OperationId: audit_post_v1_download_results
|
763
|
+
|
764
|
+
Args:
|
765
|
+
result_id: ID returned by the search API.
|
766
|
+
format: Format for the records.
|
767
|
+
|
768
|
+
Returns:
|
769
|
+
URL where search results can be downloaded.
|
770
|
+
|
771
|
+
Raises:
|
772
|
+
AuditException: If an Audit-based API exception occurs.
|
773
|
+
PangeaAPIException: If an API exception occurs.
|
774
|
+
|
775
|
+
Examples:
|
776
|
+
response = audit.download_results(
|
777
|
+
result_id="pas_[...]",
|
778
|
+
format=DownloadFormat.JSON,
|
779
|
+
)
|
780
|
+
"""
|
781
|
+
|
782
|
+
input = DownloadRequest(result_id=result_id, format=format)
|
783
|
+
return self.request.post("v1/download_results", DownloadResult, data=input.dict(exclude_none=True))
|
784
|
+
|
785
|
+
def update_published_roots(self, result: SearchResultOutput):
|
786
|
+
"""Fetches series of published root hashes from Arweave
|
787
|
+
|
788
|
+
This is used for subsequent calls to verify_consistency_proof(). Root hashes
|
789
|
+
are published on [Arweave](https://arweave.net).
|
790
|
+
|
791
|
+
Args:
|
792
|
+
result (SearchResultOutput): Result object from previous call to Audit.search() or Audit.results()
|
793
|
+
|
794
|
+
Raises:
|
795
|
+
AuditException: If an audit based api exception happens
|
796
|
+
PangeaAPIException: If an API Error happens
|
797
|
+
"""
|
798
|
+
|
799
|
+
if not result.root:
|
800
|
+
return
|
801
|
+
|
802
|
+
tree_sizes, arweave_roots = self._get_tree_sizes_and_roots(result)
|
803
|
+
|
804
|
+
# fill the missing roots from the server (if allowed)
|
805
|
+
for tree_size in tree_sizes:
|
806
|
+
pub_root = None
|
807
|
+
if tree_size in arweave_roots:
|
808
|
+
pub_root = PublishedRoot(**arweave_roots[tree_size].dict(exclude_none=True))
|
809
|
+
pub_root.source = RootSource.ARWEAVE
|
810
|
+
elif self.allow_server_roots:
|
811
|
+
resp = self.root(tree_size=tree_size)
|
812
|
+
if resp.success and resp.result is not None:
|
813
|
+
pub_root = PublishedRoot(**resp.result.data.dict(exclude_none=True))
|
814
|
+
pub_root.source = RootSource.PANGEA
|
815
|
+
if pub_root is not None:
|
816
|
+
self.pub_roots[tree_size] = pub_root
|
pangea/services/audit/models.py
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
# Author: Pangea Cyber Corporation
|
3
3
|
import datetime
|
4
4
|
import enum
|
5
|
-
from typing import Any, Dict, List, Optional, Union
|
5
|
+
from typing import Any, Dict, List, Optional, Sequence, Union
|
6
6
|
|
7
7
|
from pangea.response import APIRequestModel, APIResponseModel, PangeaResponseResult
|
8
8
|
|
@@ -206,7 +206,7 @@ class SearchRestriction(APIResponseModel):
|
|
206
206
|
Arguments:
|
207
207
|
actor -- List of actors to include in search. If empty include all.
|
208
208
|
action -- List of action to include in search. If empty include all.
|
209
|
-
source -- List of
|
209
|
+
source -- List of sources to include in search. If empty include all.
|
210
210
|
status -- List of status to include in search. If empty include all.
|
211
211
|
target -- List of targets to include in search. If empty include all.
|
212
212
|
"""
|
@@ -279,7 +279,7 @@ class SearchRequest(APIRequestModel):
|
|
279
279
|
end: Optional[str] = None
|
280
280
|
limit: Optional[int] = None
|
281
281
|
max_results: Optional[int] = None
|
282
|
-
search_restriction: Optional[
|
282
|
+
search_restriction: Optional[Dict[str, Sequence[str]]] = None
|
283
283
|
verbose: Optional[bool] = None
|
284
284
|
|
285
285
|
|
@@ -373,7 +373,24 @@ class SearchEvent(APIResponseModel):
|
|
373
373
|
signature_verification: EventVerification = EventVerification.NONE
|
374
374
|
|
375
375
|
|
376
|
-
class
|
376
|
+
class SearchResultOutput(PangeaResponseResult):
|
377
|
+
"""
|
378
|
+
Return class after a pagination search
|
379
|
+
|
380
|
+
Arguments:
|
381
|
+
count -- The total number of results that were returned by the search.
|
382
|
+
events -- A list of matching audit records.
|
383
|
+
root -- A root of a Merkle Tree.
|
384
|
+
unpublished_root -- Root of a unpublished Merkle Tree
|
385
|
+
"""
|
386
|
+
|
387
|
+
count: int
|
388
|
+
events: List[SearchEvent]
|
389
|
+
root: Optional[Root] = None
|
390
|
+
unpublished_root: Optional[Root] = None
|
391
|
+
|
392
|
+
|
393
|
+
class SearchOutput(SearchResultOutput):
|
377
394
|
"""
|
378
395
|
Result class after an audit search action
|
379
396
|
|
@@ -386,12 +403,8 @@ class SearchOutput(PangeaResponseResult):
|
|
386
403
|
unpublished_root -- Root of a unpublished Merkle Tree
|
387
404
|
"""
|
388
405
|
|
389
|
-
count: int
|
390
|
-
events: List[SearchEvent]
|
391
406
|
id: str
|
392
407
|
expires_at: datetime.datetime
|
393
|
-
root: Optional[Root] = None
|
394
|
-
unpublished_root: Optional[Root] = None
|
395
408
|
|
396
409
|
|
397
410
|
class SearchResultRequest(APIRequestModel):
|
@@ -402,25 +415,37 @@ class SearchResultRequest(APIRequestModel):
|
|
402
415
|
id -- A search results identifier returned by the search call.
|
403
416
|
limit -- Number of audit records to include from the first page of the results.
|
404
417
|
offset -- Offset from the start of the result set to start returning results from.
|
418
|
+
assert_search_restriction -- Assert the requested search results were queried with the exact same search restrictions, to ensure the results comply to the expected restrictions.
|
405
419
|
"""
|
406
420
|
|
407
421
|
id: str
|
408
422
|
limit: Optional[int] = 20
|
409
423
|
offset: Optional[int] = 0
|
424
|
+
assert_search_restriction: Optional[Dict[str, Sequence[str]]] = None
|
410
425
|
|
411
426
|
|
412
|
-
class
|
413
|
-
""
|
414
|
-
|
427
|
+
class DownloadFormat(str, enum.Enum):
|
428
|
+
JSON = "json"
|
429
|
+
"""JSON."""
|
415
430
|
|
416
|
-
|
417
|
-
|
418
|
-
events -- A list of matching audit records.
|
419
|
-
root -- A root of a Merkle Tree.
|
420
|
-
unpublished_root -- Root of a unpublished Merkle Tree
|
421
|
-
"""
|
431
|
+
CSV = "csv"
|
432
|
+
"""CSV."""
|
422
433
|
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
434
|
+
def __str__(self):
|
435
|
+
return str(self.value)
|
436
|
+
|
437
|
+
def __repr__(self):
|
438
|
+
return str(self.value)
|
439
|
+
|
440
|
+
|
441
|
+
class DownloadRequest(APIRequestModel):
|
442
|
+
result_id: str
|
443
|
+
"""ID returned by the search API."""
|
444
|
+
|
445
|
+
format: Optional[str] = None
|
446
|
+
"""Format for the records."""
|
447
|
+
|
448
|
+
|
449
|
+
class DownloadResult(PangeaResponseResult):
|
450
|
+
dest_url: str
|
451
|
+
"""URL where search results can be downloaded."""
|
pangea/services/audit/signing.py
CHANGED
@@ -7,6 +7,7 @@ from cryptography import exceptions
|
|
7
7
|
from cryptography.hazmat.primitives import serialization
|
8
8
|
from cryptography.hazmat.primitives.asymmetric import ed25519
|
9
9
|
from cryptography.hazmat.primitives.asymmetric.types import PrivateKeyTypes, PublicKeyTypes
|
10
|
+
|
10
11
|
from pangea.exceptions import PangeaException
|
11
12
|
from pangea.services.audit.util import b64decode, b64decode_ascii, b64encode_ascii
|
12
13
|
from pangea.services.vault.models.common import AsymmetricAlgorithm
|
pangea/services/audit/util.py
CHANGED
pangea/services/authn/authn.py
CHANGED
@@ -113,6 +113,10 @@ class AuthN(ServiceBase):
|
|
113
113
|
Examples:
|
114
114
|
response = authn.session.list()
|
115
115
|
"""
|
116
|
+
|
117
|
+
if isinstance(filter, dict):
|
118
|
+
filter = m.SessionListFilter(**filter)
|
119
|
+
|
116
120
|
input = m.SessionListRequest(filter=filter, last=last, order=order, order_by=order_by, size=size)
|
117
121
|
return self.request.post("v2/session/list", m.SessionListResults, data=input.dict(exclude_none=True))
|
118
122
|
|
@@ -267,6 +271,10 @@ class AuthN(ServiceBase):
|
|
267
271
|
token="ptu_wuk7tvtpswyjtlsx52b7yyi2l7zotv4a",
|
268
272
|
)
|
269
273
|
"""
|
274
|
+
|
275
|
+
if isinstance(filter, dict):
|
276
|
+
filter = m.SessionListFilter(**filter)
|
277
|
+
|
270
278
|
input = m.ClientSessionListRequest(
|
271
279
|
token=token, filter=filter, last=last, order=order, order_by=order_by, size=size
|
272
280
|
)
|
@@ -589,6 +597,10 @@ class AuthN(ServiceBase):
|
|
589
597
|
Examples:
|
590
598
|
response = authn.user.list()
|
591
599
|
"""
|
600
|
+
|
601
|
+
if isinstance(filter, dict):
|
602
|
+
filter = m.UserListFilter(**filter)
|
603
|
+
|
592
604
|
input = m.UserListRequest(
|
593
605
|
filter=filter,
|
594
606
|
last=last,
|
@@ -638,6 +650,9 @@ class AuthN(ServiceBase):
|
|
638
650
|
Examples:
|
639
651
|
response = authn.user.invites.list()
|
640
652
|
"""
|
653
|
+
if isinstance(filter, dict):
|
654
|
+
filter = m.UserInviteListFilter(**filter)
|
655
|
+
|
641
656
|
input = m.UserInviteListRequest(filter=filter, last=last, order=order, order_by=order_by, size=size)
|
642
657
|
return self.request.post(
|
643
658
|
"v2/user/invite/list", m.UserInviteListResult, data=input.dict(exclude_none=True)
|
@@ -1061,6 +1076,9 @@ class AuthN(ServiceBase):
|
|
1061
1076
|
response = authn.agreements.list()
|
1062
1077
|
"""
|
1063
1078
|
|
1079
|
+
if isinstance(filter, dict):
|
1080
|
+
filter = m.AgreementListFilter(**filter)
|
1081
|
+
|
1064
1082
|
input = m.AgreementListRequest(filter=filter, last=last, order=order, order_by=order_by, size=size)
|
1065
1083
|
return self.request.post("v2/agreements/list", m.AgreementListResult, data=input.dict(exclude_none=True))
|
1066
1084
|
|
pangea/services/authn/models.py
CHANGED
@@ -51,7 +51,7 @@ class Intelligence(PangeaResponseResult):
|
|
51
51
|
user_intel: bool
|
52
52
|
|
53
53
|
|
54
|
-
class SessionToken(
|
54
|
+
class SessionToken(PangeaResponseResult):
|
55
55
|
id: str
|
56
56
|
type: str
|
57
57
|
life: int
|
@@ -68,8 +68,8 @@ class LoginToken(SessionToken):
|
|
68
68
|
token: str
|
69
69
|
|
70
70
|
|
71
|
-
class ClientTokenCheckResult(
|
72
|
-
|
71
|
+
class ClientTokenCheckResult(SessionToken):
|
72
|
+
token: Optional[str]
|
73
73
|
|
74
74
|
|
75
75
|
class IDProvider(str, enum.Enum):
|
@@ -234,7 +234,7 @@ class UserListFilter(APIRequestModel):
|
|
234
234
|
|
235
235
|
|
236
236
|
class UserListRequest(APIRequestModel):
|
237
|
-
filter: Optional[
|
237
|
+
filter: Optional[UserListFilter] = None
|
238
238
|
last: Optional[str] = None
|
239
239
|
order: Optional[ItemOrder] = None
|
240
240
|
order_by: Optional[UserListOrderBy] = None
|
@@ -289,7 +289,7 @@ class UserInviterOrderBy(enum.Enum):
|
|
289
289
|
|
290
290
|
|
291
291
|
class UserInviteListFilter(APIRequestModel):
|
292
|
-
callback: Optional[str]
|
292
|
+
callback: Optional[str]
|
293
293
|
callback__contains: Optional[List[str]] = None
|
294
294
|
callback__in: Optional[List[str]] = None
|
295
295
|
created_at: Optional[str] = None
|
@@ -322,7 +322,7 @@ class UserInviteListFilter(APIRequestModel):
|
|
322
322
|
|
323
323
|
|
324
324
|
class UserInviteListRequest(APIRequestModel):
|
325
|
-
filter: Optional[
|
325
|
+
filter: Optional[UserInviteListFilter] = None
|
326
326
|
last: Optional[str] = None
|
327
327
|
order: Optional[ItemOrder] = None
|
328
328
|
order_by: Optional[UserInviterOrderBy] = None
|
@@ -607,7 +607,7 @@ class SessionListFilter(APIRequestModel):
|
|
607
607
|
|
608
608
|
class ClientSessionListRequest(APIRequestModel):
|
609
609
|
token: str
|
610
|
-
filter: Optional[
|
610
|
+
filter: Optional[SessionListFilter] = None
|
611
611
|
last: Optional[str] = None
|
612
612
|
order: Optional[ItemOrder] = None
|
613
613
|
order_by: Optional[SessionListOrderBy] = None
|
@@ -663,7 +663,7 @@ class SessionInvalidateResult(PangeaResponseResult):
|
|
663
663
|
|
664
664
|
|
665
665
|
class SessionListRequest(APIRequestModel):
|
666
|
-
filter: Optional[
|
666
|
+
filter: Optional[SessionListFilter] = None
|
667
667
|
last: Optional[str] = None
|
668
668
|
order: Optional[ItemOrder] = None
|
669
669
|
order_by: Optional[SessionListOrderBy] = None
|
@@ -760,7 +760,7 @@ class AgreementListFilter(APIRequestModel):
|
|
760
760
|
|
761
761
|
|
762
762
|
class AgreementListRequest(APIRequestModel):
|
763
|
-
filter: Optional[
|
763
|
+
filter: Optional[AgreementListFilter] = None
|
764
764
|
last: Optional[str] = None
|
765
765
|
order: Optional[ItemOrder] = None
|
766
766
|
order_by: Optional[AgreementListOrderBy] = None
|
pangea/services/base.py
CHANGED
@@ -3,13 +3,13 @@
|
|
3
3
|
|
4
4
|
import copy
|
5
5
|
import logging
|
6
|
-
from typing import Optional, Type, Union
|
6
|
+
from typing import Dict, Optional, Type, Union
|
7
7
|
|
8
8
|
from pangea.asyncio.request import PangeaRequestAsync
|
9
9
|
from pangea.config import PangeaConfig
|
10
10
|
from pangea.exceptions import AcceptedRequestException
|
11
11
|
from pangea.request import PangeaRequest
|
12
|
-
from pangea.response import PangeaResponse, PangeaResponseResult
|
12
|
+
from pangea.response import AttachedFile, PangeaResponse, PangeaResponseResult
|
13
13
|
|
14
14
|
|
15
15
|
class ServiceBase(object):
|
@@ -21,12 +21,12 @@ class ServiceBase(object):
|
|
21
21
|
if not token:
|
22
22
|
raise Exception("No token provided")
|
23
23
|
|
24
|
-
self.config =
|
24
|
+
self.config = copy.deepcopy(config) if config is not None else PangeaConfig()
|
25
25
|
self.logger = logging.getLogger(logger_name)
|
26
26
|
self._token = token
|
27
|
-
self.config_id: Optional[
|
28
|
-
self._request: Union[PangeaRequest, PangeaRequestAsync] = None
|
29
|
-
extra_headers = {}
|
27
|
+
self.config_id: Optional[str] = config_id
|
28
|
+
self._request: Optional[Union[PangeaRequest, PangeaRequestAsync]] = None
|
29
|
+
extra_headers: Dict = {}
|
30
30
|
self.request.set_extra_headers(extra_headers)
|
31
31
|
|
32
32
|
@property
|
@@ -38,8 +38,8 @@ class ServiceBase(object):
|
|
38
38
|
self._token = value
|
39
39
|
|
40
40
|
@property
|
41
|
-
def request(self):
|
42
|
-
if
|
41
|
+
def request(self) -> PangeaRequest:
|
42
|
+
if self._request is None or isinstance(self._request, PangeaRequestAsync):
|
43
43
|
self._request = PangeaRequest(
|
44
44
|
config=self.config,
|
45
45
|
token=self.token,
|
@@ -55,7 +55,7 @@ class ServiceBase(object):
|
|
55
55
|
exception: Optional[AcceptedRequestException] = None,
|
56
56
|
response: Optional[PangeaResponse] = None,
|
57
57
|
request_id: Optional[str] = None,
|
58
|
-
result_class: Union[Type[PangeaResponseResult],
|
58
|
+
result_class: Union[Type[PangeaResponseResult], Type[Dict]] = dict,
|
59
59
|
) -> PangeaResponse:
|
60
60
|
"""
|
61
61
|
Poll result
|
@@ -82,3 +82,6 @@ class ServiceBase(object):
|
|
82
82
|
return self.request.poll_result_by_id(request_id=request_id, result_class=result_class, check_response=True)
|
83
83
|
else:
|
84
84
|
raise AttributeError("Need to set exception, response or request_id")
|
85
|
+
|
86
|
+
def download_file(self, url: str, filename: Optional[str] = None) -> AttachedFile:
|
87
|
+
return self.request.download_file(url=url, filename=filename)
|
pangea/services/embargo.py
CHANGED
@@ -3,8 +3,7 @@
|
|
3
3
|
from typing import Any, Dict, List
|
4
4
|
|
5
5
|
from pangea.response import APIRequestModel, APIResponseModel, PangeaResponse, PangeaResponseResult
|
6
|
-
|
7
|
-
from .base import ServiceBase
|
6
|
+
from pangea.services.base import ServiceBase
|
8
7
|
|
9
8
|
|
10
9
|
class IPCheckRequest(APIRequestModel):
|
pangea/services/file_scan.py
CHANGED
@@ -2,14 +2,13 @@
|
|
2
2
|
# Author: Pangea Cyber Corporation
|
3
3
|
import io
|
4
4
|
import logging
|
5
|
-
from typing import Dict, List, Optional
|
5
|
+
from typing import Dict, List, Optional, Tuple
|
6
6
|
|
7
7
|
from pangea.request import PangeaConfig, PangeaRequest
|
8
8
|
from pangea.response import APIRequestModel, PangeaResponse, PangeaResponseResult, TransferMethod
|
9
|
+
from pangea.services.base import ServiceBase
|
9
10
|
from pangea.utils import FileUploadParams, get_file_upload_params
|
10
11
|
|
11
|
-
from .base import ServiceBase
|
12
|
-
|
13
12
|
|
14
13
|
class FileScanRequest(APIRequestModel):
|
15
14
|
"""
|
@@ -129,7 +128,7 @@ class FileScan(ServiceBase):
|
|
129
128
|
size = params.size
|
130
129
|
else:
|
131
130
|
crc, sha, size = None, None, None
|
132
|
-
files = [("upload", ("filename", file, "application/octet-stream"))]
|
131
|
+
files: List[Tuple] = [("upload", ("filename", file, "application/octet-stream"))]
|
133
132
|
else:
|
134
133
|
raise ValueError("Need to set file_path or file arguments")
|
135
134
|
|
pangea/services/intel.py
CHANGED
@@ -6,10 +6,9 @@ from typing import Dict, List, Optional
|
|
6
6
|
|
7
7
|
from pangea.exceptions import PangeaException
|
8
8
|
from pangea.response import APIRequestModel, PangeaResponse, PangeaResponseResult
|
9
|
+
from pangea.services.base import ServiceBase
|
9
10
|
from pangea.utils import hash_256_filepath
|
10
11
|
|
11
|
-
from .base import ServiceBase
|
12
|
-
|
13
12
|
|
14
13
|
class IntelCommonRequest(APIRequestModel):
|
15
14
|
"""
|
pangea/services/redact.py
CHANGED
@@ -5,8 +5,7 @@ import enum
|
|
5
5
|
from typing import Dict, List, Optional, Union
|
6
6
|
|
7
7
|
from pangea.response import APIRequestModel, APIResponseModel, PangeaResponse, PangeaResponseResult
|
8
|
-
|
9
|
-
from .base import ServiceBase
|
8
|
+
from pangea.services.base import ServiceBase
|
10
9
|
|
11
10
|
|
12
11
|
class RedactFormat(str, enum.Enum):
|