# Plan: Etapa 3 — Enforcement de Permisos

## Contexto

Ro-ut v2.x tiene el campo `permissions` en el modelo User guardado y retornado por GraphQL, pero **nunca se verifica** antes de ejecutar operaciones. Cualquier usuario autenticado puede realizar cualquier acción (crear, editar, eliminar usuarios, empresas, oficinas).

El campo `req.usuario` actualmente contiene solo el **user ID** (string), no el objeto completo del usuario con sus permisos.

## Plan de Implementación

### Paso 1: Mejorar `userExtractor` para incluir el usuario completo

**Archivo:** `src/server/middlewares/userExtractor.ts`

Actualmente `userExtractor` solo extrae el JWT y setea `req.usuario = usuario` (el ID). Necesitamos:

1. Hacer `userExtractor` asíncrono (ya lo es — `await userExtractor(...)`)
2. Después de decodificar el JWT, hacer un query a la DB para obtener el usuario completo con su campo `permissions`
3. Setear `req.usuario` como el objeto completo del usuario (no solo el ID)

```typescript
// Después de jwt.verify:
const { rows } = await db.query(
  `SELECT "Id", "user", "email", "name", "last_names", "type", "permissions"
   FROM "User" WHERE "Id" = $1`,
  [parseInt(decoded.sub)]
);
if (rows.length === 0) throw new Error('Usuario no encontrado');
req.usuario = rows[0]; // objeto completo con permissions (JSON string)
```

Esto requiere importar el connection DB en el middleware (similar a como lo hacen los models).

### Paso 2: Crear función de verificación de permisos

**Archivo nuevo:** `src/server/middlewares/permissionCheck.ts`

```typescript
export const checkPermission = (
  usuario: any,
  requiredPermission: string,
  action: string
): void => {
  const perm = usuario?.permissions?.adminUser;
  if (!perm || !perm[requiredPermission]) {
    throw new GraphQLError(
      `Permiso requerido: ${action}. Contacte a un administrador.`
    );
  }
};
```

### Paso 3: Agregar permisos de Company y Office al schema

**Archivo:** `src/server/GraphQL/schema/user.schema.ts`

Extender `AdminUser` para incluir permisos de Company y Office:

```typescript
@ObjectType('CompanyPermissions')
@InputType('InputCompanyPermissions')
export class CompanyPermissions {
    @Field() ViewCompany?: boolean;
    @Field() CreateCompany?: boolean;
    @Field() EditCompany?: boolean;
    @Field() DeleteCompany?: boolean;
}

@ObjectType('OfficePermissions')
@InputType('InputOfficePermissions')
export class OfficePermissions {
    @Field() ViewOffice?: boolean;
    @Field() CreateOffice?: boolean;
    @Field() EditOffice?: boolean;
    @Field() DeleteOffice?: boolean;
}

// Extender AdminUser:
@ObjectType('AdminUser')
@InputType('InputAdminUser')
export class AdminUser {
    @Field() ViewUser?: boolean;
    @Field() CreateUser?: boolean;
    @Field() EditUser?: boolean;
    @Field() DeleteUser?: boolean;
    @Field()
    companyPermissions?: CompanyPermissions;
    @Field()
    officePermissions?: OfficePermissions;
}
```

### Paso 4: Agregar verificación de permisos en el schema layer

Cada campo en los schema types (UsersQuery, UserMutation, CompaniesQuery, CompanyMutation, OfficesQuery, OfficeMutation) ya llama `userExtractor`. Después de eso, agregar verificación de permisos:

**`src/server/GraphQL/schema/user.schema.ts`** — en cada campo de `UsersQuery` y `UserMutation`:

```typescript
// Ejemplo para users query:
@Field(() => [User], { nullable: true })
async users(@Arg('limit') limit: number, @Arg('cursor') cursor: number, @Ctx() context: { req: any; res: any; }) {
    await userExtractor({req: context.req, res: context.res, isGraphQL: true});
    checkPermission(context.req.usuario, 'ViewUser', 'ViewUser'); // lanzar si no tiene
    return getUsersController({ args: { limit, cursor } });
}
```

Aplicar patrón similar para:
- `user()` query → `ViewUser`
- `users()` query → `ViewUser`
- `insertUser()` mutation → `CreateUser`
- `editUser()` mutation → `EditUser`
- `deleteUser()` mutation → `DeleteUser`

**`src/server/GraphQL/schema/company.schema.ts`** — en cada campo de `CompaniesQuery` y `CompanyMutation`:

```typescript
// getCompany/getCompanies → ViewCompany
// insertCompany → CreateCompany
// editCompany → EditCompany
// deleteCompany → DeleteCompany
```

**`src/server/GraphQL/schema/office.schema.ts`** — en cada campo de `OfficesQuery` y `OfficeMutation`:

```typescript
// getOffice/getOffices/officesByCompany → ViewOffice
// insertOffice → CreateOffice
// editOffice → EditOffice
// deleteOffice → DeleteOffice
```

### Paso 5: Actualizar tests

Los tests actuales esperan 200 OK siempre. Necesitamos:

1. **Tests de permisos positivos**: crear un usuario con permisos limitados y verificar que puede hacer lo que tiene permiso
2. **Tests de permisos negativos**: crear un usuario sin permisos y verificar que recibe error GraphQL

**Archivos a actualizar:**
- `src/server/tests/server/user/index.test.ts` — agregar tests de permisos
- `src/server/tests/server/company/index.test.ts` — agregar tests de permisos
- `src/server/tests/server/office/index.test.ts` — agregar tests de permisos

**Estrategia de tests:**
- Crear un usuario de prueba con `ViewUser: true` pero `CreateUser: false`
- Verificar que `users()` funciona (200)
- Verificar que `insertUser()` falla (GraphQL error)
- Lo mismo para Company y Office

### Paso 6: Actualizar seed de tests

El test de insertUser crea un admin con todos los permisos. Asegurar que el seed de tests tenga un usuario admin con todos los permisos habilitados para que los tests existentes sigan funcionando.

## Archivos Modificados

| Archivo | Acción |
|---------|--------|
| `src/server/middlewares/userExtractor.ts` | Modificar — fetch user from DB after JWT decode |
| `src/server/middlewares/permissionCheck.ts` | **Nuevo** — función checkPermission |
| `src/server/GraphQL/schema/user.schema.ts` | Modificar — extender AdminUser, agregar CompanyPermissions, OfficePermissions, agregar checks |
| `src/server/GraphQL/schema/company.schema.ts` | Modificar — agregar checks de permisos |
| `src/server/GraphQL/schema/office.schema.ts` | Modificar — agregar checks de permisos |
| `src/server/tests/server/user/index.test.ts` | Modificar — agregar tests de permisos |
| `src/server/tests/server/company/index.test.ts` | Modificar — agregar tests de permisos |
| `src/server/tests/server/office/index.test.ts` | Modificar — agregar tests de permisos |

## Ejecución

Este plan será ejecutado en background por un agente usando la skill `agent-orchestrator`.

## Verificación

1. `npm run lint` — sin errores
2. `npm run build` — compila sin errores
3. `npm run test:backend` — tests existentes pasan (admin tiene todos los permisos) + nuevos tests de permisos pasan
4. Verificar que un usuario sin permisos recibe un GraphQL error descriptivo al intentar operaciones restringidas
