bt-cli 0.4.44__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.44 → bt_cli-0.4.46}/CLAUDE.md +8 -2
- {bt_cli-0.4.44 → bt_cli-0.4.46}/PKG-INFO +30 -1
- {bt_cli-0.4.44 → bt_cli-0.4.46}/README.md +29 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/pyproject.toml +1 -1
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/__init__.py +1 -1
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/cli.py +86 -2
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/commands/configure.py +17 -2
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/core/config.py +69 -1
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/core/config_file.py +47 -1
- 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.44 → bt_cli-0.4.46}/.claude/skills/bt/SKILL.md +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/.claude/skills/entitle/SKILL.md +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/.claude/skills/epml/SKILL.md +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/.claude/skills/epmw/SKILL.md +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/.claude/skills/pra/SKILL.md +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/.claude/skills/pws/SKILL.md +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/.env.example +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/.github/workflows/ci.yml +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/.github/workflows/release.yml +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/.gitignore +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/assets/cli-help.png +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/assets/cli-output.png +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/bt-cli.spec +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/bt_entry.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/epml-clients-server-side-filters-plan.md +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/epml-implementation-plan.md +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/pf-implementation-plan.md +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/scripts/bt_entry.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/scripts/pf_onboard.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/scripts/sync-package-data.sh +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/commands/__init__.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/commands/learn.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/commands/quick.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/core/__init__.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/core/auth.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/core/client.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/core/csv_utils.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/core/errors.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/core/output.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/core/prompts.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/core/rest_debug.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/data/CLAUDE.md +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/data/__init__.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/data/skills/bt/SKILL.md +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/data/skills/entitle/SKILL.md +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/data/skills/epml/SKILL.md +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/data/skills/epmw/SKILL.md +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/data/skills/pra/SKILL.md +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/data/skills/pws/SKILL.md +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/__init__.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/client/__init__.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/client/base.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/__init__.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/accounts.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/applications.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/auth.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/bundles.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/integrations.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/permissions.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/policies.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/requests.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/resources.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/roles.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/users.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/workflows.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/models/__init__.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/models/bundle.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/models/common.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/models/integration.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/models/permission.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/models/policy.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/models/resource.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/models/role.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/models/user.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/models/workflow.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/__init__.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/client/__init__.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/client/base.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/__init__.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/audit.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/auth.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/client_pkg.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/clients.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/external_apis.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/hosts.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/iolog.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/license.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/quick.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/rbp_cmdgrps.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/rbp_entitlement.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/rbp_hostgrps.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/rbp_policy.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/rbp_roles.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/rbp_tests.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/rbp_tmdategrps.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/rbp_tx.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/rbp_usergrps.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/settings.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/siems.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/users.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/models/__init__.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epmw/__init__.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epmw/client/__init__.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epmw/client/base.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/__init__.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/audits.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/auth.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/computers.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/events.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/groups.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/policies.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/quick.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/requests.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/roles.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/tasks.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/users.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epmw/models/__init__.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/__init__.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/client/__init__.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/client/base.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/commands/__init__.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/commands/auth.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/commands/group_policies.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/commands/import_export.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/commands/jump_clients.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/commands/jump_groups.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/commands/jump_items.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/commands/jumpoints.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/commands/policies.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/commands/quick.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/commands/teams.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/commands/users.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/commands/vault.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/models/__init__.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/models/common.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/models/group_policy.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/models/jump_client.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/models/jump_group.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/models/jump_item.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/models/jumpoint.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/models/team.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/models/user.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/models/vault.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/__init__.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/client/__init__.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/client/base.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/client/beyondinsight.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/client/passwordsafe.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/__init__.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/accounts.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/assets.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/attributes.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/auth.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/clouds.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/config.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/credentials.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/databases.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/directories.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/functional.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/import_export.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/platforms.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/quick.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/search.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/secrets.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/systems.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/users.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/workgroups.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/config.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/models/__init__.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/models/account.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/models/asset.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/models/common.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/models/system.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/__init__.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/conftest.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/core/__init__.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/core/test_auth.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/core/test_config.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/core/test_errors.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/core/test_rest_debug.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/entitle/__init__.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/entitle/test_client.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/entitle/test_commands.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/entitle-smoke-test.sh +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/epml/__init__.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/epml/test_client.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/epml/test_commands.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/epmw/__init__.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/epmw/test_client.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/epmw/test_commands.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/epmw-quick-test-plan.md +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/fixtures/__init__.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/fixtures/responses.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/integration/__init__.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/integration/conftest.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/integration/helpers.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/integration/test_entitle_integration.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/integration/test_epmw_integration.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/integration/test_epmw_lifecycle.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/integration/test_pra_integration.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/integration/test_pra_lifecycle.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/integration/test_pws_integration.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/integration/test_pws_lifecycle.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/pra/__init__.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/pra/test_client.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/pra/test_commands.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/pra-smoke-test.sh +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/pra-test-plan.md +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/pws/__init__.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/pws/test_client.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/pws/test_commands.py +0 -0
- {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/pws-quick-test-plan.md +0 -0
- {bt_cli-0.4.44 → 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()
|
|
@@ -43,6 +43,11 @@ def configure_callback(
|
|
|
43
43
|
client_id: Optional[str] = typer.Option(None, "--client-id", help="OAuth Client ID"),
|
|
44
44
|
client_secret: Optional[str] = typer.Option(None, "--client-secret", help="OAuth Client Secret"),
|
|
45
45
|
api_key: Optional[str] = typer.Option(None, "--api-key", help="API Key"),
|
|
46
|
+
user_api_key: Optional[str] = typer.Option(
|
|
47
|
+
None,
|
|
48
|
+
"--user-api-key",
|
|
49
|
+
help="Entitle user-context API key (only required for `bt entitle requests create`)",
|
|
50
|
+
),
|
|
46
51
|
) -> None:
|
|
47
52
|
"""Configure bt-cli interactively or via flags.
|
|
48
53
|
|
|
@@ -65,11 +70,13 @@ def configure_callback(
|
|
|
65
70
|
return
|
|
66
71
|
|
|
67
72
|
# Check if any non-interactive flags were provided
|
|
68
|
-
has_flags = any([api_url, client_id, client_secret, api_key])
|
|
73
|
+
has_flags = any([api_url, client_id, client_secret, api_key, user_api_key])
|
|
69
74
|
|
|
70
75
|
if has_flags and product:
|
|
71
76
|
# Non-interactive mode with flags
|
|
72
|
-
_configure_with_flags(
|
|
77
|
+
_configure_with_flags(
|
|
78
|
+
product, profile, api_url, client_id, client_secret, api_key, user_api_key
|
|
79
|
+
)
|
|
73
80
|
else:
|
|
74
81
|
# Interactive mode
|
|
75
82
|
_configure_interactive(product, profile)
|
|
@@ -219,6 +226,7 @@ def _configure_with_flags(
|
|
|
219
226
|
client_id: Optional[str],
|
|
220
227
|
client_secret: Optional[str],
|
|
221
228
|
api_key: Optional[str],
|
|
229
|
+
user_api_key: Optional[str] = None,
|
|
222
230
|
) -> None:
|
|
223
231
|
"""Configure using command-line flags (non-interactive)."""
|
|
224
232
|
if product not in PRODUCTS:
|
|
@@ -240,6 +248,11 @@ def _configure_with_flags(
|
|
|
240
248
|
new_config["client_secret"] = client_secret
|
|
241
249
|
if api_key:
|
|
242
250
|
new_config["api_key"] = api_key
|
|
251
|
+
if user_api_key is not None:
|
|
252
|
+
if product != "entitle":
|
|
253
|
+
print_error("--user-api-key is only valid for product=entitle")
|
|
254
|
+
raise typer.Exit(2)
|
|
255
|
+
new_config["user_api_key"] = user_api_key
|
|
243
256
|
|
|
244
257
|
# Infer auth method
|
|
245
258
|
if api_key:
|
|
@@ -481,6 +494,7 @@ def import_from_env(
|
|
|
481
494
|
"entitle": {
|
|
482
495
|
"api_url": "BT_ENTITLE_API_URL",
|
|
483
496
|
"api_key": "BT_ENTITLE_API_KEY",
|
|
497
|
+
"user_api_key": "BT_ENTITLE_USER_API_KEY",
|
|
484
498
|
"verify_ssl": "BT_ENTITLE_VERIFY_SSL",
|
|
485
499
|
"timeout": "BT_ENTITLE_TIMEOUT",
|
|
486
500
|
},
|
|
@@ -582,6 +596,7 @@ def show_effective_config(
|
|
|
582
596
|
"Entitle": {
|
|
583
597
|
"api_url": "BT_ENTITLE_API_URL",
|
|
584
598
|
"api_key": "BT_ENTITLE_API_KEY",
|
|
599
|
+
"user_api_key": "BT_ENTITLE_USER_API_KEY",
|
|
585
600
|
"verify_ssl": "BT_ENTITLE_VERIFY_SSL",
|
|
586
601
|
"timeout": "BT_ENTITLE_TIMEOUT",
|
|
587
602
|
},
|
|
@@ -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())
|
|
@@ -51,7 +51,16 @@ PRODUCTS = {
|
|
|
51
51
|
"default": "https://api.us.entitle.io",
|
|
52
52
|
"example": "https://api.us.entitle.io or https://api.eu.entitle.io",
|
|
53
53
|
},
|
|
54
|
-
"api_key": {
|
|
54
|
+
"api_key": {
|
|
55
|
+
"prompt": "Org/Admin API Key (read + admin writes)",
|
|
56
|
+
"required": True,
|
|
57
|
+
"secret": True,
|
|
58
|
+
},
|
|
59
|
+
"user_api_key": {
|
|
60
|
+
"prompt": "User-context API Key (only for `requests create`; leave blank to skip)",
|
|
61
|
+
"required": False,
|
|
62
|
+
"secret": True,
|
|
63
|
+
},
|
|
55
64
|
"verify_ssl": {"prompt": "Verify SSL", "required": False, "secret": False, "default": True},
|
|
56
65
|
"timeout": {"prompt": "Timeout (seconds)", "required": False, "secret": False, "default": 30},
|
|
57
66
|
},
|
|
@@ -86,6 +95,33 @@ PRODUCTS = {
|
|
|
86
95
|
"timeout": {"prompt": "Timeout (seconds)", "required": False, "secret": False, "default": 30},
|
|
87
96
|
},
|
|
88
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
|
+
},
|
|
89
125
|
"epml": {
|
|
90
126
|
"name": "EPM Linux",
|
|
91
127
|
"fields": {
|
|
@@ -319,6 +355,7 @@ def _get_env_prefix(product: str) -> str:
|
|
|
319
355
|
"pra": "BT_PRA",
|
|
320
356
|
"epmw": "BT_EPM",
|
|
321
357
|
"epml": "BT_EPML",
|
|
358
|
+
"secrets": "BT_SECRETS",
|
|
322
359
|
}
|
|
323
360
|
return prefixes.get(product, f"BT_{product.upper()}")
|
|
324
361
|
|
|
@@ -341,6 +378,8 @@ def _get_env_mappings(product: str) -> dict[str, str]:
|
|
|
341
378
|
if product == "pws":
|
|
342
379
|
mappings["run_as"] = f"{prefix}_RUN_AS"
|
|
343
380
|
mappings["api_version"] = f"{prefix}_API_VERSION"
|
|
381
|
+
elif product == "entitle":
|
|
382
|
+
mappings["user_api_key"] = f"{prefix}_USER_API_KEY"
|
|
344
383
|
elif product == "epml":
|
|
345
384
|
mappings["site_id"] = f"{prefix}_SITE_ID"
|
|
346
385
|
mappings["pat"] = f"{prefix}_PAT"
|
|
@@ -348,6 +387,13 @@ def _get_env_mappings(product: str) -> dict[str, str]:
|
|
|
348
387
|
# EPM-L doesn't use api_key/client_id/client_secret — drop the inherited mappings
|
|
349
388
|
for unused in ("api_key", "client_id", "client_secret"):
|
|
350
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)
|
|
351
397
|
|
|
352
398
|
return mappings
|
|
353
399
|
|