Consolidate settings into 8 hubs and fix security vulnerabilities

Settings Consolidation (37+ pages to 8 hubs):
- Asset Type Hub: Unified type management with plugin descriptors
- Model Catalog Hub: Vendors, types, models, operating systems
- Asset Configuration Hub: Statuses, relationships, fields, identifiers
- Security Hub: Authentication, tokens, users, audit
- Site Configuration Hub: Site settings, floor map, branding, imports
- Integrations Hub: ServiceNow, Zabbix, Dell warranty
- Locations Hub: Locations, business units, employees
- Network Infrastructure Hub: VLANs, subnets

Plugin Descriptor Pattern:
- Add get_asset_type_descriptor hook to BasePlugin
- Implement descriptors in 6 plugins (printers, computers, machines, network, measuringtools, locations)
- Add /api/pluginui/asset-type-descriptors and core-type-descriptors endpoints
- Generic SubtypesTab component renders CRUD UI from descriptor schema

Security Fixes:
- Fix stored XSS via javascript: URIs in Models and Support Teams
- Add URL scheme validation (allow only http/https) in create/update endpoints
- Fix plugin contract violations in endpoints plugin (use shopdb.api imports)
- Add missing requiresAdmin guard to printer model supplies route
- Add user-facing error messages to API token load failures

Integration Improvements:
- Add persistent tab navigation to ServiceNow, Zabbix, and Dell Warranty settings
- Standardize integration settings UI across all three pages
- Fix tab visibility when navigating between integrations

Tested with 94-agent orchestrated review for ADR compliance and security.
This commit is contained in:
Proudlock
2026-09-11 10:45:12 -04:00
parent 9f78d0bfc7
commit f9f1d39f08
34 changed files with 2485 additions and 166 deletions

View File

@@ -131,3 +131,104 @@ def asset_presentation():
"""
entries = _collect('get_asset_presentation')
return success_response(entries)
@pluginui_bp.route('/asset-type-descriptors', methods=['GET'])
@require_permission('assets.view')
def asset_type_descriptors():
"""Merge enabled plugins' asset-type subtype descriptors.
The Asset Type Hub uses these to render generic CRUD interfaces for
each plugin's subtype table (printertypes, machinetypes, etc.) without
importing plugin models or views.
Each descriptor maps to ONE asset type (from assettypes table) and
tells core:
- Which API endpoint handles the subtype CRUD
- Field names for ID, label, description, color
- Required permission
- Optional plugin-specific tabs (e.g., 'supplies' for printers)
Returns: List[Dict] sorted by assettype name
"""
pm = current_app.extensions.get('plugin_manager')
if not pm:
return success_response([])
descriptors = []
for name, plugin in pm.get_all_plugins().items():
if not pm.registry.is_enabled(name):
continue
try:
desc = plugin.get_asset_type_descriptor()
if desc is None:
continue
desc['plugin'] = name
descriptors.append(desc)
except Exception:
if current_app.config.get('DEBUG') or current_app.config.get('TESTING'):
raise
current_app.logger.exception('Plugin %s get_asset_type_descriptor failed', name)
# Validation: each descriptor must have required keys
required_keys = {'assettype', 'subtype_endpoint', 'subtype_id_field',
'subtype_label_field', 'subtype_description_field',
'subtype_color_field', 'edit_permission'}
validated = []
for desc in descriptors:
missing = required_keys - set(desc.keys())
if missing:
current_app.logger.error(
'Plugin %s descriptor missing keys: %s',
desc.get('plugin', 'unknown'), missing
)
if current_app.config.get('DEBUG') or current_app.config.get('TESTING'):
raise ValueError(f"Descriptor missing keys: {missing}")
continue
validated.append(desc)
validated.sort(key=lambda d: d.get('assettype', ''))
return success_response(validated)
@pluginui_bp.route('/core-type-descriptors', methods=['GET'])
@require_permission('assets.view')
def core_type_descriptors():
"""Return type descriptors for core-owned non-asset types.
These are types managed by core (not plugins) that fit the same
management pattern as asset subtypes: location types, notification types, etc.
Returns same descriptor format as asset-type-descriptors, with optional
'custom_component' field for types that need specialized UI.
"""
descriptors = [
{
'category': 'location',
'type_name': 'Location Type',
'type_name_plural': 'Location Types',
'subtype_endpoint': '/locations/types',
'subtype_id_field': 'locationtypeid',
'subtype_label_field': 'locationtype',
'subtype_description_field': 'description',
'subtype_color_field': 'color',
'optional_tabs': [],
'edit_permission': 'admin',
'group': 'other',
},
{
'category': 'notification',
'type_name': 'Notification Type',
'type_name_plural': 'Notification Types',
'subtype_endpoint': '/notifications/types',
'subtype_id_field': 'notificationtypeid',
'subtype_label_field': 'typename',
'subtype_description_field': 'typedescription',
'subtype_color_field': 'typecolor',
'optional_tabs': [],
'edit_permission': 'admin',
'group': 'other',
'custom_component': 'NotificationTypesTab',
},
]
return success_response(descriptors)