Restrict VCL/CCP Metrics page to Admin and Leadership groups
Add requireGroup('Admin', 'Leadership') as router-level middleware on all
VCL multi-vertical routes. Hide the CCP Metrics nav item from users not in
those groups and guard the page render in App.js with a redirect fallback.
This commit is contained in:
@@ -186,8 +186,9 @@ function isSafeTempPath(filePath) {
|
|||||||
function createVCLMultiVerticalRouter(upload) {
|
function createVCLMultiVerticalRouter(upload) {
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
// All routes require authentication
|
// All routes require authentication + Leadership or Admin group
|
||||||
router.use(requireAuth());
|
router.use(requireAuth());
|
||||||
|
router.use(requireGroup('Admin', 'Leadership'));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* POST /preview
|
* POST /preview
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ const getSeverityDotColor = (severity) => {
|
|||||||
const severityLevels = ['All Severities', 'Critical', 'High', 'Medium', 'Low'];
|
const severityLevels = ['All Severities', 'Critical', 'High', 'Medium', 'Low'];
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const { isAuthenticated, loading: authLoading, canWrite, canDelete, canExport, isAdmin, getActiveTeamsParam, adminScope } = useAuth();
|
const { isAuthenticated, loading: authLoading, canWrite, canDelete, canExport, isAdmin, isInGroup, getActiveTeamsParam, adminScope } = useAuth();
|
||||||
const [searchQuery, setSearchQuery] = useState('');
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
const [selectedVendor, setSelectedVendor] = useState('All Vendors');
|
const [selectedVendor, setSelectedVendor] = useState('All Vendors');
|
||||||
const [selectedSeverity, setSelectedSeverity] = useState('All Severities');
|
const [selectedSeverity, setSelectedSeverity] = useState('All Severities');
|
||||||
@@ -1102,7 +1102,8 @@ export default function App() {
|
|||||||
{/* Page content */}
|
{/* Page content */}
|
||||||
{currentPage === 'triage' && <VulnerabilityTriagePage filterDate={calendarFilter} filterEXC={reportingExcFilter} />}
|
{currentPage === 'triage' && <VulnerabilityTriagePage filterDate={calendarFilter} filterEXC={reportingExcFilter} />}
|
||||||
{currentPage === 'compliance' && <CompliancePage onNavigate={setCurrentPage} />}
|
{currentPage === 'compliance' && <CompliancePage onNavigate={setCurrentPage} />}
|
||||||
{currentPage === 'ccp-metrics' && <CCPMetricsPage />}
|
{currentPage === 'ccp-metrics' && isInGroup('Admin', 'Leadership') && <CCPMetricsPage />}
|
||||||
|
{currentPage === 'ccp-metrics' && !isInGroup('Admin', 'Leadership') && (() => { setCurrentPage('home'); return null; })()}
|
||||||
{currentPage === 'knowledge-base' && <KnowledgeBasePage />}
|
{currentPage === 'knowledge-base' && <KnowledgeBasePage />}
|
||||||
{currentPage === 'exports' && <ExportsPage />}
|
{currentPage === 'exports' && <ExportsPage />}
|
||||||
{currentPage === 'jira' && <JiraPage />}
|
{currentPage === 'jira' && <JiraPage />}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ const NAV_ITEMS = [
|
|||||||
{ id: 'home', label: 'Home', icon: Home, color: '#0EA5E9', description: 'Main dashboard' },
|
{ id: 'home', label: 'Home', icon: Home, color: '#0EA5E9', description: 'Main dashboard' },
|
||||||
{ id: 'triage', label: 'Vuln Triage', icon: BarChart2, color: '#F59E0B', description: 'Active findings & CVE triage' },
|
{ id: 'triage', label: 'Vuln Triage', icon: BarChart2, color: '#F59E0B', description: 'Active findings & CVE triage' },
|
||||||
{ id: 'compliance', label: 'Compliance', icon: ShieldCheck, color: '#14B8A6', description: 'AEO posture & metrics' },
|
{ id: 'compliance', label: 'Compliance', icon: ShieldCheck, color: '#14B8A6', description: 'AEO posture & metrics' },
|
||||||
{ id: 'ccp-metrics', label: 'CCP Metrics', icon: Building2, color: '#A78BFA', description: 'Cross-vertical VCL reporting' },
|
{ id: 'ccp-metrics', label: 'CCP Metrics', icon: Building2, color: '#A78BFA', description: 'Cross-vertical VCL reporting', requiredGroups: ['Admin', 'Leadership'] },
|
||||||
{ id: 'knowledge-base', label: 'Knowledge Base', icon: BookOpen, color: '#10B981', description: 'Articles & documentation' },
|
{ id: 'knowledge-base', label: 'Knowledge Base', icon: BookOpen, color: '#10B981', description: 'Articles & documentation' },
|
||||||
{ id: 'exports', label: 'Exports', icon: Download, color: '#8B5CF6', description: 'Export data & reports' },
|
{ id: 'exports', label: 'Exports', icon: Download, color: '#8B5CF6', description: 'Export data & reports' },
|
||||||
{ id: 'jira', label: 'Jira Tickets', icon: Ticket, color: '#6366F1', description: 'Jira issue tracking & sync' },
|
{ id: 'jira', label: 'Jira Tickets', icon: Ticket, color: '#6366F1', description: 'Jira issue tracking & sync' },
|
||||||
@@ -16,7 +16,7 @@ const NAV_ITEMS = [
|
|||||||
const ADMIN_ITEM = { id: 'admin', label: 'Admin Panel', icon: Settings, color: '#EF4444', description: 'User management & audit' };
|
const ADMIN_ITEM = { id: 'admin', label: 'Admin Panel', icon: Settings, color: '#EF4444', description: 'User management & audit' };
|
||||||
|
|
||||||
export default function NavDrawer({ isOpen, onClose, currentPage, onNavigate }) {
|
export default function NavDrawer({ isOpen, onClose, currentPage, onNavigate }) {
|
||||||
const { isAdmin } = useAuth();
|
const { isAdmin, isInGroup } = useAuth();
|
||||||
|
|
||||||
if (!isOpen) return null;
|
if (!isOpen) return null;
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ export default function NavDrawer({ isOpen, onClose, currentPage, onNavigate })
|
|||||||
|
|
||||||
{/* Nav items */}
|
{/* Nav items */}
|
||||||
<nav style={{ display: 'flex', flexDirection: 'column', gap: '0.375rem' }}>
|
<nav style={{ display: 'flex', flexDirection: 'column', gap: '0.375rem' }}>
|
||||||
{NAV_ITEMS.map(({ id, label, icon: Icon, color, description }) => {
|
{NAV_ITEMS.filter(({ requiredGroups }) => !requiredGroups || isInGroup(...requiredGroups)).map(({ id, label, icon: Icon, color, description }) => {
|
||||||
const active = currentPage === id;
|
const active = currentPage === id;
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
|
|||||||
Reference in New Issue
Block a user