bt-cli 0.4.32__tar.gz → 0.4.33__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.33/.claude/skills/epml/SKILL.md +199 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/CLAUDE.md +8 -2
- {bt_cli-0.4.32 → bt_cli-0.4.33}/PKG-INFO +1 -1
- bt_cli-0.4.33/epml-implementation-plan.md +259 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/pyproject.toml +1 -1
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/__init__.py +1 -1
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/cli.py +154 -2
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/core/auth.py +27 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/core/client.py +17 -12
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/core/config.py +73 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/core/config_file.py +41 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/data/CLAUDE.md +14 -2
- bt_cli-0.4.33/src/bt_cli/data/skills/epml/SKILL.md +199 -0
- bt_cli-0.4.33/src/bt_cli/epml/__init__.py +1 -0
- bt_cli-0.4.33/src/bt_cli/epml/client/__init__.py +5 -0
- bt_cli-0.4.33/src/bt_cli/epml/client/base.py +595 -0
- bt_cli-0.4.33/src/bt_cli/epml/commands/__init__.py +58 -0
- bt_cli-0.4.33/src/bt_cli/epml/commands/audit.py +88 -0
- bt_cli-0.4.33/src/bt_cli/epml/commands/auth.py +70 -0
- bt_cli-0.4.33/src/bt_cli/epml/commands/client_pkg.py +99 -0
- bt_cli-0.4.33/src/bt_cli/epml/commands/external_apis.py +70 -0
- bt_cli-0.4.33/src/bt_cli/epml/commands/hosts.py +83 -0
- bt_cli-0.4.33/src/bt_cli/epml/commands/iolog.py +45 -0
- bt_cli-0.4.33/src/bt_cli/epml/commands/license.py +42 -0
- bt_cli-0.4.33/src/bt_cli/epml/commands/quick.py +67 -0
- bt_cli-0.4.33/src/bt_cli/epml/commands/rbp_cmdgrps.py +141 -0
- bt_cli-0.4.33/src/bt_cli/epml/commands/rbp_entitlement.py +32 -0
- bt_cli-0.4.33/src/bt_cli/epml/commands/rbp_hostgrps.py +141 -0
- bt_cli-0.4.33/src/bt_cli/epml/commands/rbp_policy.py +159 -0
- bt_cli-0.4.33/src/bt_cli/epml/commands/rbp_roles.py +165 -0
- bt_cli-0.4.33/src/bt_cli/epml/commands/rbp_tests.py +269 -0
- bt_cli-0.4.33/src/bt_cli/epml/commands/rbp_tmdategrps.py +77 -0
- bt_cli-0.4.33/src/bt_cli/epml/commands/rbp_tx.py +87 -0
- bt_cli-0.4.33/src/bt_cli/epml/commands/rbp_usergrps.py +141 -0
- bt_cli-0.4.33/src/bt_cli/epml/commands/settings.py +56 -0
- bt_cli-0.4.33/src/bt_cli/epml/commands/siems.py +46 -0
- bt_cli-0.4.33/src/bt_cli/epml/commands/users.py +90 -0
- bt_cli-0.4.33/src/bt_cli/epml/models/__init__.py +1 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/conftest.py +27 -1
- bt_cli-0.4.33/tests/epml/__init__.py +1 -0
- bt_cli-0.4.33/tests/epml/test_client.py +205 -0
- bt_cli-0.4.33/tests/epml/test_commands.py +186 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/.claude/skills/bt/SKILL.md +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/.claude/skills/entitle/SKILL.md +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/.claude/skills/epmw/SKILL.md +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/.claude/skills/pra/SKILL.md +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/.claude/skills/pws/SKILL.md +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/.env.example +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/.github/workflows/ci.yml +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/.github/workflows/release.yml +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/.gitignore +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/README.md +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/assets/cli-help.png +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/assets/cli-output.png +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/bt-cli.spec +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/bt_entry.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/scripts/bt_entry.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/scripts/sync-package-data.sh +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/commands/__init__.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/commands/configure.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/commands/learn.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/commands/quick.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/core/__init__.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/core/csv_utils.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/core/errors.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/core/output.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/core/prompts.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/core/rest_debug.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/data/__init__.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/data/skills/bt/SKILL.md +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/data/skills/entitle/SKILL.md +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/data/skills/epmw/SKILL.md +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/data/skills/pra/SKILL.md +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/data/skills/pws/SKILL.md +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/entitle/__init__.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/entitle/client/__init__.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/entitle/client/base.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/entitle/commands/__init__.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/entitle/commands/accounts.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/entitle/commands/applications.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/entitle/commands/auth.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/entitle/commands/bundles.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/entitle/commands/integrations.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/entitle/commands/permissions.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/entitle/commands/policies.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/entitle/commands/resources.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/entitle/commands/roles.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/entitle/commands/users.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/entitle/commands/workflows.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/entitle/models/__init__.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/entitle/models/bundle.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/entitle/models/common.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/entitle/models/integration.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/entitle/models/permission.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/entitle/models/policy.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/entitle/models/resource.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/entitle/models/role.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/entitle/models/user.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/entitle/models/workflow.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/epmw/__init__.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/epmw/client/__init__.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/epmw/client/base.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/epmw/commands/__init__.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/epmw/commands/audits.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/epmw/commands/auth.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/epmw/commands/computers.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/epmw/commands/events.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/epmw/commands/groups.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/epmw/commands/policies.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/epmw/commands/quick.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/epmw/commands/requests.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/epmw/commands/roles.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/epmw/commands/tasks.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/epmw/commands/users.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/epmw/models/__init__.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pra/__init__.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pra/client/__init__.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pra/client/base.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pra/commands/__init__.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pra/commands/auth.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pra/commands/import_export.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pra/commands/jump_clients.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pra/commands/jump_groups.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pra/commands/jump_items.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pra/commands/jumpoints.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pra/commands/policies.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pra/commands/quick.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pra/commands/teams.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pra/commands/users.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pra/commands/vault.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pra/models/__init__.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pra/models/common.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pra/models/jump_client.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pra/models/jump_group.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pra/models/jump_item.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pra/models/jumpoint.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pra/models/team.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pra/models/user.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pra/models/vault.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pws/__init__.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pws/client/__init__.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pws/client/base.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pws/client/beyondinsight.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pws/client/passwordsafe.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pws/commands/__init__.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pws/commands/accounts.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pws/commands/assets.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pws/commands/attributes.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pws/commands/auth.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pws/commands/clouds.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pws/commands/config.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pws/commands/credentials.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pws/commands/databases.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pws/commands/directories.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pws/commands/functional.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pws/commands/import_export.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pws/commands/platforms.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pws/commands/quick.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pws/commands/search.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pws/commands/secrets.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pws/commands/systems.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pws/commands/users.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pws/commands/workgroups.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pws/config.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pws/models/__init__.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pws/models/account.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pws/models/asset.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pws/models/common.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/src/bt_cli/pws/models/system.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/__init__.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/core/__init__.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/core/test_auth.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/core/test_config.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/core/test_errors.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/core/test_rest_debug.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/entitle/__init__.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/entitle/test_client.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/entitle/test_commands.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/entitle-smoke-test.sh +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/epmw/__init__.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/epmw/test_client.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/epmw/test_commands.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/epmw-quick-test-plan.md +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/fixtures/__init__.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/fixtures/responses.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/integration/__init__.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/integration/conftest.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/integration/helpers.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/integration/test_entitle_integration.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/integration/test_epmw_integration.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/integration/test_epmw_lifecycle.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/integration/test_pra_integration.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/integration/test_pra_lifecycle.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/integration/test_pws_integration.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/integration/test_pws_lifecycle.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/pra/__init__.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/pra/test_client.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/pra/test_commands.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/pra-smoke-test.sh +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/pra-test-plan.md +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/pws/__init__.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/pws/test_client.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/pws/test_commands.py +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/pws-quick-test-plan.md +0 -0
- {bt_cli-0.4.32 → bt_cli-0.4.33}/tests/pws-smoke-test.sh +0 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: epml
|
|
3
|
+
description: EPM Linux commands for endpoint privilege management — RBP roles/cmdgrps/usergrps, policy import/export, test suites, transactions, audit. Use when working with PMUL/RBP, PAT auth against api.beyondtrust.io, or `/site/<id>/epm/linux/` URLs.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# EPM Linux Commands (`bt epml`)
|
|
7
|
+
|
|
8
|
+
## Configuration
|
|
9
|
+
|
|
10
|
+
EPM Linux speaks to the cloud gateway with a **Personal Access Token** (PAT).
|
|
11
|
+
The gateway URL takes a transform that the OpenAPI spec does NOT document — see "URL transform" below.
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
export BT_EPML_API_URL=https://api.beyondtrust.io # default; usually omit
|
|
15
|
+
export BT_EPML_SITE_ID=<your-site-uuid> # required
|
|
16
|
+
export BT_EPML_PAT=PAT_xxxxxxxxxx # required (mint at app.beyondtrust.io)
|
|
17
|
+
export BT_EPML_DEFAULT_HOST=100 # default PMUL host id (optional)
|
|
18
|
+
|
|
19
|
+
bt epml auth test # confirms /settings is reachable
|
|
20
|
+
bt epml auth status # show current config (no secrets)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## URL transform — important
|
|
24
|
+
|
|
25
|
+
The OpenAPI spec lists paths starting with `/api/...` and has an empty `servers`
|
|
26
|
+
block. The deployed gateway requires a site/product prefix that ISN'T in the
|
|
27
|
+
spec. The CLI handles this in one place (`_build_url`), but you'll see this if
|
|
28
|
+
you debug raw curl:
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
spec path: /api/<rest>
|
|
32
|
+
real URL: https://api.beyondtrust.io/site/<site-id>/epm/linux/<rest>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
A direct `curl https://api.beyondtrust.io/api/v1/users -H "Authorization: Bearer <PAT>"` returns
|
|
36
|
+
`401 {"error":"Access denied for this site"}` — that's NOT a PAT/audience problem,
|
|
37
|
+
it's a missing path prefix.
|
|
38
|
+
|
|
39
|
+
## Two-authorizer reality (PAT-OK vs IAM-blocked)
|
|
40
|
+
|
|
41
|
+
The spec declares `nomine-authorizer` (PAT) globally with zero per-op overrides,
|
|
42
|
+
but the gateway has some routes wired to AWS IAM (SigV4) instead. PAT cannot
|
|
43
|
+
reach those — they return either `403 {"message":"Invalid key=value pair... SHA-256... Base64..."}`
|
|
44
|
+
or `403 {"Message":"User is not authorized... no identity-based policy allows
|
|
45
|
+
the execute-api:Invoke action"}`. Same root, different envelope.
|
|
46
|
+
|
|
47
|
+
Known IAM-only paths (PAT-blocked):
|
|
48
|
+
- `PUT /settings` (read is fine)
|
|
49
|
+
- `POST/PUT/DELETE /v4/siems` (read is fine)
|
|
50
|
+
- `/v1/hosts`, `/v6/users`, `/v6/auth`, `/v1/auth`, `/v2/appfeatures`
|
|
51
|
+
- `/v7/directoryservices/*`, `/epml/license`, `/epml/licenseinfo`
|
|
52
|
+
- All `/v6/pbul/rbp/*` (use the legacy `/pbul/{hostid}/rbp/*` instead)
|
|
53
|
+
- **Inside testsuite**: `PUT /testsuite/{id}` and `POST /testsuite/test` (per-method split — list/get/create/delete/run *suites* are fine; PUT-test and DELETE-test are fine, but POST-add-test is IAM)
|
|
54
|
+
|
|
55
|
+
## Auth + connection
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
bt epml auth test # GET /settings, reports 401 vs 403 distinctly
|
|
59
|
+
bt epml auth status # show env config
|
|
60
|
+
bt whoami # includes EPM Linux row
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Settings, users, hosts, license
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
bt epml settings list # /api/settings
|
|
67
|
+
bt epml settings get session_timeout
|
|
68
|
+
bt epml users list # /api/v1/users (table)
|
|
69
|
+
bt epml users list --v2 # /api/v2/users
|
|
70
|
+
bt epml users get <user_id>
|
|
71
|
+
bt epml hosts list # synthesized from /pbul/features
|
|
72
|
+
bt epml hosts features # full feature flags per PMUL host
|
|
73
|
+
bt epml license get # /api/epml/enhancedlicense
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Note: `bt epml hosts list` is synthesized from `/api/pbul/features` because
|
|
77
|
+
`/api/v1/hosts` is IAM-blocked.
|
|
78
|
+
|
|
79
|
+
## Audit, SIEMs, External APIs, Iologs, Client packages
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
bt epml audit sessions list --take 50
|
|
83
|
+
bt epml audit sessions categories
|
|
84
|
+
bt epml siems list # read only — write is IAM
|
|
85
|
+
bt epml external-apis list
|
|
86
|
+
bt epml external-apis get <id>
|
|
87
|
+
bt epml iolog list # default host
|
|
88
|
+
bt epml iolog list -H 200 --start 0 --len 200
|
|
89
|
+
bt epml client-pkg list # signed S3 URLs (~30min TTL)
|
|
90
|
+
bt epml client-pkg status # build queue
|
|
91
|
+
bt epml client-pkg link epml-client.x86_64.rpm
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## RBP — Role-Based Policy
|
|
95
|
+
|
|
96
|
+
All under `/api/pbul/{hostid}/rbp/...` (default host = `BT_EPML_DEFAULT_HOST`).
|
|
97
|
+
Use `-H/--host` to override.
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# Command groups
|
|
101
|
+
bt epml rbp cmdgrps list
|
|
102
|
+
bt epml rbp cmdgrps create --name "Admin Tools" --description "..."
|
|
103
|
+
bt epml rbp cmdgrps delete 12,13
|
|
104
|
+
bt epml rbp cmdgrps commands list <cmdgrp_id>
|
|
105
|
+
bt epml rbp cmdgrps commands add <cmdgrp_id> --commands "vi,less,cat"
|
|
106
|
+
bt epml rbp cmdgrps commands remove <cmdgrp_id> --ids 1,2
|
|
107
|
+
|
|
108
|
+
# Host groups
|
|
109
|
+
bt epml rbp hostgrps list
|
|
110
|
+
bt epml rbp hostgrps create --name "Admin Hosts"
|
|
111
|
+
bt epml rbp hostgrps hosts add <hostgrp_id> --hosts "web-*,db-*"
|
|
112
|
+
|
|
113
|
+
# User groups
|
|
114
|
+
bt epml rbp usergrps list
|
|
115
|
+
bt epml rbp usergrps create --name "Helpdesk"
|
|
116
|
+
bt epml rbp usergrps users add <usergrp_id> --users "alice,bob"
|
|
117
|
+
|
|
118
|
+
# Time/date groups
|
|
119
|
+
bt epml rbp tmdategrps list
|
|
120
|
+
bt epml rbp tmdategrps create --name "Working Hours"
|
|
121
|
+
|
|
122
|
+
# Roles + assignments
|
|
123
|
+
bt epml rbp roles list
|
|
124
|
+
bt epml rbp roles create --name "Helpdesk Role"
|
|
125
|
+
bt epml rbp roles duplicate <role_id>
|
|
126
|
+
bt epml rbp roles cmdgrps add <role_id> --ids 1,2
|
|
127
|
+
bt epml rbp roles cmdgrps remove <role_id> <cmdgrp_id>
|
|
128
|
+
bt epml rbp roles hostgrps list <role_id>
|
|
129
|
+
bt epml rbp roles usergrps add <role_id> --ids 5
|
|
130
|
+
bt epml rbp roles tmdategrps add <role_id> --ids 1
|
|
131
|
+
|
|
132
|
+
# Entitlement report ('who can do what')
|
|
133
|
+
bt epml rbp entitlement run
|
|
134
|
+
bt epml rbp entitlement run --raw
|
|
135
|
+
|
|
136
|
+
# Policy export/import/versions
|
|
137
|
+
bt epml rbp policy export --file policy.json
|
|
138
|
+
bt epml rbp policy import policy.json
|
|
139
|
+
bt epml rbp policy versions
|
|
140
|
+
bt epml rbp policy diff
|
|
141
|
+
bt epml rbp policy snapshot
|
|
142
|
+
bt epml rbp policy revert
|
|
143
|
+
bt epml rbp policy delete-all --yes-i-mean-it # destructive, gated
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Test suites + transactions (the killer workflow)
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
# Suites (CRUD is mostly Bearer-OK; update-suite and add-test are IAM-only — see caveats)
|
|
150
|
+
bt epml rbp tests suites list
|
|
151
|
+
bt epml rbp tests suites create -n "production-policy" -d "..."
|
|
152
|
+
bt epml rbp tests suites get <suite_id>
|
|
153
|
+
bt epml rbp tests suites delete <suite_id>
|
|
154
|
+
|
|
155
|
+
# Tests within a suite (add is IAM-blocked → use the GUI to add tests; CLI to run)
|
|
156
|
+
bt epml rbp tests tests update <test_id> -f test.json
|
|
157
|
+
bt epml rbp tests tests delete <test_id>
|
|
158
|
+
|
|
159
|
+
# Run a suite — exits 0 on pass, 1 on any failure
|
|
160
|
+
bt epml rbp tests run --suite <suite_id>
|
|
161
|
+
|
|
162
|
+
# Transactions
|
|
163
|
+
bt epml rbp tx status
|
|
164
|
+
bt epml rbp tx begin
|
|
165
|
+
bt epml rbp tx commit
|
|
166
|
+
bt epml rbp tx rollback
|
|
167
|
+
bt epml rbp tx abort
|
|
168
|
+
|
|
169
|
+
# Quick: tests-then-deploy — open a tx, edit, then commit-or-rollback based on tests
|
|
170
|
+
bt epml rbp tx begin
|
|
171
|
+
# ... make changes via rbp cmdgrps/roles/etc ...
|
|
172
|
+
bt epml quick tests-then-deploy --suite <suite_id> # commits if pass; rollbacks if fail
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Destructive operations (always confirm)
|
|
176
|
+
|
|
177
|
+
- `bt epml rbp policy delete-all --yes-i-mean-it` — wipes the entire RBP DB
|
|
178
|
+
- `bt epml rbp <resource> delete <ids>` — bulk delete by ID list
|
|
179
|
+
- `bt epml rbp tx commit` — applies in-flight changes
|
|
180
|
+
- `bt epml client-pkg ...` POST endpoints — triggers a real installer build
|
|
181
|
+
|
|
182
|
+
## Path-version policy (CLI internal)
|
|
183
|
+
|
|
184
|
+
Where the spec offers both legacy `/api/pbul/{hostid}/rbp/<x>` and newer
|
|
185
|
+
`/api/v6/pbul/rbp/<x>`, the CLI uses the **legacy** version because v6 routes
|
|
186
|
+
are mostly IAM-only. If the API team flips v6 to PAT later, the swap is a
|
|
187
|
+
single-PR change in `client/base.py`.
|
|
188
|
+
|
|
189
|
+
## Output formats
|
|
190
|
+
|
|
191
|
+
All `list` and `get` commands accept `-o/--output table|json`. Table is the
|
|
192
|
+
default for list; JSON is the default for single-resource gets.
|
|
193
|
+
|
|
194
|
+
## Troubleshooting
|
|
195
|
+
|
|
196
|
+
- **401 "Access denied for this site"** — the URL didn't include `/site/<id>/epm/linux/`. Should never happen via the CLI; if you see it, file a bug.
|
|
197
|
+
- **403 IAM error** — the operation is on an IAM-only route. See the IAM-blocked list above.
|
|
198
|
+
- **400 with detailed validation messages** — auth passed; the body shape is wrong. Cross-reference `/home/admin/epml-cloud-spec.json` schemas.
|
|
199
|
+
- **Build server returns "building": true** — `bt epml client-pkg status` keeps `true` while a build is queued; idempotent to re-check.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# BT-CLI
|
|
2
2
|
|
|
3
|
-
BeyondTrust Platform CLI for Password Safe, Entitle, PRA, and EPM
|
|
3
|
+
BeyondTrust Platform CLI for Password Safe, Entitle, PRA, EPM Windows, and EPM Linux. **Version: 0.4.33**
|
|
4
4
|
|
|
5
5
|
## Setup
|
|
6
6
|
|
|
@@ -21,6 +21,7 @@ Use these slash commands for detailed product guidance:
|
|
|
21
21
|
| `/pra` | PRA - jump items, vault, SSH CA |
|
|
22
22
|
| `/entitle` | Entitle - JIT access, bundles, workflows |
|
|
23
23
|
| `/epmw` | EPM Windows - computers, policies, requests |
|
|
24
|
+
| `/epml` | EPM Linux - RBP roles/cmdgrps/usergrps, policy, test suites, transactions |
|
|
24
25
|
|
|
25
26
|
## Command Structure
|
|
26
27
|
|
|
@@ -31,6 +32,7 @@ Use these slash commands for detailed product guidance:
|
|
|
31
32
|
| PRA | `bt pra` | `jump-items`, `vault`, `jump-groups`, `quick` |
|
|
32
33
|
| Entitle | `bt entitle` | `integrations`, `resources`, `bundles`, `permissions` |
|
|
33
34
|
| EPM Windows | `bt epmw` | `computers`, `groups`, `policies`, `requests`, `quick` |
|
|
35
|
+
| EPM Linux | `bt epml` | `rbp` (cmdgrps/hostgrps/usergrps/tmdategrps/roles/policy/tests/tx), `settings`, `users`, `audit`, `siems`, `quick` |
|
|
34
36
|
|
|
35
37
|
## Common Patterns
|
|
36
38
|
|
|
@@ -58,6 +60,7 @@ PASSWORD=$(bt pws quick checkout -s server -a admin --raw)
|
|
|
58
60
|
| Entitle | `page`/`perPage` | `{"result": [...], "pagination": {...}}` |
|
|
59
61
|
| PRA | `per_page`/`current_page` (1-indexed) | Array (pagination in headers) |
|
|
60
62
|
| EPMW | `pageNumber`/`pageSize` | `{"data": [...], "totalCount": N}` |
|
|
63
|
+
| EPML | varies — `take`/`skip` (audit, siems), `start`/`len` (iologs), plain arrays | mostly arrays; some `{total, data}` |
|
|
61
64
|
|
|
62
65
|
**Important:**
|
|
63
66
|
- EPMW computers: Use `archive` not `delete` (405 error)
|
|
@@ -67,6 +70,8 @@ PASSWORD=$(bt pws quick checkout -s server -a admin --raw)
|
|
|
67
70
|
- Entitle permissions: Use server-side filters (`--resource`, `--user`, `--integration`) - dataset can be 20k+ records
|
|
68
71
|
- Entitle users: Use `--all` flag only when needed - default fetches first page only
|
|
69
72
|
- Windows builds: Rich must be pinned to `<14.0.0` (Rich 14 has PyInstaller unicode issues)
|
|
73
|
+
- **EPML URL transform**: spec paths start with `/api/...` but real URL is `https://api.beyondtrust.io/site/<site-id>/epm/linux/<spec-path-minus-/api/>`. Encapsulated in client `_build_url` — never bypass it.
|
|
74
|
+
- **EPML two authorizers**: spec claims `nomine-authorizer` (PAT) globally but the gateway has IAM-only routes (return 403 with AWS-flavored body). PAT cannot reach those. Documented per-op in `src/bt_cli/data/skills/epml/SKILL.md`. Where `/v6/pbul/rbp/...` exists alongside legacy `/pbul/{hostid}/rbp/...`, prefer legacy — v6 is mostly IAM-only.
|
|
70
75
|
|
|
71
76
|
## Functional vs Managed Accounts
|
|
72
77
|
|
|
@@ -94,7 +99,8 @@ src/bt_cli/
|
|
|
94
99
|
├── pws/ # Password Safe
|
|
95
100
|
├── pra/ # PRA
|
|
96
101
|
├── entitle/ # Entitle
|
|
97
|
-
|
|
102
|
+
├── epmw/ # EPM Windows
|
|
103
|
+
└── epml/ # EPM Linux (PAT auth, /site/<id>/epm/linux/... URLs)
|
|
98
104
|
```
|
|
99
105
|
|
|
100
106
|
Each product follows: `client/` (API), `commands/` (CLI), `models/` (Pydantic)
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
# Plan: Add EPM Linux (`bt epml`) to bt-cli
|
|
2
|
+
|
|
3
|
+
## Context
|
|
4
|
+
|
|
5
|
+
`bt-cli` v0.4.32 already supports PWS, PRA, Entitle, EPMW. EPM Linux is the Linux counterpart to EPMW (Windows). API is reached via the BeyondTrust public gateway (`api.beyondtrust.io`) authorized by Personal Access Tokens (PATs) issued from `app.beyondtrust.io`.
|
|
6
|
+
|
|
7
|
+
Discovery is done. Cached spec at `/home/admin/epml-cloud-spec.json` (OpenAPI 3.0.0, version 25.1.6, 160 paths / 205 ops, 12 product-area tags). Verified live against site `c882e07c-753b-4fcd-a1e6-7d39defec5ae` on 2026-05-01.
|
|
8
|
+
|
|
9
|
+
## Two non-obvious gateway facts the spec doesn't tell you
|
|
10
|
+
|
|
11
|
+
### 1. URL transform
|
|
12
|
+
|
|
13
|
+
The spec lists every path with a `/api/` prefix and an empty `servers` block. The gateway does NOT route those paths at the document root. Real URL shape:
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
https://api.beyondtrust.io/site/<site-id>/epm/linux/<spec-path-minus-leading-/api/>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
So spec `/api/v1/users` → real URL `/site/<site-id>/epm/linux/v1/users`. Spec `/api/pbul/{hostid}/rbp/cmdgrps` → real URL `/site/<site-id>/epm/linux/pbul/{hostid}/rbp/cmdgrps`.
|
|
20
|
+
|
|
21
|
+
### 2. Two authorizers, one spec
|
|
22
|
+
|
|
23
|
+
Spec declares `nomine-authorizer` (PAT) globally with **zero per-op overrides**. The gateway disagrees: a chunk of routes are wired to AWS IAM (SigV4), and PATs cannot reach those.
|
|
24
|
+
|
|
25
|
+
- **PAT-Bearer routes (200 OK)** — most `/pbul/{hostid}/rbp/...`, `/pbul/features`, `/pbul/rbp/testsuite/*`, `/v1/users`, `/v2/users`, `/v4/audit/sessions/*`, `/v4/siems` (read), `/externalapis`, `/epml/enhancedlicense`, `/epml/clientpkg`, `/settings` (read), `/pbul/{hostid}/iologs`.
|
|
26
|
+
- **IAM-only routes (403, AWS SigV4 error)** — all `/v6/pbul/rbp/*`, `/v1/hosts`, `/v6/users`, `/v1/auth/*`, `/v6/auth/*`, `/v2/appfeatures`, `/v7/directoryservices`, `/epml/license`, `/epml/licenseinfo`, `/v4/audit` (root), `/pbul/{hostid}/rbp/usrgrps`, `/pbul/{hostid}/rbp/timedategrps`, `/pbul/{hostid}/rbp/version`, `PUT /settings`, `POST /v4/siems`, etc.
|
|
27
|
+
|
|
28
|
+
**Path-version policy**: prefer legacy `/pbul/{hostid}/rbp/...` over the newer `/v6/pbul/rbp/...` because the legacy host-scoped variants are mostly Bearer-reachable, the v6 variants mostly aren't. Opposite of what spec-versioning instinct suggests.
|
|
29
|
+
|
|
30
|
+
### Key gap for downstream design
|
|
31
|
+
|
|
32
|
+
**[Updated 2026-05-01]** Earlier draft called out `/pbul/{hostid}/rbp/usrgrps` as IAM-blocked — that was a path-name typo on my end. The real spec name is `usergrps`, and it (along with `tmdategrps` and `versions`) is fully Bearer-reachable. **No critical gaps in the RBP family for a PAT-only CLI.** All four child entity types under a role (cmdgrps, hostgrps, usergrps, tmdategrps) plus full policy export/import/versions/transactions ship as Bearer.
|
|
33
|
+
|
|
34
|
+
## Command tree: `bt epml`
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
bt epml
|
|
38
|
+
auth test | status
|
|
39
|
+
settings list | get
|
|
40
|
+
hosts list # synthesized from /pbul/features (since /v1/hosts is IAM)
|
|
41
|
+
users list | get
|
|
42
|
+
license get # /epml/enhancedlicense
|
|
43
|
+
siems list | get # read only — write is IAM
|
|
44
|
+
audit sessions list | sessions get | sessions categories
|
|
45
|
+
external-apis list | get | create | update | delete
|
|
46
|
+
client-pkg list | status | build # build = POST /epml/clientpkg
|
|
47
|
+
iolog list | get | replay | playback | commands
|
|
48
|
+
rbp
|
|
49
|
+
cmdgrps list | get | create | update | delete | members list/add/remove
|
|
50
|
+
hostgrps list | get | create | update | delete | members ...
|
|
51
|
+
roles list | get | create | update | delete | duplicate
|
|
52
|
+
cmdgrps add/remove | hostgrps add/remove
|
|
53
|
+
entitlement run # /pbul/{hostid}/rbp/entitlement
|
|
54
|
+
policy export | import | versions # versions list / get / revert
|
|
55
|
+
tests suites list | suites get | suites create | suites update | suites delete
|
|
56
|
+
tests add | tests update | tests delete | run
|
|
57
|
+
tx begin | commit | rollback | status
|
|
58
|
+
quick pasm-import-policy | tests-then-deploy # cross-product workflows (later)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Deferred behind IAM (don't implement; document in skill file):
|
|
62
|
+
- `bt epml rbp usrgrps`, `rbp timedategrps`, `rbp version`
|
|
63
|
+
- `bt epml settings update`
|
|
64
|
+
- `bt epml siems create|update|delete`
|
|
65
|
+
- `bt epml license upload|retire|clients`
|
|
66
|
+
- `bt epml directory ...` (entire DirectoryServices tag)
|
|
67
|
+
- `bt epml auth roles|users|...` (appliance RBAC mgmt)
|
|
68
|
+
- Everything under `/v6/pbul/rbp/*`
|
|
69
|
+
|
|
70
|
+
## Implementation phases
|
|
71
|
+
|
|
72
|
+
### Phase 1 — Foundation + auth smoke (one PR)
|
|
73
|
+
|
|
74
|
+
**1a. New `PATAuth` strategy** — `src/bt_cli/core/auth.py`
|
|
75
|
+
- Class `PATAuth(AuthStrategy)` with `pat: str` (and optional `site_id` for URL composition).
|
|
76
|
+
- `authenticate()` is a no-op (PAT is already a bearer credential, no token exchange).
|
|
77
|
+
- Sets `Authorization: Bearer <pat>` on every request.
|
|
78
|
+
- No `sign_out()`.
|
|
79
|
+
|
|
80
|
+
**1b. New `EPMLConfig`** — `src/bt_cli/core/config.py`
|
|
81
|
+
- Fields: `api_url` (default `https://api.beyondtrust.io`), `site_id`, `pat`, `default_host_id` (default `100`), `verify_ssl`, `timeout`.
|
|
82
|
+
- `load_epml_config()` env vars: `BT_EPML_API_URL`, `BT_EPML_SITE_ID`, `BT_EPML_PAT`, `BT_EPML_DEFAULT_HOST`, `BT_EPML_VERIFY_SSL`, `BT_EPML_TIMEOUT`.
|
|
83
|
+
- Wire into `load_config()` dispatcher under key `"epml"`.
|
|
84
|
+
|
|
85
|
+
**1c. Add `"epml"` entry in `src/bt_cli/core/config_file.py` PRODUCTS dict**
|
|
86
|
+
- Fields: `api_url`, `site_id`, `pat`, `default_host_id`, `verify_ssl`, `timeout`.
|
|
87
|
+
|
|
88
|
+
**1d. Module skeleton**
|
|
89
|
+
- `src/bt_cli/epml/__init__.py`
|
|
90
|
+
- `src/bt_cli/epml/client/__init__.py` — exports `EPMLClient, get_client`
|
|
91
|
+
- `src/bt_cli/epml/client/base.py` — httpx client. **Critical detail**: every request URL is built as `f"{api_url}/site/{site_id}/epm/linux{path}"` where `path` is the spec path with `/api/` stripped. Provide a single `_build_path(spec_path: str) -> str` helper used by every method.
|
|
92
|
+
- `src/bt_cli/epml/commands/__init__.py` — registers sub-typer apps
|
|
93
|
+
- `src/bt_cli/epml/commands/auth.py` — `test` calls `GET /settings` and reports 200/403/401 distinctly (the 401 vs 403 split tells us which of the two failure modes — wrong site prefix vs IAM-blocked path — happened).
|
|
94
|
+
- `src/bt_cli/epml/models/__init__.py`
|
|
95
|
+
- `src/bt_cli/epml/models/common.py`
|
|
96
|
+
|
|
97
|
+
**1e. Wire into CLI** — `src/bt_cli/cli.py`
|
|
98
|
+
- `_get_epml_app()` lazy loader.
|
|
99
|
+
- `app.add_typer(...)` registration.
|
|
100
|
+
- Add `_test_epml_connection()` → list settings.
|
|
101
|
+
- Add EPM-L line to `whoami` output.
|
|
102
|
+
- Add `"epml"` to the valid-product list in `tree_command` and `find` command.
|
|
103
|
+
- Update `main_callback` help text.
|
|
104
|
+
|
|
105
|
+
**Verify**: `bt epml auth test` connects, gets 200 from `/settings`, prints "Connected to EPM Linux at site <id>".
|
|
106
|
+
|
|
107
|
+
### Phase 2 — Read surface (one PR)
|
|
108
|
+
|
|
109
|
+
Models + client methods + commands for everything that's purely a GET against a Bearer-reachable path:
|
|
110
|
+
|
|
111
|
+
- `settings list/get` — `/settings`
|
|
112
|
+
- `users list` — `/v1/users` (paginated; spec lists `page`/`pageSize`)
|
|
113
|
+
- `users get` — `/v1/users/{id}`
|
|
114
|
+
- `hosts list` — derived from `/pbul/features` JSON (extract HostID, Hostname, IP, PbulVersion, Features map). Synthesized resource since `/v1/hosts` is IAM-blocked.
|
|
115
|
+
- `license get` — `/epml/enhancedlicense`
|
|
116
|
+
- `siems list` — `/v4/siems`
|
|
117
|
+
- `siems get` — `/v4/siems/{id}` (verify Bearer first)
|
|
118
|
+
- `audit sessions list` — `/v4/audit/sessions`
|
|
119
|
+
- `audit sessions get` — `/v4/audit/sessions/{id}`
|
|
120
|
+
- `audit sessions categories` — `/v4/audit/sessions/categories`
|
|
121
|
+
- `client-pkg list` — `/epml/clientpkg`
|
|
122
|
+
- `client-pkg status` — `/epml/clientpkg/status`
|
|
123
|
+
- `iolog list` — `/pbul/{hostid}/iologs` (host-scoped — defaults to `default_host_id`, override `--host`)
|
|
124
|
+
|
|
125
|
+
Pydantic models in `models/` per resource. Output formatter handles JSON/table/raw consistently with PWS pattern.
|
|
126
|
+
|
|
127
|
+
### Phase 3 — RBP CRUD (one PR, biggest)
|
|
128
|
+
|
|
129
|
+
Resources, all under `/pbul/{hostid}/rbp/...`:
|
|
130
|
+
|
|
131
|
+
**Command groups** (`rbp cmdgrps`)
|
|
132
|
+
- `list` — `GET /pbul/{hostid}/rbp/cmdgrps`
|
|
133
|
+
- `get` — `GET /pbul/{hostid}/rbp/cmdgrps/{id}`
|
|
134
|
+
- `create` — `POST /pbul/{hostid}/rbp/cmdgrps`
|
|
135
|
+
- `update` — `PUT /pbul/{hostid}/rbp/cmdgrps/{id}`
|
|
136
|
+
- `delete` — `DELETE /pbul/{hostid}/rbp/cmdgrps/{id}`
|
|
137
|
+
- `members list|add|remove` — child resource
|
|
138
|
+
|
|
139
|
+
**Host groups** (`rbp hostgrps`) — same shape
|
|
140
|
+
|
|
141
|
+
**Roles** (`rbp roles`)
|
|
142
|
+
- CRUD + `duplicate` (if spec has it; otherwise GET-then-POST)
|
|
143
|
+
- Sub-resources: cmdgrps, hostgrps add/remove (NOT usrgrps — IAM-blocked)
|
|
144
|
+
|
|
145
|
+
**Entitlement report** (`rbp entitlement run`)
|
|
146
|
+
- `GET /pbul/{hostid}/rbp/entitlement` — returns "who can run what" report
|
|
147
|
+
- `--raw` flag → `/pbul/{hostid}/rbp/entitlement/raw`
|
|
148
|
+
|
|
149
|
+
**Policy** (`rbp policy`)
|
|
150
|
+
- `export` — full RBP DB serialization
|
|
151
|
+
- `import` — file upload
|
|
152
|
+
- `versions list|get|revert`
|
|
153
|
+
- `delete-all` — gated behind `--yes-i-mean-it` confirmation
|
|
154
|
+
|
|
155
|
+
### Phase 4 — RBP test suites + transactions (one PR)
|
|
156
|
+
|
|
157
|
+
**Test suites** (`rbp tests`)
|
|
158
|
+
- `suites list|get|create|update|delete`
|
|
159
|
+
- `tests add|update|delete` (under a suite)
|
|
160
|
+
- `run --suite <id>` — `GET /pbul/{hostid}/rbp/testsuite/{id}/run` returns `{total, failures}` — exit 0 on zero failures, exit 1 with failure detail otherwise. **This is the killer command.**
|
|
161
|
+
|
|
162
|
+
**Transactions** (`rbp tx`)
|
|
163
|
+
- `begin` — opens a transaction (returns transaction ID)
|
|
164
|
+
- `commit` — atomic apply
|
|
165
|
+
- `rollback` — discard
|
|
166
|
+
- `status` — current pending changes
|
|
167
|
+
|
|
168
|
+
Pair them in a `quick tests-then-deploy` workflow:
|
|
169
|
+
1. `tx begin`
|
|
170
|
+
2. apply user-supplied changes
|
|
171
|
+
3. `tests run --suite <id>`
|
|
172
|
+
4. on success → `tx commit`; on failure → `tx rollback`
|
|
173
|
+
|
|
174
|
+
### Phase 5 — Tests + skill + docs (one PR)
|
|
175
|
+
|
|
176
|
+
- `tests/epml/__init__.py`
|
|
177
|
+
- `tests/epml/test_client.py` — respx-mocked unit tests for URL transform, auth header, pagination
|
|
178
|
+
- `tests/epml/test_commands.py` — CLI invocations with mocked client
|
|
179
|
+
- `tests/conftest.py` — `mock_epml_config`, `epml_env_vars` fixtures
|
|
180
|
+
- `tests/fixtures/responses.py` — `EPML_*` constants
|
|
181
|
+
- `src/bt_cli/data/skills/epml/SKILL.md` — command reference, the URL-transform gotcha, the IAM-blocked list, host-id concept, test-suite workflow
|
|
182
|
+
- Update `btcli/CLAUDE.md` and `src/bt_cli/data/CLAUDE.md` — add `bt epml` row to the Command Structure table
|
|
183
|
+
|
|
184
|
+
## Files to create (≈25)
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
src/bt_cli/epml/__init__.py
|
|
188
|
+
src/bt_cli/epml/client/__init__.py
|
|
189
|
+
src/bt_cli/epml/client/base.py
|
|
190
|
+
src/bt_cli/epml/commands/__init__.py
|
|
191
|
+
src/bt_cli/epml/commands/auth.py
|
|
192
|
+
src/bt_cli/epml/commands/settings.py
|
|
193
|
+
src/bt_cli/epml/commands/users.py
|
|
194
|
+
src/bt_cli/epml/commands/hosts.py
|
|
195
|
+
src/bt_cli/epml/commands/license.py
|
|
196
|
+
src/bt_cli/epml/commands/siems.py
|
|
197
|
+
src/bt_cli/epml/commands/audit.py
|
|
198
|
+
src/bt_cli/epml/commands/external_apis.py
|
|
199
|
+
src/bt_cli/epml/commands/client_pkg.py
|
|
200
|
+
src/bt_cli/epml/commands/iolog.py
|
|
201
|
+
src/bt_cli/epml/commands/rbp_cmdgrps.py
|
|
202
|
+
src/bt_cli/epml/commands/rbp_hostgrps.py
|
|
203
|
+
src/bt_cli/epml/commands/rbp_roles.py
|
|
204
|
+
src/bt_cli/epml/commands/rbp_entitlement.py
|
|
205
|
+
src/bt_cli/epml/commands/rbp_policy.py
|
|
206
|
+
src/bt_cli/epml/commands/rbp_tests.py
|
|
207
|
+
src/bt_cli/epml/commands/rbp_tx.py
|
|
208
|
+
src/bt_cli/epml/models/__init__.py
|
|
209
|
+
src/bt_cli/epml/models/common.py
|
|
210
|
+
src/bt_cli/epml/models/{user,host,license,siem,audit,clientpkg,cmdgrp,hostgrp,role,test,policy,settings,iolog}.py (one per resource as needed)
|
|
211
|
+
src/bt_cli/data/skills/epml/SKILL.md
|
|
212
|
+
tests/epml/__init__.py
|
|
213
|
+
tests/epml/test_client.py
|
|
214
|
+
tests/epml/test_commands.py
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Files to modify
|
|
218
|
+
|
|
219
|
+
```
|
|
220
|
+
src/bt_cli/core/auth.py — add PATAuth (~25 lines)
|
|
221
|
+
src/bt_cli/core/config.py — add EPMLConfig + load_epml_config (~40 lines)
|
|
222
|
+
src/bt_cli/core/config_file.py — add "epml" entry (~10 lines)
|
|
223
|
+
src/bt_cli/cli.py — lazy loader, register, whoami test, tree, find, help text
|
|
224
|
+
tests/conftest.py — fixtures
|
|
225
|
+
tests/fixtures/responses.py — EPML_* mock constants
|
|
226
|
+
btcli/CLAUDE.md — add bt epml row + 1-line callout for the URL transform
|
|
227
|
+
src/bt_cli/data/CLAUDE.md — same
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Key design decisions
|
|
231
|
+
|
|
232
|
+
1. **Auth: dedicated `PATAuth` strategy.** Cleaner than overloading existing OAuth2 strategies; the auth flow is genuinely different (no token exchange, just a static bearer credential).
|
|
233
|
+
2. **URL transform encapsulated in `_build_path()`.** Every client method calls it. Anyone reading the spec and writing a new method needs zero awareness — they paste the spec path verbatim.
|
|
234
|
+
3. **Synthesize `hosts list` from `/pbul/features`.** `/v1/hosts` is IAM-blocked, but `/pbul/features` returns the host data we need anyway. Surface as a normal-looking command; document the substitution in the skill file.
|
|
235
|
+
4. **Default host-id baked into config.** Most RBP commands take `--host` but default to `BT_EPML_DEFAULT_HOST` (default `100` per the discovery). Mirrors how PWS handles workgroup IDs.
|
|
236
|
+
5. **Skip everything IAM-blocked.** Don't add stub commands. Skill file documents what's intentionally absent and why, so users don't ask "why no `bt epml directory`."
|
|
237
|
+
6. **Path-version pick: legacy over v6.** Where the spec has both `/pbul/{hostid}/rbp/X` (legacy) and `/v6/pbul/rbp/X` (newer), use legacy — it's Bearer, v6 is IAM. If the API team flips v6 to Bearer later, swap in a single PR.
|
|
238
|
+
7. **Env var prefix `BT_EPML_`.** Consistent with `BT_PWS_`, `BT_PRA_`, `BT_EPM_` (Windows).
|
|
239
|
+
|
|
240
|
+
## What to bring back to the API team
|
|
241
|
+
|
|
242
|
+
Capture in the skill file as known issues; track separately as upstream asks:
|
|
243
|
+
|
|
244
|
+
1. Spec global security claims `nomine-authorizer` for every op, but the gateway has IAM-only routes. Either flip those to Bearer or override per-op `security` in the spec so it stops lying.
|
|
245
|
+
2. `/pbul/{hostid}/rbp/usrgrps` IAM-only blocks core RBP workflows — highest-priority flip request.
|
|
246
|
+
3. `/settings` is read-Bearer but write-IAM (same path, two authorizers per method). Unusual config.
|
|
247
|
+
4. Service-account / non-human auth path. PATs are user-bound. CLI/automation needs an alternative; nothing in the EPM-L surface offers one today.
|
|
248
|
+
5. `servers` is empty in the spec; the `/site/<id>/epm/linux/` prefix is invisible to anyone reading the OpenAPI doc. Add `servers: [{url: "https://api.beyondtrust.io/site/{siteId}/epm/linux", variables: {...}}]` and strip `/api/` from paths.
|
|
249
|
+
|
|
250
|
+
## Verification
|
|
251
|
+
|
|
252
|
+
1. `bt epml auth test` — 200 from `/settings`
|
|
253
|
+
2. `bt epml hosts list` — shows HostID 100 PMULPolicy
|
|
254
|
+
3. `bt epml users list` — returns user list
|
|
255
|
+
4. `bt epml rbp cmdgrps list --host 100` — non-empty
|
|
256
|
+
5. `bt epml rbp tests run --suite <id>` — runs and exits cleanly
|
|
257
|
+
6. `bt tree epml` — full tree
|
|
258
|
+
7. `bt whoami` — includes EPM-L line
|
|
259
|
+
8. `pytest tests/epml/ -v` — all green
|