cmdbsyncer 4.2.1__tar.gz → 4.2.2__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.
- {cmdbsyncer-4.2.1/cmdbsyncer.egg-info → cmdbsyncer-4.2.2}/PKG-INFO +1 -1
- cmdbsyncer-4.2.2/application/buildinfo.txt +1 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/changelog/v4.2.md +18 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/helpers/syncer_jinja.py +12 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/ansible/runner.py +11 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/ansible/site_syncer.py +1 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/admin_views.py +18 -26
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/cmk_rules.py +23 -4
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/inits.py +34 -4
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/models.py +1 -0
- cmdbsyncer-4.2.2/application/plugins/checkmk/rule_passwords.py +126 -0
- cmdbsyncer-4.2.2/application/plugins/checkmk/tests/test_rule_passwords.py +160 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/views.py +44 -18
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/static/css/cmdbsyncer.css +28 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/checkmk_rule_project_import_cmk.html +38 -2
- cmdbsyncer-4.2.2/application/templates/admin/checkmk_test_folder_scope.html +219 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2/cmdbsyncer.egg-info}/PKG-INFO +1 -1
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/cmdbsyncer.egg-info/SOURCES.txt +2 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/pyproject.toml +1 -1
- cmdbsyncer-4.2.1/application/buildinfo.txt +0 -1
- cmdbsyncer-4.2.1/application/templates/admin/checkmk_test_folder_scope.html +0 -101
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/LICENSE +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/README.md +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/_version.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/api/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/api/inventory.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/api/objects.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/api/rules.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/api/syncer.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/api/views.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/auth/views.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/changelog/.pages +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/changelog/v3.10.md +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/changelog/v3.11.md +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/changelog/v3.12.md +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/changelog/v3.5.md +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/changelog/v3.7.md +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/changelog/v3.8.md +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/changelog/v3.9.md +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/changelog/v4.0.md +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/changelog/v4.1.md +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/cli.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/config.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/docu_links.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/enterprise.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/helpers/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/helpers/audit.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/helpers/cron.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/helpers/get_account.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/helpers/inventory.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/helpers/local_config_file.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/helpers/local_config_presets.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/helpers/mongo_keys.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/helpers/notification_channels.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/helpers/notification_dispatch.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/helpers/plugins.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/helpers/sates.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/helpers/sql.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/helpers/tablib_formater.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/helpers/tests/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/helpers/tests/test_sql.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/mcp_server.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/models/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/models/account.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/models/config.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/models/cron.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/models/field_approval.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/models/forms.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/models/host.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/models/host_inventory_tree.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/models/notification_channel.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/models/notification_rule.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/models/notification_state.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/models/saved_search.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/models/states.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/models/user.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/custom_attributes/models.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/custom_attributes/rules.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/custom_attributes/views.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/debug.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/email.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/inventory/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/log/log.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/log/models.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/log/views.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/plugin.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/rule/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/rule/filter.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/rule/match.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/rule/models.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/rule/rewrite.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/rule/rule.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/rule/views.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/search_parser.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/modules/statefile.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/notices/strict_certificate_verification.txt +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/ansible/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/ansible/admin_views.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/ansible/inventory.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/ansible/models.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/ansible/playbook_rules.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/ansible/plugin.json +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/ansible/rules.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/ansible/seed.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/ansible/views.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/bmc_remedy/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/bmc_remedy/bmc_remedy.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/bmc_remedy/plugin.json +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/bi.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/cmk2.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/dcd.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/downtimes.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/groups.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/helpers.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/import_v1.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/import_v2.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/inventorize.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/notification_rules.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/passwords.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/plugin.json +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/poolfolder.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/rules.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/sites.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/syncer.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tags.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_bi.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_cmk2.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_cmk_rules.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_dcd.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_downtimes.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_groups.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_helpers.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_import_v1.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_import_v2.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_inventorize.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_notification_rules.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_passwords.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_poolfolder.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_rules.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_sites.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_syncer.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_tags.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/tests/test_users.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/checkmk/users.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/cisco_dna/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/cisco_dna/plugin.json +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/cisco_dna/syncer.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/cron.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/csv/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/csv/csv.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/csv/plugin.json +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/file_requests.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/idoit/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/idoit/admin_views.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/idoit/models.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/idoit/plugin.json +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/idoit/rules.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/idoit/syncer.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/idoit/views.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jdisc/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jdisc/applications.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jdisc/devices.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jdisc/executables.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jdisc/jdisc.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jdisc/plugin.json +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jira/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jira/jira.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jira/plugin.json +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jira_cloud/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jira_cloud/admin_views.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jira_cloud/export.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jira_cloud/jira_cloud.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jira_cloud/models.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jira_cloud/plugin.json +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jira_cloud/rules.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jira_cloud/schema_sync.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/jira_cloud/views.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/json/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/json/json.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/json/plugin.json +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/ldap/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/ldap/ldap.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/ldap/plugin.json +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/maintenance/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/maintenance/plugin.json +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/mssql/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/mssql/plugin.json +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/mysql/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/mysql/mysql.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/mysql/plugin.json +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/admin_views.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/cluster.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/contacts.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/dataflow.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/devices.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/interfaces.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/ips.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/models.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/netbox.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/networks.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/plugin.json +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/prefixes.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/rules.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/tests/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/tests/test_import_filter.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/views.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/netbox/virtualmachines.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/prtg/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/prtg/plugin.json +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/prtg/prtg.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/pyodbc/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/pyodbc/plugin.json +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/pyodbc/pyodbc.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/rest/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/rest/plugin.json +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/rest/rest.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/rules/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/rules/admin_views.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/rules/autorules.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/rules/models.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/rules/plugin.json +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/rules/rule_definitions.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/rules/rule_import_export.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/rules/views.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/syncer_shell.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/vmware/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/vmware/admin_views.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/vmware/custom_attributes.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/vmware/models.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/vmware/plugin.json +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/vmware/rules.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/vmware/views.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/vmware/vmware.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/yml/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/yml/plugin.json +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins/yml/yml.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/plugins_cli.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/static/cmdbsyncer_mark.png +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/static/favicon.png +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/static/js/main.js +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/static/logo_white.png +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/_brand.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/_theme.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/_host_tabs.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/account_pick_type.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/ansible_playbook_run.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/ansible_project_detail.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/ansible_rule_list.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/ansible_run_details.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/bulk_label_form.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/changelog_archive.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/checkmk_rule_mngmt_list.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/checkmk_rule_project_edit.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/checkmk_rule_project_import.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/checkmk_rule_project_overview.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/checkmk_settings_list.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/copy_as_new_form.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/data_quality.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/file/list.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/file/modals/form.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/host_details.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/host_edit.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/host_inventory_tree.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/host_list.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/host_relations_graph.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/host_timeline.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/index.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/local_config_editor.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/master.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/model/clone_confirm.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/model/inline_list_base.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/object_list.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/set_cmk_version_form.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/admin/set_template_form.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/base.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/change_password.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/debug.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/debug_host.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/email/newuser.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/email/notification.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/email/resetpassword.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/formular.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/license_info.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/login.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/logout_confirm.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/request_password.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/set_2fa.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/templates/set_theme.html +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/themes/dracula.css +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/themes/gruvbox-dark.css +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/themes/gruvbox-light.css +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/themes/nord.css +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/themes_registry.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/_form_fields.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/_form_sections.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/account.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/config.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/cron.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/data_quality.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/default.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/field_approval.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/fileadmin.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/host.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/host_filters.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/host_renderers.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/host_widgets.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/license.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/notification_channel.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/notification_rule.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/saved_search.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/application/views/user.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/cmdbsyncer.egg-info/dependency_links.txt +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/cmdbsyncer.egg-info/entry_points.txt +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/cmdbsyncer.egg-info/requires.txt +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/cmdbsyncer.egg-info/top_level.txt +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/requirements-ansible-windows.txt +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/requirements-ansible.txt +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/requirements-extras.txt +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/requirements.txt +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/setup.cfg +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/syncerapi/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/syncerapi/v1/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/syncerapi/v1/core/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/syncerapi/v1/inventory/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/syncerapi/v1/rest/__init__.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/tests/test_account_macro.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/tests/test_api.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/tests/test_api_csrf_invariant.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/tests/test_default_view.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/tests/test_mongo_keys.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/tests/test_plugin.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/tests/test_plugin_ssti.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/tests/test_plugins_cli.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/tests/test_rule.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/tests/test_rule_filename_detect.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/tests/test_rule_match.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/tests/test_search_parser.py +0 -0
- {cmdbsyncer-4.2.1 → cmdbsyncer-4.2.2}/tests/test_webhook_token_hash.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cmdbsyncer
|
|
3
|
-
Version: 4.2.
|
|
3
|
+
Version: 4.2.2
|
|
4
4
|
Summary: Rule-based modular synchronizer for Checkmk, Netbox, Ansible, I-DOIT, Jira CMDB and 15+ other CMDB/monitoring systems
|
|
5
5
|
Author-email: Kuhn & Ruess GmbH <info@kuhn-ruess.de>
|
|
6
6
|
Maintainer-email: Bastian Kuhn <mail@bastian-kuhn.de>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
BUILD_DATE=2026-07-14T11:01:00Z
|
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Version 4.2
|
|
2
2
|
|
|
3
|
+
## Version 4.2.2
|
|
4
|
+
|
|
5
|
+
### Ansible
|
|
6
|
+
- FIX: Playbook tasks delegated to localhost (`delegate_to: localhost`) no longer abort with "Failed to create temporary directory" under the web server — the local connection's temp dir now points at a writable per-run path instead of the web user's home
|
|
7
|
+
- FEAT: Checkmk Settings gain an "installation staging path" — the Checkmk server-management playbook stages the downloaded installer there instead of `/tmp`; leave it empty to keep using `/tmp`. Use it when the executing user can't reliably use `/tmp` (permission or private-tmp isolation)
|
|
8
|
+
- FIX: The Checkmk site-update playbook no longer fails the "set downtime" step with "401 Unauthorized" when no Checkmk API user/secret is configured — the downtime is skipped instead
|
|
9
|
+
|
|
10
|
+
### Checkmk
|
|
11
|
+
- FEAT: The Checkmk module menu is tidier — "Setup Rule Projects" now sits next to "Manage Checkmk Setup Rules", "Limit Host Export to Folders" moved under the host filters, and the separate "Export Scoping" submenu is gone
|
|
12
|
+
- FIX: "Limit Host Export to Folders" now lists rule folders in lowercase to match Checkmk's folder names, so the scope reliably matches the folders hosts are exported to
|
|
13
|
+
- FEAT: "Limit Host Export to Folders" is easier to use — clicking a rule folder or typing one adds it to a list of selected folders you can remove individually, instead of editing a comma-separated text field
|
|
14
|
+
- FEAT: Importing Setup Rules from a Checkmk folder into a project now shows a progress bar and status message while it runs, so you can see it is working
|
|
15
|
+
- FEAT: Checkmk Setup Rules can reference a syncer Checkmk Password with `{{ cmk_password("name") }}`, which resolves to that entry's Checkmk password-store ident on export — run the password export so the entry exists in the target Checkmk
|
|
16
|
+
- FEAT: Importing rules from a Checkmk folder now rewrites inline rule passwords (which Checkmk masks as `******` on read) into such password-store references, so imported rules deploy from a test to a production Checkmk without exposing or overwriting the secret — create a Checkmk Password with the reported name and export it
|
|
17
|
+
|
|
18
|
+
### Interface
|
|
19
|
+
- FIX: Card-style admin form pages (e.g. the "New Account" plugin-type picker) render with consistent padding and a theme-aware header in all four themes, instead of a header hanging off the edge of a flat card
|
|
20
|
+
|
|
3
21
|
## Version 4.2.1
|
|
4
22
|
|
|
5
23
|
### Ansible
|
|
@@ -48,6 +48,17 @@ def _cmk_cleanup_hostname(value):
|
|
|
48
48
|
return _cleanup(value)
|
|
49
49
|
|
|
50
50
|
|
|
51
|
+
def _cmk_password(name):
|
|
52
|
+
"""
|
|
53
|
+
Resolve a syncer Checkmk Password name to its password-store ident
|
|
54
|
+
(``cmdbsyncer_<id>``) so rules can reference a stored secret instead of an
|
|
55
|
+
inlined one. Lazily imported to avoid circular imports.
|
|
56
|
+
"""
|
|
57
|
+
from application.plugins.checkmk.rule_passwords import password_ident
|
|
58
|
+
|
|
59
|
+
return password_ident(name)
|
|
60
|
+
|
|
61
|
+
|
|
51
62
|
def syncer_eval(string, default=None):
|
|
52
63
|
"""
|
|
53
64
|
Evals given object
|
|
@@ -139,6 +150,7 @@ _GLOBALS = {
|
|
|
139
150
|
'merge_list_of_dicts': merge_list_of_dicts,
|
|
140
151
|
'cmk_cleanup_tag_id': _cmk_cleanup_tag_id,
|
|
141
152
|
'cmk_cleanup_hostname': _cmk_cleanup_hostname,
|
|
153
|
+
'cmk_password': _cmk_password,
|
|
142
154
|
'get_ip_network': get_ip_network,
|
|
143
155
|
'get_ip4_interface': get_ip_interface,
|
|
144
156
|
'get_ip_interface': get_ip_interface,
|
|
@@ -350,6 +350,17 @@ def _execute(stats_id, run_params: dict, cwd: Path, provider: str):
|
|
|
350
350
|
run_tmp = tempfile.mkdtemp(prefix='cmdbsyncer-ansible-')
|
|
351
351
|
env['ANSIBLE_LOCAL_TEMP'] = run_tmp
|
|
352
352
|
env['HOME'] = run_tmp
|
|
353
|
+
# ANSIBLE_LOCAL_TEMP / $HOME only cover the controller-side scratch
|
|
354
|
+
# dir. A task delegated to localhost (delegate_to: localhost) runs
|
|
355
|
+
# over the *local* connection, whose remote_tmp still defaults to
|
|
356
|
+
# ``~/.ansible/tmp`` — and that ``~`` is expanded via the passwd
|
|
357
|
+
# database (the web user's non-writable home, e.g.
|
|
358
|
+
# ``/usr/share/httpd``), ignoring $HOME. That makes such tasks abort
|
|
359
|
+
# "Failed to create temporary directory". Pin remote_tmp to the
|
|
360
|
+
# writable per-run dir (a literal /tmp path, no ``~`` to expand) so
|
|
361
|
+
# local-delegated tasks work; real SSH hosts just create this path
|
|
362
|
+
# under their own /tmp.
|
|
363
|
+
env['ANSIBLE_REMOTE_TEMP'] = run_tmp
|
|
353
364
|
inventory_path = _write_run_inventory_spec(
|
|
354
365
|
run_tmp, provider, env['CMDBSYNCER_INVENTORY_MODE'],
|
|
355
366
|
cmdbsyncer_bin=_cmdbsyncer_bin(),
|
|
@@ -47,6 +47,7 @@ class SyncSites(Plugin):
|
|
|
47
47
|
'cmk_edition': site.settings_master.cmk_edition,
|
|
48
48
|
'cmk_version': site.settings_master.cmk_version,
|
|
49
49
|
'cmk_version_filename': filename,
|
|
50
|
+
'installation_staging_path': site.settings_master.installation_staging_path or '',
|
|
50
51
|
'subscription_username': site.settings_master.subscription_username,
|
|
51
52
|
'subscription_password': site.settings_master.subscription_password,
|
|
52
53
|
'cmk_user': cmk_user,
|
|
@@ -72,6 +72,15 @@ def register_admin_views(admin):
|
|
|
72
72
|
menu_icon_value='fa-filter',
|
|
73
73
|
)
|
|
74
74
|
)
|
|
75
|
+
admin.add_view(
|
|
76
|
+
CheckmkTestFolderScopeView(
|
|
77
|
+
name="Limit Host Export to Folders",
|
|
78
|
+
category="Checkmk",
|
|
79
|
+
endpoint="checkmk_test_folder_scope",
|
|
80
|
+
menu_icon_type='fa',
|
|
81
|
+
menu_icon_value='fa-folder-open',
|
|
82
|
+
)
|
|
83
|
+
)
|
|
75
84
|
admin.add_view(
|
|
76
85
|
CheckmkRuleView(
|
|
77
86
|
CheckmkRule,
|
|
@@ -99,6 +108,15 @@ def register_admin_views(admin):
|
|
|
99
108
|
menu_icon_value='fa-cogs',
|
|
100
109
|
)
|
|
101
110
|
)
|
|
111
|
+
admin.add_view(
|
|
112
|
+
CheckmkRuleProjectView(
|
|
113
|
+
CheckmkRuleProject,
|
|
114
|
+
name="Setup Rule Projects",
|
|
115
|
+
category="Checkmk",
|
|
116
|
+
menu_icon_type='fa',
|
|
117
|
+
menu_icon_value='fa-cubes',
|
|
118
|
+
)
|
|
119
|
+
)
|
|
102
120
|
admin.add_view(
|
|
103
121
|
CheckmkTagMngmtView(
|
|
104
122
|
CheckmkTagMngmt,
|
|
@@ -183,32 +201,6 @@ def register_admin_views(admin):
|
|
|
183
201
|
menu_icon_value='fa-folder-open',
|
|
184
202
|
)
|
|
185
203
|
)
|
|
186
|
-
admin.add_sub_category(name="Export Scoping", parent_name="Checkmk")
|
|
187
|
-
# add_sub_category does not apply category_icon_classes to sub-categories,
|
|
188
|
-
# so set the menu icon directly on the created SubMenuCategory.
|
|
189
|
-
_ttp_category = admin.get_category_menu_item("Export Scoping")
|
|
190
|
-
if _ttp_category is not None:
|
|
191
|
-
_ttp_category.icon_type = 'fa'
|
|
192
|
-
_ttp_category.icon_value = 'fa-filter'
|
|
193
|
-
admin.add_view(
|
|
194
|
-
CheckmkRuleProjectView(
|
|
195
|
-
CheckmkRuleProject,
|
|
196
|
-
name="Setup Rule Projects",
|
|
197
|
-
category="Export Scoping",
|
|
198
|
-
menu_icon_type='fa',
|
|
199
|
-
menu_icon_value='fa-cubes',
|
|
200
|
-
)
|
|
201
|
-
)
|
|
202
|
-
admin.add_view(
|
|
203
|
-
CheckmkTestFolderScopeView(
|
|
204
|
-
name="Limit Host Export to Folders",
|
|
205
|
-
category="Export Scoping",
|
|
206
|
-
endpoint="checkmk_test_folder_scope",
|
|
207
|
-
menu_icon_type='fa',
|
|
208
|
-
menu_icon_value='fa-folder-open',
|
|
209
|
-
)
|
|
210
|
-
)
|
|
211
|
-
|
|
212
204
|
admin.add_view(
|
|
213
205
|
CheckmkInventorizeAttributesView(
|
|
214
206
|
CheckmkInventorizeAttributes,
|
|
@@ -11,7 +11,7 @@ from pprint import pformat
|
|
|
11
11
|
|
|
12
12
|
from rich.progress import Progress, SpinnerColumn, TimeElapsedColumn, MofNCompleteColumn
|
|
13
13
|
|
|
14
|
-
from application import logger
|
|
14
|
+
from application import app, logger
|
|
15
15
|
from application.models.host import Host
|
|
16
16
|
from application.plugins.checkmk.cmk2 import CmkException, CMK2
|
|
17
17
|
from application.helpers.syncer_jinja import render_jinja, get_list
|
|
@@ -44,6 +44,25 @@ def normalize_cmk_folder(folder):
|
|
|
44
44
|
return normalize_folder(folder)
|
|
45
45
|
|
|
46
46
|
|
|
47
|
+
def scope_folder(folder):
|
|
48
|
+
"""
|
|
49
|
+
Normalise a folder for a folder-scope selection.
|
|
50
|
+
|
|
51
|
+
Like :func:`normalize_cmk_folder`, but also prefixes a leading ``/`` and
|
|
52
|
+
lowercases the path when ``CMK_LOWERCASE_FOLDERNAMES`` is on. Checkmk's API
|
|
53
|
+
folder names are always lowercase, and the export side (``rules.py``) writes
|
|
54
|
+
them that way, so a picked or typed folder compares equal to the folder a
|
|
55
|
+
host actually lands in.
|
|
56
|
+
"""
|
|
57
|
+
folder = (folder or '').strip()
|
|
58
|
+
if folder and not folder.startswith('/'):
|
|
59
|
+
folder = '/' + folder
|
|
60
|
+
folder = normalize_cmk_folder(folder)
|
|
61
|
+
if app.config['CMK_LOWERCASE_FOLDERNAMES']:
|
|
62
|
+
folder = folder.lower()
|
|
63
|
+
return folder
|
|
64
|
+
|
|
65
|
+
|
|
47
66
|
def folder_in_scope(rule_folder, target_folder, recursive=False):
|
|
48
67
|
"""
|
|
49
68
|
True when ``rule_folder`` should be imported for ``target_folder``.
|
|
@@ -90,7 +109,7 @@ def iter_rule_folders():
|
|
|
90
109
|
for rule in CheckmkRuleMngmt.objects():
|
|
91
110
|
for outcome in rule.outcomes:
|
|
92
111
|
if outcome.folder and '{{' not in outcome.folder:
|
|
93
|
-
folders.add(
|
|
112
|
+
folders.add(scope_folder(outcome.folder))
|
|
94
113
|
|
|
95
114
|
for rule in CheckmkRule.objects(enabled=True):
|
|
96
115
|
for outcome in rule.outcomes:
|
|
@@ -99,11 +118,11 @@ def iter_rule_folders():
|
|
|
99
118
|
param = (outcome.action_param or '').strip()
|
|
100
119
|
if not param or '{{' in param:
|
|
101
120
|
continue
|
|
102
|
-
folders.add(
|
|
121
|
+
folders.add(scope_folder(param))
|
|
103
122
|
|
|
104
123
|
for pool in CheckmkFolderPool.objects():
|
|
105
124
|
if pool.folder_name:
|
|
106
|
-
folders.add(
|
|
125
|
+
folders.add(scope_folder(pool.folder_name))
|
|
107
126
|
|
|
108
127
|
return sorted(folders)
|
|
109
128
|
|
|
@@ -14,6 +14,11 @@ from application.plugins.checkmk.rules import CheckmkRulesetRule, DefaultRule
|
|
|
14
14
|
from application.plugins.checkmk.inventorize import InventorizeHosts
|
|
15
15
|
from application.plugins.checkmk.dcd import CheckmkDCDRuleSync
|
|
16
16
|
from application.plugins.checkmk.passwords import CheckmkPasswordSync
|
|
17
|
+
from application.plugins.checkmk.rule_passwords import (
|
|
18
|
+
rewrite_explicit_passwords,
|
|
19
|
+
preserve_password_macros,
|
|
20
|
+
referenced_password_names,
|
|
21
|
+
)
|
|
17
22
|
from application.plugins.checkmk.groups import CheckmkGroupSync
|
|
18
23
|
from application.plugins.checkmk.users import CheckmkUserSync
|
|
19
24
|
from application.plugins.checkmk.bi import BI
|
|
@@ -384,6 +389,7 @@ def import_project_rules_from_folder(project_name, account, folder, # pylint: d
|
|
|
384
389
|
found = syncer.fetch_rules_in_folder(folder, recursive=recursive)
|
|
385
390
|
|
|
386
391
|
imported = 0
|
|
392
|
+
password_hints = set()
|
|
387
393
|
for entry in found:
|
|
388
394
|
cmk_id = entry.get('cmk_id')
|
|
389
395
|
if not cmk_id:
|
|
@@ -402,18 +408,42 @@ def import_project_rules_from_folder(project_name, account, folder, # pylint: d
|
|
|
402
408
|
f"Imported from Checkmk account '{account}', "
|
|
403
409
|
f"folder '{folder}' (rule {cmk_id})")
|
|
404
410
|
outcome = entry['outcome']
|
|
411
|
+
# Checkmk masks explicit passwords as '******' on read, so a rule
|
|
412
|
+
# imported here can't carry a usable secret. Rewrite each into a
|
|
413
|
+
# syncer password-store reference (a cmk_password macro); on a
|
|
414
|
+
# re-import keep the macro already on the rule so a renamed one is
|
|
415
|
+
# not reverted to its default hint.
|
|
416
|
+
new_value, hints = rewrite_explicit_passwords(
|
|
417
|
+
outcome.get('value_template', ''))
|
|
418
|
+
if hints:
|
|
419
|
+
old_outcome = rule.outcomes[0] if rule.outcomes else None
|
|
420
|
+
if old_outcome is not None:
|
|
421
|
+
new_value = preserve_password_macros(
|
|
422
|
+
old_outcome.value_template, new_value)
|
|
423
|
+
outcome['value_template'] = new_value
|
|
424
|
+
password_hints.update(referenced_password_names(new_value))
|
|
405
425
|
rule.outcomes = [RuleMngmtOutcome(**outcome)]
|
|
406
426
|
rule.primary_ruleset = outcome.get('ruleset', '')
|
|
407
427
|
rule.save()
|
|
408
428
|
imported += 1
|
|
409
429
|
|
|
430
|
+
if password_hints:
|
|
431
|
+
hint_list = ', '.join(sorted(password_hints))
|
|
432
|
+
print(f"{ColorCodes.WARNING} * Rules reference password store "
|
|
433
|
+
f"entries: {hint_list}. Create matching Checkmk Passwords in "
|
|
434
|
+
f"the syncer and run 'checkmk export_passwords <account>' so "
|
|
435
|
+
f"the reference resolves in the target Checkmk.{ColorCodes.ENDC}")
|
|
436
|
+
|
|
410
437
|
message = (f"Imported {imported} rule(s) from Checkmk folder "
|
|
411
438
|
f"'{folder}' into project '{project_name}'")
|
|
412
439
|
print(f'{ColorCodes.OKGREEN}{message}{ColorCodes.ENDC}')
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
440
|
+
details = [('account', account), ('folder', folder),
|
|
441
|
+
('recursive', str(recursive)),
|
|
442
|
+
('imported', str(imported))]
|
|
443
|
+
if password_hints:
|
|
444
|
+
details.append(('password_store_entries',
|
|
445
|
+
', '.join(sorted(password_hints))))
|
|
446
|
+
log.log(message, source="cmk_project_rule_import", details=details)
|
|
417
447
|
return imported
|
|
418
448
|
except CmkException as error_obj:
|
|
419
449
|
# Record the failure, then re-raise so callers can surface it.
|
|
@@ -505,6 +505,7 @@ class CheckmkSettings(db.Document):
|
|
|
505
505
|
cmk_version = db.StringField()
|
|
506
506
|
cmk_edition = db.StringField(choices=editions)
|
|
507
507
|
cmk_version_filename = db.StringField()
|
|
508
|
+
installation_staging_path = db.StringField()
|
|
508
509
|
inital_password = db.StringField()
|
|
509
510
|
|
|
510
511
|
subscription_username = db.StringField()
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Bridge Checkmk rule secrets to the syncer's own Checkmk password store.
|
|
3
|
+
|
|
4
|
+
Checkmk masks an ``explicit_password`` in a rule value as ``******`` on every
|
|
5
|
+
read, so a rule imported from one Checkmk instance carries no usable secret and
|
|
6
|
+
would overwrite the target's password with ``******`` on deploy. Instead we
|
|
7
|
+
rewrite every ``explicit_password`` into a ``stored_password`` reference backed
|
|
8
|
+
by a syncer :class:`CheckmkPassword`. The reference is a Jinja macro
|
|
9
|
+
``{{ cmk_password("name") }}`` that resolves to the entry's stable ident
|
|
10
|
+
``cmdbsyncer_<id>`` at export time — the same ident the password export writes
|
|
11
|
+
into every Checkmk instance, so a rule deployed to test and prod resolves each
|
|
12
|
+
instance's own stored secret.
|
|
13
|
+
"""
|
|
14
|
+
import ast
|
|
15
|
+
import re
|
|
16
|
+
|
|
17
|
+
from application import logger
|
|
18
|
+
|
|
19
|
+
# Checkmk 2.3+ tags password values in a rule's ``value_raw`` as
|
|
20
|
+
# ``('cmk_postprocessed', <kind>, (<ident>, <value>))``. ``explicit_password``
|
|
21
|
+
# inlines the (masked) secret; ``stored_password`` only references a password
|
|
22
|
+
# store ident and carries no secret.
|
|
23
|
+
POSTPROCESSED = 'cmk_postprocessed'
|
|
24
|
+
EXPLICIT_PASSWORD = 'explicit_password'
|
|
25
|
+
STORED_PASSWORD = 'stored_password'
|
|
26
|
+
|
|
27
|
+
# ``{{ cmk_password("name") }}`` — tolerant of surrounding whitespace and either
|
|
28
|
+
# quote style, matching how the macro may be hand-edited on the rule.
|
|
29
|
+
_MACRO_RE = re.compile(r'\{\{\s*cmk_password\([^)]*\)\s*\}\}')
|
|
30
|
+
_MACRO_NAME_RE = re.compile(r'cmk_password\(\s*["\']([^"\']+)["\']\s*\)')
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def password_ident(name):
|
|
34
|
+
"""
|
|
35
|
+
Resolve a syncer password name to its Checkmk password-store ident.
|
|
36
|
+
|
|
37
|
+
Returns ``cmdbsyncer_<id>`` for the :class:`CheckmkPassword` named ``name``.
|
|
38
|
+
A missing name is logged and resolved to a clearly-invalid ident so only the
|
|
39
|
+
one affected rule fails to deploy (Checkmk rejects the unknown reference)
|
|
40
|
+
instead of aborting the whole export.
|
|
41
|
+
"""
|
|
42
|
+
from .models import CheckmkPassword # pylint: disable=import-outside-toplevel
|
|
43
|
+
entry = CheckmkPassword.objects(name=name).first()
|
|
44
|
+
if not entry:
|
|
45
|
+
logger.error(
|
|
46
|
+
"cmk_password(%r): no Checkmk Password with that name in the syncer",
|
|
47
|
+
name)
|
|
48
|
+
return f"cmdbsyncer_missing_{name}"
|
|
49
|
+
return f"cmdbsyncer_{entry.id}"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _macro_for(hint):
|
|
53
|
+
"""Build the ``{{ cmk_password(...) }}`` macro for a default name hint."""
|
|
54
|
+
return '{{ cmk_password("%s") }}' % (hint or 'password')
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def rewrite_explicit_passwords(value_raw):
|
|
58
|
+
"""
|
|
59
|
+
Rewrite every ``explicit_password`` in a rule ``value_raw`` string into a
|
|
60
|
+
``stored_password`` password-store reference.
|
|
61
|
+
|
|
62
|
+
Each ``('cmk_postprocessed', 'explicit_password', (<uuid>, '******'))``
|
|
63
|
+
becomes ``('cmk_postprocessed', 'stored_password', ('{{ cmk_password("<hint>") }}', ''))``
|
|
64
|
+
where ``<hint>`` defaults to the value's field name (e.g. ``secret``). The
|
|
65
|
+
hint is only a starting name — rename the macro on the rule and create a
|
|
66
|
+
matching syncer Checkmk Password.
|
|
67
|
+
|
|
68
|
+
Returns ``(new_value_raw, hints)`` where ``hints`` is the list of default
|
|
69
|
+
names inserted (empty, and ``value_raw`` returned unchanged, when the value
|
|
70
|
+
holds no explicit password or cannot be parsed).
|
|
71
|
+
"""
|
|
72
|
+
if not isinstance(value_raw, str) or EXPLICIT_PASSWORD not in value_raw:
|
|
73
|
+
return value_raw, []
|
|
74
|
+
try:
|
|
75
|
+
parsed = ast.literal_eval(value_raw)
|
|
76
|
+
except (ValueError, SyntaxError):
|
|
77
|
+
return value_raw, []
|
|
78
|
+
|
|
79
|
+
hints = []
|
|
80
|
+
|
|
81
|
+
def convert(obj, key=None):
|
|
82
|
+
if isinstance(obj, tuple):
|
|
83
|
+
if len(obj) == 3 and obj[0] == POSTPROCESSED \
|
|
84
|
+
and obj[1] == EXPLICIT_PASSWORD:
|
|
85
|
+
hint = key if isinstance(key, str) and key else 'password'
|
|
86
|
+
hints.append(hint)
|
|
87
|
+
return (POSTPROCESSED, STORED_PASSWORD, (_macro_for(hint), ''))
|
|
88
|
+
return tuple(convert(item, key) for item in obj)
|
|
89
|
+
if isinstance(obj, list):
|
|
90
|
+
return [convert(item, key) for item in obj]
|
|
91
|
+
if isinstance(obj, dict):
|
|
92
|
+
return {k: convert(v, k) for k, v in obj.items()}
|
|
93
|
+
return obj
|
|
94
|
+
|
|
95
|
+
new = convert(parsed)
|
|
96
|
+
if not hints:
|
|
97
|
+
return value_raw, []
|
|
98
|
+
return repr(new), hints
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def preserve_password_macros(old_value, new_value):
|
|
102
|
+
"""
|
|
103
|
+
Carry a rule's existing ``cmk_password`` macros over into a freshly
|
|
104
|
+
re-imported value, so a re-run of the folder import does not revert a
|
|
105
|
+
renamed macro back to its default hint.
|
|
106
|
+
|
|
107
|
+
Positional: when ``old_value`` and ``new_value`` hold the same number of
|
|
108
|
+
macros (the usual case — the rule's password fields did not change), the
|
|
109
|
+
i-th new macro is replaced by the i-th old one. On any mismatch the new
|
|
110
|
+
value is kept unchanged.
|
|
111
|
+
"""
|
|
112
|
+
if not old_value:
|
|
113
|
+
return new_value
|
|
114
|
+
old_macros = _MACRO_RE.findall(old_value)
|
|
115
|
+
new_macros = _MACRO_RE.findall(new_value)
|
|
116
|
+
if not old_macros or len(old_macros) != len(new_macros):
|
|
117
|
+
return new_value
|
|
118
|
+
carried = iter(old_macros)
|
|
119
|
+
return _MACRO_RE.sub(lambda _match: next(carried), new_value)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def referenced_password_names(text):
|
|
123
|
+
"""Return the set of syncer password names a value references via macro."""
|
|
124
|
+
if not text:
|
|
125
|
+
return set()
|
|
126
|
+
return set(_MACRO_NAME_RE.findall(text))
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unit tests for checkmk rule_passwords module
|
|
3
|
+
"""
|
|
4
|
+
# pylint: disable=missing-function-docstring,protected-access
|
|
5
|
+
import ast
|
|
6
|
+
import types
|
|
7
|
+
import unittest
|
|
8
|
+
from unittest.mock import patch, MagicMock
|
|
9
|
+
|
|
10
|
+
from application.plugins.checkmk.rule_passwords import (
|
|
11
|
+
rewrite_explicit_passwords,
|
|
12
|
+
preserve_password_macros,
|
|
13
|
+
referenced_password_names,
|
|
14
|
+
password_ident,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
# The Azure special-agent value the user reported: one explicit_password
|
|
18
|
+
# ('secret') plus an explicit_proxy that must be left untouched.
|
|
19
|
+
AZURE_VALUE = repr({
|
|
20
|
+
'subscription': 'e602c412-033e-4df7-ac6e-6d1a7942aa71',
|
|
21
|
+
'tenant': 'ef65c7f0-656e-4646-a2fd-3e45d36a39fd',
|
|
22
|
+
'client': '743d3780-7009-4865-a894-506d23edf34f',
|
|
23
|
+
'secret': ('cmk_postprocessed', 'explicit_password',
|
|
24
|
+
('uuidd3b2cc18-be10-4bfa-85e9-bdc4a27562e0', '******')),
|
|
25
|
+
'authority': 'global_',
|
|
26
|
+
'proxy': ('cmk_postprocessed', 'explicit_proxy',
|
|
27
|
+
'http://134.247.251.203:9092'),
|
|
28
|
+
'services': ['Microsoft_Network_slash_loadBalancers'],
|
|
29
|
+
'config': {},
|
|
30
|
+
'piggyback_vms': 'grouphost',
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class TestRewriteExplicitPasswords(unittest.TestCase):
|
|
35
|
+
"""rewrite_explicit_passwords"""
|
|
36
|
+
|
|
37
|
+
def test_converts_explicit_and_keeps_proxy(self):
|
|
38
|
+
new_value, hints = rewrite_explicit_passwords(AZURE_VALUE)
|
|
39
|
+
self.assertEqual(hints, ['secret'])
|
|
40
|
+
parsed = ast.literal_eval(new_value)
|
|
41
|
+
# secret is now a stored_password reference with a cmk_password macro
|
|
42
|
+
self.assertEqual(
|
|
43
|
+
parsed['secret'],
|
|
44
|
+
('cmk_postprocessed', 'stored_password',
|
|
45
|
+
('{{ cmk_password("secret") }}', '')))
|
|
46
|
+
# the explicit_proxy is untouched
|
|
47
|
+
self.assertEqual(
|
|
48
|
+
parsed['proxy'],
|
|
49
|
+
('cmk_postprocessed', 'explicit_proxy',
|
|
50
|
+
'http://134.247.251.203:9092'))
|
|
51
|
+
# unrelated fields survive
|
|
52
|
+
self.assertEqual(parsed['authority'], 'global_')
|
|
53
|
+
|
|
54
|
+
def test_no_password_returns_unchanged(self):
|
|
55
|
+
value = "{'foo': 'bar'}"
|
|
56
|
+
new_value, hints = rewrite_explicit_passwords(value)
|
|
57
|
+
self.assertEqual(new_value, value)
|
|
58
|
+
self.assertEqual(hints, [])
|
|
59
|
+
|
|
60
|
+
def test_unparseable_returns_unchanged(self):
|
|
61
|
+
value = "{'foo': explicit_password(" # contains the marker, won't parse
|
|
62
|
+
new_value, hints = rewrite_explicit_passwords(value)
|
|
63
|
+
self.assertEqual(new_value, value)
|
|
64
|
+
self.assertEqual(hints, [])
|
|
65
|
+
|
|
66
|
+
def test_non_string_returns_unchanged(self):
|
|
67
|
+
new_value, hints = rewrite_explicit_passwords(None)
|
|
68
|
+
self.assertIsNone(new_value)
|
|
69
|
+
self.assertEqual(hints, [])
|
|
70
|
+
|
|
71
|
+
def test_nested_password_uses_field_key_hint(self):
|
|
72
|
+
value = repr({'servers': [
|
|
73
|
+
{'name': 'a',
|
|
74
|
+
'token': ('cmk_postprocessed', 'explicit_password',
|
|
75
|
+
('uuidX', '******'))},
|
|
76
|
+
]})
|
|
77
|
+
new_value, hints = rewrite_explicit_passwords(value)
|
|
78
|
+
self.assertEqual(hints, ['token'])
|
|
79
|
+
parsed = ast.literal_eval(new_value)
|
|
80
|
+
self.assertEqual(
|
|
81
|
+
parsed['servers'][0]['token'],
|
|
82
|
+
('cmk_postprocessed', 'stored_password',
|
|
83
|
+
('{{ cmk_password("token") }}', '')))
|
|
84
|
+
|
|
85
|
+
def test_rewritten_value_is_valid_and_round_trips(self):
|
|
86
|
+
# The rewritten value must stay a parseable Python literal whose macro,
|
|
87
|
+
# once a resolver substitutes the ident, yields a valid stored_password.
|
|
88
|
+
new_value, _ = rewrite_explicit_passwords(AZURE_VALUE)
|
|
89
|
+
resolved = new_value.replace('{{ cmk_password("secret") }}',
|
|
90
|
+
'cmdbsyncer_deadbeef')
|
|
91
|
+
parsed = ast.literal_eval(resolved)
|
|
92
|
+
self.assertEqual(
|
|
93
|
+
parsed['secret'],
|
|
94
|
+
('cmk_postprocessed', 'stored_password',
|
|
95
|
+
('cmdbsyncer_deadbeef', '')))
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class TestPreservePasswordMacros(unittest.TestCase):
|
|
99
|
+
"""preserve_password_macros"""
|
|
100
|
+
|
|
101
|
+
def test_carries_renamed_macro_when_counts_match(self):
|
|
102
|
+
old = "{'secret': ('x', 'y', ('{{ cmk_password(\"azure-prod\") }}', ''))}"
|
|
103
|
+
new = "{'secret': ('x', 'y', ('{{ cmk_password(\"secret\") }}', ''))}"
|
|
104
|
+
merged = preserve_password_macros(old, new)
|
|
105
|
+
self.assertIn('cmk_password("azure-prod")', merged)
|
|
106
|
+
self.assertNotIn('cmk_password("secret")', merged)
|
|
107
|
+
|
|
108
|
+
def test_keeps_new_on_count_mismatch(self):
|
|
109
|
+
old = "{{ cmk_password(\"a\") }} {{ cmk_password(\"b\") }}"
|
|
110
|
+
new = "{{ cmk_password(\"secret\") }}"
|
|
111
|
+
self.assertEqual(preserve_password_macros(old, new), new)
|
|
112
|
+
|
|
113
|
+
def test_empty_old_keeps_new(self):
|
|
114
|
+
new = "{{ cmk_password(\"secret\") }}"
|
|
115
|
+
self.assertEqual(preserve_password_macros('', new), new)
|
|
116
|
+
self.assertEqual(preserve_password_macros(None, new), new)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class TestReferencedPasswordNames(unittest.TestCase):
|
|
120
|
+
"""referenced_password_names"""
|
|
121
|
+
|
|
122
|
+
def test_extracts_names_both_quote_styles(self):
|
|
123
|
+
text = ('{{ cmk_password("azure-prod") }} and '
|
|
124
|
+
"{{ cmk_password('other') }}")
|
|
125
|
+
self.assertEqual(referenced_password_names(text),
|
|
126
|
+
{'azure-prod', 'other'})
|
|
127
|
+
|
|
128
|
+
def test_empty_and_none(self):
|
|
129
|
+
self.assertEqual(referenced_password_names(''), set())
|
|
130
|
+
self.assertEqual(referenced_password_names(None), set())
|
|
131
|
+
|
|
132
|
+
def test_no_macro(self):
|
|
133
|
+
self.assertEqual(referenced_password_names("{'k': 'v'}"), set())
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class TestPasswordIdent(unittest.TestCase):
|
|
137
|
+
"""password_ident resolves against the syncer password store"""
|
|
138
|
+
|
|
139
|
+
def _patch_model(self, entry):
|
|
140
|
+
fake_models = types.ModuleType(
|
|
141
|
+
'application.plugins.checkmk.models')
|
|
142
|
+
fake_models.CheckmkPassword = MagicMock()
|
|
143
|
+
fake_models.CheckmkPassword.objects.return_value.first.return_value = entry
|
|
144
|
+
return patch.dict(
|
|
145
|
+
'sys.modules',
|
|
146
|
+
{'application.plugins.checkmk.models': fake_models})
|
|
147
|
+
|
|
148
|
+
def test_found_returns_prefixed_id(self):
|
|
149
|
+
entry = MagicMock()
|
|
150
|
+
entry.id = 'abc123'
|
|
151
|
+
with self._patch_model(entry):
|
|
152
|
+
self.assertEqual(password_ident('azure-prod'), 'cmdbsyncer_abc123')
|
|
153
|
+
|
|
154
|
+
def test_missing_returns_sentinel(self):
|
|
155
|
+
with self._patch_model(None):
|
|
156
|
+
self.assertEqual(password_ident('nope'), 'cmdbsyncer_missing_nope')
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
if __name__ == '__main__':
|
|
160
|
+
unittest.main()
|