mantis_api_client 5.2.3__tar.gz → 5.3.0__tar.gz
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.
- {mantis_api_client-5.2.3 → mantis_api_client-5.3.0}/PKG-INFO +3 -3
- {mantis_api_client-5.2.3 → mantis_api_client-5.3.0}/mantis_api_client/cli_parser/account_parser.py +57 -28
- {mantis_api_client-5.2.3 → mantis_api_client-5.3.0}/mantis_api_client/cli_parser/attack_parser.py +11 -16
- mantis_api_client-5.3.0/mantis_api_client/cli_parser/bas_parser.py +923 -0
- {mantis_api_client-5.2.3 → mantis_api_client-5.3.0}/mantis_api_client/cli_parser/basebox_parser.py +15 -14
- {mantis_api_client-5.2.3 → mantis_api_client-5.3.0}/mantis_api_client/cli_parser/dataset_parser.py +40 -30
- {mantis_api_client-5.2.3 → mantis_api_client-5.3.0}/mantis_api_client/cli_parser/lab_parser.py +24 -4
- {mantis_api_client-5.2.3 → mantis_api_client-5.3.0}/mantis_api_client/cli_parser/labs_parser.py +3 -3
- {mantis_api_client-5.2.3 → mantis_api_client-5.3.0}/mantis_api_client/cli_parser/redteam_parser.py +66 -8
- {mantis_api_client-5.2.3 → mantis_api_client-5.3.0}/mantis_api_client/cli_parser/scenario_parser.py +11 -16
- {mantis_api_client-5.2.3 → mantis_api_client-5.3.0}/mantis_api_client/cli_parser/topology_parser.py +15 -14
- {mantis_api_client-5.2.3 → mantis_api_client-5.3.0}/mantis_api_client/cli_parser/user_parser.py +10 -8
- {mantis_api_client-5.2.3 → mantis_api_client-5.3.0}/mantis_api_client/dataset_api.py +3 -7
- {mantis_api_client-5.2.3 → mantis_api_client-5.3.0}/mantis_api_client/mantis.py +13 -6
- {mantis_api_client-5.2.3 → mantis_api_client-5.3.0}/mantis_api_client/oidc.py +12 -12
- {mantis_api_client-5.2.3 → mantis_api_client-5.3.0}/mantis_api_client/scenario_api.py +87 -29
- {mantis_api_client-5.2.3 → mantis_api_client-5.3.0}/mantis_api_client/user_api.py +45 -19
- {mantis_api_client-5.2.3 → mantis_api_client-5.3.0}/mantis_api_client/utils.py +31 -10
- {mantis_api_client-5.2.3 → mantis_api_client-5.3.0}/pyproject.toml +3 -3
- {mantis_api_client-5.2.3 → mantis_api_client-5.3.0}/AUTHORS +0 -0
- {mantis_api_client-5.2.3 → mantis_api_client-5.3.0}/LICENSE +0 -0
- {mantis_api_client-5.2.3 → mantis_api_client-5.3.0}/README.md +0 -0
- {mantis_api_client-5.2.3 → mantis_api_client-5.3.0}/mantis_api_client/__init__.py +0 -0
- {mantis_api_client-5.2.3 → mantis_api_client-5.3.0}/mantis_api_client/cli_parser/log_collector_parser.py +0 -0
- {mantis_api_client-5.2.3 → mantis_api_client-5.3.0}/mantis_api_client/cli_parser/signature_parser.py +0 -0
- {mantis_api_client-5.2.3 → mantis_api_client-5.3.0}/mantis_api_client/config.py +0 -0
- {mantis_api_client-5.2.3 → mantis_api_client-5.3.0}/mantis_api_client/exceptions.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: mantis_api_client
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.3.0
|
|
4
4
|
Summary: M&NTIS Platform client API
|
|
5
5
|
License: MIT
|
|
6
6
|
Author: AMOSSYS
|
|
@@ -14,11 +14,11 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
14
14
|
Requires-Dist: alive-progress (>=3.1.5,<4.0.0)
|
|
15
15
|
Requires-Dist: argcomplete (>=1.12.1,<1.13.0)
|
|
16
16
|
Requires-Dist: colorama (>=0.4.4,<0.5.0)
|
|
17
|
-
Requires-Dist: cr-api-client (>=5.0.
|
|
17
|
+
Requires-Dist: cr-api-client (>=5.0.22,<6.0.0)
|
|
18
18
|
Requires-Dist: mantis-authz (>=0.2.3,<1.0.0)
|
|
19
19
|
Requires-Dist: mantis-catalog (>=0.1.2,<0.2.0)
|
|
20
20
|
Requires-Dist: mantis-logger (>=0.2.0,<0.3.0)
|
|
21
|
-
Requires-Dist: mantis-models (>=3.
|
|
21
|
+
Requires-Dist: mantis-models (>=3.4.0,<4.0.0)
|
|
22
22
|
Requires-Dist: omegaconf (>=2.2.2,<3.0.0)
|
|
23
23
|
Requires-Dist: pydantic (>=2.10.3,<3.0.0)
|
|
24
24
|
Requires-Dist: requests (>=2.24.0,<3.0.0)
|
{mantis_api_client-5.2.3 → mantis_api_client-5.3.0}/mantis_api_client/cli_parser/account_parser.py
RENAMED
|
@@ -18,10 +18,12 @@ import requests
|
|
|
18
18
|
from mantis_authz import jwt
|
|
19
19
|
from rich.console import Console
|
|
20
20
|
from rich.prompt import Prompt
|
|
21
|
+
from rich.tree import Tree
|
|
21
22
|
|
|
23
|
+
import mantis_api_client
|
|
22
24
|
import mantis_api_client.dataset_api as dataset_api
|
|
23
|
-
import mantis_api_client.oidc
|
|
24
25
|
import mantis_api_client.scenario_api as scenario_api
|
|
26
|
+
from mantis_api_client import user_api
|
|
25
27
|
from mantis_api_client.config import mantis_api_client_config
|
|
26
28
|
from mantis_api_client.oidc import get_oidc_client
|
|
27
29
|
from mantis_api_client.utils import colored
|
|
@@ -100,12 +102,14 @@ def status_handler(args: Any) -> None: # noqa: C901
|
|
|
100
102
|
try:
|
|
101
103
|
scenario_api_version = scenario_api.get_version()
|
|
102
104
|
scenario_vers = Version(str_vers=scenario_api_version)
|
|
105
|
+
cyber_range_version = scenario_api.get_cyberrange_version()
|
|
103
106
|
except requests.exceptions.ConnectionError:
|
|
104
107
|
exit_code = 1
|
|
105
108
|
print(" [-] API status: " + colored("not running !", "white", "on_red"))
|
|
106
109
|
else:
|
|
107
110
|
print(" [+] API status: " + colored("OK", "grey", "on_green"))
|
|
108
111
|
print(" [+] version: {}".format(scenario_api_version))
|
|
112
|
+
print(" [+] Cyber Range version: {}".format(cyber_range_version))
|
|
109
113
|
if scenario_vers.major != client_vers.major:
|
|
110
114
|
exit_code = 1
|
|
111
115
|
print(
|
|
@@ -144,7 +148,7 @@ def info_handler(args: Any) -> None: # noqa: C901
|
|
|
144
148
|
|
|
145
149
|
# Handle case where the user pertains to an organization
|
|
146
150
|
if user_groups is not None:
|
|
147
|
-
default_group = get_oidc_client().
|
|
151
|
+
default_group = get_oidc_client().get_default_workspace()
|
|
148
152
|
groups_comma_sep = ", ".join(user_groups)
|
|
149
153
|
if default_group:
|
|
150
154
|
# mark default group bold
|
|
@@ -166,6 +170,7 @@ def info_handler(args: Any) -> None: # noqa: C901
|
|
|
166
170
|
# 'login_handler' handler
|
|
167
171
|
#
|
|
168
172
|
def login_handler(args: Any) -> None:
|
|
173
|
+
oidc_client = get_oidc_client()
|
|
169
174
|
# Parameters
|
|
170
175
|
oidc_domain = args.domain
|
|
171
176
|
username = args.username
|
|
@@ -188,7 +193,6 @@ def login_handler(args: Any) -> None:
|
|
|
188
193
|
"groups",
|
|
189
194
|
"profile",
|
|
190
195
|
"email",
|
|
191
|
-
"dataset:read",
|
|
192
196
|
"scenario:run",
|
|
193
197
|
]
|
|
194
198
|
)
|
|
@@ -199,11 +203,11 @@ def login_handler(args: Any) -> None:
|
|
|
199
203
|
thread = _init_create_callback_request_handler_thread(code_placeholder)
|
|
200
204
|
thread.start()
|
|
201
205
|
if username and password:
|
|
202
|
-
token =
|
|
206
|
+
token = oidc_client.token(
|
|
203
207
|
oidc_domain, username, password, redirect_uri=redirect_uri, scope=scope
|
|
204
208
|
)
|
|
205
209
|
else:
|
|
206
|
-
auth_url =
|
|
210
|
+
auth_url = oidc_client.auth_url(
|
|
207
211
|
oidc_domain,
|
|
208
212
|
redirect_uri=redirect_uri,
|
|
209
213
|
scope=scope,
|
|
@@ -234,29 +238,54 @@ def login_handler(args: Any) -> None:
|
|
|
234
238
|
redirect_uri=redirect_uri,
|
|
235
239
|
)
|
|
236
240
|
print("Access granted")
|
|
237
|
-
|
|
238
|
-
claims = jwt.get_unverified_claims(token["access_token"])
|
|
239
|
-
selected_group = args.group
|
|
240
|
-
claimed_groups = claims.get("groups")
|
|
241
|
+
oidc_client.configure_profile(oidc_domain, token["refresh_token"])
|
|
241
242
|
|
|
242
243
|
# Handle case where the user does not pertain to an organization
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
244
|
+
subject_workspaces = user_api.fetch_current_workspaces()
|
|
245
|
+
subject_organizations = user_api.fetch_current_seats()
|
|
246
|
+
subject_orgs_wss = [
|
|
247
|
+
(sub_org, sub_ws)
|
|
248
|
+
for sub_org in subject_organizations
|
|
249
|
+
for sub_ws in subject_workspaces
|
|
250
|
+
if sub_ws["organization_id"] == sub_org["id"]
|
|
251
|
+
]
|
|
252
|
+
selected_idx: int | None = None
|
|
253
|
+
if args.workspace:
|
|
254
|
+
for i, (_, ws) in enumerate(subject_orgs_wss):
|
|
255
|
+
if args.workspace == ws["id"]:
|
|
256
|
+
selected_idx = i
|
|
257
|
+
console = Console()
|
|
258
|
+
if selected_idx is None:
|
|
259
|
+
match len(subject_orgs_wss):
|
|
260
|
+
case 0:
|
|
261
|
+
return
|
|
262
|
+
case 1:
|
|
263
|
+
selected_idx = 0
|
|
264
|
+
case _:
|
|
265
|
+
i = 1
|
|
266
|
+
root = Tree(":file_folder:[yellow]Workspace memberships")
|
|
267
|
+
for org in subject_organizations:
|
|
268
|
+
org_branch = root.add(f"Organization [magenta]{org['name']}")
|
|
269
|
+
for ws in subject_workspaces:
|
|
270
|
+
if ws["organization_id"] != org["id"]:
|
|
271
|
+
continue
|
|
272
|
+
org_branch.add(rf"[bold green]{i}[/bold green]. {ws['name']}")
|
|
273
|
+
i += 1
|
|
274
|
+
console.print(root)
|
|
275
|
+
selected_idx = (
|
|
276
|
+
int(
|
|
277
|
+
Prompt.ask(
|
|
278
|
+
"Select a default workspace",
|
|
279
|
+
choices=[str(k + 1) for k in range(len(subject_orgs_wss))],
|
|
280
|
+
)
|
|
281
|
+
)
|
|
282
|
+
- 1
|
|
283
|
+
)
|
|
284
|
+
selected_ws = subject_orgs_wss[selected_idx][1]
|
|
285
|
+
console.print(f"Workspace [b green]{selected_ws['name']}[/b green] activated")
|
|
257
286
|
|
|
258
|
-
|
|
259
|
-
oidc_domain, token["refresh_token"],
|
|
287
|
+
oidc_client.configure_profile(
|
|
288
|
+
oidc_domain, token["refresh_token"], selected_ws["id"]
|
|
260
289
|
)
|
|
261
290
|
|
|
262
291
|
|
|
@@ -360,9 +389,9 @@ def add_account_parser(root_parser: argparse.ArgumentParser, subparsers: Any) ->
|
|
|
360
389
|
help="Read your M&ntis cluster SSO password from a file",
|
|
361
390
|
)
|
|
362
391
|
parser_login.add_argument(
|
|
363
|
-
"-
|
|
364
|
-
"--
|
|
365
|
-
help="
|
|
392
|
+
"-w",
|
|
393
|
+
"--workspace",
|
|
394
|
+
help="Pass the workspace that will be used as default for workspace-aware commands",
|
|
366
395
|
)
|
|
367
396
|
parser_login.set_defaults(func=login_handler)
|
|
368
397
|
|
{mantis_api_client-5.2.3 → mantis_api_client-5.3.0}/mantis_api_client/cli_parser/attack_parser.py
RENAMED
|
@@ -11,7 +11,6 @@ from pydantic.json import pydantic_encoder
|
|
|
11
11
|
from ruamel.yaml import YAML
|
|
12
12
|
|
|
13
13
|
from mantis_api_client import scenario_api
|
|
14
|
-
from mantis_api_client import user_api
|
|
15
14
|
from mantis_api_client.oidc import get_oidc_client
|
|
16
15
|
from mantis_api_client.utils import colored
|
|
17
16
|
from mantis_api_client.utils import wait_lab
|
|
@@ -85,20 +84,19 @@ def _attack_create_or_run_lab(args: Any, do_run: bool = True):
|
|
|
85
84
|
scenario_profile = args.scenario_profile
|
|
86
85
|
lab_config_path = args.lab_config_path
|
|
87
86
|
|
|
88
|
-
if not args.
|
|
87
|
+
if not args.workspace_id:
|
|
89
88
|
try:
|
|
90
|
-
|
|
89
|
+
workspace_id = get_oidc_client().get_default_workspace(raise_exc=True)
|
|
91
90
|
except Exception as e:
|
|
92
91
|
print(colored(f"Error when fetching attacks: '{e}'", "red"))
|
|
93
92
|
sys.exit(1)
|
|
94
93
|
else:
|
|
95
|
-
|
|
94
|
+
workspace_id = args.workspace_id
|
|
96
95
|
|
|
97
96
|
# Retrieve associated group id
|
|
98
|
-
if
|
|
97
|
+
if workspace_id is None:
|
|
99
98
|
print(colored("Your subscription level does not permit to launch labs", "red"))
|
|
100
99
|
sys.exit(1)
|
|
101
|
-
group_id = user_api.get_organization_id_from_group_name(group_name)
|
|
102
100
|
|
|
103
101
|
# Safety checks
|
|
104
102
|
try:
|
|
@@ -151,23 +149,20 @@ def _attack_create_or_run_lab(args: Any, do_run: bool = True):
|
|
|
151
149
|
attack,
|
|
152
150
|
scenario_profile,
|
|
153
151
|
lab_config,
|
|
154
|
-
|
|
155
|
-
group_id,
|
|
152
|
+
workspace_id,
|
|
156
153
|
)
|
|
157
154
|
else:
|
|
158
155
|
lab_id = scenario_api.create_lab_attack(
|
|
159
156
|
attack,
|
|
160
157
|
scenario_profile,
|
|
161
158
|
lab_config,
|
|
162
|
-
|
|
163
|
-
group_id,
|
|
159
|
+
workspace_id,
|
|
164
160
|
)
|
|
165
161
|
|
|
166
|
-
print(f"[+]
|
|
162
|
+
print(f"[+] Lab ID: {lab_id}")
|
|
167
163
|
|
|
168
164
|
if do_run:
|
|
169
165
|
wait_lab(lab_id)
|
|
170
|
-
print("[+] Scenario ended")
|
|
171
166
|
|
|
172
167
|
except Exception as e:
|
|
173
168
|
print(colored(f"Error when running attack {attack_name}: '{e}'", "red"))
|
|
@@ -251,8 +246,8 @@ def add_attack_parser(root_parser: argparse.ArgumentParser, subparsers: Any) ->
|
|
|
251
246
|
help="Allows to define the unit scenario to create_lab for a unit attack",
|
|
252
247
|
)
|
|
253
248
|
parser_attack_create_lab.add_argument(
|
|
254
|
-
"--
|
|
255
|
-
dest="
|
|
249
|
+
"--workspace_id",
|
|
250
|
+
dest="workspace_id",
|
|
256
251
|
help="The group name that have ownership on lab",
|
|
257
252
|
)
|
|
258
253
|
parser_attack_create_lab.add_argument(
|
|
@@ -285,8 +280,8 @@ def add_attack_parser(root_parser: argparse.ArgumentParser, subparsers: Any) ->
|
|
|
285
280
|
help="Allows to define the unit scenario to run for a unit attack",
|
|
286
281
|
)
|
|
287
282
|
parser_attack_run_lab.add_argument(
|
|
288
|
-
"--
|
|
289
|
-
dest="
|
|
283
|
+
"--workspace_id",
|
|
284
|
+
dest="workspace_id",
|
|
290
285
|
help="The group name that have ownership on lab",
|
|
291
286
|
)
|
|
292
287
|
parser_attack_run_lab.add_argument(
|