ALL:Authoritative plugin fields

This commit is contained in:
Jokob @NetAlertX
2026-01-19 11:28:37 +00:00
parent 1e289e94e3
commit 3b203536b8
61 changed files with 5018 additions and 154 deletions

View File

@@ -72,6 +72,7 @@ from .openapi.schemas import ( # noqa: E402 [flake8 lint suppression]
BaseResponse, DeviceTotalsResponse,
DeleteDevicesRequest, DeviceImportRequest,
DeviceImportResponse, UpdateDeviceColumnRequest,
LockDeviceFieldRequest,
CopyDeviceRequest, TriggerScanRequest,
OpenPortsRequest,
OpenPortsResponse, WakeOnLanRequest,
@@ -444,6 +445,62 @@ def api_device_update_column(mac, payload=None):
return jsonify(result)
@app.route("/device/<mac>/field/lock", methods=["POST"])
@validate_request(
operation_id="lock_device_field",
summary="Lock/Unlock Device Field",
description="Lock a field to prevent plugin overwrites or unlock it to allow overwrites.",
path_params=[{
"name": "mac",
"description": "Device MAC address",
"schema": {"type": "string"}
}],
request_model=LockDeviceFieldRequest,
response_model=BaseResponse,
tags=["devices"],
auth_callable=is_authorized
)
def api_device_field_lock(mac, payload=None):
"""Lock or unlock a device field by setting its source to LOCKED or USER."""
data = request.get_json() or {}
field_name = data.get("fieldName")
should_lock = data.get("lock", False)
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()
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
})
else:
return jsonify(result), 400
except Exception as e:
mylog("error", f"Error locking field {field_name} for {mac}: {str(e)}")
return jsonify({"success": False, "error": str(e)}), 500
@app.route('/mcp/sse/device/<mac>/set-alias', methods=['POST'])
@app.route('/device/<mac>/set-alias', methods=['POST'])
@validate_request(

View File

@@ -58,6 +58,10 @@ class Device(ObjectType):
devFirstConnection = String(description="Timestamp of first discovery")
devLastConnection = String(description="Timestamp of last connection")
devLastIP = String(description="Last known IP address")
devPrimaryIPv4 = String(description="Primary IPv4 address")
devPrimaryIPv6 = String(description="Primary IPv6 address")
devVlan = String(description="VLAN identifier")
devForceStatus = String(description="Force device status (online/offline/dont_force)")
devStaticIP = Int(description="Static IP flag (0 or 1)")
devScan = Int(description="Scan flag (0 or 1)")
devLogEvents = Int(description="Log events flag (0 or 1)")
@@ -86,6 +90,16 @@ class Device(ObjectType):
devFQDN = String(description="Fully Qualified Domain Name")
devParentRelType = String(description="Relationship type to parent")
devReqNicsOnline = Int(description="Required NICs online flag")
devMacSource = String(description="Source tracking for devMac (USER, LOCKED, NEWDEV, or plugin prefix)")
devNameSource = String(description="Source tracking for devName")
devFqdnSource = String(description="Source tracking for devFQDN")
devLastIpSource = String(description="Source tracking for devLastIP")
devVendorSource = String(description="Source tracking for devVendor")
devSsidSource = String(description="Source tracking for devSSID")
devParentMacSource = String(description="Source tracking for devParentMAC")
devParentPortSource = String(description="Source tracking for devParentPort")
devParentRelTypeSource = String(description="Source tracking for devParentRelType")
devVlanSource = String(description="Source tracking for devVlan")
class DeviceResult(ObjectType):

View File

@@ -135,12 +135,26 @@ class DeviceInfo(BaseModel):
devMac: str = Field(..., description="Device MAC address")
devName: Optional[str] = Field(None, description="Device display name/alias")
devLastIP: Optional[str] = Field(None, description="Last known IP address")
devPrimaryIPv4: Optional[str] = Field(None, description="Primary IPv4 address")
devPrimaryIPv6: Optional[str] = Field(None, description="Primary IPv6 address")
devVlan: Optional[str] = Field(None, description="VLAN identifier")
devForceStatus: Optional[str] = Field(None, description="Force device status (online/offline/dont_force)")
devVendor: Optional[str] = Field(None, description="Hardware vendor from OUI lookup")
devOwner: Optional[str] = Field(None, description="Device owner")
devType: Optional[str] = Field(None, description="Device type classification")
devFavorite: Optional[int] = Field(0, description="Favorite flag (0 or 1)")
devPresentLastScan: Optional[int] = Field(None, description="Present in last scan (0 or 1)")
devStatus: Optional[str] = Field(None, description="Online/Offline status")
devMacSource: Optional[str] = Field(None, description="Source of devMac (USER, LOCKED, or plugin prefix)")
devNameSource: Optional[str] = Field(None, description="Source of devName")
devFqdnSource: Optional[str] = Field(None, description="Source of devFQDN")
devLastIpSource: Optional[str] = Field(None, description="Source of devLastIP")
devVendorSource: Optional[str] = Field(None, description="Source of devVendor")
devSsidSource: Optional[str] = Field(None, description="Source of devSSID")
devParentMacSource: Optional[str] = Field(None, description="Source of devParentMAC")
devParentPortSource: Optional[str] = Field(None, description="Source of devParentPort")
devParentRelTypeSource: Optional[str] = Field(None, description="Source of devParentRelType")
devVlanSource: Optional[str] = Field(None, description="Source of devVlan")
class DeviceSearchResponse(BaseResponse):
@@ -259,6 +273,12 @@ class UpdateDeviceColumnRequest(BaseModel):
columnValue: Any = Field(..., description="New value for the column")
class LockDeviceFieldRequest(BaseModel):
"""Request to lock/unlock a device field."""
fieldName: str = Field(..., description="Field name to lock/unlock (devMac, devName, devLastIP, etc.)")
lock: bool = Field(True, description="True to lock the field, False to unlock")
class DeviceUpdateRequest(BaseModel):
"""Request to update device fields (create/update)."""
model_config = ConfigDict(extra="allow")