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:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user