bt-cli 0.4.29__tar.gz → 0.4.30__tar.gz
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-0.4.29 → bt_cli-0.4.30}/PKG-INFO +1 -1
- {bt_cli-0.4.29 → bt_cli-0.4.30}/pyproject.toml +1 -1
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/__init__.py +1 -1
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/core/rest_debug.py +5 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pws/client/passwordsafe.py +21 -6
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pws/commands/credentials.py +24 -10
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pws/commands/quick.py +37 -16
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pws/models/common.py +16 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/.claude/skills/bt/SKILL.md +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/.claude/skills/entitle/SKILL.md +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/.claude/skills/epmw/SKILL.md +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/.claude/skills/pra/SKILL.md +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/.claude/skills/pws/SKILL.md +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/.env.example +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/.github/workflows/ci.yml +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/.github/workflows/release.yml +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/.gitignore +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/CLAUDE.md +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/README.md +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/assets/cli-help.png +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/assets/cli-output.png +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/bt-cli.spec +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/bt_entry.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/scripts/bt_entry.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/scripts/sync-package-data.sh +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/cli.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/commands/__init__.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/commands/configure.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/commands/learn.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/commands/quick.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/core/__init__.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/core/auth.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/core/client.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/core/config.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/core/config_file.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/core/csv_utils.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/core/errors.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/core/output.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/core/prompts.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/data/CLAUDE.md +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/data/__init__.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/data/skills/bt/SKILL.md +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/data/skills/entitle/SKILL.md +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/data/skills/epmw/SKILL.md +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/data/skills/pra/SKILL.md +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/data/skills/pws/SKILL.md +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/entitle/__init__.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/entitle/client/__init__.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/entitle/client/base.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/entitle/commands/__init__.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/entitle/commands/accounts.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/entitle/commands/applications.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/entitle/commands/auth.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/entitle/commands/bundles.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/entitle/commands/integrations.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/entitle/commands/permissions.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/entitle/commands/policies.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/entitle/commands/resources.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/entitle/commands/roles.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/entitle/commands/users.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/entitle/commands/workflows.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/entitle/models/__init__.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/entitle/models/bundle.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/entitle/models/common.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/entitle/models/integration.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/entitle/models/permission.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/entitle/models/policy.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/entitle/models/resource.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/entitle/models/role.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/entitle/models/user.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/entitle/models/workflow.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/epmw/__init__.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/epmw/client/__init__.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/epmw/client/base.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/epmw/commands/__init__.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/epmw/commands/audits.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/epmw/commands/auth.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/epmw/commands/computers.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/epmw/commands/events.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/epmw/commands/groups.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/epmw/commands/policies.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/epmw/commands/quick.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/epmw/commands/requests.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/epmw/commands/roles.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/epmw/commands/tasks.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/epmw/commands/users.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/epmw/models/__init__.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pra/__init__.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pra/client/__init__.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pra/client/base.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pra/commands/__init__.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pra/commands/auth.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pra/commands/import_export.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pra/commands/jump_clients.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pra/commands/jump_groups.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pra/commands/jump_items.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pra/commands/jumpoints.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pra/commands/policies.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pra/commands/quick.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pra/commands/teams.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pra/commands/users.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pra/commands/vault.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pra/models/__init__.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pra/models/common.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pra/models/jump_client.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pra/models/jump_group.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pra/models/jump_item.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pra/models/jumpoint.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pra/models/team.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pra/models/user.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pra/models/vault.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pws/__init__.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pws/client/__init__.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pws/client/base.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pws/client/beyondinsight.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pws/commands/__init__.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pws/commands/accounts.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pws/commands/assets.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pws/commands/attributes.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pws/commands/auth.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pws/commands/clouds.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pws/commands/config.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pws/commands/databases.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pws/commands/directories.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pws/commands/functional.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pws/commands/import_export.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pws/commands/platforms.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pws/commands/search.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pws/commands/secrets.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pws/commands/systems.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pws/commands/users.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pws/commands/workgroups.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pws/config.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pws/models/__init__.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pws/models/account.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pws/models/asset.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/src/bt_cli/pws/models/system.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/__init__.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/conftest.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/core/__init__.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/core/test_auth.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/core/test_config.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/core/test_errors.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/core/test_rest_debug.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/entitle/__init__.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/entitle/test_client.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/entitle/test_commands.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/entitle-smoke-test.sh +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/epmw/__init__.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/epmw/test_client.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/epmw/test_commands.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/epmw-quick-test-plan.md +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/fixtures/__init__.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/fixtures/responses.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/integration/__init__.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/integration/conftest.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/integration/helpers.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/integration/test_entitle_integration.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/integration/test_epmw_integration.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/integration/test_epmw_lifecycle.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/integration/test_pra_integration.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/integration/test_pra_lifecycle.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/integration/test_pws_integration.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/integration/test_pws_lifecycle.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/pra/__init__.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/pra/test_client.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/pra/test_commands.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/pra-smoke-test.sh +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/pra-test-plan.md +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/pws/__init__.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/pws/test_client.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/pws/test_commands.py +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/pws-quick-test-plan.md +0 -0
- {bt_cli-0.4.29 → bt_cli-0.4.30}/tests/pws-smoke-test.sh +0 -0
|
@@ -74,6 +74,7 @@ def _sanitize_body(body: Any) -> Any:
|
|
|
74
74
|
"client_secret", "client-secret", "clientsecret",
|
|
75
75
|
"authorization", "bearer", "credential", "credentials",
|
|
76
76
|
"access_token", "refresh_token", "id_token",
|
|
77
|
+
"private_key", "privatekey", "ssh_key", "sshkey", "passphrase",
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
if isinstance(body, dict):
|
|
@@ -118,6 +119,10 @@ def _truncate_body(body: Any, max_length: int = 500, sanitize: bool = True) -> s
|
|
|
118
119
|
body = json.dumps(body, indent=2)
|
|
119
120
|
|
|
120
121
|
body_str = str(body)
|
|
122
|
+
|
|
123
|
+
# Detect PEM private key material in string responses
|
|
124
|
+
if sanitize and "-----BEGIN" in body_str and "PRIVATE KEY" in body_str:
|
|
125
|
+
return "[REDACTED - private key material]"
|
|
121
126
|
if len(body_str) > max_length:
|
|
122
127
|
return body_str[:max_length] + f"\n... ({len(body_str) - max_length} more chars)"
|
|
123
128
|
return body_str
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import Any, Optional, TYPE_CHECKING
|
|
4
4
|
|
|
5
|
+
from ..models.common import CredentialType, CREDENTIAL_TYPE_META
|
|
6
|
+
|
|
5
7
|
if TYPE_CHECKING:
|
|
6
8
|
from .base import PasswordSafeClient
|
|
7
9
|
|
|
@@ -378,19 +380,32 @@ class PasswordSafeMixin:
|
|
|
378
380
|
access_type=access_type,
|
|
379
381
|
)
|
|
380
382
|
|
|
381
|
-
def get_credential(
|
|
382
|
-
""
|
|
383
|
+
def get_credential(
|
|
384
|
+
self: "PasswordSafeClient",
|
|
385
|
+
request_id: int,
|
|
386
|
+
credential_type: Optional[str] = None,
|
|
387
|
+
) -> dict[str, Any]:
|
|
388
|
+
"""Get the credential for an approved request.
|
|
383
389
|
|
|
384
390
|
Args:
|
|
385
391
|
request_id: Request ID
|
|
392
|
+
credential_type: Type of credential to retrieve:
|
|
393
|
+
password (default), dsskey (SSH private key), passphrase
|
|
386
394
|
|
|
387
395
|
Returns:
|
|
388
|
-
Credential
|
|
396
|
+
Credential dict with key based on type (Password, PrivateKey, or Passphrase)
|
|
389
397
|
"""
|
|
390
|
-
|
|
391
|
-
|
|
398
|
+
# Only send type param for non-default types (preserves backward compat)
|
|
399
|
+
params = {"type": credential_type} if credential_type and credential_type != "password" else None
|
|
400
|
+
result = self.get(f"/Credentials/{request_id}", params=params)
|
|
401
|
+
|
|
402
|
+
# Determine the response dict key based on credential type
|
|
403
|
+
ctype = CredentialType(credential_type) if credential_type else CredentialType.PASSWORD
|
|
404
|
+
meta = CREDENTIAL_TYPE_META[ctype]
|
|
405
|
+
|
|
406
|
+
# API returns credential as plain string, wrap in dict for consistency
|
|
392
407
|
if isinstance(result, str):
|
|
393
|
-
return {"
|
|
408
|
+
return {meta["key"]: result}
|
|
394
409
|
return result
|
|
395
410
|
|
|
396
411
|
def checkin_request(
|
|
@@ -6,11 +6,13 @@ import json
|
|
|
6
6
|
import httpx
|
|
7
7
|
import typer
|
|
8
8
|
from rich.console import Console
|
|
9
|
+
from rich.markup import escape as rich_escape
|
|
9
10
|
from rich.table import Table
|
|
10
11
|
from rich.panel import Panel
|
|
11
12
|
|
|
12
13
|
from ...core.output import print_error, print_api_error
|
|
13
14
|
from ..client.base import get_client
|
|
15
|
+
from ..models.common import CredentialType, CREDENTIAL_TYPE_META
|
|
14
16
|
|
|
15
17
|
app = typer.Typer(no_args_is_help=True, help="Checkout and manage credentials in Password Safe")
|
|
16
18
|
console = Console()
|
|
@@ -77,7 +79,8 @@ def checkout_credential(
|
|
|
77
79
|
f"System: {system}\n"
|
|
78
80
|
f"Account: {account}\n"
|
|
79
81
|
f"Duration: {duration} minutes\n\n"
|
|
80
|
-
f"[dim]Use 'pws credentials show {request_id}' to get the password[/dim]"
|
|
82
|
+
f"[dim]Use 'pws credentials show {request_id}' to get the password[/dim]\n"
|
|
83
|
+
f"[dim]For SSH keys: 'pws credentials show {request_id} --credential-type dsskey'[/dim]",
|
|
81
84
|
title="Checkout Request",
|
|
82
85
|
))
|
|
83
86
|
|
|
@@ -98,31 +101,42 @@ def checkout_credential(
|
|
|
98
101
|
@app.command("show")
|
|
99
102
|
def show_credential(
|
|
100
103
|
request_id: int = typer.Argument(..., help="Request ID"),
|
|
104
|
+
credential_type: str = typer.Option(
|
|
105
|
+
"password", "--credential-type", help="Credential type: password, dsskey, passphrase"
|
|
106
|
+
),
|
|
101
107
|
output: str = typer.Option("table", "--output", "-o", help="Output format: table or json"),
|
|
102
|
-
raw: bool = typer.Option(False, "--raw", "-r", help="Output only the
|
|
108
|
+
raw: bool = typer.Option(False, "--raw", "-r", help="Output only the credential value (for scripts)"),
|
|
103
109
|
) -> None:
|
|
104
|
-
"""Get the
|
|
110
|
+
"""Get the credential for an approved request.
|
|
105
111
|
|
|
106
112
|
Example:
|
|
107
113
|
pws credentials show 12345
|
|
108
|
-
pws credentials show 12345 --raw
|
|
109
|
-
|
|
114
|
+
pws credentials show 12345 --raw
|
|
115
|
+
pws credentials show 12345 --credential-type dsskey --raw
|
|
116
|
+
KEY=$(bt pws credentials show 12345 --credential-type dsskey --raw)
|
|
110
117
|
"""
|
|
118
|
+
try:
|
|
119
|
+
ctype = CredentialType(credential_type)
|
|
120
|
+
except ValueError:
|
|
121
|
+
print_error(f"Invalid credential type: {credential_type}. Must be: password, dsskey, passphrase")
|
|
122
|
+
raise typer.Exit(1)
|
|
123
|
+
|
|
124
|
+
meta = CREDENTIAL_TYPE_META[ctype]
|
|
125
|
+
|
|
111
126
|
try:
|
|
112
127
|
with get_client() as client:
|
|
113
128
|
client.authenticate()
|
|
114
|
-
credential = client.get_credential(request_id)
|
|
129
|
+
credential = client.get_credential(request_id, credential_type=credential_type)
|
|
115
130
|
|
|
116
131
|
if raw:
|
|
117
|
-
|
|
118
|
-
print(credential.get("Password", ""), end="")
|
|
132
|
+
print(credential.get(meta["key"], ""), end="")
|
|
119
133
|
elif output == "json":
|
|
120
134
|
console.print_json(json.dumps(credential, default=str))
|
|
121
135
|
else:
|
|
122
|
-
|
|
136
|
+
value = rich_escape(credential.get(meta["key"], "N/A"))
|
|
123
137
|
console.print(Panel(
|
|
124
138
|
f"Request ID: [bold cyan]{request_id}[/bold cyan]\n"
|
|
125
|
-
f"
|
|
139
|
+
f"{meta['label']}: [bold green]{value}[/bold green]",
|
|
126
140
|
title="Credential",
|
|
127
141
|
))
|
|
128
142
|
|
|
@@ -6,12 +6,14 @@ import json
|
|
|
6
6
|
import httpx
|
|
7
7
|
import typer
|
|
8
8
|
from rich.console import Console
|
|
9
|
+
from rich.markup import escape as rich_escape
|
|
9
10
|
from rich.panel import Panel
|
|
10
11
|
from rich.table import Table
|
|
11
12
|
|
|
12
13
|
from ...core.output import print_api_error, print_error, print_success, print_warning
|
|
13
14
|
from ...core.prompts import prompt_if_missing, prompt_from_list, prompt_choice
|
|
14
15
|
from ..client.base import get_client
|
|
16
|
+
from ..models.common import CredentialType, CREDENTIAL_TYPE_META
|
|
15
17
|
|
|
16
18
|
app = typer.Typer(no_args_is_help=True, help="Quick commands - common multi-step operations in one command")
|
|
17
19
|
console = Console()
|
|
@@ -23,12 +25,13 @@ def quick_checkout(
|
|
|
23
25
|
account: Optional[str] = typer.Option(None, "--account", "-a", help="Account name"),
|
|
24
26
|
duration: int = typer.Option(60, "--duration", "-d", help="Duration in minutes"),
|
|
25
27
|
reason: Optional[str] = typer.Option(None, "--reason", "-r", help="Reason for checkout"),
|
|
26
|
-
|
|
28
|
+
credential_type: str = typer.Option("password", "--credential-type", help="Credential type: password, dsskey, passphrase"),
|
|
29
|
+
raw: bool = typer.Option(False, "--raw", help="Output only the credential value (for scripts)"),
|
|
27
30
|
output: str = typer.Option("table", "--output", "-o", help="Output format: table or json"),
|
|
28
31
|
) -> None:
|
|
29
|
-
"""Checkout credentials and show password in one step.
|
|
32
|
+
"""Checkout credentials and show password/SSH key in one step.
|
|
30
33
|
|
|
31
|
-
Combines: find system -> find account -> checkout -> show
|
|
34
|
+
Combines: find system -> find account -> checkout -> show credential
|
|
32
35
|
|
|
33
36
|
If system or account not provided, prompts interactively.
|
|
34
37
|
|
|
@@ -37,6 +40,7 @@ def quick_checkout(
|
|
|
37
40
|
bt pws quick checkout -s "axion-finapp-01" -a "root"
|
|
38
41
|
bt pws quick checkout -s axion -a root --duration 30
|
|
39
42
|
PASSWORD=$(bt pws quick checkout -s server -a admin --raw)
|
|
43
|
+
KEY=$(bt pws quick checkout -s server -a admin --credential-type dsskey --raw)
|
|
40
44
|
"""
|
|
41
45
|
try:
|
|
42
46
|
with get_client() as client:
|
|
@@ -115,12 +119,19 @@ def quick_checkout(
|
|
|
115
119
|
request_id = request.get("RequestID")
|
|
116
120
|
|
|
117
121
|
# Step 4: Get the credential
|
|
118
|
-
|
|
119
|
-
|
|
122
|
+
try:
|
|
123
|
+
ctype = CredentialType(credential_type)
|
|
124
|
+
except ValueError:
|
|
125
|
+
print_error(f"Invalid credential type: {credential_type}. Must be: password, dsskey, passphrase")
|
|
126
|
+
raise typer.Exit(1)
|
|
127
|
+
meta = CREDENTIAL_TYPE_META[ctype]
|
|
128
|
+
|
|
129
|
+
credential = client.get_credential(request_id, credential_type=credential_type)
|
|
130
|
+
value = credential.get(meta["key"], "")
|
|
120
131
|
|
|
121
132
|
# Output
|
|
122
133
|
if raw:
|
|
123
|
-
print(
|
|
134
|
+
print(value, end="")
|
|
124
135
|
elif output == "json":
|
|
125
136
|
result = {
|
|
126
137
|
"request_id": request_id,
|
|
@@ -128,7 +139,8 @@ def quick_checkout(
|
|
|
128
139
|
"system_id": system_id,
|
|
129
140
|
"account": account_name,
|
|
130
141
|
"account_id": account_id,
|
|
131
|
-
"
|
|
142
|
+
"credential_type": credential_type,
|
|
143
|
+
meta["key"]: value,
|
|
132
144
|
"duration_minutes": duration,
|
|
133
145
|
}
|
|
134
146
|
console.print_json(json.dumps(result))
|
|
@@ -139,7 +151,7 @@ def quick_checkout(
|
|
|
139
151
|
f"Account: [cyan]{account_name}[/cyan] (ID: {account_id})\n"
|
|
140
152
|
f"Request ID: [bold yellow]{request_id}[/bold yellow]\n"
|
|
141
153
|
f"Duration: {duration} minutes\n\n"
|
|
142
|
-
f"
|
|
154
|
+
f"{meta['label']}: [bold green]{rich_escape(value)}[/bold green]\n\n"
|
|
143
155
|
f"[dim]Checkin: bt pws credentials checkin {request_id}[/dim]",
|
|
144
156
|
title="Quick Checkout",
|
|
145
157
|
))
|
|
@@ -288,16 +300,18 @@ def quick_password(
|
|
|
288
300
|
system: Optional[str] = typer.Option(None, "--system", "-s", help="System name"),
|
|
289
301
|
account: Optional[str] = typer.Option(None, "--account", "-a", help="Account name"),
|
|
290
302
|
duration: int = typer.Option(5, "--duration", "-d", help="Duration in minutes (default: 5)"),
|
|
291
|
-
|
|
303
|
+
credential_type: str = typer.Option("password", "--credential-type", help="Credential type: password, dsskey, passphrase"),
|
|
304
|
+
auto_checkin: bool = typer.Option(True, "--auto-checkin/--no-auto-checkin", help="Auto checkin after showing credential"),
|
|
292
305
|
) -> None:
|
|
293
|
-
"""Get a
|
|
306
|
+
"""Get a credential quickly - checkout, show, and optionally auto-checkin.
|
|
294
307
|
|
|
295
|
-
Ideal for quick lookups where you just need to see/copy the password.
|
|
308
|
+
Ideal for quick lookups where you just need to see/copy the password or SSH key.
|
|
296
309
|
If system or account not provided, prompts interactively.
|
|
297
310
|
|
|
298
311
|
Examples:
|
|
299
312
|
bt pws quick password # Interactive mode
|
|
300
313
|
bt pws quick password -s server -a root
|
|
314
|
+
bt pws quick password -s server -a root --credential-type dsskey
|
|
301
315
|
bt pws quick password -s db-server -a admin --no-auto-checkin
|
|
302
316
|
"""
|
|
303
317
|
try:
|
|
@@ -361,12 +375,19 @@ def quick_password(
|
|
|
361
375
|
)
|
|
362
376
|
request_id = request.get("RequestID")
|
|
363
377
|
|
|
364
|
-
# Get
|
|
365
|
-
|
|
366
|
-
|
|
378
|
+
# Get credential
|
|
379
|
+
try:
|
|
380
|
+
ctype = CredentialType(credential_type)
|
|
381
|
+
except ValueError:
|
|
382
|
+
print_error(f"Invalid credential type: {credential_type}. Must be: password, dsskey, passphrase")
|
|
383
|
+
raise typer.Exit(1)
|
|
384
|
+
meta = CREDENTIAL_TYPE_META[ctype]
|
|
385
|
+
|
|
386
|
+
credential = client.get_credential(request_id, credential_type=credential_type)
|
|
387
|
+
value = credential.get(meta["key"], "")
|
|
367
388
|
|
|
368
|
-
# Show
|
|
369
|
-
console.print(f"\n[bold green]{
|
|
389
|
+
# Show credential
|
|
390
|
+
console.print(f"\n[bold green]{rich_escape(value)}[/bold green]\n")
|
|
370
391
|
console.print(f"[dim]{account_name}@{system_name} (Request: {request_id})[/dim]")
|
|
371
392
|
|
|
372
393
|
# Auto checkin
|
|
@@ -1,10 +1,26 @@
|
|
|
1
1
|
"""Common models and types shared across the API."""
|
|
2
2
|
|
|
3
|
+
from enum import Enum
|
|
3
4
|
from typing import Any, Generic, Optional, TypeVar
|
|
4
5
|
from datetime import datetime
|
|
5
6
|
from pydantic import BaseModel, ConfigDict
|
|
6
7
|
|
|
7
8
|
|
|
9
|
+
class CredentialType(str, Enum):
|
|
10
|
+
"""Type of credential to retrieve from Password Safe."""
|
|
11
|
+
|
|
12
|
+
PASSWORD = "password"
|
|
13
|
+
DSSKEY = "dsskey"
|
|
14
|
+
PASSPHRASE = "passphrase"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
CREDENTIAL_TYPE_META = {
|
|
18
|
+
CredentialType.PASSWORD: {"key": "Password", "label": "Password"},
|
|
19
|
+
CredentialType.DSSKEY: {"key": "PrivateKey", "label": "Private Key"},
|
|
20
|
+
CredentialType.PASSPHRASE: {"key": "Passphrase", "label": "Passphrase"},
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
8
24
|
# Type variable for generic paginated responses
|
|
9
25
|
T = TypeVar("T", bound=BaseModel)
|
|
10
26
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|