bt-cli 0.4.13__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.
- bt_cli/__init__.py +3 -0
- bt_cli/cli.py +830 -0
- bt_cli/commands/__init__.py +1 -0
- bt_cli/commands/configure.py +415 -0
- bt_cli/commands/learn.py +229 -0
- bt_cli/commands/quick.py +784 -0
- bt_cli/core/__init__.py +1 -0
- bt_cli/core/auth.py +213 -0
- bt_cli/core/client.py +313 -0
- bt_cli/core/config.py +393 -0
- bt_cli/core/config_file.py +420 -0
- bt_cli/core/csv_utils.py +91 -0
- bt_cli/core/errors.py +247 -0
- bt_cli/core/output.py +205 -0
- bt_cli/core/prompts.py +87 -0
- bt_cli/core/rest_debug.py +221 -0
- bt_cli/data/CLAUDE.md +94 -0
- bt_cli/data/__init__.py +0 -0
- bt_cli/data/skills/bt/SKILL.md +108 -0
- bt_cli/data/skills/entitle/SKILL.md +170 -0
- bt_cli/data/skills/epmw/SKILL.md +144 -0
- bt_cli/data/skills/pra/SKILL.md +150 -0
- bt_cli/data/skills/pws/SKILL.md +198 -0
- bt_cli/entitle/__init__.py +1 -0
- bt_cli/entitle/client/__init__.py +5 -0
- bt_cli/entitle/client/base.py +443 -0
- bt_cli/entitle/commands/__init__.py +24 -0
- bt_cli/entitle/commands/accounts.py +53 -0
- bt_cli/entitle/commands/applications.py +39 -0
- bt_cli/entitle/commands/auth.py +68 -0
- bt_cli/entitle/commands/bundles.py +218 -0
- bt_cli/entitle/commands/integrations.py +60 -0
- bt_cli/entitle/commands/permissions.py +70 -0
- bt_cli/entitle/commands/policies.py +97 -0
- bt_cli/entitle/commands/resources.py +131 -0
- bt_cli/entitle/commands/roles.py +74 -0
- bt_cli/entitle/commands/users.py +123 -0
- bt_cli/entitle/commands/workflows.py +187 -0
- bt_cli/entitle/models/__init__.py +31 -0
- bt_cli/entitle/models/bundle.py +28 -0
- bt_cli/entitle/models/common.py +37 -0
- bt_cli/entitle/models/integration.py +30 -0
- bt_cli/entitle/models/permission.py +27 -0
- bt_cli/entitle/models/policy.py +25 -0
- bt_cli/entitle/models/resource.py +29 -0
- bt_cli/entitle/models/role.py +28 -0
- bt_cli/entitle/models/user.py +24 -0
- bt_cli/entitle/models/workflow.py +55 -0
- bt_cli/epmw/__init__.py +1 -0
- bt_cli/epmw/client/__init__.py +5 -0
- bt_cli/epmw/client/base.py +848 -0
- bt_cli/epmw/commands/__init__.py +33 -0
- bt_cli/epmw/commands/audits.py +250 -0
- bt_cli/epmw/commands/auth.py +55 -0
- bt_cli/epmw/commands/computers.py +140 -0
- bt_cli/epmw/commands/events.py +233 -0
- bt_cli/epmw/commands/groups.py +215 -0
- bt_cli/epmw/commands/policies.py +673 -0
- bt_cli/epmw/commands/quick.py +348 -0
- bt_cli/epmw/commands/requests.py +224 -0
- bt_cli/epmw/commands/roles.py +78 -0
- bt_cli/epmw/commands/tasks.py +38 -0
- bt_cli/epmw/commands/users.py +219 -0
- bt_cli/epmw/models/__init__.py +1 -0
- bt_cli/pra/__init__.py +1 -0
- bt_cli/pra/client/__init__.py +5 -0
- bt_cli/pra/client/base.py +618 -0
- bt_cli/pra/commands/__init__.py +30 -0
- bt_cli/pra/commands/auth.py +55 -0
- bt_cli/pra/commands/import_export.py +442 -0
- bt_cli/pra/commands/jump_clients.py +139 -0
- bt_cli/pra/commands/jump_groups.py +146 -0
- bt_cli/pra/commands/jump_items.py +638 -0
- bt_cli/pra/commands/jumpoints.py +95 -0
- bt_cli/pra/commands/policies.py +197 -0
- bt_cli/pra/commands/quick.py +470 -0
- bt_cli/pra/commands/teams.py +81 -0
- bt_cli/pra/commands/users.py +87 -0
- bt_cli/pra/commands/vault.py +564 -0
- bt_cli/pra/models/__init__.py +27 -0
- bt_cli/pra/models/common.py +12 -0
- bt_cli/pra/models/jump_client.py +25 -0
- bt_cli/pra/models/jump_group.py +15 -0
- bt_cli/pra/models/jump_item.py +72 -0
- bt_cli/pra/models/jumpoint.py +19 -0
- bt_cli/pra/models/team.py +14 -0
- bt_cli/pra/models/user.py +17 -0
- bt_cli/pra/models/vault.py +45 -0
- bt_cli/pws/__init__.py +1 -0
- bt_cli/pws/client/__init__.py +5 -0
- bt_cli/pws/client/base.py +356 -0
- bt_cli/pws/client/beyondinsight.py +869 -0
- bt_cli/pws/client/passwordsafe.py +1786 -0
- bt_cli/pws/commands/__init__.py +33 -0
- bt_cli/pws/commands/accounts.py +372 -0
- bt_cli/pws/commands/assets.py +311 -0
- bt_cli/pws/commands/auth.py +166 -0
- bt_cli/pws/commands/clouds.py +221 -0
- bt_cli/pws/commands/config.py +344 -0
- bt_cli/pws/commands/credentials.py +347 -0
- bt_cli/pws/commands/databases.py +306 -0
- bt_cli/pws/commands/directories.py +199 -0
- bt_cli/pws/commands/functional.py +298 -0
- bt_cli/pws/commands/import_export.py +452 -0
- bt_cli/pws/commands/platforms.py +118 -0
- bt_cli/pws/commands/quick.py +1646 -0
- bt_cli/pws/commands/search.py +256 -0
- bt_cli/pws/commands/secrets.py +1343 -0
- bt_cli/pws/commands/systems.py +389 -0
- bt_cli/pws/commands/users.py +415 -0
- bt_cli/pws/commands/workgroups.py +166 -0
- bt_cli/pws/config.py +18 -0
- bt_cli/pws/models/__init__.py +19 -0
- bt_cli/pws/models/account.py +186 -0
- bt_cli/pws/models/asset.py +102 -0
- bt_cli/pws/models/common.py +132 -0
- bt_cli/pws/models/system.py +121 -0
- bt_cli-0.4.13.dist-info/METADATA +417 -0
- bt_cli-0.4.13.dist-info/RECORD +121 -0
- bt_cli-0.4.13.dist-info/WHEEL +4 -0
- bt_cli-0.4.13.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,638 @@
|
|
|
1
|
+
"""Jump Item commands (shell, RDP, VNC, web, tunnels)."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
import typer
|
|
7
|
+
|
|
8
|
+
from bt_cli.core.output import OutputFormat, print_table, print_json, print_error, print_success, print_api_error
|
|
9
|
+
|
|
10
|
+
app = typer.Typer(no_args_is_help=True)
|
|
11
|
+
|
|
12
|
+
# Shell Jump subcommands
|
|
13
|
+
shell_app = typer.Typer(no_args_is_help=True, help="Shell Jump items (SSH/Telnet)")
|
|
14
|
+
app.add_typer(shell_app, name="shell")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@shell_app.command("list")
|
|
18
|
+
def list_shell_jumps(
|
|
19
|
+
jump_group_id: Optional[int] = typer.Option(None, "--jump-group", "-g", help="Filter by Jump Group"),
|
|
20
|
+
jumpoint_id: Optional[int] = typer.Option(None, "--jumpoint", "-j", help="Filter by Jumpoint"),
|
|
21
|
+
output: OutputFormat = typer.Option(OutputFormat.TABLE, "--output", "-o"),
|
|
22
|
+
):
|
|
23
|
+
"""List Shell Jump items."""
|
|
24
|
+
from bt_cli.pra.client import get_client
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
client = get_client()
|
|
28
|
+
items = client.list_shell_jumps(jump_group_id=jump_group_id, jumpoint_id=jumpoint_id)
|
|
29
|
+
|
|
30
|
+
if output == OutputFormat.JSON:
|
|
31
|
+
print_json(items)
|
|
32
|
+
else:
|
|
33
|
+
columns = [
|
|
34
|
+
("ID", "id"),
|
|
35
|
+
("Name", "name"),
|
|
36
|
+
("Hostname", "hostname"),
|
|
37
|
+
("Protocol", "protocol"),
|
|
38
|
+
("Port", "port"),
|
|
39
|
+
("Username", "username"),
|
|
40
|
+
("Jumpoint", "jumpoint_id"),
|
|
41
|
+
]
|
|
42
|
+
print_table(items, columns, title="Shell Jump Items")
|
|
43
|
+
except httpx.HTTPStatusError as e:
|
|
44
|
+
print_api_error(e, "list shell jumps")
|
|
45
|
+
raise typer.Exit(1)
|
|
46
|
+
except httpx.RequestError as e:
|
|
47
|
+
print_api_error(e, "list shell jumps")
|
|
48
|
+
raise typer.Exit(1)
|
|
49
|
+
except Exception as e:
|
|
50
|
+
print_api_error(e, "list shell jumps")
|
|
51
|
+
raise typer.Exit(1)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@shell_app.command("get")
|
|
55
|
+
def get_shell_jump(
|
|
56
|
+
item_id: int = typer.Argument(..., help="Shell Jump ID"),
|
|
57
|
+
output: OutputFormat = typer.Option(OutputFormat.TABLE, "--output", "-o"),
|
|
58
|
+
):
|
|
59
|
+
"""Get Shell Jump details."""
|
|
60
|
+
from bt_cli.pra.client import get_client
|
|
61
|
+
from rich.console import Console
|
|
62
|
+
from rich.panel import Panel
|
|
63
|
+
|
|
64
|
+
console = Console()
|
|
65
|
+
|
|
66
|
+
try:
|
|
67
|
+
client = get_client()
|
|
68
|
+
item = client.get_shell_jump(item_id)
|
|
69
|
+
|
|
70
|
+
if output == OutputFormat.JSON:
|
|
71
|
+
print_json(item)
|
|
72
|
+
else:
|
|
73
|
+
name = item.get("name", "")
|
|
74
|
+
hostname = item.get("hostname", "")
|
|
75
|
+
protocol = item.get("protocol", "")
|
|
76
|
+
port = item.get("port", "")
|
|
77
|
+
username = item.get("username", "") or "-"
|
|
78
|
+
jumpoint = item.get("jumpoint_id", "")
|
|
79
|
+
jump_group = item.get("jump_group_id", "")
|
|
80
|
+
tag = item.get("tag", "") or "-"
|
|
81
|
+
comments = item.get("comments", "") or "-"
|
|
82
|
+
|
|
83
|
+
console.print(Panel(
|
|
84
|
+
f"[bold]{name}[/bold]\n\n"
|
|
85
|
+
f"[dim]Hostname:[/dim] {hostname}\n"
|
|
86
|
+
f"[dim]Protocol:[/dim] {protocol}\n"
|
|
87
|
+
f"[dim]Port:[/dim] {port}\n"
|
|
88
|
+
f"[dim]Username:[/dim] {username}\n"
|
|
89
|
+
f"[dim]Jumpoint ID:[/dim] {jumpoint}\n"
|
|
90
|
+
f"[dim]Jump Group ID:[/dim] {jump_group}\n"
|
|
91
|
+
f"[dim]Tag:[/dim] {tag}\n"
|
|
92
|
+
f"[dim]Comments:[/dim] {comments}",
|
|
93
|
+
title="Shell Jump Details",
|
|
94
|
+
subtitle=f"ID: {item.get('id', '')}",
|
|
95
|
+
))
|
|
96
|
+
except httpx.HTTPStatusError as e:
|
|
97
|
+
print_api_error(e, "get shell jump")
|
|
98
|
+
raise typer.Exit(1)
|
|
99
|
+
except httpx.RequestError as e:
|
|
100
|
+
print_api_error(e, "get shell jump")
|
|
101
|
+
raise typer.Exit(1)
|
|
102
|
+
except Exception as e:
|
|
103
|
+
print_api_error(e, "get shell jump")
|
|
104
|
+
raise typer.Exit(1)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@shell_app.command("create")
|
|
108
|
+
def create_shell_jump(
|
|
109
|
+
name: str = typer.Option(..., "--name", "-n", help="Jump item name"),
|
|
110
|
+
hostname: str = typer.Option(..., "--hostname", "-h", help="Target hostname or IP"),
|
|
111
|
+
jumpoint_id: int = typer.Option(..., "--jumpoint", "-j", help="Jumpoint ID"),
|
|
112
|
+
jump_group_id: int = typer.Option(..., "--jump-group", "-g", help="Jump Group ID"),
|
|
113
|
+
protocol: str = typer.Option("ssh", "--protocol", "-p", help="ssh or telnet"),
|
|
114
|
+
port: int = typer.Option(22, "--port", help="Port number"),
|
|
115
|
+
username: Optional[str] = typer.Option(None, "--username", "-u", help="Default username"),
|
|
116
|
+
tag: Optional[str] = typer.Option(None, "--tag", "-t", help="Tag"),
|
|
117
|
+
comments: Optional[str] = typer.Option(None, "--comments", "-c", help="Comments"),
|
|
118
|
+
output: OutputFormat = typer.Option(OutputFormat.JSON, "--output", "-o"),
|
|
119
|
+
):
|
|
120
|
+
"""Create a Shell Jump item."""
|
|
121
|
+
from bt_cli.pra.client import get_client
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
client = get_client()
|
|
125
|
+
item = client.create_shell_jump(
|
|
126
|
+
name=name,
|
|
127
|
+
hostname=hostname,
|
|
128
|
+
jumpoint_id=jumpoint_id,
|
|
129
|
+
jump_group_id=jump_group_id,
|
|
130
|
+
protocol=protocol,
|
|
131
|
+
port=port,
|
|
132
|
+
username=username,
|
|
133
|
+
tag=tag,
|
|
134
|
+
comments=comments,
|
|
135
|
+
)
|
|
136
|
+
print_success(f"Created shell jump: {item.get('name')} (ID: {item.get('id')})")
|
|
137
|
+
if output == OutputFormat.JSON:
|
|
138
|
+
print_json(item)
|
|
139
|
+
except httpx.HTTPStatusError as e:
|
|
140
|
+
print_api_error(e, "create shell jump")
|
|
141
|
+
raise typer.Exit(1)
|
|
142
|
+
except httpx.RequestError as e:
|
|
143
|
+
print_api_error(e, "create shell jump")
|
|
144
|
+
raise typer.Exit(1)
|
|
145
|
+
except Exception as e:
|
|
146
|
+
print_api_error(e, "create shell jump")
|
|
147
|
+
raise typer.Exit(1)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@shell_app.command("update")
|
|
151
|
+
def update_shell_jump(
|
|
152
|
+
item_id: int = typer.Argument(..., help="Shell Jump ID"),
|
|
153
|
+
name: Optional[str] = typer.Option(None, "--name", "-n", help="New name"),
|
|
154
|
+
hostname: Optional[str] = typer.Option(None, "--hostname", "-h", help="New hostname or IP"),
|
|
155
|
+
jump_group_id: Optional[int] = typer.Option(None, "--jump-group", "-g", help="New Jump Group ID"),
|
|
156
|
+
protocol: Optional[str] = typer.Option(None, "--protocol", "-p", help="New protocol (ssh or telnet)"),
|
|
157
|
+
port: Optional[int] = typer.Option(None, "--port", help="New port number"),
|
|
158
|
+
username: Optional[str] = typer.Option(None, "--username", "-u", help="New username"),
|
|
159
|
+
tag: Optional[str] = typer.Option(None, "--tag", "-t", help="New tag"),
|
|
160
|
+
comments: Optional[str] = typer.Option(None, "--comments", "-c", help="New comments"),
|
|
161
|
+
output: OutputFormat = typer.Option(OutputFormat.JSON, "--output", "-o"),
|
|
162
|
+
):
|
|
163
|
+
"""Update a Shell Jump item."""
|
|
164
|
+
from bt_cli.pra.client import get_client
|
|
165
|
+
|
|
166
|
+
# Check if at least one field is provided
|
|
167
|
+
if all(v is None for v in [name, hostname, jump_group_id, protocol, port, username, tag, comments]):
|
|
168
|
+
print_error("At least one field must be provided to update")
|
|
169
|
+
raise typer.Exit(1)
|
|
170
|
+
|
|
171
|
+
try:
|
|
172
|
+
client = get_client()
|
|
173
|
+
item = client.update_shell_jump(
|
|
174
|
+
item_id=item_id,
|
|
175
|
+
name=name,
|
|
176
|
+
hostname=hostname,
|
|
177
|
+
jump_group_id=jump_group_id,
|
|
178
|
+
protocol=protocol,
|
|
179
|
+
port=port,
|
|
180
|
+
username=username,
|
|
181
|
+
tag=tag,
|
|
182
|
+
comments=comments,
|
|
183
|
+
)
|
|
184
|
+
print_success(f"Updated shell jump: {item.get('name')} (ID: {item.get('id')})")
|
|
185
|
+
if output == OutputFormat.JSON:
|
|
186
|
+
print_json(item)
|
|
187
|
+
except httpx.HTTPStatusError as e:
|
|
188
|
+
print_api_error(e, "update shell jump")
|
|
189
|
+
raise typer.Exit(1)
|
|
190
|
+
except httpx.RequestError as e:
|
|
191
|
+
print_api_error(e, "update shell jump")
|
|
192
|
+
raise typer.Exit(1)
|
|
193
|
+
except Exception as e:
|
|
194
|
+
print_api_error(e, "update shell jump")
|
|
195
|
+
raise typer.Exit(1)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
@shell_app.command("delete")
|
|
199
|
+
def delete_shell_jump(
|
|
200
|
+
item_id: int = typer.Argument(..., help="Shell Jump ID"),
|
|
201
|
+
force: bool = typer.Option(False, "--force", "-f", help="Skip confirmation"),
|
|
202
|
+
):
|
|
203
|
+
"""Delete a Shell Jump item."""
|
|
204
|
+
from bt_cli.pra.client import get_client
|
|
205
|
+
|
|
206
|
+
if not force:
|
|
207
|
+
typer.confirm(f"Delete shell jump {item_id}?", abort=True)
|
|
208
|
+
|
|
209
|
+
try:
|
|
210
|
+
client = get_client()
|
|
211
|
+
client.delete_shell_jump(item_id)
|
|
212
|
+
print_success(f"Deleted shell jump {item_id}")
|
|
213
|
+
except httpx.HTTPStatusError as e:
|
|
214
|
+
print_api_error(e, "delete shell jump")
|
|
215
|
+
raise typer.Exit(1)
|
|
216
|
+
except httpx.RequestError as e:
|
|
217
|
+
print_api_error(e, "delete shell jump")
|
|
218
|
+
raise typer.Exit(1)
|
|
219
|
+
except Exception as e:
|
|
220
|
+
print_api_error(e, "delete shell jump")
|
|
221
|
+
raise typer.Exit(1)
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
# RDP Jump subcommands
|
|
225
|
+
rdp_app = typer.Typer(no_args_is_help=True, help="Remote RDP Jump items")
|
|
226
|
+
app.add_typer(rdp_app, name="rdp")
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
@rdp_app.command("list")
|
|
230
|
+
def list_rdp_jumps(
|
|
231
|
+
jump_group_id: Optional[int] = typer.Option(None, "--jump-group", "-g", help="Filter by Jump Group"),
|
|
232
|
+
jumpoint_id: Optional[int] = typer.Option(None, "--jumpoint", "-j", help="Filter by Jumpoint"),
|
|
233
|
+
output: OutputFormat = typer.Option(OutputFormat.TABLE, "--output", "-o"),
|
|
234
|
+
):
|
|
235
|
+
"""List Remote RDP Jump items."""
|
|
236
|
+
from bt_cli.pra.client import get_client
|
|
237
|
+
|
|
238
|
+
try:
|
|
239
|
+
client = get_client()
|
|
240
|
+
items = client.list_rdp_jumps(jump_group_id=jump_group_id, jumpoint_id=jumpoint_id)
|
|
241
|
+
|
|
242
|
+
if output == OutputFormat.JSON:
|
|
243
|
+
print_json(items)
|
|
244
|
+
else:
|
|
245
|
+
columns = [
|
|
246
|
+
("ID", "id"),
|
|
247
|
+
("Name", "name"),
|
|
248
|
+
("Hostname", "hostname"),
|
|
249
|
+
("Username", "rdp_username"),
|
|
250
|
+
("Domain", "domain"),
|
|
251
|
+
("Jumpoint", "jumpoint_id"),
|
|
252
|
+
]
|
|
253
|
+
print_table(items, columns, title="RDP Jump Items")
|
|
254
|
+
except httpx.HTTPStatusError as e:
|
|
255
|
+
print_api_error(e, "list RDP jumps")
|
|
256
|
+
raise typer.Exit(1)
|
|
257
|
+
except httpx.RequestError as e:
|
|
258
|
+
print_api_error(e, "list RDP jumps")
|
|
259
|
+
raise typer.Exit(1)
|
|
260
|
+
except Exception as e:
|
|
261
|
+
print_api_error(e, "list RDP jumps")
|
|
262
|
+
raise typer.Exit(1)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
@rdp_app.command("get")
|
|
266
|
+
def get_rdp_jump(
|
|
267
|
+
item_id: int = typer.Argument(..., help="RDP Jump ID"),
|
|
268
|
+
output: OutputFormat = typer.Option(OutputFormat.TABLE, "--output", "-o"),
|
|
269
|
+
):
|
|
270
|
+
"""Get Remote RDP Jump details."""
|
|
271
|
+
from bt_cli.pra.client import get_client
|
|
272
|
+
from rich.console import Console
|
|
273
|
+
from rich.panel import Panel
|
|
274
|
+
|
|
275
|
+
console = Console()
|
|
276
|
+
|
|
277
|
+
try:
|
|
278
|
+
client = get_client()
|
|
279
|
+
item = client.get_rdp_jump(item_id)
|
|
280
|
+
|
|
281
|
+
if output == OutputFormat.JSON:
|
|
282
|
+
print_json(item)
|
|
283
|
+
else:
|
|
284
|
+
name = item.get("name", "")
|
|
285
|
+
hostname = item.get("hostname", "")
|
|
286
|
+
port = item.get("rdp_port", item.get("port", ""))
|
|
287
|
+
domain = item.get("domain", "") or "-"
|
|
288
|
+
jumpoint = item.get("jumpoint_id", "")
|
|
289
|
+
jump_group = item.get("jump_group_id", "")
|
|
290
|
+
tag = item.get("tag", "") or "-"
|
|
291
|
+
comments = item.get("comments", "") or "-"
|
|
292
|
+
|
|
293
|
+
console.print(Panel(
|
|
294
|
+
f"[bold]{name}[/bold]\n\n"
|
|
295
|
+
f"[dim]Hostname:[/dim] {hostname}\n"
|
|
296
|
+
f"[dim]Port:[/dim] {port}\n"
|
|
297
|
+
f"[dim]Domain:[/dim] {domain}\n"
|
|
298
|
+
f"[dim]Jumpoint ID:[/dim] {jumpoint}\n"
|
|
299
|
+
f"[dim]Jump Group ID:[/dim] {jump_group}\n"
|
|
300
|
+
f"[dim]Tag:[/dim] {tag}\n"
|
|
301
|
+
f"[dim]Comments:[/dim] {comments}",
|
|
302
|
+
title="RDP Jump Details",
|
|
303
|
+
subtitle=f"ID: {item.get('id', '')}",
|
|
304
|
+
))
|
|
305
|
+
except httpx.HTTPStatusError as e:
|
|
306
|
+
print_api_error(e, "get RDP jump")
|
|
307
|
+
raise typer.Exit(1)
|
|
308
|
+
except httpx.RequestError as e:
|
|
309
|
+
print_api_error(e, "get RDP jump")
|
|
310
|
+
raise typer.Exit(1)
|
|
311
|
+
except Exception as e:
|
|
312
|
+
print_api_error(e, "get RDP jump")
|
|
313
|
+
raise typer.Exit(1)
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
@rdp_app.command("create")
|
|
317
|
+
def create_rdp_jump(
|
|
318
|
+
name: str = typer.Option(..., "--name", "-n", help="Jump item name"),
|
|
319
|
+
hostname: str = typer.Option(..., "--hostname", "-h", help="Target hostname or IP"),
|
|
320
|
+
jumpoint_id: int = typer.Option(..., "--jumpoint", "-j", help="Jumpoint ID"),
|
|
321
|
+
jump_group_id: int = typer.Option(..., "--jump-group", "-g", help="Jump Group ID"),
|
|
322
|
+
rdp_username: Optional[str] = typer.Option(None, "--username", "-u", help="RDP username"),
|
|
323
|
+
domain: Optional[str] = typer.Option(None, "--domain", "-d", help="Domain"),
|
|
324
|
+
tag: Optional[str] = typer.Option(None, "--tag", "-t", help="Tag"),
|
|
325
|
+
comments: Optional[str] = typer.Option(None, "--comments", "-c", help="Comments"),
|
|
326
|
+
output: OutputFormat = typer.Option(OutputFormat.JSON, "--output", "-o"),
|
|
327
|
+
):
|
|
328
|
+
"""Create a Remote RDP Jump item."""
|
|
329
|
+
from bt_cli.pra.client import get_client
|
|
330
|
+
|
|
331
|
+
try:
|
|
332
|
+
client = get_client()
|
|
333
|
+
item = client.create_rdp_jump(
|
|
334
|
+
name=name,
|
|
335
|
+
hostname=hostname,
|
|
336
|
+
jumpoint_id=jumpoint_id,
|
|
337
|
+
jump_group_id=jump_group_id,
|
|
338
|
+
rdp_username=rdp_username,
|
|
339
|
+
domain=domain,
|
|
340
|
+
tag=tag,
|
|
341
|
+
comments=comments,
|
|
342
|
+
)
|
|
343
|
+
print_success(f"Created RDP jump: {item.get('name')} (ID: {item.get('id')})")
|
|
344
|
+
if output == OutputFormat.JSON:
|
|
345
|
+
print_json(item)
|
|
346
|
+
except httpx.HTTPStatusError as e:
|
|
347
|
+
print_api_error(e, "create RDP jump")
|
|
348
|
+
raise typer.Exit(1)
|
|
349
|
+
except httpx.RequestError as e:
|
|
350
|
+
print_api_error(e, "create RDP jump")
|
|
351
|
+
raise typer.Exit(1)
|
|
352
|
+
except Exception as e:
|
|
353
|
+
print_api_error(e, "create RDP jump")
|
|
354
|
+
raise typer.Exit(1)
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
@rdp_app.command("delete")
|
|
358
|
+
def delete_rdp_jump(
|
|
359
|
+
item_id: int = typer.Argument(..., help="RDP Jump ID"),
|
|
360
|
+
force: bool = typer.Option(False, "--force", "-f", help="Skip confirmation"),
|
|
361
|
+
):
|
|
362
|
+
"""Delete a Remote RDP Jump item."""
|
|
363
|
+
from bt_cli.pra.client import get_client
|
|
364
|
+
|
|
365
|
+
if not force:
|
|
366
|
+
typer.confirm(f"Delete RDP jump {item_id}?", abort=True)
|
|
367
|
+
|
|
368
|
+
try:
|
|
369
|
+
client = get_client()
|
|
370
|
+
client.delete_rdp_jump(item_id)
|
|
371
|
+
print_success(f"Deleted RDP jump {item_id}")
|
|
372
|
+
except httpx.HTTPStatusError as e:
|
|
373
|
+
print_api_error(e, "delete RDP jump")
|
|
374
|
+
raise typer.Exit(1)
|
|
375
|
+
except httpx.RequestError as e:
|
|
376
|
+
print_api_error(e, "delete RDP jump")
|
|
377
|
+
raise typer.Exit(1)
|
|
378
|
+
except Exception as e:
|
|
379
|
+
print_api_error(e, "delete RDP jump")
|
|
380
|
+
raise typer.Exit(1)
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
# VNC Jump subcommands
|
|
384
|
+
vnc_app = typer.Typer(no_args_is_help=True, help="Remote VNC Jump items")
|
|
385
|
+
app.add_typer(vnc_app, name="vnc")
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
@vnc_app.command("list")
|
|
389
|
+
def list_vnc_jumps(
|
|
390
|
+
jump_group_id: Optional[int] = typer.Option(None, "--jump-group", "-g", help="Filter by Jump Group"),
|
|
391
|
+
output: OutputFormat = typer.Option(OutputFormat.TABLE, "--output", "-o"),
|
|
392
|
+
):
|
|
393
|
+
"""List Remote VNC Jump items."""
|
|
394
|
+
from bt_cli.pra.client import get_client
|
|
395
|
+
|
|
396
|
+
try:
|
|
397
|
+
client = get_client()
|
|
398
|
+
items = client.list_vnc_jumps(jump_group_id=jump_group_id)
|
|
399
|
+
|
|
400
|
+
if output == OutputFormat.JSON:
|
|
401
|
+
print_json(items)
|
|
402
|
+
else:
|
|
403
|
+
columns = [
|
|
404
|
+
("ID", "id"),
|
|
405
|
+
("Name", "name"),
|
|
406
|
+
("Hostname", "hostname"),
|
|
407
|
+
("Port", "port"),
|
|
408
|
+
("Jumpoint", "jumpoint_id"),
|
|
409
|
+
]
|
|
410
|
+
print_table(items, columns, title="VNC Jump Items")
|
|
411
|
+
except httpx.HTTPStatusError as e:
|
|
412
|
+
print_api_error(e, "list VNC jumps")
|
|
413
|
+
raise typer.Exit(1)
|
|
414
|
+
except httpx.RequestError as e:
|
|
415
|
+
print_api_error(e, "list VNC jumps")
|
|
416
|
+
raise typer.Exit(1)
|
|
417
|
+
except Exception as e:
|
|
418
|
+
print_api_error(e, "list VNC jumps")
|
|
419
|
+
raise typer.Exit(1)
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
# Web Jump subcommands
|
|
423
|
+
web_app = typer.Typer(no_args_is_help=True, help="Web Jump items")
|
|
424
|
+
app.add_typer(web_app, name="web")
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
@web_app.command("list")
|
|
428
|
+
def list_web_jumps(
|
|
429
|
+
jump_group_id: Optional[int] = typer.Option(None, "--jump-group", "-g", help="Filter by Jump Group"),
|
|
430
|
+
output: OutputFormat = typer.Option(OutputFormat.TABLE, "--output", "-o"),
|
|
431
|
+
):
|
|
432
|
+
"""List Web Jump items."""
|
|
433
|
+
from bt_cli.pra.client import get_client
|
|
434
|
+
|
|
435
|
+
try:
|
|
436
|
+
client = get_client()
|
|
437
|
+
items = client.list_web_jumps(jump_group_id=jump_group_id)
|
|
438
|
+
|
|
439
|
+
if output == OutputFormat.JSON:
|
|
440
|
+
print_json(items)
|
|
441
|
+
else:
|
|
442
|
+
columns = [
|
|
443
|
+
("ID", "id"),
|
|
444
|
+
("Name", "name"),
|
|
445
|
+
("URL", "url"),
|
|
446
|
+
("Username", "username"),
|
|
447
|
+
("Jumpoint", "jumpoint_id"),
|
|
448
|
+
]
|
|
449
|
+
print_table(items, columns, title="Web Jump Items")
|
|
450
|
+
except httpx.HTTPStatusError as e:
|
|
451
|
+
print_api_error(e, "list web jumps")
|
|
452
|
+
raise typer.Exit(1)
|
|
453
|
+
except httpx.RequestError as e:
|
|
454
|
+
print_api_error(e, "list web jumps")
|
|
455
|
+
raise typer.Exit(1)
|
|
456
|
+
except Exception as e:
|
|
457
|
+
print_api_error(e, "list web jumps")
|
|
458
|
+
raise typer.Exit(1)
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
# Protocol Tunnel subcommands (TCP, MSSQL, PostgreSQL, MySQL, K8s)
|
|
462
|
+
tunnel_app = typer.Typer(no_args_is_help=True, help="Protocol Tunnel Jump items (TCP, MSSQL, PostgreSQL, MySQL, K8s)")
|
|
463
|
+
app.add_typer(tunnel_app, name="tunnel")
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
@tunnel_app.command("list")
|
|
467
|
+
def list_protocol_tunnels(
|
|
468
|
+
jump_group_id: Optional[int] = typer.Option(None, "--jump-group", "-g", help="Filter by Jump Group"),
|
|
469
|
+
tunnel_type: Optional[str] = typer.Option(None, "--type", "-t", help="Filter by type: tcp, mssql, psql, mysql, k8s"),
|
|
470
|
+
output: OutputFormat = typer.Option(OutputFormat.TABLE, "--output", "-o"),
|
|
471
|
+
):
|
|
472
|
+
"""List Protocol Tunnel Jump items."""
|
|
473
|
+
from bt_cli.pra.client import get_client
|
|
474
|
+
|
|
475
|
+
try:
|
|
476
|
+
client = get_client()
|
|
477
|
+
items = client.list_protocol_tunnels(jump_group_id=jump_group_id, tunnel_type=tunnel_type)
|
|
478
|
+
|
|
479
|
+
if output == OutputFormat.JSON:
|
|
480
|
+
print_json(items)
|
|
481
|
+
else:
|
|
482
|
+
columns = [
|
|
483
|
+
("ID", "id"),
|
|
484
|
+
("Name", "name"),
|
|
485
|
+
("Hostname", "hostname"),
|
|
486
|
+
("Type", "tunnel_type"),
|
|
487
|
+
("URL", "url"),
|
|
488
|
+
("Jumpoint", "jumpoint_id"),
|
|
489
|
+
]
|
|
490
|
+
print_table(items, columns, title="Protocol Tunnel Jump Items")
|
|
491
|
+
except httpx.HTTPStatusError as e:
|
|
492
|
+
print_api_error(e, "list protocol tunnels")
|
|
493
|
+
raise typer.Exit(1)
|
|
494
|
+
except httpx.RequestError as e:
|
|
495
|
+
print_api_error(e, "list protocol tunnels")
|
|
496
|
+
raise typer.Exit(1)
|
|
497
|
+
except Exception as e:
|
|
498
|
+
print_api_error(e, "list protocol tunnels")
|
|
499
|
+
raise typer.Exit(1)
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
@tunnel_app.command("get")
|
|
503
|
+
def get_protocol_tunnel(
|
|
504
|
+
item_id: int = typer.Argument(..., help="Protocol Tunnel ID"),
|
|
505
|
+
output: OutputFormat = typer.Option(OutputFormat.TABLE, "--output", "-o"),
|
|
506
|
+
):
|
|
507
|
+
"""Get Protocol Tunnel details."""
|
|
508
|
+
from bt_cli.pra.client import get_client
|
|
509
|
+
from rich.console import Console
|
|
510
|
+
from rich.panel import Panel
|
|
511
|
+
|
|
512
|
+
console = Console()
|
|
513
|
+
|
|
514
|
+
try:
|
|
515
|
+
client = get_client()
|
|
516
|
+
item = client.get_protocol_tunnel(item_id)
|
|
517
|
+
|
|
518
|
+
if output == OutputFormat.JSON:
|
|
519
|
+
print_json(item)
|
|
520
|
+
else:
|
|
521
|
+
name = item.get("name", "")
|
|
522
|
+
hostname = item.get("hostname", "")
|
|
523
|
+
tunnel_type = item.get("tunnel_type", "")
|
|
524
|
+
username = item.get("username", "") or "-"
|
|
525
|
+
database = item.get("database", "") or "-"
|
|
526
|
+
url = item.get("url", "") or "-"
|
|
527
|
+
jumpoint = item.get("jumpoint_id", "")
|
|
528
|
+
jump_group = item.get("jump_group_id", "")
|
|
529
|
+
tag = item.get("tag", "") or "-"
|
|
530
|
+
comments = item.get("comments", "") or "-"
|
|
531
|
+
|
|
532
|
+
content = (
|
|
533
|
+
f"[bold]{name}[/bold]\n\n"
|
|
534
|
+
f"[dim]Hostname:[/dim] {hostname}\n"
|
|
535
|
+
f"[dim]Type:[/dim] {tunnel_type}\n"
|
|
536
|
+
)
|
|
537
|
+
# Show database fields for database tunnel types
|
|
538
|
+
if tunnel_type in ("mssql", "psql", "mysql"):
|
|
539
|
+
content += f"[dim]Username:[/dim] {username}\n"
|
|
540
|
+
content += f"[dim]Database:[/dim] {database}\n"
|
|
541
|
+
if tunnel_type == "k8s":
|
|
542
|
+
content += f"[dim]URL:[/dim] {url}\n"
|
|
543
|
+
content += (
|
|
544
|
+
f"[dim]Jumpoint ID:[/dim] {jumpoint}\n"
|
|
545
|
+
f"[dim]Jump Group ID:[/dim] {jump_group}\n"
|
|
546
|
+
f"[dim]Tag:[/dim] {tag}\n"
|
|
547
|
+
f"[dim]Comments:[/dim] {comments}"
|
|
548
|
+
)
|
|
549
|
+
|
|
550
|
+
console.print(Panel(
|
|
551
|
+
content,
|
|
552
|
+
title="Protocol Tunnel Details",
|
|
553
|
+
subtitle=f"ID: {item.get('id', '')}",
|
|
554
|
+
))
|
|
555
|
+
except httpx.HTTPStatusError as e:
|
|
556
|
+
print_api_error(e, "get protocol tunnel")
|
|
557
|
+
raise typer.Exit(1)
|
|
558
|
+
except httpx.RequestError as e:
|
|
559
|
+
print_api_error(e, "get protocol tunnel")
|
|
560
|
+
raise typer.Exit(1)
|
|
561
|
+
except Exception as e:
|
|
562
|
+
print_api_error(e, "get protocol tunnel")
|
|
563
|
+
raise typer.Exit(1)
|
|
564
|
+
|
|
565
|
+
|
|
566
|
+
@tunnel_app.command("create")
|
|
567
|
+
def create_protocol_tunnel(
|
|
568
|
+
name: str = typer.Option(..., "--name", "-n", help="Jump item name"),
|
|
569
|
+
hostname: str = typer.Option(..., "--hostname", "-h", help="Target hostname (auto-set for k8s)"),
|
|
570
|
+
jumpoint_id: int = typer.Option(..., "--jumpoint", "-j", help="Jumpoint ID (Linux for k8s)"),
|
|
571
|
+
jump_group_id: int = typer.Option(..., "--jump-group", "-g", help="Jump Group ID"),
|
|
572
|
+
tunnel_type: str = typer.Option("tcp", "--type", "-t", help="tcp, mssql, psql, mysql, or k8s"),
|
|
573
|
+
tunnel_definitions: Optional[str] = typer.Option(None, "--ports", help="TCP port pairs (e.g., '22;24;26;28')"),
|
|
574
|
+
username: Optional[str] = typer.Option(None, "--username", "-u", help="Database username (for mssql, psql, mysql)"),
|
|
575
|
+
database: Optional[str] = typer.Option(None, "--database", "-d", help="Database name (for mssql, psql, mysql)"),
|
|
576
|
+
url: Optional[str] = typer.Option(None, "--url", help="K8s API URL (required for k8s)"),
|
|
577
|
+
ca_certificates: Optional[str] = typer.Option(None, "--ca-cert", help="K8s CA cert (required for k8s)"),
|
|
578
|
+
tag: Optional[str] = typer.Option(None, "--tag", help="Tag"),
|
|
579
|
+
comments: Optional[str] = typer.Option(None, "--comments", "-c", help="Comments"),
|
|
580
|
+
output: OutputFormat = typer.Option(OutputFormat.JSON, "--output", "-o"),
|
|
581
|
+
):
|
|
582
|
+
"""Create a Protocol Tunnel Jump item (TCP, MSSQL, PostgreSQL, MySQL, or K8s)."""
|
|
583
|
+
from bt_cli.pra.client import get_client
|
|
584
|
+
|
|
585
|
+
try:
|
|
586
|
+
client = get_client()
|
|
587
|
+
item = client.create_protocol_tunnel(
|
|
588
|
+
name=name,
|
|
589
|
+
hostname=hostname,
|
|
590
|
+
jumpoint_id=jumpoint_id,
|
|
591
|
+
jump_group_id=jump_group_id,
|
|
592
|
+
tunnel_type=tunnel_type,
|
|
593
|
+
tunnel_definitions=tunnel_definitions,
|
|
594
|
+
username=username,
|
|
595
|
+
database=database,
|
|
596
|
+
url=url,
|
|
597
|
+
ca_certificates=ca_certificates,
|
|
598
|
+
tag=tag,
|
|
599
|
+
comments=comments,
|
|
600
|
+
)
|
|
601
|
+
print_success(f"Created protocol tunnel: {item.get('name')} (ID: {item.get('id')})")
|
|
602
|
+
if output == OutputFormat.JSON:
|
|
603
|
+
print_json(item)
|
|
604
|
+
except httpx.HTTPStatusError as e:
|
|
605
|
+
print_api_error(e, "create protocol tunnel")
|
|
606
|
+
raise typer.Exit(1)
|
|
607
|
+
except httpx.RequestError as e:
|
|
608
|
+
print_api_error(e, "create protocol tunnel")
|
|
609
|
+
raise typer.Exit(1)
|
|
610
|
+
except Exception as e:
|
|
611
|
+
print_api_error(e, "create protocol tunnel")
|
|
612
|
+
raise typer.Exit(1)
|
|
613
|
+
|
|
614
|
+
|
|
615
|
+
@tunnel_app.command("delete")
|
|
616
|
+
def delete_protocol_tunnel(
|
|
617
|
+
item_id: int = typer.Argument(..., help="Protocol Tunnel ID"),
|
|
618
|
+
force: bool = typer.Option(False, "--force", "-f", help="Skip confirmation"),
|
|
619
|
+
):
|
|
620
|
+
"""Delete a Protocol Tunnel Jump item."""
|
|
621
|
+
from bt_cli.pra.client import get_client
|
|
622
|
+
|
|
623
|
+
if not force:
|
|
624
|
+
typer.confirm(f"Delete protocol tunnel {item_id}?", abort=True)
|
|
625
|
+
|
|
626
|
+
try:
|
|
627
|
+
client = get_client()
|
|
628
|
+
client.delete_protocol_tunnel(item_id)
|
|
629
|
+
print_success(f"Deleted protocol tunnel {item_id}")
|
|
630
|
+
except httpx.HTTPStatusError as e:
|
|
631
|
+
print_api_error(e, "delete protocol tunnel")
|
|
632
|
+
raise typer.Exit(1)
|
|
633
|
+
except httpx.RequestError as e:
|
|
634
|
+
print_api_error(e, "delete protocol tunnel")
|
|
635
|
+
raise typer.Exit(1)
|
|
636
|
+
except Exception as e:
|
|
637
|
+
print_api_error(e, "delete protocol tunnel")
|
|
638
|
+
raise typer.Exit(1)
|