Initial commit: add compliance_checks table, per-check metadata on assets, and compliance audit trail

This commit is contained in:
2026-04-24 07:14:27 +03:00
commit e63b486ec2
58 changed files with 6468 additions and 0 deletions

View File

@@ -0,0 +1,84 @@
from ldap3 import Server, Connection, ALL, SUBTREE
from flask import current_app
class LDAPService:
"""Wraps ldap3 to sync users from Active Directory."""
ATTRIBUTES = [
'employeeID', 'sAMAccountName', 'givenName', 'sn',
'mail', 'department', 'title', 'telephoneNumber',
'distinguishedName', 'physicalDeliveryOfficeName',
'userAccountControl',
]
def _connect(self):
cfg = current_app.config
server = Server(
cfg['LDAP_SERVER'],
port=cfg['LDAP_PORT'],
use_ssl=cfg['LDAP_USE_SSL'],
get_info=ALL,
)
conn = Connection(
server,
user=cfg['LDAP_BIND_USER'],
password=cfg['LDAP_BIND_PASSWORD'],
auto_bind=True,
)
return conn
def sync_users(self):
"""
Query AD and return a list of dicts ready to be upserted into the
User model. Raises an exception if the connection fails.
"""
cfg = current_app.config
conn = self._connect()
conn.search(
search_base=cfg['LDAP_BASE_DN'],
search_filter=cfg['LDAP_USER_SEARCH_FILTER'],
search_scope=SUBTREE,
attributes=self.ATTRIBUTES,
)
wid_attr = cfg['LDAP_WINDOWS_ID_ATTR']
users = []
for entry in conn.entries:
# Resolve windows_id from the configured attribute, fall back to sAMAccountName
raw_wid = str(getattr(entry, wid_attr, '') or '')
if not raw_wid:
raw_wid = str(entry.sAMAccountName or '')
if not raw_wid:
continue # skip entries with no identifier
# userAccountControl bit 2 = disabled account
uac = 0
try:
uac = int(str(entry.userAccountControl or 0))
except (ValueError, TypeError):
pass
is_active = not bool(uac & 2)
users.append({
'windows_id': raw_wid.strip(),
'first_name': str(entry.givenName or '').strip(),
'last_name': str(entry.sn or '').strip(),
'email': str(entry.mail or '').strip(),
'department': str(entry.department or '').strip(),
'job_title': str(entry.title or '').strip(),
'phone': str(entry.telephoneNumber or '').strip(),
'location': str(entry.physicalDeliveryOfficeName or '').strip(),
'ldap_dn': str(entry.distinguishedName or '').strip(),
'is_active': is_active,
})
conn.unbind()
return users
def test_connection(self):
"""Returns True if a bind succeeds, raises on failure."""
conn = self._connect()
conn.unbind()
return True