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:
@@ -36,40 +36,158 @@ export default [
|
||||
meta: { requiresAuth: true, requiresAdmin: true }
|
||||
},
|
||||
{
|
||||
path: 'settings/vendors',
|
||||
name: 'vendors',
|
||||
component: () => import('../../views/vendors/VendorsList.vue'),
|
||||
path: 'settings/catalog',
|
||||
name: 'model-catalog',
|
||||
component: () => import('../../views/settings/ModelCatalogHub.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: true },
|
||||
children: [
|
||||
{
|
||||
path: 'vendors',
|
||||
name: 'model-catalog-vendors',
|
||||
component: () => import('../../views/vendors/VendorsList.vue')
|
||||
},
|
||||
{
|
||||
path: 'types',
|
||||
name: 'model-catalog-types',
|
||||
component: () => import('../../views/settings/ModelTypesList.vue')
|
||||
},
|
||||
{
|
||||
path: 'models',
|
||||
name: 'model-catalog-models',
|
||||
component: () => import('../../views/settings/ModelsList.vue')
|
||||
},
|
||||
{
|
||||
path: 'operatingsystems',
|
||||
name: 'model-catalog-os',
|
||||
component: () => import('../../.plugins-staged/computers/views/OperatingSystemsList.vue')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'settings/assetconfig',
|
||||
name: 'asset-config',
|
||||
component: () => import('../../views/settings/AssetConfigHub.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: true },
|
||||
children: [
|
||||
{
|
||||
path: 'statuses',
|
||||
name: 'asset-config-statuses',
|
||||
component: () => import('../../views/settings/StatusesList.vue')
|
||||
},
|
||||
{
|
||||
path: 'relationships',
|
||||
name: 'asset-config-relationships',
|
||||
component: () => import('../../views/settings/RelationshipTypesList.vue')
|
||||
},
|
||||
{
|
||||
path: 'fields',
|
||||
name: 'asset-config-fields',
|
||||
component: () => import('../../views/settings/CustomFieldsList.vue')
|
||||
},
|
||||
{
|
||||
path: 'identifiers',
|
||||
name: 'asset-config-identifiers',
|
||||
component: () => import('../../views/settings/AssetIdentifiersSettings.vue')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'settings/security',
|
||||
name: 'security',
|
||||
component: () => import('../../views/settings/SecurityHub.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: true },
|
||||
children: [
|
||||
{
|
||||
path: 'authentication',
|
||||
name: 'security-authentication',
|
||||
component: () => import('../../views/settings/AuthenticationSettings.vue')
|
||||
},
|
||||
{
|
||||
path: 'tokens',
|
||||
name: 'security-tokens',
|
||||
component: () => import('../../views/settings/ApiTokensList.vue')
|
||||
},
|
||||
{
|
||||
path: 'users',
|
||||
name: 'security-users',
|
||||
component: () => import('../../views/settings/UsersList.vue')
|
||||
},
|
||||
{
|
||||
path: 'audit',
|
||||
name: 'security-audit',
|
||||
component: () => import('../../views/settings/AuditSettings.vue')
|
||||
},
|
||||
{
|
||||
path: 'auditlogs',
|
||||
name: 'security-auditlogs',
|
||||
component: () => import('../../views/settings/AuditLogs.vue')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'settings/siteconfig',
|
||||
name: 'site-config',
|
||||
component: () => import('../../views/settings/SiteConfigHub.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: true },
|
||||
children: [
|
||||
{
|
||||
path: 'site',
|
||||
name: 'site-config-site',
|
||||
component: () => import('../../views/settings/SiteSettings.vue')
|
||||
},
|
||||
{
|
||||
path: 'floormap',
|
||||
name: 'site-config-floormap',
|
||||
component: () => import('../../views/settings/FloorMapSettings.vue')
|
||||
},
|
||||
{
|
||||
path: 'branding',
|
||||
name: 'site-config-branding',
|
||||
component: () => import('../../views/settings/BrandingSettings.vue')
|
||||
},
|
||||
{
|
||||
path: 'imports',
|
||||
name: 'site-config-imports',
|
||||
component: () => import('../../views/settings/ImportSiteAssets.vue')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'settings/integrations',
|
||||
name: 'integrations',
|
||||
component: () => import('../../views/settings/IntegrationsHub.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: true }
|
||||
},
|
||||
{
|
||||
path: 'settings/modeltypes',
|
||||
name: 'modeltypes',
|
||||
component: () => import('../../views/settings/ModelTypesList.vue'),
|
||||
path: 'settings/servicenow',
|
||||
name: 'servicenow-settings',
|
||||
component: () => import('../../views/settings/ServiceNowSettings.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: true }
|
||||
},
|
||||
{
|
||||
path: 'settings/locations',
|
||||
name: 'locations',
|
||||
component: () => import('../../views/settings/LocationsList.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: true }
|
||||
name: 'locations-hub',
|
||||
component: () => import('../../views/settings/LocationsHub.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: true },
|
||||
children: [
|
||||
{
|
||||
path: 'list',
|
||||
name: 'locations-hub-locations',
|
||||
component: () => import('../../views/settings/LocationsList.vue')
|
||||
},
|
||||
{
|
||||
path: 'businessunits',
|
||||
name: 'locations-hub-businessunits',
|
||||
component: () => import('../../views/settings/BusinessUnitsList.vue')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'settings/statuses',
|
||||
name: 'statuses',
|
||||
component: () => import('../../views/settings/StatusesList.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: true }
|
||||
},
|
||||
{
|
||||
path: 'settings/models',
|
||||
name: 'models',
|
||||
component: () => import('../../views/settings/ModelsList.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: true }
|
||||
},
|
||||
{
|
||||
path: 'settings/businessunits',
|
||||
name: 'businessunits',
|
||||
component: () => import('../../views/settings/BusinessUnitsList.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: true }
|
||||
path: 'settings/network',
|
||||
name: 'network-hub',
|
||||
component: () => import('../../views/settings/NetworkHub.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: true },
|
||||
children: []
|
||||
},
|
||||
{
|
||||
path: 'settings/supportteams',
|
||||
@@ -83,18 +201,6 @@ export default [
|
||||
component: () => import('../../views/settings/DashboardDefaultsList.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: true }
|
||||
},
|
||||
{
|
||||
path: 'settings/site',
|
||||
name: 'site-settings',
|
||||
component: () => import('../../views/settings/SiteSettings.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: true }
|
||||
},
|
||||
{
|
||||
path: 'settings/relationshiptypes',
|
||||
name: 'relationship-types',
|
||||
component: () => import('../../views/settings/RelationshipTypesList.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: true }
|
||||
},
|
||||
{
|
||||
path: 'settings/locationtypes',
|
||||
name: 'location-types',
|
||||
@@ -103,15 +209,22 @@ export default [
|
||||
},
|
||||
{
|
||||
path: 'settings/assettypes',
|
||||
name: 'asset-types',
|
||||
component: () => import('../../views/settings/AssetTypesList.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: true }
|
||||
},
|
||||
{
|
||||
path: 'settings/customfields',
|
||||
name: 'custom-fields',
|
||||
component: () => import('../../views/settings/CustomFieldsList.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: true }
|
||||
component: () => import('../../views/settings/AssetTypeHub.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: true },
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'asset-types-hub',
|
||||
component: () => import('../../views/settings/asset-types/TypesTabWrapper.vue'),
|
||||
props: route => ({ assettypeid: route.params.assettypeid || route.params.assettypeid })
|
||||
},
|
||||
{
|
||||
path: ':assettypeid/subtypes',
|
||||
name: 'asset-types-hub-subtypes',
|
||||
component: () => import('../../views/settings/asset-types/TypesTabWrapper.vue'),
|
||||
props: route => ({ assettypeid: route.params.assettypeid })
|
||||
},
|
||||
]
|
||||
},
|
||||
// The old monolithic System Settings tab page is split into one routed page
|
||||
// per section (below). This redirect keeps old bookmarks working: bare
|
||||
@@ -138,24 +251,6 @@ export default [
|
||||
},
|
||||
meta: { requiresAuth: true, requiresAdmin: true }
|
||||
},
|
||||
{
|
||||
path: 'settings/servicenow',
|
||||
name: 'servicenow-settings',
|
||||
component: () => import('../../views/settings/ServiceNowSettings.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: true }
|
||||
},
|
||||
{
|
||||
path: 'settings/branding',
|
||||
name: 'branding-settings',
|
||||
component: () => import('../../views/settings/BrandingSettings.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: true }
|
||||
},
|
||||
{
|
||||
path: 'settings/importsiteassets',
|
||||
name: 'import-site-assets',
|
||||
component: () => import('../../views/settings/ImportSiteAssets.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: true }
|
||||
},
|
||||
{
|
||||
path: 'settings/printing',
|
||||
name: 'printing-settings',
|
||||
@@ -168,60 +263,18 @@ export default [
|
||||
component: () => import('../../views/settings/EmailSettings.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: true }
|
||||
},
|
||||
{
|
||||
path: 'settings/audit',
|
||||
name: 'audit-settings',
|
||||
component: () => import('../../views/settings/AuditSettings.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: true }
|
||||
},
|
||||
{
|
||||
path: 'settings/authentication',
|
||||
name: 'authentication-settings',
|
||||
component: () => import('../../views/settings/AuthenticationSettings.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: true }
|
||||
},
|
||||
{
|
||||
path: 'settings/apitokens',
|
||||
name: 'apitokens-settings',
|
||||
component: () => import('../../views/settings/ApiTokensList.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: true }
|
||||
},
|
||||
{
|
||||
path: 'settings/assetidentifiers',
|
||||
name: 'asset-identifiers-settings',
|
||||
component: () => import('../../views/settings/AssetIdentifiersSettings.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: true }
|
||||
},
|
||||
{
|
||||
path: 'settings/globalsearch',
|
||||
name: 'global-search-settings',
|
||||
component: () => import('../../views/settings/GlobalSearchSettings.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: true }
|
||||
},
|
||||
{
|
||||
path: 'settings/floormap',
|
||||
name: 'floor-map-settings',
|
||||
component: () => import('../../views/settings/FloorMapSettings.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: true }
|
||||
},
|
||||
{
|
||||
path: 'settings/plugins',
|
||||
name: 'plugins',
|
||||
component: () => import('../../views/settings/PluginsList.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: true }
|
||||
},
|
||||
{
|
||||
path: 'settings/auditlogs',
|
||||
name: 'audit-logs',
|
||||
component: () => import('../../views/settings/AuditLogs.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: true }
|
||||
},
|
||||
{
|
||||
path: 'settings/users',
|
||||
name: 'users',
|
||||
component: () => import('../../views/settings/UsersList.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: true }
|
||||
},
|
||||
{
|
||||
path: 'settings/shopdbbackups',
|
||||
name: 'shopdbbackups',
|
||||
|
||||
Reference in New Issue
Block a user