fix: solución de bloqueos y estandarización de permisos
This commit is contained in:
@@ -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'
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user