cmdbsyncer 4.0.3__tar.gz → 4.1.0.dev1__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.0.3/cmdbsyncer.egg-info → cmdbsyncer-4.1.0.dev1}/PKG-INFO +2 -1
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/__init__.py +94 -6
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/api/__init__.py +32 -8
- cmdbsyncer-4.1.0.dev1/application/api/inventory.py +54 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/api/objects.py +169 -3
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/api/rules.py +13 -2
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/api/syncer.py +9 -4
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/api/views.py +2 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/auth/views.py +63 -10
- cmdbsyncer-4.1.0.dev1/application/buildinfo.txt +1 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/changelog/v4.0.md +0 -15
- cmdbsyncer-4.1.0.dev1/application/changelog/v4.1.md +102 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/config.py +38 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/helpers/local_config_presets.py +5 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/models/account.py +5 -2
- cmdbsyncer-4.1.0.dev1/application/models/cron.py +204 -0
- cmdbsyncer-4.1.0.dev1/application/models/field_approval.py +55 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/models/forms.py +24 -12
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/models/host.py +247 -12
- cmdbsyncer-4.1.0.dev1/application/models/host_inventory_tree.py +48 -0
- cmdbsyncer-4.1.0.dev1/application/models/saved_search.py +43 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/models/user.py +12 -5
- cmdbsyncer-4.1.0.dev1/application/modules/email.py +127 -0
- cmdbsyncer-4.1.0.dev1/application/modules/inventory/__init__.py +104 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/modules/plugin.py +41 -6
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/ansible/__init__.py +185 -59
- cmdbsyncer-4.1.0.dev1/application/plugins/ansible/admin_views.py +93 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/ansible/inventory.py +5 -5
- cmdbsyncer-4.1.0.dev1/application/plugins/ansible/models.py +279 -0
- cmdbsyncer-4.1.0.dev1/application/plugins/ansible/playbook_rules.py +92 -0
- cmdbsyncer-4.1.0.dev1/application/plugins/ansible/runner.py +222 -0
- cmdbsyncer-4.1.0.dev1/application/plugins/ansible/views.py +556 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/__init__.py +67 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/admin_views.py +11 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/bi.py +2 -2
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/cmk_rules.py +318 -1
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/dcd.py +1 -1
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/downtimes.py +1 -1
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/groups.py +8 -7
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/inits.py +35 -1
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/inventorize.py +74 -4
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/models.py +83 -1
- cmdbsyncer-4.1.0.dev1/application/plugins/checkmk/notification_rules.py +471 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/tests/test_bi.py +7 -7
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/tests/test_cmk_rules.py +177 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/tests/test_groups.py +2 -2
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/tests/test_inventorize.py +104 -3
- cmdbsyncer-4.1.0.dev1/application/plugins/checkmk/tests/test_notification_rules.py +436 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/views.py +508 -38
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/cisco_dna/syncer.py +4 -1
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/maintenance/__init__.py +72 -5
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/rules/autorules.py +2 -2
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/rules/rule_definitions.py +1 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/static/css/cmdbsyncer.css +25 -0
- cmdbsyncer-4.1.0.dev1/application/templates/_theme.html +27 -0
- cmdbsyncer-4.1.0.dev1/application/templates/admin/_host_tabs.html +91 -0
- cmdbsyncer-4.1.0.dev1/application/templates/admin/ansible_playbook_run.html +122 -0
- cmdbsyncer-4.1.0.dev1/application/templates/admin/ansible_project_detail.html +178 -0
- cmdbsyncer-4.1.0.dev1/application/templates/admin/ansible_rule_list.html +61 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/templates/admin/bulk_label_form.html +2 -1
- cmdbsyncer-4.1.0.dev1/application/templates/admin/checkmk_rule_mngmt_list.html +221 -0
- cmdbsyncer-4.1.0.dev1/application/templates/admin/data_quality.html +435 -0
- cmdbsyncer-4.1.0.dev1/application/templates/admin/host_details.html +11 -0
- cmdbsyncer-4.1.0.dev1/application/templates/admin/host_edit.html +13 -0
- cmdbsyncer-4.1.0.dev1/application/templates/admin/host_inventory_tree.html +356 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/templates/admin/host_list.html +78 -0
- cmdbsyncer-4.1.0.dev1/application/templates/admin/host_relations_graph.html +255 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/templates/admin/host_timeline.html +3 -6
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/templates/admin/master.html +94 -9
- cmdbsyncer-4.1.0.dev1/application/templates/admin/model/inline_list_base.html +58 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/templates/admin/set_template_form.html +2 -1
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/templates/base.html +1 -9
- cmdbsyncer-4.1.0.dev1/application/templates/change_password.html +19 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/templates/debug_host.html +149 -14
- cmdbsyncer-4.1.0.dev1/application/templates/email/resetpassword.html +83 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/templates/formular.html +1 -6
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/templates/license_info.html +29 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/templates/login.html +2 -2
- cmdbsyncer-4.1.0.dev1/application/templates/request_password.html +240 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/templates/set_2fa.html +4 -2
- cmdbsyncer-4.1.0.dev1/application/templates/set_theme.html +38 -0
- cmdbsyncer-4.1.0.dev1/application/themes_registry.py +106 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/views/account.py +5 -28
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/views/config.py +17 -2
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/views/cron.py +95 -39
- cmdbsyncer-4.1.0.dev1/application/views/data_quality.py +322 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/views/default.py +10 -1
- cmdbsyncer-4.1.0.dev1/application/views/field_approval.py +168 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/views/host.py +870 -1124
- cmdbsyncer-4.1.0.dev1/application/views/host_filters.py +336 -0
- cmdbsyncer-4.1.0.dev1/application/views/host_renderers.py +838 -0
- cmdbsyncer-4.1.0.dev1/application/views/host_widgets.py +195 -0
- cmdbsyncer-4.1.0.dev1/application/views/license.py +156 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/views/notification_channel.py +2 -4
- cmdbsyncer-4.1.0.dev1/application/views/saved_search.py +130 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/views/user.py +13 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1/cmdbsyncer.egg-info}/PKG-INFO +2 -1
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/cmdbsyncer.egg-info/SOURCES.txt +35 -2
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/cmdbsyncer.egg-info/requires.txt +1 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/pyproject.toml +1 -1
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/requirements-ansible.txt +1 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/tests/test_api.py +15 -3
- cmdbsyncer-4.1.0.dev1/tests/test_api_csrf_invariant.py +129 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/tests/test_plugin.py +75 -3
- cmdbsyncer-4.1.0.dev1/tests/test_webhook_token_hash.py +128 -0
- cmdbsyncer-4.0.3/application/buildinfo.txt +0 -1
- cmdbsyncer-4.0.3/application/models/cron.py +0 -129
- cmdbsyncer-4.0.3/application/modules/email.py +0 -52
- cmdbsyncer-4.0.3/application/plugins/ansible/admin_views.py +0 -41
- cmdbsyncer-4.0.3/application/plugins/ansible/models.py +0 -75
- cmdbsyncer-4.0.3/application/plugins/ansible/rest_api/ansible.py +0 -38
- cmdbsyncer-4.0.3/application/plugins/ansible/views.py +0 -20
- cmdbsyncer-4.0.3/application/templates/email/resetpassword.html +0 -9
- cmdbsyncer-4.0.3/application/views/license.py +0 -39
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/LICENSE +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/README.md +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/_version.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/changelog/.pages +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/changelog/v3.10.md +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/changelog/v3.11.md +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/changelog/v3.12.md +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/changelog/v3.5.md +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/changelog/v3.7.md +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/changelog/v3.8.md +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/changelog/v3.9.md +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/cli.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/docu_links.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/enterprise.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/helpers/__init__.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/helpers/audit.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/helpers/cron.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/helpers/get_account.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/helpers/inventory.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/helpers/local_config_file.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/helpers/mongo_keys.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/helpers/notification_channels.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/helpers/notification_dispatch.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/helpers/plugins.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/helpers/sates.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/helpers/sql.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/helpers/syncer_jinja.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/helpers/tablib_formater.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/helpers/tests/__init__.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/helpers/tests/test_sql.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/mcp_server.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/models/__init__.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/models/config.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/models/notification_channel.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/models/notification_rule.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/models/notification_state.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/models/states.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/modules/__init__.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/modules/custom_attributes/models.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/modules/custom_attributes/rules.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/modules/custom_attributes/views.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/modules/debug.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/modules/log/log.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/modules/log/models.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/modules/log/views.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/modules/rule/__init__.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/modules/rule/filter.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/modules/rule/match.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/modules/rule/models.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/modules/rule/rewrite.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/modules/rule/rule.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/modules/rule/views.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/modules/statefile.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/notices/strict_certificate_verification.txt +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/__init__.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/ansible/rules.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/ansible/site_syncer.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/bmc_remedy/__init__.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/bmc_remedy/bmc_remedy.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/cmk2.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/helpers.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/import_v1.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/import_v2.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/passwords.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/poolfolder.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/rules.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/sites.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/syncer.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/tags.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/tests/__init__.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/tests/test_cmk2.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/tests/test_dcd.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/tests/test_downtimes.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/tests/test_helpers.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/tests/test_import_v1.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/tests/test_import_v2.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/tests/test_passwords.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/tests/test_poolfolder.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/tests/test_rules.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/tests/test_sites.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/tests/test_syncer.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/tests/test_tags.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/tests/test_users.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/checkmk/users.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/cisco_dna/__init__.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/cron.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/csv/__init__.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/csv/csv.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/file_requests.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/idoit/__init__.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/idoit/admin_views.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/idoit/models.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/idoit/rules.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/idoit/syncer.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/idoit/views.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/jdisc/__init__.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/jdisc/applications.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/jdisc/devices.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/jdisc/executables.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/jdisc/jdisc.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/jira/__init__.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/jira/jira.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/jira_cloud/__init__.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/jira_cloud/jira_cloud.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/json/__init__.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/json/json.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/ldap/__init__.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/ldap/ldap.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/mssql/__init__.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/mysql/__init__.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/mysql/mysql.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/netbox/__init__.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/netbox/admin_views.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/netbox/cluster.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/netbox/contacts.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/netbox/dataflow.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/netbox/devices.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/netbox/interfaces.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/netbox/ips.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/netbox/models.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/netbox/netbox.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/netbox/networks.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/netbox/prefixes.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/netbox/rules.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/netbox/views.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/netbox/virtualmachines.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/prtg/__init__.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/prtg/prtg.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/pyodbc/__init__.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/pyodbc/pyodbc.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/rest/__init__.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/rest/rest.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/rules/__init__.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/rules/admin_views.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/rules/models.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/rules/rule_import_export.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/rules/views.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/syncer_shell.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/vmware/__init__.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/vmware/admin_views.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/vmware/custom_attributes.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/vmware/models.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/vmware/rules.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/vmware/views.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/vmware/vmware.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/yml/__init__.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins/yml/yml.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/plugins_cli.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/static/js/main.js +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/static/logo_white.png +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/templates/_brand.html +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/templates/admin/account_pick_type.html +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/templates/admin/changelog_archive.html +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/templates/admin/checkmk_settings_list.html +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/templates/admin/copy_as_new_form.html +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/templates/admin/file/list.html +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/templates/admin/file/modals/form.html +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/templates/admin/index.html +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/templates/admin/local_config_editor.html +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/templates/admin/model/clone_confirm.html +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/templates/admin/object_list.html +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/templates/admin/set_cmk_version_form.html +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/templates/debug.html +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/templates/email/newuser.html +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/templates/email/notification.html +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/templates/logout_confirm.html +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/views/_form_fields.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/views/_form_sections.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/views/fileadmin.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/application/views/notification_rule.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/cmdbsyncer.egg-info/dependency_links.txt +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/cmdbsyncer.egg-info/entry_points.txt +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/cmdbsyncer.egg-info/top_level.txt +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/requirements-extras.txt +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/requirements.txt +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/setup.cfg +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/syncerapi/__init__.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/syncerapi/v1/__init__.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/syncerapi/v1/core/__init__.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/syncerapi/v1/inventory/__init__.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/syncerapi/v1/rest/__init__.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/tests/test_default_view.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/tests/test_mongo_keys.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/tests/test_plugin_ssti.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/tests/test_plugins_cli.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/tests/test_rule.py +0 -0
- {cmdbsyncer-4.0.3 → cmdbsyncer-4.1.0.dev1}/tests/test_rule_match.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cmdbsyncer
|
|
3
|
-
Version: 4.0.
|
|
3
|
+
Version: 4.1.0.dev1
|
|
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>
|
|
@@ -63,6 +63,7 @@ Requires-Dist: pykerberos==1.2.4; extra == "ansible"
|
|
|
63
63
|
Requires-Dist: pywinrm==0.4.3; extra == "ansible"
|
|
64
64
|
Requires-Dist: ntlm-auth==1.5.0; extra == "ansible"
|
|
65
65
|
Requires-Dist: ansible==10.5.0; extra == "ansible"
|
|
66
|
+
Requires-Dist: cmdbsyncer-inventory==0.1.4; extra == "ansible"
|
|
66
67
|
Dynamic: license-file
|
|
67
68
|
|
|
68
69
|
# CMDBsyncer
|
|
@@ -202,6 +202,50 @@ if not CLI_MODE:
|
|
|
202
202
|
"""
|
|
203
203
|
app.config['CHANGES'] = get_changes()
|
|
204
204
|
|
|
205
|
+
@app.context_processor
|
|
206
|
+
def _inject_pending_approvals():
|
|
207
|
+
"""
|
|
208
|
+
Feed the navbar badge in master.html. Runs on every admin
|
|
209
|
+
request, so it must be cheap and fail-soft — the count is a
|
|
210
|
+
nicety, not a correctness requirement.
|
|
211
|
+
"""
|
|
212
|
+
# pylint: disable=import-outside-toplevel
|
|
213
|
+
from flask_login import current_user as _user
|
|
214
|
+
if not app.config.get('CMDB_MODE'):
|
|
215
|
+
return {'pending_approval_count': 0}
|
|
216
|
+
try:
|
|
217
|
+
if not _user.is_authenticated:
|
|
218
|
+
return {'pending_approval_count': 0}
|
|
219
|
+
from application.views.field_approval import pending_count
|
|
220
|
+
count = pending_count()
|
|
221
|
+
except Exception: # pylint: disable=broad-exception-caught
|
|
222
|
+
return {'pending_approval_count': 0}
|
|
223
|
+
return {
|
|
224
|
+
'pending_approval_count': count,
|
|
225
|
+
'pending_approval_url': '/admin/fieldapproval/',
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
@app.context_processor
|
|
229
|
+
def _inject_user_theme():
|
|
230
|
+
"""Expose the logged-in user's theme to ``_theme.html``.
|
|
231
|
+
Anonymous visitors and any error path fall back to the
|
|
232
|
+
default theme so the public templates (login, license)
|
|
233
|
+
render the same as before. Themes that no longer exist on
|
|
234
|
+
disk (e.g. an operator removed the CSS file) also fall
|
|
235
|
+
back, so a stale ``user.theme`` value never 404s."""
|
|
236
|
+
# pylint: disable=import-outside-toplevel
|
|
237
|
+
from flask_login import current_user as _user
|
|
238
|
+
from application.themes_registry import is_known
|
|
239
|
+
try:
|
|
240
|
+
if _user.is_authenticated:
|
|
241
|
+
slug = _user.theme or 'default'
|
|
242
|
+
if not is_known(slug):
|
|
243
|
+
slug = 'default'
|
|
244
|
+
return {'user_theme': slug}
|
|
245
|
+
except Exception: # pylint: disable=broad-exception-caught
|
|
246
|
+
pass
|
|
247
|
+
return {'user_theme': 'default'}
|
|
248
|
+
|
|
205
249
|
@app.context_processor
|
|
206
250
|
def _inject_https_warning():
|
|
207
251
|
"""Show a banner when HTTPS is required (default) but the
|
|
@@ -283,6 +327,12 @@ def _plugin_packages():
|
|
|
283
327
|
return [p for p in (external_plugins_package, plugins_package) if p is not None]
|
|
284
328
|
|
|
285
329
|
|
|
330
|
+
# Cached list of dotted plugin module names. Populated by
|
|
331
|
+
# ``_import_all_plugins`` so the web-layer doesn't have to walk the
|
|
332
|
+
# plugin packages with ``pkgutil.iter_modules`` a second time at startup.
|
|
333
|
+
_DISCOVERED_PLUGIN_MODULES = []
|
|
334
|
+
|
|
335
|
+
|
|
286
336
|
def _import_all_plugins():
|
|
287
337
|
"""Import every enabled plugin module so its side-effect registrations
|
|
288
338
|
fire — Click CLI groups, sync hooks, cron entries.
|
|
@@ -292,8 +342,10 @@ def _import_all_plugins():
|
|
|
292
342
|
here; they are web-only and handled by
|
|
293
343
|
``_register_all_plugin_admin_views``.
|
|
294
344
|
"""
|
|
345
|
+
_DISCOVERED_PLUGIN_MODULES.clear()
|
|
295
346
|
for package in _plugin_packages():
|
|
296
347
|
for module_name in _iter_enabled_plugin_modules(package):
|
|
348
|
+
_DISCOVERED_PLUGIN_MODULES.append(module_name)
|
|
297
349
|
try:
|
|
298
350
|
importlib.import_module(module_name)
|
|
299
351
|
except Exception: # pylint: disable=broad-exception-caught
|
|
@@ -303,11 +355,7 @@ def _import_all_plugins():
|
|
|
303
355
|
|
|
304
356
|
|
|
305
357
|
def _register_all_plugin_admin_views(admin_):
|
|
306
|
-
|
|
307
|
-
for package in _plugin_packages():
|
|
308
|
-
plugin_modules.extend(_iter_enabled_plugin_modules(package))
|
|
309
|
-
|
|
310
|
-
for module_name in plugin_modules:
|
|
358
|
+
for module_name in _DISCOVERED_PLUGIN_MODULES:
|
|
311
359
|
admin_module_name = f"{module_name}.admin_views"
|
|
312
360
|
try:
|
|
313
361
|
admin_module = importlib.import_module(admin_module_name)
|
|
@@ -343,8 +391,19 @@ def _register_web_layer(): # pylint: disable=too-many-locals,too-many-statement
|
|
|
343
391
|
from application.auth.views import AUTH
|
|
344
392
|
app.register_blueprint(AUTH)
|
|
345
393
|
|
|
394
|
+
from application.themes_registry import init_themes, themes_blueprint
|
|
395
|
+
init_themes(app)
|
|
396
|
+
app.register_blueprint(themes_blueprint)
|
|
397
|
+
|
|
346
398
|
from application.api.views import API_BP as api
|
|
347
399
|
app.register_blueprint(api, url_prefix="/api/v1")
|
|
400
|
+
# Intentional: every /api/v1 route authenticates per request via
|
|
401
|
+
# @require_token (Basic Auth) or @require_api_role, so CSRF tokens
|
|
402
|
+
# would only break external clients (CLI, MCP, Ansible inventory
|
|
403
|
+
# pulls, Grafana scrapes — none of which fetch a CSRF cookie). The
|
|
404
|
+
# invariant that no /api/v1/* route falls back to session auth is
|
|
405
|
+
# enforced by tests/test_api_csrf_invariant.py — DO NOT remove this
|
|
406
|
+
# exempt during a code review without first reading that test.
|
|
348
407
|
csrf.exempt(api)
|
|
349
408
|
|
|
350
409
|
# Give the enterprise package a chance to register its own auth-related
|
|
@@ -383,9 +442,14 @@ def _register_web_layer(): # pylint: disable=too-many-locals,too-many-statement
|
|
|
383
442
|
|
|
384
443
|
# .-- Host
|
|
385
444
|
from application.models.host import Host
|
|
386
|
-
from application.views.host import
|
|
445
|
+
from application.views.host import (
|
|
446
|
+
HostArchiveView, HostModelView, ObjectModelView, TemplateModelView,
|
|
447
|
+
)
|
|
387
448
|
admin.add_view(HostModelView(Host, name="Hosts",
|
|
388
449
|
menu_icon_type='fa', menu_icon_value='fa-server'))
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
|
|
389
453
|
admin.add_category(name="Objects", icon_type='fa', icon_value='fa-folder-open')
|
|
390
454
|
admin.add_view(ObjectModelView(Host, name="All Objects", endpoint="Objects",
|
|
391
455
|
category="Objects",
|
|
@@ -393,6 +457,14 @@ def _register_web_layer(): # pylint: disable=too-many-locals,too-many-statement
|
|
|
393
457
|
admin.add_view(TemplateModelView(Host, name="Templates", endpoint="Objects Templates",
|
|
394
458
|
category="Objects",
|
|
395
459
|
menu_icon_type='fa', menu_icon_value='fa-files-o'))
|
|
460
|
+
admin.add_view(HostArchiveView(Host, name="Archive", endpoint="archive",
|
|
461
|
+
category="Objects",
|
|
462
|
+
menu_icon_type='fa', menu_icon_value='fa-archive'))
|
|
463
|
+
from application.views.data_quality import DataQualityView
|
|
464
|
+
admin.add_view(DataQualityView(name="Data Quality", endpoint="data_quality",
|
|
465
|
+
category="Objects",
|
|
466
|
+
menu_icon_type='fa',
|
|
467
|
+
menu_icon_value='fa-stethoscope'))
|
|
396
468
|
#.
|
|
397
469
|
# .-- Global
|
|
398
470
|
from application.modules.custom_attributes.models import CustomAttributeRule
|
|
@@ -438,6 +510,12 @@ def _register_web_layer(): # pylint: disable=too-many-locals,too-many-statement
|
|
|
438
510
|
admin.add_sub_category(name="Compliance", parent_name="Settings")
|
|
439
511
|
admin.add_sub_category(name="Notifications", parent_name="Settings")
|
|
440
512
|
admin.add_sub_category(name="Backups", parent_name="Settings")
|
|
513
|
+
from application.models.field_approval import FieldApproval
|
|
514
|
+
from application.views.field_approval import FieldApprovalView
|
|
515
|
+
admin.add_view(FieldApprovalView(FieldApproval, name="Approvals",
|
|
516
|
+
category="Settings",
|
|
517
|
+
menu_icon_type='fa',
|
|
518
|
+
menu_icon_value='fa-hourglass-half'))
|
|
441
519
|
|
|
442
520
|
from application.modules.log.models import LogEntry
|
|
443
521
|
from application.modules.log.views import LogView
|
|
@@ -459,6 +537,13 @@ def _register_web_layer(): # pylint: disable=too-many-locals,too-many-statement
|
|
|
459
537
|
endpoint='config.local_config_editor',
|
|
460
538
|
icon_type='fa', icon_value='fa-file-code-o'))
|
|
461
539
|
|
|
540
|
+
from application.models.saved_search import SavedSearch
|
|
541
|
+
from application.views.saved_search import SavedSearchView
|
|
542
|
+
admin.add_view(SavedSearchView(SavedSearch, name="Saved Searches",
|
|
543
|
+
category="Settings",
|
|
544
|
+
menu_icon_type='fa',
|
|
545
|
+
menu_icon_value='fa-bookmark'))
|
|
546
|
+
|
|
462
547
|
from application.views.license import LicenseView
|
|
463
548
|
admin.add_view(LicenseView(name="License", endpoint="license", category="Settings",
|
|
464
549
|
menu_icon_type='fa', menu_icon_value='fa-id-card'))
|
|
@@ -493,6 +578,9 @@ def _register_web_layer(): # pylint: disable=too-many-locals,too-many-statement
|
|
|
493
578
|
admin.add_link(MenuLink(name='Set 2FA Code', category='Account',
|
|
494
579
|
url=f"{app.config['BASE_PREFIX']}set-2fa",
|
|
495
580
|
icon_type='fa', icon_value='fa-shield'))
|
|
581
|
+
admin.add_link(MenuLink(name='Theme', category='Account',
|
|
582
|
+
url=f"{app.config['BASE_PREFIX']}set-theme",
|
|
583
|
+
icon_type='fa', icon_value='fa-paint-brush'))
|
|
496
584
|
admin.add_link(MenuLink(name='Logout', category='Account',
|
|
497
585
|
url=f"{app.config['BASE_PREFIX']}logout",
|
|
498
586
|
icon_type='fa', icon_value='fa-sign-out'))
|
|
@@ -5,6 +5,7 @@ API
|
|
|
5
5
|
from functools import wraps
|
|
6
6
|
from flask import abort, request, current_app
|
|
7
7
|
from mongoengine.errors import DoesNotExist, MultipleObjectsReturned
|
|
8
|
+
from application.helpers.audit import audit
|
|
8
9
|
from application.models.account import Account
|
|
9
10
|
from application.models.user import User
|
|
10
11
|
from application import log
|
|
@@ -73,15 +74,38 @@ def _authenticate_user():
|
|
|
73
74
|
except DoesNotExist:
|
|
74
75
|
_abort_unauthorized("Invalid credentials")
|
|
75
76
|
except MultipleObjectsReturned:
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
77
|
+
# Several users match the same login string. Historically this
|
|
78
|
+
# happens when the `name` field (added later, sparse) accidentally
|
|
79
|
+
# matches another account's `email`. We can't drop the branch —
|
|
80
|
+
# purging or unique-indexing the existing duplicates breaks logins
|
|
81
|
+
# for users whose `name` was empty when the field was introduced.
|
|
82
|
+
# So: walk every candidate (constant-time, no early exit) and
|
|
83
|
+
# surface the collision as an audit event so it can be cleaned up.
|
|
84
|
+
candidates = list(User.objects(
|
|
85
|
+
disabled__ne=True,
|
|
86
|
+
__raw__={'$or': [{'name': username}, {'email': username}]}
|
|
87
|
+
))
|
|
88
|
+
# Constant-time match: check every candidate so a wrong password
|
|
89
|
+
# against the first colliding user takes the same time as the
|
|
90
|
+
# last, which keeps timing from leaking *which* duplicate exists.
|
|
91
|
+
match = None
|
|
92
|
+
for candidate in candidates:
|
|
93
|
+
if candidate.check_password(user_password) and match is None:
|
|
94
|
+
match = candidate
|
|
95
|
+
audit('user.login.collision',
|
|
96
|
+
outcome='success' if match else 'failure',
|
|
97
|
+
actor_type='user',
|
|
98
|
+
actor_id=str(match.id) if match else None,
|
|
99
|
+
actor_name=username,
|
|
100
|
+
metadata={
|
|
101
|
+
'reason': 'multiple users matched login string',
|
|
102
|
+
'candidate_count': len(candidates),
|
|
103
|
+
'candidate_ids': [str(c.id) for c in candidates],
|
|
104
|
+
'matched_id': str(match.id) if match else None,
|
|
105
|
+
})
|
|
106
|
+
if match is None:
|
|
84
107
|
_abort_unauthorized("Invalid credentials")
|
|
108
|
+
return match, username
|
|
85
109
|
if not user_result.check_password(user_password):
|
|
86
110
|
_abort_unauthorized("Invalid credentials")
|
|
87
111
|
return user_result, username
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Ansible namespace — inventory provider HTTP endpoints.
|
|
3
|
+
|
|
4
|
+
The cross-module provider registry (`application.modules.inventory`)
|
|
5
|
+
holds named providers. This namespace exposes them in the Ansible JSON
|
|
6
|
+
shape under `/api/v1/ansible/inventory/<provider>` — same convention
|
|
7
|
+
as the CLI (`cmdbsyncer ansible inventory <provider>`), so URL and
|
|
8
|
+
command path read in the same order.
|
|
9
|
+
|
|
10
|
+
Both the CLI and this HTTP endpoint share `render_ansible_inventory`,
|
|
11
|
+
so data and format stay in lockstep regardless of how a consumer
|
|
12
|
+
reaches them.
|
|
13
|
+
"""
|
|
14
|
+
from flask import request
|
|
15
|
+
from flask_restx import Namespace, Resource
|
|
16
|
+
|
|
17
|
+
from application.api import require_token
|
|
18
|
+
from application.modules.inventory import (
|
|
19
|
+
list_inventory_providers,
|
|
20
|
+
render_ansible_inventory,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
API = Namespace('ansible', description='Ansible-side endpoints (inventory provider)')
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@API.route('/inventory')
|
|
27
|
+
class AnsibleProviderIndex(Resource):
|
|
28
|
+
"""List the providers the Ansible-format adapter can serve."""
|
|
29
|
+
|
|
30
|
+
@require_token
|
|
31
|
+
def get(self):
|
|
32
|
+
"""Names of all registered inventory providers."""
|
|
33
|
+
return {'providers': list_inventory_providers()}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@API.route('/inventory/<provider>')
|
|
37
|
+
class AnsibleProviderInventory(Resource):
|
|
38
|
+
"""Full inventory for `provider`, in Ansible JSON shape.
|
|
39
|
+
|
|
40
|
+
Pass `?host=NAME` to get a single host's vars dict instead of the
|
|
41
|
+
full catalog — same contract Ansible expects from a dynamic
|
|
42
|
+
inventory script.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
@require_token
|
|
46
|
+
def get(self, provider):
|
|
47
|
+
"""Render the provider in Ansible-format JSON."""
|
|
48
|
+
host = request.args.get('host')
|
|
49
|
+
result = render_ansible_inventory(provider, host=host)
|
|
50
|
+
if result is None:
|
|
51
|
+
return {'message': f'Unknown provider: {provider}'}, 404
|
|
52
|
+
if result is False:
|
|
53
|
+
return {'message': f'Host not found: {host}'}, 404
|
|
54
|
+
return result
|
|
@@ -11,8 +11,11 @@ from mongoengine.errors import DoesNotExist
|
|
|
11
11
|
|
|
12
12
|
from flask import request, abort
|
|
13
13
|
from flask_restx import Namespace, Resource, reqparse, fields
|
|
14
|
+
from application import app
|
|
14
15
|
from application.api import require_token
|
|
15
|
-
from application.models.host import
|
|
16
|
+
from application.models.host import (
|
|
17
|
+
Host, HostError, RELATION_TYPES, RELATION_INVERSE_LABEL,
|
|
18
|
+
)
|
|
16
19
|
# @TODO pre_deletion method for Host so no import needed
|
|
17
20
|
from application.plugins.checkmk.models import CheckmkFolderPool
|
|
18
21
|
|
|
@@ -164,6 +167,41 @@ LIST_RESPONSE = API.model('list_response', {
|
|
|
164
167
|
'_links': fields.Nested(LIST_LINKS),
|
|
165
168
|
})
|
|
166
169
|
|
|
170
|
+
_RELATION_TYPE_VALUES = [t for t, _ in RELATION_TYPES]
|
|
171
|
+
|
|
172
|
+
RELATION_INPUT = API.model('relation_input', {
|
|
173
|
+
'type': fields.String(required=True, enum=_RELATION_TYPE_VALUES,
|
|
174
|
+
description='Edge type. One of: '
|
|
175
|
+
+ ', '.join(_RELATION_TYPE_VALUES)),
|
|
176
|
+
'target': fields.String(required=True,
|
|
177
|
+
description='Hostname of the target object.'),
|
|
178
|
+
'source': fields.String(required=False, default='manual',
|
|
179
|
+
description='Free-form provenance tag '
|
|
180
|
+
'(default "manual"). Stored on the '
|
|
181
|
+
'edge so importers can later prune '
|
|
182
|
+
'their own edges without touching '
|
|
183
|
+
'manual ones.'),
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
RELATION_ITEM = API.model('relation_item', {
|
|
187
|
+
'type': fields.String(description='Edge type code'),
|
|
188
|
+
'type_label': fields.String(description='Human label for the type'),
|
|
189
|
+
'target': fields.String(description='Target hostname'),
|
|
190
|
+
'source': fields.String(description='Provenance tag (manual / import / …)'),
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
RELATIONS_RESPONSE = API.model('relations_response', {
|
|
194
|
+
'hostname': fields.String,
|
|
195
|
+
'outgoing': fields.List(fields.Nested(RELATION_ITEM)),
|
|
196
|
+
'inbound': fields.List(fields.Nested(RELATION_ITEM)),
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _require_cmdb_mode():
|
|
201
|
+
"""Abort with 404 unless CMDB_MODE is enabled."""
|
|
202
|
+
if not app.config.get('CMDB_MODE'):
|
|
203
|
+
abort(404, 'CMDB_MODE is disabled on this install.')
|
|
204
|
+
|
|
167
205
|
|
|
168
206
|
# ---------------------------------------------------------------------------
|
|
169
207
|
# Endpoints
|
|
@@ -393,9 +431,13 @@ parser.add_argument('start', type=int, help='Pagination start')
|
|
|
393
431
|
parser.add_argument('limit', type=int, help='Pagination limit')
|
|
394
432
|
|
|
395
433
|
|
|
434
|
+
MAX_PAGE_LIMIT = 10000
|
|
435
|
+
|
|
436
|
+
|
|
396
437
|
@API.route('/all')
|
|
397
438
|
@API.param('start', 'Zero-based page offset. Default ``1``.', type='integer')
|
|
398
|
-
@API.param('limit', 'Page size, max items returned per call. Default ``100
|
|
439
|
+
@API.param('limit', 'Page size, max items returned per call. Default ``100``,'
|
|
440
|
+
f' max ``{MAX_PAGE_LIMIT}``.',
|
|
399
441
|
type='integer')
|
|
400
442
|
class HostDetailListApi(Resource):
|
|
401
443
|
"""Paginated listing of every host."""
|
|
@@ -403,7 +445,8 @@ class HostDetailListApi(Resource):
|
|
|
403
445
|
@API.doc(security=['x-login-user', 'basicAuth'])
|
|
404
446
|
@API.response(200, 'A page of host objects plus pagination links.',
|
|
405
447
|
LIST_RESPONSE)
|
|
406
|
-
@API.response(400, 'Pagination params not numeric or
|
|
448
|
+
@API.response(400, 'Pagination params not numeric, negative, or limit '
|
|
449
|
+
f'above {MAX_PAGE_LIMIT}.', ERROR)
|
|
407
450
|
@API.response(401, 'Authentication failed', ERROR)
|
|
408
451
|
@require_token
|
|
409
452
|
def get(self):
|
|
@@ -420,6 +463,8 @@ class HostDetailListApi(Resource):
|
|
|
420
463
|
abort(400, "start and limit must be integers")
|
|
421
464
|
if start < 0 or limit < 0:
|
|
422
465
|
abort(400, "start and limit must be non-negative")
|
|
466
|
+
if limit > MAX_PAGE_LIMIT:
|
|
467
|
+
abort(400, f"limit must be <= {MAX_PAGE_LIMIT}")
|
|
423
468
|
end = start+limit
|
|
424
469
|
|
|
425
470
|
db_objecs = Host.objects(is_object__ne=True)
|
|
@@ -441,3 +486,124 @@ class HostDetailListApi(Resource):
|
|
|
441
486
|
'size': total,
|
|
442
487
|
'_links': links,
|
|
443
488
|
}
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
@API.route('/<hostname>/relations')
|
|
492
|
+
@API.param('hostname', 'Existing host name. CMDB_MODE only.')
|
|
493
|
+
class HostRelationsApi(Resource):
|
|
494
|
+
"""
|
|
495
|
+
Read, add or remove typed Host relations.
|
|
496
|
+
|
|
497
|
+
Only available when ``CMDB_MODE`` is True. Outgoing edges live on
|
|
498
|
+
the source host; inbound edges are computed via a Mongo query and
|
|
499
|
+
are read-only on this endpoint (mutate them on the source host).
|
|
500
|
+
"""
|
|
501
|
+
|
|
502
|
+
@staticmethod
|
|
503
|
+
def _serialize_outgoing(host):
|
|
504
|
+
type_label = dict(RELATION_TYPES)
|
|
505
|
+
out = []
|
|
506
|
+
for rel in (host.relations or []):
|
|
507
|
+
target = rel.target_host
|
|
508
|
+
if not target:
|
|
509
|
+
continue
|
|
510
|
+
out.append({
|
|
511
|
+
'type': rel.type,
|
|
512
|
+
'type_label': type_label.get(rel.type, rel.type),
|
|
513
|
+
'target': target.hostname,
|
|
514
|
+
'source': rel.source or '',
|
|
515
|
+
})
|
|
516
|
+
return out
|
|
517
|
+
|
|
518
|
+
@staticmethod
|
|
519
|
+
def _serialize_inbound(host):
|
|
520
|
+
if not host.pk:
|
|
521
|
+
return []
|
|
522
|
+
out = []
|
|
523
|
+
sources = Host.objects(__raw__={'relations.target_host': host.pk}).only(
|
|
524
|
+
'hostname', 'relations'
|
|
525
|
+
)
|
|
526
|
+
for src in sources:
|
|
527
|
+
for rel in (src.relations or []):
|
|
528
|
+
if rel.target_host and rel.target_host.pk == host.pk:
|
|
529
|
+
out.append({
|
|
530
|
+
'type': rel.type,
|
|
531
|
+
'type_label': RELATION_INVERSE_LABEL.get(rel.type,
|
|
532
|
+
rel.type),
|
|
533
|
+
'target': src.hostname,
|
|
534
|
+
'source': rel.source or '',
|
|
535
|
+
})
|
|
536
|
+
return out
|
|
537
|
+
|
|
538
|
+
@API.doc(security=['x-login-user', 'basicAuth'])
|
|
539
|
+
@API.response(200, 'Outgoing and inbound edges for this host.',
|
|
540
|
+
RELATIONS_RESPONSE)
|
|
541
|
+
@API.response(401, 'Authentication failed', ERROR)
|
|
542
|
+
@API.response(404, 'Host not found or CMDB_MODE disabled.', ERROR)
|
|
543
|
+
@require_token
|
|
544
|
+
def get(self, hostname):
|
|
545
|
+
"""List outgoing + inbound relations for *hostname*."""
|
|
546
|
+
_require_cmdb_mode()
|
|
547
|
+
try:
|
|
548
|
+
host = Host.objects.get(hostname=hostname)
|
|
549
|
+
except DoesNotExist:
|
|
550
|
+
return {'error': 'Host not found'}, 404
|
|
551
|
+
return {
|
|
552
|
+
'hostname': hostname,
|
|
553
|
+
'outgoing': self._serialize_outgoing(host),
|
|
554
|
+
'inbound': self._serialize_inbound(host),
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
@API.doc(security=['x-login-user', 'basicAuth'])
|
|
558
|
+
@API.expect(RELATION_INPUT, validate=True)
|
|
559
|
+
@API.response(200, 'Relation added (or no-op if it already existed).',
|
|
560
|
+
STATUS)
|
|
561
|
+
@API.response(400, 'Unknown relation type or self-edge.', ERROR)
|
|
562
|
+
@API.response(401, 'Authentication failed', ERROR)
|
|
563
|
+
@API.response(404, 'Source or target host not found, or CMDB_MODE off.',
|
|
564
|
+
ERROR)
|
|
565
|
+
@require_token
|
|
566
|
+
def post(self, hostname):
|
|
567
|
+
"""Add a relation from *hostname* to ``target``."""
|
|
568
|
+
_require_cmdb_mode()
|
|
569
|
+
body = request.json or {}
|
|
570
|
+
target_name = body.get('target')
|
|
571
|
+
rtype = body.get('type')
|
|
572
|
+
rsource = body.get('source') or 'manual'
|
|
573
|
+
try:
|
|
574
|
+
host = Host.objects.get(hostname=hostname)
|
|
575
|
+
target = Host.objects.get(hostname=target_name)
|
|
576
|
+
except DoesNotExist:
|
|
577
|
+
return {'error': 'Host or target not found'}, 404
|
|
578
|
+
try:
|
|
579
|
+
added = host.add_relation(target, rtype, source=rsource)
|
|
580
|
+
except HostError as exc:
|
|
581
|
+
abort(400, str(exc))
|
|
582
|
+
if added:
|
|
583
|
+
host.save()
|
|
584
|
+
return {'status': 'added'}, 200
|
|
585
|
+
return {'status': 'unchanged'}, 200
|
|
586
|
+
|
|
587
|
+
@API.doc(security=['x-login-user', 'basicAuth'])
|
|
588
|
+
@API.expect(RELATION_INPUT, validate=True)
|
|
589
|
+
@API.response(200, 'Relation removed (or no-op if it never existed).',
|
|
590
|
+
STATUS)
|
|
591
|
+
@API.response(401, 'Authentication failed', ERROR)
|
|
592
|
+
@API.response(404, 'Source or target host not found, or CMDB_MODE off.',
|
|
593
|
+
ERROR)
|
|
594
|
+
@require_token
|
|
595
|
+
def delete(self, hostname):
|
|
596
|
+
"""Remove the relation ``(type, target)`` from *hostname*."""
|
|
597
|
+
_require_cmdb_mode()
|
|
598
|
+
body = request.json or {}
|
|
599
|
+
target_name = body.get('target')
|
|
600
|
+
rtype = body.get('type')
|
|
601
|
+
try:
|
|
602
|
+
host = Host.objects.get(hostname=hostname)
|
|
603
|
+
target = Host.objects.get(hostname=target_name)
|
|
604
|
+
except DoesNotExist:
|
|
605
|
+
return {'error': 'Host or target not found'}, 404
|
|
606
|
+
if host.remove_relation(target, rtype):
|
|
607
|
+
host.save()
|
|
608
|
+
return {'status': 'removed'}, 200
|
|
609
|
+
return {'status': 'unchanged'}, 200
|
|
@@ -14,6 +14,7 @@ import json
|
|
|
14
14
|
from flask import request
|
|
15
15
|
from flask_restx import Namespace, Resource, fields
|
|
16
16
|
|
|
17
|
+
from application import log
|
|
17
18
|
from application.api import require_token
|
|
18
19
|
from application.plugins.rules.rule_definitions import rules as enabled_rules
|
|
19
20
|
from application.plugins.rules.rule_import_export import (
|
|
@@ -105,11 +106,21 @@ AUTORULES_RESPONSE = API.model('autorules_response', {
|
|
|
105
106
|
|
|
106
107
|
|
|
107
108
|
def _decode_rule_lines(rules):
|
|
108
|
-
"""Yield JSON dicts from ``to_json()`` strings.
|
|
109
|
+
"""Yield JSON dicts from ``to_json()`` strings.
|
|
110
|
+
|
|
111
|
+
Skipped lines are surfaced in the syncer Log so a corrupted rule
|
|
112
|
+
document doesn't silently disappear from /rules/<type> exports.
|
|
113
|
+
"""
|
|
109
114
|
for raw in rules:
|
|
110
115
|
try:
|
|
111
116
|
yield json.loads(raw)
|
|
112
|
-
except (ValueError, TypeError):
|
|
117
|
+
except (ValueError, TypeError) as exp:
|
|
118
|
+
preview = (raw[:200] + '…') if isinstance(raw, str) and len(raw) > 200 else raw
|
|
119
|
+
log.log(
|
|
120
|
+
f"Skipping rule with invalid JSON: {exp}",
|
|
121
|
+
source='api.rules',
|
|
122
|
+
details=[('error', str(exp)), ('preview', str(preview))],
|
|
123
|
+
)
|
|
113
124
|
continue
|
|
114
125
|
|
|
115
126
|
|
|
@@ -8,7 +8,6 @@ namespace. The ``/cron/trigger/<group>`` route additionally accepts an
|
|
|
8
8
|
``X-Webhook-Token`` header tied to the CronGroup, so external systems
|
|
9
9
|
can fire a sync without carrying a user credential.
|
|
10
10
|
"""
|
|
11
|
-
import hmac
|
|
12
11
|
from datetime import datetime, timedelta
|
|
13
12
|
from mongoengine.errors import DoesNotExist
|
|
14
13
|
from flask import request
|
|
@@ -302,7 +301,13 @@ class SyncerCronTriggerApi(Resource):
|
|
|
302
301
|
|
|
303
302
|
token = request.headers.get('X-Webhook-Token')
|
|
304
303
|
if auth_method is None and token:
|
|
305
|
-
|
|
304
|
+
# Hash-on-read: legacy rows still carrying plaintext get
|
|
305
|
+
# upgraded the first time they authenticate after the
|
|
306
|
+
# rollout, so operators don't have to run a separate
|
|
307
|
+
# migration step.
|
|
308
|
+
if group.migrate_legacy_webhook_token():
|
|
309
|
+
group.save()
|
|
310
|
+
if not group.webhook_enabled or not group.webhook_token_hash:
|
|
306
311
|
log.log("Webhook trigger rejected", source="API",
|
|
307
312
|
details=[('group', group_name),
|
|
308
313
|
('reason', 'webhook disabled or no token set'),
|
|
@@ -313,8 +318,8 @@ class SyncerCronTriggerApi(Resource):
|
|
|
313
318
|
target_name=group_name,
|
|
314
319
|
metadata={'reason': 'webhook disabled or no token set'})
|
|
315
320
|
return {'error': "Webhook not enabled for this group"}, 403
|
|
316
|
-
# hmac.compare_digest
|
|
317
|
-
if not
|
|
321
|
+
# verify_webhook_token uses hmac.compare_digest internally.
|
|
322
|
+
if not group.verify_webhook_token(token):
|
|
318
323
|
log.log("Webhook trigger rejected", source="API",
|
|
319
324
|
details=[('group', group_name),
|
|
320
325
|
('reason', 'token mismatch'),
|
|
@@ -8,6 +8,7 @@ from application import app, limiter
|
|
|
8
8
|
from application.api.syncer import API as syncer
|
|
9
9
|
from application.api.objects import API as objects
|
|
10
10
|
from application.api.rules import API as rules
|
|
11
|
+
from application.api.inventory import API as ansible
|
|
11
12
|
|
|
12
13
|
API_BP = Blueprint('api', __name__)
|
|
13
14
|
|
|
@@ -52,3 +53,4 @@ API = Api(API_BP, authorizations=AUTHORIZATIONS,
|
|
|
52
53
|
API.add_namespace(syncer, path='/syncer')
|
|
53
54
|
API.add_namespace(objects, path='/objects')
|
|
54
55
|
API.add_namespace(rules, path='/rules')
|
|
56
|
+
API.add_namespace(ansible, path='/ansible')
|