bt-cli 0.4.7__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 +88 -0
- bt_cli/data/__init__.py +0 -0
- bt_cli/data/skills/bt/SKILL.md +98 -0
- bt_cli/data/skills/entitle/SKILL.md +159 -0
- bt_cli/data/skills/epmw/SKILL.md +145 -0
- bt_cli/data/skills/pra/SKILL.md +149 -0
- bt_cli/data/skills/pws/SKILL.md +197 -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.7.dist-info/METADATA +172 -0
- bt_cli-0.4.7.dist-info/RECORD +121 -0
- bt_cli-0.4.7.dist-info/WHEEL +4 -0
- bt_cli-0.4.7.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
"""Cross-entity search for Password Safe."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
import httpx
|
|
7
|
+
import typer
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.table import Table
|
|
10
|
+
|
|
11
|
+
from ...core.output import print_api_error
|
|
12
|
+
from ..client.base import get_client
|
|
13
|
+
|
|
14
|
+
app = typer.Typer(no_args_is_help=True, help="Search across all PWS entities")
|
|
15
|
+
console = Console()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@app.callback(invoke_without_command=True)
|
|
19
|
+
def search(
|
|
20
|
+
ctx: typer.Context,
|
|
21
|
+
query: str = typer.Argument(..., help="Search term"),
|
|
22
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table or json"),
|
|
23
|
+
) -> None:
|
|
24
|
+
"""Search across all Password Safe entities.
|
|
25
|
+
|
|
26
|
+
Searches functional accounts, managed accounts, managed systems,
|
|
27
|
+
assets, and secrets in one command.
|
|
28
|
+
|
|
29
|
+
Examples:
|
|
30
|
+
bt pws search functional001
|
|
31
|
+
bt pws search axion
|
|
32
|
+
bt pws search root
|
|
33
|
+
bt pws search 10.0.12 # Search by IP
|
|
34
|
+
"""
|
|
35
|
+
if ctx.invoked_subcommand is not None:
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
query_lower = query.lower()
|
|
39
|
+
results = {
|
|
40
|
+
"query": query,
|
|
41
|
+
"functional_accounts": [],
|
|
42
|
+
"managed_accounts": [],
|
|
43
|
+
"managed_systems": [],
|
|
44
|
+
"assets": [],
|
|
45
|
+
"secrets": [],
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
try:
|
|
49
|
+
with get_client() as client:
|
|
50
|
+
client.authenticate()
|
|
51
|
+
|
|
52
|
+
console.print(f"[dim]Searching PWS for '{query}'...[/dim]\n")
|
|
53
|
+
|
|
54
|
+
# Search functional accounts
|
|
55
|
+
try:
|
|
56
|
+
functional = client.list_functional_accounts()
|
|
57
|
+
results["functional_accounts"] = [
|
|
58
|
+
f for f in functional
|
|
59
|
+
if query_lower in str(f.get("AccountName", "")).lower()
|
|
60
|
+
or query_lower in str(f.get("DisplayName", "")).lower()
|
|
61
|
+
or query_lower in str(f.get("Description", "")).lower()
|
|
62
|
+
][:10]
|
|
63
|
+
except Exception as e:
|
|
64
|
+
console.print(f"[dim]Functional accounts: {e}[/dim]")
|
|
65
|
+
|
|
66
|
+
# Search managed accounts
|
|
67
|
+
try:
|
|
68
|
+
# Use API search if available
|
|
69
|
+
accounts = client.list_managed_accounts(account_name=query, limit=50)
|
|
70
|
+
if not accounts:
|
|
71
|
+
# Fall back to listing and filtering
|
|
72
|
+
all_accounts = client.list_managed_accounts(limit=200)
|
|
73
|
+
accounts = [
|
|
74
|
+
a for a in all_accounts
|
|
75
|
+
if query_lower in str(a.get("AccountName", "")).lower()
|
|
76
|
+
or query_lower in str(a.get("SystemName", "")).lower()
|
|
77
|
+
]
|
|
78
|
+
results["managed_accounts"] = accounts[:10]
|
|
79
|
+
except Exception as e:
|
|
80
|
+
console.print(f"[dim]Managed accounts: {e}[/dim]")
|
|
81
|
+
|
|
82
|
+
# Search managed systems
|
|
83
|
+
try:
|
|
84
|
+
systems = client.list_managed_systems(search=query, limit=50)
|
|
85
|
+
if not systems:
|
|
86
|
+
all_systems = client.list_managed_systems(limit=200)
|
|
87
|
+
systems = [
|
|
88
|
+
s for s in all_systems
|
|
89
|
+
if query_lower in str(s.get("SystemName", "")).lower()
|
|
90
|
+
or query_lower in str(s.get("IPAddress", "")).lower()
|
|
91
|
+
or query_lower in str(s.get("Description", "")).lower()
|
|
92
|
+
]
|
|
93
|
+
results["managed_systems"] = systems[:10]
|
|
94
|
+
except Exception as e:
|
|
95
|
+
console.print(f"[dim]Managed systems: {e}[/dim]")
|
|
96
|
+
|
|
97
|
+
# Search assets
|
|
98
|
+
try:
|
|
99
|
+
assets = client.search_assets(query, limit=50)
|
|
100
|
+
if not assets:
|
|
101
|
+
all_assets = client.list_assets(limit=200)
|
|
102
|
+
assets = [
|
|
103
|
+
a for a in all_assets
|
|
104
|
+
if query_lower in str(a.get("AssetName", "")).lower()
|
|
105
|
+
or query_lower in str(a.get("IPAddress", "")).lower()
|
|
106
|
+
or query_lower in str(a.get("DnsName", "")).lower()
|
|
107
|
+
]
|
|
108
|
+
results["assets"] = assets[:10]
|
|
109
|
+
except Exception as e:
|
|
110
|
+
console.print(f"[dim]Assets: {e}[/dim]")
|
|
111
|
+
|
|
112
|
+
# Search secrets (if accessible)
|
|
113
|
+
try:
|
|
114
|
+
# Get all safes and search folders/secrets
|
|
115
|
+
safes = client.list_safes()
|
|
116
|
+
secrets_found = []
|
|
117
|
+
for safe in safes[:5]: # Limit to first 5 safes
|
|
118
|
+
safe_id = safe.get("Id") or safe.get("id")
|
|
119
|
+
try:
|
|
120
|
+
folders = client.list_secrets_folders(safe_id)
|
|
121
|
+
for folder in folders:
|
|
122
|
+
folder_id = folder.get("Id") or folder.get("id")
|
|
123
|
+
try:
|
|
124
|
+
secrets = client.list_secrets(folder_id)
|
|
125
|
+
for secret in secrets:
|
|
126
|
+
title = secret.get("Title", "")
|
|
127
|
+
if query_lower in title.lower():
|
|
128
|
+
secret["_safe_name"] = safe.get("Name", "")
|
|
129
|
+
secret["_folder_name"] = folder.get("Name", "")
|
|
130
|
+
secrets_found.append(secret)
|
|
131
|
+
except Exception:
|
|
132
|
+
pass
|
|
133
|
+
except Exception:
|
|
134
|
+
pass
|
|
135
|
+
results["secrets"] = secrets_found[:10]
|
|
136
|
+
except Exception as e:
|
|
137
|
+
console.print(f"[dim]Secrets: {e}[/dim]")
|
|
138
|
+
|
|
139
|
+
# Output
|
|
140
|
+
if output == "json":
|
|
141
|
+
console.print_json(json.dumps(results, default=str))
|
|
142
|
+
return
|
|
143
|
+
|
|
144
|
+
total_found = 0
|
|
145
|
+
|
|
146
|
+
# Functional accounts
|
|
147
|
+
if results["functional_accounts"]:
|
|
148
|
+
total_found += len(results["functional_accounts"])
|
|
149
|
+
table = Table(title="Functional Accounts")
|
|
150
|
+
table.add_column("ID", style="cyan")
|
|
151
|
+
table.add_column("Name", style="green")
|
|
152
|
+
table.add_column("Display Name", style="yellow")
|
|
153
|
+
table.add_column("Platform", style="blue")
|
|
154
|
+
table.add_column("Elevation")
|
|
155
|
+
|
|
156
|
+
for f in results["functional_accounts"]:
|
|
157
|
+
table.add_row(
|
|
158
|
+
str(f.get("FunctionalAccountID", "")),
|
|
159
|
+
f.get("AccountName", ""),
|
|
160
|
+
f.get("DisplayName", "") or "-",
|
|
161
|
+
str(f.get("PlatformID", "")),
|
|
162
|
+
f.get("ElevationCommand", "") or "-",
|
|
163
|
+
)
|
|
164
|
+
console.print(table)
|
|
165
|
+
console.print()
|
|
166
|
+
|
|
167
|
+
# Managed accounts
|
|
168
|
+
if results["managed_accounts"]:
|
|
169
|
+
total_found += len(results["managed_accounts"])
|
|
170
|
+
table = Table(title="Managed Accounts")
|
|
171
|
+
table.add_column("ID", style="cyan")
|
|
172
|
+
table.add_column("Account", style="green")
|
|
173
|
+
table.add_column("System", style="yellow")
|
|
174
|
+
table.add_column("Domain")
|
|
175
|
+
|
|
176
|
+
for a in results["managed_accounts"]:
|
|
177
|
+
acc_id = a.get("ManagedAccountID", a.get("AccountId", ""))
|
|
178
|
+
table.add_row(
|
|
179
|
+
str(acc_id),
|
|
180
|
+
a.get("AccountName", ""),
|
|
181
|
+
a.get("SystemName", "") or "-",
|
|
182
|
+
a.get("DomainName", "") or "-",
|
|
183
|
+
)
|
|
184
|
+
console.print(table)
|
|
185
|
+
console.print()
|
|
186
|
+
|
|
187
|
+
# Managed systems
|
|
188
|
+
if results["managed_systems"]:
|
|
189
|
+
total_found += len(results["managed_systems"])
|
|
190
|
+
table = Table(title="Managed Systems")
|
|
191
|
+
table.add_column("ID", style="cyan")
|
|
192
|
+
table.add_column("Name", style="green")
|
|
193
|
+
table.add_column("IP", style="yellow")
|
|
194
|
+
table.add_column("Platform")
|
|
195
|
+
|
|
196
|
+
for s in results["managed_systems"]:
|
|
197
|
+
table.add_row(
|
|
198
|
+
str(s.get("ManagedSystemID", "")),
|
|
199
|
+
s.get("SystemName", ""),
|
|
200
|
+
s.get("IPAddress", "") or "-",
|
|
201
|
+
s.get("PlatformName", str(s.get("PlatformID", ""))),
|
|
202
|
+
)
|
|
203
|
+
console.print(table)
|
|
204
|
+
console.print()
|
|
205
|
+
|
|
206
|
+
# Assets
|
|
207
|
+
if results["assets"]:
|
|
208
|
+
total_found += len(results["assets"])
|
|
209
|
+
table = Table(title="Assets")
|
|
210
|
+
table.add_column("ID", style="cyan")
|
|
211
|
+
table.add_column("Name", style="green")
|
|
212
|
+
table.add_column("IP", style="yellow")
|
|
213
|
+
table.add_column("DNS")
|
|
214
|
+
|
|
215
|
+
for a in results["assets"]:
|
|
216
|
+
table.add_row(
|
|
217
|
+
str(a.get("AssetID", "")),
|
|
218
|
+
a.get("AssetName", "") or "-",
|
|
219
|
+
a.get("IPAddress", "") or "-",
|
|
220
|
+
a.get("DnsName", "") or "-",
|
|
221
|
+
)
|
|
222
|
+
console.print(table)
|
|
223
|
+
console.print()
|
|
224
|
+
|
|
225
|
+
# Secrets
|
|
226
|
+
if results["secrets"]:
|
|
227
|
+
total_found += len(results["secrets"])
|
|
228
|
+
table = Table(title="Secrets")
|
|
229
|
+
table.add_column("Safe", style="cyan")
|
|
230
|
+
table.add_column("Folder", style="yellow")
|
|
231
|
+
table.add_column("Title", style="green")
|
|
232
|
+
|
|
233
|
+
for s in results["secrets"]:
|
|
234
|
+
table.add_row(
|
|
235
|
+
s.get("_safe_name", ""),
|
|
236
|
+
s.get("_folder_name", ""),
|
|
237
|
+
s.get("Title", ""),
|
|
238
|
+
)
|
|
239
|
+
console.print(table)
|
|
240
|
+
console.print()
|
|
241
|
+
|
|
242
|
+
# Summary
|
|
243
|
+
if total_found == 0:
|
|
244
|
+
console.print(f"[yellow]No results found for '{query}'[/yellow]")
|
|
245
|
+
else:
|
|
246
|
+
console.print(f"[dim]Found {total_found} result(s) for '{query}'[/dim]")
|
|
247
|
+
|
|
248
|
+
except httpx.HTTPStatusError as e:
|
|
249
|
+
print_api_error(e, "search")
|
|
250
|
+
raise typer.Exit(1)
|
|
251
|
+
except httpx.RequestError as e:
|
|
252
|
+
print_api_error(e, "search")
|
|
253
|
+
raise typer.Exit(1)
|
|
254
|
+
except Exception as e:
|
|
255
|
+
print_api_error(e, "search")
|
|
256
|
+
raise typer.Exit(1)
|