fix: solución de bloqueos y estandarización de permisos

This commit is contained in:
2026-04-27 10:47:30 -05:00
parent fa3cbb4d0a
commit 5c2a84e95d
312 changed files with 13889 additions and 7911 deletions

View File

@@ -23,6 +23,7 @@ export interface User {
name?: string;
tenantId?: number;
roles: string[];
permissions: string[];
}
export interface AuthState {
@@ -114,6 +115,14 @@ export const authStore = createAuthStore();
export const isAuthenticated = derived(authStore, ($a) => $a.isAuthenticated);
export const currentUser = derived(authStore, ($a) => $a.user);
/**
* Verifica si el usuario tiene un permiso específico
*/
export function userHasPermission(user: User | null, permission: string): boolean {
if (!user) return false;
return user.roles.includes('admin') || user.permissions.includes(permission);
}
// ─────────────────────────────────────────────────────────
// Inicialización
// ─────────────────────────────────────────────────────────
@@ -134,7 +143,7 @@ export const initAuth = async (): Promise<boolean> => {
if (cookieToken) {
authStore.setToken(cookieToken);
authStore.setAuthenticated(true);
await loadUserInfo(cookieToken).catch(() => {});
await loadUserInfo(cookieToken).catch(() => { });
authStore.setLoading(false);
return true;
}
@@ -196,24 +205,40 @@ const updateAuthState = async () => {
const tenantChanged = previousTenantId !== undefined && previousTenantId !== tenantId;
// Obtener permisos actuales para evitar que el SSO los borre si fallara el fetch posterior
let currentPerms: string[] = [];
try {
const { get } = await import('svelte/store');
const currentState = get(authStore);
currentPerms = currentState.user?.permissions || [];
} catch { }
const user: User = {
id: profile.id ?? '',
username: profile.username ?? '',
email: profile.email,
name: `${profile.firstName ?? ''} ${profile.lastName ?? ''}`.trim(),
tenantId,
roles
roles,
permissions: parsed?.permissions?.length ? parsed.permissions : currentPerms
};
authStore.setAuthenticated(true);
authStore.setUser(user);
authStore.setToken(token);
// ⚠️ IMPORTANTE: El SSO original de Keycloak no inyecta los permisos granulares
// que viven en la base de datos de PostgreSQL en nuestro `permissions: parsed?.permissions`.
// Necesitamos hacer polling a /auth/me para que `user.permissions` se rellene.
if (token) {
await loadUserInfo(token).catch(() => { });
}
if (tenantChanged && browser) {
try {
const { companyStore } = await import('./stores/company.svelte');
companyStore.clear();
} catch {}
} catch { }
}
previousTenantId = tenantId;
@@ -244,7 +269,7 @@ const setupKeycloakTokenHooks = () => {
.then(({ getSessionManager }) => {
getSessionManager()?.updateToken(keycloakInstance!.token!);
})
.catch(() => {});
.catch(() => { });
}
})
.catch(() => {
@@ -322,6 +347,15 @@ export const login = async (credentials: {
// User info
// ─────────────────────────────────────────────────────────
export const refreshPermissions = async () => {
const token = getToken();
if (token) {
await loadUserInfo(token);
return true;
}
return false;
};
const loadUserInfo = async (token: string) => {
try {
authStore.setToken(token);
@@ -335,7 +369,8 @@ const loadUserInfo = async (token: string) => {
email: d.email,
name: d.name,
tenantId: d.tenant_id,
roles: d.realm_access?.roles ?? []
roles: d.roles ?? [],
permissions: d.permissions ?? []
});
}
} catch (err) {
@@ -355,13 +390,13 @@ export const logout = async () => {
try {
const { destroySessionManager } = await import('./session-manager');
destroySessionManager();
} catch {}
} catch { }
// Limpiar store de compañías
try {
const { companyStore } = await import('./stores/company.svelte');
companyStore.clear();
} catch {}
} catch { }
// Limpiar estado en memoria
authStore.reset();
@@ -374,7 +409,7 @@ export const logout = async () => {
if (keycloakInstance?.authenticated) {
try {
await fetch('/logout', { method: 'POST' });
} catch {}
} catch { }
await keycloakInstance.logout({
redirectUri: window.location.origin + '/login'
});