phase1: RLS simplified to tenant isolation only + canAccess fallback removed + useUserPermissions hook + security kernel docs

This commit is contained in:
Agent Zero
2026-07-29 16:36:51 +02:00
parent 66fd387301
commit 8da803156e
9 changed files with 222 additions and 16 deletions
@@ -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();
+1 -4
View File
@@ -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);
};
+1 -3
View File
@@ -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);
+4
View File
@@ -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;
+39
View File
@@ -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 };
}
-3
View File
@@ -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);
};
-3
View File
@@ -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);
};