MCP refactor

Signed-off-by: GitHub <noreply@github.com>
This commit is contained in:
Jokob @NetAlertX
2025-12-07 08:20:51 +00:00
committed by GitHub
parent 36e5751221
commit 8c982cd476
20 changed files with 900 additions and 1485 deletions

View File

@@ -1,121 +1,134 @@
from front.plugins.plugin_helper import is_mac
from logger import mylog
from models.plugin_object_instance import PluginObjectInstance
from database import get_temp_db_connection
# -------------------------------------------------------------------------------
# Device object handling (WIP)
# -------------------------------------------------------------------------------
class DeviceInstance:
def __init__(self, db):
self.db = db
# Get all
def getAll(self):
self.db.sql.execute("""
SELECT * FROM Devices
""")
return self.db.sql.fetchall()
# Get all with unknown names
def getUnknown(self):
self.db.sql.execute("""
SELECT * FROM Devices WHERE devName in ("(unknown)", "(name not found)", "" )
""")
return self.db.sql.fetchall()
# Get specific column value based on devMac
def getValueWithMac(self, column_name, devMac):
query = f"SELECT {column_name} FROM Devices WHERE devMac = ?"
self.db.sql.execute(query, (devMac,))
result = self.db.sql.fetchone()
return result[column_name] if result else None
# Get all down
def getDown(self):
self.db.sql.execute("""
SELECT * FROM Devices WHERE devAlertDown = 1 and devPresentLastScan = 0
""")
return self.db.sql.fetchall()
# Get all down
def getOffline(self):
self.db.sql.execute("""
SELECT * FROM Devices WHERE devPresentLastScan = 0
""")
return self.db.sql.fetchall()
# Get a device by devGUID
def getByGUID(self, devGUID):
self.db.sql.execute("SELECT * FROM Devices WHERE devGUID = ?", (devGUID,))
result = self.db.sql.fetchone()
return dict(result) if result else None
# Check if a device exists by devGUID
def exists(self, devGUID):
self.db.sql.execute(
"SELECT COUNT(*) AS count FROM Devices WHERE devGUID = ?", (devGUID,)
)
result = self.db.sql.fetchone()
return result["count"] > 0
# Get a device by its last IP address
def getByIP(self, ip):
self.db.sql.execute("SELECT * FROM Devices WHERE devLastIP = ?", (ip,))
row = self.db.sql.fetchone()
return dict(row) if row else None
# Search devices by partial mac, name or IP
def search(self, query):
like = f"%{query}%"
self.db.sql.execute(
"SELECT * FROM Devices WHERE devMac LIKE ? OR devName LIKE ? OR devLastIP LIKE ?",
(like, like, like),
)
rows = self.db.sql.fetchall()
# --- helpers --------------------------------------------------------------
def _fetchall(self, query, params=()):
conn = get_temp_db_connection()
rows = conn.execute(query, params).fetchall()
conn.close()
return [dict(r) for r in rows]
# Get the most recently discovered device
def getLatest(self):
self.db.sql.execute("SELECT * FROM Devices ORDER BY devFirstConnection DESC LIMIT 1")
row = self.db.sql.fetchone()
def _fetchone(self, query, params=()):
conn = get_temp_db_connection()
row = conn.execute(query, params).fetchone()
conn.close()
return dict(row) if row else None
def getNetworkTopology(self):
"""Returns nodes and links for the current Devices table.
def _execute(self, query, params=()):
conn = get_temp_db_connection()
cur = conn.cursor()
cur.execute(query, params)
conn.commit()
conn.close()
Nodes: {id, name, vendor}
Links: {source, target, port}
"""
self.db.sql.execute("SELECT devName, devMac, devParentMAC, devParentPort, devVendor FROM Devices")
rows = self.db.sql.fetchall()
nodes = []
links = []
for row in rows:
nodes.append({"id": row['devMac'], "name": row['devName'], "vendor": row['devVendor']})
if row['devParentMAC']:
links.append({"source": row['devParentMAC'], "target": row['devMac'], "port": row['devParentPort']})
# --- public API -----------------------------------------------------------
def getAll(self):
return self._fetchall("SELECT * FROM Devices")
def getUnknown(self):
return self._fetchall("""
SELECT * FROM Devices
WHERE devName IN ("(unknown)", "(name not found)", "")
""")
def getValueWithMac(self, column_name, devMac):
row = self._fetchone(f"""
SELECT {column_name} FROM Devices WHERE devMac = ?
""", (devMac,))
return row.get(column_name) if row else None
def getDown(self):
return self._fetchall("""
SELECT * FROM Devices
WHERE devAlertDown = 1 AND devPresentLastScan = 0
""")
def getOffline(self):
return self._fetchall("""
SELECT * FROM Devices
WHERE devPresentLastScan = 0
""")
def getByGUID(self, devGUID):
return self._fetchone("""
SELECT * FROM Devices WHERE devGUID = ?
""", (devGUID,))
def exists(self, devGUID):
row = self._fetchone("""
SELECT COUNT(*) as count FROM Devices WHERE devGUID = ?
""", (devGUID,))
return row['count'] > 0 if row else False
def getByIP(self, ip):
return self._fetchone("""
SELECT * FROM Devices WHERE devLastIP = ?
""", (ip,))
def search(self, query):
like = f"%{query}%"
return self._fetchall("""
SELECT * FROM Devices
WHERE devMac LIKE ? OR devName LIKE ? OR devLastIP LIKE ?
""", (like, like, like))
def getLatest(self):
return self._fetchone("""
SELECT * FROM Devices
ORDER BY devFirstConnection DESC LIMIT 1
""")
def getNetworkTopology(self):
rows = self._fetchall("""
SELECT devName, devMac, devParentMAC, devParentPort, devVendor FROM Devices
""")
nodes = [{"id": r["devMac"], "name": r["devName"], "vendor": r["devVendor"]} for r in rows]
links = [{"source": r["devParentMAC"], "target": r["devMac"], "port": r["devParentPort"]}
for r in rows if r["devParentMAC"]]
return {"nodes": nodes, "links": links}
# Update a specific field for a device
def updateField(self, devGUID, field, value):
if not self.exists(devGUID):
m = f"[Device] In 'updateField': GUID {devGUID} not found."
mylog("none", m)
raise ValueError(m)
msg = f"[Device] updateField: GUID {devGUID} not found"
mylog("none", msg)
raise ValueError(msg)
self._execute(f"UPDATE Devices SET {field}=? WHERE devGUID=?", (value, devGUID))
self.db.sql.execute(
f"""
UPDATE Devices SET {field} = ? WHERE devGUID = ?
""",
(value, devGUID),
)
self.db.commitDB()
# Delete a device by devGUID
def delete(self, devGUID):
if not self.exists(devGUID):
m = f"[Device] In 'delete': GUID {devGUID} not found."
mylog("none", m)
raise ValueError(m)
msg = f"[Device] delete: GUID {devGUID} not found"
mylog("none", msg)
raise ValueError(msg)
self._execute("DELETE FROM Devices WHERE devGUID=?", (devGUID,))
self.db.sql.execute("DELETE FROM Devices WHERE devGUID = ?", (devGUID,))
self.db.commitDB()
def resolvePrimaryID(self, target):
if is_mac(target):
return target.lower()
dev = self.getByIP(target)
return dev['devMac'].lower() if dev else None
def getOpenPorts(self, target):
primary = self.resolvePrimaryID(target)
if not primary:
return []
objs = PluginObjectInstance().getByField(
plugPrefix='NMAP',
matchedColumn='Object_PrimaryID',
matchedKey=primary,
returnFields=['Object_SecondaryID', 'Watched_Value2']
)
ports = []
for o in objs:
port = int(o.get('Object_SecondaryID') or 0)
ports.append({"port": port, "service": o.get('Watched_Value2', '')})
return ports

View File

@@ -0,0 +1,106 @@
from datetime import datetime, timedelta
from logger import mylog
from database import get_temp_db_connection
# -------------------------------------------------------------------------------
# Event handling (Matches table: Events)
# -------------------------------------------------------------------------------
class EventInstance:
def _conn(self):
"""Always return a new DB connection (thread-safe)."""
return get_temp_db_connection()
def _rows_to_list(self, rows):
return [dict(r) for r in rows]
# Get all events
def get_all(self):
conn = self._conn()
rows = conn.execute(
"SELECT * FROM Events ORDER BY eve_DateTime DESC"
).fetchall()
conn.close()
return self._rows_to_list(rows)
# --- Get last n events ---
def get_last_n(self, n=10):
conn = self._conn()
rows = conn.execute("""
SELECT * FROM Events
ORDER BY eve_DateTime DESC
LIMIT ?
""", (n,)).fetchall()
return self._rows_to_list(rows)
# --- Specific helper for last 10 ---
def get_last(self):
return self.get_last_n(10)
# Get events in the last 24h
def get_recent(self):
since = datetime.now() - timedelta(hours=24)
conn = self._conn()
rows = conn.execute("""
SELECT * FROM Events
WHERE eve_DateTime >= ?
ORDER BY eve_DateTime DESC
""", (since,)).fetchall()
conn.close()
return self._rows_to_list(rows)
# Get events from last N hours
def get_by_hours(self, hours: int):
if hours <= 0:
mylog("warn", f"[Events] get_by_hours({hours}) -> invalid value")
return []
since = datetime.now() - timedelta(hours=hours)
conn = self._conn()
rows = conn.execute("""
SELECT * FROM Events
WHERE eve_DateTime >= ?
ORDER BY eve_DateTime DESC
""", (since,)).fetchall()
conn.close()
return self._rows_to_list(rows)
# Get events in a date range
def get_by_range(self, start: datetime, end: datetime):
if end < start:
mylog("error", f"[Events] get_by_range invalid: {start} > {end}")
raise ValueError("Start must not be after end")
conn = self._conn()
rows = conn.execute("""
SELECT * FROM Events
WHERE eve_DateTime BETWEEN ? AND ?
ORDER BY eve_DateTime DESC
""", (start, end)).fetchall()
conn.close()
return self._rows_to_list(rows)
# Insert new event
def add(self, mac, ip, eventType, info="", pendingAlert=True, pairRow=None):
conn = self._conn()
conn.execute("""
INSERT INTO Events (
eve_MAC, eve_IP, eve_DateTime,
eve_EventType, eve_AdditionalInfo,
eve_PendingAlertEmail, eve_PairEventRowid
) VALUES (?,?,?,?,?,?,?)
""", (mac, ip, datetime.now(), eventType, info,
1 if pendingAlert else 0, pairRow))
conn.commit()
conn.close()
# Delete old events
def delete_older_than(self, days: int):
cutoff = datetime.now() - timedelta(days=days)
conn = self._conn()
result = conn.execute("DELETE FROM Events WHERE eve_DateTime < ?", (cutoff,))
conn.commit()
deleted_count = result.rowcount
conn.close()
return deleted_count

View File

@@ -1,79 +1,91 @@
from logger import mylog
from database import get_temp_db_connection
# -------------------------------------------------------------------------------
# Plugin object handling (WIP)
# Plugin object handling (THREAD-SAFE REWRITE)
# -------------------------------------------------------------------------------
class PluginObjectInstance:
def __init__(self, db):
self.db = db
# Get all plugin objects
def getAll(self):
self.db.sql.execute("""
SELECT * FROM Plugins_Objects
""")
return self.db.sql.fetchall()
# Get plugin object by ObjectGUID
def getByGUID(self, ObjectGUID):
self.db.sql.execute(
"SELECT * FROM Plugins_Objects WHERE ObjectGUID = ?", (ObjectGUID,)
)
result = self.db.sql.fetchone()
return dict(result) if result else None
# Check if a plugin object exists by ObjectGUID
def exists(self, ObjectGUID):
self.db.sql.execute(
"SELECT COUNT(*) AS count FROM Plugins_Objects WHERE ObjectGUID = ?",
(ObjectGUID,),
)
result = self.db.sql.fetchone()
return result["count"] > 0
# Get objects by plugin name
def getByPlugin(self, plugin):
self.db.sql.execute("SELECT * FROM Plugins_Objects WHERE Plugin = ?", (plugin,))
return self.db.sql.fetchall()
# Get plugin objects by primary ID and plugin name
def getByPrimary(self, plugin, primary_id):
self.db.sql.execute(
"SELECT * FROM Plugins_Objects WHERE Plugin = ? AND Object_PrimaryID = ?",
(plugin, primary_id),
)
rows = self.db.sql.fetchall()
# -------------- Internal DB helper wrappers --------------------------------
def _fetchall(self, query, params=()):
conn = get_temp_db_connection()
rows = conn.execute(query, params).fetchall()
conn.close()
return [dict(r) for r in rows]
# Get objects by status
def getByStatus(self, status):
self.db.sql.execute("SELECT * FROM Plugins_Objects WHERE Status = ?", (status,))
return self.db.sql.fetchall()
def _fetchone(self, query, params=()):
conn = get_temp_db_connection()
row = conn.execute(query, params).fetchone()
conn.close()
return dict(row) if row else None
def _execute(self, query, params=()):
conn = get_temp_db_connection()
conn.execute(query, params)
conn.commit()
conn.close()
# ---------------------------------------------------------------------------
# Public API — identical behaviour, now thread-safe + self-contained
# ---------------------------------------------------------------------------
def getAll(self):
return self._fetchall("SELECT * FROM Plugins_Objects")
def getByGUID(self, ObjectGUID):
return self._fetchone(
"SELECT * FROM Plugins_Objects WHERE ObjectGUID = ?", (ObjectGUID,)
)
def exists(self, ObjectGUID):
row = self._fetchone("""
SELECT COUNT(*) AS count FROM Plugins_Objects WHERE ObjectGUID = ?
""", (ObjectGUID,))
return row["count"] > 0 if row else False
def getByPlugin(self, plugin):
return self._fetchall(
"SELECT * FROM Plugins_Objects WHERE Plugin = ?", (plugin,)
)
def getByField(self, plugPrefix, matchedColumn, matchedKey, returnFields=None):
rows = self._fetchall(
f"SELECT * FROM Plugins_Objects WHERE Plugin = ? AND {matchedColumn} = ?",
(plugPrefix, matchedKey.lower())
)
if not returnFields:
return rows
return [{f: row.get(f) for f in returnFields} for row in rows]
def getByPrimary(self, plugin, primary_id):
return self._fetchall("""
SELECT * FROM Plugins_Objects
WHERE Plugin = ? AND Object_PrimaryID = ?
""", (plugin, primary_id))
def getByStatus(self, status):
return self._fetchall("""
SELECT * FROM Plugins_Objects WHERE Status = ?
""", (status,))
# Update a specific field for a plugin object
def updateField(self, ObjectGUID, field, value):
if not self.exists(ObjectGUID):
m = f"[PluginObject] In 'updateField': GUID {ObjectGUID} not found."
mylog("none", m)
raise ValueError(m)
msg = f"[PluginObject] updateField: GUID {ObjectGUID} not found."
mylog("none", msg)
raise ValueError(msg)
self.db.sql.execute(
f"""
UPDATE Plugins_Objects SET {field} = ? WHERE ObjectGUID = ?
""",
(value, ObjectGUID),
self._execute(
f"UPDATE Plugins_Objects SET {field}=? WHERE ObjectGUID=?",
(value, ObjectGUID)
)
self.db.commitDB()
# Delete a plugin object by ObjectGUID
def delete(self, ObjectGUID):
if not self.exists(ObjectGUID):
m = f"[PluginObject] In 'delete': GUID {ObjectGUID} not found."
mylog("none", m)
raise ValueError(m)
msg = f"[PluginObject] delete: GUID {ObjectGUID} not found."
mylog("none", msg)
raise ValueError(msg)
self.db.sql.execute(
"DELETE FROM Plugins_Objects WHERE ObjectGUID = ?", (ObjectGUID,)
)
self.db.commitDB()
self._execute("DELETE FROM Plugins_Objects WHERE ObjectGUID=?", (ObjectGUID,))