admin / Synapse-Cortex
publicSelf Hosted ITSM Tool with RBAC/Tenanting and MFA
Synapse-Cortex / Synapse-Cortexv2 / app / utils.py
2635 B · main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | import hashlib import re import uuid from typing import Iterable, Optional MAC_ADDRESS_PATTERN = re.compile(r"^([0-9A-Fa-f]{2}[:-]){5}[0-9A-Fa-f]{2}$") # Best-effort keyword hints against the original default category names, used # only as a fallback when a tenant's option names don't directly match # NetscanXi's free-text device_type guess. Asset types are admin-configurable # (see app.models.AssetTypeOption), not a fixed enum, so the primary match is # always against whatever names actually exist for the tenant right now. _DEVICE_TYPE_KEYWORD_HINTS = ( ("Server", ("server",)), ("Network Device", ("router", "switch", "firewall", "access point", "network")), ("Printer", ("printer",)), ("Laptop", ("laptop", "notebook")), ("Mobile", ("mobile", "phone", "tablet")), ("Workstation", ("workstation", "desktop")), ) def match_asset_type_name(raw: Optional[str], available_names: Iterable[str]) -> str: """Best-effort match of NetscanXi's free-text device_type guess against a tenant's current AssetTypeOption names. The raw string is always preserved verbatim in Asset.netscanxi_device_type regardless of how (or whether) this resolves, so nothing is lost even when the guess is wrong. Falls back to an option named "Other" if present, else the first available option name, so this only ever returns a name the caller can use to look up an existing option - never an invented one. """ names = list(available_names) other = next((n for n in names if n.strip().lower() == "other"), None) fallback = other or (names[0] if names else "Other") if not raw: return fallback lowered = raw.lower() for name in names: if name.lower() in lowered or lowered in name.lower(): return name for hint_name, keywords in _DEVICE_TYPE_KEYWORD_HINTS: if hint_name in names and any(keyword in lowered for keyword in keywords): return hint_name return fallback def normalize_mac(mac_address: str) -> str: return mac_address.strip().lower().replace("-", ":") def is_valid_mac(mac_address: str) -> bool: return bool(MAC_ADDRESS_PATTERN.match(mac_address.strip())) def generate_asset_id(tenant_id: uuid.UUID, mac_address: str) -> str: """Derive a deterministic asset_id from the tenant and the device MAC address. Same tenant + MAC always yields the same asset_id, logically binding the CMDB record to the physical NIC it was registered against. """ digest = hashlib.sha256(f"{tenant_id}:{normalize_mac(mac_address)}".encode()).hexdigest() return f"AST-{digest[:10].upper()}" |