pangea-sdk 3.8.0__tar.gz → 3.8.0b2__tar.gz
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_sdk-3.8.0 → pangea_sdk-3.8.0b2}/PKG-INFO +15 -31
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/README.md +12 -27
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/__init__.py +2 -1
- pangea_sdk-3.8.0b2/pangea/asyncio/__init__.py +1 -0
- pangea_sdk-3.8.0b2/pangea/asyncio/file_uploader.py +39 -0
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/asyncio/request.py +19 -14
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/asyncio/services/__init__.py +2 -1
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/asyncio/services/audit.py +14 -149
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/asyncio/services/authn.py +48 -64
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/asyncio/services/intel.py +4 -4
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/asyncio/services/redact.py +1 -20
- pangea_sdk-3.8.0b2/pangea/asyncio/services/sanitize.py +185 -0
- pangea_sdk-3.8.0b2/pangea/asyncio/services/share.py +573 -0
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/asyncio/services/vault.py +4 -21
- pangea_sdk-3.8.0b2/pangea/file_uploader.py +35 -0
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/request.py +21 -24
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/response.py +28 -27
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/services/__init__.py +2 -1
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/services/audit/audit.py +16 -151
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/services/audit/models.py +5 -44
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/services/authn/authn.py +4 -20
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/services/base.py +6 -23
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/services/file_scan.py +0 -1
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/services/intel.py +2 -2
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/services/redact.py +1 -20
- pangea_sdk-3.8.0b2/pangea/services/sanitize.py +275 -0
- pangea_sdk-3.8.0b2/pangea/services/share/file_format.py +170 -0
- pangea_sdk-3.8.0b2/pangea/services/share/share.py +877 -0
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/services/vault/vault.py +6 -23
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/utils.py +91 -19
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pyproject.toml +3 -4
- pangea_sdk-3.8.0/pangea/asyncio/services/authz.py +0 -259
- pangea_sdk-3.8.0/pangea/services/authz.py +0 -377
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/asyncio/services/base.py +0 -0
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/asyncio/services/embargo.py +0 -0
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/asyncio/services/file_scan.py +0 -0
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/audit_logger.py +0 -0
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/config.py +0 -0
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/deep_verify.py +0 -0
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/deprecated.py +0 -0
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/dump_audit.py +0 -0
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/exceptions.py +0 -0
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/py.typed +0 -0
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/services/audit/exceptions.py +0 -0
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/services/audit/signing.py +0 -0
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/services/audit/util.py +0 -0
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/services/authn/models.py +0 -0
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/services/embargo.py +0 -0
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/services/vault/models/asymmetric.py +0 -0
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/services/vault/models/common.py +0 -0
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/services/vault/models/secret.py +0 -0
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/services/vault/models/symmetric.py +0 -0
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/tools.py +0 -0
- {pangea_sdk-3.8.0 → pangea_sdk-3.8.0b2}/pangea/verify_audit.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: pangea-sdk
|
3
|
-
Version: 3.8.
|
3
|
+
Version: 3.8.0b2
|
4
4
|
Summary: Pangea API SDK
|
5
5
|
Home-page: https://pangea.cloud/docs/sdk/python/
|
6
6
|
License: MIT
|
@@ -18,14 +18,13 @@ Classifier: Topic :: Software Development
|
|
18
18
|
Classifier: Topic :: Software Development :: Libraries
|
19
19
|
Requires-Dist: aiohttp (>=3.8.6,<4.0.0)
|
20
20
|
Requires-Dist: asyncio (>=3.4.3,<4.0.0)
|
21
|
-
Requires-Dist: cryptography (>=42.0.
|
21
|
+
Requires-Dist: cryptography (>=42.0.5,<43.0.0)
|
22
22
|
Requires-Dist: deprecated (>=1.2.14,<2.0.0)
|
23
23
|
Requires-Dist: google-crc32c (>=1.5.0,<2.0.0)
|
24
|
-
Requires-Dist: pydantic (>=1.10.
|
24
|
+
Requires-Dist: pydantic (>=1.10.14,<2.0.0)
|
25
25
|
Requires-Dist: python-dateutil (>=2.9.0,<3.0.0)
|
26
26
|
Requires-Dist: requests (>=2.31.0,<3.0.0)
|
27
27
|
Requires-Dist: requests-toolbelt (>=1.0.0,<2.0.0)
|
28
|
-
Requires-Dist: typing-extensions (>=4.7.1,<5.0.0)
|
29
28
|
Project-URL: Repository, https://github.com/pangeacyber/pangea-python/tree/main/packages/pangea-sdk
|
30
29
|
Description-Content-Type: text/markdown
|
31
30
|
|
@@ -45,8 +44,6 @@ above.
|
|
45
44
|
|
46
45
|
## Installation
|
47
46
|
|
48
|
-
#### GA releases
|
49
|
-
|
50
47
|
Via pip:
|
51
48
|
|
52
49
|
```bash
|
@@ -59,32 +56,10 @@ Via poetry:
|
|
59
56
|
$ poetry add pangea-sdk
|
60
57
|
```
|
61
58
|
|
62
|
-
<a name="beta-releases"></a>
|
63
|
-
|
64
|
-
#### Beta releases
|
65
|
-
|
66
|
-
Pre-release versions may be available with the `b` (beta) denotation in the
|
67
|
-
version number. These releases serve to preview beta services and APIs. Per
|
68
|
-
Semantic Versioning, they are considered unstable and do not carry the same
|
69
|
-
compatibility guarantees as stable releases. [Beta changelog](https://github.com/pangeacyber/pangea-python/blob/beta/CHANGELOG.md).
|
70
|
-
|
71
|
-
Via pip:
|
72
|
-
|
73
|
-
```bash
|
74
|
-
$ pip3 install pangea-sdk==3.8.0b2
|
75
|
-
```
|
76
|
-
|
77
|
-
Via poetry:
|
78
|
-
|
79
|
-
```bash
|
80
|
-
$ poetry add pangea-sdk==3.8.0b2
|
81
|
-
```
|
82
|
-
|
83
59
|
## Usage
|
84
60
|
|
85
61
|
- [Documentation][]
|
86
|
-
- [
|
87
|
-
- [Beta Examples][]
|
62
|
+
- [Examples][]
|
88
63
|
|
89
64
|
General usage would be to create a token for a service through the
|
90
65
|
[Pangea Console][] and then construct an API client for that respective service.
|
@@ -143,6 +118,16 @@ if __name__ == "__main__":
|
|
143
118
|
asyncio.run(main())
|
144
119
|
```
|
145
120
|
|
121
|
+
|
122
|
+
<a name="beta-releases"></a>
|
123
|
+
|
124
|
+
## Beta releases
|
125
|
+
|
126
|
+
Pre-release versions may be available with the `beta` denotation in the version
|
127
|
+
number. These releases serve to preview beta services and APIs. Per Semantic
|
128
|
+
Versioning, they are considered unstable and do not carry the same compatibility
|
129
|
+
guarantees as stable releases.
|
130
|
+
|
146
131
|
## Secure Audit Log - Integrity Tools
|
147
132
|
|
148
133
|
The Python Pangea SDK also includes some extra features to validate Audit Service log's integrity. Here we explain how to run them.
|
@@ -228,8 +213,7 @@ It accepts multiple file formats:
|
|
228
213
|
|
229
214
|
|
230
215
|
[Documentation]: https://pangea.cloud/docs/sdk/python/
|
231
|
-
[
|
232
|
-
[Beta Examples]: https://github.com/pangeacyber/pangea-python/tree/beta/examples
|
216
|
+
[Examples]: https://github.com/pangeacyber/pangea-python/tree/main/examples
|
233
217
|
[Pangea Console]: https://console.pangea.cloud/
|
234
218
|
[Slack]: https://pangea.cloud/join-slack/
|
235
219
|
[Secure Audit Log]: https://pangea.cloud/docs/audit
|
@@ -14,8 +14,6 @@ above.
|
|
14
14
|
|
15
15
|
## Installation
|
16
16
|
|
17
|
-
#### GA releases
|
18
|
-
|
19
17
|
Via pip:
|
20
18
|
|
21
19
|
```bash
|
@@ -28,32 +26,10 @@ Via poetry:
|
|
28
26
|
$ poetry add pangea-sdk
|
29
27
|
```
|
30
28
|
|
31
|
-
<a name="beta-releases"></a>
|
32
|
-
|
33
|
-
#### Beta releases
|
34
|
-
|
35
|
-
Pre-release versions may be available with the `b` (beta) denotation in the
|
36
|
-
version number. These releases serve to preview beta services and APIs. Per
|
37
|
-
Semantic Versioning, they are considered unstable and do not carry the same
|
38
|
-
compatibility guarantees as stable releases. [Beta changelog](https://github.com/pangeacyber/pangea-python/blob/beta/CHANGELOG.md).
|
39
|
-
|
40
|
-
Via pip:
|
41
|
-
|
42
|
-
```bash
|
43
|
-
$ pip3 install pangea-sdk==3.8.0b2
|
44
|
-
```
|
45
|
-
|
46
|
-
Via poetry:
|
47
|
-
|
48
|
-
```bash
|
49
|
-
$ poetry add pangea-sdk==3.8.0b2
|
50
|
-
```
|
51
|
-
|
52
29
|
## Usage
|
53
30
|
|
54
31
|
- [Documentation][]
|
55
|
-
- [
|
56
|
-
- [Beta Examples][]
|
32
|
+
- [Examples][]
|
57
33
|
|
58
34
|
General usage would be to create a token for a service through the
|
59
35
|
[Pangea Console][] and then construct an API client for that respective service.
|
@@ -112,6 +88,16 @@ if __name__ == "__main__":
|
|
112
88
|
asyncio.run(main())
|
113
89
|
```
|
114
90
|
|
91
|
+
|
92
|
+
<a name="beta-releases"></a>
|
93
|
+
|
94
|
+
## Beta releases
|
95
|
+
|
96
|
+
Pre-release versions may be available with the `beta` denotation in the version
|
97
|
+
number. These releases serve to preview beta services and APIs. Per Semantic
|
98
|
+
Versioning, they are considered unstable and do not carry the same compatibility
|
99
|
+
guarantees as stable releases.
|
100
|
+
|
115
101
|
## Secure Audit Log - Integrity Tools
|
116
102
|
|
117
103
|
The Python Pangea SDK also includes some extra features to validate Audit Service log's integrity. Here we explain how to run them.
|
@@ -197,8 +183,7 @@ It accepts multiple file formats:
|
|
197
183
|
|
198
184
|
|
199
185
|
[Documentation]: https://pangea.cloud/docs/sdk/python/
|
200
|
-
[
|
201
|
-
[Beta Examples]: https://github.com/pangeacyber/pangea-python/tree/beta/examples
|
186
|
+
[Examples]: https://github.com/pangeacyber/pangea-python/tree/main/examples
|
202
187
|
[Pangea Console]: https://console.pangea.cloud/
|
203
188
|
[Slack]: https://pangea.cloud/join-slack/
|
204
189
|
[Secure Audit Log]: https://pangea.cloud/docs/audit
|
@@ -1,6 +1,7 @@
|
|
1
|
-
__version__ = "3.8.
|
1
|
+
__version__ = "3.8.0beta2"
|
2
2
|
|
3
3
|
from pangea.asyncio.request import PangeaRequestAsync
|
4
4
|
from pangea.config import PangeaConfig
|
5
|
+
from pangea.file_uploader import FileUploader
|
5
6
|
from pangea.request import PangeaRequest
|
6
7
|
from pangea.response import PangeaResponse
|
@@ -0,0 +1 @@
|
|
1
|
+
from .file_uploader import FileUploaderAsync
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# Copyright 2022 Pangea Cyber Corporation
|
2
|
+
# Author: Pangea Cyber Corporation
|
3
|
+
import io
|
4
|
+
import logging
|
5
|
+
from typing import Dict, Optional
|
6
|
+
|
7
|
+
from pangea.asyncio.request import PangeaRequestAsync
|
8
|
+
from pangea.request import PangeaConfig
|
9
|
+
from pangea.response import TransferMethod
|
10
|
+
|
11
|
+
|
12
|
+
class FileUploaderAsync:
|
13
|
+
def __init__(self) -> None:
|
14
|
+
self.logger = logging.getLogger("pangea")
|
15
|
+
self._request: PangeaRequestAsync = PangeaRequestAsync(
|
16
|
+
config=PangeaConfig(),
|
17
|
+
token="",
|
18
|
+
service="FileUploader",
|
19
|
+
logger=self.logger,
|
20
|
+
)
|
21
|
+
|
22
|
+
async def upload_file(
|
23
|
+
self,
|
24
|
+
url: str,
|
25
|
+
file: io.BufferedReader,
|
26
|
+
transfer_method: TransferMethod = TransferMethod.PUT_URL,
|
27
|
+
file_details: Optional[Dict] = None,
|
28
|
+
) -> None:
|
29
|
+
if transfer_method == TransferMethod.PUT_URL:
|
30
|
+
files = [("file", ("filename", file, "application/octet-stream"))]
|
31
|
+
await self._request.put_presigned_url(url=url, files=files) # type: ignore[arg-type]
|
32
|
+
elif transfer_method == TransferMethod.POST_URL:
|
33
|
+
files = [("file", ("filename", file, "application/octet-stream"))]
|
34
|
+
await self._request.post_presigned_url(url=url, data=file_details, files=files) # type: ignore[arg-type]
|
35
|
+
else:
|
36
|
+
raise ValueError(f"Transfer method not supported: {transfer_method}")
|
37
|
+
|
38
|
+
async def close(self) -> None:
|
39
|
+
await self._request.session.close()
|
@@ -3,20 +3,18 @@
|
|
3
3
|
|
4
4
|
import asyncio
|
5
5
|
import json
|
6
|
+
import os
|
6
7
|
import time
|
7
8
|
from typing import Dict, List, Optional, Tuple, Type, Union
|
8
9
|
|
9
10
|
import aiohttp
|
10
11
|
from aiohttp import FormData
|
11
|
-
from typing_extensions import TypeVar
|
12
12
|
|
13
13
|
import pangea.exceptions as pe
|
14
14
|
from pangea.request import MultipartResponse, PangeaRequestBase
|
15
15
|
from pangea.response import AttachedFile, PangeaResponse, PangeaResponseResult, ResponseStatus, TransferMethod
|
16
16
|
from pangea.utils import default_encoder
|
17
17
|
|
18
|
-
TResult = TypeVar("TResult", bound=PangeaResponseResult, default=PangeaResponseResult)
|
19
|
-
|
20
18
|
|
21
19
|
class PangeaRequestAsync(PangeaRequestBase):
|
22
20
|
"""An object that makes direct calls to Pangea Service APIs.
|
@@ -30,12 +28,12 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
30
28
|
async def post(
|
31
29
|
self,
|
32
30
|
endpoint: str,
|
33
|
-
result_class: Type[
|
31
|
+
result_class: Type[PangeaResponseResult],
|
34
32
|
data: Union[str, Dict] = {},
|
35
33
|
files: List[Tuple] = [],
|
36
34
|
poll_result: bool = True,
|
37
35
|
url: Optional[str] = None,
|
38
|
-
) -> PangeaResponse
|
36
|
+
) -> PangeaResponse:
|
39
37
|
"""Makes the POST call to a Pangea Service endpoint.
|
40
38
|
|
41
39
|
Args:
|
@@ -93,7 +91,9 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
93
91
|
|
94
92
|
return self._check_response(pangea_response)
|
95
93
|
|
96
|
-
async def get(
|
94
|
+
async def get(
|
95
|
+
self, path: str, result_class: Type[PangeaResponseResult], check_response: bool = True
|
96
|
+
) -> PangeaResponse[Type[PangeaResponseResult]]:
|
97
97
|
"""Makes the GET call to a Pangea Service endpoint.
|
98
98
|
|
99
99
|
Args:
|
@@ -110,7 +110,7 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
110
110
|
|
111
111
|
async with self.session.get(url, headers=self._headers()) as requests_response:
|
112
112
|
await self._check_http_errors(requests_response)
|
113
|
-
pangea_response = PangeaResponse(
|
113
|
+
pangea_response = PangeaResponse( # type: ignore[var-annotated]
|
114
114
|
requests_response, result_class=result_class, json=await requests_response.json()
|
115
115
|
)
|
116
116
|
|
@@ -131,11 +131,11 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
131
131
|
raise pe.ServiceTemporarilyUnavailable(await resp.json())
|
132
132
|
|
133
133
|
async def poll_result_by_id(
|
134
|
-
self, request_id: str, result_class: Type[
|
135
|
-
)
|
134
|
+
self, request_id: str, result_class: Union[Type[PangeaResponseResult], Type[dict]], check_response: bool = True
|
135
|
+
):
|
136
136
|
path = self._get_poll_path(request_id)
|
137
137
|
self.logger.debug(json.dumps({"service": self.service, "action": "poll_result_once", "url": path}))
|
138
|
-
return await self.get(path, result_class, check_response=check_response)
|
138
|
+
return await self.get(path, result_class, check_response=check_response) # type: ignore[arg-type]
|
139
139
|
|
140
140
|
async def poll_result_once(self, response: PangeaResponse, check_response: bool = True):
|
141
141
|
request_id = response.request_id
|
@@ -180,6 +180,7 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
180
180
|
"service": self.service,
|
181
181
|
"action": "download_file",
|
182
182
|
"url": url,
|
183
|
+
"filename": filename,
|
183
184
|
"status": "start",
|
184
185
|
}
|
185
186
|
)
|
@@ -295,6 +296,9 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
295
296
|
raise AttributeError("files attribute should have at least 1 file")
|
296
297
|
|
297
298
|
response = await self.request_presigned_url(endpoint=endpoint, result_class=result_class, data=data)
|
299
|
+
if response.success: # This should only happen when uploading a zero bytes file
|
300
|
+
return response.raw_response
|
301
|
+
|
298
302
|
if response.accepted_result is None:
|
299
303
|
raise pe.PangeaException("No accepted_result field when requesting presigned url")
|
300
304
|
if response.accepted_result.post_url is None:
|
@@ -314,9 +318,8 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
314
318
|
) -> PangeaResponse:
|
315
319
|
# Send request
|
316
320
|
try:
|
317
|
-
# This should return 202 (AcceptedRequestException)
|
318
|
-
|
319
|
-
raise pe.PresignedURLException("Should return 202", resp)
|
321
|
+
# This should return 202 (AcceptedRequestException) at least zero size file is sent
|
322
|
+
return await self.post(endpoint=endpoint, result_class=result_class, data=data, poll_result=False)
|
320
323
|
except pe.AcceptedRequestException as e:
|
321
324
|
accepted_exception = e
|
322
325
|
except Exception as e:
|
@@ -325,7 +328,9 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
325
328
|
# Receive 202
|
326
329
|
return await self._poll_presigned_url(accepted_exception.response)
|
327
330
|
|
328
|
-
async def _poll_presigned_url(
|
331
|
+
async def _poll_presigned_url(
|
332
|
+
self, response: PangeaResponse[Type[PangeaResponseResult]]
|
333
|
+
) -> PangeaResponse[Type[PangeaResponseResult]]:
|
329
334
|
if response.http_status != 202:
|
330
335
|
raise AttributeError("Response should be 202")
|
331
336
|
|
@@ -1,8 +1,9 @@
|
|
1
1
|
from .audit import AuditAsync
|
2
2
|
from .authn import AuthNAsync
|
3
|
-
from .authz import AuthZAsync
|
4
3
|
from .embargo import EmbargoAsync
|
5
4
|
from .file_scan import FileScanAsync
|
6
5
|
from .intel import DomainIntelAsync, FileIntelAsync, IpIntelAsync, UrlIntelAsync, UserIntelAsync
|
7
6
|
from .redact import RedactAsync
|
7
|
+
from .share import ShareAsync
|
8
|
+
from .sanitize import SanitizeAsync
|
8
9
|
from .vault import VaultAsync
|
@@ -1,14 +1,11 @@
|
|
1
1
|
# Copyright 2022 Pangea Cyber Corporation
|
2
2
|
# Author: Pangea Cyber Corporation
|
3
|
-
from __future__ import annotations
|
4
|
-
|
5
3
|
import datetime
|
6
|
-
from typing import Any, Dict, List, Optional,
|
4
|
+
from typing import Any, Dict, List, Optional, Union
|
7
5
|
|
8
6
|
import pangea.exceptions as pexc
|
9
7
|
from pangea.asyncio.services.base import ServiceBaseAsync
|
10
|
-
from pangea.
|
11
|
-
from pangea.response import PangeaResponse, PangeaResponseResult
|
8
|
+
from pangea.response import PangeaResponse
|
12
9
|
from pangea.services.audit.audit import AuditBase
|
13
10
|
from pangea.services.audit.exceptions import AuditException
|
14
11
|
from pangea.services.audit.models import (
|
@@ -16,7 +13,6 @@ from pangea.services.audit.models import (
|
|
16
13
|
DownloadRequest,
|
17
14
|
DownloadResult,
|
18
15
|
Event,
|
19
|
-
ExportRequest,
|
20
16
|
LogBulkResult,
|
21
17
|
LogResult,
|
22
18
|
PublishedRoot,
|
@@ -60,33 +56,14 @@ class AuditAsync(ServiceBaseAsync, AuditBase):
|
|
60
56
|
|
61
57
|
def __init__(
|
62
58
|
self,
|
63
|
-
token
|
64
|
-
config
|
59
|
+
token,
|
60
|
+
config=None,
|
65
61
|
private_key_file: str = "",
|
66
|
-
public_key_info:
|
67
|
-
tenant_id: str
|
68
|
-
logger_name
|
69
|
-
config_id: str
|
70
|
-
)
|
71
|
-
"""
|
72
|
-
Audit client
|
73
|
-
|
74
|
-
Initializes a new Audit client.
|
75
|
-
|
76
|
-
Args:
|
77
|
-
token: Pangea API token.
|
78
|
-
config: Configuration.
|
79
|
-
private_key_file: Private key filepath.
|
80
|
-
public_key_info: Public key information.
|
81
|
-
tenant_id: Tenant ID.
|
82
|
-
logger_name: Logger name.
|
83
|
-
config_id: Configuration ID.
|
84
|
-
|
85
|
-
Examples:
|
86
|
-
config = PangeaConfig(domain="pangea_domain")
|
87
|
-
audit = AuditAsync(token="pangea_token", config=config)
|
88
|
-
"""
|
89
|
-
|
62
|
+
public_key_info: Dict[str, str] = {},
|
63
|
+
tenant_id: Optional[str] = None,
|
64
|
+
logger_name="pangea",
|
65
|
+
config_id: Optional[str] = None,
|
66
|
+
):
|
90
67
|
# FIXME: Temporary check to deprecate config_id from PangeaConfig.
|
91
68
|
# Delete it when deprecate PangeaConfig.config_id
|
92
69
|
if config_id and config is not None and config.config_id is not None:
|
@@ -295,7 +272,7 @@ class AuditAsync(ServiceBaseAsync, AuditBase):
|
|
295
272
|
end: Optional[Union[datetime.datetime, str]] = None,
|
296
273
|
limit: Optional[int] = None,
|
297
274
|
max_results: Optional[int] = None,
|
298
|
-
search_restriction: Optional[
|
275
|
+
search_restriction: Optional[dict] = None,
|
299
276
|
verbose: Optional[bool] = None,
|
300
277
|
verify_consistency: bool = False,
|
301
278
|
verify_events: bool = True,
|
@@ -324,7 +301,7 @@ class AuditAsync(ServiceBaseAsync, AuditBase):
|
|
324
301
|
end (datetime, optional): An RFC-3339 formatted timestamp, or relative time adjustment from the current time.
|
325
302
|
limit (int, optional): Optional[int] = None,
|
326
303
|
max_results (int, optional): Maximum number of results to return.
|
327
|
-
search_restriction (
|
304
|
+
search_restriction (dict, optional): A list of keys to restrict the search results to. Useful for partitioning data available to the query string.
|
328
305
|
verbose (bool, optional): If true, response include root and membership and consistency proofs.
|
329
306
|
verify_consistency (bool): True to verify logs consistency
|
330
307
|
verify_events (bool): True to verify hash events and signatures
|
@@ -370,7 +347,6 @@ class AuditAsync(ServiceBaseAsync, AuditBase):
|
|
370
347
|
id: str,
|
371
348
|
limit: Optional[int] = 20,
|
372
349
|
offset: Optional[int] = 0,
|
373
|
-
assert_search_restriction: Optional[Dict[str, Sequence[str]]] = None,
|
374
350
|
verify_consistency: bool = False,
|
375
351
|
verify_events: bool = True,
|
376
352
|
) -> PangeaResponse[SearchResultOutput]:
|
@@ -385,7 +361,6 @@ class AuditAsync(ServiceBaseAsync, AuditBase):
|
|
385
361
|
id (string): the id of a search action, found in `response.result.id`
|
386
362
|
limit (integer, optional): the maximum number of results to return, default is 20
|
387
363
|
offset (integer, optional): the position of the first result to return, default is 0
|
388
|
-
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.
|
389
364
|
verify_consistency (bool): True to verify logs consistency
|
390
365
|
verify_events (bool): True to verify hash events and signatures
|
391
366
|
Raises:
|
@@ -403,8 +378,7 @@ class AuditAsync(ServiceBaseAsync, AuditBase):
|
|
403
378
|
result_res: PangeaResponse[SearchResultsOutput] = audit.results(
|
404
379
|
id=search_res.result.id,
|
405
380
|
limit=10,
|
406
|
-
offset=0
|
407
|
-
assert_search_restriction={'source': ["monitor"]})
|
381
|
+
offset=0)
|
408
382
|
"""
|
409
383
|
|
410
384
|
if limit <= 0: # type: ignore[operator]
|
@@ -417,7 +391,6 @@ class AuditAsync(ServiceBaseAsync, AuditBase):
|
|
417
391
|
id=id,
|
418
392
|
limit=limit,
|
419
393
|
offset=offset,
|
420
|
-
assert_search_restriction=assert_search_restriction,
|
421
394
|
)
|
422
395
|
response = await self.request.post("v1/results", SearchResultOutput, data=input.dict(exclude_none=True))
|
423
396
|
if verify_consistency and response.result is not None:
|
@@ -425,107 +398,6 @@ class AuditAsync(ServiceBaseAsync, AuditBase):
|
|
425
398
|
|
426
399
|
return self.handle_results_response(response, verify_consistency, verify_events)
|
427
400
|
|
428
|
-
async def export(
|
429
|
-
self,
|
430
|
-
*,
|
431
|
-
format: DownloadFormat = DownloadFormat.CSV,
|
432
|
-
start: Optional[datetime.datetime] = None,
|
433
|
-
end: Optional[datetime.datetime] = None,
|
434
|
-
order: Optional[SearchOrder] = None,
|
435
|
-
order_by: Optional[str] = None,
|
436
|
-
verbose: bool = True,
|
437
|
-
) -> PangeaResponse[PangeaResponseResult]:
|
438
|
-
"""
|
439
|
-
Export from the audit log
|
440
|
-
|
441
|
-
Bulk export of data from the Secure Audit Log, with optional filtering.
|
442
|
-
|
443
|
-
OperationId: audit_post_v1_export
|
444
|
-
|
445
|
-
Args:
|
446
|
-
format: Format for the records.
|
447
|
-
start: The start of the time range to perform the search on.
|
448
|
-
end: The end of the time range to perform the search on. If omitted,
|
449
|
-
then all records up to the latest will be searched.
|
450
|
-
order: Specify the sort order of the response.
|
451
|
-
order_by: Name of column to sort the results by.
|
452
|
-
verbose: Whether or not to include the root hash of the tree and the
|
453
|
-
membership proof for each record.
|
454
|
-
|
455
|
-
Raises:
|
456
|
-
AuditException: If an audit based api exception happens
|
457
|
-
PangeaAPIException: If an API Error happens
|
458
|
-
|
459
|
-
Examples:
|
460
|
-
export_res = await audit.export(verbose=False)
|
461
|
-
|
462
|
-
# Export may take several dozens of minutes, so polling for the result
|
463
|
-
# should be done in a loop. That is omitted here for brevity's sake.
|
464
|
-
try:
|
465
|
-
await audit.poll_result(request_id=export_res.request_id)
|
466
|
-
except AcceptedRequestException:
|
467
|
-
# Retry later.
|
468
|
-
|
469
|
-
# Download the result when it's ready.
|
470
|
-
download_res = await audit.download_results(request_id=export_res.request_id)
|
471
|
-
download_res.result.dest_url
|
472
|
-
# => https://pangea-runtime.s3.amazonaws.com/audit/xxxxx/search_results_[...]
|
473
|
-
"""
|
474
|
-
input = ExportRequest(
|
475
|
-
format=format,
|
476
|
-
start=start,
|
477
|
-
end=end,
|
478
|
-
order=order,
|
479
|
-
order_by=order_by,
|
480
|
-
verbose=verbose,
|
481
|
-
)
|
482
|
-
try:
|
483
|
-
return await self.request.post(
|
484
|
-
"v1/export", PangeaResponseResult, data=input.dict(exclude_none=True), poll_result=False
|
485
|
-
)
|
486
|
-
except pexc.AcceptedRequestException as e:
|
487
|
-
return e.response
|
488
|
-
|
489
|
-
async def log_stream(self, data: dict) -> PangeaResponse[PangeaResponseResult]:
|
490
|
-
"""
|
491
|
-
Log streaming endpoint
|
492
|
-
|
493
|
-
This API allows 3rd party vendors (like Auth0) to stream events to this
|
494
|
-
endpoint where the structure of the payload varies across different
|
495
|
-
vendors.
|
496
|
-
|
497
|
-
OperationId: audit_post_v1_log_stream
|
498
|
-
|
499
|
-
Args:
|
500
|
-
data: Event data. The exact schema of this will vary by vendor.
|
501
|
-
|
502
|
-
Raises:
|
503
|
-
AuditException: If an audit based api exception happens
|
504
|
-
PangeaAPIException: If an API Error happens
|
505
|
-
|
506
|
-
Examples:
|
507
|
-
data = {
|
508
|
-
"logs": [
|
509
|
-
{
|
510
|
-
"log_id": "some log ID",
|
511
|
-
"data": {
|
512
|
-
"date": "2024-03-29T17:26:50.193Z",
|
513
|
-
"type": "sapi",
|
514
|
-
"description": "Create a log stream",
|
515
|
-
"client_id": "some client ID",
|
516
|
-
"ip": "127.0.0.1",
|
517
|
-
"user_agent": "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0",
|
518
|
-
"user_id": "some user ID",
|
519
|
-
},
|
520
|
-
}
|
521
|
-
# ...
|
522
|
-
]
|
523
|
-
}
|
524
|
-
|
525
|
-
response = await audit.log_stream(data)
|
526
|
-
"""
|
527
|
-
return await self.request.post("v1/log_stream", PangeaResponseResult, data=data)
|
528
|
-
|
529
401
|
async def root(self, tree_size: Optional[int] = None) -> PangeaResponse[RootResult]:
|
530
402
|
"""
|
531
403
|
Tamperproof verification
|
@@ -551,10 +423,7 @@ class AuditAsync(ServiceBaseAsync, AuditBase):
|
|
551
423
|
return await self.request.post("v1/root", RootResult, data=input.dict(exclude_none=True))
|
552
424
|
|
553
425
|
async def download_results(
|
554
|
-
self,
|
555
|
-
result_id: Optional[str] = None,
|
556
|
-
format: DownloadFormat = DownloadFormat.CSV,
|
557
|
-
request_id: Optional[str] = None,
|
426
|
+
self, result_id: str, format: Optional[DownloadFormat] = None
|
558
427
|
) -> PangeaResponse[DownloadResult]:
|
559
428
|
"""
|
560
429
|
Download search results
|
@@ -566,7 +435,6 @@ class AuditAsync(ServiceBaseAsync, AuditBase):
|
|
566
435
|
Args:
|
567
436
|
result_id: ID returned by the search API.
|
568
437
|
format: Format for the records.
|
569
|
-
request_id: ID returned by the export API.
|
570
438
|
|
571
439
|
Returns:
|
572
440
|
URL where search results can be downloaded.
|
@@ -582,10 +450,7 @@ class AuditAsync(ServiceBaseAsync, AuditBase):
|
|
582
450
|
)
|
583
451
|
"""
|
584
452
|
|
585
|
-
|
586
|
-
raise ValueError("must pass one of `request_id` or `result_id`")
|
587
|
-
|
588
|
-
input = DownloadRequest(request_id=request_id, result_id=result_id, format=format)
|
453
|
+
input = DownloadRequest(result_id=result_id, format=format)
|
589
454
|
return await self.request.post("v1/download_results", DownloadResult, data=input.dict(exclude_none=True))
|
590
455
|
|
591
456
|
async def update_published_roots(self, result: SearchResultOutput):
|