Files
shopdb-flask/frontend/src/views/settings/SecurityHub.vue
Proudlock f9f1d39f08 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.
2026-09-11 10:45:12 -04:00

119 lines
2.4 KiB
Vue

<template>
<div class="security-hub">
<div class="hub-header">
<h2>Access & Security</h2>
<p class="description">Manage authentication, API tokens, users, roles, and audit logs</p>
</div>
<div class="tabs">
<router-link
:to="{ name: 'security-authentication' }"
class="tab"
:class="{ active: $route.name === 'security-authentication' }"
>
Authentication
</router-link>
<router-link
:to="{ name: 'security-tokens' }"
class="tab"
:class="{ active: $route.name === 'security-tokens' }"
>
API Tokens
</router-link>
<router-link
:to="{ name: 'security-users' }"
class="tab"
:class="{ active: $route.name === 'security-users' }"
>
Users & Roles
</router-link>
<router-link
:to="{ name: 'security-audit' }"
class="tab"
:class="{ active: $route.name === 'security-audit' }"
>
Audit Settings
</router-link>
<router-link
:to="{ name: 'security-auditlogs' }"
class="tab"
:class="{ active: $route.name === 'security-auditlogs' }"
>
Audit Logs
</router-link>
</div>
<div class="tab-content">
<router-view />
</div>
</div>
</template>
<script setup>
import { onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
const route = useRoute()
const router = useRouter()
onMounted(() => {
if (route.name === 'security') {
router.replace({ name: 'security-authentication' })
}
})
</script>
<style scoped>
.security-hub {
max-width: 1400px;
margin: 0 auto;
}
.hub-header {
margin-bottom: 2rem;
}
.hub-header h2 {
margin: 0 0 0.5rem;
font-size: 1.75rem;
}
.description {
margin: 0;
color: var(--text-light);
font-size: 0.95rem;
}
.tabs {
display: flex;
gap: 0.25rem;
border-bottom: 1px solid var(--border-color);
background: var(--bg-secondary);
margin-bottom: 2rem;
}
.tab {
padding: 0.75rem 1.5rem;
text-decoration: none;
color: var(--text-light);
border-bottom: 2px solid transparent;
transition: all 0.15s;
font-weight: 500;
font-size: 0.95rem;
}
.tab:hover {
color: var(--text);
background: var(--hover-bg);
}
.tab.active {
color: var(--primary);
border-bottom-color: var(--primary);
}
.tab-content {
/* Content styled by child components */
}
</style>