Skip to content

Migracion: portal_users

Informacion

  • Database: {tenant} (cada DB de tenant)
  • Schema: Dinamico — mismo schema que ordcon
  • Level: LEVEL_SUCURSAL o LEVEL_EMPRESA segun configuracion del tenant

Proposito

Tabla de credenciales y seguridad para usuarios del portal. Vincula un cliente existente en ordcon con su cuenta de acceso.

Regla de Nivel de Migracion

La migracion debe ejecutarse en el mismo nivel que ordcon para el tenant:

  • Si ordcon esta configurado como LEVEL_SUCURSAL (por defecto): La migracion corre en cada schema sucXXXX, creando una tabla portal_users por sucursal.
  • Si ordcon esta configurado como LEVEL_EMPRESA: La migracion corre solo en public, creando una unica tabla portal_users compartida.

La migracion debe consultar configuracion_niveles_tablas para determinar el nivel de ordcon y aplicar el mismo nivel a portal_users.

php
public function getLevel(): array
{
    // Respetar el mismo nivel que ordcon
    $ordconLevel = $this->getTableLevel('ordcon');
    return [$ordconLevel]; // LEVEL_SUCURSAL o LEVEL_EMPRESA
}

Definicion DDL

sql
CREATE TABLE portal_users (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    cliente_id      INTEGER NOT NULL,
    dni_cuit        VARCHAR(20) NOT NULL,
    email           VARCHAR(255) NOT NULL,
    password_hash   VARCHAR(255) NOT NULL,
    last_login      TIMESTAMP NULL,
    refresh_token   VARCHAR(255) NULL,
    refresh_token_expires TIMESTAMP NULL,
    failed_login_attempts INTEGER NOT NULL DEFAULT 0,
    locked_until    TIMESTAMP NULL,
    created_at      TIMESTAMP NOT NULL DEFAULT NOW(),
    updated_at      TIMESTAMP NOT NULL DEFAULT NOW(),

    CONSTRAINT fk_portal_users_ordcon
        FOREIGN KEY (cliente_id)
        REFERENCES ordcon(cnro)
        ON DELETE CASCADE
);

Indices

sql
-- Unicidad de DNI/CUIT dentro del schema (credencial de login)
CREATE UNIQUE INDEX uq_portal_users_dni_cuit
    ON portal_users (dni_cuit);

-- Busqueda por cliente vinculado
CREATE INDEX idx_portal_users_cliente_id
    ON portal_users (cliente_id);

-- Busqueda por email (password reset)
CREATE INDEX idx_portal_users_email
    ON portal_users (email);

-- Unicidad de refresh token (lookup rapido + una sesion por usuario)
CREATE UNIQUE INDEX uq_portal_users_refresh_token
    ON portal_users (refresh_token)
    WHERE refresh_token IS NOT NULL;

Campos

CampoTipoRestriccionesDescripcion
idUUIDPK, default gen_random_uuid()Identificador unico — usado como portal_user_id en JWT
cliente_idintegerFK → ordcon.cnro, NOT NULLVinculacion con cliente existente del ERP
dni_cuitvarchar(20)UNIQUE, NOT NULLDNI o CUIT del usuario (credencial de login)
emailvarchar(255)NOT NULLEmail del usuario (para password reset)
password_hashvarchar(255)NOT NULLHash bcrypt del password
refresh_tokenvarchar(255)UNIQUE (parcial), nullableUUID del refresh token activo
refresh_token_expirestimestampnullableExpiracion del refresh token
last_logintimestampnullableTimestamp del ultimo login exitoso
failed_login_attemptsintegerNOT NULL, default 0Contador de intentos fallidos consecutivos
locked_untiltimestampnullableSi no es null y es futuro, la cuenta esta bloqueada
created_attimestampNOT NULL, default now()Fecha de creacion del registro
updated_attimestampNOT NULL, default now()Ultima modificacion

Restricciones

  • FK a ordcon: cliente_id referencia ordcon.cnro con CASCADE. Si se elimina el cliente del ERP, se elimina su acceso al portal.
  • UNIQUE dni_cuit: Solo un usuario del portal por DNI/CUIT dentro del schema. Esto es natural porque un DNI/CUIT identifica a una persona fisica/juridica unica.
  • password_hash: Almacena hash bcrypt (no texto plano). El campo es NOT NULL porque el portal usa autenticacion con password obligatorio.

Consideraciones de Seguridad

  • failed_login_attempts se incrementa en cada login fallido y se resetea a 0 en cada login exitoso
  • locked_until se establece a NOW() + 15 minutos cuando failed_login_attempts alcanza 5
  • La aplicacion debe verificar locked_until antes de intentar validar el password

Rollback

sql
DROP TABLE IF EXISTS portal_users CASCADE;