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.
Files changed (121) hide show
  1. bt_cli/__init__.py +3 -0
  2. bt_cli/cli.py +830 -0
  3. bt_cli/commands/__init__.py +1 -0
  4. bt_cli/commands/configure.py +415 -0
  5. bt_cli/commands/learn.py +229 -0
  6. bt_cli/commands/quick.py +784 -0
  7. bt_cli/core/__init__.py +1 -0
  8. bt_cli/core/auth.py +213 -0
  9. bt_cli/core/client.py +313 -0
  10. bt_cli/core/config.py +393 -0
  11. bt_cli/core/config_file.py +420 -0
  12. bt_cli/core/csv_utils.py +91 -0
  13. bt_cli/core/errors.py +247 -0
  14. bt_cli/core/output.py +205 -0
  15. bt_cli/core/prompts.py +87 -0
  16. bt_cli/core/rest_debug.py +221 -0
  17. bt_cli/data/CLAUDE.md +94 -0
  18. bt_cli/data/__init__.py +0 -0
  19. bt_cli/data/skills/bt/SKILL.md +108 -0
  20. bt_cli/data/skills/entitle/SKILL.md +170 -0
  21. bt_cli/data/skills/epmw/SKILL.md +144 -0
  22. bt_cli/data/skills/pra/SKILL.md +150 -0
  23. bt_cli/data/skills/pws/SKILL.md +198 -0
  24. bt_cli/entitle/__init__.py +1 -0
  25. bt_cli/entitle/client/__init__.py +5 -0
  26. bt_cli/entitle/client/base.py +443 -0
  27. bt_cli/entitle/commands/__init__.py +24 -0
  28. bt_cli/entitle/commands/accounts.py +53 -0
  29. bt_cli/entitle/commands/applications.py +39 -0
  30. bt_cli/entitle/commands/auth.py +68 -0
  31. bt_cli/entitle/commands/bundles.py +218 -0
  32. bt_cli/entitle/commands/integrations.py +60 -0
  33. bt_cli/entitle/commands/permissions.py +70 -0
  34. bt_cli/entitle/commands/policies.py +97 -0
  35. bt_cli/entitle/commands/resources.py +131 -0
  36. bt_cli/entitle/commands/roles.py +74 -0
  37. bt_cli/entitle/commands/users.py +123 -0
  38. bt_cli/entitle/commands/workflows.py +187 -0
  39. bt_cli/entitle/models/__init__.py +31 -0
  40. bt_cli/entitle/models/bundle.py +28 -0
  41. bt_cli/entitle/models/common.py +37 -0
  42. bt_cli/entitle/models/integration.py +30 -0
  43. bt_cli/entitle/models/permission.py +27 -0
  44. bt_cli/entitle/models/policy.py +25 -0
  45. bt_cli/entitle/models/resource.py +29 -0
  46. bt_cli/entitle/models/role.py +28 -0
  47. bt_cli/entitle/models/user.py +24 -0
  48. bt_cli/entitle/models/workflow.py +55 -0
  49. bt_cli/epmw/__init__.py +1 -0
  50. bt_cli/epmw/client/__init__.py +5 -0
  51. bt_cli/epmw/client/base.py +848 -0
  52. bt_cli/epmw/commands/__init__.py +33 -0
  53. bt_cli/epmw/commands/audits.py +250 -0
  54. bt_cli/epmw/commands/auth.py +55 -0
  55. bt_cli/epmw/commands/computers.py +140 -0
  56. bt_cli/epmw/commands/events.py +233 -0
  57. bt_cli/epmw/commands/groups.py +215 -0
  58. bt_cli/epmw/commands/policies.py +673 -0
  59. bt_cli/epmw/commands/quick.py +348 -0
  60. bt_cli/epmw/commands/requests.py +224 -0
  61. bt_cli/epmw/commands/roles.py +78 -0
  62. bt_cli/epmw/commands/tasks.py +38 -0
  63. bt_cli/epmw/commands/users.py +219 -0
  64. bt_cli/epmw/models/__init__.py +1 -0
  65. bt_cli/pra/__init__.py +1 -0
  66. bt_cli/pra/client/__init__.py +5 -0
  67. bt_cli/pra/client/base.py +618 -0
  68. bt_cli/pra/commands/__init__.py +30 -0
  69. bt_cli/pra/commands/auth.py +55 -0
  70. bt_cli/pra/commands/import_export.py +442 -0
  71. bt_cli/pra/commands/jump_clients.py +139 -0
  72. bt_cli/pra/commands/jump_groups.py +146 -0
  73. bt_cli/pra/commands/jump_items.py +638 -0
  74. bt_cli/pra/commands/jumpoints.py +95 -0
  75. bt_cli/pra/commands/policies.py +197 -0
  76. bt_cli/pra/commands/quick.py +470 -0
  77. bt_cli/pra/commands/teams.py +81 -0
  78. bt_cli/pra/commands/users.py +87 -0
  79. bt_cli/pra/commands/vault.py +564 -0
  80. bt_cli/pra/models/__init__.py +27 -0
  81. bt_cli/pra/models/common.py +12 -0
  82. bt_cli/pra/models/jump_client.py +25 -0
  83. bt_cli/pra/models/jump_group.py +15 -0
  84. bt_cli/pra/models/jump_item.py +72 -0
  85. bt_cli/pra/models/jumpoint.py +19 -0
  86. bt_cli/pra/models/team.py +14 -0
  87. bt_cli/pra/models/user.py +17 -0
  88. bt_cli/pra/models/vault.py +45 -0
  89. bt_cli/pws/__init__.py +1 -0
  90. bt_cli/pws/client/__init__.py +5 -0
  91. bt_cli/pws/client/base.py +356 -0
  92. bt_cli/pws/client/beyondinsight.py +869 -0
  93. bt_cli/pws/client/passwordsafe.py +1786 -0
  94. bt_cli/pws/commands/__init__.py +33 -0
  95. bt_cli/pws/commands/accounts.py +372 -0
  96. bt_cli/pws/commands/assets.py +311 -0
  97. bt_cli/pws/commands/auth.py +166 -0
  98. bt_cli/pws/commands/clouds.py +221 -0
  99. bt_cli/pws/commands/config.py +344 -0
  100. bt_cli/pws/commands/credentials.py +347 -0
  101. bt_cli/pws/commands/databases.py +306 -0
  102. bt_cli/pws/commands/directories.py +199 -0
  103. bt_cli/pws/commands/functional.py +298 -0
  104. bt_cli/pws/commands/import_export.py +452 -0
  105. bt_cli/pws/commands/platforms.py +118 -0
  106. bt_cli/pws/commands/quick.py +1646 -0
  107. bt_cli/pws/commands/search.py +256 -0
  108. bt_cli/pws/commands/secrets.py +1343 -0
  109. bt_cli/pws/commands/systems.py +389 -0
  110. bt_cli/pws/commands/users.py +415 -0
  111. bt_cli/pws/commands/workgroups.py +166 -0
  112. bt_cli/pws/config.py +18 -0
  113. bt_cli/pws/models/__init__.py +19 -0
  114. bt_cli/pws/models/account.py +186 -0
  115. bt_cli/pws/models/asset.py +102 -0
  116. bt_cli/pws/models/common.py +132 -0
  117. bt_cli/pws/models/system.py +121 -0
  118. bt_cli-0.4.13.dist-info/METADATA +417 -0
  119. bt_cli-0.4.13.dist-info/RECORD +121 -0
  120. bt_cli-0.4.13.dist-info/WHEEL +4 -0
  121. bt_cli-0.4.13.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)