pangea-sdk 3.8.0__py3-none-any.whl → 5.3.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 +2 -1
- pangea/asyncio/__init__.py +1 -0
- pangea/asyncio/file_uploader.py +39 -0
- pangea/asyncio/request.py +46 -23
- pangea/asyncio/services/__init__.py +2 -0
- pangea/asyncio/services/audit.py +46 -20
- pangea/asyncio/services/authn.py +123 -61
- pangea/asyncio/services/authz.py +57 -31
- pangea/asyncio/services/base.py +21 -2
- pangea/asyncio/services/embargo.py +2 -2
- pangea/asyncio/services/file_scan.py +24 -9
- pangea/asyncio/services/intel.py +104 -30
- pangea/asyncio/services/redact.py +52 -3
- pangea/asyncio/services/sanitize.py +217 -0
- pangea/asyncio/services/share.py +733 -0
- pangea/asyncio/services/vault.py +1709 -766
- pangea/crypto/rsa.py +135 -0
- pangea/deep_verify.py +7 -1
- pangea/dump_audit.py +9 -8
- pangea/file_uploader.py +35 -0
- pangea/request.py +70 -49
- pangea/response.py +36 -17
- pangea/services/__init__.py +2 -0
- pangea/services/audit/audit.py +57 -29
- pangea/services/audit/models.py +12 -3
- pangea/services/audit/signing.py +6 -5
- pangea/services/audit/util.py +3 -3
- pangea/services/authn/authn.py +120 -66
- pangea/services/authn/models.py +167 -11
- pangea/services/authz.py +53 -30
- pangea/services/base.py +16 -2
- pangea/services/embargo.py +2 -2
- pangea/services/file_scan.py +32 -15
- pangea/services/intel.py +155 -30
- pangea/services/redact.py +132 -3
- pangea/services/sanitize.py +388 -0
- pangea/services/share/file_format.py +170 -0
- pangea/services/share/share.py +1440 -0
- pangea/services/vault/models/asymmetric.py +120 -18
- pangea/services/vault/models/common.py +439 -141
- pangea/services/vault/models/keys.py +94 -0
- pangea/services/vault/models/secret.py +27 -3
- pangea/services/vault/models/symmetric.py +68 -22
- pangea/services/vault/vault.py +1690 -766
- pangea/tools.py +6 -7
- pangea/utils.py +94 -33
- pangea/verify_audit.py +270 -83
- {pangea_sdk-3.8.0.dist-info → pangea_sdk-5.3.0.dist-info}/METADATA +21 -29
- pangea_sdk-5.3.0.dist-info/RECORD +56 -0
- {pangea_sdk-3.8.0.dist-info → pangea_sdk-5.3.0.dist-info}/WHEEL +1 -1
- pangea_sdk-3.8.0.dist-info/RECORD +0 -46
pangea/asyncio/services/authz.py
CHANGED
@@ -1,9 +1,12 @@
|
|
1
1
|
# Copyright 2022 Pangea Cyber Corporation
|
2
2
|
# Author: Pangea Cyber Corporation
|
3
3
|
|
4
|
-
from
|
4
|
+
from __future__ import annotations
|
5
|
+
|
6
|
+
from typing import Any, Dict, List, Optional
|
5
7
|
|
6
8
|
from pangea.asyncio.services.base import ServiceBaseAsync
|
9
|
+
from pangea.config import PangeaConfig
|
7
10
|
from pangea.response import PangeaResponse
|
8
11
|
from pangea.services.authz import (
|
9
12
|
CheckRequest,
|
@@ -28,17 +31,16 @@ from pangea.services.authz import (
|
|
28
31
|
|
29
32
|
|
30
33
|
class AuthZAsync(ServiceBaseAsync):
|
31
|
-
"""AuthZ service client.
|
34
|
+
"""AuthZ service client.
|
32
35
|
|
33
36
|
Provides methods to interact with the Pangea AuthZ Service.
|
34
37
|
Documentation for the AuthZ Service API can be found at
|
35
|
-
<https://pangea.cloud/docs/api/authz>.
|
36
|
-
is subject to change.
|
38
|
+
<https://pangea.cloud/docs/api/authz>.
|
37
39
|
|
38
40
|
Examples:
|
39
41
|
import os
|
42
|
+
from pangea.asyncio.services import AuthZAsync
|
40
43
|
from pangea.config import PangeaConfig
|
41
|
-
from pangea.services import AuthZ
|
42
44
|
|
43
45
|
PANGEA_TOKEN = os.getenv("PANGEA_AUTHZ_TOKEN")
|
44
46
|
|
@@ -50,15 +52,32 @@ class AuthZAsync(ServiceBaseAsync):
|
|
50
52
|
|
51
53
|
service_name = "authz"
|
52
54
|
|
53
|
-
def __init__(
|
55
|
+
def __init__(
|
56
|
+
self, token: str, config: PangeaConfig | None = None, logger_name: str = "pangea", config_id: str | None = None
|
57
|
+
) -> None:
|
58
|
+
"""
|
59
|
+
AuthZ client
|
60
|
+
|
61
|
+
Initializes a new AuthZ client.
|
62
|
+
|
63
|
+
Args:
|
64
|
+
token: Pangea API token.
|
65
|
+
config: Configuration.
|
66
|
+
logger_name: Logger name.
|
67
|
+
config_id: Configuration ID.
|
68
|
+
|
69
|
+
Examples:
|
70
|
+
config = PangeaConfig(domain="aws.us.pangea.cloud")
|
71
|
+
authz = AuthZAsync(token="pangea_token", config=config)
|
72
|
+
"""
|
73
|
+
|
54
74
|
super().__init__(token, config, logger_name, config_id=config_id)
|
55
75
|
|
56
76
|
async def tuple_create(self, tuples: List[Tuple]) -> PangeaResponse[TupleCreateResult]:
|
57
|
-
"""Create tuples.
|
77
|
+
"""Create tuples.
|
58
78
|
|
59
79
|
Create tuples in the AuthZ Service. The request will fail if there is no schema
|
60
80
|
or the tuples do not validate against the schema.
|
61
|
-
How to install a [Beta release](https://pangea.cloud/docs/sdk/python/#beta-releases).
|
62
81
|
|
63
82
|
Args:
|
64
83
|
tuples (List[Tuple]): List of tuples to be created.
|
@@ -84,7 +103,9 @@ class AuthZAsync(ServiceBaseAsync):
|
|
84
103
|
"""
|
85
104
|
|
86
105
|
input_data = TupleCreateRequest(tuples=tuples)
|
87
|
-
return await self.request.post(
|
106
|
+
return await self.request.post(
|
107
|
+
"v1/tuple/create", TupleCreateResult, data=input_data.model_dump(exclude_none=True)
|
108
|
+
)
|
88
109
|
|
89
110
|
async def tuple_list(
|
90
111
|
self,
|
@@ -94,12 +115,11 @@ class AuthZAsync(ServiceBaseAsync):
|
|
94
115
|
order: Optional[ItemOrder] = None,
|
95
116
|
order_by: Optional[TupleOrderBy] = None,
|
96
117
|
) -> PangeaResponse[TupleListResult]:
|
97
|
-
"""List tuples.
|
118
|
+
"""List tuples.
|
98
119
|
|
99
120
|
Return a paginated list of filtered tuples. The filter is given in terms
|
100
121
|
of a tuple. Fill out the fields that you want to filter. If the filter
|
101
122
|
is empty it will return all the tuples.
|
102
|
-
How to install a [Beta release](https://pangea.cloud/docs/sdk/python/#beta-releases).
|
103
123
|
|
104
124
|
Args:
|
105
125
|
filter (TupleListFilter): The filter for listing tuples.
|
@@ -120,15 +140,14 @@ class AuthZAsync(ServiceBaseAsync):
|
|
120
140
|
await authz.tuple_list(TupleListFilter(subject_type="user", subject_id="user_1"))
|
121
141
|
"""
|
122
142
|
input_data = TupleListRequest(
|
123
|
-
filter=filter.
|
143
|
+
filter=filter.model_dump(exclude_none=True), size=size, last=last, order=order, order_by=order_by
|
124
144
|
)
|
125
|
-
return await self.request.post("v1/tuple/list", TupleListResult, data=input_data.
|
145
|
+
return await self.request.post("v1/tuple/list", TupleListResult, data=input_data.model_dump(exclude_none=True))
|
126
146
|
|
127
147
|
async def tuple_delete(self, tuples: List[Tuple]) -> PangeaResponse[TupleDeleteResult]:
|
128
|
-
"""Delete tuples.
|
148
|
+
"""Delete tuples.
|
129
149
|
|
130
150
|
Delete tuples in the AuthZ Service.
|
131
|
-
How to install a [Beta release](https://pangea.cloud/docs/sdk/python/#beta-releases).
|
132
151
|
|
133
152
|
Args:
|
134
153
|
tuples (List[Tuple]): List of tuples to be deleted.
|
@@ -154,7 +173,9 @@ class AuthZAsync(ServiceBaseAsync):
|
|
154
173
|
"""
|
155
174
|
|
156
175
|
input_data = TupleDeleteRequest(tuples=tuples)
|
157
|
-
return await self.request.post(
|
176
|
+
return await self.request.post(
|
177
|
+
"v1/tuple/delete", TupleDeleteResult, data=input_data.model_dump(exclude_none=True)
|
178
|
+
)
|
158
179
|
|
159
180
|
async def check(
|
160
181
|
self,
|
@@ -162,19 +183,18 @@ class AuthZAsync(ServiceBaseAsync):
|
|
162
183
|
action: str,
|
163
184
|
subject: Subject,
|
164
185
|
debug: Optional[bool] = None,
|
165
|
-
attributes: Optional[Dict[str,
|
186
|
+
attributes: Optional[Dict[str, Any]] = None,
|
166
187
|
) -> PangeaResponse[CheckResult]:
|
167
|
-
"""Perform a check request.
|
188
|
+
"""Perform a check request.
|
168
189
|
|
169
190
|
Check if a subject has permission to perform an action on the resource.
|
170
|
-
How to install a [Beta release](https://pangea.cloud/docs/sdk/python/#beta-releases).
|
171
191
|
|
172
192
|
Args:
|
173
193
|
resource (Resource): The resource to check.
|
174
194
|
action (str): The action to check.
|
175
195
|
subject (Subject): The subject to check.
|
176
196
|
debug (Optional[bool]): Setting this value to True will provide a detailed analysis of the check.
|
177
|
-
attributes (Optional[Dict[str,
|
197
|
+
attributes (Optional[Dict[str, Any]]): Additional attributes for the check.
|
178
198
|
|
179
199
|
Raises:
|
180
200
|
PangeaAPIException: If an API Error happens.
|
@@ -194,19 +214,21 @@ class AuthZAsync(ServiceBaseAsync):
|
|
194
214
|
"""
|
195
215
|
|
196
216
|
input_data = CheckRequest(resource=resource, action=action, subject=subject, debug=debug, attributes=attributes)
|
197
|
-
return await self.request.post("v1/check", CheckResult, data=input_data.
|
217
|
+
return await self.request.post("v1/check", CheckResult, data=input_data.model_dump(exclude_none=True))
|
198
218
|
|
199
|
-
async def list_resources(
|
200
|
-
|
219
|
+
async def list_resources(
|
220
|
+
self, type: str, action: str, subject: Subject, attributes: Optional[Dict[str, Any]] = None
|
221
|
+
) -> PangeaResponse[ListResourcesResult]:
|
222
|
+
"""List resources.
|
201
223
|
|
202
224
|
Given a type, action, and subject, list all the resources in the
|
203
225
|
type that the subject has access to the action with.
|
204
|
-
How to install a [Beta release](https://pangea.cloud/docs/sdk/python/#beta-releases).
|
205
226
|
|
206
227
|
Args:
|
207
228
|
type (str): The type to filter resources.
|
208
229
|
action (str): The action to filter resources.
|
209
230
|
subject (Subject): The subject to filter resources.
|
231
|
+
attributes (Optional[Dict[str, Any]]): A JSON object of attribute data.
|
210
232
|
|
211
233
|
Raises:
|
212
234
|
PangeaAPIException: If an API Error happens.
|
@@ -224,21 +246,23 @@ class AuthZAsync(ServiceBaseAsync):
|
|
224
246
|
)
|
225
247
|
"""
|
226
248
|
|
227
|
-
input_data = ListResourcesRequest(type=type, action=action, subject=subject)
|
249
|
+
input_data = ListResourcesRequest(type=type, action=action, subject=subject, attributes=attributes)
|
228
250
|
return await self.request.post(
|
229
|
-
"v1/list-resources", ListResourcesResult, data=input_data.
|
251
|
+
"v1/list-resources", ListResourcesResult, data=input_data.model_dump(exclude_none=True)
|
230
252
|
)
|
231
253
|
|
232
|
-
async def list_subjects(
|
233
|
-
|
254
|
+
async def list_subjects(
|
255
|
+
self, resource: Resource, action: str, attributes: Optional[Dict[str, Any]] = None
|
256
|
+
) -> PangeaResponse[ListSubjectsResult]:
|
257
|
+
"""List subjects.
|
234
258
|
|
235
259
|
Given a resource and an action, return the list of subjects who have
|
236
260
|
access to the action for the given resource.
|
237
|
-
How to install a [Beta release](https://pangea.cloud/docs/sdk/python/#beta-releases).
|
238
261
|
|
239
262
|
Args:
|
240
263
|
resource (Resource): The resource to filter subjects.
|
241
264
|
action (str): The action to filter subjects.
|
265
|
+
attributes (Optional[Dict[str, Any]]): A JSON object of attribute data.
|
242
266
|
|
243
267
|
Raises:
|
244
268
|
PangeaAPIException: If an API Error happens.
|
@@ -255,5 +279,7 @@ class AuthZAsync(ServiceBaseAsync):
|
|
255
279
|
)
|
256
280
|
"""
|
257
281
|
|
258
|
-
input_data = ListSubjectsRequest(resource=resource, action=action)
|
259
|
-
return await self.request.post(
|
282
|
+
input_data = ListSubjectsRequest(resource=resource, action=action, attributes=attributes)
|
283
|
+
return await self.request.post(
|
284
|
+
"v1/list-subjects", ListSubjectsResult, data=input_data.model_dump(exclude_none=True)
|
285
|
+
)
|
pangea/asyncio/services/base.py
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
# Copyright 2022 Pangea Cyber Corporation
|
2
2
|
# Author: Pangea Cyber Corporation
|
3
|
+
from __future__ import annotations
|
3
4
|
|
4
5
|
from typing import Dict, Optional, Type, Union
|
5
6
|
|
7
|
+
from typing_extensions import override
|
8
|
+
|
6
9
|
from pangea.asyncio.request import PangeaRequestAsync
|
7
10
|
from pangea.exceptions import AcceptedRequestException
|
8
11
|
from pangea.response import AttachedFile, PangeaResponse, PangeaResponseResult
|
@@ -23,6 +26,7 @@ class ServiceBaseAsync(ServiceBase):
|
|
23
26
|
|
24
27
|
return self._request
|
25
28
|
|
29
|
+
@override
|
26
30
|
async def poll_result( # type: ignore[override]
|
27
31
|
self,
|
28
32
|
exception: Optional[AcceptedRequestException] = None,
|
@@ -36,7 +40,8 @@ class ServiceBaseAsync(ServiceBase):
|
|
36
40
|
Returns request's result that has been accepted by the server
|
37
41
|
|
38
42
|
Args:
|
39
|
-
exception
|
43
|
+
exception: Exception that was previously raised by the SDK on a call
|
44
|
+
that is being processed.
|
40
45
|
|
41
46
|
Returns:
|
42
47
|
PangeaResponse
|
@@ -58,7 +63,21 @@ class ServiceBaseAsync(ServiceBase):
|
|
58
63
|
else:
|
59
64
|
raise AttributeError("Need to set exception, response or request_id")
|
60
65
|
|
61
|
-
|
66
|
+
@override
|
67
|
+
async def download_file(self, url: str, filename: str | None = None) -> AttachedFile: # type: ignore[override]
|
68
|
+
"""
|
69
|
+
Download file
|
70
|
+
|
71
|
+
Download a file from the specified URL and save it with the given
|
72
|
+
filename.
|
73
|
+
|
74
|
+
Args:
|
75
|
+
url: URL of the file to download
|
76
|
+
filename: Name to save the downloaded file as. If not provided, the
|
77
|
+
filename will be determined from the Content-Disposition header or
|
78
|
+
the URL.
|
79
|
+
"""
|
80
|
+
|
62
81
|
return await self.request.download_file(url=url, filename=filename)
|
63
82
|
|
64
83
|
async def close(self):
|
@@ -57,7 +57,7 @@ class EmbargoAsync(ServiceBaseAsync):
|
|
57
57
|
response = embargo.ip_check("190.6.64.94")
|
58
58
|
"""
|
59
59
|
input = e.IPCheckRequest(ip=ip)
|
60
|
-
return await self.request.post("v1/ip/check", e.EmbargoResult, data=input.
|
60
|
+
return await self.request.post("v1/ip/check", e.EmbargoResult, data=input.model_dump())
|
61
61
|
|
62
62
|
async def iso_check(self, iso_code: str) -> PangeaResponse[e.EmbargoResult]:
|
63
63
|
"""
|
@@ -84,4 +84,4 @@ class EmbargoAsync(ServiceBaseAsync):
|
|
84
84
|
response = embargo.iso_check("CU")
|
85
85
|
"""
|
86
86
|
input = e.ISOCheckRequest(iso_code=iso_code)
|
87
|
-
return await self.request.post("v1/iso/check", result_class=e.EmbargoResult, data=input.
|
87
|
+
return await self.request.post("v1/iso/check", result_class=e.EmbargoResult, data=input.model_dump())
|
@@ -59,12 +59,14 @@ class FileScanAsync(ServiceBaseAsync):
|
|
59
59
|
OperationId: file_scan_post_v1_scan
|
60
60
|
|
61
61
|
Args:
|
62
|
-
file (io.BufferedReader, optional): file to be scanned (should be opened with read permissions and in binary format)
|
63
62
|
file_path (str, optional): filepath to be opened and scanned
|
63
|
+
file (io.BufferedReader, optional): file to be scanned (should be opened with read permissions and in binary format)
|
64
64
|
verbose (bool, optional): Echo the API parameters in the response
|
65
65
|
raw (bool, optional): Include raw data from this provider
|
66
66
|
provider (str, optional): Scan file using this provider
|
67
67
|
sync_call (bool, optional): True to wait until server returns a result, False to return immediately and retrieve result asynchronously
|
68
|
+
transfer_method (TransferMethod, optional): Transfer method used to upload the file data.
|
69
|
+
source_url (str, optional): A URL where the Pangea APIs can fetch the contents of the input file.
|
68
70
|
|
69
71
|
Raises:
|
70
72
|
PangeaAPIException: If an API Error happens
|
@@ -77,7 +79,7 @@ class FileScanAsync(ServiceBaseAsync):
|
|
77
79
|
Examples:
|
78
80
|
try:
|
79
81
|
with open("./path/to/file.pdf", "rb") as f:
|
80
|
-
response = client.file_scan(file=f, verbose=True, provider="crowdstrike")
|
82
|
+
response = await client.file_scan(file=f, verbose=True, provider="crowdstrike")
|
81
83
|
print(f"Response: {response.result}")
|
82
84
|
except pe.PangeaAPIException as e:
|
83
85
|
print(f"Request Error: {e.response.summary}")
|
@@ -85,6 +87,15 @@ class FileScanAsync(ServiceBaseAsync):
|
|
85
87
|
print(f"\\t{err.detail} \\n")
|
86
88
|
"""
|
87
89
|
|
90
|
+
if transfer_method == TransferMethod.SOURCE_URL and source_url is None:
|
91
|
+
raise ValueError("`source_url` argument is required when using `TransferMethod.SOURCE_URL`.")
|
92
|
+
|
93
|
+
if source_url is not None and transfer_method != TransferMethod.SOURCE_URL:
|
94
|
+
raise ValueError(
|
95
|
+
"`transfer_method` should be `TransferMethod.SOURCE_URL` when using the `source_url` argument."
|
96
|
+
)
|
97
|
+
|
98
|
+
files: Optional[List[Tuple]] = None
|
88
99
|
if file or file_path:
|
89
100
|
if file_path:
|
90
101
|
file = open(file_path, "rb")
|
@@ -95,9 +106,9 @@ class FileScanAsync(ServiceBaseAsync):
|
|
95
106
|
size = params.size
|
96
107
|
else:
|
97
108
|
crc, sha, size = None, None, None
|
98
|
-
files
|
99
|
-
|
100
|
-
raise ValueError("Need to set file_path or
|
109
|
+
files = [("upload", ("filename", file, "application/octet-stream"))]
|
110
|
+
elif source_url is None:
|
111
|
+
raise ValueError("Need to set one of `file_path`, `file`, or `source_url` arguments.")
|
101
112
|
|
102
113
|
input = m.FileScanRequest(
|
103
114
|
verbose=verbose,
|
@@ -109,8 +120,12 @@ class FileScanAsync(ServiceBaseAsync):
|
|
109
120
|
transfer_method=transfer_method,
|
110
121
|
source_url=source_url,
|
111
122
|
)
|
112
|
-
data = input.
|
113
|
-
|
123
|
+
data = input.model_dump(exclude_none=True)
|
124
|
+
try:
|
125
|
+
return await self.request.post("v1/scan", m.FileScanResult, data=data, files=files, poll_result=sync_call)
|
126
|
+
finally:
|
127
|
+
if file_path and file:
|
128
|
+
file.close()
|
114
129
|
|
115
130
|
async def request_upload_url(
|
116
131
|
self,
|
@@ -131,7 +146,7 @@ class FileScanAsync(ServiceBaseAsync):
|
|
131
146
|
input.sha256 = params.sha256_hex
|
132
147
|
input.size = params.size
|
133
148
|
|
134
|
-
data = input.
|
149
|
+
data = input.model_dump(exclude_none=True)
|
135
150
|
return await self.request.request_presigned_url("v1/scan", m.FileScanResult, data=data)
|
136
151
|
|
137
152
|
|
@@ -154,7 +169,7 @@ class FileUploaderAsync:
|
|
154
169
|
) -> None:
|
155
170
|
if transfer_method == TransferMethod.PUT_URL:
|
156
171
|
files = [("file", ("filename", file, "application/octet-stream"))]
|
157
|
-
await self._request.put_presigned_url(url=url, files=files)
|
172
|
+
await self._request.put_presigned_url(url=url, files=files)
|
158
173
|
elif transfer_method == TransferMethod.POST_URL:
|
159
174
|
files = [("file", ("filename", file, "application/octet-stream"))]
|
160
175
|
await self._request.post_presigned_url(url=url, data=file_details, files=files) # type: ignore[arg-type]
|