Cada tenant tiene su propio administrador asignado desde aquí. El Administrador del Tenant gestiona los usuarios internos y sus permisos dentro del tenant.
Total asociaciones
4
Activas
3
Suspendidas
1
Tenants cubiertos
2
Filtros:
4 asociaciones registradas
Administrador
Tenant
Estado
Asignado
Acciones
MG
María García Rodríguez
mgarcia@edu.gov.co
Sec. de Educación Cundinamarca
Activa
15 mar 2026
AP
Ana Sofía Pérez
aperez@chia.gov.co
Alcaldía Municipal de Chía
Activa
02 abr 2026
RC
Ricardo Castañeda
rcastaneda@edu.gov.co
Sec. de Educación Cundinamarca
Activa
20 mar 2026
JL
Jhon David López
jlopez@chia.gov.co
Alcaldía Municipal de Chía
Suspendida
15 abr 2026
Nueva asociación
El usuario asociado se convierte en Administrador del Tenant. Podrá crear y gestionar usuarios internos dentro de ese tenant.
Solo usuarios activos. El correo no debe estar ya asociado a este tenant.
pause_circle
Suspender acceso
play_circle
Reactivar acceso
link_off
Revocar acceso
Esta acción no puede deshacerse.
-- ============================================================
-- TABLA PRINCIPAL: user_tenant_associations
-- Asociación muchos-a-muchos entre users y tenants
-- El rol asignado siempre es tenant_admin (definido server-side)
-- ============================================================
CREATE TABLE user_tenant_associations (
id UUID STORAGE PLAIN DEFAULT gen_random_uuid() NOT NULL,
user_id UUID STORAGE PLAIN NOT NULL,
tenant_id UUID STORAGE PLAIN NOT NULL,
-- Rol siempre tenant_admin; gestionado internamente por el tenant
role VARCHAR(30) NOT NULL DEFAULT 'tenant_admin'
CHECK (role = 'tenant_admin'),
-- Estado del ciclo de vida de la asociación
state_id VARCHAR(100) NOT NULL DEFAULT 'active'
CHECK (state_id IN ('active', 'suspended', 'revoked')),
origin_id INTEGER STORAGE PLAIN NOT NULL,
-- Notas opcionales del Super Admin al crear la asociación
notes TEXT,
-- Auditoría estándar
created_at TIMESTAMP WITH TIME ZONE STORAGE PLAIN DEFAULT now() NOT NULL,
updated_at TIMESTAMP WITH TIME ZONE STORAGE PLAIN DEFAULT now() NOT NULL,
created_by UUID STORAGE PLAIN NOT NULL,
updated_by UUID STORAGE PLAIN NOT NULL,
-- Campos de ciclo de vida extendido
suspended_at TIMESTAMP WITH TIME ZONE STORAGE PLAIN,
suspended_by UUID STORAGE PLAIN,
revoked_at TIMESTAMP WITH TIME ZONE STORAGE PLAIN,
revoked_by UUID STORAGE PLAIN,
archived_at TIMESTAMP WITH TIME ZONE STORAGE PLAIN,
archived_by UUID STORAGE PLAIN,
CONSTRAINT pk_user_tenant_associations PRIMARY KEY (id),
CONSTRAINT fk_uta_user
FOREIGN KEY (user_id) REFERENCES users(id),
CONSTRAINT fk_uta_tenant
FOREIGN KEY (tenant_id) REFERENCES tenants(id),
CONSTRAINT fk_uta_created_by
FOREIGN KEY (created_by) REFERENCES users(id),
CONSTRAINT fk_uta_updated_by
FOREIGN KEY (updated_by) REFERENCES users(id),
CONSTRAINT fk_uta_suspended_by
FOREIGN KEY (suspended_by) REFERENCES users(id),
CONSTRAINT fk_uta_revoked_by
FOREIGN KEY (revoked_by) REFERENCES users(id),
-- Unicidad: solo una asociación active|suspended por par user-tenant
-- Una asociación revocada históricamente + una nueva activa son válidas
CONSTRAINT uq_uta_user_tenant_active
EXCLUDE USING btree (user_id WITH =, tenant_id WITH =)
WHERE (state_id IN ('active', 'suspended'))
);
-- Índices de consulta frecuente
CREATE INDEX idx_uta_tenant_id ON user_tenant_associations(tenant_id);
CREATE INDEX idx_uta_user_id ON user_tenant_associations(user_id);
CREATE INDEX idx_uta_state_id ON user_tenant_associations(state_id)
WHERE state_id != 'revoked';
CREATE INDEX idx_uta_origin_id ON user_tenant_associations(origin_id);
-- ============================================================
-- REGLAS DE NEGOCIO REFLEJADAS EN ESQUEMA
-- RB-001: EXCLUDE constraint garantiza unicidad active+suspended
-- sin necesidad de validación previa en el endpoint
-- RB-003: Protección del último admin se valida en capa aplicación
-- antes de ejecutar UPDATE state_id = 'suspended'|'revoked'
-- RB-004: user_id → FK a users(id); validación state_id=active
-- se hace en capa aplicación antes del INSERT
-- ============================================================