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/services/authn/models.py
CHANGED
@@ -1,10 +1,16 @@
|
|
1
1
|
# Copyright 2022 Pangea Cyber Corporation
|
2
2
|
# Author: Pangea Cyber Corporation
|
3
3
|
|
4
|
+
from __future__ import annotations
|
5
|
+
|
4
6
|
import enum
|
5
7
|
from typing import Dict, List, NewType, Optional, Union
|
8
|
+
from warnings import warn
|
9
|
+
|
10
|
+
from typing_extensions import deprecated
|
6
11
|
|
7
12
|
import pangea.services.intel as im
|
13
|
+
from pangea.deprecated import pangea_deprecated
|
8
14
|
from pangea.response import APIRequestModel, APIResponseModel, PangeaResponseResult
|
9
15
|
from pangea.services.vault.models.common import JWK, JWKec, JWKrsa
|
10
16
|
|
@@ -12,9 +18,49 @@ Scopes = NewType("Scopes", List[str])
|
|
12
18
|
|
13
19
|
|
14
20
|
class Profile(Dict[str, str]):
|
15
|
-
|
16
|
-
|
17
|
-
|
21
|
+
@property
|
22
|
+
def first_name(self) -> str:
|
23
|
+
warn(
|
24
|
+
'`Profile.first_name` is deprecated. Use `Profile["first_name"]` instead.', DeprecationWarning, stacklevel=2
|
25
|
+
)
|
26
|
+
return self["first_name"]
|
27
|
+
|
28
|
+
@first_name.setter
|
29
|
+
def first_name(self, value: str) -> None:
|
30
|
+
warn(
|
31
|
+
'`Profile.first_name` is deprecated. Use `Profile["first_name"]` instead.', DeprecationWarning, stacklevel=2
|
32
|
+
)
|
33
|
+
self["first_name"] = value
|
34
|
+
|
35
|
+
@property
|
36
|
+
def last_name(self) -> str:
|
37
|
+
warn('`Profile.last_name` is deprecated. Use `Profile["last_name"]` instead.', DeprecationWarning, stacklevel=2)
|
38
|
+
return self["last_name"]
|
39
|
+
|
40
|
+
@last_name.setter
|
41
|
+
def last_name(self, value: str) -> None:
|
42
|
+
warn('`Profile.last_name` is deprecated. Use `Profile["last_name"]` instead.', DeprecationWarning, stacklevel=2)
|
43
|
+
self["last_name"] = value
|
44
|
+
|
45
|
+
@property
|
46
|
+
def phone(self) -> str:
|
47
|
+
warn('`Profile.phone` is deprecated. Use `Profile["phone"]` instead.', DeprecationWarning, stacklevel=2)
|
48
|
+
return self["phone"]
|
49
|
+
|
50
|
+
@phone.setter
|
51
|
+
def phone(self, value: str) -> None:
|
52
|
+
warn('`Profile.phone` is deprecated. Use `Profile["phone"]` instead.', DeprecationWarning, stacklevel=2)
|
53
|
+
self["phone"] = value
|
54
|
+
|
55
|
+
@deprecated("`Profile.model_dump()` is deprecated. `Profile` is already a `dict[str, str]`.")
|
56
|
+
@pangea_deprecated(reason="`Profile` is already a `dict[str, str]`.")
|
57
|
+
def model_dump(self, *, exclude_none: bool = False) -> dict[str, str]:
|
58
|
+
warn(
|
59
|
+
"`Profile.model_dump()` is deprecated. `Profile` is already a `dict[str, str]`.",
|
60
|
+
DeprecationWarning,
|
61
|
+
stacklevel=2,
|
62
|
+
)
|
63
|
+
return self
|
18
64
|
|
19
65
|
|
20
66
|
class ClientPasswordChangeRequest(APIRequestModel):
|
@@ -59,7 +105,7 @@ class SessionToken(PangeaResponseResult):
|
|
59
105
|
identity: str
|
60
106
|
email: str
|
61
107
|
scopes: Optional[Scopes] = None
|
62
|
-
profile: Profile
|
108
|
+
profile: Union[Profile, Dict[str, str]]
|
63
109
|
created_at: str
|
64
110
|
intelligence: Optional[Intelligence] = None
|
65
111
|
|
@@ -69,7 +115,7 @@ class LoginToken(SessionToken):
|
|
69
115
|
|
70
116
|
|
71
117
|
class ClientTokenCheckResult(SessionToken):
|
72
|
-
token: Optional[str]
|
118
|
+
token: Optional[str] = None
|
73
119
|
|
74
120
|
|
75
121
|
class IDProvider(str, enum.Enum):
|
@@ -150,34 +196,93 @@ class UserListOrderBy(enum.Enum):
|
|
150
196
|
|
151
197
|
|
152
198
|
class Authenticator(APIResponseModel):
|
199
|
+
"""Authenticator."""
|
200
|
+
|
153
201
|
id: str
|
202
|
+
"""An ID for an authenticator."""
|
203
|
+
|
154
204
|
type: str
|
205
|
+
"""An authentication mechanism."""
|
206
|
+
|
155
207
|
enabled: bool
|
208
|
+
"""Enabled."""
|
209
|
+
|
156
210
|
provider: Optional[str] = None
|
211
|
+
"""Provider."""
|
212
|
+
|
213
|
+
provider_name: Optional[str] = None
|
214
|
+
"""Provider name."""
|
215
|
+
|
157
216
|
rpid: Optional[str] = None
|
217
|
+
"""RPID."""
|
218
|
+
|
158
219
|
phase: Optional[str] = None
|
220
|
+
"""Phase."""
|
221
|
+
|
222
|
+
enrolling_browser: Optional[str] = None
|
223
|
+
"""Enrolling browser."""
|
224
|
+
|
225
|
+
enrolling_ip: Optional[str] = None
|
226
|
+
"""Enrolling IP."""
|
227
|
+
|
228
|
+
created_at: str
|
229
|
+
"""A time in ISO-8601 format."""
|
230
|
+
|
231
|
+
updated_at: str
|
232
|
+
"""A time in ISO-8601 format."""
|
233
|
+
|
234
|
+
state: Optional[str] = None
|
235
|
+
"""State."""
|
159
236
|
|
160
237
|
|
161
238
|
class User(PangeaResponseResult):
|
162
239
|
id: str
|
240
|
+
"""The identity of a user or a service."""
|
241
|
+
|
163
242
|
email: str
|
164
|
-
|
243
|
+
"""An email address."""
|
244
|
+
|
245
|
+
username: str
|
246
|
+
"""A username."""
|
247
|
+
|
248
|
+
profile: Union[Profile, Dict[str, str]]
|
249
|
+
"""A user profile as a collection of string properties."""
|
250
|
+
|
165
251
|
verified: bool
|
252
|
+
"""True if the user's email has been verified."""
|
253
|
+
|
166
254
|
disabled: bool
|
255
|
+
"""True if the service administrator has disabled user account."""
|
256
|
+
|
167
257
|
accepted_eula_id: Optional[str] = None
|
258
|
+
"""An ID for an agreement."""
|
259
|
+
|
168
260
|
accepted_privacy_policy_id: Optional[str] = None
|
261
|
+
"""An ID for an agreement."""
|
262
|
+
|
169
263
|
last_login_at: Optional[str] = None
|
264
|
+
"""A time in ISO-8601 format."""
|
265
|
+
|
170
266
|
created_at: str
|
267
|
+
"""A time in ISO-8601 format."""
|
268
|
+
|
171
269
|
login_count: int = 0
|
172
270
|
last_login_ip: Optional[str] = None
|
173
271
|
last_login_city: Optional[str] = None
|
174
272
|
last_login_country: Optional[str] = None
|
175
273
|
authenticators: List[Authenticator] = []
|
274
|
+
"""A list of authenticators."""
|
176
275
|
|
177
276
|
|
178
277
|
class UserCreateRequest(APIRequestModel):
|
179
278
|
email: str
|
180
|
-
|
279
|
+
"""An email address."""
|
280
|
+
|
281
|
+
profile: Union[Profile, Dict[str, str]]
|
282
|
+
"""A user profile as a collection of string properties."""
|
283
|
+
|
284
|
+
username: Optional[str] = None
|
285
|
+
"""A username."""
|
181
286
|
|
182
287
|
|
183
288
|
class UserCreateResult(User):
|
@@ -186,7 +291,13 @@ class UserCreateResult(User):
|
|
186
291
|
|
187
292
|
class UserDeleteRequest(APIRequestModel):
|
188
293
|
email: Optional[str] = None
|
294
|
+
"""An email address."""
|
295
|
+
|
189
296
|
id: Optional[str] = None
|
297
|
+
"""The identity of a user or a service."""
|
298
|
+
|
299
|
+
username: Optional[str] = None
|
300
|
+
"""A username."""
|
190
301
|
|
191
302
|
|
192
303
|
class UserDeleteResult(PangeaResponseResult):
|
@@ -289,7 +400,7 @@ class UserInviterOrderBy(enum.Enum):
|
|
289
400
|
|
290
401
|
|
291
402
|
class UserInviteListFilter(APIRequestModel):
|
292
|
-
callback: Optional[str]
|
403
|
+
callback: Optional[str] = None
|
293
404
|
callback__contains: Optional[List[str]] = None
|
294
405
|
callback__in: Optional[List[str]] = None
|
295
406
|
created_at: Optional[str] = None
|
@@ -343,7 +454,13 @@ class UserInviteDeleteResult(PangeaResponseResult):
|
|
343
454
|
|
344
455
|
class UserProfileGetRequest(APIRequestModel):
|
345
456
|
id: Optional[str] = None
|
457
|
+
"""The identity of a user or a service."""
|
458
|
+
|
346
459
|
email: Optional[str] = None
|
460
|
+
"""An email address."""
|
461
|
+
|
462
|
+
username: Optional[str] = None
|
463
|
+
"""A username."""
|
347
464
|
|
348
465
|
|
349
466
|
class UserProfileGetResult(User):
|
@@ -351,9 +468,17 @@ class UserProfileGetResult(User):
|
|
351
468
|
|
352
469
|
|
353
470
|
class UserProfileUpdateRequest(APIRequestModel):
|
354
|
-
profile: Profile
|
471
|
+
profile: Union[Profile, Dict[str, str]]
|
472
|
+
"""Updates to a user profile."""
|
473
|
+
|
355
474
|
id: Optional[str] = None
|
475
|
+
"""The identity of a user or a service."""
|
476
|
+
|
356
477
|
email: Optional[str] = None
|
478
|
+
"""An email address."""
|
479
|
+
|
480
|
+
username: Optional[str] = None
|
481
|
+
"""A username."""
|
357
482
|
|
358
483
|
|
359
484
|
class UserProfileUpdateResult(User):
|
@@ -362,9 +487,25 @@ class UserProfileUpdateResult(User):
|
|
362
487
|
|
363
488
|
class UserUpdateRequest(APIRequestModel):
|
364
489
|
id: Optional[str] = None
|
490
|
+
"""The identity of a user or a service."""
|
491
|
+
|
365
492
|
email: Optional[str] = None
|
493
|
+
"""An email address."""
|
494
|
+
|
366
495
|
disabled: Optional[bool] = None
|
496
|
+
"""
|
497
|
+
New disabled value. Disabling a user account will prevent them from logging
|
498
|
+
in.
|
499
|
+
"""
|
500
|
+
|
367
501
|
unlock: Optional[bool] = None
|
502
|
+
"""
|
503
|
+
Unlock a user account if it has been locked out due to failed authentication
|
504
|
+
attempts.
|
505
|
+
"""
|
506
|
+
|
507
|
+
username: Optional[str] = None
|
508
|
+
"""A username."""
|
368
509
|
|
369
510
|
|
370
511
|
class UserUpdateResult(User):
|
@@ -386,8 +527,16 @@ class ClientJWKSResult(PangeaResponseResult):
|
|
386
527
|
|
387
528
|
class UserAuthenticatorsDeleteRequest(APIRequestModel):
|
388
529
|
id: Optional[str] = None
|
530
|
+
"""The identity of a user or a service."""
|
531
|
+
|
389
532
|
email: Optional[str] = None
|
533
|
+
"""An email address."""
|
534
|
+
|
390
535
|
authenticator_id: str
|
536
|
+
"""An ID for an authenticator."""
|
537
|
+
|
538
|
+
username: Optional[str] = None
|
539
|
+
"""A username."""
|
391
540
|
|
392
541
|
|
393
542
|
class UserAuthenticatorsDeleteResult(PangeaResponseResult):
|
@@ -396,11 +545,18 @@ class UserAuthenticatorsDeleteResult(PangeaResponseResult):
|
|
396
545
|
|
397
546
|
class UserAuthenticatorsListRequest(APIRequestModel):
|
398
547
|
email: Optional[str] = None
|
548
|
+
"""An email address."""
|
549
|
+
|
399
550
|
id: Optional[str] = None
|
551
|
+
"""The identity of a user or a service."""
|
552
|
+
|
553
|
+
username: Optional[str] = None
|
554
|
+
"""A username."""
|
400
555
|
|
401
556
|
|
402
557
|
class UserAuthenticatorsListResult(PangeaResponseResult):
|
403
558
|
authenticators: List[Authenticator] = []
|
559
|
+
"""A list of authenticators."""
|
404
560
|
|
405
561
|
|
406
562
|
class FlowCompleteRequest(APIRequestModel):
|
@@ -499,7 +655,7 @@ class FlowUpdateDataPassword(APIRequestModel):
|
|
499
655
|
|
500
656
|
|
501
657
|
class FlowUpdateDataProfile(APIRequestModel):
|
502
|
-
profile: Profile
|
658
|
+
profile: Union[Profile, Dict[str, str]]
|
503
659
|
|
504
660
|
|
505
661
|
class FlowUpdateDataProvisionalEnrollment(APIRequestModel):
|
@@ -621,7 +777,7 @@ class SessionItem(APIResponseModel):
|
|
621
777
|
expire: str
|
622
778
|
email: str
|
623
779
|
scopes: Optional[Scopes] = None
|
624
|
-
profile: Profile
|
780
|
+
profile: Union[Profile, Dict[str, str]]
|
625
781
|
created_at: str
|
626
782
|
active_token: Optional[SessionToken] = None
|
627
783
|
|
pangea/services/authz.py
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
# Copyright 2022 Pangea Cyber Corporation
|
2
2
|
# Author: Pangea Cyber Corporation
|
3
|
+
from __future__ import annotations
|
3
4
|
|
4
5
|
import enum
|
5
6
|
from typing import Any, Dict, List, Optional, Union
|
6
7
|
|
8
|
+
from pangea.config import PangeaConfig
|
7
9
|
from pangea.response import APIRequestModel, APIResponseModel, PangeaResponse, PangeaResponseResult
|
8
10
|
from pangea.services.base import ServiceBase
|
9
11
|
|
@@ -113,7 +115,7 @@ class CheckRequest(APIRequestModel):
|
|
113
115
|
class DebugPath(APIResponseModel):
|
114
116
|
type: str
|
115
117
|
id: str
|
116
|
-
action: Optional[str]
|
118
|
+
action: Optional[str] = None
|
117
119
|
|
118
120
|
|
119
121
|
class Debug(APIResponseModel):
|
@@ -132,6 +134,7 @@ class ListResourcesRequest(APIRequestModel):
|
|
132
134
|
type: str
|
133
135
|
action: str
|
134
136
|
subject: Subject
|
137
|
+
attributes: Optional[Dict[str, Any]] = None
|
135
138
|
|
136
139
|
|
137
140
|
class ListResourcesResult(PangeaResponseResult):
|
@@ -141,6 +144,7 @@ class ListResourcesResult(PangeaResponseResult):
|
|
141
144
|
class ListSubjectsRequest(APIRequestModel):
|
142
145
|
resource: Resource
|
143
146
|
action: str
|
147
|
+
attributes: Optional[Dict[str, Any]] = None
|
144
148
|
|
145
149
|
|
146
150
|
class ListSubjectsResult(PangeaResponseResult):
|
@@ -148,12 +152,11 @@ class ListSubjectsResult(PangeaResponseResult):
|
|
148
152
|
|
149
153
|
|
150
154
|
class AuthZ(ServiceBase):
|
151
|
-
"""AuthZ service client.
|
155
|
+
"""AuthZ service client.
|
152
156
|
|
153
157
|
Provides methods to interact with the Pangea AuthZ Service.
|
154
158
|
Documentation for the AuthZ Service API can be found at
|
155
|
-
<https://pangea.cloud/docs/api/authz>.
|
156
|
-
is subject to change.
|
159
|
+
<https://pangea.cloud/docs/api/authz>.
|
157
160
|
|
158
161
|
Examples:
|
159
162
|
import os
|
@@ -170,15 +173,32 @@ class AuthZ(ServiceBase):
|
|
170
173
|
|
171
174
|
service_name = "authz"
|
172
175
|
|
173
|
-
def __init__(
|
176
|
+
def __init__(
|
177
|
+
self, token: str, config: PangeaConfig | None = None, logger_name: str = "pangea", config_id: str | None = None
|
178
|
+
) -> None:
|
179
|
+
"""
|
180
|
+
AuthZ client
|
181
|
+
|
182
|
+
Initializes a new AuthZ client.
|
183
|
+
|
184
|
+
Args:
|
185
|
+
token: Pangea API token.
|
186
|
+
config: Configuration.
|
187
|
+
logger_name: Logger name.
|
188
|
+
config_id: Configuration ID.
|
189
|
+
|
190
|
+
Examples:
|
191
|
+
config = PangeaConfig(domain="aws.us.pangea.cloud")
|
192
|
+
authz = AuthZ(token="pangea_token", config=config)
|
193
|
+
"""
|
194
|
+
|
174
195
|
super().__init__(token, config, logger_name, config_id=config_id)
|
175
196
|
|
176
197
|
def tuple_create(self, tuples: List[Tuple]) -> PangeaResponse[TupleCreateResult]:
|
177
|
-
"""Create tuples.
|
198
|
+
"""Create tuples.
|
178
199
|
|
179
200
|
Create tuples in the AuthZ Service. The request will fail if there is no schema
|
180
201
|
or the tuples do not validate against the schema.
|
181
|
-
How to install a [Beta release](https://pangea.cloud/docs/sdk/python/#beta-releases).
|
182
202
|
|
183
203
|
Args:
|
184
204
|
tuples (List[Tuple]): List of tuples to be created.
|
@@ -204,7 +224,7 @@ class AuthZ(ServiceBase):
|
|
204
224
|
"""
|
205
225
|
|
206
226
|
input_data = TupleCreateRequest(tuples=tuples)
|
207
|
-
return self.request.post("v1/tuple/create", TupleCreateResult, data=input_data.
|
227
|
+
return self.request.post("v1/tuple/create", TupleCreateResult, data=input_data.model_dump(exclude_none=True))
|
208
228
|
|
209
229
|
def tuple_list(
|
210
230
|
self,
|
@@ -214,12 +234,11 @@ class AuthZ(ServiceBase):
|
|
214
234
|
order: Optional[ItemOrder] = None,
|
215
235
|
order_by: Optional[TupleOrderBy] = None,
|
216
236
|
) -> PangeaResponse[TupleListResult]:
|
217
|
-
"""List tuples.
|
237
|
+
"""List tuples.
|
218
238
|
|
219
239
|
Return a paginated list of filtered tuples. The filter is given in terms
|
220
240
|
of a tuple. Fill out the fields that you want to filter. If the filter
|
221
241
|
is empty it will return all the tuples.
|
222
|
-
How to install a [Beta release](https://pangea.cloud/docs/sdk/python/#beta-releases).
|
223
242
|
|
224
243
|
Args:
|
225
244
|
filter (TupleListFilter): The filter for listing tuples.
|
@@ -240,15 +259,14 @@ class AuthZ(ServiceBase):
|
|
240
259
|
authz.tuple_list(TupleListFilter(subject_type="user", subject_id="user_1"))
|
241
260
|
"""
|
242
261
|
input_data = TupleListRequest(
|
243
|
-
filter=filter.
|
262
|
+
filter=filter.model_dump(exclude_none=True), size=size, last=last, order=order, order_by=order_by
|
244
263
|
)
|
245
|
-
return self.request.post("v1/tuple/list", TupleListResult, data=input_data.
|
264
|
+
return self.request.post("v1/tuple/list", TupleListResult, data=input_data.model_dump(exclude_none=True))
|
246
265
|
|
247
266
|
def tuple_delete(self, tuples: List[Tuple]) -> PangeaResponse[TupleDeleteResult]:
|
248
|
-
"""Delete tuples.
|
267
|
+
"""Delete tuples.
|
249
268
|
|
250
269
|
Delete tuples in the AuthZ Service.
|
251
|
-
How to install a [Beta release](https://pangea.cloud/docs/sdk/python/#beta-releases).
|
252
270
|
|
253
271
|
Args:
|
254
272
|
tuples (List[Tuple]): List of tuples to be deleted.
|
@@ -274,7 +292,7 @@ class AuthZ(ServiceBase):
|
|
274
292
|
"""
|
275
293
|
|
276
294
|
input_data = TupleDeleteRequest(tuples=tuples)
|
277
|
-
return self.request.post("v1/tuple/delete", TupleDeleteResult, data=input_data.
|
295
|
+
return self.request.post("v1/tuple/delete", TupleDeleteResult, data=input_data.model_dump(exclude_none=True))
|
278
296
|
|
279
297
|
def check(
|
280
298
|
self,
|
@@ -282,19 +300,18 @@ class AuthZ(ServiceBase):
|
|
282
300
|
action: str,
|
283
301
|
subject: Subject,
|
284
302
|
debug: Optional[bool] = None,
|
285
|
-
attributes: Optional[Dict[str,
|
303
|
+
attributes: Optional[Dict[str, Any]] = None,
|
286
304
|
) -> PangeaResponse[CheckResult]:
|
287
|
-
"""Perform a check request.
|
305
|
+
"""Perform a check request.
|
288
306
|
|
289
307
|
Check if a subject has permission to perform an action on the resource.
|
290
|
-
How to install a [Beta release](https://pangea.cloud/docs/sdk/python/#beta-releases).
|
291
308
|
|
292
309
|
Args:
|
293
310
|
resource (Resource): The resource to check.
|
294
311
|
action (str): The action to check.
|
295
312
|
subject (Subject): The subject to check.
|
296
313
|
debug (Optional[bool]): Setting this value to True will provide a detailed analysis of the check.
|
297
|
-
attributes (Optional[Dict[str,
|
314
|
+
attributes (Optional[Dict[str, Any]]): Additional attributes for the check.
|
298
315
|
|
299
316
|
Raises:
|
300
317
|
PangeaAPIException: If an API Error happens.
|
@@ -314,19 +331,21 @@ class AuthZ(ServiceBase):
|
|
314
331
|
"""
|
315
332
|
|
316
333
|
input_data = CheckRequest(resource=resource, action=action, subject=subject, debug=debug, attributes=attributes)
|
317
|
-
return self.request.post("v1/check", CheckResult, data=input_data.
|
334
|
+
return self.request.post("v1/check", CheckResult, data=input_data.model_dump(exclude_none=True))
|
318
335
|
|
319
|
-
def list_resources(
|
320
|
-
|
336
|
+
def list_resources(
|
337
|
+
self, type: str, action: str, subject: Subject, attributes: Optional[Dict[str, Any]] = None
|
338
|
+
) -> PangeaResponse[ListResourcesResult]:
|
339
|
+
"""List resources.
|
321
340
|
|
322
341
|
Given a type, action, and subject, list all the resources in the
|
323
342
|
type that the subject has access to the action with.
|
324
|
-
How to install a [Beta release](https://pangea.cloud/docs/sdk/python/#beta-releases).
|
325
343
|
|
326
344
|
Args:
|
327
345
|
type (str): The type to filter resources.
|
328
346
|
action (str): The action to filter resources.
|
329
347
|
subject (Subject): The subject to filter resources.
|
348
|
+
attributes (Optional[Dict[str, Any]]): A JSON object of attribute data.
|
330
349
|
|
331
350
|
Raises:
|
332
351
|
PangeaAPIException: If an API Error happens.
|
@@ -344,19 +363,23 @@ class AuthZ(ServiceBase):
|
|
344
363
|
)
|
345
364
|
"""
|
346
365
|
|
347
|
-
input_data = ListResourcesRequest(type=type, action=action, subject=subject)
|
348
|
-
return self.request.post(
|
366
|
+
input_data = ListResourcesRequest(type=type, action=action, subject=subject, attributes=attributes)
|
367
|
+
return self.request.post(
|
368
|
+
"v1/list-resources", ListResourcesResult, data=input_data.model_dump(exclude_none=True)
|
369
|
+
)
|
349
370
|
|
350
|
-
def list_subjects(
|
351
|
-
|
371
|
+
def list_subjects(
|
372
|
+
self, resource: Resource, action: str, attributes: Optional[Dict[str, Any]] = None
|
373
|
+
) -> PangeaResponse[ListSubjectsResult]:
|
374
|
+
"""List subjects.
|
352
375
|
|
353
376
|
Given a resource and an action, return the list of subjects who have
|
354
377
|
access to the action for the given resource.
|
355
|
-
How to install a [Beta release](https://pangea.cloud/docs/sdk/python/#beta-releases).
|
356
378
|
|
357
379
|
Args:
|
358
380
|
resource (Resource): The resource to filter subjects.
|
359
381
|
action (str): The action to filter subjects.
|
382
|
+
attributes (Optional[Dict[str, Any]]): A JSON object of attribute data.
|
360
383
|
|
361
384
|
Raises:
|
362
385
|
PangeaAPIException: If an API Error happens.
|
@@ -373,5 +396,5 @@ class AuthZ(ServiceBase):
|
|
373
396
|
)
|
374
397
|
"""
|
375
398
|
|
376
|
-
input_data = ListSubjectsRequest(resource=resource, action=action)
|
377
|
-
return self.request.post("v1/list-subjects", ListSubjectsResult, data=input_data.
|
399
|
+
input_data = ListSubjectsRequest(resource=resource, action=action, attributes=attributes)
|
400
|
+
return self.request.post("v1/list-subjects", ListSubjectsResult, data=input_data.model_dump(exclude_none=True))
|
pangea/services/base.py
CHANGED
@@ -80,7 +80,8 @@ class ServiceBase(object):
|
|
80
80
|
Returns request's result that has been accepted by the server
|
81
81
|
|
82
82
|
Args:
|
83
|
-
exception
|
83
|
+
exception: Exception that was previously raised by the SDK on a call
|
84
|
+
that is being processed.
|
84
85
|
|
85
86
|
Returns:
|
86
87
|
PangeaResponse
|
@@ -100,5 +101,18 @@ class ServiceBase(object):
|
|
100
101
|
else:
|
101
102
|
raise AttributeError("Need to set exception, response or request_id")
|
102
103
|
|
103
|
-
def download_file(self, url: str, filename:
|
104
|
+
def download_file(self, url: str, filename: str | None = None) -> AttachedFile:
|
105
|
+
"""
|
106
|
+
Download file
|
107
|
+
|
108
|
+
Download a file from the specified URL and save it with the given
|
109
|
+
filename.
|
110
|
+
|
111
|
+
Args:
|
112
|
+
url: URL of the file to download
|
113
|
+
filename: Name to save the downloaded file as. If not provided, the
|
114
|
+
filename will be determined from the Content-Disposition header or
|
115
|
+
the URL.
|
116
|
+
"""
|
117
|
+
|
104
118
|
return self.request.download_file(url=url, filename=filename)
|
pangea/services/embargo.py
CHANGED
@@ -103,7 +103,7 @@ class Embargo(ServiceBase):
|
|
103
103
|
response = embargo.ip_check("190.6.64.94")
|
104
104
|
"""
|
105
105
|
input = IPCheckRequest(ip=ip)
|
106
|
-
return self.request.post("v1/ip/check", EmbargoResult, data=input.
|
106
|
+
return self.request.post("v1/ip/check", EmbargoResult, data=input.model_dump())
|
107
107
|
|
108
108
|
def iso_check(self, iso_code: str) -> PangeaResponse[EmbargoResult]:
|
109
109
|
"""
|
@@ -130,4 +130,4 @@ class Embargo(ServiceBase):
|
|
130
130
|
response = embargo.iso_check("CU")
|
131
131
|
"""
|
132
132
|
input = ISOCheckRequest(iso_code=iso_code)
|
133
|
-
return self.request.post("v1/iso/check", result_class=EmbargoResult, data=input.
|
133
|
+
return self.request.post("v1/iso/check", result_class=EmbargoResult, data=input.model_dump())
|
pangea/services/file_scan.py
CHANGED
@@ -11,22 +11,25 @@ from pangea.utils import FileUploadParams, get_file_upload_params
|
|
11
11
|
|
12
12
|
|
13
13
|
class FileScanRequest(APIRequestModel):
|
14
|
-
"""
|
15
|
-
File Scan request data
|
16
|
-
|
17
|
-
provider (str, optional): Provider of the information. Default provider defined by the configuration.
|
18
|
-
verbose (bool, optional): Echo back the parameters of the API in the response
|
19
|
-
raw (bool, optional): Return additional details from the provider.
|
20
|
-
"""
|
14
|
+
"""File Scan request data."""
|
21
15
|
|
22
16
|
verbose: Optional[bool] = None
|
17
|
+
"""Echo back the parameters of the API in the response."""
|
18
|
+
|
23
19
|
raw: Optional[bool] = None
|
20
|
+
"""Return additional details from the provider."""
|
21
|
+
|
24
22
|
provider: Optional[str] = None
|
23
|
+
"""Provider of the information. Default provider defined by the configuration."""
|
24
|
+
|
25
25
|
size: Optional[int] = None
|
26
26
|
crc32c: Optional[str] = None
|
27
27
|
sha256: Optional[str] = None
|
28
28
|
source_url: Optional[str] = None
|
29
|
+
"""A URL where the file to be scanned can be downloaded."""
|
30
|
+
|
29
31
|
transfer_method: TransferMethod = TransferMethod.POST_URL
|
32
|
+
"""The transfer method used to upload the file data."""
|
30
33
|
|
31
34
|
|
32
35
|
class FileScanData(PangeaResponseResult):
|
@@ -71,7 +74,6 @@ class FileScan(ServiceBase):
|
|
71
74
|
"""
|
72
75
|
|
73
76
|
service_name = "file-scan"
|
74
|
-
version = "v1"
|
75
77
|
|
76
78
|
def file_scan(
|
77
79
|
self,
|
@@ -92,12 +94,14 @@ class FileScan(ServiceBase):
|
|
92
94
|
OperationId: file_scan_post_v1_scan
|
93
95
|
|
94
96
|
Args:
|
95
|
-
file (io.BufferedReader, optional): file to be scanned (should be opened with read permissions and in binary format)
|
96
97
|
file_path (str, optional): filepath to be opened and scanned
|
98
|
+
file (io.BufferedReader, optional): file to be scanned (should be opened with read permissions and in binary format)
|
97
99
|
verbose (bool, optional): Echo the API parameters in the response
|
98
100
|
raw (bool, optional): Include raw data from this provider
|
99
101
|
provider (str, optional): Scan file using this provider
|
100
102
|
sync_call (bool, optional): True to wait until server returns a result, False to return immediately and retrieve result asynchronously
|
103
|
+
transfer_method (TransferMethod, optional): Transfer method used to upload the file data.
|
104
|
+
source_url (str, optional): A URL where the Pangea APIs can fetch the contents of the input file.
|
101
105
|
|
102
106
|
Raises:
|
103
107
|
PangeaAPIException: If an API Error happens
|
@@ -118,6 +122,15 @@ class FileScan(ServiceBase):
|
|
118
122
|
print(f"\\t{err.detail} \\n")
|
119
123
|
"""
|
120
124
|
|
125
|
+
if transfer_method == TransferMethod.SOURCE_URL and source_url is None:
|
126
|
+
raise ValueError("`source_url` argument is required when using `TransferMethod.SOURCE_URL`.")
|
127
|
+
|
128
|
+
if source_url is not None and transfer_method != TransferMethod.SOURCE_URL:
|
129
|
+
raise ValueError(
|
130
|
+
"`transfer_method` should be `TransferMethod.SOURCE_URL` when using the `source_url` argument."
|
131
|
+
)
|
132
|
+
|
133
|
+
files: Optional[List[Tuple]] = None
|
121
134
|
if file or file_path:
|
122
135
|
if file_path:
|
123
136
|
file = open(file_path, "rb")
|
@@ -128,9 +141,9 @@ class FileScan(ServiceBase):
|
|
128
141
|
size = params.size
|
129
142
|
else:
|
130
143
|
crc, sha, size = None, None, None
|
131
|
-
files
|
132
|
-
|
133
|
-
raise ValueError("Need to set file_path or
|
144
|
+
files = [("upload", ("filename", file, "application/octet-stream"))]
|
145
|
+
elif source_url is None:
|
146
|
+
raise ValueError("Need to set one of `file_path`, `file`, or `source_url` arguments.")
|
134
147
|
|
135
148
|
input = FileScanRequest(
|
136
149
|
verbose=verbose,
|
@@ -142,8 +155,12 @@ class FileScan(ServiceBase):
|
|
142
155
|
transfer_method=transfer_method,
|
143
156
|
source_url=source_url,
|
144
157
|
)
|
145
|
-
data = input.
|
146
|
-
|
158
|
+
data = input.model_dump(exclude_none=True)
|
159
|
+
try:
|
160
|
+
return self.request.post("v1/scan", FileScanResult, data=data, files=files, poll_result=sync_call)
|
161
|
+
finally:
|
162
|
+
if file_path and file:
|
163
|
+
file.close()
|
147
164
|
|
148
165
|
def request_upload_url(
|
149
166
|
self,
|
@@ -164,7 +181,7 @@ class FileScan(ServiceBase):
|
|
164
181
|
input.sha256 = params.sha256_hex
|
165
182
|
input.size = params.size
|
166
183
|
|
167
|
-
data = input.
|
184
|
+
data = input.model_dump(exclude_none=True)
|
168
185
|
return self.request.request_presigned_url("v1/scan", FileScanResult, data=data)
|
169
186
|
|
170
187
|
|