pangea-sdk 6.1.1__py3-none-any.whl → 6.2.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 +9 -1
- pangea/asyncio/__init__.py +1 -0
- pangea/asyncio/file_uploader.py +4 -2
- pangea/asyncio/request.py +52 -17
- pangea/asyncio/services/__init__.py +2 -0
- pangea/asyncio/services/ai_guard.py +9 -12
- pangea/asyncio/services/audit.py +6 -1
- pangea/asyncio/services/authn.py +15 -4
- pangea/asyncio/services/base.py +4 -0
- pangea/asyncio/services/file_scan.py +7 -1
- pangea/asyncio/services/intel.py +26 -28
- pangea/asyncio/services/redact.py +4 -0
- pangea/asyncio/services/sanitize.py +5 -1
- pangea/asyncio/services/share.py +5 -1
- pangea/asyncio/services/vault.py +4 -0
- pangea/audit_logger.py +3 -1
- pangea/deep_verify.py +13 -13
- pangea/deprecated.py +1 -1
- pangea/dump_audit.py +2 -3
- pangea/exceptions.py +8 -5
- pangea/file_uploader.py +4 -0
- pangea/request.py +63 -47
- pangea/response.py +21 -18
- pangea/services/__init__.py +2 -0
- pangea/services/ai_guard.py +35 -24
- pangea/services/audit/audit.py +10 -7
- pangea/services/audit/models.py +71 -34
- pangea/services/audit/signing.py +1 -1
- pangea/services/audit/util.py +10 -10
- pangea/services/authn/authn.py +15 -4
- pangea/services/authn/models.py +10 -56
- pangea/services/authz.py +4 -0
- pangea/services/base.py +7 -4
- pangea/services/embargo.py +6 -0
- pangea/services/file_scan.py +7 -1
- pangea/services/intel.py +36 -19
- pangea/services/redact.py +4 -0
- pangea/services/sanitize.py +5 -1
- pangea/services/share/share.py +13 -7
- pangea/services/vault/models/asymmetric.py +4 -0
- pangea/services/vault/models/common.py +4 -0
- pangea/services/vault/models/symmetric.py +4 -0
- pangea/services/vault/vault.py +2 -4
- pangea/tools.py +13 -9
- pangea/utils.py +3 -5
- pangea/verify_audit.py +23 -27
- {pangea_sdk-6.1.1.dist-info → pangea_sdk-6.2.0.dist-info}/METADATA +36 -17
- pangea_sdk-6.2.0.dist-info/RECORD +60 -0
- pangea_sdk-6.1.1.dist-info/RECORD +0 -60
- {pangea_sdk-6.1.1.dist-info → pangea_sdk-6.2.0.dist-info}/WHEEL +0 -0
pangea/services/ai_guard.py
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
from
|
3
|
+
from collections.abc import Sequence
|
4
|
+
from typing import Generic, Literal, Optional, overload
|
5
|
+
|
6
|
+
from pydantic import BaseModel, ConfigDict
|
7
|
+
from typing_extensions import TypeVar
|
4
8
|
|
5
9
|
from pangea.config import PangeaConfig
|
6
10
|
from pangea.response import APIRequestModel, APIResponseModel, PangeaResponse, PangeaResponseResult
|
@@ -17,6 +21,13 @@ MaliciousEntityAction = Literal["report", "defang", "disabled", "block"]
|
|
17
21
|
PiiEntityAction = Literal["disabled", "report", "block", "mask", "partial_masking", "replacement", "hash", "fpe"]
|
18
22
|
|
19
23
|
|
24
|
+
class Message(BaseModel):
|
25
|
+
model_config = ConfigDict(extra="forbid")
|
26
|
+
|
27
|
+
role: str
|
28
|
+
content: str
|
29
|
+
|
30
|
+
|
20
31
|
class CodeDetectionOverride(APIRequestModel):
|
21
32
|
disabled: Optional[bool] = None
|
22
33
|
action: Optional[Literal["report", "block"]] = None
|
@@ -24,14 +35,14 @@ class CodeDetectionOverride(APIRequestModel):
|
|
24
35
|
|
25
36
|
class LanguageDetectionOverride(APIRequestModel):
|
26
37
|
disabled: Optional[bool] = None
|
27
|
-
allow: Optional[
|
28
|
-
block: Optional[
|
29
|
-
report: Optional[
|
38
|
+
allow: Optional[list[str]] = None
|
39
|
+
block: Optional[list[str]] = None
|
40
|
+
report: Optional[list[str]] = None
|
30
41
|
|
31
42
|
|
32
43
|
class TopicDetectionOverride(APIRequestModel):
|
33
44
|
disabled: Optional[bool] = None
|
34
|
-
block: Optional[
|
45
|
+
block: Optional[list[str]] = None
|
35
46
|
|
36
47
|
|
37
48
|
class PromptInjectionOverride(APIRequestModel):
|
@@ -179,7 +190,7 @@ class PromptInjectionResult(APIResponseModel):
|
|
179
190
|
action: str
|
180
191
|
"""The action taken by this Detector"""
|
181
192
|
|
182
|
-
analyzer_responses:
|
193
|
+
analyzer_responses: list[AnalyzerResponse]
|
183
194
|
"""Triggered prompt injection analyzers."""
|
184
195
|
|
185
196
|
|
@@ -192,20 +203,20 @@ class PiiEntity(APIResponseModel):
|
|
192
203
|
|
193
204
|
|
194
205
|
class PiiEntityResult(APIResponseModel):
|
195
|
-
entities:
|
206
|
+
entities: list[PiiEntity]
|
196
207
|
"""Detected redaction rules."""
|
197
208
|
|
198
209
|
|
199
210
|
class MaliciousEntity(APIResponseModel):
|
200
211
|
type: str
|
201
212
|
value: str
|
202
|
-
action: str
|
213
|
+
action: Optional[str] = None
|
203
214
|
start_pos: Optional[int] = None
|
204
|
-
raw: Optional[
|
215
|
+
raw: Optional[dict[str, object]] = None
|
205
216
|
|
206
217
|
|
207
218
|
class MaliciousEntityResult(APIResponseModel):
|
208
|
-
entities:
|
219
|
+
entities: list[MaliciousEntity]
|
209
220
|
"""Detected harmful items."""
|
210
221
|
|
211
222
|
|
@@ -215,11 +226,11 @@ class CustomEntity(APIResponseModel):
|
|
215
226
|
action: str
|
216
227
|
"""The action taken on this Entity"""
|
217
228
|
start_pos: Optional[int] = None
|
218
|
-
raw: Optional[
|
229
|
+
raw: Optional[dict[str, object]] = None
|
219
230
|
|
220
231
|
|
221
232
|
class CustomEntityResult(APIResponseModel):
|
222
|
-
entities:
|
233
|
+
entities: list[CustomEntity]
|
223
234
|
"""Detected redaction rules."""
|
224
235
|
|
225
236
|
|
@@ -233,7 +244,7 @@ class SecretsEntity(APIResponseModel):
|
|
233
244
|
|
234
245
|
|
235
246
|
class SecretsEntityResult(APIResponseModel):
|
236
|
-
entities:
|
247
|
+
entities: list[SecretsEntity]
|
237
248
|
"""Detected redaction rules."""
|
238
249
|
|
239
250
|
|
@@ -266,22 +277,22 @@ class TextGuardDetectors(APIResponseModel):
|
|
266
277
|
prompt_injection: Optional[TextGuardDetector[PromptInjectionResult]] = None
|
267
278
|
pii_entity: Optional[TextGuardDetector[PiiEntityResult]] = None
|
268
279
|
malicious_entity: Optional[TextGuardDetector[MaliciousEntityResult]] = None
|
269
|
-
custom_entity: Optional[TextGuardDetector[
|
280
|
+
custom_entity: Optional[TextGuardDetector[object]] = None
|
270
281
|
secrets_detection: Optional[TextGuardDetector[SecretsEntityResult]] = None
|
271
|
-
profanity_and_toxicity: Optional[TextGuardDetector[
|
282
|
+
profanity_and_toxicity: Optional[TextGuardDetector[object]] = None
|
272
283
|
language_detection: Optional[TextGuardDetector[LanguageDetectionResult]] = None
|
273
284
|
topic_detection: Optional[TextGuardDetector[TopicDetectionResult]] = None
|
274
285
|
code_detection: Optional[TextGuardDetector[CodeDetectionResult]] = None
|
275
286
|
|
276
287
|
|
277
|
-
class TextGuardResult(PangeaResponseResult
|
288
|
+
class TextGuardResult(PangeaResponseResult):
|
278
289
|
detectors: TextGuardDetectors
|
279
290
|
"""Result of the recipe analyzing and input prompt."""
|
280
291
|
|
281
292
|
prompt_text: Optional[str] = None
|
282
293
|
"""Updated prompt text, if applicable."""
|
283
294
|
|
284
|
-
prompt_messages: Optional[
|
295
|
+
prompt_messages: Optional[object] = None
|
285
296
|
"""Updated structured prompt, if applicable."""
|
286
297
|
|
287
298
|
blocked: bool
|
@@ -345,7 +356,7 @@ class AIGuard(ServiceBase):
|
|
345
356
|
debug: bool | None = None,
|
346
357
|
overrides: Overrides | None = None,
|
347
358
|
log_fields: LogFields | None = None,
|
348
|
-
) -> PangeaResponse[TextGuardResult
|
359
|
+
) -> PangeaResponse[TextGuardResult]:
|
349
360
|
"""
|
350
361
|
Text Guard for scanning LLM inputs and outputs
|
351
362
|
|
@@ -373,12 +384,12 @@ class AIGuard(ServiceBase):
|
|
373
384
|
def guard_text(
|
374
385
|
self,
|
375
386
|
*,
|
376
|
-
messages:
|
387
|
+
messages: Sequence[Message],
|
377
388
|
recipe: str | None = None,
|
378
389
|
debug: bool | None = None,
|
379
390
|
overrides: Overrides | None = None,
|
380
391
|
log_fields: LogFields | None = None,
|
381
|
-
) -> PangeaResponse[TextGuardResult
|
392
|
+
) -> PangeaResponse[TextGuardResult]:
|
382
393
|
"""
|
383
394
|
Text Guard for scanning LLM inputs and outputs
|
384
395
|
|
@@ -400,19 +411,19 @@ class AIGuard(ServiceBase):
|
|
400
411
|
log_field: Additional fields to include in activity log
|
401
412
|
|
402
413
|
Examples:
|
403
|
-
response = ai_guard.guard_text(messages=[
|
414
|
+
response = ai_guard.guard_text(messages=[Message(role="user", content="hello world")])
|
404
415
|
"""
|
405
416
|
|
406
|
-
def guard_text(
|
417
|
+
def guard_text(
|
407
418
|
self,
|
408
419
|
text: str | None = None,
|
409
420
|
*,
|
410
|
-
messages:
|
421
|
+
messages: Sequence[Message] | None = None,
|
411
422
|
recipe: str | None = None,
|
412
423
|
debug: bool | None = None,
|
413
424
|
overrides: Overrides | None = None,
|
414
425
|
log_fields: LogFields | None = None,
|
415
|
-
) -> PangeaResponse[TextGuardResult
|
426
|
+
) -> PangeaResponse[TextGuardResult]:
|
416
427
|
"""
|
417
428
|
Text Guard for scanning LLM inputs and outputs
|
418
429
|
|
pangea/services/audit/audit.py
CHANGED
@@ -1,9 +1,14 @@
|
|
1
1
|
# Copyright 2022 Pangea Cyber Corporation
|
2
2
|
# Author: Pangea Cyber Corporation
|
3
|
+
|
4
|
+
# TODO: Modernize.
|
5
|
+
# ruff: noqa: UP006, UP035
|
6
|
+
|
3
7
|
from __future__ import annotations
|
4
8
|
|
5
9
|
import datetime
|
6
10
|
import json
|
11
|
+
from collections.abc import Mapping
|
7
12
|
from typing import Any, Dict, Iterable, List, Optional, Sequence, Set, Tuple, Union
|
8
13
|
|
9
14
|
import pangea.exceptions as pexc
|
@@ -55,7 +60,7 @@ from pangea.utils import canonicalize_nested_json
|
|
55
60
|
|
56
61
|
class AuditBase:
|
57
62
|
def __init__(
|
58
|
-
self, private_key_file: str = "", public_key_info:
|
63
|
+
self, private_key_file: str = "", public_key_info: Mapping[str, str] = {}, tenant_id: str | None = None
|
59
64
|
) -> None:
|
60
65
|
self.pub_roots: Dict[int, PublishedRoot] = {}
|
61
66
|
self.buffer_data: Optional[str] = None
|
@@ -90,7 +95,7 @@ class AuditBase:
|
|
90
95
|
return input # type: ignore[return-value]
|
91
96
|
|
92
97
|
def _process_log(self, event: dict, sign_local: bool) -> LogEvent:
|
93
|
-
if event.get("tenant_id"
|
98
|
+
if event.get("tenant_id") is None and self.tenant_id:
|
94
99
|
event["tenant_id"] = self.tenant_id
|
95
100
|
|
96
101
|
event = {k: v for k, v in event.items() if v is not None}
|
@@ -221,10 +226,7 @@ class AuditBase:
|
|
221
226
|
tree_sizes.add(result.root.size)
|
222
227
|
tree_sizes.difference_update(self.pub_roots.keys())
|
223
228
|
|
224
|
-
if tree_sizes
|
225
|
-
arweave_roots = get_arweave_published_roots(result.root.tree_name, tree_sizes)
|
226
|
-
else:
|
227
|
-
arweave_roots = {}
|
229
|
+
arweave_roots = get_arweave_published_roots(result.root.tree_name, tree_sizes) if tree_sizes else {}
|
228
230
|
|
229
231
|
return tree_sizes, arweave_roots
|
230
232
|
|
@@ -336,6 +338,7 @@ class AuditBase:
|
|
336
338
|
public_key = get_public_key(audit_envelope.public_key)
|
337
339
|
|
338
340
|
if audit_envelope and audit_envelope.signature and public_key:
|
341
|
+
assert audit_envelope.event
|
339
342
|
v = Verifier()
|
340
343
|
verification = v.verify_signature(
|
341
344
|
audit_envelope.signature,
|
@@ -385,7 +388,7 @@ class Audit(ServiceBase, AuditBase):
|
|
385
388
|
token: str,
|
386
389
|
config: PangeaConfig | None = None,
|
387
390
|
private_key_file: str = "",
|
388
|
-
public_key_info:
|
391
|
+
public_key_info: Mapping[str, str] = {},
|
389
392
|
tenant_id: str | None = None,
|
390
393
|
logger_name: str = "pangea",
|
391
394
|
config_id: str | None = None,
|
pangea/services/audit/models.py
CHANGED
@@ -1,11 +1,18 @@
|
|
1
1
|
# Copyright 2022 Pangea Cyber Corporation
|
2
2
|
# Author: Pangea Cyber Corporation
|
3
|
+
|
4
|
+
# TODO: Modernize.
|
5
|
+
# ruff: noqa: UP006, UP035
|
6
|
+
|
3
7
|
from __future__ import annotations
|
4
8
|
|
5
9
|
import datetime
|
6
10
|
import enum
|
7
11
|
from typing import Any, Dict, List, Optional, Sequence, Union
|
8
12
|
|
13
|
+
from pydantic import Field
|
14
|
+
from typing_extensions import Annotated
|
15
|
+
|
9
16
|
from pangea.response import APIRequestModel, APIResponseModel, PangeaDateTime, PangeaResponseResult
|
10
17
|
|
11
18
|
|
@@ -113,20 +120,25 @@ class Event(Dict[str, Any]):
|
|
113
120
|
|
114
121
|
|
115
122
|
class EventEnvelope(APIResponseModel):
|
116
|
-
|
117
|
-
Contain extra information about an event.
|
123
|
+
event: Optional[dict[str, Any]] = None
|
118
124
|
|
119
|
-
|
120
|
-
|
121
|
-
signature
|
122
|
-
|
123
|
-
|
125
|
+
signature: Optional[str] = None
|
126
|
+
"""
|
127
|
+
This is the signature of the hash of the canonicalized event that can be
|
128
|
+
verified with the public key provided in the public_key field. Signatures
|
129
|
+
cannot be used with the redaction feature turned on. If redaction is
|
130
|
+
required, the user needs to perform redaction before computing the signature
|
131
|
+
that is to be sent with the message. The SDK facilitates this for users.
|
124
132
|
"""
|
125
133
|
|
126
|
-
event: Dict[str, Any]
|
127
|
-
signature: Optional[str] = None
|
128
134
|
public_key: Optional[str] = None
|
129
|
-
|
135
|
+
"""
|
136
|
+
The base64-encoded ed25519 public key used for the signature, if one is
|
137
|
+
provided
|
138
|
+
"""
|
139
|
+
|
140
|
+
received_at: Optional[PangeaDateTime] = None
|
141
|
+
"""A Pangea provided timestamp of when the event was received."""
|
130
142
|
|
131
143
|
|
132
144
|
class LogRequest(APIRequestModel):
|
@@ -177,21 +189,28 @@ class LogBulkRequest(APIRequestModel):
|
|
177
189
|
|
178
190
|
|
179
191
|
class LogResult(PangeaResponseResult):
|
192
|
+
envelope: Optional[EventEnvelope] = None
|
180
193
|
"""
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
hash -- Event envelope hash.
|
185
|
-
unpublished_root -- The current unpublished root.
|
186
|
-
membership_proof -- A proof for verifying the unpublished root.
|
187
|
-
consistency_proof -- If prev_root was present in the request, this proof verifies that the new unpublished root is a continuation of the prev_root
|
194
|
+
The sealed envelope containing the event that was logged. Includes event
|
195
|
+
metadata such as optional client-side signature details and server-added
|
196
|
+
timestamps.
|
188
197
|
"""
|
189
198
|
|
190
|
-
|
191
|
-
hash
|
199
|
+
hash: Annotated[Optional[str], Field(max_length=64, min_length=64)] = None
|
200
|
+
"""The hash of the event data."""
|
201
|
+
|
192
202
|
unpublished_root: Optional[str] = None
|
203
|
+
"""The current unpublished root."""
|
204
|
+
|
193
205
|
membership_proof: Optional[str] = None
|
206
|
+
"""A proof for verifying that the buffer_root contains the received event"""
|
207
|
+
|
194
208
|
consistency_proof: Optional[List[str]] = None
|
209
|
+
"""
|
210
|
+
If prev_buffer_root was present in the request, this proof verifies that the
|
211
|
+
new unpublished root is a continuation of prev_unpublished_root
|
212
|
+
"""
|
213
|
+
|
195
214
|
consistency_verification: EventVerification = EventVerification.NONE
|
196
215
|
membership_verification: EventVerification = EventVerification.NONE
|
197
216
|
signature_verification: EventVerification = EventVerification.NONE
|
@@ -354,29 +373,47 @@ class RootResult(PangeaResponseResult):
|
|
354
373
|
|
355
374
|
|
356
375
|
class SearchEvent(APIResponseModel):
|
376
|
+
envelope: EventEnvelope
|
377
|
+
|
378
|
+
membership_proof: Optional[str] = None
|
379
|
+
"""A cryptographic proof that the record has been persisted in the log"""
|
380
|
+
|
381
|
+
hash: Annotated[Optional[str], Field(max_length=64, min_length=64)] = None
|
382
|
+
"""The record's hash"""
|
383
|
+
|
384
|
+
published: Optional[bool] = None
|
385
|
+
"""
|
386
|
+
If true, a root has been published after this event. If false, there is no
|
387
|
+
published root for this event
|
357
388
|
"""
|
358
|
-
Event information received after a search request
|
359
389
|
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
membership_proof -- A cryptographic proof that the record has been persisted in the log.
|
365
|
-
consistency_verification -- Consistency verification calculated if required.
|
366
|
-
membership_verification -- Membership verification calculated if required.
|
367
|
-
signature_verification -- Signature verification calculated if required.
|
368
|
-
fpe_context -- The context data needed to decrypt secure audit events that have been redacted with format preserving encryption.
|
390
|
+
imported: Optional[bool] = None
|
391
|
+
"""
|
392
|
+
If true, the even was imported manually and not logged by the standard
|
393
|
+
procedure. Some features such as tamper proofing may not be available
|
369
394
|
"""
|
370
395
|
|
371
|
-
envelope: EventEnvelope
|
372
|
-
hash: str
|
373
|
-
membership_proof: Optional[str] = None
|
374
|
-
published: Optional[bool] = None
|
375
396
|
leaf_index: Optional[int] = None
|
397
|
+
"""
|
398
|
+
The index of the leaf of the Merkle Tree where this record was inserted or
|
399
|
+
null if published=false
|
400
|
+
"""
|
401
|
+
|
402
|
+
valid_signature: Optional[bool] = None
|
403
|
+
"""
|
404
|
+
Result of the verification of the Vault signature, if the event was signed
|
405
|
+
and the parameter `verify_signature` is `true`
|
406
|
+
"""
|
407
|
+
|
408
|
+
fpe_context: Optional[str] = None
|
409
|
+
"""
|
410
|
+
The context data needed to decrypt secure audit events that have been
|
411
|
+
redacted with format preserving encryption.
|
412
|
+
"""
|
413
|
+
|
376
414
|
consistency_verification: EventVerification = EventVerification.NONE
|
377
415
|
membership_verification: EventVerification = EventVerification.NONE
|
378
416
|
signature_verification: EventVerification = EventVerification.NONE
|
379
|
-
fpe_context: Optional[str] = None
|
380
417
|
|
381
418
|
|
382
419
|
class SearchResultOutput(PangeaResponseResult):
|
pangea/services/audit/signing.py
CHANGED
@@ -81,7 +81,7 @@ class Signer:
|
|
81
81
|
with open(self.private_key_file, "rb") as file:
|
82
82
|
file_bytes = file.read()
|
83
83
|
except FileNotFoundError:
|
84
|
-
raise Exception(f"Error: Failed opening private key file {self.private_key_file}")
|
84
|
+
raise Exception(f"Error: Failed opening private key file {self.private_key_file}") from None
|
85
85
|
|
86
86
|
privkey = self._decode_private_key(file_bytes)
|
87
87
|
for cls, signer in signers.items():
|
pangea/services/audit/util.py
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# Copyright 2022 Pangea Cyber Corporation
|
2
2
|
# Author: Pangea Cyber Corporation
|
3
|
+
|
4
|
+
# TODO: Modernize.
|
5
|
+
# ruff: noqa: UP006, UP035
|
6
|
+
|
7
|
+
from __future__ import annotations
|
8
|
+
|
3
9
|
import base64
|
4
10
|
import json
|
5
11
|
import logging
|
@@ -155,9 +161,9 @@ def verify_membership_proof(node_hash: Hash, root_hash: Hash, proof: MembershipP
|
|
155
161
|
def normalize_log(data: dict) -> dict:
|
156
162
|
ans = {}
|
157
163
|
for key in data:
|
158
|
-
if
|
164
|
+
if isinstance(data[key], datetime):
|
159
165
|
ans[key] = format_datetime(data[key])
|
160
|
-
elif
|
166
|
+
elif isinstance(data[key], dict):
|
161
167
|
ans[key] = normalize_log(data[key]) # type: ignore[assignment]
|
162
168
|
else:
|
163
169
|
ans[key] = data[key]
|
@@ -223,9 +229,7 @@ def get_arweave_published_roots(tree_name: str, tree_sizes: Collection[int]) ->
|
|
223
229
|
}
|
224
230
|
}
|
225
231
|
}
|
226
|
-
""".replace(
|
227
|
-
"{tree_sizes}", ", ".join(f'"{tree_size}"' for tree_size in tree_sizes)
|
228
|
-
).replace(
|
232
|
+
""".replace("{tree_sizes}", ", ".join(f'"{tree_size}"' for tree_size in tree_sizes)).replace(
|
229
233
|
"{tree_name}", tree_name
|
230
234
|
)
|
231
235
|
|
@@ -273,8 +277,4 @@ def verify_consistency_proof(new_root: Hash, prev_root: Hash, proof: Consistency
|
|
273
277
|
return False
|
274
278
|
|
275
279
|
logger.debug("Verifying the proofs for the new root")
|
276
|
-
for item in proof
|
277
|
-
if not verify_membership_proof(item.node_hash, new_root, item.proof):
|
278
|
-
return False
|
279
|
-
|
280
|
-
return True
|
280
|
+
return all(verify_membership_proof(item.node_hash, new_root, item.proof) for item in proof)
|
pangea/services/authn/authn.py
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
# Copyright 2022 Pangea Cyber Corporation
|
2
2
|
# Author: Pangea Cyber Corporation
|
3
|
+
|
4
|
+
# TODO: Modernize.
|
5
|
+
# ruff: noqa: UP006, UP035
|
6
|
+
|
3
7
|
from __future__ import annotations
|
4
8
|
|
9
|
+
from collections.abc import Mapping
|
5
10
|
from typing import Dict, List, Literal, Optional, Union
|
6
11
|
|
7
12
|
import pangea.services.authn.models as m
|
@@ -477,7 +482,7 @@ class AuthN(ServiceBase):
|
|
477
482
|
def create(
|
478
483
|
self,
|
479
484
|
email: str,
|
480
|
-
profile:
|
485
|
+
profile: Mapping[str, str],
|
481
486
|
*,
|
482
487
|
username: str | None = None,
|
483
488
|
) -> PangeaResponse[m.UserCreateResult]:
|
@@ -864,7 +869,7 @@ class AuthN(ServiceBase):
|
|
864
869
|
|
865
870
|
def update(
|
866
871
|
self,
|
867
|
-
profile:
|
872
|
+
profile: Mapping[str, str],
|
868
873
|
id: str | None = None,
|
869
874
|
email: str | None = None,
|
870
875
|
*,
|
@@ -991,7 +996,10 @@ class AuthN(ServiceBase):
|
|
991
996
|
return self.request.post("v2/flow/complete", m.FlowCompleteResult, data=input.model_dump(exclude_none=True))
|
992
997
|
|
993
998
|
def restart(
|
994
|
-
self,
|
999
|
+
self,
|
1000
|
+
flow_id: str,
|
1001
|
+
choice: m.FlowChoice,
|
1002
|
+
data: m.FlowRestartData = {}, # noqa: B006
|
995
1003
|
) -> PangeaResponse[m.FlowRestartResult]:
|
996
1004
|
"""
|
997
1005
|
Restart a sign-up/sign-in flow
|
@@ -1062,7 +1070,10 @@ class AuthN(ServiceBase):
|
|
1062
1070
|
return self.request.post("v2/flow/start", m.FlowStartResult, data=input.model_dump(exclude_none=True))
|
1063
1071
|
|
1064
1072
|
def update(
|
1065
|
-
self,
|
1073
|
+
self,
|
1074
|
+
flow_id: str,
|
1075
|
+
choice: m.FlowChoice,
|
1076
|
+
data: m.FlowUpdateData = {}, # noqa: B006
|
1066
1077
|
) -> PangeaResponse[m.FlowUpdateResult]:
|
1067
1078
|
"""
|
1068
1079
|
Update a sign-up/sign-in flow
|
pangea/services/authn/models.py
CHANGED
@@ -1,68 +1,22 @@
|
|
1
1
|
# Copyright 2022 Pangea Cyber Corporation
|
2
2
|
# Author: Pangea Cyber Corporation
|
3
3
|
|
4
|
+
# TODO: Modernize.
|
5
|
+
# ruff: noqa: UP006, UP035
|
6
|
+
|
4
7
|
from __future__ import annotations
|
5
8
|
|
6
9
|
import enum
|
10
|
+
from collections.abc import Mapping
|
7
11
|
from typing import Dict, List, NewType, Optional, Union
|
8
|
-
from warnings import warn
|
9
|
-
|
10
|
-
from typing_extensions import deprecated
|
11
12
|
|
12
13
|
import pangea.services.intel as im
|
13
|
-
from pangea.deprecated import pangea_deprecated
|
14
14
|
from pangea.response import APIRequestModel, APIResponseModel, PangeaResponseResult
|
15
15
|
from pangea.services.vault.models.common import JWK, JWKec, JWKrsa
|
16
16
|
|
17
17
|
Scopes = NewType("Scopes", List[str])
|
18
18
|
|
19
19
|
|
20
|
-
class Profile(Dict[str, str]):
|
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
|
64
|
-
|
65
|
-
|
66
20
|
class ClientPasswordChangeRequest(APIRequestModel):
|
67
21
|
token: str
|
68
22
|
old_password: str
|
@@ -105,7 +59,7 @@ class SessionToken(PangeaResponseResult):
|
|
105
59
|
identity: str
|
106
60
|
email: str
|
107
61
|
scopes: Optional[Scopes] = None
|
108
|
-
profile:
|
62
|
+
profile: dict[str, str]
|
109
63
|
created_at: str
|
110
64
|
intelligence: Optional[Intelligence] = None
|
111
65
|
|
@@ -245,7 +199,7 @@ class User(PangeaResponseResult):
|
|
245
199
|
username: str
|
246
200
|
"""A username."""
|
247
201
|
|
248
|
-
profile:
|
202
|
+
profile: dict[str, str]
|
249
203
|
"""A user profile as a collection of string properties."""
|
250
204
|
|
251
205
|
verified: bool
|
@@ -278,7 +232,7 @@ class UserCreateRequest(APIRequestModel):
|
|
278
232
|
email: str
|
279
233
|
"""An email address."""
|
280
234
|
|
281
|
-
profile:
|
235
|
+
profile: Mapping[str, str]
|
282
236
|
"""A user profile as a collection of string properties."""
|
283
237
|
|
284
238
|
username: Optional[str] = None
|
@@ -468,7 +422,7 @@ class UserProfileGetResult(User):
|
|
468
422
|
|
469
423
|
|
470
424
|
class UserProfileUpdateRequest(APIRequestModel):
|
471
|
-
profile:
|
425
|
+
profile: Mapping[str, str]
|
472
426
|
"""Updates to a user profile."""
|
473
427
|
|
474
428
|
id: Optional[str] = None
|
@@ -655,7 +609,7 @@ class FlowUpdateDataPassword(APIRequestModel):
|
|
655
609
|
|
656
610
|
|
657
611
|
class FlowUpdateDataProfile(APIRequestModel):
|
658
|
-
profile:
|
612
|
+
profile: dict[str, str]
|
659
613
|
|
660
614
|
|
661
615
|
class FlowUpdateDataProvisionalEnrollment(APIRequestModel):
|
@@ -777,7 +731,7 @@ class SessionItem(APIResponseModel):
|
|
777
731
|
expire: str
|
778
732
|
email: str
|
779
733
|
scopes: Optional[Scopes] = None
|
780
|
-
profile:
|
734
|
+
profile: dict[str, str]
|
781
735
|
created_at: str
|
782
736
|
active_token: Optional[SessionToken] = None
|
783
737
|
|
pangea/services/authz.py
CHANGED
pangea/services/base.py
CHANGED
@@ -1,10 +1,14 @@
|
|
1
1
|
# Copyright 2022 Pangea Cyber Corporation
|
2
2
|
# Author: Pangea Cyber Corporation
|
3
|
+
|
4
|
+
# TODO: Modernize.
|
5
|
+
# ruff: noqa: UP006, UP035
|
6
|
+
|
3
7
|
from __future__ import annotations
|
4
8
|
|
5
9
|
import copy
|
6
10
|
import logging
|
7
|
-
from typing import
|
11
|
+
from typing import Optional, Type, Union
|
8
12
|
|
9
13
|
from typing_extensions import TypeVar
|
10
14
|
|
@@ -17,7 +21,7 @@ from pangea.response import AttachedFile, PangeaResponse, PangeaResponseResult
|
|
17
21
|
TResult = TypeVar("TResult", bound=PangeaResponseResult, default=PangeaResponseResult)
|
18
22
|
|
19
23
|
|
20
|
-
class ServiceBase
|
24
|
+
class ServiceBase:
|
21
25
|
service_name: str = "base"
|
22
26
|
|
23
27
|
def __init__(
|
@@ -43,8 +47,7 @@ class ServiceBase(object):
|
|
43
47
|
self._token = token
|
44
48
|
self.config_id: Optional[str] = config_id
|
45
49
|
self._request: Optional[Union[PangeaRequest, PangeaRequestAsync]] = None
|
46
|
-
|
47
|
-
self.request.set_extra_headers(extra_headers)
|
50
|
+
self.request.set_extra_headers({})
|
48
51
|
|
49
52
|
@property
|
50
53
|
def token(self) -> str:
|
pangea/services/embargo.py
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# Copyright 2022 Pangea Cyber Corporation
|
2
2
|
# Author: Pangea Cyber Corporation
|
3
|
+
|
4
|
+
# TODO: Use `list` instead of `List`.
|
5
|
+
# ruff: noqa: UP006, UP035
|
6
|
+
|
7
|
+
from __future__ import annotations
|
8
|
+
|
3
9
|
from typing import Any, Dict, List
|
4
10
|
|
5
11
|
from pangea.response import APIRequestModel, APIResponseModel, PangeaResponse, PangeaResponseResult
|