RBAC: Enforce read-only viewer role across backend and frontend
- Add global WriteAccessGuard that blocks POST/PUT/PATCH/DELETE for viewer role - Add @AllowViewer() decorator for endpoints viewers need (switch-org, intro-seen, AI recommendations) - Add useIsReadOnly hook to auth store for frontend role checks - Hide write UI (add/edit/delete/import buttons, inline editors) in all 13 data pages for viewers - Disable inline NumberInputs on Budgets and Monthly Actuals pages for viewers - Skip onboarding wizard for viewer role users Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -32,6 +32,8 @@ export function AppLayout() {
|
||||
// Only run for non-impersonating users with an org selected, on dashboard
|
||||
if (isImpersonating || !currentOrg || !user) return;
|
||||
if (!location.pathname.startsWith('/dashboard')) return;
|
||||
// Read-only users (viewers) skip onboarding entirely
|
||||
if (currentOrg.role === 'viewer') return;
|
||||
|
||||
if (user.hasSeenIntro === false || user.hasSeenIntro === undefined) {
|
||||
// Delay to ensure DOM elements are rendered for tour targeting
|
||||
@@ -40,7 +42,7 @@ export function AppLayout() {
|
||||
} else if (currentOrg.settings?.onboardingComplete !== true) {
|
||||
setShowWizard(true);
|
||||
}
|
||||
}, [user?.hasSeenIntro, currentOrg?.id, currentOrg?.settings?.onboardingComplete, isImpersonating, location.pathname]);
|
||||
}, [user?.hasSeenIntro, currentOrg?.id, currentOrg?.role, currentOrg?.settings?.onboardingComplete, isImpersonating, location.pathname]);
|
||||
|
||||
const handleTourComplete = () => {
|
||||
setShowTour(false);
|
||||
|
||||
Reference in New Issue
Block a user