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.
Files changed (50) hide show
  1. pangea/__init__.py +9 -1
  2. pangea/asyncio/__init__.py +1 -0
  3. pangea/asyncio/file_uploader.py +4 -2
  4. pangea/asyncio/request.py +52 -17
  5. pangea/asyncio/services/__init__.py +2 -0
  6. pangea/asyncio/services/ai_guard.py +9 -12
  7. pangea/asyncio/services/audit.py +6 -1
  8. pangea/asyncio/services/authn.py +15 -4
  9. pangea/asyncio/services/base.py +4 -0
  10. pangea/asyncio/services/file_scan.py +7 -1
  11. pangea/asyncio/services/intel.py +26 -28
  12. pangea/asyncio/services/redact.py +4 -0
  13. pangea/asyncio/services/sanitize.py +5 -1
  14. pangea/asyncio/services/share.py +5 -1
  15. pangea/asyncio/services/vault.py +4 -0
  16. pangea/audit_logger.py +3 -1
  17. pangea/deep_verify.py +13 -13
  18. pangea/deprecated.py +1 -1
  19. pangea/dump_audit.py +2 -3
  20. pangea/exceptions.py +8 -5
  21. pangea/file_uploader.py +4 -0
  22. pangea/request.py +63 -47
  23. pangea/response.py +21 -18
  24. pangea/services/__init__.py +2 -0
  25. pangea/services/ai_guard.py +35 -24
  26. pangea/services/audit/audit.py +10 -7
  27. pangea/services/audit/models.py +71 -34
  28. pangea/services/audit/signing.py +1 -1
  29. pangea/services/audit/util.py +10 -10
  30. pangea/services/authn/authn.py +15 -4
  31. pangea/services/authn/models.py +10 -56
  32. pangea/services/authz.py +4 -0
  33. pangea/services/base.py +7 -4
  34. pangea/services/embargo.py +6 -0
  35. pangea/services/file_scan.py +7 -1
  36. pangea/services/intel.py +36 -19
  37. pangea/services/redact.py +4 -0
  38. pangea/services/sanitize.py +5 -1
  39. pangea/services/share/share.py +13 -7
  40. pangea/services/vault/models/asymmetric.py +4 -0
  41. pangea/services/vault/models/common.py +4 -0
  42. pangea/services/vault/models/symmetric.py +4 -0
  43. pangea/services/vault/vault.py +2 -4
  44. pangea/tools.py +13 -9
  45. pangea/utils.py +3 -5
  46. pangea/verify_audit.py +23 -27
  47. {pangea_sdk-6.1.1.dist-info → pangea_sdk-6.2.0.dist-info}/METADATA +36 -17
  48. pangea_sdk-6.2.0.dist-info/RECORD +60 -0
  49. pangea_sdk-6.1.1.dist-info/RECORD +0 -60
  50. {pangea_sdk-6.1.1.dist-info → pangea_sdk-6.2.0.dist-info}/WHEEL +0 -0
@@ -1,6 +1,10 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import Any, Dict, Generic, List, Literal, Optional, TypeVar, overload
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[List[str]] = None
28
- block: Optional[List[str]] = None
29
- report: Optional[List[str]] = None
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[List[str]] = None
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: List[AnalyzerResponse]
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: List[PiiEntity]
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[Dict[str, Any]] = None
215
+ raw: Optional[dict[str, object]] = None
205
216
 
206
217
 
207
218
  class MaliciousEntityResult(APIResponseModel):
208
- entities: List[MaliciousEntity]
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[Dict[str, Any]] = None
229
+ raw: Optional[dict[str, object]] = None
219
230
 
220
231
 
221
232
  class CustomEntityResult(APIResponseModel):
222
- entities: List[CustomEntity]
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: List[SecretsEntity]
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[Any]] = None
280
+ custom_entity: Optional[TextGuardDetector[object]] = None
270
281
  secrets_detection: Optional[TextGuardDetector[SecretsEntityResult]] = None
271
- profanity_and_toxicity: Optional[TextGuardDetector[Any]] = None
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, Generic[_T]):
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[_T] = None
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[None]]:
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: _T,
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[_T]]:
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=[{"role": "user", "content": "hello world"}])
414
+ response = ai_guard.guard_text(messages=[Message(role="user", content="hello world")])
404
415
  """
405
416
 
406
- def guard_text( # type: ignore[misc]
417
+ def guard_text(
407
418
  self,
408
419
  text: str | None = None,
409
420
  *,
410
- messages: _T | None = None,
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[None]]:
426
+ ) -> PangeaResponse[TextGuardResult]:
416
427
  """
417
428
  Text Guard for scanning LLM inputs and outputs
418
429
 
@@ -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: dict[str, str] = {}, tenant_id: str | None = None
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", None) is None and self.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: dict[str, str] = {},
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,
@@ -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
- Arguments:
120
- event -- Event describing auditable activity.
121
- signature -- An optional client-side signature for forgery protection.
122
- public_key -- The base64-encoded ed25519 public key used for the signature, if one is provided
123
- received_at -- A server-supplied timestamp
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
- received_at: PangeaDateTime
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
- Result class after an audit log action
182
-
183
- envelope -- Event envelope information.
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
- envelope: Optional[EventEnvelope] = None
191
- hash: str
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
- Arguments:
361
- envelope -- Event related information.
362
- hash -- The record's hash.
363
- leaf_index -- The index of the leaf of the Merkle Tree where this record was inserted.
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):
@@ -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():
@@ -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 type(data[key]) == datetime:
164
+ if isinstance(data[key], datetime):
159
165
  ans[key] = format_datetime(data[key])
160
- elif type(data[key]) == dict:
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)
@@ -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: m.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: m.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, flow_id: str, choice: m.FlowChoice, data: m.FlowRestartData = {}
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, flow_id: str, choice: m.FlowChoice, data: m.FlowUpdateData = {}
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
@@ -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: Union[Profile, Dict[str, str]]
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: Union[Profile, Dict[str, str]]
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: Union[Profile, Dict[str, str]]
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: Union[Profile, Dict[str, str]]
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: Union[Profile, Dict[str, str]]
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: Union[Profile, Dict[str, str]]
734
+ profile: dict[str, str]
781
735
  created_at: str
782
736
  active_token: Optional[SessionToken] = None
783
737
 
pangea/services/authz.py CHANGED
@@ -1,5 +1,9 @@
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
+
3
7
  from __future__ import annotations
4
8
 
5
9
  import enum
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 Dict, Optional, Type, Union
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(object):
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
- extra_headers: Dict = {}
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:
@@ -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