Skip to content

Imagy Identity — Modelo de Datos

Base de Datos

  • Nombre: imagy_identity
  • Usuario: imagy_identity_app (NOBYPASSRLS)
  • RLS: Habilitado en tablas de datos de tenant. Tablas de control plane sin RLS.
  • Compartida: Ambos servicios (Gateway y Management) acceden a la misma BD

Diagrama ER

Tablas

tenants (Control Plane — sin RLS)

Tabla principal de tenants. Gestionada por platform admins y delegated admins.

ColumnaTipoDescripcion
idUUID PKIdentificador unico del tenant
nameVARCHAR(200)Nombre comercial del tenant
slugVARCHAR(50)Slug unico para URLs (ej: fintech-abc)
realm_nameVARCHAR(100)Nombre del realm en Keycloak
statusVARCHAR(20)active, suspended, onboarding, deleted
configJSONBConfiguracion general (timezone, locale, currency, etc.)
themeJSONBBranding (logo_url, primary_color, secondary_color, favicon_url, css_override)
modulesJSONBModulos habilitados con configuracion por modulo
custom_domainVARCHAR(255)Dominio custom del tenant (nullable)
planVARCHAR(50)Plan contratado (free, starter, professional, enterprise)
max_usersINTLimite de usuarios segun plan
created_atTIMESTAMPTZFecha de creacion
updated_atTIMESTAMPTZUltima modificacion
suspended_atTIMESTAMPTZFecha de suspension (nullable)

Constraints:

  • UNIQUE(slug)
  • UNIQUE(realm_name)
  • UNIQUE(custom_domain) WHERE custom_domain IS NOT NULL

Ejemplo de config:

json
{
  "timezone": "America/Guayaquil",
  "locale": "es-EC",
  "currency": "USD",
  "date_format": "DD/MM/YYYY",
  "session_timeout_minutes": 30,
  "mfa_required": false,
  "allowed_origins": ["https://fintech-abc.reimaginetech.io"]
}

Ejemplo de theme:

json
{
  "logo_url": "https://cdn.reimaginetech.io/tenants/fintech-abc/logo.svg",
  "favicon_url": "https://cdn.reimaginetech.io/tenants/fintech-abc/favicon.ico",
  "primary_color": "#1E40AF",
  "secondary_color": "#3B82F6",
  "font_family": "Inter",
  "css_override": null
}

users (con RLS por tenant_id)

Usuarios registrados en la plataforma. Sincronizados con Keycloak.

ColumnaTipoDescripcion
idUUID PKIdentificador interno
tenant_idUUID FKTenant al que pertenece (RLS)
keycloak_idVARCHAR(255)ID del usuario en Keycloak (sub claim)
emailVARCHAR(255)Email del usuario
first_nameVARCHAR(100)Nombre
last_nameVARCHAR(100)Apellido
phoneVARCHAR(50)Telefono (nullable)
statusVARCHAR(20)active, inactive, suspended, pending_verification
rolesTEXT[]Array de codigos de rol asignados
organization_idUUID FKOrganizacion dentro del tenant (nullable)
last_login_atTIMESTAMPTZUltimo login registrado
created_atTIMESTAMPTZFecha de creacion
updated_atTIMESTAMPTZUltima modificacion

Constraints:

  • UNIQUE(tenant_id, keycloak_id)
  • UNIQUE(tenant_id, email)

organizations (con RLS por tenant_id)

Sub-agrupaciones dentro de un tenant. Mapeadas a Keycloak Organizations.

ColumnaTipoDescripcion
idUUID PKIdentificador interno
tenant_idUUID FKTenant propietario (RLS)
keycloak_org_idVARCHAR(255)ID de la organizacion en Keycloak
nameVARCHAR(200)Nombre de la organizacion
descriptionTEXTDescripcion (nullable)
is_activeBOOLEANSi esta activa
created_atTIMESTAMPTZFecha de creacion
updated_atTIMESTAMPTZUltima modificacion

roles (con RLS por tenant_id)

Roles con permisos granulares. Pueden ser de sistema (no editables) o custom del tenant.

ColumnaTipoDescripcion
idUUID PKIdentificador interno
tenant_idUUID FKTenant propietario (RLS). NULL para roles de sistema
codeVARCHAR(50)Codigo unico del rol (ej: tenant_admin, operator, viewer)
nameVARCHAR(100)Nombre legible
descriptionTEXTDescripcion del rol
permissionsTEXT[]Array de permisos (ej: ["subject:read", "subject:lists:manage"])
is_systemBOOLEANSi es un rol de sistema (no editable por tenant)
created_atTIMESTAMPTZFecha de creacion
updated_atTIMESTAMPTZUltima modificacion

Roles de sistema (is_system = true):

  • platform_admin — Acceso total a la plataforma
  • delegated_admin — Administra tenants asignados
  • tenant_admin — Administra su propio tenant
  • operator — Opera dentro del tenant
  • viewer — Solo lectura
  • applicant — Usuario final (creado por pipeline de credito)

modules (sin RLS — catalogo global)

Catalogo de modulos disponibles en la plataforma.

ColumnaTipoDescripcion
idUUID PKIdentificador
codeVARCHAR(50)Codigo unico (imagflow, imaglend, imagsign, imagid)
nameVARCHAR(100)Nombre del modulo
descriptionTEXTDescripcion
is_activeBOOLEANSi esta disponible para activacion

tenant_modules (con RLS por tenant_id)

Relacion tenant-modulo con configuracion especifica.

ColumnaTipoDescripcion
idUUID PKIdentificador
tenant_idUUID FKTenant (RLS)
module_idUUID FKModulo activado
is_activeBOOLEANSi esta activo para este tenant
configJSONBConfiguracion especifica del modulo para este tenant
activated_atTIMESTAMPTZFecha de activacion

oidc_clients (con RLS por tenant_id)

Clientes OIDC/SAML configurados por tenant.

ColumnaTipoDescripcion
idUUID PKIdentificador
tenant_idUUID FKTenant propietario (RLS)
client_idVARCHAR(255)Client ID en Keycloak
client_nameVARCHAR(200)Nombre descriptivo
protocolVARCHAR(20)oidc, saml
configJSONBConfiguracion (redirect_uris, scopes, etc.)
is_activeBOOLEANSi esta activo
created_atTIMESTAMPTZFecha de creacion

RLS Policies

sql
-- Users: aislamiento por tenant
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON users
    FOR ALL USING (tenant_id = get_current_tenant_id());

-- Control plane (platform admin sin tenant context)
CREATE POLICY control_plane ON users
    FOR ALL USING (
        current_setting('app.current_tenant_id', true) IS NULL
        OR current_setting('app.current_tenant_id', true) = ''
    );

-- Misma politica para organizations, roles, tenant_modules, oidc_clients
-- tenants NO tiene RLS (es tabla de control plane)

Indices

sql
-- Tenants
CREATE UNIQUE INDEX idx_tenants_slug ON tenants(slug);
CREATE UNIQUE INDEX idx_tenants_realm ON tenants(realm_name);
CREATE UNIQUE INDEX idx_tenants_domain ON tenants(custom_domain) WHERE custom_domain IS NOT NULL;
CREATE INDEX idx_tenants_status ON tenants(status);

-- Users
CREATE UNIQUE INDEX idx_users_tenant_keycloak ON users(tenant_id, keycloak_id);
CREATE UNIQUE INDEX idx_users_tenant_email ON users(tenant_id, email);
CREATE INDEX idx_users_tenant_status ON users(tenant_id, status);
CREATE INDEX idx_users_tenant_org ON users(tenant_id, organization_id);
CREATE INDEX idx_users_last_login ON users(tenant_id, last_login_at DESC);

-- Organizations
CREATE UNIQUE INDEX idx_orgs_tenant_keycloak ON organizations(tenant_id, keycloak_org_id);
CREATE INDEX idx_orgs_tenant_active ON organizations(tenant_id, is_active) WHERE is_active = true;

-- Roles
CREATE UNIQUE INDEX idx_roles_tenant_code ON roles(tenant_id, code);
CREATE INDEX idx_roles_system ON roles(is_system) WHERE is_system = true;

-- Tenant Modules
CREATE UNIQUE INDEX idx_tenant_modules_unique ON tenant_modules(tenant_id, module_id);

Tablas de Soporte

sessions (Redis — no en PostgreSQL)

Las sesiones se almacenan en Redis/Valkey, no en la base de datos relacional:

Key: session:{session_id}
Value: {
  "user_id": "uuid",
  "tenant_id": "uuid",
  "access_token": "encrypted:...",
  "refresh_token": "encrypted:...",
  "id_token": "encrypted:...",
  "expires_at": 1716134400,
  "created_at": 1716048000
}
TTL: 1800 (30 minutos, configurable por tenant)

idempotency_keys

sql
CREATE TABLE idempotency_keys (
    key VARCHAR(255) NOT NULL,
    tenant_id UUID NOT NULL,
    response_status INT NOT NULL,
    response_body JSONB NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    expires_at TIMESTAMPTZ NOT NULL DEFAULT NOW() + INTERVAL '24 hours',
    PRIMARY KEY (key, tenant_id)
);

Reimagine Tech LLC — Documentacion Interna