fix: corregir acceso automático al Hub por dominio

This commit is contained in:
2026-07-30 16:27:52 -04:00
parent a6eb81831a
commit a5fe1f7b27
5 changed files with 129 additions and 14 deletions
+6
View File
@@ -25,3 +25,9 @@ No es necesario modificar los workflows de n8n.
| `role = member`, `is_active = false` | Usuario bloqueado |
Los registros `member` activos se eliminan durante la migración porque ya no son necesarios.
## Corrección de compatibilidad con Supabase self-hosted
La validación del perfil consulta `glm_hub_authorized_users` como una lista limitada a una fila. Cuando no existe una fila, el resultado vacío se interpreta como **Usuario normal**, evitando que algunas versiones self-hosted de PostgREST rechacen el inicio de sesión al solicitar un objeto único sin coincidencias.
Para instalaciones que todavía conserven la función anterior de autorización, ejecuta `SUPABASE-GLM-HUB-HOTFIX-ACCESO-POR-DOMINIO.sql` una sola vez.
@@ -0,0 +1,103 @@
-- ============================================================================
-- GLM HUB — HOTFIX DE ACCESO AUTOMÁTICO POR DOMINIO
-- ============================================================================
-- Ejecutar una sola vez en Supabase Studio > SQL Editor.
--
-- Resultado esperado:
-- * Todo usuario autenticado con @gomezleemarketing.com entra como Usuario.
-- * La ausencia de una fila en glm_hub_authorized_users NO bloquea el acceso.
-- * La tabla queda reservada para administradores y bloqueos excepcionales.
-- ============================================================================
begin;
comment on table public.glm_hub_authorized_users is
'Excepciones del GLM Hub: administradores activos y usuarios bloqueados. Los usuarios corporativos normales no requieren registro.';
-- Cada usuario puede consultar únicamente su propia excepción. Una respuesta
-- vacía significa que es un Usuario normal del dominio corporativo.
drop policy if exists "GLM Hub users can read only their active access"
on public.glm_hub_authorized_users;
drop policy if exists "GLM Hub users can read their own control record"
on public.glm_hub_authorized_users;
create policy "GLM Hub users can read their own control record"
on public.glm_hub_authorized_users
for select
to authenticated
using (
email = lower(coalesce((select auth.jwt() ->> 'email'), ''))
);
-- La ausencia de registro significa acceso normal como Usuario.
create or replace function public.glm_hub_is_active_user()
returns boolean
language plpgsql
stable
security definer
set search_path = ''
as $$
declare
v_email text;
begin
v_email := lower(coalesce(auth.jwt() ->> 'email', ''));
return
auth.uid() is not null
and v_email ~ '^[a-z0-9.!#$%&''*+/=?^_`{|}~-]+@gomezleemarketing\.com$'
and not exists (
select 1
from public.glm_hub_authorized_users as access
where access.email = v_email
and access.is_active = false
);
end;
$$;
-- Solo una fila activa con role = admin concede funciones administrativas.
create or replace function public.glm_hub_is_admin()
returns boolean
language plpgsql
stable
security definer
set search_path = ''
as $$
declare
v_email text;
begin
v_email := lower(coalesce(auth.jwt() ->> 'email', ''));
return
auth.uid() is not null
and exists (
select 1
from public.glm_hub_authorized_users as access
where access.email = v_email
and access.role = 'admin'
and access.is_active = true
);
end;
$$;
alter function public.glm_hub_is_active_user() owner to postgres;
alter function public.glm_hub_is_admin() owner to postgres;
revoke all on function public.glm_hub_is_active_user() from public, anon;
revoke all on function public.glm_hub_is_admin() from public, anon;
grant execute on function public.glm_hub_is_active_user() to authenticated;
grant execute on function public.glm_hub_is_admin() to authenticated;
grant execute on function public.glm_hub_is_active_user() to service_role;
grant execute on function public.glm_hub_is_admin() to service_role;
-- Limpia registros normales antiguos. Se conservan administradores y bloqueos.
delete from public.glm_hub_authorized_users
where role = 'member'
and is_active = true;
notify pgrst, 'reload schema';
commit;
-- VERIFICACIÓN OPCIONAL:
-- select public.glm_hub_is_active_user() as puede_entrar,
-- public.glm_hub_is_admin() as es_administrador;
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -9,7 +9,7 @@
<link rel="shortcut icon" type="image/png" href="https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM.png?v=glm-hub-20260722-v3" />
<link rel="apple-touch-icon" href="https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM.png?v=glm-hub-20260722-v3" />
<title>GLM Hub — GomezLee Marketing</title>
<script type="module" crossorigin src="/assets/index-_vT1KbDN.js"></script>
<script type="module" crossorigin src="/assets/index-Kd6NFVcJ.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-9ByAKvcq.css">
</head>
<body>
+8 -2
View File
@@ -139,12 +139,18 @@ async function authorizeSupabaseSession(authSession: SupabaseAuthSession): Promi
// Todos los correos corporativos entran como Usuario por defecto.
// La tabla solo conserva administradores y bloqueos excepcionales.
// Se consulta como una lista limitada a una fila en lugar de usar
// `maybeSingle()`. En algunas instalaciones self-hosted de PostgREST, una
// consulta sin coincidencias puede responder como error al solicitar un
// objeto único. Una lista vacía, en cambio, representa correctamente a un
// usuario corporativo normal que no necesita registro en la tabla.
const { data, error } = await supabase
.from("glm_hub_authorized_users")
.select("email, full_name, role, is_active")
.eq("email", email)
.maybeSingle();
const access = data as AuthorizedUserRow | null;
.limit(1);
const accessRows = (data ?? []) as AuthorizedUserRow[];
const access = accessRows[0] ?? null;
if (error) {
console.error("No se pudo validar el perfil de acceso:", error);