splunk-soar-sdk 3.6.1__py3-none-any.whl → 3.8.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.
- soar_sdk/actions_manager.py +40 -0
- soar_sdk/app.py +52 -4
- soar_sdk/asset.py +61 -69
- soar_sdk/asset_state.py +13 -3
- soar_sdk/auth/__init__.py +41 -0
- soar_sdk/auth/client.py +540 -0
- soar_sdk/auth/factories.py +120 -0
- soar_sdk/auth/flows.py +172 -0
- soar_sdk/auth/httpx_auth.py +97 -0
- soar_sdk/auth/models.py +101 -0
- soar_sdk/cli/package/cli.py +6 -4
- soar_sdk/shims/phantom/base_connector.py +7 -0
- {splunk_soar_sdk-3.6.1.dist-info → splunk_soar_sdk-3.8.0.dist-info}/METADATA +3 -1
- {splunk_soar_sdk-3.6.1.dist-info → splunk_soar_sdk-3.8.0.dist-info}/RECORD +17 -11
- {splunk_soar_sdk-3.6.1.dist-info → splunk_soar_sdk-3.8.0.dist-info}/WHEEL +0 -0
- {splunk_soar_sdk-3.6.1.dist-info → splunk_soar_sdk-3.8.0.dist-info}/entry_points.txt +0 -0
- {splunk_soar_sdk-3.6.1.dist-info → splunk_soar_sdk-3.8.0.dist-info}/licenses/LICENSE +0 -0
soar_sdk/actions_manager.py
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import json
|
|
1
2
|
import os
|
|
3
|
+
import shutil
|
|
4
|
+
import tempfile
|
|
2
5
|
from pathlib import Path
|
|
3
6
|
from typing import Any
|
|
4
7
|
|
|
@@ -135,3 +138,40 @@ class ActionsManager(BaseConnector):
|
|
|
135
138
|
This is useful for contexts such as Webhooks, where the app dir isn't necessarily the cwd, but we still need to load the app JSON reliably.
|
|
136
139
|
"""
|
|
137
140
|
self.__app_dir = app_dir
|
|
141
|
+
|
|
142
|
+
def _get_state_file_path(self, asset_id: str) -> Path:
|
|
143
|
+
"""Get the state file path for an asset."""
|
|
144
|
+
return Path(self.get_state_dir()) / f"{asset_id}_state.json"
|
|
145
|
+
|
|
146
|
+
def load_state_from_file(self, asset_id: str) -> dict:
|
|
147
|
+
"""Load state directly from file."""
|
|
148
|
+
state_file = self._get_state_file_path(asset_id)
|
|
149
|
+
if state_file.exists():
|
|
150
|
+
return json.loads(state_file.read_text())
|
|
151
|
+
return {}
|
|
152
|
+
|
|
153
|
+
def save_state_to_file(self, asset_id: str, state: dict) -> None:
|
|
154
|
+
"""Save state directly to file using atomic write."""
|
|
155
|
+
state_file = self._get_state_file_path(asset_id)
|
|
156
|
+
state_file.parent.mkdir(parents=True, exist_ok=True)
|
|
157
|
+
|
|
158
|
+
fd, tmp_path = tempfile.mkstemp(dir=state_file.parent)
|
|
159
|
+
tmp_file = Path(tmp_path)
|
|
160
|
+
try:
|
|
161
|
+
with os.fdopen(fd, "w") as f:
|
|
162
|
+
f.write(json.dumps(state))
|
|
163
|
+
shutil.move(tmp_file, state_file)
|
|
164
|
+
except Exception:
|
|
165
|
+
if tmp_file.exists():
|
|
166
|
+
tmp_file.unlink()
|
|
167
|
+
raise
|
|
168
|
+
|
|
169
|
+
def reload_state_from_file(self, asset_id: str) -> dict:
|
|
170
|
+
"""Reload state from file and update in-memory state.
|
|
171
|
+
|
|
172
|
+
Needed for OAuth flow where one process (webhook) updates the state file and another process (action) needs to see those changes.
|
|
173
|
+
"""
|
|
174
|
+
state = self.load_state_from_file(asset_id)
|
|
175
|
+
if state:
|
|
176
|
+
self.save_state(state)
|
|
177
|
+
return state
|
soar_sdk/app.py
CHANGED
|
@@ -6,6 +6,7 @@ import uuid
|
|
|
6
6
|
from collections.abc import Callable, Iterator
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
from typing import Any
|
|
9
|
+
from urllib.parse import urlparse
|
|
9
10
|
from zoneinfo import ZoneInfo
|
|
10
11
|
|
|
11
12
|
from soar_sdk.abstract import SOARClient, SOARClientAuth
|
|
@@ -226,12 +227,16 @@ class App:
|
|
|
226
227
|
self._asset = self.asset_cls.model_validate(self._raw_asset_config)
|
|
227
228
|
|
|
228
229
|
asset_id = self.soar_client.get_asset_id()
|
|
229
|
-
|
|
230
|
+
app_id = str(self.app_meta_info["appid"])
|
|
231
|
+
|
|
232
|
+
self._asset._auth_state = AssetState(
|
|
233
|
+
self.actions_manager, "auth", asset_id, app_id=app_id
|
|
234
|
+
)
|
|
230
235
|
self._asset._cache_state = AssetState(
|
|
231
|
-
self.actions_manager, "cache", asset_id
|
|
236
|
+
self.actions_manager, "cache", asset_id, app_id=app_id
|
|
232
237
|
)
|
|
233
238
|
self._asset._ingest_state = AssetState(
|
|
234
|
-
self.actions_manager, "ingest", asset_id
|
|
239
|
+
self.actions_manager, "ingest", asset_id, app_id=app_id
|
|
235
240
|
)
|
|
236
241
|
return self._asset
|
|
237
242
|
|
|
@@ -789,6 +794,47 @@ class App:
|
|
|
789
794
|
"""Decorator for registering a webhook handler."""
|
|
790
795
|
return WebhookDecorator(self, url_pattern, allowed_methods)
|
|
791
796
|
|
|
797
|
+
def get_webhook_url(self, route: str) -> str:
|
|
798
|
+
"""Build the full URL for a webhook route (used for OAuth flow)."""
|
|
799
|
+
system_info = self.soar_client.get("rest/system_info").json()
|
|
800
|
+
base_url = system_info.get("base_url", "").rstrip("/")
|
|
801
|
+
parsed = urlparse(base_url)
|
|
802
|
+
|
|
803
|
+
webhook_port = self._get_webhook_port()
|
|
804
|
+
webhook_base = f"{parsed.scheme}://{parsed.hostname}:{webhook_port}"
|
|
805
|
+
|
|
806
|
+
config = self.actions_manager.get_config()
|
|
807
|
+
directory = config.get(
|
|
808
|
+
"directory", f"{self.app_meta_info['name']}_{self.app_meta_info['appid']}"
|
|
809
|
+
)
|
|
810
|
+
asset_id = str(self.soar_client.get_asset_id())
|
|
811
|
+
|
|
812
|
+
return f"{webhook_base}/webhook/{directory}/{asset_id}/{route}"
|
|
813
|
+
|
|
814
|
+
def _get_webhook_port(self) -> int:
|
|
815
|
+
"""Get the webhook port from the feature flag configuration."""
|
|
816
|
+
try:
|
|
817
|
+
response = self.soar_client.get("rest/feature_flag/webhooks")
|
|
818
|
+
if response.status_code == 200:
|
|
819
|
+
data = response.json()
|
|
820
|
+
config = data.get("config", {})
|
|
821
|
+
if port := config.get("webhooks_port"):
|
|
822
|
+
return int(port)
|
|
823
|
+
except Exception: # noqa: S110
|
|
824
|
+
pass
|
|
825
|
+
return 3500
|
|
826
|
+
|
|
827
|
+
def _load_webhook_state(self, asset_id: str) -> None:
|
|
828
|
+
"""Load state from file for webhooks."""
|
|
829
|
+
state = self.actions_manager.load_state_from_file(asset_id)
|
|
830
|
+
if state:
|
|
831
|
+
self.actions_manager.save_state(state)
|
|
832
|
+
|
|
833
|
+
def _save_webhook_state(self, asset_id: str) -> None:
|
|
834
|
+
"""Save state to file for webhooks."""
|
|
835
|
+
state = self.actions_manager.load_state() or {}
|
|
836
|
+
self.actions_manager.save_state_to_file(asset_id, state)
|
|
837
|
+
|
|
792
838
|
def handle_webhook(
|
|
793
839
|
self,
|
|
794
840
|
method: str,
|
|
@@ -813,7 +859,7 @@ class App:
|
|
|
813
859
|
user_session_token=soar_auth_token,
|
|
814
860
|
base_url=soar_base_url,
|
|
815
861
|
)
|
|
816
|
-
self.soar_client.update_client(soar_auth, asset_id)
|
|
862
|
+
self.soar_client.update_client(soar_auth, str(asset_id))
|
|
817
863
|
|
|
818
864
|
normalized_query = {}
|
|
819
865
|
for key, value in query.items():
|
|
@@ -829,6 +875,7 @@ class App:
|
|
|
829
875
|
|
|
830
876
|
self.actions_manager.override_app_dir(self.app_root)
|
|
831
877
|
self.actions_manager._load_app_json()
|
|
878
|
+
self._load_webhook_state(str(asset_id))
|
|
832
879
|
request = WebhookRequest(
|
|
833
880
|
method=method,
|
|
834
881
|
headers=headers,
|
|
@@ -846,4 +893,5 @@ class App:
|
|
|
846
893
|
raise TypeError(
|
|
847
894
|
f"Webhook handler must return a WebhookResponse, got {type(response)}"
|
|
848
895
|
)
|
|
896
|
+
self._save_webhook_state(str(asset_id))
|
|
849
897
|
return response.model_dump()
|
soar_sdk/asset.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from enum import Enum
|
|
1
2
|
from typing import Any, NotRequired
|
|
2
3
|
from zoneinfo import ZoneInfo
|
|
3
4
|
|
|
@@ -17,6 +18,14 @@ remove_when_soar_newer_than(
|
|
|
17
18
|
)
|
|
18
19
|
|
|
19
20
|
|
|
21
|
+
class FieldCategory(str, Enum):
|
|
22
|
+
"""Categories used to group asset configuration fields in the SOAR UI."""
|
|
23
|
+
|
|
24
|
+
CONNECTIVITY = "connectivity"
|
|
25
|
+
ACTION = "action"
|
|
26
|
+
INGEST = "ingest"
|
|
27
|
+
|
|
28
|
+
|
|
20
29
|
def AssetField(
|
|
21
30
|
description: str | None = None,
|
|
22
31
|
required: bool = True,
|
|
@@ -24,33 +33,36 @@ def AssetField(
|
|
|
24
33
|
value_list: list | None = None,
|
|
25
34
|
sensitive: bool = False,
|
|
26
35
|
alias: str | None = None,
|
|
36
|
+
category: FieldCategory = FieldCategory.CONNECTIVITY,
|
|
37
|
+
is_file: bool = False,
|
|
27
38
|
) -> Any: # noqa: ANN401
|
|
28
|
-
"""
|
|
29
|
-
|
|
30
|
-
The field needs extra metadata that is later used for the configuration of the app.
|
|
31
|
-
This function takes care of the required information for the manifest JSON file and fills in defaults.
|
|
39
|
+
"""Define an asset configuration field with SOAR-specific metadata.
|
|
32
40
|
|
|
33
41
|
Args:
|
|
34
|
-
description:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
description: Human-friendly label for the field shown in the asset form.
|
|
43
|
+
required: Whether the field must be provided. When True and ``default`` is
|
|
44
|
+
``None``, the field is marked as required in the manifest.
|
|
45
|
+
default: Default value for optional fields. Ignored when ``required`` is
|
|
46
|
+
True and no explicit default is provided.
|
|
47
|
+
value_list: Optional dropdown options presented to the user.
|
|
48
|
+
sensitive: Marks the field as secret so it is encrypted and hidden from logs.
|
|
49
|
+
alias: Alternate name to emit in the manifest instead of the attribute name.
|
|
50
|
+
category: Grouping used to organize fields in the SOAR UI.
|
|
51
|
+
is_file: Marks the field as a file upload field. The field must be typed as
|
|
52
|
+
str and will receive the file contents as a string.
|
|
43
53
|
|
|
44
54
|
Returns:
|
|
45
|
-
|
|
55
|
+
A Pydantic ``Field`` carrying the metadata needed for manifest generation.
|
|
46
56
|
"""
|
|
47
|
-
json_schema_extra: dict[str, Any] = {}
|
|
57
|
+
json_schema_extra: dict[str, Any] = {"category": category}
|
|
48
58
|
if required is not None:
|
|
49
59
|
json_schema_extra["required"] = required
|
|
50
60
|
if value_list is not None:
|
|
51
61
|
json_schema_extra["value_list"] = value_list
|
|
52
62
|
if sensitive is not None:
|
|
53
63
|
json_schema_extra["sensitive"] = sensitive
|
|
64
|
+
if is_file:
|
|
65
|
+
json_schema_extra["is_file"] = True
|
|
54
66
|
|
|
55
67
|
# Use ... for required fields
|
|
56
68
|
field_default: Any = ... if default is None and required else default
|
|
@@ -59,7 +71,7 @@ def AssetField(
|
|
|
59
71
|
default=field_default,
|
|
60
72
|
description=description,
|
|
61
73
|
alias=alias,
|
|
62
|
-
json_schema_extra=json_schema_extra
|
|
74
|
+
json_schema_extra=json_schema_extra,
|
|
63
75
|
)
|
|
64
76
|
|
|
65
77
|
|
|
@@ -80,6 +92,7 @@ class AssetFieldSpecification(TypedDict):
|
|
|
80
92
|
"""
|
|
81
93
|
|
|
82
94
|
data_type: str
|
|
95
|
+
category: FieldCategory
|
|
83
96
|
description: NotRequired[str]
|
|
84
97
|
required: NotRequired[bool]
|
|
85
98
|
default: NotRequired[str | int | float | bool]
|
|
@@ -114,7 +127,10 @@ class BaseAsset(BaseModel):
|
|
|
114
127
|
|
|
115
128
|
Note:
|
|
116
129
|
Field names cannot start with "_reserved_" or use names reserved by
|
|
117
|
-
the SOAR platform to avoid conflicts with internal fields.
|
|
130
|
+
the SOAR platform to avoid conflicts with internal fields. The runtime
|
|
131
|
+
attaches ``auth_state``, ``cache_state``, and ``ingest_state`` when an
|
|
132
|
+
app context is available; accessing them without that context raises
|
|
133
|
+
``AppContextRequired``.
|
|
118
134
|
"""
|
|
119
135
|
|
|
120
136
|
model_config = ConfigDict(
|
|
@@ -124,25 +140,15 @@ class BaseAsset(BaseModel):
|
|
|
124
140
|
@model_validator(mode="before")
|
|
125
141
|
@classmethod
|
|
126
142
|
def validate_no_reserved_fields(cls, values: dict[str, Any]) -> dict[str, Any]:
|
|
127
|
-
"""
|
|
128
|
-
|
|
129
|
-
This validator ensures that asset field names don't conflict with
|
|
130
|
-
platform-reserved fields or internal SOAR configuration fields.
|
|
143
|
+
"""Prevent subclasses from using names reserved by the platform.
|
|
131
144
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
The validated values dictionary.
|
|
145
|
+
The validator inspects annotated field names to ensure they do not start
|
|
146
|
+
with ``_reserved_`` and do not collide with fields injected by the SOAR
|
|
147
|
+
service (see ``AppConfig``). The ``values`` argument is unused but kept
|
|
148
|
+
for Pydantic compatibility.
|
|
137
149
|
|
|
138
150
|
Raises:
|
|
139
|
-
ValueError: If a
|
|
140
|
-
with platform-reserved field names.
|
|
141
|
-
|
|
142
|
-
Note:
|
|
143
|
-
The SOAR platform injects fields like "_reserved_credential_management"
|
|
144
|
-
into asset configs, so this prevents the entire "_reserved_" namespace
|
|
145
|
-
from being used in user-defined assets.
|
|
151
|
+
ValueError: If a reserved or injected field name is used.
|
|
146
152
|
"""
|
|
147
153
|
for field_name in cls.__annotations__:
|
|
148
154
|
# The platform injects fields like "_reserved_credential_management" into asset configs,
|
|
@@ -183,33 +189,20 @@ class BaseAsset(BaseModel):
|
|
|
183
189
|
|
|
184
190
|
@classmethod
|
|
185
191
|
def to_json_schema(cls) -> dict[str, AssetFieldSpecification]:
|
|
186
|
-
"""Generate
|
|
192
|
+
"""Generate manifest-ready schema entries from the asset definition.
|
|
187
193
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
194
|
+
Each field is converted into a SOAR manifest dictionary that includes the
|
|
195
|
+
data type, requirement flag, default value, dropdown options, and an order
|
|
196
|
+
index. Alias names are honored when present. Sensitive fields are emitted
|
|
197
|
+
as ``password`` data types and must be annotated as ``str``. Defaults are
|
|
198
|
+
serialized directly, with ``ZoneInfo`` defaults represented by their key.
|
|
191
199
|
|
|
192
200
|
Returns:
|
|
193
|
-
|
|
194
|
-
including data types, descriptions, requirements, and other metadata.
|
|
201
|
+
Mapping of field (or alias) names to schema specifications.
|
|
195
202
|
|
|
196
203
|
Raises:
|
|
197
|
-
TypeError: If a field type cannot be serialized or
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
Example:
|
|
201
|
-
>>> class MyAsset(BaseAsset):
|
|
202
|
-
... host: str = AssetField(description="Server hostname")
|
|
203
|
-
... port: int = AssetField(description="Server port", default=443)
|
|
204
|
-
>>> schema = MyAsset.to_json_schema()
|
|
205
|
-
>>> schema["host"]["data_type"]
|
|
206
|
-
'string'
|
|
207
|
-
>>> schema["host"]["required"]
|
|
208
|
-
True
|
|
209
|
-
|
|
210
|
-
Note:
|
|
211
|
-
Sensitive fields are automatically converted to "password" type
|
|
212
|
-
regardless of their Python type annotation, and must be str type.
|
|
204
|
+
TypeError: If a field type cannot be serialized or a sensitive field is
|
|
205
|
+
not declared as ``str``.
|
|
213
206
|
"""
|
|
214
207
|
params: dict[str, AssetFieldSpecification] = {}
|
|
215
208
|
|
|
@@ -234,6 +227,13 @@ class BaseAsset(BaseModel):
|
|
|
234
227
|
)
|
|
235
228
|
type_name = "password"
|
|
236
229
|
|
|
230
|
+
if json_schema_extra.get("is_file", False):
|
|
231
|
+
if field_type is not str:
|
|
232
|
+
raise TypeError(
|
|
233
|
+
f"File parameter {field_name} must be type str, not {field_type.__name__}"
|
|
234
|
+
)
|
|
235
|
+
type_name = "file"
|
|
236
|
+
|
|
237
237
|
if not (description := field.description):
|
|
238
238
|
description = cls._default_field_description(field_name)
|
|
239
239
|
|
|
@@ -242,6 +242,7 @@ class BaseAsset(BaseModel):
|
|
|
242
242
|
required=bool(json_schema_extra.get("required", True)),
|
|
243
243
|
description=description,
|
|
244
244
|
order=field_order,
|
|
245
|
+
category=json_schema_extra.get("category", FieldCategory.CONNECTIVITY),
|
|
245
246
|
)
|
|
246
247
|
|
|
247
248
|
if (default := field.default) not in (PydanticUndefined, None):
|
|
@@ -258,12 +259,7 @@ class BaseAsset(BaseModel):
|
|
|
258
259
|
|
|
259
260
|
@classmethod
|
|
260
261
|
def fields_requiring_decryption(cls) -> set[str]:
|
|
261
|
-
"""
|
|
262
|
-
|
|
263
|
-
Returns:
|
|
264
|
-
A set of field names that are marked as sensitive and need
|
|
265
|
-
decryption before use.
|
|
266
|
-
"""
|
|
262
|
+
"""Return attribute names marked as sensitive (aliases are ignored)."""
|
|
267
263
|
return {
|
|
268
264
|
field_name
|
|
269
265
|
for field_name, field in cls.model_fields.items()
|
|
@@ -273,11 +269,7 @@ class BaseAsset(BaseModel):
|
|
|
273
269
|
|
|
274
270
|
@classmethod
|
|
275
271
|
def timezone_fields(cls) -> set[str]:
|
|
276
|
-
"""
|
|
277
|
-
|
|
278
|
-
Returns:
|
|
279
|
-
A set of field names that use the ZoneInfo type.
|
|
280
|
-
"""
|
|
272
|
+
"""Return attribute names typed as ``ZoneInfo`` (aliases are ignored)."""
|
|
281
273
|
return {
|
|
282
274
|
field_name
|
|
283
275
|
for field_name, field in cls.model_fields.items()
|
|
@@ -290,21 +282,21 @@ class BaseAsset(BaseModel):
|
|
|
290
282
|
|
|
291
283
|
@property
|
|
292
284
|
def auth_state(self) -> AssetState:
|
|
293
|
-
"""
|
|
285
|
+
"""Authentication state persisted by SOAR (encrypted at rest); raises if no app context."""
|
|
294
286
|
if self._auth_state is None:
|
|
295
287
|
raise AppContextRequired()
|
|
296
288
|
return self._auth_state
|
|
297
289
|
|
|
298
290
|
@property
|
|
299
291
|
def cache_state(self) -> AssetState:
|
|
300
|
-
"""
|
|
292
|
+
"""Cache for miscellaneous data persisted by SOAR (encrypted at rest); raises if no app context."""
|
|
301
293
|
if self._cache_state is None:
|
|
302
294
|
raise AppContextRequired()
|
|
303
295
|
return self._cache_state
|
|
304
296
|
|
|
305
297
|
@property
|
|
306
298
|
def ingest_state(self) -> AssetState:
|
|
307
|
-
"""
|
|
299
|
+
"""Ingestion checkpoints persisted by SOAR (encrypted at rest); raises if no app context."""
|
|
308
300
|
if self._ingest_state is None:
|
|
309
301
|
raise AppContextRequired()
|
|
310
302
|
return self._ingest_state
|
soar_sdk/asset_state.py
CHANGED
|
@@ -1,24 +1,34 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from collections.abc import Iterator, MutableMapping
|
|
3
|
+
from typing import Any
|
|
3
4
|
|
|
4
5
|
from soar_sdk.shims.phantom.base_connector import BaseConnector
|
|
5
6
|
from soar_sdk.shims.phantom.encryption_helper import encryption_helper
|
|
6
7
|
|
|
7
8
|
AssetStateKeyType = str
|
|
8
|
-
AssetStateValueType =
|
|
9
|
+
AssetStateValueType = Any
|
|
9
10
|
AssetStateType = dict[AssetStateKeyType, AssetStateValueType]
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
class AssetState(MutableMapping[AssetStateKeyType, AssetStateValueType]):
|
|
13
14
|
"""An adapter to the asset state stored within SOAR. The state can be split into multiple partitions; this object represents a single partition. State is automatically encrypted at rest."""
|
|
14
15
|
|
|
15
|
-
def __init__(
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
backend: BaseConnector,
|
|
19
|
+
state_key: str,
|
|
20
|
+
asset_id: str,
|
|
21
|
+
app_id: str | None = None,
|
|
22
|
+
) -> None:
|
|
16
23
|
self.backend = backend
|
|
17
24
|
self.state_key = state_key
|
|
18
25
|
self.asset_id = asset_id
|
|
26
|
+
self.app_id = app_id
|
|
19
27
|
|
|
20
|
-
def get_all(self) -> AssetStateType:
|
|
28
|
+
def get_all(self, *, force_reload: bool = False) -> AssetStateType:
|
|
21
29
|
"""Get the entirety of this part of the asset state."""
|
|
30
|
+
if force_reload:
|
|
31
|
+
self.backend.reload_state_from_file(self.asset_id)
|
|
22
32
|
state = self.backend.load_state() or {}
|
|
23
33
|
if not (part_encrypted := state.get(self.state_key)):
|
|
24
34
|
return {}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from soar_sdk.auth.client import (
|
|
2
|
+
CertificateOAuthClient,
|
|
3
|
+
OAuthClientError,
|
|
4
|
+
SOARAssetOAuthClient,
|
|
5
|
+
)
|
|
6
|
+
from soar_sdk.auth.factories import (
|
|
7
|
+
create_oauth_auth,
|
|
8
|
+
create_oauth_callback_handler,
|
|
9
|
+
create_oauth_client,
|
|
10
|
+
)
|
|
11
|
+
from soar_sdk.auth.flows import (
|
|
12
|
+
AuthorizationCodeFlow,
|
|
13
|
+
ClientCredentialsFlow,
|
|
14
|
+
)
|
|
15
|
+
from soar_sdk.auth.httpx_auth import (
|
|
16
|
+
BasicAuth,
|
|
17
|
+
OAuthBearerAuth,
|
|
18
|
+
StaticTokenAuth,
|
|
19
|
+
)
|
|
20
|
+
from soar_sdk.auth.models import (
|
|
21
|
+
CertificateCredentials,
|
|
22
|
+
OAuthConfig,
|
|
23
|
+
OAuthToken,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"AuthorizationCodeFlow",
|
|
28
|
+
"BasicAuth",
|
|
29
|
+
"CertificateCredentials",
|
|
30
|
+
"CertificateOAuthClient",
|
|
31
|
+
"ClientCredentialsFlow",
|
|
32
|
+
"OAuthBearerAuth",
|
|
33
|
+
"OAuthClientError",
|
|
34
|
+
"OAuthConfig",
|
|
35
|
+
"OAuthToken",
|
|
36
|
+
"SOARAssetOAuthClient",
|
|
37
|
+
"StaticTokenAuth",
|
|
38
|
+
"create_oauth_auth",
|
|
39
|
+
"create_oauth_callback_handler",
|
|
40
|
+
"create_oauth_client",
|
|
41
|
+
]
|