diff --git a/docs/API_DEVICE_FIELD_LOCK.md b/docs/API_DEVICE_FIELD_LOCK.md index 61180cf7..9c5eed65 100644 --- a/docs/API_DEVICE_FIELD_LOCK.md +++ b/docs/API_DEVICE_FIELD_LOCK.md @@ -151,7 +151,6 @@ The authoritative field update logic prevents plugin overwrites: ## See Also - [Device locking](./DEVICE_FIELD_LOCK.md) -- [Device source fields](./DEVICE_SOURCE_FIELDS.md) +- [Device source fields - Authoritative updates](./DEVICE_SOURCE_FIELDS.md) - [API Device Endpoints Documentation](./API_DEVICE.md) -- [Authoritative Field Updates System](./PLUGINS_DEV.md#authoritative-fields) - [Plugin Configuration Reference](./PLUGINS_DEV_CONFIG.md) diff --git a/docs/DEVICE_FIELD_LOCK.md b/docs/DEVICE_FIELD_LOCK.md index b68709b7..6a507e48 100644 --- a/docs/DEVICE_FIELD_LOCK.md +++ b/docs/DEVICE_FIELD_LOCK.md @@ -157,9 +157,8 @@ Overwrite rules are ## See also - [Device locking](./DEVICE_FIELD_LOCK.md) -- [Device source fields](./DEVICE_SOURCE_FIELDS.md) +- [Device source fields - Authoritative updates](./DEVICE_SOURCE_FIELDS.md) - [API Device Endpoints Documentation](./API_DEVICE.md) -- [Authoritative Field Updates System](./PLUGINS_DEV.md#authoritative-fields) - [Plugin Configuration Reference](./PLUGINS_DEV_CONFIG.md) - [Device locking APIs](API_DEVICE_FIELD_LOCK.md) - [Device management](DEVICE_MANAGEMENT.md) diff --git a/docs/DEVICE_SOURCE_FIELDS.md b/docs/DEVICE_SOURCE_FIELDS.md index 7f2bdc52..b61493ae 100644 --- a/docs/DEVICE_SOURCE_FIELDS.md +++ b/docs/DEVICE_SOURCE_FIELDS.md @@ -65,9 +65,8 @@ Some fields, like **IP Addresses** (`devLastIP`) and **Full Domain Names** (`dev ## See also: - [Device locking](./DEVICE_FIELD_LOCK.md) -- [Device source fields](./DEVICE_SOURCE_FIELDS.md) +- [Device source fields - Authoritative updates](./DEVICE_SOURCE_FIELDS.md) - [API Device Endpoints Documentation](./API_DEVICE.md) -- [Authoritative Field Updates System](./PLUGINS_DEV.md#authoritative-fields) - [Plugin Configuration Reference](./PLUGINS_DEV_CONFIG.md) - [Device locking APIs](API_DEVICE_FIELD_LOCK.md) - [Device management](DEVICE_MANAGEMENT.md) diff --git a/docs/PLUGINS.md b/docs/PLUGINS.md index 07817819..adc61a9e 100755 --- a/docs/PLUGINS.md +++ b/docs/PLUGINS.md @@ -22,20 +22,20 @@ NetAlertX supports additional plugins to extend its functionality, each with its ## Plugin types -| Plugin type | Icon | Description | When to run | Required | Data source [?](./PLUGINS_DEV.md) | -| -------------- | ---- | ---------------------------------------------------------------- | ----------------------------------- | -------- | ------------------------------------- | -| publisher | ▶️ | Sending notifications to services. | `on_notification` | ✖ | Script | -| dev scanner | 🔍 | Create devices in the app, manages online/offline device status. | `schedule` | ✖ | Script / SQLite DB | -| name discovery | 🆎 | Discovers names of devices via various protocols. | `before_name_updates`, `schedule` | ✖ | Script | -| importer | 📥 | Importing devices from another service. | `schedule` | ✖ | Script / SQLite DB | -| system | ⚙ | Providing core system functionality. | `schedule` / always on | ✖/✔ | Script / Template | -| other | ♻ | Other plugins | misc | ✖ | Script / Template | +| Plugin type | Icon | Description | When to run | Required | Data source [?](./PLUGINS_DEV.md) | +| -------------- | ---- | ------------------------------------------------------------------------- | ----------------------------------- | -------- | ------------------------------------- | +| publisher | ▶️ | Sending notifications to services. | `on_notification` | ✖ | Script | +| dev scanner | 🔍 | On-network scanner discovering devices witout a 3rd party service | `schedule` | ✖ | Script / SQLite DB | +| name discovery | 🆎 | Discovers names of devices via various protocols. | `before_name_updates`, `schedule` | ✖ | Script | +| importer | 📥 | Importing devices from another service. | `schedule` | ✖ | Script / SQLite DB | +| system | ⚙ | Providing core system functionality. | `schedule` / always on | ✖/✔ | Script / Template | +| other | ♻ | Other plugins | misc | ✖ | Script / Template | ## Features -| Icon | Description | -| ---- | ------------------------------------------------------------ | -| 🖧 | Auto-imports the network topology diagram | +| Icon | Description | +| ---- | -------------------------------------------------------------- | +| 🖧 | Auto-imports the network topology diagram | | 🔄 | Has the option to sync some data back into the plugin source | @@ -48,46 +48,47 @@ Device-detecting plugins insert values into the `CurrentScan` database table. T | `APPRISE` | [_publisher_apprise](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/_publisher_apprise/) | ▶️ | Apprise notification proxy | | | | `ARPSCAN` | [arp_scan](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/arp_scan/) | 🔍 | ARP-scan on current network | | | | `AVAHISCAN` | [avahi_scan](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/avahi_scan/) | 🆎 | Avahi (mDNS-based) name resolution | | | -| `ASUSWRT` | [asuswrt_import](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/asuswrt_import/) | 🔍 | Import connected devices from AsusWRT | | | +| `ASUSWRT` | [asuswrt_import](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/asuswrt_import/) | 📥 | Import connected devices from AsusWRT | | | | `CSVBCKP` | [csv_backup](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/csv_backup/) | ⚙ | CSV devices backup | | | | `CUSTPROP` | [custom_props](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/custom_props/) | ⚙ | Managing custom device properties values | | Yes | | `DBCLNP` | [db_cleanup](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/db_cleanup/) | ⚙ | Database cleanup | | Yes\* | | `DDNS` | [ddns_update](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/ddns_update/) | ⚙ | DDNS update | | | -| `DHCPLSS` | [dhcp_leases](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/dhcp_leases/) | 🔍/📥/🆎 | Import devices from DHCP leases | | | +| `DHCPLSS` | [dhcp_leases](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/dhcp_leases/) | 📥/🆎 | Import devices from DHCP leases | | | | `DHCPSRVS` | [dhcp_servers](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/dhcp_servers/) | ♻ | DHCP servers | | | | `DIGSCAN` | [dig_scan](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/dig_scan/) | 🆎 | Dig (DNS) Name resolution | | | -| `FREEBOX` | [freebox](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/freebox/) | 🔍/♻/🆎 | Pull data and names from Freebox/Iliadbox | | | -| `FRITZBOX` | [fritzbox](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/fritzbox/) | 🔍 | Fritz!Box device scanner via TR-064 | | | +| `FREEBOX` | [freebox](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/freebox/) |📥/♻/🆎 | Pull data and names from Freebox/Iliadbox | | | +| `FRITZBOX` | [fritzbox](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/fritzbox/) | 📥 | Fritz!Box device scanner via TR-064 | | | | `ICMP` | [icmp_scan](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/icmp_scan/) | ♻ | ICMP (ping) status checker | | | | `INTRNT` | [internet_ip](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/internet_ip/) | 🔍 | Internet IP scanner | | | | `INTRSPD` | [internet_speedtest](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/internet_speedtest/) | ♻ | Internet speed test | | | | `IPNEIGH` | [ipneigh](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/ipneigh/) | 🔍 | Scan ARP (IPv4) and NDP (IPv6) tables | | | -| `KEALSS` | [kea_api](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/kea_api/) | 🔍/🆎 | Pull lease data from the Kea DHCP API | | | -| `LUCIRPC` | [luci_import](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/luci_import/) | 🔍 | Import connected devices from OpenWRT | | | +| `KEALSS` | [kea_api](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/kea_api/) | 📥/🆎 | Pull lease data from the Kea DHCP API | | | +| `LUCIRPC` | [luci_import](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/luci_import/) | 📥 | Import connected devices from OpenWRT | | | | `MAINT` | [maintenance](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/maintenance/) | ⚙ | Maintenance of logs, etc. | | | -| `MQTT` | [_publisher_mqtt](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/_publisher_mqtt/) | ▶️ | MQTT for synching to Home Assistant | | | +| `MQTT` | [_publisher_mqtt](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/_publisher_mqtt/) | ▶️ | MQTT for synching to Home Assistant | | | | `MTSCAN` | [mikrotik_scan](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/mikrotik_scan/) | 🔍 | Mikrotik device import & sync | | | | `NBTSCAN` | [nbtscan_scan](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/nbtscan_scan/) | 🆎 | Nbtscan (NetBIOS-based) name resolution | | | | `NEWDEV` | [newdev_template](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/newdev_template/) | ⚙ | New device template | | Yes | | `NMAP` | [nmap_scan](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/nmap_scan/) | ♻ | Nmap port scanning & discovery | | | -| `NMAPDEV` | [nmap_dev_scan](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/nmap_dev_scan/) | 🔍 | Nmap dev scan on current network | | | +| `NMAPDEV` | [nmap_dev_scan](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/nmap_dev_scan/) | 🔍 | Nmap dev scan on current network | | | | `NSLOOKUP` | [nslookup_scan](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/nslookup_scan/) | 🆎 | NSLookup (DNS-based) name resolution | | | | `NTFPRCS` | [notification_processing](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/notification_processing/) | ⚙ | Notification processing | | Yes | | `NTFY` | [_publisher_ntfy](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/_publisher_ntfy/) | ▶️ | NTFY notifications | | | -| `OMDSDN` | [omada_sdn_imp](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/omada_sdn_imp/) | 📥/🆎 ❌ | UNMAINTAINED use `OMDSDNOPENAPI` | 🖧 🔄 | | +| `OMDSDN` | [omada_sdn_imp](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/omada_sdn_imp/) | 📥/🆎 ❌ | UNMAINTAINED use `OMDSDNOPENAPI` | 🖧 🔄 | | | `OMDSDNOPENAPI` | [omada_sdn_openapi](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/omada_sdn_openapi/) | 📥/🆎 | OMADA TP-Link import via OpenAPI | 🖧 | | -| `PIHOLE` | [pihole_scan](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/pihole_scan/) | 🔍/🆎/📥 | Pi-hole device import & sync | | | -| `PIHOLEAPI` | [pihole_api_scan](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/pihole_api_scan/) | 🔍/🆎/📥 | Pi-hole device import & sync via API v6+ | | | +| `PIHOLE` | [pihole_scan](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/pihole_scan/) | 🆎/📥 | Pi-hole device import & sync | | | +| `PIHOLEAPI` | [pihole_api_scan](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/pihole_api_scan/) | 🆎/📥 | Pi-hole device import & sync via API v6+ | | | | `PUSHSAFER` | [_publisher_pushsafer](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/_publisher_pushsafer/) | ▶️ | Pushsafer notifications | | | | `PUSHOVER` | [_publisher_pushover](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/_publisher_pushover/) | ▶️ | Pushover notifications | | | +| `RSTIMPRT` | [rest_import](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/rest_import/) | 📥/🆎 | Import via a REST API endpoint | 🖧 | | | `SETPWD` | [set_password](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/set_password/) | ⚙ | Set password | | Yes | | `SMTP` | [_publisher_email](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/_publisher_email/) | ▶️ | Email notifications | | | | `SNMPDSC` | [snmp_discovery](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/snmp_discovery/) | 🔍/📥 | SNMP device import & sync | | | -| `SYNC` | [sync](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/sync/) | 🔍/⚙/📥 | Sync & import from NetAlertX instances | 🖧 🔄 | Yes | +| `SYNC` | [sync](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/sync/) | ⚙/📥 | Sync & import from NetAlertX instances | 🖧 🔄 | Yes | | `TELEGRAM` | [_publisher_telegram](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/_publisher_telegram/) | ▶️ | Telegram notifications | | | | `UI` | [ui_settings](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/ui_settings/) | ♻ | UI specific settings | | Yes | -| `UNFIMP` | [unifi_import](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/unifi_import/) | 🔍/📥/🆎 | UniFi device import & sync | 🖧 | | -| `UNIFIAPI` | [unifi_api_import](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/unifi_api_import/) | 🔍/📥/🆎 | UniFi device import (SM API, multi-site) | | | +| `UNFIMP` | [unifi_import](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/unifi_import/) | 📥/🆎 | UniFi device import & sync | 🖧 | | +| `UNIFIAPI` | [unifi_api_import](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/unifi_api_import/) | 📥/🆎 | UniFi device import (SM API, multi-site) | | | | `VNDRPDT` | [vendor_update](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/vendor_update/) | ⚙ | Vendor database update | | | | `WEBHOOK` | [_publisher_webhook](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/_publisher_webhook/) | ▶️ | Webhook notifications | | | | `WEBMON` | [website_monitor](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/website_monitor/) | ♻ | Website down monitoring | | | @@ -112,7 +113,8 @@ Plugins can be enabled via Settings, and can be disabled as needed. ### Disabling, Unloading and Ignoring plugins 1. Change the `_RUN` Setting to `disabled` if you want to disable the plugin, but keep the settings -1. If you want to speed up the application, you can unload the plugin by unselecting it in the `LOADED_PLUGINS` setting. +1. (Important) Save the settings +1. (Opptional) If you want to speed up the application, you can unload the plugin by unselecting it in the `LOADED_PLUGINS` setting (plugins have to be disabled first - see above steps). - Careful, once you save the Settings Unloaded plugin settings will be lost (old `app.conf` files are kept in the `/config` folder) 1. You can completely ignore plugins by placing a `ignore_plugin` file into the plugin directory. Ignored plugins won't show up in the `LOADED_PLUGINS` setting. diff --git a/front/plugins/rest_import/README.md b/front/plugins/rest_import/README.md index 27610c54..3ce6c4cb 100644 --- a/front/plugins/rest_import/README.md +++ b/front/plugins/rest_import/README.md @@ -63,7 +63,7 @@ Enable **Generate Fake MAC** when the API does not expose MAC addresses (e.g. re ``` **Configuration:** -``` +```text Name: OPNsense DHCP URL: https://firewall/api/dnsmasq/leases/search Method: GET diff --git a/test/scan/FIELD_LOCK_TEST_SUMMARY.md b/test/scan/FIELD_LOCK_TEST_SUMMARY.md deleted file mode 100644 index 3adcbdb6..00000000 --- a/test/scan/FIELD_LOCK_TEST_SUMMARY.md +++ /dev/null @@ -1,282 +0,0 @@ -# Field Lock Scenarios - Comprehensive Test Suite - -Created comprehensive tests for all device field locking scenarios in NetAlertX using two complementary approaches. - -## Test Files - -### 1. Unit Tests - Direct Authorization Logic -**File:** `/workspaces/NetAlertX/test/authoritative_fields/test_field_lock_scenarios.py` -- Tests the `can_overwrite_field()` function directly -- Verifies authorization rules without database operations -- Fast, focused unit tests with direct assertions - -**16 Unit Tests covering:** - -#### Protected Sources (No Override) -- ✅ `test_locked_source_prevents_plugin_overwrite()` - LOCKED source blocks updates -- ✅ `test_user_source_prevents_plugin_overwrite()` - USER source blocks updates - -#### Updatable Sources (Allow Override) -- ✅ `test_newdev_source_allows_plugin_overwrite()` - NEWDEV allows plugin updates -- ✅ `test_empty_current_source_allows_plugin_overwrite()` - Empty source allows updates - -#### Plugin Ownership Rules -- ✅ `test_plugin_source_allows_same_plugin_overwrite()` - Plugin can update its own fields -- ✅ `test_plugin_source_allows_different_plugin_overwrite_with_set_always()` - Different plugin CAN update WITH SET_ALWAYS -- ✅ `test_plugin_source_rejects_different_plugin_without_set_always()` - Different plugin CANNOT update WITHOUT SET_ALWAYS - -#### SET_EMPTY Authorization -- ✅ `test_set_empty_allows_overwrite_on_empty_field()` - SET_EMPTY works with NEWDEV -- ✅ `test_set_empty_rejects_overwrite_on_non_empty_field()` - SET_EMPTY doesn't override plugin fields -- ✅ `test_set_empty_with_empty_string_source()` - SET_EMPTY works with empty string source - -#### Empty Value Handling -- ✅ `test_empty_plugin_value_not_used()` - Empty string values rejected -- ✅ `test_whitespace_only_plugin_value_not_used()` - Whitespace-only values rejected -- ✅ `test_none_plugin_value_not_used()` - None values rejected - -#### SET_ALWAYS Override Behavior -- ✅ `test_set_always_overrides_plugin_ownership()` - SET_ALWAYS overrides other plugins but NOT USER/LOCKED -- ✅ `test_multiple_plugins_set_always_scenarios()` - Multi-plugin update scenarios - -#### Multi-Field Scenarios -- ✅ `test_different_fields_with_different_sources()` - Each field respects its own source - ---- - -### 2. Integration Tests - Real Scan Simulation -**File:** `/workspaces/NetAlertX/test/authoritative_fields/test_field_lock_scan_integration.py` -- Simulates real-world scanner operations with CurrentScan/Devices tables -- Tests full scan update pipeline -- Verifies field locking behavior in realistic scenarios - -**8 Integration Tests covering:** - -#### Field Source Protection -- ✅ `test_scan_updates_newdev_device_name()` - NEWDEV fields are populated from scan -- ✅ `test_scan_does_not_update_user_field_name()` - USER fields remain unchanged during scan -- ✅ `test_scan_does_not_update_locked_field()` - LOCKED fields remain unchanged during scan - -#### Vendor Discovery -- ✅ `test_scan_updates_empty_vendor_field()` - Empty vendor gets populated from scan - -#### IP Address Handling -- ✅ `test_scan_updates_ip_addresses()` - IPv4 and IPv6 set from scan data -- ✅ `test_scan_updates_ipv6_without_changing_ipv4()` - IPv6 update preserves existing IPv4 - -#### Device Status -- ✅ `test_scan_updates_presence_status()` - Offline devices correctly marked as not present - -#### Multi-Device Scenarios -- ✅ `test_scan_multiple_devices_mixed_sources()` - Complex multi-device scan with mixed source types - ---- - -### 3. IP Format & Field Locking Tests (`test_ip_format_and_locking.py`) -- IP format validation (IPv4/IPv6) -- Invalid IP rejection -- Address format variations -- Multi-scan IP update scenarios - -**6 IP Format Tests covering:** - -#### IPv4 & IPv6 Validation -- ✅ `test_valid_ipv4_format_accepted()` - Valid IPv4 sets devPrimaryIPv4 -- ✅ `test_valid_ipv6_format_accepted()` - Valid IPv6 sets devPrimaryIPv6 - -#### Invalid Values -- ✅ `test_invalid_ip_values_rejected()` - Rejects: empty, "null", "(unknown)", "(Unknown)" - -#### Multi-Scan Scenarios -- ✅ `test_ipv4_ipv6_mixed_in_multiple_scans()` - IPv4 then IPv6 updates preserve both - -#### Format Variations -- ✅ `test_ipv4_address_format_variations()` - Tests 6 IPv4 ranges: loopback, private, broadcast -- ✅ `test_ipv6_address_format_variations()` - Tests 5 IPv6 formats: loopback, link-local, full address - ---- - -## Total Tests: 33 - -- 10 Authoritative handler tests (existing) -- 3 Device status mapping tests (existing) -- 17 Field lock scenarios (unit tests) -- 8 Field lock scan integration tests -- 2 IP update logic tests (existing, refactored) -- 6 IP format validation tests - -## Test Execution Commands - -### Run all authoritative fields tests -```bash -cd /workspaces/NetAlertX -python -m pytest test/authoritative_fields/ -v -``` - -### Run all field lock tests -```bash -python -m pytest test/authoritative_fields/test_field_lock_scenarios.py test/authoritative_fields/test_field_lock_scan_integration.py -v -``` - -### Run IP format validation tests -```bash -python -m pytest test/authoritative_fields/test_ip_format_and_locking.py -v -``` - ---- - -## Test Architecture - -### Unit Tests (`test_field_lock_scenarios.py`) - -**Approach:** Direct function testing -- Imports: `can_overwrite_field()` from `server.db.authoritative_handler` -- No database setup required -- Fast execution -- Tests authorization logic in isolation - -**Structure:** -```python -def test_scenario(): - result = can_overwrite_field( - field_name="devName", - current_source="LOCKED", - plugin_prefix="ARPSCAN", - plugin_settings={"set_always": [], "set_empty": []}, - field_value="New Value", - ) - assert result is False -``` - -### Integration Tests (`test_field_lock_scan_integration.py`) - -**Approach:** Full pipeline simulation -- Sets up in-memory SQLite database -- Creates Devices and CurrentScan tables -- Populates with realistic scan data -- Calls `device_handling.update_devices_data_from_scan()` -- Verifies final state in Devices table - -**Fixtures:** -- `@pytest.fixture scan_db`: In-memory SQLite database with full schema -- `@pytest.fixture mock_device_handlers`: Mocks device_handling helper functions - -**Structure:** -```python -def test_scan_scenario(scan_db, mock_device_handlers): - cur = scan_db.cursor() - - # Insert device with specific source - cur.execute("INSERT INTO Devices ...") - - # Insert scan results - cur.execute("INSERT INTO CurrentScan ...") - scan_db.commit() - - # Run actual scan update - db = Mock() - db.sql_connection = scan_db - db.sql = cur - device_handling.update_devices_data_from_scan(db) - - # Verify results - row = cur.execute("SELECT ... FROM Devices") - assert row["field"] == "expected_value" -``` - ---- - -## Key Scenarios Tested - -### Protection Rules (Honored in Both Unit & Integration Tests) - -| Scenario | Current Source | Plugin Action | Result | -|----------|---|---|---| -| **User Protection** | USER | Try to update | ❌ BLOCKED | -| **Explicit Lock** | LOCKED | Try to update | ❌ BLOCKED | -| **Default/Empty** | NEWDEV or "" | Try to update with value | ✅ ALLOWED | -| **Same Plugin** | PluginA | PluginA tries to update | ✅ ALLOWED | -| **Different Plugin** | PluginA | PluginB tries to update (no SET_ALWAYS) | ❌ BLOCKED | -| **Different Plugin (SET_ALWAYS)** | PluginA | PluginB tries with SET_ALWAYS | ✅ ALLOWED | -| **SET_ALWAYS > USER** | USER | PluginA with SET_ALWAYS | ❌ BLOCKED (USER always protected) | -| **SET_ALWAYS > LOCKED** | LOCKED | PluginA with SET_ALWAYS | ❌ BLOCKED (LOCKED always protected) | -| **Empty Value** | NEWDEV | Plugin provides empty/None | ❌ BLOCKED | - ---- - -## Field Support - -All 10 lockable fields tested: -1. `devMac` - Device MAC address -2. `devName` - Device hostname/alias -3. `devFQDN` - Fully qualified domain name -4. `devLastIP` - Last known IP address -5. `devVendor` - Device manufacturer -6. `devSSID` - WiFi network name -7. `devParentMAC` - Parent/gateway MAC -8. `devParentPort` - Parent device port -9. `devParentRelType` - Relationship type -10. `devVlan` - VLAN identifier - ---- - -## Plugins Referenced in Tests - -- **ARPSCAN** - ARP scanning network discovery -- **NBTSCAN** - NetBIOS name resolution -- **PIHOLEAPI** - Pi-hole DNS/Ad blocking integration -- **UNIFIAPI** - Ubiquiti UniFi network controller integration -- **DHCPLSS** - DHCP lease scanning (referenced in config examples) - ---- - -## Authorization Rules Reference - -**From `server/db/authoritative_handler.py` - `can_overwrite_field()` function:** - -1. **Rule 1 (USER & LOCKED Protection):** If `current_source` is "USER" or "LOCKED" → Return `False` immediately - - These are ABSOLUTE protections - even SET_ALWAYS cannot override -2. **Rule 2 (Value Validation):** If `field_value` (the NEW value to write) is empty/None/whitespace → Return `False` immediately - - Plugin cannot write empty values - only meaningful data allowed -3. **Rule 3 (SET_ALWAYS Override):** If field is in plugin's `set_always` list → Return `True` - - Allows overwriting ANY source (except USER/LOCKED already blocked in Rule 1) - - Works on empty current values, plugin-owned fields, other plugins' fields -4. **Rule 4 (SET_EMPTY):** If field is in plugin's `set_empty` list AND current_source is empty/"NEWDEV" → Return `True` - - Restrictive: Only fills empty fields, won't overwrite plugin-owned fields -5. **Rule 5 (Default):** If current_source is empty/"NEWDEV" → Return `True`, else → Return `False` - - Default behavior: only overwrite empty/unset fields - -**Key Principles:** -- **USER and LOCKED** = Absolute protection (cannot be overwritten, even with SET_ALWAYS) -- **SET_ALWAYS** = Allow overwrite of: own fields, other plugin fields, empty current values, NEWDEV fields -- **SET_EMPTY** = "Set only if empty" - fills empty fields only, won't overwrite existing plugin data -- **Default** = Plugins can only update NEWDEV/empty fields without authorization -- Plugin ownership (e.g., "ARPSCAN") is treated like any other non-protected source for override purposes - ---- - -## Related Documentation - -- **User Guide:** [DEVICE_FIELD_LOCK.md](../../docs/DEVICE_FIELD_LOCK.md) - User-friendly field locking instructions -- **API Documentation:** [API_DEVICE_FIELD_LOCK.md](../../docs/API_DEVICE_FIELD_LOCK.md) - Endpoint documentation -- **Plugin Configuration:** [PLUGINS_DEV_CONFIG.md](../../docs/PLUGINS_DEV_CONFIG.md) - SET_ALWAYS/SET_EMPTY configuration guide -- **Device Management:** [DEVICE_MANAGEMENT.md](../../docs/DEVICE_MANAGEMENT.md) - Device management admin guide - ---- - -## Implementation Files - -**Code Under Test:** -- `server/db/authoritative_handler.py` - Authorization logic -- `server/scan/device_handling.py` - Scan update pipeline -- `server/api_server/api_server_start.py` - API endpoints for field locking - -**Test Files:** -- `test/authoritative_fields/test_field_lock_scenarios.py` - Unit tests -- `test/authoritative_fields/test_field_lock_scan_integration.py` - Integration tests - ---- - -**Created:** January 19, 2026 -**Last Updated:** January 19, 2026 -**Status:** ✅ 24 comprehensive tests created covering all scenarios