bt-cli 0.4.70__tar.gz → 0.4.72__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.70 → bt_cli-0.4.72}/.github/workflows/ci.yml +3 -1
- {bt_cli-0.4.70 → bt_cli-0.4.72}/CLAUDE.md +5 -3
- {bt_cli-0.4.70 → bt_cli-0.4.72}/PKG-INFO +2 -2
- bt_cli-0.4.72/bt-cli-cheatsheet.html +229 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/pyproject.toml +5 -1
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/__init__.py +1 -1
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/commands/learn.py +1 -1
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/commands/quick.py +31 -31
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/core/config.py +40 -11
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/core/output.py +3 -1
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/core/prompts.py +5 -5
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/data/skills/entitle/SKILL.md +52 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/entitle/client/base.py +75 -3
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/entitle/commands/__init__.py +3 -0
- bt_cli-0.4.72/src/bt_cli/entitle/commands/audit.py +139 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/entitle/commands/bundles.py +1 -1
- bt_cli-0.4.72/src/bt_cli/entitle/commands/jit.py +137 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/entitle/commands/permissions.py +1 -1
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/entitle/commands/requests.py +2 -2
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/entitle/commands/resources.py +2 -2
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epml/commands/rbp_cmdgrps.py +1 -1
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epml/commands/rbp_hostgrps.py +1 -1
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epml/commands/rbp_tmdategrps.py +1 -1
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epml/commands/rbp_usergrps.py +1 -1
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epmw/commands/policies.py +3 -3
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epmw/commands/quick.py +4 -4
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pra/commands/group_policies.py +1 -1
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pra/commands/jump_clients.py +1 -1
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pra/commands/jump_items.py +4 -4
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pra/commands/quick.py +16 -16
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pra/commands/users.py +2 -2
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pra/commands/vault.py +3 -3
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pws/commands/accounts.py +3 -3
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pws/commands/assets.py +1 -1
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pws/commands/attributes.py +2 -2
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pws/commands/clouds.py +2 -2
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pws/commands/databases.py +1 -1
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pws/commands/functional.py +4 -4
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pws/commands/quick.py +65 -65
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pws/commands/search.py +2 -2
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pws/commands/secrets.py +10 -10
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pws/commands/sessions.py +3 -3
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pws/commands/systems.py +4 -4
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pws/commands/workgroups.py +1 -1
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/conftest.py +40 -0
- bt_cli-0.4.72/tests/core/test_json_stdout_purity.py +304 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/entitle/test_client.py +59 -8
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/entitle/test_commands.py +113 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/epml/test_commands.py +20 -20
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/integration/conftest.py +5 -1
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/pf/test_commands.py +15 -15
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/pra/test_jump_item_fixes.py +10 -2
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/pws/test_commands.py +24 -13
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/secrets/test_commands.py +40 -40
- {bt_cli-0.4.70 → bt_cli-0.4.72}/.claude/skills/bt/SKILL.md +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/.claude/skills/entitle/SKILL.md +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/.claude/skills/epml/SKILL.md +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/.claude/skills/epmw/SKILL.md +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/.claude/skills/pra/SKILL.md +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/.claude/skills/pws/SKILL.md +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/.env.example +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/.github/workflows/release.yml +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/.gitignore +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/README.md +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/assets/cli-help.png +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/assets/cli-output.png +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/bt-cli.spec +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/bt_entry.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/epml-clients-server-side-filters-plan.md +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/epml-implementation-plan.md +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/pf-implementation-plan.md +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/scripts/bt_entry.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/scripts/pf_onboard.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/scripts/sync-package-data.sh +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/cli.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/commands/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/commands/configure.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/core/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/core/auth.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/core/client.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/core/config_file.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/core/csv_utils.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/core/errors.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/core/rest_debug.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/data/CLAUDE.md +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/data/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/data/skills/bt/SKILL.md +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/data/skills/epml/SKILL.md +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/data/skills/epmw/SKILL.md +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/data/skills/pf/SKILL.md +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/data/skills/pra/SKILL.md +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/data/skills/pws/SKILL.md +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/data/skills/secrets/SKILL.md +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/data/skills/session-analysis/SKILL.md +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/entitle/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/entitle/client/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/entitle/commands/accounts.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/entitle/commands/applications.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/entitle/commands/auth.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/entitle/commands/integrations.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/entitle/commands/policies.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/entitle/commands/roles.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/entitle/commands/users.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/entitle/commands/workflows.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/entitle/models/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/entitle/models/bundle.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/entitle/models/common.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/entitle/models/integration.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/entitle/models/permission.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/entitle/models/policy.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/entitle/models/resource.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/entitle/models/role.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/entitle/models/user.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/entitle/models/workflow.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epml/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epml/client/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epml/client/base.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epml/commands/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epml/commands/audit.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epml/commands/auth.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epml/commands/client_pkg.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epml/commands/clients.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epml/commands/external_apis.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epml/commands/hosts.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epml/commands/iolog.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epml/commands/license.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epml/commands/quick.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epml/commands/rbp_entitlement.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epml/commands/rbp_policy.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epml/commands/rbp_roles.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epml/commands/rbp_tests.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epml/commands/rbp_tx.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epml/commands/settings.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epml/commands/siems.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epml/commands/users.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epml/models/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epmw/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epmw/client/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epmw/client/base.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epmw/commands/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epmw/commands/audits.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epmw/commands/auth.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epmw/commands/computers.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epmw/commands/events.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epmw/commands/groups.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epmw/commands/requests.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epmw/commands/roles.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epmw/commands/tasks.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epmw/commands/users.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/epmw/models/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pf/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pf/client/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pf/client/base.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pf/commands/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pf/commands/auth.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pf/commands/machines.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pf/commands/tokens.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pf/commands/user.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pf/commands/users.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pra/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pra/client/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pra/client/base.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pra/commands/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pra/commands/auth.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pra/commands/import_export.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pra/commands/jump_groups.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pra/commands/jumpoints.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pra/commands/policies.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pra/commands/teams.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pra/models/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pra/models/common.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pra/models/group_policy.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pra/models/jump_client.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pra/models/jump_group.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pra/models/jump_item.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pra/models/jumpoint.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pra/models/team.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pra/models/user.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pra/models/vault.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pws/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pws/client/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pws/client/base.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pws/client/beyondinsight.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pws/client/passwordsafe.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pws/commands/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pws/commands/audit.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pws/commands/auth.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pws/commands/config.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pws/commands/credentials.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pws/commands/directories.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pws/commands/import_export.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pws/commands/platforms.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pws/commands/users.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pws/config.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pws/models/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pws/models/account.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pws/models/asset.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pws/models/common.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/pws/models/system.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/secrets/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/secrets/client/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/secrets/client/base.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/secrets/commands/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/secrets/commands/_hints.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/secrets/commands/auth.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/secrets/commands/dynamic.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/secrets/commands/folders.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/secrets/commands/integrations.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/secrets/commands/leases.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/secrets/commands/static.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/src/bt_cli/secrets/models/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/core/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/core/test_auth.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/core/test_config.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/core/test_config_file.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/core/test_errors.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/core/test_output.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/core/test_rest_debug.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/entitle/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/entitle-smoke-test.sh +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/epml/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/epml/test_client.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/epmw/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/epmw/test_client.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/epmw/test_commands.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/epmw-quick-test-plan.md +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/fixtures/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/fixtures/responses.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/integration/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/integration/helpers.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/integration/test_entitle_integration.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/integration/test_epmw_integration.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/integration/test_epmw_lifecycle.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/integration/test_pra_integration.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/integration/test_pra_lifecycle.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/integration/test_pws_integration.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/integration/test_pws_lifecycle.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/pf/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/pf/test_client.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/pra/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/pra/test_client.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/pra/test_commands.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/pra-smoke-test.sh +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/pra-test-plan.md +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/pws/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/pws/test_client.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/pws/test_quick_sessions.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/pws-quick-test-plan.md +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/pws-smoke-test.sh +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/secrets/__init__.py +0 -0
- {bt_cli-0.4.70 → bt_cli-0.4.72}/tests/secrets/test_client.py +0 -0
|
@@ -100,7 +100,9 @@ jobs:
|
|
|
100
100
|
pip install -e ".[test]"
|
|
101
101
|
|
|
102
102
|
- name: Run integration tests
|
|
103
|
-
|
|
103
|
+
# --integration is the opt-in these tests now require; without it they
|
|
104
|
+
# skip, since credentials alone are no longer the trigger.
|
|
105
|
+
run: pytest tests/integration -v -m integration --integration
|
|
104
106
|
env:
|
|
105
107
|
BT_PWS_API_URL: ${{ secrets.BT_PWS_API_URL }}
|
|
106
108
|
BT_PWS_CLIENT_ID: ${{ secrets.BT_PWS_CLIENT_ID }}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# BT-CLI
|
|
2
2
|
|
|
3
|
-
BeyondTrust Platform CLI for Password Safe, Entitle, PRA, EPM Windows, EPM Linux, and the BeyondTrust Secrets API. **Version: 0.4.
|
|
3
|
+
BeyondTrust Platform CLI for Password Safe, Entitle, PRA, EPM Windows, EPM Linux, and the BeyondTrust Secrets API. **Version: 0.4.72**
|
|
4
4
|
|
|
5
5
|
## Setup
|
|
6
6
|
|
|
@@ -42,7 +42,7 @@ Use these slash commands for detailed product guidance:
|
|
|
42
42
|
| Cross-product | `bt quick` | `pasm-onboard`, `pasm-offboard`, `pasm-search` |
|
|
43
43
|
| Password Safe | `bt pws` | `systems`, `accounts`, `credentials`, `sessions`, `secrets`, `audit`, `quick` |
|
|
44
44
|
| PRA | `bt pra` | `jump-items`, `vault`, `jump-groups`, `quick` |
|
|
45
|
-
| Entitle | `bt entitle` | `integrations`, `resources`, `bundles`, `permissions` |
|
|
45
|
+
| Entitle | `bt entitle` | `integrations`, `resources`, `bundles`, `permissions`, `jit`, `audit` |
|
|
46
46
|
| EPM Windows | `bt epmw` | `computers`, `groups`, `policies`, `requests`, `quick` |
|
|
47
47
|
| EPM Linux | `bt epml` | `rbp` (cmdgrps/hostgrps/usergrps/tmdategrps/roles/policy/tests/tx), `settings`, `users`, `audit`, `siems`, `quick` |
|
|
48
48
|
| Secrets API | `bt secrets` | `folders`, `static`, `dynamic` (incl. `generate`), `leases`, `integrations` |
|
|
@@ -82,7 +82,7 @@ PASSWORD=$(bt pws quick checkout -s server -a admin --raw)
|
|
|
82
82
|
- PRA K8s tunnels: Require Linux jumpoint
|
|
83
83
|
- PWS assets: Must create via workgroup endpoint
|
|
84
84
|
- ECM integration: PWS system name must match PRA jump item name
|
|
85
|
-
-
|
|
85
|
+
- **`-o json` is pure JSON on stdout — in every product.** All human status/progress banners *and interactive prompts* go to **stderr** (`core/output.py` `err_console`; `typer.confirm`/`typer.prompt` are called with `err=True`), so `... -o json | jq` and `json.load(stdin)` are safe even for `quick` workflow commands that narrate their steps. Enforced by static guards in `tests/core/test_json_stdout_purity.py` — when adding a command, use `print_success`/`print_warning`/`print_info` or `err_console.print` for anything that isn't the command's data. Consequence: piping *table* output to a file drops the narration (it's on stderr) — that's intended. PRA API 4xx `errors` objects are surfaced field-by-field, not collapsed to "Validation Failed".
|
|
86
86
|
- **PRA tunnel create gates fields by `tunnel_type`**: tcp→`--ports`, mssql→`--username`/`--database`, k8s→`--url`/`--ca-cert`. **psql/mysql are created BARE** — the public Config API has no write path for their username/database (POST rejects, PATCH no-ops, PUT 405s) though GET shows them; CLI warns + ignores those flags. Set in `/login` UI.
|
|
87
87
|
- **PRA `export jump-items` defaults to a REAL export** (all types: shell/rdp/vnc/web/tunnel, full type-specific columns) that round-trips through `import jump-items`; `--sample` gives the blank template. `bt pra jump-items web create` exists.
|
|
88
88
|
- **PRA vault `associate -t protocol_tunnel` is MSSQL-only** (live-verified): server rejects psql/mysql/tcp/k8s with "Only SQL Server Tunnels are allowed" — use tag/criteria association for those. **TCP `--ports` needs listen;destination *pairs*** (even count; odd → 400 "number of parameters is invalid"). Jumpoint listing is `bt pra jumpoints list` (plural).
|
|
@@ -92,6 +92,8 @@ PASSWORD=$(bt pws quick checkout -s server -a admin --raw)
|
|
|
92
92
|
- **PWS session recording download = headless replay under `/pbsm/replay`** (there is **no** video-export API — `?video/?mp4/?webm` return JSON, not media). Flow: `GET /Sessions/{id}` for `Token`+`RecordKey`+`Protocol` → `POST /pbsm/replay` `{id:Token, record_key, protocol:"RDP"|"SSH", headless:true}` → `PUT /pbsm/replay/{rid}` `{offset:<ms>}` (seek) → `GET /pbsm/replay/{rid}?png=<scale>` (one PNG per offset) → `DELETE /pbsm/replay/{rid}`. **Warm-up:** the first `PUT` 404s (`"no such file or directory"`) until the renderer loads — retry ~0.5s. The seek response is state `{offset, next, eof, duration}`; **`next` = next recorded frame's offset**, enabling event-driven stepping. **`bt pws sessions analyze <id>` (or `--dir`)** preps a replay dump for LLM analysis without calling an LLM: reads `index.json`, selects **keyframes** (RDP frames whose PNG byte-size changed ≥`--threshold`%, dependency-free — no Pillow, UNION the frame that renders each **typed keystroke burst**, anchored via the recording `StartTime` → each keyframe tagged `screen-change`/`after-typing` with the `typed` text, so the model reads targeted spots not every frame), and prints a token/USD **cost estimate** (`--model opus|sonnet|haiku`) for focused (keyframes) vs all-frames; writes `analysis.json`. SSH → recommends the transcript over frames. Consumed by the `/session-analysis` skill (interactive; the model reads only the keyframes). Surfaced as `bt pws sessions replay <id>`. **Default = even coverage + dedup**: ~60 evenly-spaced frames across the whole session (`--frames N`) with consecutive-identical frames skipped (`--dedup`, sha1-based) — because event-driven over-samples RDP (it records many deltas as the screen paints, so `next`-stepping clusters at the login and burns the frame budget before covering the timeline). Modes are mutually exclusive: `--frames` (default) / `--fps` / `--interval` / `--all-frames` (event-driven forensic). Also `--from/--to`, `--scale`, `--max-frames`, `--no-dedup`; `--mp4` stitches via ffmpeg, else prints the command + install hint. Always `DELETE`s the replay in `finally`.
|
|
93
93
|
- Entitle permissions: Use server-side filters (`--resource`, `--user`, `--integration`) - dataset can be 20k+ records
|
|
94
94
|
- Entitle users: Use `--all` flag only when needed - default fetches first page only
|
|
95
|
+
- **Entitle tokens are region-bound**: a token minted in one region 401s `"The token received doesn't exist"` on the others. Servers: `api.entitle.io`, `api.us.entitle.io`, `api.ca.entitle.io` — set `BT_ENTITLE_API_URL`/`entitle.api_url` to the tenant's region (that misleading 401 also = wrong-region config, not just a dead token). **No `GET /users/{id}`** — `users get` resolves via `list_users(search=...)`.
|
|
96
|
+
- **Entitle `jit` / `audit`** (spec-derived, live-verified): `bt entitle jit list` wraps `GET /eligibleJitAccess` — with an org token `--user` is **required** (else 400), and the response is `{roles,bundles,pagination}` not the `result` envelope. `bt entitle audit search` wraps `POST /auditLogs/search` (Experimental) — **date window must be < 24h** (server 400s on ≥24h; CLI enforces client-side, default `--hours 24`), no pagination (returns all rows in range), each row's context is one of `integration`/`idp`/`ticket`/`user`. Full spec at `/projects/reference/entitle-api-reference.openapi.json`.
|
|
95
97
|
- Windows builds: Rich must be pinned to `<14.0.0` (Rich 14 has PyInstaller unicode issues)
|
|
96
98
|
- **EPML URL transform**: spec paths start with `/api/...` but real URL is `https://api.beyondtrust.io/site/<site-id>/epm/linux/<spec-path-minus-/api/>`. Encapsulated in client `_build_url` — never bypass it.
|
|
97
99
|
- **EPML two authorizers**: spec claims `nomine-authorizer` (PAT) globally but the gateway has IAM-only routes (return 403 with AWS-flavored body). PAT cannot reach those. Documented per-op in `src/bt_cli/data/skills/epml/SKILL.md`. Where `/v6/pbul/rbp/...` exists alongside legacy `/pbul/{hostid}/rbp/...`, prefer legacy — v6 is mostly IAM-only.
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>BT-CLI Demo Flow — Working Commands (pf922288)</title>
|
|
6
|
+
<style>
|
|
7
|
+
:root{
|
|
8
|
+
--bg:#0d1117;
|
|
9
|
+
--panel:#151b23;
|
|
10
|
+
--border:#2a323d;
|
|
11
|
+
--border-strong:#3a4452;
|
|
12
|
+
--text:#e6edf3;
|
|
13
|
+
--text-secondary:#9aa7b5;
|
|
14
|
+
--text-muted:#6b7684;
|
|
15
|
+
--accent:#3ea6ff;
|
|
16
|
+
--green:#3fb950;
|
|
17
|
+
--amber:#d29922;
|
|
18
|
+
--tag-bg:#1c2733;
|
|
19
|
+
--mono: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
|
|
20
|
+
--sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
21
|
+
}
|
|
22
|
+
*{box-sizing:border-box}
|
|
23
|
+
body{background:var(--bg);color:var(--text);font-family:var(--sans);font-size:17px;margin:0;padding:38px 22px 72px}
|
|
24
|
+
.wrap{max-width:960px;margin:0 auto}
|
|
25
|
+
h1{font-size:30px;font-weight:600;margin:0 0 6px}
|
|
26
|
+
.sub{font-size:16px;color:var(--text-muted);margin:0 0 24px;line-height:1.5}
|
|
27
|
+
.legend{display:flex;flex-wrap:wrap;gap:18px;font-size:15px;color:var(--text-secondary);margin:0 0 26px;padding:15px 18px;border:1px solid var(--border);border-radius:8px;background:var(--panel)}
|
|
28
|
+
.legend span{display:inline-flex;align-items:center;gap:7px}
|
|
29
|
+
.dot{width:10px;height:10px;border-radius:50%}
|
|
30
|
+
.dot.v{background:var(--green)} .dot.s{background:var(--amber)} .dot.r{background:var(--accent)}
|
|
31
|
+
.refbox{border:1px solid var(--border-strong);border-radius:8px;padding:16px 18px;margin:0 0 30px;background:#0a0e14;font-family:var(--mono);font-size:15px;line-height:1.85;color:var(--text-secondary)}
|
|
32
|
+
.refbox b{color:var(--text)}
|
|
33
|
+
.step{font-size:14px;text-transform:uppercase;letter-spacing:.05em;color:var(--text-muted);margin:32px 0 12px;font-weight:600}
|
|
34
|
+
.step:first-of-type{margin-top:0}
|
|
35
|
+
.row{border:1px solid var(--border);border-radius:8px;padding:16px 18px;margin-bottom:12px;background:var(--panel)}
|
|
36
|
+
.row.state{border-color:#4a3d1a}
|
|
37
|
+
.hd{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px;gap:10px}
|
|
38
|
+
.lbl{font-size:17px;font-weight:500;color:var(--text)}
|
|
39
|
+
.tags{display:flex;gap:7px;flex-shrink:0}
|
|
40
|
+
.tag{font-size:13px;font-weight:600;letter-spacing:.03em;color:var(--accent);background:var(--tag-bg);border:1px solid var(--border-strong);border-radius:4px;padding:3px 10px;white-space:nowrap}
|
|
41
|
+
.tag.v{color:var(--green);border-color:#264a30}
|
|
42
|
+
.tag.s{color:var(--amber);border-color:#4a3d1a}
|
|
43
|
+
.cmdrow{display:flex;align-items:stretch;gap:9px;margin-bottom:7px}
|
|
44
|
+
.cmd{flex:1;font-family:var(--mono);font-size:15.5px;line-height:1.6;color:#c9e8ff;background:#0a0e14;border:1px solid var(--border);border-radius:6px;padding:11px 14px;overflow-x:auto;white-space:pre}
|
|
45
|
+
.cpy{flex-shrink:0;width:44px;font-size:18px;display:flex;align-items:center;justify-content:center;border-radius:6px;border:1px solid var(--border-strong);background:transparent;cursor:pointer;color:var(--text-secondary);transition:background .12s,color .12s}
|
|
46
|
+
.cpy:hover{background:var(--tag-bg);color:var(--text)}
|
|
47
|
+
.cpy.copied{color:var(--green);border-color:var(--green)}
|
|
48
|
+
.note{font-size:15px;color:var(--text-muted);margin-top:8px;line-height:1.6}
|
|
49
|
+
.note b{color:var(--text-secondary)}
|
|
50
|
+
.note code{font-family:var(--mono);font-size:14px;color:#c9e8ff}
|
|
51
|
+
.flag{border:1px dashed var(--border-strong);border-radius:8px;padding:16px 18px;font-size:16px;color:var(--text-secondary);line-height:1.7}
|
|
52
|
+
.flag b{color:var(--text)}
|
|
53
|
+
</style>
|
|
54
|
+
</head>
|
|
55
|
+
<body>
|
|
56
|
+
<div class="wrap">
|
|
57
|
+
<h1>BT-CLI demo flow — working commands</h1>
|
|
58
|
+
<p class="sub">Verified live against tenant pf922288, 01 Jul 2026. Real IDs baked in. Ephemeral IDs (request/session) are captured at runtime, not hardcoded.</p>
|
|
59
|
+
|
|
60
|
+
<div class="legend">
|
|
61
|
+
<span><span class="dot r"></span>tag = docs bucket (BT-CLI / API)</span>
|
|
62
|
+
<span><span class="dot v"></span><b style="color:var(--green)">VERIFIED</b> — ran live, non-destructive, safe to demo</span>
|
|
63
|
+
<span><span class="dot s"></span><b style="color:var(--amber)">STATE-CHANGING</b> — correct syntax, changes/creates/deletes; run deliberately</span>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<div class="refbox">
|
|
67
|
+
<b>Real values (pf922288)</b><br>
|
|
68
|
+
system ad.upm.academy .......... ID <b>19</b> (Active Directory)<br>
|
|
69
|
+
system linhost2 ................ ID <b>6</b> (Linux — used for the create demo)<br>
|
|
70
|
+
account svc_app ................. ID <b>18119</b> (exists; credential test PASSED)<br>
|
|
71
|
+
safe GitHub - BTSolution-Engineering ... <b>9b56a95b-0a8a-4be6-de3c-08dd5118ea68</b><br>
|
|
72
|
+
folder IaC-Demo ............... <b>bec2694b-994a-47cd-07c2-08de5e7a5e2d</b><br>
|
|
73
|
+
group SecretsSafe_Access ..... ID <b>9</b> (Dev_Ansible=10, API_Users=11)<br>
|
|
74
|
+
secret path ................... <b>GitHub - BTSolution-Engineering/IaC-Demo/AWS</b><br>
|
|
75
|
+
perm flags ..................... None, Read, Create, Edit, Delete, Share, Manage
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<div class="step">Setup — run once per session</div>
|
|
79
|
+
<div class="row">
|
|
80
|
+
<div class="hd"><span class="lbl">Session setup</span><span class="tags"><span class="tag v">VERIFIED</span></span></div>
|
|
81
|
+
<div class="cmdrow"><div class="cmd" id="c0">source .venv/bin/activate</div><button class="cpy" onclick="cp('c0',this)">⧉</button></div>
|
|
82
|
+
<div class="cmdrow"><div class="cmd" id="c2">bt whoami</div><button class="cpy" onclick="cp('c2',this)">⧉</button></div>
|
|
83
|
+
<div class="note">Credentials load from <b>~/.bt-cli/config.yaml</b> — no <code>source .env</code> needed. <code>bt whoami</code> should show Password Safe = Connected (user NH-Ansible, ID 120).</div>
|
|
84
|
+
</div>
|
|
85
|
+
<div class="row">
|
|
86
|
+
<div class="hd"><span class="lbl">Show the underlying REST call (demo toggle)</span><span class="tags"><span class="tag">API</span><span class="tag v">VERIFIED</span></span></div>
|
|
87
|
+
<div class="cmdrow"><div class="cmd" id="cR">bt --show-rest pws accounts get 18119</div><button class="cpy" onclick="cp('cR',this)">⧉</button></div>
|
|
88
|
+
<div class="note">Prepend <b><code>bt --show-rest</code></b> to <b>any</b> command to print the REST request + response panels (method, URL, headers, body) — auth headers auto-redacted. Great for proving each command hits the public API. Also works as env: <code>export BT_SHOW_REST=1</code>.</div>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
<div class="step">1 · Create account or secret — BT-CLI</div>
|
|
92
|
+
<div class="row state">
|
|
93
|
+
<div class="hd"><span class="lbl">Create managed account (local, on a non-directory system)</span><span class="tags"><span class="tag">BT-CLI</span><span class="tag s">STATE</span></span></div>
|
|
94
|
+
<div class="cmdrow"><div class="cmd" id="c3">bt pws accounts create --system 6 --name "svc_demo01" --password 'PASSWORD' --change-frequency xdays --change-days 1 --change-time 02:00</div><button class="cpy" onclick="cp('c3',this)">⧉</button></div>
|
|
95
|
+
<div class="note"><b>--system takes the integer ID</b> (6 = linhost2, Linux). Sub a real PASSWORD. Note: directory accounts (like svc_app on ad.upm.academy) require a UserPrincipalName and are onboarded via <b>discovery</b>, not this manual create — so the create demo uses a local system.</div>
|
|
96
|
+
</div>
|
|
97
|
+
<div class="row state">
|
|
98
|
+
<div class="hd"><span class="lbl">Create secret (Secrets Safe)</span><span class="tags"><span class="tag">BT-CLI</span><span class="tag s">STATE</span></span></div>
|
|
99
|
+
<div class="cmdrow"><div class="cmd" id="c4">bt pws secrets secrets create --folder bec2694b-994a-47cd-07c2-08de5e7a5e2d --title "db-admin-demo" --username admin --password 'PASSWORD'</div><button class="cpy" onclick="cp('c4',this)">⧉</button></div>
|
|
100
|
+
<div class="note">--folder is a folder <b>GUID</b> (IaC-Demo). Creates a real secret in that folder.</div>
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
<div class="step">2 · Assign policy, ownership, permissions — API</div>
|
|
104
|
+
<div class="row">
|
|
105
|
+
<div class="hd"><span class="lbl">List permission flags</span><span class="tags"><span class="tag">API</span><span class="tag v">VERIFIED</span></span></div>
|
|
106
|
+
<div class="cmdrow"><div class="cmd" id="c5">bt pws secrets safes permissions-options</div><button class="cpy" onclick="cp('c5',this)">⧉</button></div>
|
|
107
|
+
</div>
|
|
108
|
+
<div class="row">
|
|
109
|
+
<div class="hd"><span class="lbl">Show current grants on a safe</span><span class="tags"><span class="tag">API</span><span class="tag v">VERIFIED</span></span></div>
|
|
110
|
+
<div class="cmdrow"><div class="cmd" id="c6">bt pws secrets safes permissions 9b56a95b-0a8a-4be6-de3c-08dd5118ea68</div><button class="cpy" onclick="cp('c6',this)">⧉</button></div>
|
|
111
|
+
</div>
|
|
112
|
+
<div class="row state">
|
|
113
|
+
<div class="hd"><span class="lbl">Grant permissions to a group</span><span class="tags"><span class="tag">API</span><span class="tag s">STATE</span></span></div>
|
|
114
|
+
<div class="cmdrow"><div class="cmd" id="c7">bt pws secrets safes grant 9b56a95b-0a8a-4be6-de3c-08dd5118ea68 --group 9 --permissions "Read, Create, Edit, Delete, Share, Manage"</div><button class="cpy" onclick="cp('c7',this)">⧉</button></div>
|
|
115
|
+
<div class="note"><b>Flags aren't freely combinable</b> — the server validates the whole set. Valid combos (verified live): <code>Read</code> · <code>Read, Edit</code> · <code>Read, Create, Edit, Delete, Share</code> · <code>Read, Create, Edit, Delete, Share, Manage</code> (full owner). <b>Manage only works with the full set</b> — <code>Read,Edit,Manage</code> is rejected as an "Invalid permission combination".</div>
|
|
116
|
+
</div>
|
|
117
|
+
<div class="row state">
|
|
118
|
+
<div class="hd"><span class="lbl">Revoke all permissions for a group</span><span class="tags"><span class="tag">API</span><span class="tag s">STATE</span></span></div>
|
|
119
|
+
<div class="cmdrow"><div class="cmd" id="c8">bt pws secrets safes revoke 9b56a95b-0a8a-4be6-de3c-08dd5118ea68 --group 9</div><button class="cpy" onclick="cp('c8',this)">⧉</button></div>
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
<div class="step">3 · Trigger verification and rotation — BT-CLI</div>
|
|
123
|
+
<div class="row">
|
|
124
|
+
<div class="hd"><span class="lbl">Verify stored credential (non-destructive)</span><span class="tags"><span class="tag">BT-CLI</span><span class="tag v">VERIFIED</span></span></div>
|
|
125
|
+
<div class="cmdrow"><div class="cmd" id="c9">bt pws quick test -s "ad.upm.academy" -a svc_app</div><button class="cpy" onclick="cp('c9',this)">⧉</button></div>
|
|
126
|
+
<div class="note">Ran live → <b>Credential test PASSED for svc_app@ad.upm.academy</b>. Quick commands accept the system <b>name</b>.</div>
|
|
127
|
+
</div>
|
|
128
|
+
<div class="row state">
|
|
129
|
+
<div class="hd"><span class="lbl">Rotate now</span><span class="tags"><span class="tag">BT-CLI</span><span class="tag s">STATE</span></span></div>
|
|
130
|
+
<div class="cmdrow"><div class="cmd" id="c10">bt pws quick rotate -s "ad.upm.academy" -a svc_app</div><button class="cpy" onclick="cp('c10',this)">⧉</button></div>
|
|
131
|
+
<div class="note">Fires the platform rotation/propagation. Changes the live password.</div>
|
|
132
|
+
</div>
|
|
133
|
+
|
|
134
|
+
<div class="step">4 · Submit and approve access request — API</div>
|
|
135
|
+
<div class="row state">
|
|
136
|
+
<div class="hd"><span class="lbl">Submit checkout request (capture the request ID)</span><span class="tags"><span class="tag">API</span><span class="tag s">STATE</span></span></div>
|
|
137
|
+
<div class="cmdrow"><div class="cmd" id="c11">bt pws credentials checkout --system "ad.upm.academy" --account "svc_app" --reason "demo" -o json</div><button class="cpy" onclick="cp('c11',this)">⧉</button></div>
|
|
138
|
+
<div class="note">Returns a <b>RequestID</b> in the JSON. <b>Requires the account to be under an access policy with a schedule your identity can use</b> — otherwise you get <code>"Could not find a schedule to use with this release request."</code> If so, use <b>quick checkout</b> (step 5), which lists only accounts you can actually request.</div>
|
|
139
|
+
</div>
|
|
140
|
+
<div class="row">
|
|
141
|
+
<div class="hd"><span class="lbl">List pending requests</span><span class="tags"><span class="tag">API</span><span class="tag v">VERIFIED</span></span></div>
|
|
142
|
+
<div class="cmdrow"><div class="cmd" id="c12">bt pws credentials list --status pending</div><button class="cpy" onclick="cp('c12',this)">⧉</button></div>
|
|
143
|
+
<div class="note">Ran live (currently "No requests found"). Also: <code>--all</code>, <code>--status approved</code>.</div>
|
|
144
|
+
</div>
|
|
145
|
+
<div class="row state">
|
|
146
|
+
<div class="hd"><span class="lbl">Approve a pending request</span><span class="tags"><span class="tag">API</span><span class="tag s">STATE</span></span></div>
|
|
147
|
+
<div class="cmdrow"><div class="cmd" id="c13">bt pws credentials approve REQUEST_ID</div><button class="cpy" onclick="cp('c13',this)">⧉</button></div>
|
|
148
|
+
<div class="note">REQUEST_ID is the integer from the checkout above / from <code>credentials list</code>.</div>
|
|
149
|
+
</div>
|
|
150
|
+
|
|
151
|
+
<div class="step">5 · Retrieve or broker access — API</div>
|
|
152
|
+
<div class="row state">
|
|
153
|
+
<div class="hd"><span class="lbl">Quick checkout — the easy path (request + approve + retrieve)</span><span class="tags"><span class="tag">BT-CLI</span><span class="tag s">STATE</span></span></div>
|
|
154
|
+
<div class="cmdrow"><div class="cmd" id="c14b">bt pws quick checkout</div><button class="cpy" onclick="cp('c14b',this)">⧉</button></div>
|
|
155
|
+
<div class="note"><b>Run with no args to go interactive</b> — it lists only the accounts you're actually allowed to request, you pick one, and it does request → approve → show password in one step. This sidesteps the access-policy/schedule issue above.</div>
|
|
156
|
+
</div>
|
|
157
|
+
<div class="row state">
|
|
158
|
+
<div class="hd"><span class="lbl">Quick checkout — scripted, to a variable</span><span class="tags"><span class="tag">BT-CLI</span><span class="tag s">STATE</span></span></div>
|
|
159
|
+
<div class="cmdrow"><div class="cmd" id="c14">PW=$(bt pws quick checkout -s "SYSTEM" -a "ACCOUNT" --raw)</div><button class="cpy" onclick="cp('c14',this)">⧉</button></div>
|
|
160
|
+
<div class="note">Same flow, non-interactive. Fill SYSTEM/ACCOUNT with one shown in the interactive list. <code>echo "$PW"</code> to view.</div>
|
|
161
|
+
</div>
|
|
162
|
+
<div class="row">
|
|
163
|
+
<div class="hd"><span class="lbl">Get a Secrets Safe secret value</span><span class="tags"><span class="tag">API</span><span class="tag v">VERIFIED</span></span></div>
|
|
164
|
+
<div class="cmdrow"><div class="cmd" id="c15">bt pws quick get-secret "GitHub - BTSolution-Engineering/IaC-Demo/AWS" --raw</div><button class="cpy" onclick="cp('c15',this)">⧉</button></div>
|
|
165
|
+
<div class="note">Path is <b>Safe/Folder/SecretName</b>. Resolved live to Secret "AWS". Drop <code>--raw</code> for a table view.</div>
|
|
166
|
+
</div>
|
|
167
|
+
|
|
168
|
+
<div class="step">6 · Revoke access — API</div>
|
|
169
|
+
<div class="row state">
|
|
170
|
+
<div class="hd"><span class="lbl">Check in + rotate on release</span><span class="tags"><span class="tag">API</span><span class="tag s">STATE</span></span></div>
|
|
171
|
+
<div class="cmdrow"><div class="cmd" id="c16">bt pws credentials checkin REQUEST_ID --rotate</div><button class="cpy" onclick="cp('c16',this)">⧉</button></div>
|
|
172
|
+
<div class="note">REQUEST_ID is an integer (from step 4/5).</div>
|
|
173
|
+
</div>
|
|
174
|
+
<div class="row">
|
|
175
|
+
<div class="hd"><span class="lbl">List active sessions (capture the session ID)</span><span class="tags"><span class="tag">API</span><span class="tag v">VERIFIED</span></span></div>
|
|
176
|
+
<div class="cmdrow"><div class="cmd" id="c17">bt pws sessions list --active</div><button class="cpy" onclick="cp('c17',this)">⧉</button></div>
|
|
177
|
+
<div class="note">Ran live (currently "No sessions found"). The <b>ID</b> column is the SESSION_ID (integer) below.</div>
|
|
178
|
+
</div>
|
|
179
|
+
<div class="row state">
|
|
180
|
+
<div class="hd"><span class="lbl">Suspend session (resumable)</span><span class="tags"><span class="tag">API</span><span class="tag s">STATE</span></span></div>
|
|
181
|
+
<div class="cmdrow"><div class="cmd" id="c18">bt pws sessions suspend SESSION_ID</div><button class="cpy" onclick="cp('c18',this)">⧉</button></div>
|
|
182
|
+
</div>
|
|
183
|
+
<div class="row state">
|
|
184
|
+
<div class="hd"><span class="lbl">Terminate session (permanent)</span><span class="tags"><span class="tag">API</span><span class="tag s">STATE</span></span></div>
|
|
185
|
+
<div class="cmdrow"><div class="cmd" id="c19">bt pws sessions terminate SESSION_ID --force</div><button class="cpy" onclick="cp('c19',this)">⧉</button></div>
|
|
186
|
+
</div>
|
|
187
|
+
|
|
188
|
+
<div class="step">7 · Offboard the account — BT-CLI</div>
|
|
189
|
+
<div class="row state">
|
|
190
|
+
<div class="hd"><span class="lbl">Offboard system (removes system + accounts + asset)</span><span class="tags"><span class="tag">BT-CLI</span><span class="tag s">STATE</span></span></div>
|
|
191
|
+
<div class="cmdrow"><div class="cmd" id="c20">bt pws quick offboard -s "ad.upm.academy"</div><button class="cpy" onclick="cp('c20',this)">⧉</button></div>
|
|
192
|
+
<div class="note"><b>Destructive:</b> deletes ad.upm.academy (ID 19) and all its accounts incl. svc_app. Don't run unless you mean it.</div>
|
|
193
|
+
</div>
|
|
194
|
+
|
|
195
|
+
<div class="step">8 · Export audit evidence — API</div>
|
|
196
|
+
<div class="row">
|
|
197
|
+
<div class="hd"><span class="lbl">Full evidence export (JSON)</span><span class="tags"><span class="tag">API</span><span class="tag v">VERIFIED</span></span></div>
|
|
198
|
+
<div class="cmdrow"><div class="cmd" id="c21">bt pws audit export --file ~/evidence.json</div><button class="cpy" onclick="cp('c21',this)">⧉</button></div>
|
|
199
|
+
<div class="note">Defaults to BOTH requests and sessions when no flag is given. <b>--file must be a writable path</b> — the CLI fetches the evidence then writes the file, so a "Permission denied" here means the directory isn't writable (use <code>~/</code> or <code>/tmp</code>, not a read-only checkout dir).</div>
|
|
200
|
+
</div>
|
|
201
|
+
<div class="row">
|
|
202
|
+
<div class="hd"><span class="lbl">Approved requests (CSV)</span><span class="tags"><span class="tag">API</span><span class="tag v">VERIFIED</span></span></div>
|
|
203
|
+
<div class="cmdrow"><div class="cmd" id="c22">bt pws audit export --requests --status approved --file ~/svc_app-evidence.csv --format csv</div><button class="cpy" onclick="cp('c22',this)">⧉</button></div>
|
|
204
|
+
</div>
|
|
205
|
+
<div class="row">
|
|
206
|
+
<div class="hd"><span class="lbl">Session evidence (CSV)</span><span class="tags"><span class="tag">API</span><span class="tag v">VERIFIED</span></span></div>
|
|
207
|
+
<div class="cmdrow"><div class="cmd" id="c23">bt pws audit export --sessions --file ~/sessions.csv --format csv</div><button class="cpy" onclick="cp('c23',this)">⧉</button></div>
|
|
208
|
+
</div>
|
|
209
|
+
|
|
210
|
+
<div class="step">9-10 · Integrations + API auth — out of scope</div>
|
|
211
|
+
<div class="flag">
|
|
212
|
+
<b>ServiceNow / SailPoint</b> and <b>scoped-token API auth</b> (certs, rate limits, bulk ops) are UI/console config, not bt-cli commands — nothing to copy/paste here.
|
|
213
|
+
</div>
|
|
214
|
+
</div>
|
|
215
|
+
|
|
216
|
+
<script>
|
|
217
|
+
function cp(id, btn){
|
|
218
|
+
const text = document.getElementById(id).innerText;
|
|
219
|
+
const done = () => { const o = btn.textContent; btn.textContent='✓'; btn.classList.add('copied'); setTimeout(()=>{btn.textContent=o;btn.classList.remove('copied');},1200); };
|
|
220
|
+
if (navigator.clipboard && navigator.clipboard.writeText) navigator.clipboard.writeText(text).then(done).catch(()=>fallbackCopy(text,done));
|
|
221
|
+
else fallbackCopy(text,done);
|
|
222
|
+
}
|
|
223
|
+
function fallbackCopy(text, done){
|
|
224
|
+
const ta=document.createElement('textarea'); ta.value=text; ta.style.position='fixed'; ta.style.opacity='0';
|
|
225
|
+
document.body.appendChild(ta); ta.select(); try{document.execCommand('copy');}catch(e){} document.body.removeChild(ta); done();
|
|
226
|
+
}
|
|
227
|
+
</script>
|
|
228
|
+
</body>
|
|
229
|
+
</html>
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "bt-cli"
|
|
7
|
-
version = "0.4.
|
|
7
|
+
version = "0.4.72"
|
|
8
8
|
description = "BeyondTrust Platform CLI (unofficial) - Password Safe, Entitle, PRA, EPM"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -75,3 +75,7 @@ packages = ["src/bt_cli"]
|
|
|
75
75
|
[tool.pytest.ini_options]
|
|
76
76
|
asyncio_mode = "auto"
|
|
77
77
|
testpaths = ["tests"]
|
|
78
|
+
# Test the working tree, not whatever bt_cli happens to be installed in
|
|
79
|
+
# site-packages. Without this a non-editable `pip install bt-cli` shadows
|
|
80
|
+
# src/ and the suite silently tests a stale copy at the same version number.
|
|
81
|
+
pythonpath = ["src"]
|
|
@@ -221,7 +221,7 @@ def clear_learnings(
|
|
|
221
221
|
bt learn clear --force
|
|
222
222
|
"""
|
|
223
223
|
if not force:
|
|
224
|
-
if not typer.confirm("Clear all learnings?"):
|
|
224
|
+
if not typer.confirm("Clear all learnings?", err=True):
|
|
225
225
|
console.print("[yellow]Cancelled[/yellow]")
|
|
226
226
|
raise typer.Exit(0)
|
|
227
227
|
|
|
@@ -11,7 +11,7 @@ from rich.console import Console
|
|
|
11
11
|
from rich.panel import Panel
|
|
12
12
|
from rich.table import Table
|
|
13
13
|
|
|
14
|
-
from ..core.output import print_api_error, print_error, print_success, print_warning
|
|
14
|
+
from ..core.output import err_console, print_api_error, print_error, print_success, print_warning
|
|
15
15
|
from ..core.prompts import prompt_if_missing, prompt_from_list
|
|
16
16
|
|
|
17
17
|
|
|
@@ -128,7 +128,7 @@ def pasm_onboard(
|
|
|
128
128
|
for i, row in enumerate(rows, 1):
|
|
129
129
|
console.print(f" {i}. {row.get('name', '?')} ({row.get('ip', '?')})")
|
|
130
130
|
|
|
131
|
-
if not typer.confirm("\nProceed with bulk onboarding?"):
|
|
131
|
+
if not typer.confirm("\nProceed with bulk onboarding?", err=True):
|
|
132
132
|
console.print("[yellow]Cancelled.[/yellow]")
|
|
133
133
|
raise typer.Exit(0)
|
|
134
134
|
|
|
@@ -158,19 +158,19 @@ def pasm_onboard(
|
|
|
158
158
|
)
|
|
159
159
|
results.append({"name": row.get('name'), "status": "success"})
|
|
160
160
|
except Exception as e:
|
|
161
|
-
|
|
161
|
+
err_console.print(f"[red]Failed: {e}[/red]")
|
|
162
162
|
results.append({"name": row.get('name'), "status": "failed", "error": str(e)})
|
|
163
163
|
|
|
164
164
|
# Summary
|
|
165
165
|
console.print(f"\n[bold]Bulk Onboarding Summary:[/bold]")
|
|
166
166
|
success = sum(1 for r in results if r["status"] == "success")
|
|
167
167
|
failed = sum(1 for r in results if r["status"] == "failed")
|
|
168
|
-
|
|
168
|
+
err_console.print(f" [green]Success: {success}[/green]")
|
|
169
169
|
if failed:
|
|
170
|
-
|
|
170
|
+
err_console.print(f" [red]Failed: {failed}[/red]")
|
|
171
171
|
for r in results:
|
|
172
172
|
if r["status"] == "failed":
|
|
173
|
-
|
|
173
|
+
err_console.print(f" - {r['name']}: {r.get('error', 'Unknown error')}")
|
|
174
174
|
|
|
175
175
|
if output == "json":
|
|
176
176
|
console.print_json(json.dumps(results, default=str))
|
|
@@ -273,7 +273,7 @@ def _onboard_single_host(
|
|
|
273
273
|
pws.authenticate()
|
|
274
274
|
|
|
275
275
|
# Create asset
|
|
276
|
-
|
|
276
|
+
err_console.print(f"[dim]PWS: Creating asset '{name}'...[/dim]")
|
|
277
277
|
asset = pws.create_asset(
|
|
278
278
|
workgroup_id=workgroup,
|
|
279
279
|
ip_address=ip,
|
|
@@ -281,10 +281,10 @@ def _onboard_single_host(
|
|
|
281
281
|
dns_name=dns,
|
|
282
282
|
)
|
|
283
283
|
asset_id = asset.get("AssetID")
|
|
284
|
-
|
|
284
|
+
err_console.print(f" [green]Created asset ID: {asset_id}[/green]")
|
|
285
285
|
|
|
286
286
|
# Create managed system
|
|
287
|
-
|
|
287
|
+
err_console.print(f"[dim]PWS: Creating managed system '{name}'...[/dim]")
|
|
288
288
|
system = pws.create_managed_system(
|
|
289
289
|
system_name=name,
|
|
290
290
|
platform_id=platform,
|
|
@@ -295,7 +295,7 @@ def _onboard_single_host(
|
|
|
295
295
|
elevation_command=elevation,
|
|
296
296
|
)
|
|
297
297
|
system_id = system.get("ManagedSystemID")
|
|
298
|
-
|
|
298
|
+
err_console.print(f" [green]Created managed system ID: {system_id}[/green]")
|
|
299
299
|
|
|
300
300
|
result["pws"] = {
|
|
301
301
|
"created": True,
|
|
@@ -314,7 +314,7 @@ def _onboard_single_host(
|
|
|
314
314
|
account_password = generate_password()
|
|
315
315
|
generated_password = True
|
|
316
316
|
|
|
317
|
-
|
|
317
|
+
err_console.print(f"[dim]PWS: Creating managed account '{account}'...[/dim]")
|
|
318
318
|
account_obj = pws.create_managed_account(
|
|
319
319
|
system_id=system_id,
|
|
320
320
|
account_name=account,
|
|
@@ -322,7 +322,7 @@ def _onboard_single_host(
|
|
|
322
322
|
auto_management_flag=True if functional_account else False,
|
|
323
323
|
)
|
|
324
324
|
account_id = account_obj.get("ManagedAccountID")
|
|
325
|
-
|
|
325
|
+
err_console.print(f" [green]Created managed account ID: {account_id}[/green]")
|
|
326
326
|
|
|
327
327
|
if generated_password:
|
|
328
328
|
console.print(f" [yellow]Generated password (save this!):[/yellow] [bold]{account_password}[/bold]")
|
|
@@ -339,7 +339,7 @@ def _onboard_single_host(
|
|
|
339
339
|
pra_user = pra_username or ("ec2-admin" if jump_type == "shell" else None)
|
|
340
340
|
|
|
341
341
|
if jump_type == "shell":
|
|
342
|
-
|
|
342
|
+
err_console.print(f"[dim]PRA: Creating shell jump item '{name}'...[/dim]")
|
|
343
343
|
jump_item = pra.create_shell_jump(
|
|
344
344
|
name=name,
|
|
345
345
|
hostname=ip,
|
|
@@ -350,9 +350,9 @@ def _onboard_single_host(
|
|
|
350
350
|
username=pra_user,
|
|
351
351
|
)
|
|
352
352
|
jump_id = jump_item.get("id")
|
|
353
|
-
|
|
353
|
+
err_console.print(f" [green]Created shell jump ID: {jump_id}[/green]")
|
|
354
354
|
else:
|
|
355
|
-
|
|
355
|
+
err_console.print(f"[dim]PRA: Creating RDP jump item '{name}'...[/dim]")
|
|
356
356
|
jump_item = pra.create_rdp_jump(
|
|
357
357
|
name=name,
|
|
358
358
|
hostname=ip,
|
|
@@ -361,7 +361,7 @@ def _onboard_single_host(
|
|
|
361
361
|
rdp_port=port if port != 22 else 3389,
|
|
362
362
|
)
|
|
363
363
|
jump_id = jump_item.get("id")
|
|
364
|
-
|
|
364
|
+
err_console.print(f" [green]Created RDP jump ID: {jump_id}[/green]")
|
|
365
365
|
|
|
366
366
|
result["pra"] = {
|
|
367
367
|
"created": True,
|
|
@@ -489,7 +489,7 @@ def pasm_offboard(
|
|
|
489
489
|
else:
|
|
490
490
|
pws_asset_id = pws_system.get("AssetID")
|
|
491
491
|
except Exception as e:
|
|
492
|
-
|
|
492
|
+
err_console.print(f"[yellow]PWS search error: {e}[/yellow]")
|
|
493
493
|
|
|
494
494
|
# Search PRA
|
|
495
495
|
if not skip_pra:
|
|
@@ -512,7 +512,7 @@ def pasm_offboard(
|
|
|
512
512
|
pra_jump_type = "rdp"
|
|
513
513
|
break
|
|
514
514
|
except Exception as e:
|
|
515
|
-
|
|
515
|
+
err_console.print(f"[yellow]PRA search error: {e}[/yellow]")
|
|
516
516
|
|
|
517
517
|
# Show what will be deleted
|
|
518
518
|
console.print(f"\n[bold]Will delete '{name}':[/bold]")
|
|
@@ -541,7 +541,7 @@ def pasm_offboard(
|
|
|
541
541
|
|
|
542
542
|
# Confirm
|
|
543
543
|
if not force:
|
|
544
|
-
confirm = typer.confirm("\nProceed with deletion?")
|
|
544
|
+
confirm = typer.confirm("\nProceed with deletion?", err=True)
|
|
545
545
|
if not confirm:
|
|
546
546
|
console.print("[yellow]Cancelled.[/yellow]")
|
|
547
547
|
raise typer.Exit(0)
|
|
@@ -557,21 +557,21 @@ def pasm_offboard(
|
|
|
557
557
|
for acc in pws_accounts:
|
|
558
558
|
acc_id = acc.get("ManagedAccountID", acc.get("AccountId"))
|
|
559
559
|
acc_name = acc.get("AccountName")
|
|
560
|
-
|
|
560
|
+
err_console.print(f"[dim]PWS: Deleting account {acc_name}...[/dim]")
|
|
561
561
|
pws.delete_managed_account(acc_id)
|
|
562
562
|
deleted_accounts.append({"id": acc_id, "name": acc_name})
|
|
563
|
-
|
|
563
|
+
err_console.print(f" [green]Deleted account: {acc_name}[/green]")
|
|
564
564
|
|
|
565
565
|
# Delete system
|
|
566
|
-
|
|
566
|
+
err_console.print(f"[dim]PWS: Deleting system {pws_system.get('SystemName')}...[/dim]")
|
|
567
567
|
pws.delete_managed_system(system_id)
|
|
568
|
-
|
|
568
|
+
err_console.print(f" [green]Deleted system: {pws_system.get('SystemName')}[/green]")
|
|
569
569
|
|
|
570
570
|
# Delete asset
|
|
571
571
|
if pws_asset_id:
|
|
572
|
-
|
|
572
|
+
err_console.print(f"[dim]PWS: Deleting asset {pws_asset_id}...[/dim]")
|
|
573
573
|
pws.delete_asset(pws_asset_id)
|
|
574
|
-
|
|
574
|
+
err_console.print(f" [green]Deleted asset ID: {pws_asset_id}[/green]")
|
|
575
575
|
|
|
576
576
|
result["pws"] = {
|
|
577
577
|
"deleted": True,
|
|
@@ -585,12 +585,12 @@ def pasm_offboard(
|
|
|
585
585
|
pra = get_pra_client()
|
|
586
586
|
jump_id = pra_jump.get("id")
|
|
587
587
|
|
|
588
|
-
|
|
588
|
+
err_console.print(f"[dim]PRA: Deleting {pra_jump_type} jump item {pra_jump.get('name')}...[/dim]")
|
|
589
589
|
if pra_jump_type == "shell":
|
|
590
590
|
pra.delete_shell_jump(jump_id)
|
|
591
591
|
else:
|
|
592
592
|
pra.delete_rdp_jump(jump_id)
|
|
593
|
-
|
|
593
|
+
err_console.print(f" [green]Deleted {pra_jump_type} jump: {pra_jump.get('name')}[/green]")
|
|
594
594
|
|
|
595
595
|
result["pra"] = {
|
|
596
596
|
"deleted": True,
|
|
@@ -650,7 +650,7 @@ def pasm_search(
|
|
|
650
650
|
try:
|
|
651
651
|
with get_pws_client() as pws:
|
|
652
652
|
pws.authenticate()
|
|
653
|
-
|
|
653
|
+
err_console.print(f"[dim]Searching PWS for '{query}'...[/dim]")
|
|
654
654
|
|
|
655
655
|
systems = pws.list_managed_systems()
|
|
656
656
|
result["pws"]["systems"] = [
|
|
@@ -661,12 +661,12 @@ def pasm_search(
|
|
|
661
661
|
accounts = pws.list_managed_accounts(account_name=query)
|
|
662
662
|
result["pws"]["accounts"] = accounts[:20]
|
|
663
663
|
except Exception as e:
|
|
664
|
-
|
|
664
|
+
err_console.print(f"[yellow]PWS search error: {e}[/yellow]")
|
|
665
665
|
|
|
666
666
|
# Search PRA
|
|
667
667
|
try:
|
|
668
668
|
pra = get_pra_client()
|
|
669
|
-
|
|
669
|
+
err_console.print(f"[dim]Searching PRA for '{query}'...[/dim]")
|
|
670
670
|
|
|
671
671
|
shell_jumps = pra.list_shell_jumps()
|
|
672
672
|
result["pra"]["shell_jumps"] = [
|
|
@@ -682,7 +682,7 @@ def pasm_search(
|
|
|
682
682
|
or query_lower in j.get("hostname", "").lower()
|
|
683
683
|
][:20]
|
|
684
684
|
except Exception as e:
|
|
685
|
-
|
|
685
|
+
err_console.print(f"[yellow]PRA search error: {e}[/yellow]")
|
|
686
686
|
|
|
687
687
|
if output == "json":
|
|
688
688
|
console.print_json(json.dumps(result, default=str))
|