cmdbsyncer 3.12.6__py3-none-any.whl
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.
- application/__init__.py +354 -0
- application/_version.py +8 -0
- application/api/__init__.py +92 -0
- application/api/objects.py +282 -0
- application/api/syncer.py +145 -0
- application/api/views.py +44 -0
- application/auth/views.py +240 -0
- application/buildinfo.txt +1 -0
- application/cli.py +31 -0
- application/config.py +199 -0
- application/docu_links.py +11 -0
- application/enterprise.py +55 -0
- application/helpers/__init__.py +0 -0
- application/helpers/cron.py +46 -0
- application/helpers/get_account.py +88 -0
- application/helpers/inventory.py +95 -0
- application/helpers/plugins.py +132 -0
- application/helpers/sates.py +26 -0
- application/helpers/sql.py +36 -0
- application/helpers/syncer_jinja.py +181 -0
- application/helpers/tablib_formater.py +16 -0
- application/models/__init__.py +0 -0
- application/models/account.py +164 -0
- application/models/config.py +11 -0
- application/models/cron.py +101 -0
- application/models/forms.py +59 -0
- application/models/host.py +593 -0
- application/models/states.py +14 -0
- application/models/user.py +142 -0
- application/modules/__init__.py +0 -0
- application/modules/custom_attributes/models.py +27 -0
- application/modules/custom_attributes/rules.py +35 -0
- application/modules/custom_attributes/views.py +37 -0
- application/modules/debug.py +100 -0
- application/modules/email.py +52 -0
- application/modules/log/log.py +65 -0
- application/modules/log/models.py +33 -0
- application/modules/log/views.py +80 -0
- application/modules/plugin.py +424 -0
- application/modules/rule/__init__.py +0 -0
- application/modules/rule/filter.py +45 -0
- application/modules/rule/match.py +110 -0
- application/modules/rule/models.py +214 -0
- application/modules/rule/rewrite.py +102 -0
- application/modules/rule/rule.py +328 -0
- application/modules/rule/views.py +727 -0
- application/modules/statefile.py +99 -0
- application/plugins/__init__.py +4 -0
- application/plugins/ansible/__init__.py +260 -0
- application/plugins/ansible/admin_views.py +41 -0
- application/plugins/ansible/inventory.py +137 -0
- application/plugins/ansible/models.py +75 -0
- application/plugins/ansible/rest_api/ansible.py +38 -0
- application/plugins/ansible/rules.py +24 -0
- application/plugins/ansible/site_syncer.py +82 -0
- application/plugins/ansible/views.py +20 -0
- application/plugins/bmc_remedy/__init__.py +21 -0
- application/plugins/bmc_remedy/bmc_remedy.py +95 -0
- application/plugins/checkmk/__init__.py +578 -0
- application/plugins/checkmk/admin_views.py +204 -0
- application/plugins/checkmk/bi.py +170 -0
- application/plugins/checkmk/cmk2.py +181 -0
- application/plugins/checkmk/cmk_rules.py +422 -0
- application/plugins/checkmk/dcd.py +181 -0
- application/plugins/checkmk/downtimes.py +323 -0
- application/plugins/checkmk/groups.py +296 -0
- application/plugins/checkmk/helpers.py +34 -0
- application/plugins/checkmk/import_v1.py +69 -0
- application/plugins/checkmk/import_v2.py +64 -0
- application/plugins/checkmk/inits.py +443 -0
- application/plugins/checkmk/inventorize.py +317 -0
- application/plugins/checkmk/models.py +716 -0
- application/plugins/checkmk/passwords.py +91 -0
- application/plugins/checkmk/poolfolder.py +38 -0
- application/plugins/checkmk/rules.py +330 -0
- application/plugins/checkmk/sites.py +39 -0
- application/plugins/checkmk/syncer.py +1265 -0
- application/plugins/checkmk/tags.py +401 -0
- application/plugins/checkmk/tests/__init__.py +3 -0
- application/plugins/checkmk/tests/test_bi.py +122 -0
- application/plugins/checkmk/tests/test_cmk2.py +262 -0
- application/plugins/checkmk/tests/test_cmk_rules.py +238 -0
- application/plugins/checkmk/tests/test_dcd.py +207 -0
- application/plugins/checkmk/tests/test_downtimes.py +148 -0
- application/plugins/checkmk/tests/test_groups.py +159 -0
- application/plugins/checkmk/tests/test_helpers.py +108 -0
- application/plugins/checkmk/tests/test_import_v1.py +93 -0
- application/plugins/checkmk/tests/test_import_v2.py +154 -0
- application/plugins/checkmk/tests/test_inventorize.py +148 -0
- application/plugins/checkmk/tests/test_passwords.py +118 -0
- application/plugins/checkmk/tests/test_poolfolder.py +114 -0
- application/plugins/checkmk/tests/test_rules.py +244 -0
- application/plugins/checkmk/tests/test_sites.py +66 -0
- application/plugins/checkmk/tests/test_syncer.py +1060 -0
- application/plugins/checkmk/tests/test_tags.py +240 -0
- application/plugins/checkmk/tests/test_users.py +190 -0
- application/plugins/checkmk/users.py +105 -0
- application/plugins/checkmk/views.py +1095 -0
- application/plugins/cisco_dna/__init__.py +67 -0
- application/plugins/cisco_dna/syncer.py +227 -0
- application/plugins/cron.py +198 -0
- application/plugins/csv/__init__.py +156 -0
- application/plugins/csv/csv.py +95 -0
- application/plugins/file_requests.py +39 -0
- application/plugins/idoit/__init__.py +143 -0
- application/plugins/idoit/admin_views.py +17 -0
- application/plugins/idoit/models.py +69 -0
- application/plugins/idoit/rules.py +43 -0
- application/plugins/idoit/syncer.py +320 -0
- application/plugins/idoit/views.py +51 -0
- application/plugins/jdisc/__init__.py +153 -0
- application/plugins/jdisc/applications.py +56 -0
- application/plugins/jdisc/devices.py +169 -0
- application/plugins/jdisc/executables.py +52 -0
- application/plugins/jdisc/jdisc.py +114 -0
- application/plugins/jira/__init__.py +19 -0
- application/plugins/jira/jira.py +74 -0
- application/plugins/jira_cloud/__init__.py +25 -0
- application/plugins/jira_cloud/jira_cloud.py +109 -0
- application/plugins/json/__init__.py +21 -0
- application/plugins/json/json.py +12 -0
- application/plugins/ldap/__init__.py +38 -0
- application/plugins/ldap/ldap.py +123 -0
- application/plugins/maintenance/__init__.py +365 -0
- application/plugins/mssql/__init__.py +53 -0
- application/plugins/mysql/__init__.py +33 -0
- application/plugins/mysql/mysql.py +93 -0
- application/plugins/netbox/__init__.py +530 -0
- application/plugins/netbox/admin_views.py +64 -0
- application/plugins/netbox/cluster.py +41 -0
- application/plugins/netbox/contacts.py +30 -0
- application/plugins/netbox/dataflow.py +179 -0
- application/plugins/netbox/devices.py +192 -0
- application/plugins/netbox/interfaces.py +124 -0
- application/plugins/netbox/ips.py +112 -0
- application/plugins/netbox/models.py +486 -0
- application/plugins/netbox/netbox.py +389 -0
- application/plugins/netbox/networks.py +22 -0
- application/plugins/netbox/prefixes.py +22 -0
- application/plugins/netbox/rules.py +441 -0
- application/plugins/netbox/views.py +202 -0
- application/plugins/netbox/virtualmachines.py +189 -0
- application/plugins/prtg/__init__.py +42 -0
- application/plugins/prtg/prtg.py +168 -0
- application/plugins/pyodbc/__init__.py +52 -0
- application/plugins/pyodbc/pyodbc.py +97 -0
- application/plugins/rest/__init__.py +31 -0
- application/plugins/rest/rest.py +131 -0
- application/plugins/rules/__init__.py +56 -0
- application/plugins/rules/admin_views.py +16 -0
- application/plugins/rules/autorules.py +170 -0
- application/plugins/rules/models.py +23 -0
- application/plugins/rules/rule_definitions.py +44 -0
- application/plugins/rules/rule_import_export.py +142 -0
- application/plugins/rules/views.py +76 -0
- application/plugins/syncer_shell.py +245 -0
- application/plugins/vmware/__init__.py +83 -0
- application/plugins/vmware/admin_views.py +18 -0
- application/plugins/vmware/custom_attributes.py +170 -0
- application/plugins/vmware/models.py +64 -0
- application/plugins/vmware/rules.py +29 -0
- application/plugins/vmware/views.py +15 -0
- application/plugins/vmware/vmware.py +38 -0
- application/plugins/yml/__init__.py +55 -0
- application/plugins/yml/yml.py +167 -0
- application/plugins_cli.py +222 -0
- application/static/js/main.js +3 -0
- application/static/logo_white.png +0 -0
- application/templates/admin/file/list.html +197 -0
- application/templates/admin/index.html +264 -0
- application/templates/admin/master.html +466 -0
- application/templates/admin/set_cmk_version_form.html +14 -0
- application/templates/admin/set_template_form.html +21 -0
- application/templates/base.html +202 -0
- application/templates/debug.html +100 -0
- application/templates/debug_host.html +168 -0
- application/templates/email/newuser.html +10 -0
- application/templates/email/resetpassword.html +9 -0
- application/templates/formular.html +10 -0
- application/templates/license_info.html +81 -0
- application/templates/login.html +22 -0
- application/templates/set_2fa.html +63 -0
- application/views/account.py +238 -0
- application/views/config.py +29 -0
- application/views/cron.py +137 -0
- application/views/default.py +210 -0
- application/views/fileadmin.py +28 -0
- application/views/host.py +1648 -0
- application/views/license.py +39 -0
- application/views/user.py +55 -0
- cmdbsyncer-3.12.6.dist-info/METADATA +316 -0
- cmdbsyncer-3.12.6.dist-info/RECORD +201 -0
- cmdbsyncer-3.12.6.dist-info/WHEEL +5 -0
- cmdbsyncer-3.12.6.dist-info/entry_points.txt +3 -0
- cmdbsyncer-3.12.6.dist-info/licenses/LICENSE +21 -0
- cmdbsyncer-3.12.6.dist-info/top_level.txt +2 -0
- syncerapi/__init__.py +0 -0
- syncerapi/v1/__init__.py +9 -0
- syncerapi/v1/core/__init__.py +7 -0
- syncerapi/v1/inventory/__init__.py +1 -0
- syncerapi/v1/rest/__init__.py +2 -0
application/__init__.py
ADDED
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
""" Main entry """
|
|
2
|
+
# Flask app factory with intentional deferred imports to avoid circular imports.
|
|
3
|
+
# pylint: disable=wrong-import-position,import-outside-toplevel,ungrouped-imports,line-too-long,wildcard-import,unused-wildcard-import,cyclic-import
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
import logging
|
|
7
|
+
import importlib
|
|
8
|
+
import pkgutil
|
|
9
|
+
import warnings
|
|
10
|
+
from logging import config as log_config
|
|
11
|
+
from tablib.formats import registry as tablib_registry
|
|
12
|
+
import mongoengine
|
|
13
|
+
from sortedcontainers import SortedDict
|
|
14
|
+
from flask import Flask, url_for, redirect
|
|
15
|
+
from flask_admin import Admin
|
|
16
|
+
from flask_admin.menu import MenuLink
|
|
17
|
+
from flask_login import LoginManager
|
|
18
|
+
from flask_mail import Mail
|
|
19
|
+
from flask_bootstrap import Bootstrap
|
|
20
|
+
from flask_mongoengine import MongoEngine
|
|
21
|
+
from flask_wtf.csrf import CSRFProtect
|
|
22
|
+
|
|
23
|
+
from application.helpers.tablib_formater import ExportObjects
|
|
24
|
+
|
|
25
|
+
warnings.filterwarnings('ignore', category=UserWarning)
|
|
26
|
+
|
|
27
|
+
tablib_registry.register('syncer_rules', ExportObjects())
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _read_version_from_changelog():
|
|
31
|
+
"""
|
|
32
|
+
Resolve the current version from the newest changelog/v*.md file by
|
|
33
|
+
reading its first `## Version x.y.z` header. Used as the dev-mode source
|
|
34
|
+
so VERSION tracks the changelog without waiting for `make sync-version`.
|
|
35
|
+
"""
|
|
36
|
+
import glob as _glob
|
|
37
|
+
import re as _re
|
|
38
|
+
changelog_dir = os.path.join(os.path.dirname(__file__), "..", "changelog")
|
|
39
|
+
files = _glob.glob(os.path.join(changelog_dir, "v*.md"))
|
|
40
|
+
|
|
41
|
+
def _key(path):
|
|
42
|
+
m = _re.search(r"v(\d+)\.(\d+)\.md$", path)
|
|
43
|
+
return (int(m.group(1)), int(m.group(2))) if m else (0, 0)
|
|
44
|
+
|
|
45
|
+
for path in sorted(files, key=_key, reverse=True):
|
|
46
|
+
with open(path, encoding="utf-8") as fh:
|
|
47
|
+
for changelog_line in fh:
|
|
48
|
+
m = _re.match(r"^## Version (\d+\.\d+\.\d+)\s*$", changelog_line)
|
|
49
|
+
if m:
|
|
50
|
+
return m.group(1)
|
|
51
|
+
return None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _resolve_version():
|
|
55
|
+
# In a source checkout the changelog directory is present and authoritative
|
|
56
|
+
# so edits become visible without running `make sync-version`. In an
|
|
57
|
+
# installed wheel the changelog is gone and `_version.py` is the single
|
|
58
|
+
# source of truth (written at build time and matched by pyproject.toml).
|
|
59
|
+
changelog_dir = os.path.join(os.path.dirname(__file__), "..", "changelog")
|
|
60
|
+
if os.path.isdir(changelog_dir):
|
|
61
|
+
from_changelog = _read_version_from_changelog()
|
|
62
|
+
if from_changelog:
|
|
63
|
+
return from_changelog
|
|
64
|
+
from application._version import __version__
|
|
65
|
+
return __version__
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
VERSION = _resolve_version()
|
|
69
|
+
|
|
70
|
+
CONFIG_MAP = {
|
|
71
|
+
'prod': 'application.config.ProductionConfig',
|
|
72
|
+
'compose': 'application.config.ComposeConfig',
|
|
73
|
+
'base': 'application.config.BaseConfig',
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
app = Flask(__name__)
|
|
77
|
+
config_name = os.environ.get('config', 'base').lower()
|
|
78
|
+
app.config.from_object(CONFIG_MAP.get(config_name, CONFIG_MAP['base']))
|
|
79
|
+
if config_name == "base":
|
|
80
|
+
app.jinja_env.auto_reload = True
|
|
81
|
+
csrf = CSRFProtect(app)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
## Read Build Data
|
|
85
|
+
|
|
86
|
+
# buildinfo.txt lives inside the package so it ships with the wheel; the
|
|
87
|
+
# pre-commit hook rewrites it on every commit so dev runs also see a fresh
|
|
88
|
+
# timestamp. Missing file (e.g. running from a raw checkout without the
|
|
89
|
+
# hook installed) is not fatal — we just expose an 'unknown' build date.
|
|
90
|
+
_buildinfo_path = os.path.join(app.root_path, "buildinfo.txt")
|
|
91
|
+
if os.path.isfile(_buildinfo_path):
|
|
92
|
+
with open(_buildinfo_path, encoding="utf-8") as _buildinfo:
|
|
93
|
+
for line in _buildinfo:
|
|
94
|
+
name, key = line.split('=')
|
|
95
|
+
app.config[name] = key.strip()
|
|
96
|
+
else:
|
|
97
|
+
app.config.setdefault("BUILD_DATE", "unknown")
|
|
98
|
+
|
|
99
|
+
log_config.dictConfig(app.config['LOGGING'])
|
|
100
|
+
logger = logging.getLogger('debug')
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
try:
|
|
104
|
+
from local_config import config
|
|
105
|
+
app.config.update(config)
|
|
106
|
+
except ModuleNotFoundError:
|
|
107
|
+
pass
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
if '--debug' in sys.argv:
|
|
111
|
+
logger.setLevel(logging.DEBUG)
|
|
112
|
+
|
|
113
|
+
if app.config['DEBUG']:
|
|
114
|
+
logger.info('Loaded Debug Mode')
|
|
115
|
+
|
|
116
|
+
## Sentry
|
|
117
|
+
if app.config['SENTRY_ENABLED']:
|
|
118
|
+
import sentry_sdk
|
|
119
|
+
from sentry_sdk.integrations.flask import FlaskIntegration
|
|
120
|
+
|
|
121
|
+
def filter_events(event, _hint):
|
|
122
|
+
"""
|
|
123
|
+
Filter a list of Exception from sending to sentry
|
|
124
|
+
"""
|
|
125
|
+
excp = event.get('exception', {})
|
|
126
|
+
values = excp.get('values', [])
|
|
127
|
+
if values:
|
|
128
|
+
excp_type = values[0]['type']
|
|
129
|
+
if excp_type in [
|
|
130
|
+
'timeout',
|
|
131
|
+
]:
|
|
132
|
+
return None
|
|
133
|
+
return event
|
|
134
|
+
|
|
135
|
+
sentry_sdk.init(
|
|
136
|
+
dsn=app.config['SENTRY_DSN'],
|
|
137
|
+
before_send=filter_events,
|
|
138
|
+
integrations=[FlaskIntegration(),
|
|
139
|
+
],
|
|
140
|
+
release=VERSION
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
try:
|
|
144
|
+
db = MongoEngine()
|
|
145
|
+
from uwsgidecorators import postfork
|
|
146
|
+
|
|
147
|
+
@postfork
|
|
148
|
+
def setup_db():
|
|
149
|
+
"""db init in uwsgi"""
|
|
150
|
+
db.init_app(app)
|
|
151
|
+
|
|
152
|
+
except ImportError:
|
|
153
|
+
#print(" \033[91mWARNING: STANDALONE MODE - NOT FOR PROD\033[0m")
|
|
154
|
+
#print(" * HINT: uwsgi modul not loaded")
|
|
155
|
+
# Output makes problems for commands
|
|
156
|
+
db = MongoEngine(app)
|
|
157
|
+
|
|
158
|
+
def init_db():
|
|
159
|
+
"""DB Init for Multiprocessing Pool"""
|
|
160
|
+
mongoengine.disconnect()
|
|
161
|
+
with app.app_context():
|
|
162
|
+
MongoEngine(app)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
from application.helpers.sates import get_changes
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@app.before_request
|
|
169
|
+
def load_before_request():
|
|
170
|
+
"""
|
|
171
|
+
Helper to have up to date data for each request
|
|
172
|
+
"""
|
|
173
|
+
app.config['CHANGES'] = get_changes()
|
|
174
|
+
|
|
175
|
+
# We need the db in the Module
|
|
176
|
+
from application.modules.log.log import Log
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
log = Log()
|
|
180
|
+
|
|
181
|
+
mail = Mail(app)
|
|
182
|
+
bootstrap = Bootstrap(app)
|
|
183
|
+
|
|
184
|
+
login_manager = LoginManager()
|
|
185
|
+
login_manager.init_app(app)
|
|
186
|
+
login_manager.login_view = 'auth.login'
|
|
187
|
+
login_manager.login_message = False
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
cron_register = SortedDict()
|
|
192
|
+
plugin_register = []
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
from application.views.default import IndexView, DefaultModelView
|
|
196
|
+
|
|
197
|
+
from application.auth.views import AUTH
|
|
198
|
+
app.register_blueprint(AUTH)
|
|
199
|
+
|
|
200
|
+
from application.modules.rule.views import FiltereModelView, RewriteAttributeView
|
|
201
|
+
|
|
202
|
+
@app.route('/')
|
|
203
|
+
def page_redirect():
|
|
204
|
+
"""
|
|
205
|
+
Redirect to admin Panel
|
|
206
|
+
"""
|
|
207
|
+
return redirect(url_for("admin.index"))
|
|
208
|
+
|
|
209
|
+
def _register_all_plugin_admin_views():
|
|
210
|
+
from application.helpers.plugins import is_plugin_disabled
|
|
211
|
+
import application.plugins as plugins_package
|
|
212
|
+
import plugins as external_plugins_package
|
|
213
|
+
|
|
214
|
+
plugin_modules = []
|
|
215
|
+
|
|
216
|
+
for _, module_name, _ in pkgutil.iter_modules(
|
|
217
|
+
external_plugins_package.__path__, external_plugins_package.__name__ + "."
|
|
218
|
+
):
|
|
219
|
+
# module_name is e.g. "plugins.netbox" — extract the short ident
|
|
220
|
+
short_name = module_name.rsplit(".", 1)[-1]
|
|
221
|
+
if is_plugin_disabled(short_name):
|
|
222
|
+
logger.info("Plugin '%s' is disabled, skipping", short_name)
|
|
223
|
+
continue
|
|
224
|
+
plugin_modules.append(module_name)
|
|
225
|
+
|
|
226
|
+
for _, module_name, _ in pkgutil.iter_modules(
|
|
227
|
+
plugins_package.__path__, plugins_package.__name__ + "."
|
|
228
|
+
):
|
|
229
|
+
short_name = module_name.rsplit(".", 1)[-1]
|
|
230
|
+
if is_plugin_disabled(short_name):
|
|
231
|
+
logger.info("Plugin '%s' is disabled, skipping", short_name)
|
|
232
|
+
continue
|
|
233
|
+
plugin_modules.append(module_name)
|
|
234
|
+
|
|
235
|
+
for module_name in plugin_modules:
|
|
236
|
+
admin_module_name = f"{module_name}.admin_views"
|
|
237
|
+
try:
|
|
238
|
+
admin_module = importlib.import_module(admin_module_name)
|
|
239
|
+
except ModuleNotFoundError as exc:
|
|
240
|
+
if exc.name == admin_module_name:
|
|
241
|
+
continue
|
|
242
|
+
raise
|
|
243
|
+
except Exception: # pylint: disable=broad-exception-caught
|
|
244
|
+
logger.exception(
|
|
245
|
+
"Failed to register admin views for plugin %s", module_name
|
|
246
|
+
)
|
|
247
|
+
if '--debug' in sys.argv:
|
|
248
|
+
raise
|
|
249
|
+
continue
|
|
250
|
+
|
|
251
|
+
register = getattr(admin_module, "register_admin_views", None)
|
|
252
|
+
if callable(register):
|
|
253
|
+
register(admin)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
from application.api.views import API_BP as api
|
|
257
|
+
app.register_blueprint(api, url_prefix="/api/v1")
|
|
258
|
+
csrf.exempt(api)
|
|
259
|
+
|
|
260
|
+
admin = Admin(app, name=f"cmdbsyncer {VERSION}",
|
|
261
|
+
index_view=IndexView(),
|
|
262
|
+
category_icon_classes={
|
|
263
|
+
'Accounts': 'fa fa-users',
|
|
264
|
+
'Ansible': 'fa fa-cogs',
|
|
265
|
+
'Checkmk': 'fa fa-heartbeat',
|
|
266
|
+
'Checkmk Server': 'fa fa-building',
|
|
267
|
+
'Cronjobs': 'fa fa-clock-o',
|
|
268
|
+
'i-doit': 'fa fa-sitemap',
|
|
269
|
+
'Manage Business Intelligence': 'fa fa-sitemap',
|
|
270
|
+
'Modules': 'fa fa-puzzle-piece',
|
|
271
|
+
'Netbox': 'fa fa-database',
|
|
272
|
+
'Plugin: Dataflow': 'fa fa-arrows',
|
|
273
|
+
'Profile': 'fa fa-user-cog',
|
|
274
|
+
'Syncer Rules': 'fa fa-bolt',
|
|
275
|
+
'VMware': 'fa fa-server'
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
# .-- Host
|
|
280
|
+
from application.models.host import Host
|
|
281
|
+
from application.views.host import HostModelView, ObjectModelView, TemplateModelView
|
|
282
|
+
admin.add_view(HostModelView(Host, name="Hosts", menu_icon_type='fa', menu_icon_value='fa-server'))
|
|
283
|
+
admin.add_category(name="Objects", icon_type='fa', icon_value='fa-folder-open')
|
|
284
|
+
admin.add_view(ObjectModelView(Host, name="All Objects", endpoint="Objects",category="Objects", menu_icon_type='fa', menu_icon_value='fa-cubes'))
|
|
285
|
+
admin.add_view(TemplateModelView(Host, name="Templates", endpoint="Objects Templates",category="Objects", menu_icon_type='fa', menu_icon_value='fa-files-o'))
|
|
286
|
+
#.
|
|
287
|
+
# .-- Global
|
|
288
|
+
from application.modules.custom_attributes.models import CustomAttributeRule
|
|
289
|
+
from application.modules.custom_attributes.views import CustomAttributeView
|
|
290
|
+
admin.add_view(CustomAttributeView(CustomAttributeRule, name="Global Custom Attributes", category="Modules", menu_icon_type='fa', menu_icon_value='fa-cog'))
|
|
291
|
+
#.
|
|
292
|
+
|
|
293
|
+
_register_all_plugin_admin_views()
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
from application.models.account import Account
|
|
297
|
+
from application.views.account import AccountModelView, ChildAccountModelView
|
|
298
|
+
admin.add_category(name="Accounts", icon_type='fa', icon_value='fa-users')
|
|
299
|
+
admin.add_view(AccountModelView(Account, name="Accounts", category="Accounts", menu_icon_type='fa', menu_icon_value='fa-user-circle'))
|
|
300
|
+
admin.add_view(ChildAccountModelView(Account, name="Config Childs", endpoint='account_childs', category="Accounts", menu_icon_type='fa', menu_icon_value='fa-users'))
|
|
301
|
+
|
|
302
|
+
from application.models.cron import CronGroup, CronStats
|
|
303
|
+
from application.views.cron import CronStatsView, CronGroupView
|
|
304
|
+
admin.add_view(CronGroupView(CronGroup, name="Cronjob Group", category="Cronjobs", menu_icon_type='fa', menu_icon_value='fa-calendar'))
|
|
305
|
+
admin.add_view(CronStatsView(CronStats, name="State Table", category="Cronjobs", menu_icon_type='fa', menu_icon_value='fa-table'))
|
|
306
|
+
|
|
307
|
+
from application.views.fileadmin import FileAdminView
|
|
308
|
+
if os.path.exists(app.config['FILEADMIN_PATH']):
|
|
309
|
+
file_admin_view = FileAdminView(app.config['FILEADMIN_PATH'], name="Filemanager", menu_icon_type='fa', menu_icon_value='fa-folder-open')
|
|
310
|
+
admin.add_view(file_admin_view)
|
|
311
|
+
csrf.exempt(file_admin_view.blueprint)
|
|
312
|
+
|
|
313
|
+
from application.modules.log.models import LogEntry
|
|
314
|
+
from application.modules.log.views import LogView
|
|
315
|
+
admin.add_view(LogView(LogEntry, name="Log", menu_icon_type='fa', menu_icon_value='fa-file-text-o'))
|
|
316
|
+
|
|
317
|
+
#.
|
|
318
|
+
# .-- Config
|
|
319
|
+
admin.add_category(name="Profile", icon_type='fa', icon_value='fa-user-cog')
|
|
320
|
+
from application.models.user import User
|
|
321
|
+
from application.views.user import UserView
|
|
322
|
+
admin.add_view(UserView(User, category='Profile', menu_icon_type='fa', menu_icon_value='fa-user'))
|
|
323
|
+
|
|
324
|
+
from application.models.config import Config
|
|
325
|
+
from application.views.config import ConfigModelView
|
|
326
|
+
|
|
327
|
+
admin.add_view(ConfigModelView(Config, name="System Config", category="Profile", menu_icon_type='fa', menu_icon_value='fa-cogs'))
|
|
328
|
+
|
|
329
|
+
from application.views.license import LicenseView
|
|
330
|
+
admin.add_view(LicenseView(name="License", endpoint="license", category="Profile",
|
|
331
|
+
menu_icon_type='fa', menu_icon_value='fa-id-card'))
|
|
332
|
+
#.
|
|
333
|
+
|
|
334
|
+
# .-- Rest
|
|
335
|
+
admin.add_link(MenuLink(name='Change Password', category='Profile',
|
|
336
|
+
url=f"{app.config['BASE_PREFIX']}change-password",
|
|
337
|
+
icon_type='fa', icon_value='fa-key'))
|
|
338
|
+
admin.add_link(MenuLink(name='Set 2FA Code', category='Profile',
|
|
339
|
+
url=f"{app.config['BASE_PREFIX']}set-2fa",
|
|
340
|
+
icon_type='fa', icon_value='fa-shield'))
|
|
341
|
+
admin.add_link(MenuLink(name='Logout', category='Profile',
|
|
342
|
+
url=f"{app.config['BASE_PREFIX']}logout",
|
|
343
|
+
icon_type='fa', icon_value='fa-sign-out'))
|
|
344
|
+
|
|
345
|
+
#.
|
|
346
|
+
admin.add_link(MenuLink(name='Commit Changes',
|
|
347
|
+
url="#activate_changes",
|
|
348
|
+
class_name="toggle_activate_modal btn btn-primary commit-changes-btn",
|
|
349
|
+
icon_type='fa', icon_value='fa-check-circle'))
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
from plugins import *
|
|
354
|
+
from application.plugins import *
|
application/_version.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""Single source of truth for the cmdbsyncer version.
|
|
2
|
+
|
|
3
|
+
Regenerated from the newest ``changelog/v*.md`` entry by ``make sync-version``.
|
|
4
|
+
Kept as a standalone module so ``pyproject.toml`` can resolve the version via
|
|
5
|
+
``[tool.setuptools.dynamic]`` without importing the Flask application.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
__version__ = "3.12.6"
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""
|
|
2
|
+
API
|
|
3
|
+
"""
|
|
4
|
+
from functools import wraps
|
|
5
|
+
from flask import abort, request, current_app
|
|
6
|
+
from mongoengine.errors import DoesNotExist
|
|
7
|
+
from application.models.account import Account
|
|
8
|
+
from application.models.user import User
|
|
9
|
+
from application import log
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _is_secure_api_request():
|
|
13
|
+
if current_app.config.get("ALLOW_INSECURE_API_AUTH"):
|
|
14
|
+
return True
|
|
15
|
+
if request.is_secure:
|
|
16
|
+
return True
|
|
17
|
+
if request.headers.get("X-Forwarded-Proto", "").lower() == "https":
|
|
18
|
+
return True
|
|
19
|
+
if request.host.split(":", 1)[0].lower() == "localhost":
|
|
20
|
+
return True
|
|
21
|
+
return request.remote_addr in {"127.0.0.1", "::1"}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _extract_login_credentials():
|
|
25
|
+
auth = request.authorization
|
|
26
|
+
if auth and auth.username and auth.password is not None:
|
|
27
|
+
return auth.username, auth.password
|
|
28
|
+
|
|
29
|
+
login_user = request.headers.get('x-login-user')
|
|
30
|
+
if login_user:
|
|
31
|
+
if ':' not in login_user:
|
|
32
|
+
abort(401, "Invalid login")
|
|
33
|
+
return login_user.split(':', 1)
|
|
34
|
+
return None, None
|
|
35
|
+
|
|
36
|
+
def _abort_unauthorized(reason="Unauthorized"):
|
|
37
|
+
details = [
|
|
38
|
+
('reason', reason),
|
|
39
|
+
('user', f"{request.authorization.username if request.authorization else 'unknown'}"),
|
|
40
|
+
('ip', request.remote_addr),
|
|
41
|
+
]
|
|
42
|
+
log.log("API Login failed",
|
|
43
|
+
details=details,
|
|
44
|
+
source="API")
|
|
45
|
+
abort(401, "unauthorized")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def require_token(fn):
|
|
49
|
+
"""
|
|
50
|
+
Decorator for Endpoints with token
|
|
51
|
+
"""
|
|
52
|
+
@wraps(fn)
|
|
53
|
+
def decorated_view(*args, **kwargs):
|
|
54
|
+
username, user_password = _extract_login_credentials()
|
|
55
|
+
if username:
|
|
56
|
+
if not _is_secure_api_request():
|
|
57
|
+
details = [
|
|
58
|
+
('reason', 'HTTPS required'),
|
|
59
|
+
('user', username),
|
|
60
|
+
('ip', request.remote_addr),
|
|
61
|
+
]
|
|
62
|
+
log.log("API Login failed", details=details, source="API")
|
|
63
|
+
abort(401, "HTTPS is required for password-based API authentication")
|
|
64
|
+
try:
|
|
65
|
+
user_result = User.objects.get(
|
|
66
|
+
disabled__ne=True,
|
|
67
|
+
__raw__={'$or': [{'name': username}, {'email': username}]}
|
|
68
|
+
)
|
|
69
|
+
roles = user_result.api_roles
|
|
70
|
+
current_path = request.path.replace('/api/v1/','')
|
|
71
|
+
if roles:
|
|
72
|
+
allowed = False
|
|
73
|
+
for role in roles:
|
|
74
|
+
if role == 'all':
|
|
75
|
+
allowed = True
|
|
76
|
+
break
|
|
77
|
+
if current_path.startswith(role):
|
|
78
|
+
allowed = True
|
|
79
|
+
if not allowed:
|
|
80
|
+
_abort_unauthorized(f"User '{username}' not allowed for path '{current_path}'")
|
|
81
|
+
except DoesNotExist:
|
|
82
|
+
_abort_unauthorized("Invalid credentials")
|
|
83
|
+
if not user_result.check_password(user_password):
|
|
84
|
+
_abort_unauthorized("Invalid credentials")
|
|
85
|
+
elif request.headers.get('x-login-token'):
|
|
86
|
+
_abort_unauthorized("Invalid or removed login token")
|
|
87
|
+
else:
|
|
88
|
+
_abort_unauthorized("No credentials provided")
|
|
89
|
+
|
|
90
|
+
return fn(*args, **kwargs)
|
|
91
|
+
|
|
92
|
+
return decorated_view
|