publicUserIdentity

Overview

services/api/src/utils/publicUserIdentity.js centraliza la lógica de identidad pública para usuarios anonimizados. Su objetivo es devolver una versión segura de un User cuando la cuenta está en un estado que ya no debe mostrarse con identidad real.

Este módulo existe para evitar duplicar la misma regla en varios lugares del backend. El comentario del archivo indica que se reutiliza en cualquier endpoint que serialice un User poblado dentro de otro documento, como comentarios de listings o eventos de actividad.

[!IMPORTANT] accountStatus se usa solo para decidir si se enmascara la identidad. El campo no se expone en la salida.

How it works

El módulo trabaja con dos decisiones simples:

  1. Detecta si el estado de la cuenta pertenece a la lista de estados anonimizados.
  2. Si corresponde, devuelve una identidad pública enmascarada.
  3. Si no corresponde, devuelve los campos públicos del usuario, sin accountStatus.

La función principal también acepta forceMask. Ese parámetro permite enmascarar aunque el User no exista en el populate, o aunque el estado final todavía no esté reflejado en el documento fuente.

flowchart TD
    A[User document] --> B{forceMask}
    B -->|true| C[Masked identity]
    B -->|false| D{Has userDoc}
    D -->|no| E[null]
    D -->|yes| F[Read accountStatus]
    F --> G{Anonymized status}
    G -->|yes| C
    G -->|no| H[Public fields without accountStatus]

Key components

{
    "title": "ANONYMIZED_ACCOUNT_STATUSES",
    "content": "Set con los estados de cuenta que activan el enmascarado de identidad. Incluye \"anonymized_pending_purge\" y \"purged\".",
    "type": "function",
    "filePath": "services/api/src/utils/publicUserIdentity.js",
    "badges": [
        "public"
    ]
}
{
    "title": "MASKED_IDENTITY",
    "content": "Objeto inmutable con la identidad p\u00fablica est\u00e1ndar para usuarios anonimizados. Define los valores que se devuelven cuando la cuenta debe ocultarse.",
    "type": "function",
    "filePath": "services/api/src/utils/publicUserIdentity.js",
    "badges": [
        "public"
    ]
}
{
    "title": "isAnonymizedAccountStatus",
    "content": "Funci\u00f3n p\u00fablica que comprueba si un estado de cuenta pertenece al conjunto de estados anonimizados. Devuelve un booleano.",
    "type": "function",
    "filePath": "services/api/src/utils/publicUserIdentity.js",
    "badges": [
        "public"
    ]
}
{
    "title": "maskAnonymizedUserIdentity",
    "content": "Funci\u00f3n p\u00fablica que devuelve una versi\u00f3n segura del usuario. Enmascara la identidad cuando `forceMask` es verdadero o cuando `accountStatus` indica un usuario anonimizado.",
    "type": "function",
    "filePath": "services/api/src/utils/publicUserIdentity.js",
    "badges": [
        "public"
    ]
}

Data & fields

[
    {
        "name": "ANONYMIZED_ACCOUNT_STATUSES",
        "type": "Set<string>",
        "required": true,
        "description": "Conjunto de estados de cuenta que activan el enmascarado.",
        "children": [
            {
                "name": "anonymized_pending_purge",
                "type": "string",
                "required": true,
                "description": "Estado intermedio de una cuenta que ya debe mostrarse como anonimizada."
            },
            {
                "name": "purged",
                "type": "string",
                "required": true,
                "description": "Estado final de una cuenta eliminada o purgada."
            }
        ]
    },
    {
        "name": "MASKED_IDENTITY",
        "type": "object",
        "required": true,
        "description": "Plantilla de identidad pública usada cuando la cuenta debe ocultarse.",
        "children": [
            {
                "name": "_id",
                "type": "null",
                "required": true,
                "description": "Identificador vacío para evitar exponer el ID real."
            },
            {
                "name": "name",
                "type": "string",
                "required": true,
                "description": "Nombre visible para usuarios anonimizados."
            },
            {
                "name": "username",
                "type": "null",
                "required": true,
                "description": "Nombre de usuario oculto."
            },
            {
                "name": "photo",
                "type": "string",
                "required": true,
                "description": "Foto vacía en la identidad enmascarada."
            },
            {
                "name": "isAnonymized",
                "type": "boolean",
                "required": true,
                "description": "Marca explícita de que la identidad fue anonimizada."
            }
        ]
    }
]

MASKED_IDENTITY shape

El objeto enmascarado tiene una forma fija y predecible. El módulo lo crea con Object.freeze, así que no puede modificarse accidentalmente en tiempo de ejecución.

{
  "_id": null,
  "name": "Usuario anonimizado",
  "username": null,
  "photo": "",
  "isAnonymized": true
}

Behaviors

  • isAnonymizedAccountStatus(accountStatus) — devuelve true cuando accountStatus pertenece a ANONYMIZED_ACCOUNT_STATUSES.
  • maskAnonymizedUserIdentity(userDoc, { forceMask = false } = {}) — devuelve MASKED_IDENTITY, null o los campos públicos del usuario, según el estado recibido.

isAnonymizedAccountStatus

Esta función consulta el Set definido en el módulo. Su salida es determinista y solo depende del valor recibido.

maskAnonymizedUserIdentity

Esta función implementa la serialización pública del usuario. Su flujo es el siguiente:

  • Si forceMask es true, devuelve una copia de MASKED_IDENTITY.
  • Si userDoc no existe, devuelve null.
  • Si accountStatus es un estado anonimizado, devuelve una copia de MASKED_IDENTITY.
  • En cualquier otro caso, devuelve los campos públicos del usuario sin accountStatus.

[!NOTE] La función espera un userDoc resultado de un populate() en modo lean. El comentario del archivo indica que ese documento debe incluir accountStatus en la selección.

Workflows

  1. Recibir usuario poblado | El código recibe un userDoc ya serializable desde un populate().
  2. Evaluar forzado | Si forceMask está activo, la función enmascara de inmediato.
  3. Revisar estado | Si existe userDoc, se evalúa accountStatus.
  4. Decidir salida | La función devuelve la identidad enmascarada, null o los campos públicos.

Gotchas & edge cases

[!WARNING] Si el populate() no incluye accountStatus, la decisión de anonimización no puede detectarse desde este módulo. El comentario del archivo indica que ese campo debe venir seleccionado.

[!CAUTION] Cuando forceMask es true, la función devuelve identidad enmascarada incluso si el usuario no llegó en el documento o si el estado todavía no está sincronizado con el estado final.

[!NOTE] La salida pública nunca conserva accountStatus. Si el usuario no está anoninimizado, el módulo devuelve el resto de campos públicos tal como vienen en userDoc.

Este utilitario está pensado para cualquier ruta o serializador del backend que inserte un User dentro de otro documento. El propio archivo cita comentarios de listings y eventos de actividad como ejemplos de uso.

Su papel es coherente con una capa de serialización determinista: la decisión de ocultar identidad se toma aquí, no en cada módulo consumidor.