mantis_api_client 5.5.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.
- mantis_api_client/__init__.py +21 -0
- mantis_api_client/cli_parser/account_parser.py +451 -0
- mantis_api_client/cli_parser/attack_parser.py +317 -0
- mantis_api_client/cli_parser/bas_parser.py +1204 -0
- mantis_api_client/cli_parser/basebox_parser.py +258 -0
- mantis_api_client/cli_parser/dataset_parser.py +200 -0
- mantis_api_client/cli_parser/lab_parser.py +805 -0
- mantis_api_client/cli_parser/labs_parser.py +87 -0
- mantis_api_client/cli_parser/log_collector_parser.py +71 -0
- mantis_api_client/cli_parser/redteam_parser.py +375 -0
- mantis_api_client/cli_parser/scenario_parser.py +311 -0
- mantis_api_client/cli_parser/signature_parser.py +147 -0
- mantis_api_client/cli_parser/topology_parser.py +255 -0
- mantis_api_client/cli_parser/user_parser.py +225 -0
- mantis_api_client/config.py +73 -0
- mantis_api_client/dataset_api.py +267 -0
- mantis_api_client/exceptions.py +27 -0
- mantis_api_client/mantis.py +186 -0
- mantis_api_client/oidc.py +302 -0
- mantis_api_client/scenario_api.py +1196 -0
- mantis_api_client/user_api.py +282 -0
- mantis_api_client/utils.py +130 -0
- mantis_api_client-5.5.0.dist-info/AUTHORS +1 -0
- mantis_api_client-5.5.0.dist-info/LICENSE +19 -0
- mantis_api_client-5.5.0.dist-info/METADATA +33 -0
- mantis_api_client-5.5.0.dist-info/RECORD +28 -0
- mantis_api_client-5.5.0.dist-info/WHEEL +4 -0
- mantis_api_client-5.5.0.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
import argparse
|
|
3
|
+
import json
|
|
4
|
+
import sys
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from pydantic.json import pydantic_encoder
|
|
8
|
+
|
|
9
|
+
from mantis_api_client import user_api
|
|
10
|
+
from mantis_api_client.oidc import get_oidc_client
|
|
11
|
+
from mantis_api_client.utils import colored
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# #
|
|
15
|
+
# # 'user_list_handler' handler
|
|
16
|
+
# #
|
|
17
|
+
# def user_list_handler(args: Any) -> None:
|
|
18
|
+
# try:
|
|
19
|
+
# users = user_api.fetch_users()
|
|
20
|
+
# except Exception as e:
|
|
21
|
+
# print(colored(f"Error when fetching users: '{e}'", "red"))
|
|
22
|
+
# sys.exit(1)
|
|
23
|
+
|
|
24
|
+
# if args.json:
|
|
25
|
+
# print(json.dumps(users, default=pydantic_encoder))
|
|
26
|
+
# return
|
|
27
|
+
|
|
28
|
+
# print("[+] Organization users:")
|
|
29
|
+
# for user in users["data"]:
|
|
30
|
+
# print(f" [+] {user['id']}: {user['given_name']} {user['last_name']}")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
#
|
|
34
|
+
# 'user_self_handler' handler
|
|
35
|
+
#
|
|
36
|
+
def user_self_handler(args: Any) -> None:
|
|
37
|
+
try:
|
|
38
|
+
user = user_api.fetch_current_user()
|
|
39
|
+
except Exception as e:
|
|
40
|
+
print(colored(f"Error when fetching current user information: '{e}'", "red"))
|
|
41
|
+
sys.exit(1)
|
|
42
|
+
|
|
43
|
+
if args.json:
|
|
44
|
+
print(json.dumps(user, default=pydantic_encoder))
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
print("[+] Current user information:")
|
|
48
|
+
print(f" [+] ID: {user['id']}")
|
|
49
|
+
print(f" [+] Name: {user['given_name']} {user['last_name']}")
|
|
50
|
+
print(f" [+] Email: {user['email']}")
|
|
51
|
+
print(f" [+] Creation date: {user['creation_date']}")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
#
|
|
55
|
+
# 'user_info_handler' handler
|
|
56
|
+
#
|
|
57
|
+
def user_info_handler(args: Any) -> None:
|
|
58
|
+
# Parameters
|
|
59
|
+
user_id = args.user_id
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
user = user_api.fetch_user(user_id)
|
|
63
|
+
except Exception as e:
|
|
64
|
+
print(
|
|
65
|
+
colored(
|
|
66
|
+
f"Error when fetching information for user '{user_id}': '{e}'",
|
|
67
|
+
"red",
|
|
68
|
+
"on_white",
|
|
69
|
+
)
|
|
70
|
+
)
|
|
71
|
+
sys.exit(1)
|
|
72
|
+
|
|
73
|
+
if args.json:
|
|
74
|
+
print(json.dumps(user, default=pydantic_encoder))
|
|
75
|
+
return
|
|
76
|
+
|
|
77
|
+
print("[+] User information:")
|
|
78
|
+
print(f" [+] ID: {user['id']}")
|
|
79
|
+
print(f" [+] Name: {user['given_name']} {user['last_name']}")
|
|
80
|
+
print(f" [+] Email: {user['email']}")
|
|
81
|
+
print(f" [+] Creation date: {user['creation_date']}")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
#
|
|
85
|
+
# 'organization_info_handler' handler
|
|
86
|
+
#
|
|
87
|
+
def organization_info_handler(args: Any) -> None:
|
|
88
|
+
try:
|
|
89
|
+
# Retrieve current organization name
|
|
90
|
+
workspace_id = get_oidc_client().get_default_workspace(raise_exc=True)
|
|
91
|
+
|
|
92
|
+
# Retrieve associated organization id
|
|
93
|
+
if workspace_id is None:
|
|
94
|
+
print("Current user not linked to a workspace")
|
|
95
|
+
sys.exit(0)
|
|
96
|
+
|
|
97
|
+
# Retrieve organization info
|
|
98
|
+
workspaces_info = iter(user_api.fetch_current_workspaces())
|
|
99
|
+
organization_id = next(
|
|
100
|
+
workspace_info["organization_id"]
|
|
101
|
+
for workspace_info in workspaces_info
|
|
102
|
+
if workspace_info["id"] == workspace_id
|
|
103
|
+
)
|
|
104
|
+
organization_info = user_api.fetch_organization(organization_id)
|
|
105
|
+
|
|
106
|
+
# Retrieve plan info
|
|
107
|
+
plan_info = user_api.get_plan(organization_info["plan_id"])
|
|
108
|
+
|
|
109
|
+
# Retrieve plan limits
|
|
110
|
+
plan_limits = user_api.get_plan_limits(organization_info["plan_id"])
|
|
111
|
+
|
|
112
|
+
# Retrieve credits list
|
|
113
|
+
credits_list = user_api.get_credits_list(organization_id)
|
|
114
|
+
|
|
115
|
+
# Retrieve consumption history
|
|
116
|
+
consumption_history = user_api.get_consumption_history(organization_id)
|
|
117
|
+
except Exception as e:
|
|
118
|
+
print(
|
|
119
|
+
colored(
|
|
120
|
+
f"Error when fetching information for organization '{workspace_id}': '{e}'",
|
|
121
|
+
"red",
|
|
122
|
+
"on_white",
|
|
123
|
+
)
|
|
124
|
+
)
|
|
125
|
+
sys.exit(1)
|
|
126
|
+
|
|
127
|
+
if args.json:
|
|
128
|
+
print(json.dumps(organization_info, default=pydantic_encoder))
|
|
129
|
+
print(json.dumps(plan_info, default=pydantic_encoder))
|
|
130
|
+
print(json.dumps(plan_limits, default=pydantic_encoder))
|
|
131
|
+
print(json.dumps(credits_list, default=pydantic_encoder))
|
|
132
|
+
print(json.dumps(consumption_history, default=pydantic_encoder))
|
|
133
|
+
return
|
|
134
|
+
|
|
135
|
+
print("[+] Organization information:")
|
|
136
|
+
print(f" [+] ID: {organization_info['id']}")
|
|
137
|
+
print(f" [+] Name: {organization_info['name']}")
|
|
138
|
+
print(f" [+] Subscription start: {organization_info['subscription_start']}")
|
|
139
|
+
print(f" [+] Subscription end: {organization_info['subscription_end']}")
|
|
140
|
+
|
|
141
|
+
print("[+] Plan information:")
|
|
142
|
+
print(f" [+] ID: {plan_info['id']}")
|
|
143
|
+
print(f" [+] Name: {plan_info['name']}")
|
|
144
|
+
print(f" [+] Frequency: {plan_info['frequency']}")
|
|
145
|
+
|
|
146
|
+
print("[+] Plan limits:")
|
|
147
|
+
print(f" [+] Max active labs: {plan_limits['nb_active_labs']}")
|
|
148
|
+
print(f" [+] Max accounts: {plan_limits['nb_accounts']}")
|
|
149
|
+
print(f" [+] Max workspaces: {plan_limits['nb_workspaces']}")
|
|
150
|
+
print(f" [+] Max lab duration: {plan_limits['lab_max_duration']}")
|
|
151
|
+
|
|
152
|
+
print("[+] Credits:")
|
|
153
|
+
if "data" in credits_list:
|
|
154
|
+
for credit in credits_list["data"]:
|
|
155
|
+
print(f" [+] ID: {credit['id']}")
|
|
156
|
+
print(f" [+] Purpose: {credit['purpose']}")
|
|
157
|
+
print(
|
|
158
|
+
f" [+] Period: {credit['creation_date']} to {credit['expiration_date']}"
|
|
159
|
+
)
|
|
160
|
+
print(f" [+] Credit: {credit['remaining']}/{credit['total']}")
|
|
161
|
+
else:
|
|
162
|
+
print(" [+] No credits")
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def add_user_parser(
|
|
166
|
+
root_parser: argparse.ArgumentParser,
|
|
167
|
+
subparsers: Any,
|
|
168
|
+
) -> None:
|
|
169
|
+
# --------------------
|
|
170
|
+
# --- User API options (users)
|
|
171
|
+
# --------------------
|
|
172
|
+
|
|
173
|
+
parser_user = subparsers.add_parser(
|
|
174
|
+
"user",
|
|
175
|
+
help="User API related commands",
|
|
176
|
+
formatter_class=root_parser.formatter_class,
|
|
177
|
+
)
|
|
178
|
+
subparsers_user = parser_user.add_subparsers()
|
|
179
|
+
|
|
180
|
+
# # 'user_list' command
|
|
181
|
+
# parser_user_list = subparsers_user.add_parser(
|
|
182
|
+
# "list",
|
|
183
|
+
# help="List all organization users",
|
|
184
|
+
# formatter_class=root_parser.formatter_class,
|
|
185
|
+
# )
|
|
186
|
+
# parser_user_list.set_defaults(func=user_list_handler)
|
|
187
|
+
# parser_user_list.add_argument(
|
|
188
|
+
# "--json", help="Return JSON result.", action="store_true"
|
|
189
|
+
# )
|
|
190
|
+
|
|
191
|
+
# 'user_self' command
|
|
192
|
+
parser_user_self = subparsers_user.add_parser(
|
|
193
|
+
"self",
|
|
194
|
+
help="Retrieve information for current user",
|
|
195
|
+
formatter_class=root_parser.formatter_class,
|
|
196
|
+
)
|
|
197
|
+
parser_user_self.set_defaults(func=user_self_handler)
|
|
198
|
+
parser_user_self.add_argument(
|
|
199
|
+
"--json", help="Return JSON result.", action="store_true"
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
# 'user_info' command
|
|
203
|
+
parser_user_info = subparsers_user.add_parser(
|
|
204
|
+
"info",
|
|
205
|
+
help="Retrieve information about a specific user",
|
|
206
|
+
formatter_class=root_parser.formatter_class,
|
|
207
|
+
)
|
|
208
|
+
parser_user_info.set_defaults(func=user_info_handler)
|
|
209
|
+
parser_user_info.add_argument("user_id", type=str, help="The user ID")
|
|
210
|
+
parser_user_info.add_argument(
|
|
211
|
+
"--json", help="Return JSON result.", action="store_true"
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
# 'orga_info' command
|
|
215
|
+
parser_user_info = subparsers_user.add_parser(
|
|
216
|
+
"organization",
|
|
217
|
+
help="Retrieve information about current organization",
|
|
218
|
+
formatter_class=root_parser.formatter_class,
|
|
219
|
+
)
|
|
220
|
+
parser_user_info.set_defaults(func=organization_info_handler)
|
|
221
|
+
parser_user_info.add_argument(
|
|
222
|
+
"--json", help="Return JSON result.", action="store_true"
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
parser_user.set_defaults(func=lambda _: parser_user.print_help())
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
#
|
|
4
|
+
# Copyright (c) 2016-2021 AMOSSYS
|
|
5
|
+
#
|
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
# in the Software without restriction, including without limitation the rights
|
|
9
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
# furnished to do so, subject to the following conditions:
|
|
12
|
+
#
|
|
13
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
# copies or substantial portions of the Software.
|
|
15
|
+
#
|
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
# SOFTWARE.
|
|
23
|
+
#
|
|
24
|
+
from dataclasses import dataclass
|
|
25
|
+
from dataclasses import field
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
from typing import Optional
|
|
28
|
+
|
|
29
|
+
from mantis_authz.config import AuthzConfig
|
|
30
|
+
from omegaconf import OmegaConf
|
|
31
|
+
from omegaconf import SI
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass
|
|
35
|
+
class MantisOIDCConfig(AuthzConfig):
|
|
36
|
+
realm: str = "mantis"
|
|
37
|
+
client_id: str = "mantis-cli"
|
|
38
|
+
domain: Optional[str] = None
|
|
39
|
+
redirect_url: dict = field(
|
|
40
|
+
default_factory=lambda: {
|
|
41
|
+
"scheme": "http",
|
|
42
|
+
"host": "localhost",
|
|
43
|
+
"port": 9876,
|
|
44
|
+
"path": "/callback",
|
|
45
|
+
}
|
|
46
|
+
)
|
|
47
|
+
redirect_url_timeout: int = SI(
|
|
48
|
+
"${oc.decode:${oc.env:OIDC_REDIRECT_URL_TIMEOUT,60}}"
|
|
49
|
+
)
|
|
50
|
+
redirect_uri: str = (
|
|
51
|
+
"${oc.env:OIDC_REDIRECT_URI,${.redirect_url.scheme}://${.redirect_url.host}:${.redirect_url.port}${.redirect_url.path}}"
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass
|
|
56
|
+
class MantisApiClientConfig:
|
|
57
|
+
dataset_api_url: str
|
|
58
|
+
dataset_web_url: str
|
|
59
|
+
scenario_api_url: str
|
|
60
|
+
user_api_url: str
|
|
61
|
+
cacert: Optional[Path] = SI("${oc.env:CR_CA_CERT,null}")
|
|
62
|
+
cert: Optional[Path] = SI("${oc.env:CR_CLIENT_CERT,null}")
|
|
63
|
+
key: Optional[Path] = SI("${oc.env:CR_CLIENT_KEY,null}")
|
|
64
|
+
|
|
65
|
+
config_file: Path = SI(
|
|
66
|
+
"${oc.env:MANTIS_CONFIG,${oc.env:XDG_CONFIG_HOME,${oc.env:HOME}/.config}/mantis/config.yml}"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# OIDC
|
|
70
|
+
oidc: MantisOIDCConfig = field(default_factory=MantisOIDCConfig)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
mantis_api_client_config = OmegaConf.structured(MantisApiClientConfig)
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
#
|
|
4
|
+
# Copyright (c) 2016-2021 AMOSSYS
|
|
5
|
+
#
|
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
# in the Software without restriction, including without limitation the rights
|
|
9
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
# furnished to do so, subject to the following conditions:
|
|
12
|
+
#
|
|
13
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
# copies or substantial portions of the Software.
|
|
15
|
+
#
|
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
# SOFTWARE.
|
|
23
|
+
#
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
from typing import Any
|
|
26
|
+
from uuid import UUID
|
|
27
|
+
|
|
28
|
+
import requests
|
|
29
|
+
from mantis_dataset_model.dataset_model import Manifest
|
|
30
|
+
|
|
31
|
+
from mantis_api_client.config import mantis_api_client_config
|
|
32
|
+
from mantis_api_client.oidc import authorize
|
|
33
|
+
|
|
34
|
+
# -------------------------------------------------------------------------- #
|
|
35
|
+
# Internal helpers
|
|
36
|
+
# -------------------------------------------------------------------------- #
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@authorize
|
|
40
|
+
def _get(route: str, **kwargs: Any) -> requests.Response:
|
|
41
|
+
return requests.get(
|
|
42
|
+
f"{mantis_api_client_config.dataset_api_url}{route}",
|
|
43
|
+
verify=mantis_api_client_config.cacert,
|
|
44
|
+
cert=(mantis_api_client_config.cert, mantis_api_client_config.key),
|
|
45
|
+
**kwargs,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@authorize
|
|
50
|
+
def _post(route: str, **kwargs: Any) -> requests.Response:
|
|
51
|
+
timeout = kwargs.pop("timeout", 30)
|
|
52
|
+
return requests.post(
|
|
53
|
+
f"{mantis_api_client_config.dataset_api_url}{route}",
|
|
54
|
+
verify=mantis_api_client_config.cacert,
|
|
55
|
+
cert=(mantis_api_client_config.cert, mantis_api_client_config.key),
|
|
56
|
+
timeout=timeout,
|
|
57
|
+
**kwargs,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@authorize
|
|
62
|
+
def _put(route: str, **kwargs: Any) -> requests.Response:
|
|
63
|
+
return requests.put(
|
|
64
|
+
f"{mantis_api_client_config.dataset_api_url}{route}",
|
|
65
|
+
verify=mantis_api_client_config.cacert,
|
|
66
|
+
cert=(mantis_api_client_config.cert, mantis_api_client_config.key),
|
|
67
|
+
**kwargs,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@authorize
|
|
72
|
+
def _delete(route: str, **kwargs: Any) -> requests.Response:
|
|
73
|
+
return requests.delete(
|
|
74
|
+
f"{mantis_api_client_config.dataset_api_url}{route}",
|
|
75
|
+
verify=mantis_api_client_config.cacert,
|
|
76
|
+
cert=(mantis_api_client_config.cert, mantis_api_client_config.key),
|
|
77
|
+
**kwargs,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _handle_error(
|
|
82
|
+
result: requests.Response, context_error_msg: str
|
|
83
|
+
) -> requests.Response:
|
|
84
|
+
if (
|
|
85
|
+
result.headers.get("content-type") == "application/json"
|
|
86
|
+
and "message" in result.json()
|
|
87
|
+
):
|
|
88
|
+
error_msg = result.json()["message"]
|
|
89
|
+
else:
|
|
90
|
+
error_msg = result.text
|
|
91
|
+
|
|
92
|
+
raise Exception(
|
|
93
|
+
f"{context_error_msg}. "
|
|
94
|
+
f"Status code: '{result.status_code}'.\n"
|
|
95
|
+
f"Error message: '{error_msg}'."
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
# -------------------------------------------------------------------------- #
|
|
100
|
+
# Internal helpers
|
|
101
|
+
# -------------------------------------------------------------------------- #
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def get_version() -> str:
|
|
105
|
+
"""
|
|
106
|
+
Return dataset API version.
|
|
107
|
+
|
|
108
|
+
:return: The version inumber n a string
|
|
109
|
+
"""
|
|
110
|
+
result = _get("/version")
|
|
111
|
+
|
|
112
|
+
if result.status_code != 200:
|
|
113
|
+
_handle_error(result, "Cannot retrieve dataset API version")
|
|
114
|
+
|
|
115
|
+
return result.json()
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
# -------------------------------------------------------------------------- #
|
|
119
|
+
# Dataset API
|
|
120
|
+
# -------------------------------------------------------------------------- #
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def fetch_datasets() -> Any:
|
|
124
|
+
"""
|
|
125
|
+
List all available datasets
|
|
126
|
+
|
|
127
|
+
:return: the list of manifests
|
|
128
|
+
"""
|
|
129
|
+
result = _get("/")
|
|
130
|
+
|
|
131
|
+
if result.status_code != 200:
|
|
132
|
+
_handle_error(result, "Cannot retrieve datasets from dataset API")
|
|
133
|
+
|
|
134
|
+
return result.json()
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def fetch_dataset_by_uuid(partial_dataset_id: str) -> Any:
|
|
138
|
+
"""
|
|
139
|
+
Get the full JSON manifest of a specific dataset
|
|
140
|
+
|
|
141
|
+
:param partial_dataset_id: UUID of the dataset to fetch, or prefix of it that uniquely identifies the dataset
|
|
142
|
+
|
|
143
|
+
If a full UUID is given, returns the identified dataset metadata if it
|
|
144
|
+
exists. If a partial UUID (prefix of an existing UUID) is given, and this
|
|
145
|
+
prefix uniquely identifies one and only one dataset, the latter's metadata
|
|
146
|
+
is returned.
|
|
147
|
+
"""
|
|
148
|
+
result = _get(f"/{partial_dataset_id}")
|
|
149
|
+
|
|
150
|
+
if result.status_code != 200:
|
|
151
|
+
_handle_error(result, "Cannot retrieve dataset from dataset API")
|
|
152
|
+
else:
|
|
153
|
+
dataset_data = result.json()
|
|
154
|
+
dataset = Manifest(**dataset_data)
|
|
155
|
+
|
|
156
|
+
return dataset
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def fetch_dataset_resource(
|
|
160
|
+
partial_dataset_id: str, resource_type_str: str, resource_id: UUID
|
|
161
|
+
) -> Any:
|
|
162
|
+
"""
|
|
163
|
+
Get the description of a specific resource.
|
|
164
|
+
|
|
165
|
+
This function *does not* fetch the file(s) associated with one resource,
|
|
166
|
+
it merely returns the portion of the JSON manifest that describes that
|
|
167
|
+
particular resource. This JSON includes, in particular, the list
|
|
168
|
+
of files included in the resource, and the URLs to download them. It is
|
|
169
|
+
up to the user to manually fetch these URLs.
|
|
170
|
+
|
|
171
|
+
:param partial_dataset_id: UUID of the dataset to which the resource belongs, or a prefix of it that uniquely identifies it
|
|
172
|
+
:param resource_type_str: type of the resource to fetch. Must be a string among "log", "pcap", "memory_dump", "redteam_report", "life_report"
|
|
173
|
+
:param resource_id: UUID of the resource
|
|
174
|
+
:return: JSON structure describing the resource
|
|
175
|
+
"""
|
|
176
|
+
result = _get(f"/{partial_dataset_id}/{resource_type_str}/{resource_id}")
|
|
177
|
+
|
|
178
|
+
if result.status_code != 200:
|
|
179
|
+
_handle_error(result, "Cannot retrieve dataset from dataset API")
|
|
180
|
+
|
|
181
|
+
return result.json()
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
# def delete_dataset(
|
|
185
|
+
# dataset_id: str, force: bool = False
|
|
186
|
+
# ) -> None:
|
|
187
|
+
# """
|
|
188
|
+
# Delete a specific dataset
|
|
189
|
+
|
|
190
|
+
# :param dataset_id: the full UUID of the dataset to delete
|
|
191
|
+
# :param force: (optional, default False) if True, forces the deletion of the dataset, even if there are validation errors in the manifest, or in the dataset contents. If the manifest is corrupted, remotely stored resources may not be deleted even though delete_remote_data was set to True.
|
|
192
|
+
|
|
193
|
+
# """
|
|
194
|
+
|
|
195
|
+
# raise NotImplementedError("This API method is not currently available")
|
|
196
|
+
|
|
197
|
+
# # result = _delete(
|
|
198
|
+
# # f"/{dataset_id}",
|
|
199
|
+
# # params={"force": force},
|
|
200
|
+
# # )
|
|
201
|
+
|
|
202
|
+
# # if result.status_code != 200:
|
|
203
|
+
# # _handle_error(result, "Cannot delete dataset from dataset API")
|
|
204
|
+
|
|
205
|
+
# # return None
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
# def delete_all_datasets(force: bool = False) -> Any:
|
|
209
|
+
# """
|
|
210
|
+
# Delete all datasets
|
|
211
|
+
|
|
212
|
+
# :param force: (optional, default False) if True, forces the deletion of the datasets, even if there are validation errors in the manifests, or in the datasets contents. If a manifest is corrupted, remotely stored resources for that dataset may not be deleted even though delete_remote_data was set to True.
|
|
213
|
+
# :return: a JSON structure specifying the datasets that were successfully deleted, and the errors encountered
|
|
214
|
+
|
|
215
|
+
# """
|
|
216
|
+
|
|
217
|
+
# raise NotImplementedError("This API method is not currently available")
|
|
218
|
+
|
|
219
|
+
# # result = _delete(
|
|
220
|
+
# # "/all", params={"force": force}
|
|
221
|
+
# # )
|
|
222
|
+
|
|
223
|
+
# # if result.status_code != 200:
|
|
224
|
+
# # _handle_error(result, "Cannot delete datasets from dataset API")
|
|
225
|
+
|
|
226
|
+
# # return result.json()
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def get_archive_infos_for_dataset(partial_dataset_id: str) -> Any:
|
|
230
|
+
"""
|
|
231
|
+
Retrieve information about a zip archive for a partial_dataset_id.
|
|
232
|
+
|
|
233
|
+
:param partial_dataset_id: the partial or complete UUID of the dataset
|
|
234
|
+
:return: a JSON structure containing size, url and if the archive exists.
|
|
235
|
+
"""
|
|
236
|
+
result = _get(f"/{partial_dataset_id}/zip")
|
|
237
|
+
|
|
238
|
+
if result.status_code != 200:
|
|
239
|
+
_handle_error(
|
|
240
|
+
result,
|
|
241
|
+
"Cannot retrieve dataset archive information from dataset API",
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
return result.json()
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def upload_dataset(dataset_path: Path, workspace_id: UUID | None) -> UUID:
|
|
248
|
+
# Uploading the dataset
|
|
249
|
+
params = {"filename": dataset_path.name, "owner": workspace_id}
|
|
250
|
+
with dataset_path.open("rb") as f:
|
|
251
|
+
result = _post("/upload", params=params, data=f, timeout=None)
|
|
252
|
+
|
|
253
|
+
if result.status_code != 200:
|
|
254
|
+
_handle_error(
|
|
255
|
+
result,
|
|
256
|
+
"Cannot retrieve dataset archive information from dataset API",
|
|
257
|
+
)
|
|
258
|
+
dataset_id = UUID(result.json()["dataset_id"])
|
|
259
|
+
|
|
260
|
+
return dataset_id
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def upload_dataset_status(dataset_id: UUID) -> Any:
|
|
264
|
+
# Getting information about the Dataset
|
|
265
|
+
state = _get(f"/upload/{dataset_id}/status")
|
|
266
|
+
state = state.json()
|
|
267
|
+
return state
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
#
|
|
4
|
+
# Copyright (c) 2016-2021 AMOSSYS
|
|
5
|
+
#
|
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
# in the Software without restriction, including without limitation the rights
|
|
9
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
# furnished to do so, subject to the following conditions:
|
|
12
|
+
#
|
|
13
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
# copies or substantial portions of the Software.
|
|
15
|
+
#
|
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
# SOFTWARE.
|
|
23
|
+
#
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ConfigException(Exception):
|
|
27
|
+
pass
|