pangea-sdk 3.8.0__py3-none-any.whl → 5.3.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. pangea/__init__.py +2 -1
  2. pangea/asyncio/__init__.py +1 -0
  3. pangea/asyncio/file_uploader.py +39 -0
  4. pangea/asyncio/request.py +46 -23
  5. pangea/asyncio/services/__init__.py +2 -0
  6. pangea/asyncio/services/audit.py +46 -20
  7. pangea/asyncio/services/authn.py +123 -61
  8. pangea/asyncio/services/authz.py +57 -31
  9. pangea/asyncio/services/base.py +21 -2
  10. pangea/asyncio/services/embargo.py +2 -2
  11. pangea/asyncio/services/file_scan.py +24 -9
  12. pangea/asyncio/services/intel.py +104 -30
  13. pangea/asyncio/services/redact.py +52 -3
  14. pangea/asyncio/services/sanitize.py +217 -0
  15. pangea/asyncio/services/share.py +733 -0
  16. pangea/asyncio/services/vault.py +1709 -766
  17. pangea/crypto/rsa.py +135 -0
  18. pangea/deep_verify.py +7 -1
  19. pangea/dump_audit.py +9 -8
  20. pangea/file_uploader.py +35 -0
  21. pangea/request.py +70 -49
  22. pangea/response.py +36 -17
  23. pangea/services/__init__.py +2 -0
  24. pangea/services/audit/audit.py +57 -29
  25. pangea/services/audit/models.py +12 -3
  26. pangea/services/audit/signing.py +6 -5
  27. pangea/services/audit/util.py +3 -3
  28. pangea/services/authn/authn.py +120 -66
  29. pangea/services/authn/models.py +167 -11
  30. pangea/services/authz.py +53 -30
  31. pangea/services/base.py +16 -2
  32. pangea/services/embargo.py +2 -2
  33. pangea/services/file_scan.py +32 -15
  34. pangea/services/intel.py +155 -30
  35. pangea/services/redact.py +132 -3
  36. pangea/services/sanitize.py +388 -0
  37. pangea/services/share/file_format.py +170 -0
  38. pangea/services/share/share.py +1440 -0
  39. pangea/services/vault/models/asymmetric.py +120 -18
  40. pangea/services/vault/models/common.py +439 -141
  41. pangea/services/vault/models/keys.py +94 -0
  42. pangea/services/vault/models/secret.py +27 -3
  43. pangea/services/vault/models/symmetric.py +68 -22
  44. pangea/services/vault/vault.py +1690 -766
  45. pangea/tools.py +6 -7
  46. pangea/utils.py +94 -33
  47. pangea/verify_audit.py +270 -83
  48. {pangea_sdk-3.8.0.dist-info → pangea_sdk-5.3.0.dist-info}/METADATA +21 -29
  49. pangea_sdk-5.3.0.dist-info/RECORD +56 -0
  50. {pangea_sdk-3.8.0.dist-info → pangea_sdk-5.3.0.dist-info}/WHEEL +1 -1
  51. pangea_sdk-3.8.0.dist-info/RECORD +0 -46
@@ -1,9 +1,12 @@
1
1
  # Copyright 2022 Pangea Cyber Corporation
2
2
  # Author: Pangea Cyber Corporation
3
3
 
4
- from typing import Dict, List, Optional, Union
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. (Beta)
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>. Note that this service is in Beta and
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__(self, token: str, config=None, logger_name="pangea", config_id: Optional[str] = None):
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. (Beta)
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("v1/tuple/create", TupleCreateResult, data=input_data.dict(exclude_none=True))
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. (Beta)
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.dict(exclude_none=True), size=size, last=last, order=order, order_by=order_by
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.dict(exclude_none=True))
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. (Beta)
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("v1/tuple/delete", TupleDeleteResult, data=input_data.dict(exclude_none=True))
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, Union[int, str]]] = None,
186
+ attributes: Optional[Dict[str, Any]] = None,
166
187
  ) -> PangeaResponse[CheckResult]:
167
- """Perform a check request. (Beta)
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, Union[int, str]]]): Additional attributes for the check.
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.dict(exclude_none=True))
217
+ return await self.request.post("v1/check", CheckResult, data=input_data.model_dump(exclude_none=True))
198
218
 
199
- async def list_resources(self, type: str, action: str, subject: Subject) -> PangeaResponse[ListResourcesResult]:
200
- """List resources. (Beta)
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.dict(exclude_none=True)
251
+ "v1/list-resources", ListResourcesResult, data=input_data.model_dump(exclude_none=True)
230
252
  )
231
253
 
232
- async def list_subjects(self, resource: Resource, action: str) -> PangeaResponse[ListSubjectsResult]:
233
- """List subjects. (Beta)
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("v1/list-subjects", ListSubjectsResult, data=input_data.dict(exclude_none=True))
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]