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,221 @@
|
|
|
1
|
+
"""REST API debugging module for bt-cli.
|
|
2
|
+
|
|
3
|
+
Provides httpx event hooks to display API call details when --show-rest is enabled.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
import re
|
|
8
|
+
from typing import Any, Dict
|
|
9
|
+
|
|
10
|
+
import httpx
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
from rich.panel import Panel
|
|
13
|
+
from rich.syntax import Syntax
|
|
14
|
+
|
|
15
|
+
# Global flag for REST debugging
|
|
16
|
+
_show_rest = False
|
|
17
|
+
|
|
18
|
+
# Console for output
|
|
19
|
+
_console = Console(stderr=True)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def set_show_rest(enabled: bool) -> None:
|
|
23
|
+
"""Enable or disable REST API call display."""
|
|
24
|
+
global _show_rest
|
|
25
|
+
_show_rest = enabled
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def is_show_rest() -> bool:
|
|
29
|
+
"""Check if REST debugging is enabled."""
|
|
30
|
+
return _show_rest
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _sanitize_headers(headers: httpx.Headers) -> Dict[str, str]:
|
|
34
|
+
"""Sanitize headers by fully redacting sensitive values.
|
|
35
|
+
|
|
36
|
+
Security: Never show any part of sensitive values like tokens or API keys.
|
|
37
|
+
"""
|
|
38
|
+
sanitized = {}
|
|
39
|
+
sensitive_patterns = [
|
|
40
|
+
r"authorization",
|
|
41
|
+
r"ps-auth",
|
|
42
|
+
r"x-api-key",
|
|
43
|
+
r"api-key",
|
|
44
|
+
r"secret",
|
|
45
|
+
r"token",
|
|
46
|
+
r"password",
|
|
47
|
+
r"bearer",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
for key, value in headers.items():
|
|
51
|
+
key_lower = key.lower()
|
|
52
|
+
is_sensitive = any(re.search(p, key_lower) for p in sensitive_patterns)
|
|
53
|
+
|
|
54
|
+
if is_sensitive and value:
|
|
55
|
+
# Fully redact sensitive values - never show any characters
|
|
56
|
+
sanitized[key] = "[REDACTED]"
|
|
57
|
+
else:
|
|
58
|
+
sanitized[key] = value
|
|
59
|
+
|
|
60
|
+
return sanitized
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _sanitize_body(body: Any) -> Any:
|
|
64
|
+
"""Sanitize request/response body by redacting sensitive fields.
|
|
65
|
+
|
|
66
|
+
Security: Redact credentials in form data and JSON bodies.
|
|
67
|
+
"""
|
|
68
|
+
if body is None:
|
|
69
|
+
return None
|
|
70
|
+
|
|
71
|
+
# Sensitive field names (case-insensitive matching)
|
|
72
|
+
sensitive_fields = {
|
|
73
|
+
"password", "secret", "token", "api_key", "apikey", "api-key",
|
|
74
|
+
"client_secret", "client-secret", "clientsecret",
|
|
75
|
+
"authorization", "bearer", "credential", "credentials",
|
|
76
|
+
"access_token", "refresh_token", "id_token",
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if isinstance(body, dict):
|
|
80
|
+
sanitized = {}
|
|
81
|
+
for key, value in body.items():
|
|
82
|
+
key_lower = key.lower().replace("-", "_")
|
|
83
|
+
if any(s in key_lower for s in sensitive_fields):
|
|
84
|
+
sanitized[key] = "[REDACTED]"
|
|
85
|
+
elif isinstance(value, (dict, list)):
|
|
86
|
+
sanitized[key] = _sanitize_body(value)
|
|
87
|
+
else:
|
|
88
|
+
sanitized[key] = value
|
|
89
|
+
return sanitized
|
|
90
|
+
elif isinstance(body, list):
|
|
91
|
+
return [_sanitize_body(item) for item in body]
|
|
92
|
+
else:
|
|
93
|
+
return body
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _truncate_body(body: Any, max_length: int = 500, sanitize: bool = True) -> str:
|
|
97
|
+
"""Truncate body for display.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
body: Request/response body
|
|
101
|
+
max_length: Maximum characters to display
|
|
102
|
+
sanitize: If True, redact sensitive fields before display
|
|
103
|
+
"""
|
|
104
|
+
if body is None:
|
|
105
|
+
return "(empty)"
|
|
106
|
+
|
|
107
|
+
if isinstance(body, bytes):
|
|
108
|
+
try:
|
|
109
|
+
body = body.decode("utf-8")
|
|
110
|
+
except UnicodeDecodeError:
|
|
111
|
+
return f"(binary data, {len(body)} bytes)"
|
|
112
|
+
|
|
113
|
+
# Sanitize sensitive data before display
|
|
114
|
+
if sanitize and isinstance(body, (dict, list)):
|
|
115
|
+
body = _sanitize_body(body)
|
|
116
|
+
|
|
117
|
+
if isinstance(body, (dict, list)):
|
|
118
|
+
body = json.dumps(body, indent=2)
|
|
119
|
+
|
|
120
|
+
body_str = str(body)
|
|
121
|
+
if len(body_str) > max_length:
|
|
122
|
+
return body_str[:max_length] + f"\n... ({len(body_str) - max_length} more chars)"
|
|
123
|
+
return body_str
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def log_request(request: httpx.Request) -> None:
|
|
127
|
+
"""Log outgoing HTTP request."""
|
|
128
|
+
if not _show_rest:
|
|
129
|
+
return
|
|
130
|
+
|
|
131
|
+
# Build request info
|
|
132
|
+
method = request.method
|
|
133
|
+
url = str(request.url)
|
|
134
|
+
headers = _sanitize_headers(request.headers)
|
|
135
|
+
|
|
136
|
+
# Get request body if present
|
|
137
|
+
body = None
|
|
138
|
+
if request.content:
|
|
139
|
+
try:
|
|
140
|
+
body = json.loads(request.content)
|
|
141
|
+
except (json.JSONDecodeError, UnicodeDecodeError):
|
|
142
|
+
body = request.content
|
|
143
|
+
|
|
144
|
+
# Build output
|
|
145
|
+
lines = [
|
|
146
|
+
f"[bold cyan]{method}[/bold cyan] [white]{url}[/white]",
|
|
147
|
+
"",
|
|
148
|
+
"[dim]Headers:[/dim]",
|
|
149
|
+
]
|
|
150
|
+
|
|
151
|
+
for key, value in headers.items():
|
|
152
|
+
lines.append(f" [green]{key}:[/green] {value}")
|
|
153
|
+
|
|
154
|
+
if body:
|
|
155
|
+
lines.append("")
|
|
156
|
+
lines.append("[dim]Body:[/dim]")
|
|
157
|
+
body_str = _truncate_body(body, max_length=300)
|
|
158
|
+
lines.append(f" {body_str}")
|
|
159
|
+
|
|
160
|
+
_console.print(Panel(
|
|
161
|
+
"\n".join(lines),
|
|
162
|
+
title="[bold yellow]REST Request[/bold yellow]",
|
|
163
|
+
border_style="yellow",
|
|
164
|
+
expand=False,
|
|
165
|
+
))
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def log_response(response: httpx.Response) -> None:
|
|
169
|
+
"""Log HTTP response."""
|
|
170
|
+
if not _show_rest:
|
|
171
|
+
return
|
|
172
|
+
|
|
173
|
+
# Get status info
|
|
174
|
+
status_code = response.status_code
|
|
175
|
+
status_text = response.reason_phrase or ""
|
|
176
|
+
|
|
177
|
+
# Color based on status
|
|
178
|
+
if status_code < 300:
|
|
179
|
+
status_color = "green"
|
|
180
|
+
elif status_code < 400:
|
|
181
|
+
status_color = "yellow"
|
|
182
|
+
else:
|
|
183
|
+
status_color = "red"
|
|
184
|
+
|
|
185
|
+
# Get response body
|
|
186
|
+
try:
|
|
187
|
+
# Need to read the response to get body
|
|
188
|
+
response.read()
|
|
189
|
+
body = response.json()
|
|
190
|
+
except (json.JSONDecodeError, UnicodeDecodeError):
|
|
191
|
+
body = response.text if response.text else "(empty)"
|
|
192
|
+
except Exception:
|
|
193
|
+
body = "(could not read response)"
|
|
194
|
+
|
|
195
|
+
# Build output
|
|
196
|
+
lines = [
|
|
197
|
+
f"[bold {status_color}]{status_code} {status_text}[/bold {status_color}]",
|
|
198
|
+
"",
|
|
199
|
+
"[dim]Response Body:[/dim]",
|
|
200
|
+
_truncate_body(body, max_length=500),
|
|
201
|
+
]
|
|
202
|
+
|
|
203
|
+
_console.print(Panel(
|
|
204
|
+
"\n".join(lines),
|
|
205
|
+
title="[bold blue]REST Response[/bold blue]",
|
|
206
|
+
border_style="blue",
|
|
207
|
+
expand=False,
|
|
208
|
+
))
|
|
209
|
+
_console.print() # Add spacing between calls
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def get_event_hooks() -> Dict[str, list]:
|
|
213
|
+
"""Get httpx event hooks for request/response logging.
|
|
214
|
+
|
|
215
|
+
Usage:
|
|
216
|
+
client = httpx.Client(event_hooks=get_event_hooks())
|
|
217
|
+
"""
|
|
218
|
+
return {
|
|
219
|
+
"request": [log_request],
|
|
220
|
+
"response": [log_response],
|
|
221
|
+
}
|
bt_cli/data/CLAUDE.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# BT-CLI
|
|
2
|
+
|
|
3
|
+
BeyondTrust Platform CLI for Password Safe, Entitle, PRA, and EPM Windows.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# From project root
|
|
9
|
+
source .venv/bin/activate && source .env
|
|
10
|
+
bt whoami # Test all connections
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Skills Available
|
|
14
|
+
|
|
15
|
+
Use these slash commands for detailed product guidance:
|
|
16
|
+
|
|
17
|
+
| Skill | Purpose |
|
|
18
|
+
|-------|---------|
|
|
19
|
+
| `/bt` | Cross-product commands (PASM onboard/offboard/search) |
|
|
20
|
+
| `/pws` | Password Safe - credentials, systems, secrets |
|
|
21
|
+
| `/pra` | PRA - jump items, vault, SSH CA |
|
|
22
|
+
| `/entitle` | Entitle - JIT access, bundles, workflows |
|
|
23
|
+
| `/epmw` | EPM Windows - computers, policies, requests |
|
|
24
|
+
|
|
25
|
+
## Command Structure
|
|
26
|
+
|
|
27
|
+
| Product | Command | Key Operations |
|
|
28
|
+
|---------|---------|----------------|
|
|
29
|
+
| Cross-product | `bt quick` | `pasm-onboard`, `pasm-offboard`, `pasm-search` |
|
|
30
|
+
| Password Safe | `bt pws` | `systems`, `accounts`, `credentials`, `secrets`, `quick` |
|
|
31
|
+
| PRA | `bt pra` | `jump-items`, `vault`, `jump-groups`, `quick` |
|
|
32
|
+
| Entitle | `bt entitle` | `integrations`, `resources`, `bundles`, `permissions` |
|
|
33
|
+
| EPM Windows | `bt epmw` | `computers`, `groups`, `policies`, `requests`, `quick` |
|
|
34
|
+
|
|
35
|
+
## Common Patterns
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# All list commands support JSON output
|
|
39
|
+
bt pws systems list -o json
|
|
40
|
+
bt pra jump-items shell list -o json
|
|
41
|
+
|
|
42
|
+
# Quick commands combine multiple API calls
|
|
43
|
+
bt pws quick checkout -s "system" -a "account"
|
|
44
|
+
bt pws quick onboard -n "host" -i "10.0.1.50" -w 3
|
|
45
|
+
|
|
46
|
+
# Raw output for scripting
|
|
47
|
+
PASSWORD=$(bt pws quick checkout -s server -a admin --raw)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## API Quirks (Gotchas)
|
|
51
|
+
|
|
52
|
+
| Product | Pagination | Response Format |
|
|
53
|
+
|---------|------------|-----------------|
|
|
54
|
+
| PWS | `limit`/`offset` | `{"TotalCount": N, "Data": [...]}` |
|
|
55
|
+
| Entitle | `page`/`perPage` | `{"result": [...], "pagination": {...}}` |
|
|
56
|
+
| PRA | `per_page`/`current_page` (1-indexed) | Array (pagination in headers) |
|
|
57
|
+
| EPMW | `pageNumber`/`pageSize` | `{"data": [...], "totalCount": N}` |
|
|
58
|
+
|
|
59
|
+
**Important:**
|
|
60
|
+
- EPMW computers: Use `archive` not `delete` (405 error)
|
|
61
|
+
- PRA K8s tunnels: Require Linux jumpoint
|
|
62
|
+
- PWS assets: Must create via workgroup endpoint
|
|
63
|
+
- ECM integration: PWS system name must match PRA jump item name
|
|
64
|
+
|
|
65
|
+
## Functional vs Managed Accounts
|
|
66
|
+
|
|
67
|
+
**Functional accounts** (`bt pws functional`) - Service accounts used BY Password Safe to connect to systems for auto-management (password rotation, discovery). One functional account can manage many systems.
|
|
68
|
+
|
|
69
|
+
**Managed accounts** (`bt pws accounts`) - User accounts ON systems that Password Safe manages (stores/rotates passwords for). These are what users check out.
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# List functional accounts to find the right one for your platform
|
|
73
|
+
bt pws functional list
|
|
74
|
+
|
|
75
|
+
# List workgroups to find the right one
|
|
76
|
+
bt pws workgroups list
|
|
77
|
+
|
|
78
|
+
# Use functional account when onboarding a system
|
|
79
|
+
bt pws quick onboard -n "server" -i "10.0.1.50" -w <WORKGROUP_ID> -f <FUNC_ACCT_ID> -e "sudo"
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Project Structure
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
src/bt_cli/
|
|
86
|
+
├── cli.py # Root dispatcher
|
|
87
|
+
├── core/ # Shared (config, auth, output)
|
|
88
|
+
├── pws/ # Password Safe
|
|
89
|
+
├── pra/ # PRA
|
|
90
|
+
├── entitle/ # Entitle
|
|
91
|
+
└── epmw/ # EPM Windows
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Each product follows: `client/` (API), `commands/` (CLI), `models/` (Pydantic)
|
bt_cli/data/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bt
|
|
3
|
+
description: BeyondTrust platform CLI cross-product commands. Use when working across PWS, PRA, Entitle, or EPMW together, or for PASM workflows combining Password Safe and PRA.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# BT-Admin Cross-Product Commands
|
|
7
|
+
|
|
8
|
+
CLI for BeyondTrust platform: Password Safe, PRA, Entitle, EPM Windows.
|
|
9
|
+
|
|
10
|
+
## IMPORTANT: Destructive Operations
|
|
11
|
+
|
|
12
|
+
**ALWAYS confirm with the user before running destructive commands:**
|
|
13
|
+
- `delete` - Removes resources permanently
|
|
14
|
+
- `offboard` - Removes systems/accounts from management
|
|
15
|
+
- `archive` - Archives computers (EPMW)
|
|
16
|
+
- `revoke` - Revokes permissions (Entitle)
|
|
17
|
+
|
|
18
|
+
Before executing any destructive operation:
|
|
19
|
+
1. List what will be affected
|
|
20
|
+
2. Ask user for explicit confirmation
|
|
21
|
+
3. Never use `--force` flag without user approval
|
|
22
|
+
|
|
23
|
+
## Setup
|
|
24
|
+
|
|
25
|
+
The CLI should be installed and configured. Test with:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
bt whoami # Verify all products are connected
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Test All Connections
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
bt whoami # Test all configured products
|
|
35
|
+
bt whoami -o json # JSON output
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Cross-Product Quick Commands (`bt quick`)
|
|
39
|
+
|
|
40
|
+
### PASM Search (PWS + PRA)
|
|
41
|
+
Find systems/accounts across both products. Shows ECM alignment (names must match for credential injection).
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
bt quick pasm-search axion
|
|
45
|
+
bt quick pasm-search web-server -o json
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### PASM Onboard (PWS + PRA)
|
|
49
|
+
Onboard a host to both Password Safe and PRA in one command.
|
|
50
|
+
|
|
51
|
+
**First, discover your environment IDs** (see "Discover Environment IDs" below).
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# Linux SSH host (use IDs from your environment)
|
|
55
|
+
bt quick pasm-onboard -n "my-server" -i "10.0.1.50" \
|
|
56
|
+
-w <workgroup_id> -j <jumpoint_id> -g <jump_group_id>
|
|
57
|
+
|
|
58
|
+
# Full options
|
|
59
|
+
bt quick pasm-onboard \
|
|
60
|
+
--name "web-01" \
|
|
61
|
+
--ip "10.0.1.100" \
|
|
62
|
+
--workgroup <workgroup_id> \
|
|
63
|
+
--jumpoint <jumpoint_id> \
|
|
64
|
+
--jump-group <jump_group_id> \
|
|
65
|
+
--functional-account <func_acct_id> \
|
|
66
|
+
--elevation "sudo" \
|
|
67
|
+
--pra-username "admin"
|
|
68
|
+
|
|
69
|
+
# Windows RDP host
|
|
70
|
+
bt quick pasm-onboard -n "win-srv" -i "10.0.2.10" \
|
|
71
|
+
-w <workgroup_id> -p <platform_id> -j <jumpoint_id> -g <jump_group_id> \
|
|
72
|
+
--jump-type rdp --port 3389
|
|
73
|
+
|
|
74
|
+
# Skip one product
|
|
75
|
+
bt quick pasm-onboard -n "pra-only" -i "10.0.1.5" -j <jumpoint_id> -g <jump_group_id> --skip-pws
|
|
76
|
+
bt quick pasm-onboard -n "pws-only" -i "10.0.1.6" -w <workgroup_id> --skip-pra
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### PASM Offboard
|
|
80
|
+
Remove from both products.
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
bt quick pasm-offboard -n "my-server"
|
|
84
|
+
bt quick pasm-offboard -n "web-01" --force
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Discover Environment IDs
|
|
88
|
+
|
|
89
|
+
Use these commands to find the IDs for your environment:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# PWS resources
|
|
93
|
+
bt pws workgroups list # Find workgroup IDs
|
|
94
|
+
bt pws functional list # Find functional account IDs
|
|
95
|
+
bt pws platforms list # Find platform IDs
|
|
96
|
+
|
|
97
|
+
# PRA resources
|
|
98
|
+
bt pra jumpoint list # Find jumpoint IDs
|
|
99
|
+
bt pra jump-groups list # Find jump group IDs
|
|
100
|
+
bt pra vault groups list # Find vault account group IDs
|
|
101
|
+
|
|
102
|
+
# Entitle resources
|
|
103
|
+
bt entitle integrations list # Find integration IDs
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Product-Specific Skills
|
|
107
|
+
|
|
108
|
+
Use `/pws`, `/pra`, `/entitle`, `/epmw` for product-specific commands.
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: entitle
|
|
3
|
+
description: Entitle commands for JIT access, bundles, workflows, and permissions. Use when working with access requests, approval workflows, or managing user entitlements.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Entitle Commands (`bt entitle`)
|
|
7
|
+
|
|
8
|
+
## IMPORTANT: Destructive Operations
|
|
9
|
+
|
|
10
|
+
**ALWAYS confirm with the user before:**
|
|
11
|
+
- `bt entitle resources delete` - Deletes resource from integration
|
|
12
|
+
- `bt entitle bundles delete` - Deletes access bundle
|
|
13
|
+
- `bt entitle permissions revoke` - Revokes active permission
|
|
14
|
+
|
|
15
|
+
List affected resources first, then ask for explicit confirmation.
|
|
16
|
+
|
|
17
|
+
## Integrations & Resources
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# List integrations (connected apps)
|
|
21
|
+
bt entitle integrations list
|
|
22
|
+
bt entitle integrations get <integration_id>
|
|
23
|
+
|
|
24
|
+
# List resources in an integration
|
|
25
|
+
bt entitle resources list --integration <integration_id>
|
|
26
|
+
bt entitle resources get <resource_id>
|
|
27
|
+
|
|
28
|
+
# Virtual integration resources (use integration ID from `bt entitle integrations list`)
|
|
29
|
+
bt entitle resources create-virtual \
|
|
30
|
+
--name "Customer-05 (Bing7)" \
|
|
31
|
+
--integration <virtual_integration_id> \
|
|
32
|
+
--source-role-id <role_id> \
|
|
33
|
+
--role-name "Start Session"
|
|
34
|
+
bt entitle resources delete <resource_id>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Bundles
|
|
38
|
+
|
|
39
|
+
Access bundles group multiple roles across integrations.
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
bt entitle bundles list
|
|
43
|
+
bt entitle bundles get <bundle_id>
|
|
44
|
+
bt entitle bundles create \
|
|
45
|
+
--name "Dev AWS Access" \
|
|
46
|
+
--description "Development AWS console access" \
|
|
47
|
+
--workflow <workflow_id> \
|
|
48
|
+
--role <role_id> \
|
|
49
|
+
--duration 3600
|
|
50
|
+
bt entitle bundles delete <bundle_id>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Workflows
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
bt entitle workflows list
|
|
57
|
+
bt entitle workflows get <workflow_id>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Available Workflows:**
|
|
61
|
+
| Name | Type | Use Case |
|
|
62
|
+
|------|------|----------|
|
|
63
|
+
| Auto Approve | Automatic | Requests <= 12 hours |
|
|
64
|
+
| Low sensitivity | Simple | Single approver |
|
|
65
|
+
| Medium Sensitivity | Standard | Standard approval |
|
|
66
|
+
| Production access | Multi-step | Duration-based tiers |
|
|
67
|
+
|
|
68
|
+
## Users & Permissions
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Users
|
|
72
|
+
bt entitle users list
|
|
73
|
+
bt entitle users get <user_id>
|
|
74
|
+
|
|
75
|
+
# Active permissions
|
|
76
|
+
bt entitle permissions list
|
|
77
|
+
bt entitle permissions list --user <user_id>
|
|
78
|
+
bt entitle permissions revoke <permission_id>
|
|
79
|
+
|
|
80
|
+
# Accounts
|
|
81
|
+
bt entitle accounts list --integration <integration_id>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Roles & Policies
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
bt entitle roles list
|
|
88
|
+
bt entitle roles list --resource <resource_id>
|
|
89
|
+
bt entitle roles get <role_id>
|
|
90
|
+
|
|
91
|
+
bt entitle policies list
|
|
92
|
+
bt entitle policies get <policy_id>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Common Workflows
|
|
96
|
+
|
|
97
|
+
### Add PRA Jump Group to Entitle
|
|
98
|
+
|
|
99
|
+
**Important:** PRA integration sync is **asynchronous** (runs hourly). After creating a new PRA jump group, either:
|
|
100
|
+
- Wait up to 1 hour for auto-sync
|
|
101
|
+
- Or manually trigger sync in Entitle UI: Integrations → PRA → Sync Now
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
# 1. Find your PRA integration ID
|
|
105
|
+
bt entitle integrations list | grep -i pra
|
|
106
|
+
|
|
107
|
+
# 2. Trigger PRA sync in Entitle UI (or wait for hourly sync)
|
|
108
|
+
|
|
109
|
+
# 3. Find the new PRA resource
|
|
110
|
+
bt entitle resources list --integration <pra_integration_id>
|
|
111
|
+
|
|
112
|
+
# 4. Get "Start Sessions Only" role ID
|
|
113
|
+
bt entitle roles list --resource <pra_resource_id>
|
|
114
|
+
|
|
115
|
+
# 5. Add to virtual integration (find ID with `bt entitle integrations list`)
|
|
116
|
+
bt entitle resources create-virtual \
|
|
117
|
+
--name "Customer-05" \
|
|
118
|
+
--integration <virtual_integration_id> \
|
|
119
|
+
--source-role-id <role_id> \
|
|
120
|
+
--role-name "Start Session"
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Check User Access
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
bt entitle permissions list --user "user@example.com" -o json
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Discover IDs
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
# Find integration IDs
|
|
133
|
+
bt entitle integrations list
|
|
134
|
+
|
|
135
|
+
# Find workflow IDs
|
|
136
|
+
bt entitle workflows list
|
|
137
|
+
|
|
138
|
+
# Find resource IDs within an integration
|
|
139
|
+
bt entitle resources list --integration <integration_id>
|
|
140
|
+
|
|
141
|
+
# Find role IDs for a resource
|
|
142
|
+
bt entitle roles list --resource <resource_id>
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Integrations Available
|
|
146
|
+
|
|
147
|
+
- Cloud10 (Virtual Application)
|
|
148
|
+
- NexusDyn - Azure
|
|
149
|
+
- Customer Access (Virtual)
|
|
150
|
+
- Privileged Remote Access (PRA)
|
|
151
|
+
- AWS - SandBox Account
|
|
152
|
+
- Nexusdyn - Postgres ERP Database
|
|
153
|
+
- NexusDyn - EntraID
|
|
154
|
+
|
|
155
|
+
## Supported Applications (75+)
|
|
156
|
+
|
|
157
|
+
**Cloud:** AWS, Azure, GCP, Oracle OCI
|
|
158
|
+
**Identity:** Okta, Entra ID, OneLogin, JumpCloud
|
|
159
|
+
**DevOps:** GitHub, GitLab, Jenkins, Terraform Cloud
|
|
160
|
+
**Databases:** Postgres, MySQL, MSSQL, MongoDB, Snowflake
|
|
161
|
+
**Kubernetes:** EKS, AKS, GKE, Rancher
|
|
162
|
+
**BeyondTrust:** Password Safe, PRA, Remote Support
|
|
163
|
+
|
|
164
|
+
## API Notes
|
|
165
|
+
|
|
166
|
+
- Base path: `/public/v1`
|
|
167
|
+
- Pagination: `page`/`perPage`
|
|
168
|
+
- Response: `{"result": [...], "pagination": {...}}`
|
|
169
|
+
- All IDs are UUIDs (strings)
|
|
170
|
+
- Bearer token auth
|