Add page visibility by group with centralized matrix
Introduce a Page Visibility Matrix that controls which pages each user
group can access, enforced in both frontend and backend:
Frontend:
- Create frontend/src/config/pageVisibility.js with PAGE_VISIBILITY
matrix and canAccessPage() / getAccessiblePages() helpers
- NavDrawer: replace inline requiredGroups with canAccessPage() filter
- App.js: replace per-page isInGroup()/isAdmin() checks with generic
route guard in setCurrentPage; remove VALID_PAGES constant
- localStorage validation: verify persisted page is accessible on load
Backend (page-level access enforcement):
- jiraTickets.js: add router-level requireGroup('Admin','Standard_User')
- archerTemplates.js: add router-level requireGroup('Admin','Standard_User')
- VCL multi-vertical already had requireGroup('Admin','Leadership')
Visibility matrix:
- Home, Knowledge Base: all groups
- Triage, Compliance, Exports: Admin, Standard_User, Leadership
- CCP Metrics: Admin, Leadership
- Jira, Archer Templates: Admin, Standard_User
- Admin Panel: Admin only
- Read_Only sees only Home and Knowledge Base
This commit is contained in:
@@ -1,12 +1,13 @@
|
||||
import React from 'react';
|
||||
import { X, Home, BarChart2, BookOpen, Download, ShieldCheck, Settings, Ticket, Building2, Layers } from 'lucide-react';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import { canAccessPage } from '../config/pageVisibility';
|
||||
|
||||
const NAV_ITEMS = [
|
||||
{ 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: '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', requiredGroups: ['Admin', 'Leadership'] },
|
||||
{ id: 'ccp-metrics', label: 'CCP Metrics', icon: Building2, color: '#A78BFA', description: 'Cross-vertical VCL reporting' },
|
||||
{ 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: 'jira', label: 'Jira Tickets', icon: Ticket, color: '#6366F1', description: 'Jira issue tracking & sync' },
|
||||
@@ -16,7 +17,7 @@ const NAV_ITEMS = [
|
||||
const ADMIN_ITEM = { id: 'admin', label: 'Admin Panel', icon: Settings, color: '#EF4444', description: 'User management & audit' };
|
||||
|
||||
export default function NavDrawer({ isOpen, onClose, currentPage, onNavigate }) {
|
||||
const { isAdmin, isInGroup } = useAuth();
|
||||
const { user } = useAuth();
|
||||
|
||||
if (!isOpen) return null;
|
||||
|
||||
@@ -70,7 +71,7 @@ export default function NavDrawer({ isOpen, onClose, currentPage, onNavigate })
|
||||
|
||||
{/* Nav items */}
|
||||
<nav style={{ display: 'flex', flexDirection: 'column', gap: '0.375rem' }}>
|
||||
{NAV_ITEMS.filter(({ requiredGroups }) => !requiredGroups || isInGroup(...requiredGroups)).map(({ id, label, icon: Icon, color, description }) => {
|
||||
{NAV_ITEMS.filter(item => canAccessPage(item.id, user?.group)).map(({ id, label, icon: Icon, color, description }) => {
|
||||
const active = currentPage === id;
|
||||
return (
|
||||
<button
|
||||
@@ -124,8 +125,8 @@ export default function NavDrawer({ isOpen, onClose, currentPage, onNavigate })
|
||||
);
|
||||
})}
|
||||
|
||||
{/* Admin panel link — visible only to Admin group */}
|
||||
{isAdmin() && (() => {
|
||||
{/* Admin panel link — visible based on page visibility matrix */}
|
||||
{canAccessPage('admin', user?.group) && (() => {
|
||||
const { id, label, icon: Icon, color, description } = ADMIN_ITEM;
|
||||
const active = currentPage === id;
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user