pangea-sdk 3.8.0b1__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.
Files changed (48) hide show
  1. pangea/__init__.py +1 -1
  2. pangea/asyncio/file_uploader.py +1 -1
  3. pangea/asyncio/request.py +49 -31
  4. pangea/asyncio/services/__init__.py +2 -0
  5. pangea/asyncio/services/audit.py +192 -31
  6. pangea/asyncio/services/authn.py +187 -109
  7. pangea/asyncio/services/authz.py +285 -0
  8. pangea/asyncio/services/base.py +21 -2
  9. pangea/asyncio/services/embargo.py +2 -2
  10. pangea/asyncio/services/file_scan.py +24 -9
  11. pangea/asyncio/services/intel.py +108 -34
  12. pangea/asyncio/services/redact.py +72 -4
  13. pangea/asyncio/services/sanitize.py +217 -0
  14. pangea/asyncio/services/share.py +246 -73
  15. pangea/asyncio/services/vault.py +1710 -750
  16. pangea/crypto/rsa.py +135 -0
  17. pangea/deep_verify.py +7 -1
  18. pangea/dump_audit.py +9 -8
  19. pangea/request.py +83 -59
  20. pangea/response.py +49 -31
  21. pangea/services/__init__.py +2 -0
  22. pangea/services/audit/audit.py +205 -42
  23. pangea/services/audit/models.py +56 -8
  24. pangea/services/audit/signing.py +6 -5
  25. pangea/services/audit/util.py +3 -3
  26. pangea/services/authn/authn.py +140 -70
  27. pangea/services/authn/models.py +167 -11
  28. pangea/services/authz.py +400 -0
  29. pangea/services/base.py +39 -8
  30. pangea/services/embargo.py +2 -2
  31. pangea/services/file_scan.py +32 -15
  32. pangea/services/intel.py +157 -32
  33. pangea/services/redact.py +152 -4
  34. pangea/services/sanitize.py +388 -0
  35. pangea/services/share/share.py +683 -107
  36. pangea/services/vault/models/asymmetric.py +120 -18
  37. pangea/services/vault/models/common.py +439 -141
  38. pangea/services/vault/models/keys.py +94 -0
  39. pangea/services/vault/models/secret.py +27 -3
  40. pangea/services/vault/models/symmetric.py +68 -22
  41. pangea/services/vault/vault.py +1690 -749
  42. pangea/tools.py +6 -7
  43. pangea/utils.py +16 -27
  44. pangea/verify_audit.py +270 -83
  45. {pangea_sdk-3.8.0b1.dist-info → pangea_sdk-5.3.0.dist-info}/METADATA +43 -35
  46. pangea_sdk-5.3.0.dist-info/RECORD +56 -0
  47. {pangea_sdk-3.8.0b1.dist-info → pangea_sdk-5.3.0.dist-info}/WHEEL +1 -1
  48. pangea_sdk-3.8.0b1.dist-info/RECORD +0 -50
@@ -0,0 +1,285 @@
1
+ # Copyright 2022 Pangea Cyber Corporation
2
+ # Author: Pangea Cyber Corporation
3
+
4
+ from __future__ import annotations
5
+
6
+ from typing import Any, Dict, List, Optional
7
+
8
+ from pangea.asyncio.services.base import ServiceBaseAsync
9
+ from pangea.config import PangeaConfig
10
+ from pangea.response import PangeaResponse
11
+ from pangea.services.authz import (
12
+ CheckRequest,
13
+ CheckResult,
14
+ ItemOrder,
15
+ ListResourcesRequest,
16
+ ListResourcesResult,
17
+ ListSubjectsRequest,
18
+ ListSubjectsResult,
19
+ Resource,
20
+ Subject,
21
+ Tuple,
22
+ TupleCreateRequest,
23
+ TupleCreateResult,
24
+ TupleDeleteRequest,
25
+ TupleDeleteResult,
26
+ TupleListFilter,
27
+ TupleListRequest,
28
+ TupleListResult,
29
+ TupleOrderBy,
30
+ )
31
+
32
+
33
+ class AuthZAsync(ServiceBaseAsync):
34
+ """AuthZ service client.
35
+
36
+ Provides methods to interact with the Pangea AuthZ Service.
37
+ Documentation for the AuthZ Service API can be found at
38
+ <https://pangea.cloud/docs/api/authz>.
39
+
40
+ Examples:
41
+ import os
42
+ from pangea.asyncio.services import AuthZAsync
43
+ from pangea.config import PangeaConfig
44
+
45
+ PANGEA_TOKEN = os.getenv("PANGEA_AUTHZ_TOKEN")
46
+
47
+ authz_config = PangeaConfig(domain="aws.us.pangea.cloud")
48
+
49
+ # Setup Pangea AuthZ service client
50
+ authz = AuthZAsync(token=PANGEA_TOKEN, config=authz_config)
51
+ """
52
+
53
+ service_name = "authz"
54
+
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
+
74
+ super().__init__(token, config, logger_name, config_id=config_id)
75
+
76
+ async def tuple_create(self, tuples: List[Tuple]) -> PangeaResponse[TupleCreateResult]:
77
+ """Create tuples.
78
+
79
+ Create tuples in the AuthZ Service. The request will fail if there is no schema
80
+ or the tuples do not validate against the schema.
81
+
82
+ Args:
83
+ tuples (List[Tuple]): List of tuples to be created.
84
+
85
+ Raises:
86
+ PangeaAPIException: If an API Error happens.
87
+
88
+ Returns:
89
+ Pangea Response with empty result.
90
+ Available response fields can be found in our
91
+ [API Documentation](https://pangea.cloud/docs/api/authz#/v1/tuple/create).
92
+
93
+ Examples:
94
+ await authz.tuple_create(
95
+ tuples=[
96
+ Tuple(
97
+ resource=Resource(type="file", id="file_1"),
98
+ relation="owner",
99
+ subject=Subject(type="user", id="user_1"),
100
+ )
101
+ ]
102
+ )
103
+ """
104
+
105
+ input_data = TupleCreateRequest(tuples=tuples)
106
+ return await self.request.post(
107
+ "v1/tuple/create", TupleCreateResult, data=input_data.model_dump(exclude_none=True)
108
+ )
109
+
110
+ async def tuple_list(
111
+ self,
112
+ filter: TupleListFilter,
113
+ size: Optional[int] = None,
114
+ last: Optional[str] = None,
115
+ order: Optional[ItemOrder] = None,
116
+ order_by: Optional[TupleOrderBy] = None,
117
+ ) -> PangeaResponse[TupleListResult]:
118
+ """List tuples.
119
+
120
+ Return a paginated list of filtered tuples. The filter is given in terms
121
+ of a tuple. Fill out the fields that you want to filter. If the filter
122
+ is empty it will return all the tuples.
123
+
124
+ Args:
125
+ filter (TupleListFilter): The filter for listing tuples.
126
+ size (Optional[int]): The size of the result set. Default is None.
127
+ last (Optional[str]): The last token from a previous response. Default is None.
128
+ order (Optional[ItemOrder]): Order results asc(ending) or desc(ending).
129
+ order_by (Optional[TupleOrderBy]): Which field to order results by.
130
+
131
+ Raises:
132
+ PangeaAPIException: If an API Error happens.
133
+
134
+ Returns:
135
+ Pangea Response with a list of tuples and the last token.
136
+ Available response fields can be found in our
137
+ [API Documentation](https://pangea.cloud/docs/api/authz#/v1/tuple/list).
138
+
139
+ Examples:
140
+ await authz.tuple_list(TupleListFilter(subject_type="user", subject_id="user_1"))
141
+ """
142
+ input_data = TupleListRequest(
143
+ filter=filter.model_dump(exclude_none=True), size=size, last=last, order=order, order_by=order_by
144
+ )
145
+ return await self.request.post("v1/tuple/list", TupleListResult, data=input_data.model_dump(exclude_none=True))
146
+
147
+ async def tuple_delete(self, tuples: List[Tuple]) -> PangeaResponse[TupleDeleteResult]:
148
+ """Delete tuples.
149
+
150
+ Delete tuples in the AuthZ Service.
151
+
152
+ Args:
153
+ tuples (List[Tuple]): List of tuples to be deleted.
154
+
155
+ Raises:
156
+ PangeaAPIException: If an API Error happens.
157
+
158
+ Returns:
159
+ Pangea Response with empty result.
160
+ Available response fields can be found in our
161
+ [API Documentation](https://pangea.cloud/docs/api/authz#/v1/tuple/delete).
162
+
163
+ Examples:
164
+ await authz.tuple_delete(
165
+ tuples=[
166
+ Tuple(
167
+ resource=Resource(type="file", id="file_1"),
168
+ relation="owner",
169
+ subject=Subject(type="user", id="user_1"),
170
+ )
171
+ ]
172
+ )
173
+ """
174
+
175
+ input_data = TupleDeleteRequest(tuples=tuples)
176
+ return await self.request.post(
177
+ "v1/tuple/delete", TupleDeleteResult, data=input_data.model_dump(exclude_none=True)
178
+ )
179
+
180
+ async def check(
181
+ self,
182
+ resource: Resource,
183
+ action: str,
184
+ subject: Subject,
185
+ debug: Optional[bool] = None,
186
+ attributes: Optional[Dict[str, Any]] = None,
187
+ ) -> PangeaResponse[CheckResult]:
188
+ """Perform a check request.
189
+
190
+ Check if a subject has permission to perform an action on the resource.
191
+
192
+ Args:
193
+ resource (Resource): The resource to check.
194
+ action (str): The action to check.
195
+ subject (Subject): The subject to check.
196
+ debug (Optional[bool]): Setting this value to True will provide a detailed analysis of the check.
197
+ attributes (Optional[Dict[str, Any]]): Additional attributes for the check.
198
+
199
+ Raises:
200
+ PangeaAPIException: If an API Error happens.
201
+
202
+ Returns:
203
+ Pangea Response with the result of the check.
204
+ Available response fields can be found in our
205
+ [API Documentation](https://pangea.cloud/docs/api/authz#/v1/check).
206
+
207
+ Examples:
208
+ await authz.check(
209
+ resource=Resource(type="file", id="file_1"),
210
+ action="update",
211
+ subject=Subject(type="user", id="user_1"),
212
+ debug=True,
213
+ )
214
+ """
215
+
216
+ input_data = CheckRequest(resource=resource, action=action, subject=subject, debug=debug, attributes=attributes)
217
+ return await self.request.post("v1/check", CheckResult, data=input_data.model_dump(exclude_none=True))
218
+
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.
223
+
224
+ Given a type, action, and subject, list all the resources in the
225
+ type that the subject has access to the action with.
226
+
227
+ Args:
228
+ type (str): The type to filter resources.
229
+ action (str): The action to filter resources.
230
+ subject (Subject): The subject to filter resources.
231
+ attributes (Optional[Dict[str, Any]]): A JSON object of attribute data.
232
+
233
+ Raises:
234
+ PangeaAPIException: If an API Error happens.
235
+
236
+ Returns:
237
+ Pangea Response with a list of resource IDs.
238
+ Available response fields can be found in our
239
+ [API Documentation](https://pangea.cloud/docs/api/authz#/v1/list-resources).
240
+
241
+ Examples:
242
+ await authz.list_resources(
243
+ type="file",
244
+ action="update",
245
+ subject=Subject(type="user", id="user_1"),
246
+ )
247
+ """
248
+
249
+ input_data = ListResourcesRequest(type=type, action=action, subject=subject, attributes=attributes)
250
+ return await self.request.post(
251
+ "v1/list-resources", ListResourcesResult, data=input_data.model_dump(exclude_none=True)
252
+ )
253
+
254
+ async def list_subjects(
255
+ self, resource: Resource, action: str, attributes: Optional[Dict[str, Any]] = None
256
+ ) -> PangeaResponse[ListSubjectsResult]:
257
+ """List subjects.
258
+
259
+ Given a resource and an action, return the list of subjects who have
260
+ access to the action for the given resource.
261
+
262
+ Args:
263
+ resource (Resource): The resource to filter subjects.
264
+ action (str): The action to filter subjects.
265
+ attributes (Optional[Dict[str, Any]]): A JSON object of attribute data.
266
+
267
+ Raises:
268
+ PangeaAPIException: If an API Error happens.
269
+
270
+ Returns:
271
+ Pangea Response with a list of subjects.
272
+ Available response fields can be found in our
273
+ [API Documentation](https://pangea.cloud/docs/api/authz#/v1/list-subjects).
274
+
275
+ Examples:
276
+ await authz.list_subjects(
277
+ resource=Resource(type="file", id="file_1"),
278
+ action="update",
279
+ )
280
+ """
281
+
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
+ )
@@ -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 (AcceptedRequestException): Exception raise by SDK on the call that is been processed.
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
- async def download_file(self, url: str, filename: Optional[str] = None) -> AttachedFile: # type: ignore[override]
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.dict())
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.dict())
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: List[Tuple] = [("upload", ("filename", file, "application/octet-stream"))]
99
- else:
100
- raise ValueError("Need to set file_path or file arguments")
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.dict(exclude_none=True)
113
- return await self.request.post("v1/scan", m.FileScanResult, data=data, files=files, poll_result=sync_call)
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.dict(exclude_none=True)
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) # type: ignore[arg-type]
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]