bt-cli 0.4.45__tar.gz → 0.4.46__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.45 → bt_cli-0.4.46}/CLAUDE.md +8 -2
- {bt_cli-0.4.45 → bt_cli-0.4.46}/PKG-INFO +30 -1
- {bt_cli-0.4.45 → bt_cli-0.4.46}/README.md +29 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/pyproject.toml +1 -1
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/__init__.py +1 -1
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/cli.py +86 -2
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/core/config.py +69 -1
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/core/config_file.py +35 -0
- bt_cli-0.4.46/src/bt_cli/data/skills/secrets/SKILL.md +201 -0
- bt_cli-0.4.46/src/bt_cli/secrets/__init__.py +1 -0
- bt_cli-0.4.46/src/bt_cli/secrets/client/__init__.py +5 -0
- bt_cli-0.4.46/src/bt_cli/secrets/client/base.py +404 -0
- bt_cli-0.4.46/src/bt_cli/secrets/commands/__init__.py +23 -0
- bt_cli-0.4.46/src/bt_cli/secrets/commands/auth.py +67 -0
- bt_cli-0.4.46/src/bt_cli/secrets/commands/dynamic.py +295 -0
- bt_cli-0.4.46/src/bt_cli/secrets/commands/folders.py +153 -0
- bt_cli-0.4.46/src/bt_cli/secrets/commands/integrations.py +201 -0
- bt_cli-0.4.46/src/bt_cli/secrets/commands/leases.py +122 -0
- bt_cli-0.4.46/src/bt_cli/secrets/commands/static.py +251 -0
- bt_cli-0.4.46/src/bt_cli/secrets/models/__init__.py +1 -0
- bt_cli-0.4.46/tests/secrets/__init__.py +1 -0
- bt_cli-0.4.46/tests/secrets/test_client.py +263 -0
- bt_cli-0.4.46/tests/secrets/test_commands.py +291 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/.claude/skills/bt/SKILL.md +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/.claude/skills/entitle/SKILL.md +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/.claude/skills/epml/SKILL.md +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/.claude/skills/epmw/SKILL.md +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/.claude/skills/pra/SKILL.md +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/.claude/skills/pws/SKILL.md +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/.env.example +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/.github/workflows/ci.yml +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/.github/workflows/release.yml +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/.gitignore +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/assets/cli-help.png +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/assets/cli-output.png +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/bt-cli.spec +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/bt_entry.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/epml-clients-server-side-filters-plan.md +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/epml-implementation-plan.md +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/pf-implementation-plan.md +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/scripts/bt_entry.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/scripts/pf_onboard.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/scripts/sync-package-data.sh +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/commands/__init__.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/commands/configure.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/commands/learn.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/commands/quick.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/core/__init__.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/core/auth.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/core/client.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/core/csv_utils.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/core/errors.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/core/output.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/core/prompts.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/core/rest_debug.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/data/CLAUDE.md +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/data/__init__.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/data/skills/bt/SKILL.md +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/data/skills/entitle/SKILL.md +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/data/skills/epml/SKILL.md +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/data/skills/epmw/SKILL.md +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/data/skills/pra/SKILL.md +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/data/skills/pws/SKILL.md +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/entitle/__init__.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/entitle/client/__init__.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/entitle/client/base.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/__init__.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/accounts.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/applications.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/auth.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/bundles.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/integrations.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/permissions.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/policies.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/requests.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/resources.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/roles.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/users.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/workflows.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/entitle/models/__init__.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/entitle/models/bundle.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/entitle/models/common.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/entitle/models/integration.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/entitle/models/permission.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/entitle/models/policy.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/entitle/models/resource.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/entitle/models/role.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/entitle/models/user.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/entitle/models/workflow.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epml/__init__.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epml/client/__init__.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epml/client/base.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epml/commands/__init__.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epml/commands/audit.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epml/commands/auth.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epml/commands/client_pkg.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epml/commands/clients.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epml/commands/external_apis.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epml/commands/hosts.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epml/commands/iolog.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epml/commands/license.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epml/commands/quick.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epml/commands/rbp_cmdgrps.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epml/commands/rbp_entitlement.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epml/commands/rbp_hostgrps.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epml/commands/rbp_policy.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epml/commands/rbp_roles.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epml/commands/rbp_tests.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epml/commands/rbp_tmdategrps.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epml/commands/rbp_tx.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epml/commands/rbp_usergrps.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epml/commands/settings.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epml/commands/siems.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epml/commands/users.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epml/models/__init__.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epmw/__init__.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epmw/client/__init__.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epmw/client/base.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/__init__.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/audits.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/auth.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/computers.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/events.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/groups.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/policies.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/quick.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/requests.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/roles.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/tasks.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/users.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/epmw/models/__init__.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pra/__init__.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pra/client/__init__.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pra/client/base.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pra/commands/__init__.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pra/commands/auth.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pra/commands/group_policies.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pra/commands/import_export.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pra/commands/jump_clients.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pra/commands/jump_groups.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pra/commands/jump_items.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pra/commands/jumpoints.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pra/commands/policies.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pra/commands/quick.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pra/commands/teams.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pra/commands/users.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pra/commands/vault.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pra/models/__init__.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pra/models/common.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pra/models/group_policy.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pra/models/jump_client.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pra/models/jump_group.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pra/models/jump_item.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pra/models/jumpoint.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pra/models/team.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pra/models/user.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pra/models/vault.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pws/__init__.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pws/client/__init__.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pws/client/base.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pws/client/beyondinsight.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pws/client/passwordsafe.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pws/commands/__init__.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pws/commands/accounts.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pws/commands/assets.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pws/commands/attributes.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pws/commands/auth.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pws/commands/clouds.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pws/commands/config.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pws/commands/credentials.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pws/commands/databases.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pws/commands/directories.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pws/commands/functional.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pws/commands/import_export.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pws/commands/platforms.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pws/commands/quick.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pws/commands/search.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pws/commands/secrets.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pws/commands/systems.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pws/commands/users.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pws/commands/workgroups.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pws/config.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pws/models/__init__.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pws/models/account.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pws/models/asset.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pws/models/common.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/src/bt_cli/pws/models/system.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/__init__.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/conftest.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/core/__init__.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/core/test_auth.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/core/test_config.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/core/test_errors.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/core/test_rest_debug.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/entitle/__init__.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/entitle/test_client.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/entitle/test_commands.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/entitle-smoke-test.sh +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/epml/__init__.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/epml/test_client.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/epml/test_commands.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/epmw/__init__.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/epmw/test_client.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/epmw/test_commands.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/epmw-quick-test-plan.md +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/fixtures/__init__.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/fixtures/responses.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/integration/__init__.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/integration/conftest.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/integration/helpers.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/integration/test_entitle_integration.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/integration/test_epmw_integration.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/integration/test_epmw_lifecycle.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/integration/test_pra_integration.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/integration/test_pra_lifecycle.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/integration/test_pws_integration.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/integration/test_pws_lifecycle.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/pra/__init__.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/pra/test_client.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/pra/test_commands.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/pra-smoke-test.sh +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/pra-test-plan.md +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/pws/__init__.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/pws/test_client.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/pws/test_commands.py +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/pws-quick-test-plan.md +0 -0
- {bt_cli-0.4.45 → bt_cli-0.4.46}/tests/pws-smoke-test.sh +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# BT-CLI
|
|
2
2
|
|
|
3
|
-
BeyondTrust Platform CLI for Password Safe, Entitle, PRA, EPM Windows, and
|
|
3
|
+
BeyondTrust Platform CLI for Password Safe, Entitle, PRA, EPM Windows, EPM Linux, and the BeyondTrust Secrets API. **Version: 0.4.46**
|
|
4
4
|
|
|
5
5
|
## Setup
|
|
6
6
|
|
|
@@ -31,6 +31,7 @@ Use these slash commands for detailed product guidance:
|
|
|
31
31
|
| `/entitle` | Entitle - JIT access, bundles, workflows |
|
|
32
32
|
| `/epmw` | EPM Windows - computers, policies, requests |
|
|
33
33
|
| `/epml` | EPM Linux - RBP roles/cmdgrps/usergrps, policy, test suites, transactions |
|
|
34
|
+
| `/secrets` | Secrets API - folders, static, dynamic AWS credentials, leases, integrations |
|
|
34
35
|
|
|
35
36
|
## Command Structure
|
|
36
37
|
|
|
@@ -42,6 +43,7 @@ Use these slash commands for detailed product guidance:
|
|
|
42
43
|
| Entitle | `bt entitle` | `integrations`, `resources`, `bundles`, `permissions` |
|
|
43
44
|
| EPM Windows | `bt epmw` | `computers`, `groups`, `policies`, `requests`, `quick` |
|
|
44
45
|
| EPM Linux | `bt epml` | `rbp` (cmdgrps/hostgrps/usergrps/tmdategrps/roles/policy/tests/tx), `settings`, `users`, `audit`, `siems`, `quick` |
|
|
46
|
+
| Secrets API | `bt secrets` | `folders`, `static`, `dynamic` (incl. `generate`), `leases`, `integrations` |
|
|
45
47
|
|
|
46
48
|
## Common Patterns
|
|
47
49
|
|
|
@@ -70,6 +72,7 @@ PASSWORD=$(bt pws quick checkout -s server -a admin --raw)
|
|
|
70
72
|
| PRA | `per_page`/`current_page` (1-indexed) | Array (pagination in headers) |
|
|
71
73
|
| EPMW | `pageNumber`/`pageSize` | `{"data": [...], "totalCount": N}` |
|
|
72
74
|
| EPML | varies — `take`/`skip` (audit, siems), `start`/`len` (iologs), plain arrays | mostly arrays; some `{total, data}` |
|
|
75
|
+
| Secrets | `path`/`recursive`/`folder` query params | `{"data": [...]}` for lists; bare objects for single GETs |
|
|
73
76
|
|
|
74
77
|
**Important:**
|
|
75
78
|
- EPMW computers: Use `archive` not `delete` (405 error)
|
|
@@ -84,6 +87,8 @@ PASSWORD=$(bt pws quick checkout -s server -a admin --raw)
|
|
|
84
87
|
- **EPML body-shape gotchas**: role create needs `action ∈ {A,R}`; hostgrp/usergrp create needs `type ∈ {I,E}` (Internal=static / External=directory-resolved); role assignments need `--kind S|R|B` for hostgrps/usergrps (S=Submit user, R=Run-as user); collection deletes use `?id=N` query params (CLI loops); child collections (commands/hosts/users/tmdates) have **no per-item delete** — `clear` nukes all, use `replace` to keep some.
|
|
85
88
|
- **EPML role update is upsert-on-id (overwrite, not partial)**: hitting `POST /roles` with `{id, ...}` clears any field you don't include. The CLI's `roles update` does read-modify-write so it feels partial; child relations are filtered out of the merge so assignments survive. Direct `curl` users must send the full record.
|
|
86
89
|
- **EPML role banner format**: `bt epml rbp roles create --banner-text "Title"` builds a `############`-framed message using `%rbprole%`/`%event%` server substitutions, mirroring the appliance's existing roles. See the worked role example in the EPM-L SKILL.md.
|
|
90
|
+
- **Secrets dynamic path is `/dynamic`** (NOT `/dynamic-secrets` as the doc claims); version header is `2026-02-16` (NOT 2026-01-02); `dynamic/{name}/generate` returns 201; `/leases/revoke` is scope-gated and 403 is the expected lab response — `bt secrets leases revoke` exits 3 with a clean warning, not generic failure. Documented in `src/bt_cli/data/skills/secrets/SKILL.md`.
|
|
91
|
+
- **Secrets `generate` mints real AWS STS credentials and is audited** — don't loop it in dev. Use `dynamic list`/`get` + `leases list` for browsing.
|
|
87
92
|
|
|
88
93
|
## Functional vs Managed Accounts
|
|
89
94
|
|
|
@@ -112,7 +117,8 @@ src/bt_cli/
|
|
|
112
117
|
├── pra/ # PRA
|
|
113
118
|
├── entitle/ # Entitle
|
|
114
119
|
├── epmw/ # EPM Windows
|
|
115
|
-
|
|
120
|
+
├── epml/ # EPM Linux (PAT auth, /site/<id>/epm/linux/... URLs)
|
|
121
|
+
└── secrets/ # BeyondTrust Secrets API (PAT auth, /site/<id>/secrets/... URLs)
|
|
116
122
|
```
|
|
117
123
|
|
|
118
124
|
Each product follows: `client/` (API), `commands/` (CLI), `models/` (Pydantic)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bt-cli
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.46
|
|
4
4
|
Summary: BeyondTrust Platform CLI (unofficial) - Password Safe, Entitle, PRA, EPM
|
|
5
5
|
Author-email: Dave Grendysz <dgrendysz@beyondtrust.com>
|
|
6
6
|
License: MIT
|
|
@@ -57,6 +57,7 @@ Unofficial BeyondTrust Platform CLI - manage privileged access across your envir
|
|
|
57
57
|
| **Entitle** | `bt entitle` | Just-in-time access requests and approval workflows |
|
|
58
58
|
| **PRA** | `bt pra` | Privileged remote access - jump items, sessions, vault |
|
|
59
59
|
| **EPM Windows** | `bt epmw` | Endpoint privilege management - computers, policies, admin requests |
|
|
60
|
+
| **Secrets API** | `bt secrets` | BeyondTrust Secrets API — folders, static secrets, dynamic AWS credentials, leases |
|
|
60
61
|
|
|
61
62
|
## Installation
|
|
62
63
|
|
|
@@ -105,6 +106,33 @@ export BT_EPM_CLIENT_ID=your-client-id
|
|
|
105
106
|
export BT_EPM_CLIENT_SECRET=your-client-secret
|
|
106
107
|
```
|
|
107
108
|
|
|
109
|
+
### Secrets API
|
|
110
|
+
|
|
111
|
+
PAT-bearer auth against the BeyondTrust public API gateway. Mint a PAT at
|
|
112
|
+
[app.beyondtrust.io](https://app.beyondtrust.io).
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
export BT_SECRETS_API_URL=https://api.beyondtrust.io # default
|
|
116
|
+
export BT_SECRETS_SITE_ID=<site-uuid> # required
|
|
117
|
+
export BT_SECRETS_PAT=PAT_xxx # required
|
|
118
|
+
export BT_SECRETS_API_VERSION=2026-02-16 # default (doc page is stale)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Common commands:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
bt secrets folders list -p lab # filter server-side
|
|
125
|
+
bt secrets dynamic list -p lab/aws -r # browse dynamic-secret configs
|
|
126
|
+
bt secrets dynamic generate lab/aws/ec2-readonly # mint shell `export AWS_*` lines
|
|
127
|
+
eval $(bt secrets dynamic generate lab/aws/ec2-readonly) # load into current shell
|
|
128
|
+
bt secrets leases list lab/aws/ec2-readonly # who has active leases
|
|
129
|
+
bt secrets static get lab/db/creds # read a static secret (default JSON)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
`bt secrets dynamic generate` mints **real** AWS STS credentials and is
|
|
133
|
+
audited — don't loop it. `bt secrets leases revoke` is scope-gated; most
|
|
134
|
+
PATs return 403 and exit code 3 with a clean warning. TTL still revokes.
|
|
135
|
+
|
|
108
136
|
### Using a .env File
|
|
109
137
|
|
|
110
138
|
Create a `.env` file and source it before running commands:
|
|
@@ -121,6 +149,7 @@ bt pws auth test # Test Password Safe connection
|
|
|
121
149
|
bt entitle auth test # Test Entitle connection
|
|
122
150
|
bt pra auth test # Test PRA connection
|
|
123
151
|
bt epmw auth test # Test EPM Windows connection
|
|
152
|
+
bt secrets auth test # Test Secrets API connection
|
|
124
153
|
```
|
|
125
154
|
|
|
126
155
|
---
|
|
@@ -10,6 +10,7 @@ Unofficial BeyondTrust Platform CLI - manage privileged access across your envir
|
|
|
10
10
|
| **Entitle** | `bt entitle` | Just-in-time access requests and approval workflows |
|
|
11
11
|
| **PRA** | `bt pra` | Privileged remote access - jump items, sessions, vault |
|
|
12
12
|
| **EPM Windows** | `bt epmw` | Endpoint privilege management - computers, policies, admin requests |
|
|
13
|
+
| **Secrets API** | `bt secrets` | BeyondTrust Secrets API — folders, static secrets, dynamic AWS credentials, leases |
|
|
13
14
|
|
|
14
15
|
## Installation
|
|
15
16
|
|
|
@@ -58,6 +59,33 @@ export BT_EPM_CLIENT_ID=your-client-id
|
|
|
58
59
|
export BT_EPM_CLIENT_SECRET=your-client-secret
|
|
59
60
|
```
|
|
60
61
|
|
|
62
|
+
### Secrets API
|
|
63
|
+
|
|
64
|
+
PAT-bearer auth against the BeyondTrust public API gateway. Mint a PAT at
|
|
65
|
+
[app.beyondtrust.io](https://app.beyondtrust.io).
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
export BT_SECRETS_API_URL=https://api.beyondtrust.io # default
|
|
69
|
+
export BT_SECRETS_SITE_ID=<site-uuid> # required
|
|
70
|
+
export BT_SECRETS_PAT=PAT_xxx # required
|
|
71
|
+
export BT_SECRETS_API_VERSION=2026-02-16 # default (doc page is stale)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Common commands:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
bt secrets folders list -p lab # filter server-side
|
|
78
|
+
bt secrets dynamic list -p lab/aws -r # browse dynamic-secret configs
|
|
79
|
+
bt secrets dynamic generate lab/aws/ec2-readonly # mint shell `export AWS_*` lines
|
|
80
|
+
eval $(bt secrets dynamic generate lab/aws/ec2-readonly) # load into current shell
|
|
81
|
+
bt secrets leases list lab/aws/ec2-readonly # who has active leases
|
|
82
|
+
bt secrets static get lab/db/creds # read a static secret (default JSON)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`bt secrets dynamic generate` mints **real** AWS STS credentials and is
|
|
86
|
+
audited — don't loop it. `bt secrets leases revoke` is scope-gated; most
|
|
87
|
+
PATs return 403 and exit code 3 with a clean warning. TTL still revokes.
|
|
88
|
+
|
|
61
89
|
### Using a .env File
|
|
62
90
|
|
|
63
91
|
Create a `.env` file and source it before running commands:
|
|
@@ -74,6 +102,7 @@ bt pws auth test # Test Password Safe connection
|
|
|
74
102
|
bt entitle auth test # Test Entitle connection
|
|
75
103
|
bt pra auth test # Test PRA connection
|
|
76
104
|
bt epmw auth test # Test EPM Windows connection
|
|
105
|
+
bt secrets auth test # Test Secrets API connection
|
|
77
106
|
```
|
|
78
107
|
|
|
79
108
|
---
|
|
@@ -89,6 +89,12 @@ def _get_epml_app() -> typer.Typer:
|
|
|
89
89
|
return epml_app
|
|
90
90
|
|
|
91
91
|
|
|
92
|
+
def _get_secrets_app() -> typer.Typer:
|
|
93
|
+
"""Lazy load BeyondTrust Secrets API commands."""
|
|
94
|
+
from .secrets.commands import app as secrets_app
|
|
95
|
+
return secrets_app
|
|
96
|
+
|
|
97
|
+
|
|
92
98
|
def _get_configure_app() -> typer.Typer:
|
|
93
99
|
"""Lazy load configure commands."""
|
|
94
100
|
from .commands.configure import app as configure_app
|
|
@@ -134,6 +140,11 @@ try:
|
|
|
134
140
|
except Exception:
|
|
135
141
|
pass # EPML module not ready yet
|
|
136
142
|
|
|
143
|
+
try:
|
|
144
|
+
app.add_typer(_get_secrets_app(), name="secrets", help="BeyondTrust Secrets API commands")
|
|
145
|
+
except Exception:
|
|
146
|
+
pass # Secrets module not ready yet
|
|
147
|
+
|
|
137
148
|
try:
|
|
138
149
|
app.add_typer(_get_configure_app(), name="configure", help="Configure bt-cli settings")
|
|
139
150
|
except Exception:
|
|
@@ -288,9 +299,9 @@ def tree_command(
|
|
|
288
299
|
|
|
289
300
|
if product:
|
|
290
301
|
product = product.lower()
|
|
291
|
-
if product not in ["pws", "pra", "entitle", "epmw", "epml", "quick", "configure"]:
|
|
302
|
+
if product not in ["pws", "pra", "entitle", "epmw", "epml", "secrets", "quick", "configure"]:
|
|
292
303
|
console.print(f"[red]Unknown product: {product}[/red]")
|
|
293
|
-
console.print("Available: pws, pra, entitle, epmw, epml, quick, configure")
|
|
304
|
+
console.print("Available: pws, pra, entitle, epmw, epml, secrets, quick, configure")
|
|
294
305
|
raise typer.Exit(1)
|
|
295
306
|
|
|
296
307
|
tree = Tree("[bold cyan]bt[/bold cyan]")
|
|
@@ -404,6 +415,16 @@ def tree_command(
|
|
|
404
415
|
rbp.add("tx status|begin|commit|rollback|abort")
|
|
405
416
|
epml.add("[green]quick[/green] tests-then-deploy")
|
|
406
417
|
|
|
418
|
+
# Secrets API
|
|
419
|
+
if not product or product == "secrets":
|
|
420
|
+
secrets = tree.add("[bold yellow]secrets[/bold yellow] - BeyondTrust Secrets API")
|
|
421
|
+
secrets.add("[green]auth[/green] test|status")
|
|
422
|
+
secrets.add("[green]folders[/green] list|get|create|delete")
|
|
423
|
+
secrets.add("[green]static[/green] list|get|create|update|delete")
|
|
424
|
+
secrets.add("[green]dynamic[/green] list|get|generate|create|delete")
|
|
425
|
+
secrets.add("[green]leases[/green] list|get|revoke")
|
|
426
|
+
secrets.add("[green]integrations[/green] list|get|create|update|delete")
|
|
427
|
+
|
|
407
428
|
# Quick
|
|
408
429
|
if not product or product == "quick":
|
|
409
430
|
quick = tree.add("[bold yellow]quick[/bold yellow] - Cross-product workflows")
|
|
@@ -591,6 +612,31 @@ def _get_all_commands() -> list[tuple[str, str]]:
|
|
|
591
612
|
("bt epml rbp tests tests delete", "Delete a test"),
|
|
592
613
|
("bt epml rbp tests run", "Run a test suite, exit non-zero on failure"),
|
|
593
614
|
("bt epml quick tests-then-deploy", "Run tests, commit or rollback the open RBP transaction"),
|
|
615
|
+
# Secrets API
|
|
616
|
+
("bt secrets auth test", "Test BeyondTrust Secrets API connection"),
|
|
617
|
+
("bt secrets auth status", "Show Secrets API configuration status"),
|
|
618
|
+
("bt secrets folders list", "List folders (server-side filter)"),
|
|
619
|
+
("bt secrets folders get", "Get folder metadata"),
|
|
620
|
+
("bt secrets folders create", "Create a folder"),
|
|
621
|
+
("bt secrets folders delete", "Delete a folder"),
|
|
622
|
+
("bt secrets static list", "List static secrets"),
|
|
623
|
+
("bt secrets static get", "Get a static secret value"),
|
|
624
|
+
("bt secrets static create", "Create a static secret"),
|
|
625
|
+
("bt secrets static update", "Update a static secret (bumps version)"),
|
|
626
|
+
("bt secrets static delete", "Delete a static secret"),
|
|
627
|
+
("bt secrets dynamic list", "List dynamic-secret configs"),
|
|
628
|
+
("bt secrets dynamic get", "Get a dynamic-secret config"),
|
|
629
|
+
("bt secrets dynamic generate", "Mint a fresh dynamic credential (e.g. AWS STS)"),
|
|
630
|
+
("bt secrets dynamic create", "Create a dynamic-secret config"),
|
|
631
|
+
("bt secrets dynamic delete", "Delete a dynamic-secret config"),
|
|
632
|
+
("bt secrets leases list", "List active leases for a dynamic secret"),
|
|
633
|
+
("bt secrets leases get", "Get lease details by id"),
|
|
634
|
+
("bt secrets leases revoke", "Revoke a lease (scope-gated; lab returns 403)"),
|
|
635
|
+
("bt secrets integrations list", "List integrations"),
|
|
636
|
+
("bt secrets integrations get", "Get an integration"),
|
|
637
|
+
("bt secrets integrations create", "Create an integration"),
|
|
638
|
+
("bt secrets integrations update", "Update an integration (PUT or --partial PATCH)"),
|
|
639
|
+
("bt secrets integrations delete", "Delete an integration"),
|
|
594
640
|
# Quick
|
|
595
641
|
("bt quick pasm-onboard", "Onboard host to PWS + PRA (Total PASM)"),
|
|
596
642
|
("bt quick pasm-offboard", "Offboard host from PWS + PRA"),
|
|
@@ -817,6 +863,11 @@ def whoami(
|
|
|
817
863
|
if epml_result:
|
|
818
864
|
results.append(epml_result)
|
|
819
865
|
|
|
866
|
+
# Test Secrets API
|
|
867
|
+
secrets_result = _test_secrets_connection()
|
|
868
|
+
if secrets_result:
|
|
869
|
+
results.append(secrets_result)
|
|
870
|
+
|
|
820
871
|
if not results:
|
|
821
872
|
console.print("[yellow]No products configured.[/yellow]")
|
|
822
873
|
console.print("\nTo configure products, set environment variables or run:")
|
|
@@ -1024,6 +1075,39 @@ def _test_epml_connection() -> Optional[dict]:
|
|
|
1024
1075
|
}
|
|
1025
1076
|
|
|
1026
1077
|
|
|
1078
|
+
def _test_secrets_connection() -> Optional[dict]:
|
|
1079
|
+
"""Test BeyondTrust Secrets API connection and return status."""
|
|
1080
|
+
try:
|
|
1081
|
+
from .core.config import load_secrets_config
|
|
1082
|
+
from .secrets.client import get_client
|
|
1083
|
+
|
|
1084
|
+
config = load_secrets_config()
|
|
1085
|
+
masked_pat = config.pat[:12] + "..." if len(config.pat) > 12 else "***"
|
|
1086
|
+
result = {
|
|
1087
|
+
"product": "Secrets API",
|
|
1088
|
+
"url": f"{config.api_url}/site/{config.site_id}/secrets",
|
|
1089
|
+
"auth_method": f"PAT ({masked_pat})",
|
|
1090
|
+
"connected": False,
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
with get_client() as client:
|
|
1094
|
+
folders = client.list_folders()
|
|
1095
|
+
result["connected"] = True
|
|
1096
|
+
result["user_info"] = f"site {config.site_id[:8]}…, {len(folders)} root folder(s)"
|
|
1097
|
+
|
|
1098
|
+
return result
|
|
1099
|
+
except ValueError:
|
|
1100
|
+
return None
|
|
1101
|
+
except Exception as e:
|
|
1102
|
+
return {
|
|
1103
|
+
"product": "Secrets API",
|
|
1104
|
+
"url": "",
|
|
1105
|
+
"auth_method": "-",
|
|
1106
|
+
"connected": False,
|
|
1107
|
+
"error": str(e)[:50],
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
|
|
1027
1111
|
def run() -> None:
|
|
1028
1112
|
"""Run the CLI application."""
|
|
1029
1113
|
app()
|
|
@@ -116,6 +116,35 @@ class EPMWConfig(ProductConfig):
|
|
|
116
116
|
)
|
|
117
117
|
|
|
118
118
|
|
|
119
|
+
@dataclass
|
|
120
|
+
class SecretsConfig(ProductConfig):
|
|
121
|
+
"""BeyondTrust Secrets API configuration.
|
|
122
|
+
|
|
123
|
+
Uses Personal Access Token (PAT) bearer authentication against the
|
|
124
|
+
BeyondTrust public API gateway. URLs are composed as:
|
|
125
|
+
|
|
126
|
+
{api_url}/site/{site_id}/secrets/<path>
|
|
127
|
+
|
|
128
|
+
The `api_version` is sent as the `bt-secrets-api-version` header on
|
|
129
|
+
every request. The live API version differs from the public doc page
|
|
130
|
+
(which lists 2026-01-02 — that gets rejected); the default below is
|
|
131
|
+
what the server currently accepts.
|
|
132
|
+
"""
|
|
133
|
+
|
|
134
|
+
site_id: str = ""
|
|
135
|
+
pat: str = ""
|
|
136
|
+
api_version: str = "2026-02-16"
|
|
137
|
+
|
|
138
|
+
def validate(self) -> None:
|
|
139
|
+
"""Validate configuration."""
|
|
140
|
+
if not self.api_url:
|
|
141
|
+
raise ValueError("BT_SECRETS_API_URL is required")
|
|
142
|
+
if not self.site_id:
|
|
143
|
+
raise ValueError("BT_SECRETS_SITE_ID is required")
|
|
144
|
+
if not self.pat:
|
|
145
|
+
raise ValueError("BT_SECRETS_PAT is required")
|
|
146
|
+
|
|
147
|
+
|
|
119
148
|
@dataclass
|
|
120
149
|
class EPMLConfig(ProductConfig):
|
|
121
150
|
"""EPM Linux configuration.
|
|
@@ -448,11 +477,49 @@ def load_epml_config(env_file: Optional[str] = None, profile: Optional[str] = No
|
|
|
448
477
|
return config
|
|
449
478
|
|
|
450
479
|
|
|
480
|
+
def load_secrets_config(env_file: Optional[str] = None, profile: Optional[str] = None) -> SecretsConfig:
|
|
481
|
+
"""Load BeyondTrust Secrets API configuration.
|
|
482
|
+
|
|
483
|
+
Configuration sources (in order of precedence):
|
|
484
|
+
1. Environment variables
|
|
485
|
+
2. Config file (~/.bt-cli/config.yaml)
|
|
486
|
+
|
|
487
|
+
Environment variables:
|
|
488
|
+
BT_SECRETS_API_URL - Gateway URL (default: https://api.beyondtrust.io)
|
|
489
|
+
BT_SECRETS_SITE_ID - Site UUID (required)
|
|
490
|
+
BT_SECRETS_PAT - Personal Access Token (required)
|
|
491
|
+
BT_SECRETS_API_VERSION - API version header (default: 2026-02-16)
|
|
492
|
+
BT_SECRETS_VERIFY_SSL - SSL verification (default: true)
|
|
493
|
+
BT_SECRETS_TIMEOUT - Request timeout in seconds (default: 30)
|
|
494
|
+
"""
|
|
495
|
+
if env_file:
|
|
496
|
+
load_dotenv(env_file)
|
|
497
|
+
else:
|
|
498
|
+
load_dotenv()
|
|
499
|
+
|
|
500
|
+
profile = profile or _get_profile()
|
|
501
|
+
layered = get_layered_config("secrets", profile)
|
|
502
|
+
|
|
503
|
+
if "pat" in layered:
|
|
504
|
+
layered["pat"] = _resolve_value(layered["pat"])
|
|
505
|
+
|
|
506
|
+
config = SecretsConfig(
|
|
507
|
+
api_url=layered.get("api_url") or os.getenv("BT_SECRETS_API_URL", "https://api.beyondtrust.io"),
|
|
508
|
+
site_id=layered.get("site_id") or os.getenv("BT_SECRETS_SITE_ID", ""),
|
|
509
|
+
pat=layered.get("pat") or os.getenv("BT_SECRETS_PAT", ""),
|
|
510
|
+
api_version=layered.get("api_version") or os.getenv("BT_SECRETS_API_VERSION", "2026-02-16"),
|
|
511
|
+
verify_ssl=_to_bool(layered.get("verify_ssl")) if "verify_ssl" in layered else _get_bool(os.getenv("BT_SECRETS_VERIFY_SSL")),
|
|
512
|
+
timeout=_to_float(layered.get("timeout")) if "timeout" in layered else _get_float(os.getenv("BT_SECRETS_TIMEOUT"), 30.0),
|
|
513
|
+
)
|
|
514
|
+
config.validate()
|
|
515
|
+
return config
|
|
516
|
+
|
|
517
|
+
|
|
451
518
|
def load_config(product: str, env_file: Optional[str] = None) -> ProductConfig:
|
|
452
519
|
"""Load configuration for a specific product.
|
|
453
520
|
|
|
454
521
|
Args:
|
|
455
|
-
product: Product name ('pws', 'entitle', 'pra', 'epmw')
|
|
522
|
+
product: Product name ('pws', 'entitle', 'pra', 'epmw', 'epml', 'secrets')
|
|
456
523
|
env_file: Optional path to .env file
|
|
457
524
|
|
|
458
525
|
Returns:
|
|
@@ -467,6 +534,7 @@ def load_config(product: str, env_file: Optional[str] = None) -> ProductConfig:
|
|
|
467
534
|
"pra": load_pra_config,
|
|
468
535
|
"epmw": load_epmw_config,
|
|
469
536
|
"epml": load_epml_config,
|
|
537
|
+
"secrets": load_secrets_config,
|
|
470
538
|
}
|
|
471
539
|
|
|
472
540
|
loader = loaders.get(product.lower())
|
|
@@ -95,6 +95,33 @@ PRODUCTS = {
|
|
|
95
95
|
"timeout": {"prompt": "Timeout (seconds)", "required": False, "secret": False, "default": 30},
|
|
96
96
|
},
|
|
97
97
|
},
|
|
98
|
+
"secrets": {
|
|
99
|
+
"name": "BeyondTrust Secrets API",
|
|
100
|
+
"fields": {
|
|
101
|
+
"api_url": {
|
|
102
|
+
"prompt": "Gateway URL",
|
|
103
|
+
"required": True,
|
|
104
|
+
"secret": False,
|
|
105
|
+
"default": "https://api.beyondtrust.io",
|
|
106
|
+
"example": "https://api.beyondtrust.io",
|
|
107
|
+
},
|
|
108
|
+
"site_id": {
|
|
109
|
+
"prompt": "Site ID (UUID)",
|
|
110
|
+
"required": True,
|
|
111
|
+
"secret": False,
|
|
112
|
+
"example": "7f735e1b-5ecb-49fa-87e8-eddf54a9c745",
|
|
113
|
+
},
|
|
114
|
+
"pat": {"prompt": "Personal Access Token", "required": True, "secret": True},
|
|
115
|
+
"api_version": {
|
|
116
|
+
"prompt": "API version (bt-secrets-api-version header)",
|
|
117
|
+
"required": False,
|
|
118
|
+
"secret": False,
|
|
119
|
+
"default": "2026-02-16",
|
|
120
|
+
},
|
|
121
|
+
"verify_ssl": {"prompt": "Verify SSL", "required": False, "secret": False, "default": True},
|
|
122
|
+
"timeout": {"prompt": "Timeout (seconds)", "required": False, "secret": False, "default": 30},
|
|
123
|
+
},
|
|
124
|
+
},
|
|
98
125
|
"epml": {
|
|
99
126
|
"name": "EPM Linux",
|
|
100
127
|
"fields": {
|
|
@@ -328,6 +355,7 @@ def _get_env_prefix(product: str) -> str:
|
|
|
328
355
|
"pra": "BT_PRA",
|
|
329
356
|
"epmw": "BT_EPM",
|
|
330
357
|
"epml": "BT_EPML",
|
|
358
|
+
"secrets": "BT_SECRETS",
|
|
331
359
|
}
|
|
332
360
|
return prefixes.get(product, f"BT_{product.upper()}")
|
|
333
361
|
|
|
@@ -359,6 +387,13 @@ def _get_env_mappings(product: str) -> dict[str, str]:
|
|
|
359
387
|
# EPM-L doesn't use api_key/client_id/client_secret — drop the inherited mappings
|
|
360
388
|
for unused in ("api_key", "client_id", "client_secret"):
|
|
361
389
|
mappings.pop(unused, None)
|
|
390
|
+
elif product == "secrets":
|
|
391
|
+
mappings["site_id"] = f"{prefix}_SITE_ID"
|
|
392
|
+
mappings["pat"] = f"{prefix}_PAT"
|
|
393
|
+
mappings["api_version"] = f"{prefix}_API_VERSION"
|
|
394
|
+
# Secrets API doesn't use api_key/client_id/client_secret — drop the inherited mappings
|
|
395
|
+
for unused in ("api_key", "client_id", "client_secret"):
|
|
396
|
+
mappings.pop(unused, None)
|
|
362
397
|
|
|
363
398
|
return mappings
|
|
364
399
|
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: secrets
|
|
3
|
+
description: BeyondTrust Secrets API commands — folders, static secrets, dynamic AWS credentials, leases, integrations. Use when minting STS credentials, managing secret folders/values, or browsing/revoking dynamic-credential leases.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Secrets API Commands (`bt secrets`)
|
|
7
|
+
|
|
8
|
+
Direct CLI for the BeyondTrust Secrets API. PAT-bearer auth, same gateway as
|
|
9
|
+
EPM-L (`api.beyondtrust.io`). **Not** a wrapper around Entitle.
|
|
10
|
+
|
|
11
|
+
## IMPORTANT: Destructive Operations
|
|
12
|
+
|
|
13
|
+
**ALWAYS confirm with the user before:**
|
|
14
|
+
- `bt secrets folders delete --recursive` — wipes the folder and all contents
|
|
15
|
+
- `bt secrets folders delete --permanent` — bypasses soft-delete
|
|
16
|
+
- `bt secrets static delete` / `dynamic delete` — removes the secret/config
|
|
17
|
+
- `bt secrets integrations delete` — disables downstream dynamic generation
|
|
18
|
+
|
|
19
|
+
List affected resources first, then ask for explicit confirmation. Every
|
|
20
|
+
delete supports `--force` to skip the prompt — only use it when the user has
|
|
21
|
+
already confirmed.
|
|
22
|
+
|
|
23
|
+
## IMPORTANT: Generate Sparingly
|
|
24
|
+
|
|
25
|
+
`bt secrets dynamic generate <path>` mints **real** AWS STS credentials and
|
|
26
|
+
shows up in the audit trail. Don't loop it during development. For exploring,
|
|
27
|
+
use `dynamic list` / `dynamic get` / `leases list` — those don't mint.
|
|
28
|
+
|
|
29
|
+
## Configuration
|
|
30
|
+
|
|
31
|
+
Set these before running commands. PAT comes from `app.beyondtrust.io`.
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
export BT_SECRETS_API_URL=https://api.beyondtrust.io # default
|
|
35
|
+
export BT_SECRETS_SITE_ID=<site-uuid> # required
|
|
36
|
+
export BT_SECRETS_PAT=PAT_xxx # required
|
|
37
|
+
export BT_SECRETS_API_VERSION=2026-02-16 # default; doc page is stale
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
bt secrets auth test # verifies PAT + site + version-header reach the API
|
|
42
|
+
bt secrets auth status # env-var view (no network)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Path Semantics
|
|
46
|
+
|
|
47
|
+
Every item lives at a "path" like `lab/aws/cloudwatch-readonly`. The CLI
|
|
48
|
+
auto-splits the trailing segment as the `name` and the rest as the parent
|
|
49
|
+
`folder`, so users (and you) pass single paths everywhere. The folder may be
|
|
50
|
+
empty for top-level items.
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
bt secrets dynamic get lab/aws/cloudwatch-readonly # folder=lab/aws, name=cloudwatch-readonly
|
|
54
|
+
bt secrets folders get lab/aws # folder=lab, name=aws
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Folders
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
bt secrets folders list # all root folders
|
|
61
|
+
bt secrets folders list -p lab # filter server-side by path
|
|
62
|
+
bt secrets folders list -p lab -r # recursive
|
|
63
|
+
bt secrets folders get lab/aws # metadata
|
|
64
|
+
bt secrets folders create lab/aws/staging
|
|
65
|
+
bt secrets folders delete lab/aws/staging
|
|
66
|
+
bt secrets folders delete lab/aws/staging --recursive --force # nuke contents
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Static Secrets
|
|
70
|
+
|
|
71
|
+
Versioned key/value secrets. The body is a JSON object — pass it inline via
|
|
72
|
+
`--data` or from disk via `--data-file`.
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
bt secrets static list -p lab # server-side filter
|
|
76
|
+
bt secrets static get lab/db-pass # default JSON output (values are structured)
|
|
77
|
+
bt secrets static get lab/db-pass --version 2 # specific historical version
|
|
78
|
+
bt secrets static create lab/db-pass -d '{"password":"hunter2","username":"admin"}'
|
|
79
|
+
bt secrets static update lab/db-pass -d '{"password":"newpass"}' # bumps version, returns 204
|
|
80
|
+
bt secrets static delete lab/db-pass --force
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Dynamic Secrets
|
|
84
|
+
|
|
85
|
+
Configurations that mint short-lived credentials on demand (AWS STS today).
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
bt secrets dynamic list -p lab/aws -r
|
|
89
|
+
bt secrets dynamic get lab/aws/ec2-readonly
|
|
90
|
+
|
|
91
|
+
# Mint fresh credentials — default prints shell `export AWS_*` lines.
|
|
92
|
+
bt secrets dynamic generate lab/aws/ec2-readonly
|
|
93
|
+
eval $(bt secrets dynamic generate lab/aws/ec2-readonly) # one-liner load into shell
|
|
94
|
+
aws sts get-caller-identity # verify
|
|
95
|
+
|
|
96
|
+
# Other formats:
|
|
97
|
+
bt secrets dynamic generate lab/aws/ec2-readonly --format json # full payload w/ leaseId
|
|
98
|
+
bt secrets dynamic generate lab/aws/ec2-readonly --format env # KEY=value (no `export`)
|
|
99
|
+
bt secrets dynamic generate lab/aws/ec2-readonly --format dotenv # KEY="value" for .env files
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Create dynamic-secret config (AWS example):
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
bt secrets dynamic create lab/aws/my-new-role -d '{
|
|
106
|
+
"type":"aws",
|
|
107
|
+
"integrationName":"AWS-Green-SE-Lab",
|
|
108
|
+
"credentialType":"assumed_role",
|
|
109
|
+
"roleArn":"arn:aws:iam::614212341445:role/beyondtrust/my-role",
|
|
110
|
+
"ttl":900
|
|
111
|
+
}'
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Leases
|
|
115
|
+
|
|
116
|
+
Each `generate` call creates a lease. Leases auto-expire at TTL; the
|
|
117
|
+
`revoke` endpoint is **scope-gated** and most lab PATs return 403 — that's
|
|
118
|
+
not a generic error, it's the expected "you can't revoke" signal.
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
bt secrets leases list lab/aws/ec2-readonly
|
|
122
|
+
bt secrets leases get <lease-uuid> # rich detail: externalEntityId (assumed-role ARN), etc.
|
|
123
|
+
bt secrets leases revoke <lease-uuid> # exit 3 + warning if PAT lacks the revoke scope
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Integrations
|
|
127
|
+
|
|
128
|
+
External-system connections (currently AWS). Required before a dynamic-secret
|
|
129
|
+
config can reference an `integrationName`.
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
bt secrets integrations list
|
|
133
|
+
bt secrets integrations list -t aws
|
|
134
|
+
bt secrets integrations get my-aws
|
|
135
|
+
bt secrets integrations create my-aws -d '{
|
|
136
|
+
"type":"aws",
|
|
137
|
+
"roleArn":"arn:aws:iam::123456789012:role/MyRole",
|
|
138
|
+
"externalId":"ext-id"
|
|
139
|
+
}'
|
|
140
|
+
bt secrets integrations update my-aws -d '{...}' # PUT (full replace)
|
|
141
|
+
bt secrets integrations update my-aws --partial -d '{...}' # PATCH (partial)
|
|
142
|
+
bt secrets integrations delete my-aws --force
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## API Quirks (Gotchas)
|
|
146
|
+
|
|
147
|
+
- **Dynamic path is `/dynamic`, NOT `/dynamic-secrets`** — the static doc
|
|
148
|
+
page hasn't caught up to the live API. The client handles this; never
|
|
149
|
+
hand-hack URLs.
|
|
150
|
+
- **API version**: header value is `bt-secrets-api-version: 2026-02-16`. The
|
|
151
|
+
doc says `2026-01-02` and the server rejects it.
|
|
152
|
+
- **POST `/dynamic/{name}/generate` returns 201**, not 200. The client
|
|
153
|
+
accepts both via `raise_for_status` semantics.
|
|
154
|
+
- **`/leases/revoke` is scope-gated** — `bt secrets leases revoke` exits 3
|
|
155
|
+
with a clean warning when the server returns 403. TTL still expires the
|
|
156
|
+
lease eventually.
|
|
157
|
+
- **Rate limit**: 500/window. The client retries 429 once, honoring
|
|
158
|
+
`x-ratelimit-reset` (clamped to ≤ 30s so a bogus header can't park the
|
|
159
|
+
CLI).
|
|
160
|
+
- **PAT redaction**: errors and logs route through `bt_cli.core.errors` which
|
|
161
|
+
scrubs `Authorization` headers and bearer tokens before display. Never
|
|
162
|
+
add a `print(client.pat)` to debug — the PAT must never echo back.
|
|
163
|
+
|
|
164
|
+
## Output Formats
|
|
165
|
+
|
|
166
|
+
- All `list` commands default to rich tables; pass `-o json` for scripting.
|
|
167
|
+
- `get` commands default to JSON since values are structured.
|
|
168
|
+
- `dynamic generate` defaults to `export` shell lines — designed for
|
|
169
|
+
`eval $(...)`.
|
|
170
|
+
|
|
171
|
+
## Common Workflows
|
|
172
|
+
|
|
173
|
+
### Mint creds, run an AWS command, let TTL revoke
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
eval $(bt secrets dynamic generate lab/aws/ec2-readonly)
|
|
177
|
+
aws ec2 describe-instances --max-results 5
|
|
178
|
+
# (don't bother revoking — TTL handles it)
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Audit who currently has access via a dynamic config
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
bt secrets leases list lab/aws/full-admin -o json \
|
|
185
|
+
| jq -r '.[] | "\(.leaseId) \(.expiration) \(.externalEntityId)"'
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Browse the tree
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
bt secrets folders list -p lab -r
|
|
192
|
+
bt secrets static list -p lab -r
|
|
193
|
+
bt secrets dynamic list -p lab -r
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## API Notes
|
|
197
|
+
|
|
198
|
+
- Base URL: `https://api.beyondtrust.io/site/<site-id>/secrets`
|
|
199
|
+
- Auth: `Authorization: Bearer <PAT>`
|
|
200
|
+
- Version header: `bt-secrets-api-version: 2026-02-16`
|
|
201
|
+
- Rate limit: 500/window — respect `x-ratelimit-reset` on 429.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""BeyondTrust Secrets API module."""
|