phase1: RLS simplified to tenant isolation only + canAccess fallback removed + useUserPermissions hook + security kernel docs
This commit is contained in:
@@ -163,9 +163,6 @@ export function ContactDetail({ contact, loading, onEdit, onDeleted }: ContactDe
|
||||
const { hasPermission, hasFieldAccess } = usePermission();
|
||||
const authUser = useAuthStore((state) => state.user);
|
||||
const canAccess = (perm: string): boolean => {
|
||||
if (authUser?.is_system_admin) return true;
|
||||
const perms = authUser?.permissions || [];
|
||||
if (perms.length === 0) return true;
|
||||
return hasPermission(perm);
|
||||
};
|
||||
const createPersonMutation = useCreateContactPerson();
|
||||
|
||||
@@ -45,12 +45,9 @@ export function Sidebar() {
|
||||
const { hasPermission } = usePermission();
|
||||
const user = useAuthStore((state) => state.user);
|
||||
|
||||
// Fallback: system admin or empty permissions = show everything
|
||||
// Use hasPermission directly — permissions are loaded via useUserPermissions hook
|
||||
const canAccess = (perm?: string): boolean => {
|
||||
if (!perm) return true;
|
||||
if (user?.is_system_admin) return true;
|
||||
const perms = user?.permissions || [];
|
||||
if (perms.length === 0) return true; // No permissions loaded — show all, backend 403 handles it
|
||||
return hasPermission(perm);
|
||||
};
|
||||
|
||||
|
||||
@@ -22,10 +22,8 @@ export function TopBar() {
|
||||
const logoutMutation = useLogout();
|
||||
const minimizedWindows = useWindowStore((s) => s.windows.filter((w) => w.state === 'minimized'));
|
||||
const { hasPermission } = usePermission();
|
||||
// Use hasPermission directly — permissions are loaded via useUserPermissions hook
|
||||
const canAccess = (perm: string): boolean => {
|
||||
if (user?.is_system_admin) return true;
|
||||
const perms = user?.permissions || [];
|
||||
if (perms.length === 0) return true;
|
||||
return hasPermission(perm);
|
||||
};
|
||||
const restoreWindow = useWindowStore((s) => s.restoreWindow);
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { useCurrentUser } from '@/api/hooks';
|
||||
import { useUserPermissions } from '@/hooks/useUserPermissions';
|
||||
|
||||
export function useAuth() {
|
||||
const store = useAuthStore();
|
||||
const { data, isLoading, isError, error } = useCurrentUser();
|
||||
|
||||
// Load permissions after authentication
|
||||
useUserPermissions();
|
||||
|
||||
useEffect(() => {
|
||||
if (isError) {
|
||||
const status = (error as any)?.status || 0;
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { apiGet } from '@/api/client';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
interface PermissionsResponse {
|
||||
permissions: string[];
|
||||
denied_permissions: string[];
|
||||
field_permissions: Record<string, any>;
|
||||
is_system_admin: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the current user's resolved permissions from /api/v1/auth/me/permissions
|
||||
* and stores them in the authStore.
|
||||
*/
|
||||
export function useUserPermissions() {
|
||||
const { isAuthenticated, setPermissions } = useAuthStore();
|
||||
|
||||
const { data, isSuccess } = useQuery<PermissionsResponse>({
|
||||
queryKey: ['user-permissions'],
|
||||
queryFn: () => apiGet<PermissionsResponse>('/api/v1/auth/me/permissions'),
|
||||
enabled: isAuthenticated,
|
||||
staleTime: 5 * 60 * 1000,
|
||||
retry: 1,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (isSuccess && data) {
|
||||
setPermissions(
|
||||
data.permissions || [],
|
||||
data.is_system_admin || false,
|
||||
data.field_permissions || {},
|
||||
);
|
||||
}
|
||||
}, [isSuccess, data, setPermissions]);
|
||||
|
||||
return { data, isSuccess };
|
||||
}
|
||||
@@ -24,9 +24,6 @@ export function ContactDetailPage() {
|
||||
const { hasPermission } = usePermission();
|
||||
const authUser = useAuthStore((state) => state.user);
|
||||
const canAccess = (perm: string): boolean => {
|
||||
if (authUser?.is_system_admin) return true;
|
||||
const perms = authUser?.permissions || [];
|
||||
if (perms.length === 0) return true;
|
||||
return hasPermission(perm);
|
||||
};
|
||||
|
||||
|
||||
@@ -45,9 +45,6 @@ export function ContactsListPage() {
|
||||
const { hasPermission } = usePermission();
|
||||
const authUser = useAuthStore((state) => state.user);
|
||||
const canAccess = (perm: string): boolean => {
|
||||
if (authUser?.is_system_admin) return true;
|
||||
const perms = authUser?.permissions || [];
|
||||
if (perms.length === 0) return true;
|
||||
return hasPermission(perm);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user