mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-03-31 07:12:23 -07:00
feat: Enhance plugin configurations and improve MAC normalization
This commit is contained in:
@@ -44,7 +44,7 @@ from models.user_events_queue_instance import UserEventsQueueInstance # noqa: E
|
||||
|
||||
from models.event_instance import EventInstance # noqa: E402 [flake8 lint suppression]
|
||||
# Import tool logic from the MCP/tools module to reuse behavior (no blueprints)
|
||||
from plugin_helper import is_mac # noqa: E402 [flake8 lint suppression]
|
||||
from plugin_helper import is_mac, normalize_mac # noqa: E402 [flake8 lint suppression]
|
||||
# is_mac is provided in mcp_endpoint and used by those handlers
|
||||
# mcp_endpoint contains helper functions; routes moved into this module to keep a single place for routes
|
||||
from messaging.in_app import ( # noqa: E402 [flake8 lint suppression]
|
||||
@@ -469,33 +469,28 @@ def api_device_field_lock(mac, payload=None):
|
||||
if not field_name:
|
||||
return jsonify({"success": False, "error": "fieldName is required"}), 400
|
||||
|
||||
# Validate that the field can be locked
|
||||
source_field = field_name + "Source"
|
||||
allowed_tracked_fields = {
|
||||
"devMac", "devName", "devLastIP", "devVendor", "devFQDN",
|
||||
"devSSID", "devParentMAC", "devParentPort", "devParentRelType", "devVlan"
|
||||
}
|
||||
if field_name not in allowed_tracked_fields:
|
||||
return jsonify({"success": False, "error": f"Field '{field_name}' cannot be locked"}), 400
|
||||
|
||||
device_handler = DeviceInstance()
|
||||
normalized_mac = normalize_mac(mac)
|
||||
|
||||
try:
|
||||
# When locking: set source to LOCKED
|
||||
# When unlocking: check current value and let plugins take over
|
||||
new_source = "LOCKED" if should_lock else "NEWDEV"
|
||||
result = device_handler.updateDeviceColumn(mac, source_field, new_source)
|
||||
|
||||
if result.get("success"):
|
||||
action = "locked" if should_lock else "unlocked"
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"message": f"Field {field_name} {action}",
|
||||
"fieldName": field_name,
|
||||
"locked": should_lock
|
||||
})
|
||||
if should_lock:
|
||||
result = device_handler.lockDeviceField(normalized_mac, field_name)
|
||||
action = "locked"
|
||||
else:
|
||||
return jsonify(result), 400
|
||||
result = device_handler.unlockDeviceField(normalized_mac, field_name)
|
||||
action = "unlocked"
|
||||
|
||||
response = dict(result)
|
||||
response["fieldName"] = field_name
|
||||
response["locked"] = should_lock
|
||||
|
||||
if response.get("success"):
|
||||
response.setdefault("message", f"Field {field_name} {action}")
|
||||
return jsonify(response)
|
||||
|
||||
if "does not support" in response.get("error", ""):
|
||||
response["error"] = f"Field '{field_name}' cannot be {action}"
|
||||
return jsonify(response), 400
|
||||
except Exception as e:
|
||||
mylog("none", f"Error locking field {field_name} for {mac}: {str(e)}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
|
||||
@@ -152,12 +152,20 @@ def enforce_source_on_user_update(devMac, updates_dict, conn):
|
||||
cur = conn.cursor()
|
||||
|
||||
# Check if field has a corresponding source and should be updated
|
||||
cur = conn.cursor()
|
||||
try:
|
||||
cur.execute("PRAGMA table_info(Devices)")
|
||||
device_columns = {row["name"] for row in cur.fetchall()}
|
||||
except Exception:
|
||||
device_columns = set()
|
||||
|
||||
updates_to_apply = {}
|
||||
for field_name, new_value in updates_dict.items():
|
||||
if field_name in FIELD_SOURCE_MAP:
|
||||
source_field = FIELD_SOURCE_MAP[field_name]
|
||||
# User is updating this field, so mark it as USER
|
||||
updates_to_apply[source_field] = "USER"
|
||||
if not device_columns or source_field in device_columns:
|
||||
updates_to_apply[source_field] = "USER"
|
||||
|
||||
if not updates_to_apply:
|
||||
return
|
||||
@@ -198,6 +206,15 @@ def lock_field(devMac, field_name, conn):
|
||||
|
||||
source_field = FIELD_SOURCE_MAP[field_name]
|
||||
cur = conn.cursor()
|
||||
try:
|
||||
cur.execute("PRAGMA table_info(Devices)")
|
||||
device_columns = {row["name"] for row in cur.fetchall()}
|
||||
except Exception:
|
||||
device_columns = set()
|
||||
|
||||
if device_columns and source_field not in device_columns:
|
||||
mylog("debug", [f"[lock_field] Source column {source_field} missing for {field_name}"])
|
||||
return
|
||||
|
||||
sql = f"UPDATE Devices SET {source_field}='LOCKED' WHERE devMac = ?"
|
||||
|
||||
@@ -227,6 +244,15 @@ def unlock_field(devMac, field_name, conn):
|
||||
|
||||
source_field = FIELD_SOURCE_MAP[field_name]
|
||||
cur = conn.cursor()
|
||||
try:
|
||||
cur.execute("PRAGMA table_info(Devices)")
|
||||
device_columns = {row["name"] for row in cur.fetchall()}
|
||||
except Exception:
|
||||
device_columns = set()
|
||||
|
||||
if device_columns and source_field not in device_columns:
|
||||
mylog("debug", [f"[unlock_field] Source column {source_field} missing for {field_name}"])
|
||||
return
|
||||
|
||||
# Unlock by resetting to empty (allows overwrite)
|
||||
sql = f"UPDATE Devices SET {source_field}='' WHERE devMac = ?"
|
||||
|
||||
@@ -593,7 +593,6 @@ class DeviceInstance:
|
||||
conn = get_temp_db_connection()
|
||||
cur = conn.cursor()
|
||||
cur.execute(sql, values)
|
||||
conn.commit()
|
||||
|
||||
if data.get("createNew", False):
|
||||
# Initialize source-tracking fields on device creation.
|
||||
@@ -617,7 +616,6 @@ class DeviceInstance:
|
||||
source_values.append(normalized_mac)
|
||||
source_sql = f"UPDATE Devices SET {set_clause} WHERE devMac = ?"
|
||||
cur.execute(source_sql, source_values)
|
||||
conn.commit()
|
||||
|
||||
# Enforce source tracking on user updates
|
||||
# User-updated fields should have their *Source set to "USER"
|
||||
@@ -631,6 +629,8 @@ class DeviceInstance:
|
||||
conn.close()
|
||||
return {"success": False, "error": f"Source tracking failed: {e}"}
|
||||
|
||||
# Commit all changes atomically after all operations succeed
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
mylog("debug", f"[DeviceInstance] setDeviceData SQL: {sql.strip()}")
|
||||
|
||||
@@ -545,12 +545,13 @@ def create_new_devices(db):
|
||||
# Derive primary IP family values
|
||||
cur_IP = str(cur_IP).strip() if cur_IP else ""
|
||||
cur_IP_normalized = check_IP_format(cur_IP) if ":" not in cur_IP else cur_IP
|
||||
|
||||
# Validate IPv6 addresses using format_ip_long for consistency
|
||||
if ":" in cur_IP_normalized:
|
||||
|
||||
# Validate IPv6 addresses using format_ip_long for consistency (do not store integer result)
|
||||
if cur_IP_normalized and ":" in cur_IP_normalized:
|
||||
validated_ipv6 = format_ip_long(cur_IP_normalized)
|
||||
cur_IP_normalized = validated_ipv6 if validated_ipv6 else ""
|
||||
|
||||
if validated_ipv6 is None or validated_ipv6 < 0:
|
||||
cur_IP_normalized = ""
|
||||
|
||||
primary_ipv4 = cur_IP_normalized if cur_IP_normalized and ":" not in cur_IP_normalized else ""
|
||||
primary_ipv6 = cur_IP_normalized if cur_IP_normalized and ":" in cur_IP_normalized else ""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user