pangea-sdk 3.8.0b1__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 (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]