bt-cli 0.4.28__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.28 → bt_cli-0.4.30}/CLAUDE.md +1 -1
- {bt_cli-0.4.28 → bt_cli-0.4.30}/PKG-INFO +1 -1
- {bt_cli-0.4.28 → bt_cli-0.4.30}/pyproject.toml +1 -1
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/__init__.py +1 -1
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/commands/configure.py +218 -2
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/core/rest_debug.py +5 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pws/client/passwordsafe.py +21 -6
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pws/commands/credentials.py +24 -10
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pws/commands/quick.py +37 -16
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pws/models/common.py +16 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/.claude/skills/bt/SKILL.md +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/.claude/skills/entitle/SKILL.md +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/.claude/skills/epmw/SKILL.md +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/.claude/skills/pra/SKILL.md +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/.claude/skills/pws/SKILL.md +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/.env.example +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/.github/workflows/ci.yml +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/.github/workflows/release.yml +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/.gitignore +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/README.md +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/assets/cli-help.png +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/assets/cli-output.png +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/bt-cli.spec +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/bt_entry.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/scripts/bt_entry.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/scripts/sync-package-data.sh +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/cli.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/commands/__init__.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/commands/learn.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/commands/quick.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/core/__init__.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/core/auth.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/core/client.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/core/config.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/core/config_file.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/core/csv_utils.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/core/errors.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/core/output.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/core/prompts.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/data/CLAUDE.md +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/data/__init__.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/data/skills/bt/SKILL.md +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/data/skills/entitle/SKILL.md +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/data/skills/epmw/SKILL.md +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/data/skills/pra/SKILL.md +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/data/skills/pws/SKILL.md +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/entitle/__init__.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/entitle/client/__init__.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/entitle/client/base.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/entitle/commands/__init__.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/entitle/commands/accounts.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/entitle/commands/applications.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/entitle/commands/auth.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/entitle/commands/bundles.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/entitle/commands/integrations.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/entitle/commands/permissions.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/entitle/commands/policies.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/entitle/commands/resources.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/entitle/commands/roles.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/entitle/commands/users.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/entitle/commands/workflows.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/entitle/models/__init__.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/entitle/models/bundle.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/entitle/models/common.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/entitle/models/integration.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/entitle/models/permission.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/entitle/models/policy.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/entitle/models/resource.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/entitle/models/role.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/entitle/models/user.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/entitle/models/workflow.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/epmw/__init__.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/epmw/client/__init__.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/epmw/client/base.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/epmw/commands/__init__.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/epmw/commands/audits.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/epmw/commands/auth.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/epmw/commands/computers.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/epmw/commands/events.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/epmw/commands/groups.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/epmw/commands/policies.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/epmw/commands/quick.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/epmw/commands/requests.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/epmw/commands/roles.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/epmw/commands/tasks.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/epmw/commands/users.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/epmw/models/__init__.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pra/__init__.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pra/client/__init__.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pra/client/base.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pra/commands/__init__.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pra/commands/auth.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pra/commands/import_export.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pra/commands/jump_clients.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pra/commands/jump_groups.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pra/commands/jump_items.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pra/commands/jumpoints.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pra/commands/policies.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pra/commands/quick.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pra/commands/teams.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pra/commands/users.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pra/commands/vault.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pra/models/__init__.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pra/models/common.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pra/models/jump_client.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pra/models/jump_group.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pra/models/jump_item.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pra/models/jumpoint.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pra/models/team.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pra/models/user.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pra/models/vault.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pws/__init__.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pws/client/__init__.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pws/client/base.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pws/client/beyondinsight.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pws/commands/__init__.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pws/commands/accounts.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pws/commands/assets.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pws/commands/attributes.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pws/commands/auth.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pws/commands/clouds.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pws/commands/config.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pws/commands/databases.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pws/commands/directories.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pws/commands/functional.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pws/commands/import_export.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pws/commands/platforms.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pws/commands/search.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pws/commands/secrets.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pws/commands/systems.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pws/commands/users.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pws/commands/workgroups.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pws/config.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pws/models/__init__.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pws/models/account.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pws/models/asset.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/src/bt_cli/pws/models/system.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/__init__.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/conftest.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/core/__init__.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/core/test_auth.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/core/test_config.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/core/test_errors.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/core/test_rest_debug.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/entitle/__init__.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/entitle/test_client.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/entitle/test_commands.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/entitle-smoke-test.sh +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/epmw/__init__.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/epmw/test_client.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/epmw/test_commands.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/epmw-quick-test-plan.md +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/fixtures/__init__.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/fixtures/responses.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/integration/__init__.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/integration/conftest.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/integration/helpers.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/integration/test_entitle_integration.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/integration/test_epmw_integration.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/integration/test_epmw_lifecycle.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/integration/test_pra_integration.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/integration/test_pra_lifecycle.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/integration/test_pws_integration.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/integration/test_pws_lifecycle.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/pra/__init__.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/pra/test_client.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/pra/test_commands.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/pra-smoke-test.sh +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/pra-test-plan.md +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/pws/__init__.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/pws/test_client.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/pws/test_commands.py +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/pws-quick-test-plan.md +0 -0
- {bt_cli-0.4.28 → bt_cli-0.4.30}/tests/pws-smoke-test.sh +0 -0
|
@@ -298,11 +298,21 @@ def show_config(
|
|
|
298
298
|
profile: Optional[str] = typer.Option(None, "--profile", help="Profile to show"),
|
|
299
299
|
show_secrets: bool = typer.Option(False, "--show-secrets", help="Show secret values"),
|
|
300
300
|
) -> None:
|
|
301
|
-
"""Show
|
|
301
|
+
"""Show configuration from config file.
|
|
302
|
+
|
|
303
|
+
Note: This only shows profiles saved to ~/.bt-cli/config.yaml.
|
|
304
|
+
If you're using environment variables or .env file, use:
|
|
305
|
+
|
|
306
|
+
bt configure effective
|
|
307
|
+
|
|
308
|
+
to see the actual configuration in use.
|
|
309
|
+
"""
|
|
302
310
|
config = load_config_file()
|
|
303
311
|
|
|
304
312
|
if not config.profiles:
|
|
305
|
-
print_warning("No
|
|
313
|
+
print_warning("No profiles in config file. Run 'bt configure' to set up.")
|
|
314
|
+
console.print("\n[dim]Tip: If using .env or environment variables, run:[/dim]")
|
|
315
|
+
console.print("[cyan] bt configure effective[/cyan]")
|
|
306
316
|
raise typer.Exit(0)
|
|
307
317
|
|
|
308
318
|
profiles_to_show = [profile] if profile else config.list_profiles()
|
|
@@ -413,3 +423,209 @@ def show_path() -> None:
|
|
|
413
423
|
console.print("[green]Config file exists[/green]")
|
|
414
424
|
else:
|
|
415
425
|
console.print("[yellow]Config file does not exist yet[/yellow]")
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
@app.command("import-env")
|
|
429
|
+
def import_from_env(
|
|
430
|
+
profile: str = typer.Option("default", "--profile", "-p", help="Profile name to save as"),
|
|
431
|
+
force: bool = typer.Option(False, "--force", "-f", help="Overwrite existing profile"),
|
|
432
|
+
) -> None:
|
|
433
|
+
"""Import configuration from environment variables into a profile.
|
|
434
|
+
|
|
435
|
+
This saves the current BT_* environment variables (including .env file)
|
|
436
|
+
into a named profile in the config file. Useful for:
|
|
437
|
+
|
|
438
|
+
- Converting .env setup to profile-based config
|
|
439
|
+
- Creating profiles from existing environment
|
|
440
|
+
- Backing up current config to file
|
|
441
|
+
|
|
442
|
+
Example:
|
|
443
|
+
bt configure import-env --profile production
|
|
444
|
+
"""
|
|
445
|
+
import os
|
|
446
|
+
from dotenv import load_dotenv, find_dotenv
|
|
447
|
+
from rich.prompt import Confirm
|
|
448
|
+
|
|
449
|
+
# Load .env if present
|
|
450
|
+
env_file = find_dotenv()
|
|
451
|
+
if env_file:
|
|
452
|
+
load_dotenv(env_file)
|
|
453
|
+
console.print(f"[dim]Loaded .env from: {env_file}[/dim]\n")
|
|
454
|
+
|
|
455
|
+
config = load_config_file()
|
|
456
|
+
|
|
457
|
+
# Check if profile exists
|
|
458
|
+
if profile in config.profiles and not force:
|
|
459
|
+
if not Confirm.ask(f"Profile '{profile}' exists. Overwrite?", default=False):
|
|
460
|
+
print_info("Cancelled")
|
|
461
|
+
raise typer.Exit(0)
|
|
462
|
+
|
|
463
|
+
# Product environment variable mappings
|
|
464
|
+
env_mappings = {
|
|
465
|
+
"pws": {
|
|
466
|
+
"api_url": "BT_PWS_API_URL",
|
|
467
|
+
"api_key": "BT_PWS_API_KEY",
|
|
468
|
+
"client_id": "BT_PWS_CLIENT_ID",
|
|
469
|
+
"client_secret": "BT_PWS_CLIENT_SECRET",
|
|
470
|
+
"run_as": "BT_PWS_RUN_AS",
|
|
471
|
+
"verify_ssl": "BT_PWS_VERIFY_SSL",
|
|
472
|
+
"timeout": "BT_PWS_TIMEOUT",
|
|
473
|
+
},
|
|
474
|
+
"pra": {
|
|
475
|
+
"api_url": "BT_PRA_API_URL",
|
|
476
|
+
"client_id": "BT_PRA_CLIENT_ID",
|
|
477
|
+
"client_secret": "BT_PRA_CLIENT_SECRET",
|
|
478
|
+
"verify_ssl": "BT_PRA_VERIFY_SSL",
|
|
479
|
+
"timeout": "BT_PRA_TIMEOUT",
|
|
480
|
+
},
|
|
481
|
+
"entitle": {
|
|
482
|
+
"api_url": "BT_ENTITLE_API_URL",
|
|
483
|
+
"api_key": "BT_ENTITLE_API_KEY",
|
|
484
|
+
"verify_ssl": "BT_ENTITLE_VERIFY_SSL",
|
|
485
|
+
"timeout": "BT_ENTITLE_TIMEOUT",
|
|
486
|
+
},
|
|
487
|
+
"epmw": {
|
|
488
|
+
"api_url": "BT_EPM_API_URL",
|
|
489
|
+
"client_id": "BT_EPM_CLIENT_ID",
|
|
490
|
+
"client_secret": "BT_EPM_CLIENT_SECRET",
|
|
491
|
+
"verify_ssl": "BT_EPM_VERIFY_SSL",
|
|
492
|
+
"timeout": "BT_EPM_TIMEOUT",
|
|
493
|
+
},
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
imported = []
|
|
497
|
+
for product, env_vars in env_mappings.items():
|
|
498
|
+
product_config = {}
|
|
499
|
+
for setting, env_var in env_vars.items():
|
|
500
|
+
value = os.getenv(env_var)
|
|
501
|
+
if value:
|
|
502
|
+
# Convert types
|
|
503
|
+
if setting == "verify_ssl":
|
|
504
|
+
product_config[setting] = value.lower() not in ("false", "0", "no")
|
|
505
|
+
elif setting == "timeout":
|
|
506
|
+
try:
|
|
507
|
+
product_config[setting] = float(value)
|
|
508
|
+
except ValueError:
|
|
509
|
+
product_config[setting] = 30.0
|
|
510
|
+
else:
|
|
511
|
+
product_config[setting] = value
|
|
512
|
+
|
|
513
|
+
if product_config.get("api_url"):
|
|
514
|
+
# Infer auth_method for PWS
|
|
515
|
+
if product == "pws":
|
|
516
|
+
if product_config.get("api_key"):
|
|
517
|
+
product_config["auth_method"] = "apikey"
|
|
518
|
+
elif product_config.get("client_id"):
|
|
519
|
+
product_config["auth_method"] = "oauth"
|
|
520
|
+
|
|
521
|
+
config.set_product_config(product, product_config, profile)
|
|
522
|
+
imported.append(product)
|
|
523
|
+
|
|
524
|
+
if imported:
|
|
525
|
+
if not config.default_profile or profile == "default":
|
|
526
|
+
config.default_profile = profile
|
|
527
|
+
save_config_file(config)
|
|
528
|
+
print_success(f"Imported {', '.join(imported)} into profile '{profile}'")
|
|
529
|
+
console.print(f"[dim]Config saved to: {CONFIG_FILE}[/dim]")
|
|
530
|
+
else:
|
|
531
|
+
print_warning("No configuration found in environment variables")
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
@app.command("effective")
|
|
535
|
+
def show_effective_config(
|
|
536
|
+
show_secrets: bool = typer.Option(False, "--show-secrets", help="Show secret values"),
|
|
537
|
+
) -> None:
|
|
538
|
+
"""Show effective configuration from all sources.
|
|
539
|
+
|
|
540
|
+
Shows what configuration is actually in use, combining:
|
|
541
|
+
- Environment variables (BT_PWS_*, BT_PRA_*, etc.)
|
|
542
|
+
- .env file (if present)
|
|
543
|
+
- Config file profiles (~/.bt-cli/config.yaml)
|
|
544
|
+
|
|
545
|
+
This is useful for debugging why the CLI connects successfully
|
|
546
|
+
but 'bt configure show' appears empty.
|
|
547
|
+
"""
|
|
548
|
+
import os
|
|
549
|
+
from dotenv import load_dotenv, find_dotenv
|
|
550
|
+
|
|
551
|
+
# Load .env if present
|
|
552
|
+
env_file = find_dotenv()
|
|
553
|
+
if env_file:
|
|
554
|
+
load_dotenv(env_file)
|
|
555
|
+
console.print(f"[dim]Loaded .env from: {env_file}[/dim]\n")
|
|
556
|
+
|
|
557
|
+
def mask_secret(key: str, value: str) -> str:
|
|
558
|
+
if not value:
|
|
559
|
+
return "[dim]not set[/dim]"
|
|
560
|
+
if not show_secrets and any(s in key.lower() for s in ["secret", "key", "password"]):
|
|
561
|
+
return "****" + value[-4:] if len(value) > 4 else "****"
|
|
562
|
+
return value
|
|
563
|
+
|
|
564
|
+
# Product environment variable mappings
|
|
565
|
+
products = {
|
|
566
|
+
"Password Safe": {
|
|
567
|
+
"api_url": "BT_PWS_API_URL",
|
|
568
|
+
"api_key": "BT_PWS_API_KEY",
|
|
569
|
+
"client_id": "BT_PWS_CLIENT_ID",
|
|
570
|
+
"client_secret": "BT_PWS_CLIENT_SECRET",
|
|
571
|
+
"run_as": "BT_PWS_RUN_AS",
|
|
572
|
+
"verify_ssl": "BT_PWS_VERIFY_SSL",
|
|
573
|
+
"timeout": "BT_PWS_TIMEOUT",
|
|
574
|
+
},
|
|
575
|
+
"PRA": {
|
|
576
|
+
"api_url": "BT_PRA_API_URL",
|
|
577
|
+
"client_id": "BT_PRA_CLIENT_ID",
|
|
578
|
+
"client_secret": "BT_PRA_CLIENT_SECRET",
|
|
579
|
+
"verify_ssl": "BT_PRA_VERIFY_SSL",
|
|
580
|
+
"timeout": "BT_PRA_TIMEOUT",
|
|
581
|
+
},
|
|
582
|
+
"Entitle": {
|
|
583
|
+
"api_url": "BT_ENTITLE_API_URL",
|
|
584
|
+
"api_key": "BT_ENTITLE_API_KEY",
|
|
585
|
+
"verify_ssl": "BT_ENTITLE_VERIFY_SSL",
|
|
586
|
+
"timeout": "BT_ENTITLE_TIMEOUT",
|
|
587
|
+
},
|
|
588
|
+
"EPM Windows": {
|
|
589
|
+
"api_url": "BT_EPM_API_URL",
|
|
590
|
+
"client_id": "BT_EPM_CLIENT_ID",
|
|
591
|
+
"client_secret": "BT_EPM_CLIENT_SECRET",
|
|
592
|
+
"verify_ssl": "BT_EPM_VERIFY_SSL",
|
|
593
|
+
"timeout": "BT_EPM_TIMEOUT",
|
|
594
|
+
},
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
# Check active profile
|
|
598
|
+
active_profile = os.getenv("BT_PROFILE", "default")
|
|
599
|
+
console.print(f"[bold]Active Profile:[/bold] {active_profile}\n")
|
|
600
|
+
|
|
601
|
+
# Show effective config for each product
|
|
602
|
+
for product_name, env_vars in products.items():
|
|
603
|
+
table = Table(title=product_name, show_header=True, title_style="bold cyan")
|
|
604
|
+
table.add_column("Setting", style="green")
|
|
605
|
+
table.add_column("Env Var", style="dim")
|
|
606
|
+
table.add_column("Value")
|
|
607
|
+
table.add_column("Source", style="dim")
|
|
608
|
+
|
|
609
|
+
has_config = False
|
|
610
|
+
for setting, env_var in env_vars.items():
|
|
611
|
+
value = os.getenv(env_var, "")
|
|
612
|
+
if value:
|
|
613
|
+
has_config = True
|
|
614
|
+
source = ".env" if env_file else "environment"
|
|
615
|
+
table.add_row(setting, env_var, mask_secret(setting, value), source)
|
|
616
|
+
|
|
617
|
+
if has_config:
|
|
618
|
+
console.print(table)
|
|
619
|
+
console.print()
|
|
620
|
+
else:
|
|
621
|
+
console.print(f"[dim]{product_name}: not configured[/dim]\n")
|
|
622
|
+
|
|
623
|
+
# Also show config file info
|
|
624
|
+
config = load_config_file()
|
|
625
|
+
if config.profiles:
|
|
626
|
+
console.print("[bold]Config File Profiles:[/bold]")
|
|
627
|
+
for profile_name in config.list_profiles():
|
|
628
|
+
products_list = list(config.profiles[profile_name].keys())
|
|
629
|
+
is_default = " (default)" if profile_name == config.default_profile else ""
|
|
630
|
+
console.print(f" {profile_name}{is_default}: {', '.join(products_list)}")
|
|
631
|
+
console.print(f"\n[dim]Note: Environment variables override config file settings[/dim]")
|
|
@@ -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
|