# Aprovisionamiento SCIM

> El endpoint SCIM 2.0 que un proveedor de identidad llama para aprovisionar personas y grupos en una organización de Probara, con sus filtros, reglas de PATCH, formas de rol, errores y límites.

Probara sirve SCIM 2.0 (RFC 7643 y RFC 7644) en `https://app.probara.net/scim/v2`. Un proveedor de identidad lo usa para crear, actualizar, desactivar y eliminar a las personas de una organización, y para enviar los grupos de esa organización. Esta página es la referencia del endpoint. Para conectar un proveedor desde la app, lee la [guía de aprovisionamiento SCIM](https://docs.probara.net/es/guides/scim-provisioning/).

SCIM es una función del plan Enterprise. En cualquier otro plan, cada petición con un token válido responde `403` con el detalle `SCIM requires the Enterprise plan.`

## Ruta rápida

1. Crea un token en la página SCIM del espacio de trabajo y copia su secreto. Probara lo muestra una sola vez.
2. Envía `Authorization: Bearer <token>` en cada petición a `https://app.probara.net/scim/v2`.
3. Comprueba la conexión con `GET /ServiceProviderConfig`, que responde `200` con `application/scim+json`, y con `GET /Users?count=1`.

## URL base y autenticación

| Tema                      | Comportamiento                                                                                                                                                                                                            |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| URL base                  | `https://app.probara.net/scim/v2`, la misma para todas las organizaciones. Solo el token decide la organización. No se lee ningún identificador de organización de la URL ni de una cabecera.                             |
| Token                     | Empieza por `probara_scim_`, seguido de 43 caracteres base64url. Se crea en la página SCIM del espacio de trabajo, se muestra una vez y vale como máximo 365 días. Una organización tiene como máximo dos tokens activos. |
| Cabecera                  | `Authorization: Bearer <token>`. Cualquier otro esquema se rechaza.                                                                                                                                                       |
| Alcance                   | Un token SCIM solo funciona en `/scim/v2`. `/api/v1` lo rechaza.                                                                                                                                                          |
| Rechazo                   | `401` con `WWW-Authenticate: Bearer`. El detalle es el mismo si el token falta, es desconocido, está revocado o ha caducado, así que nunca indica qué comprobación falló.                                                 |
| Estado de la organización | Mientras una organización está suspendida o programada para eliminación, cada escritura responde `403`. Las lecturas siguen abiertas.                                                                                     |

## Endpoints

| Método y ruta                                                | Respuesta                                                                                                                                 |
| ------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `GET /ServiceProviderConfig`                                 | Capacidades. Patch y filtros están soportados, con hasta 100 resultados por página. Bulk, cambio de contraseña, orden y ETag no lo están. |
| `GET /ResourceTypes`, `GET /ResourceTypes/{id}`              | Los tipos de recurso User y Group. Cualquier otro id responde `404`.                                                                      |
| `GET /Schemas`, `GET /Schemas/{urn}`                         | El esquema User, sus extensiones enterprise y de Probara, y el esquema Group. Cualquier otro URN responde `404`.                          |
| `GET /Users`, `POST /Users`                                  | Listar o crear personas. Una creación responde `201` con cabecera `Location`.                                                             |
| `GET /Users/{id}`, `PUT /Users/{id}`, `PATCH /Users/{id}`    | Leer, reemplazar o parchear una persona.                                                                                                  |
| `DELETE /Users/{id}`                                         | Quita la membresía de la persona, si tiene una, y después el registro SCIM. Responde `204` sin cuerpo.                                    |
| `GET /Groups`, `POST /Groups`                                | Listar o crear grupos. Una creación responde `201` con cabecera `Location`.                                                               |
| `GET /Groups/{id}`, `PUT /Groups/{id}`, `PATCH /Groups/{id}` | Leer, reemplazar o parchear un grupo.                                                                                                     |
| `DELETE /Groups/{id}`                                        | Elimina el grupo y sus accesos. Responde `204` sin cuerpo.                                                                                |
| `/Bulk`, `/Me`, `/.search`                                   | Responden `501`.                                                                                                                          |

Una barra final nombra la misma ruta. Un método que la ruta no acepta responde `405` con una cabecera `Allow` que lista los métodos aceptados. Cualquier otra ruta responde `404`.

## Filtros

| Recurso  | Filtro                                | Coincidencia              |
| -------- | ------------------------------------- | ------------------------- |
| Usuarios | `userName eq "…"`                     | Sin distinguir mayúsculas |
| Usuarios | `externalId eq "…"`                   | Exacta                    |
| Usuarios | `emails[type eq "work"].value eq "…"` | Sin distinguir mayúsculas |
| Grupos   | `displayName eq "…"`                  | Sin distinguir mayúsculas |
| Grupos   | `externalId eq "…"`                   | Exacta                    |

Las condiciones se unen con `and`. Los nombres de atributo y `eq` no distinguen mayúsculas. Dentro de un valor, `\"` representa una comilla y `\\` una barra invertida. Cualquier otro filtro responde `400` con `scimType` `invalidFilter`. Un filtro sin coincidencias responde `200` con `totalResults` en `0` y un arreglo `Resources` vacío, nunca `404`.

## Paginación y atributos

| Parámetro            | Comportamiento                                                                                                                                                         |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `startIndex`         | Empieza en 1. Un valor menor que 1 cuenta como 1. Un valor que no es un número entero responde `400` con `scimType` `invalidValue`.                                    |
| `count`              | Hasta 100, y 100 si se omite. Un valor mayor cuenta como 100, y uno negativo como 0. Un valor que no es un número entero responde `400` con `scimType` `invalidValue`. |
| Orden                | Del más antiguo al más reciente, por fecha de creación y después por id. El orden es el mismo en todas las páginas.                                                    |
| `attributes`         | Devuelve solo los atributos indicados.                                                                                                                                 |
| `excludedAttributes` | Quita los atributos indicados. Probara admite `excludedAttributes=members` en los grupos.                                                                              |

`schemas`, `id` y `meta` siempre se devuelven. Un URN con un subatributo, como `urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:department`, selecciona solo ese subatributo. `totalResults` cuenta todas las coincidencias, e `itemsPerPage` cuenta los recursos de la página.

## Usuarios

### Atributos

| Atributo                                                                                    | Comportamiento                                                                                                                                                     |
| ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `userName`                                                                                  | Obligatorio al crear y al reemplazar. Único por organización, con comparación sin distinguir mayúsculas.                                                           |
| `emails` (tipo `work`)                                                                      | La dirección de trabajo, que debe ser una dirección de correo. Si no hay dirección de trabajo, `userName` debe ser una dirección de correo y sirve como dirección. |
| `active`                                                                                    | Un booleano, o la cadena `"True"` o `"False"` en cualquier combinación de mayúsculas. Vale `true` por defecto al crear.                                            |
| `externalId`, `displayName`, `title`, `name.givenName`, `name.familyName`, `name.formatted` | Se guardan por organización. El perfil global de la persona nunca cambia.                                                                                          |
| `department` y `employeeNumber` de la extensión enterprise                                  | Se guardan por organización.                                                                                                                                       |
| `role` de Probara y `roles` de Entra                                                        | Valores de rol. Consulta [Roles](#roles).                                                                                                                          |
| `password`                                                                                  | Se ignora. Probara no sincroniza contraseñas.                                                                                                                      |
| `groups`                                                                                    | Se ignora en el cuerpo de un usuario. La pertenencia a grupos se envía por `/Groups`.                                                                              |

### Crear y reemplazar

`POST` y `PUT` reciben a la persona completa. `PUT` borra cada atributo que el cuerpo omite.

Un `POST` para una dirección que ya es miembro de la organización adopta esa membresía en el nuevo registro, así que nadie se añade dos veces. Un `POST` cuyo `userName` o dirección de trabajo ya pertenece a otro registro de la organización responde `409` con `scimType` `uniqueness`.

Una persona de uno de los dominios verificados de la organización recibe una cuenta y una membresía en el mismo cambio, con el rol que indica [Roles](#roles). Una persona de fuera de esos dominios recibe una invitación, y su registro queda sin cuenta hasta que la acepte.

### Patch

`PATCH` recibe un arreglo `Operations`. `op` es `add`, `replace` o `remove`, en cualquier combinación de mayúsculas. Las operaciones se aplican en orden, y una sola operación rechazada rechaza la petición entera.

| Ruta                                                                                                    | Comportamiento                                                                                           |
| ------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `active`                                                                                                | Cambia el estado. La desactivación y la reactivación se describen más abajo.                             |
| `userName`, `externalId`, `displayName`, `title`, `name.givenName`, `name.familyName`, `name.formatted` | Asignan el valor. `remove` lo borra, salvo `userName`, que responde `400` con `scimType` `invalidValue`. |
| `emails[type eq "work"].value`, `emails`                                                                | Asignan la dirección de trabajo.                                                                         |
| `department`, `employeeNumber` y sus formas URN de extensión                                            | Asignan el valor.                                                                                        |
| Las rutas de rol descritas en [Roles](#roles)                                                           | Asignan el rol.                                                                                          |
| `emails[type eq "home"]`, `password`, `groups`                                                          | Se ignoran.                                                                                              |
| Cualquier otra ruta                                                                                     | Responde `400` con `scimType` `invalidPath`.                                                             |

Un `replace` sin `path` recibe un objeto de atributos, la forma de Okta. Sus claves se leen como rutas de atributo. Si el objeto incluye el propio recurso, se omiten su `id`, `schemas` y `meta`, que no cambian nada, tanto en usuarios como en grupos.

### Cambios de dirección

La dirección de una persona es su correo de trabajo, o `userName` cuando no hay correo de trabajo. Solo se permite cambiarla si la dirección anterior y la nueva están en dominios verificados de la organización y la cuenta no pertenece a ninguna otra organización. Si no, la respuesta es `400` con `scimType` `mutability` y un motivo. Un cambio solo de `userName`, mientras un correo de trabajo sostiene la dirección, cambia únicamente la etiqueta.

### Desactivación, reactivación y eliminación

| Cambio                               | Efecto                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `active` a `false` (`PATCH` o `PUT`) | La membresía se conserva, desactivada: la siguiente petición de la persona a la organización se rechaza, y el puesto queda libre. Su rol, su acceso a proyectos y sus grupos se conservan. Terminan las sesiones creadas mediante la conexión de inicio de sesión único de la organización. Se revocan los tokens API personales y los permisos OAuth y MCP de la organización. Las asignaciones abiertas de ejecuciones y las solicitudes de revisión pendientes quedan sin asignar. El registro SCIM se conserva, inactivo, y sigue apareciendo en `GET` y en los filtros. |
| `active` a `true`                    | Vuelve el mismo `id`. La membresía vuelve a estar activa con su rol, el que den sus grupos, su atributo o el rol por defecto, tras una comprobación de puesto. Con **Gestionado en Probara** el rol se conserva. Sus grupos recordados se restauran. Los tokens y permisos revocados no se restauran.                                                                                                                                                                                                                                                                        |
| `DELETE`                             | Se quita la membresía: el puesto queda libre, el acceso de la persona termina, y sus grupos y su trabajo abierto salen con ella. Después se elimina el registro SCIM. La cuenta se conserva. Después, el `id` responde `404`.                                                                                                                                                                                                                                                                                                                                                |

En todos los casos la cuenta se conserva. Los tokens de aplicación de CI de la organización no se ven afectados. Los casos, suites, ejecuciones, resultados, defectos, comentarios y revisiones que creó la persona se conservan y siguen asociados a ella.

El propietario de una organización no puede ser desaprovisionado. Una desactivación o un `DELETE` del propietario responde `400` con el detalle `The organization owner can't be deprovisioned by SCIM. Transfer ownership first.` y no escribe nada.

## Grupos

Cada grupo que envía el proveedor de identidad se convierte en un grupo de usuarios de Probara que gestiona ese proveedor.

- **Nombre y miembros** los envía el proveedor de identidad. En Probara, la descripción del grupo y sus permisos de proyecto, de paneles y de consultas guardadas siguen siendo editables. Renombrar, eliminar o cambiar los miembros del grupo en Probara responde `409`.
- **Miembros** se aplican solo cuando tienen una cuenta vinculada y están activos en la organización. Los demás quedan recordados, y se unen al grupo de Probara cuando se activan.
- **Choques de nombre.** Un nombre de grupo que coincide con un grupo creado en Probara, sin distinguir mayúsculas, responde `409` con `scimType` `uniqueness`. No se escribe nada.
- **`PUT`** reemplaza el nombre, el `externalId` y la lista completa de miembros.
- **`PATCH`** con `add` o `remove` cambia solo a las personas que nombra. Un `replace` sobre `members` fija la lista completa. Quitar a alguien que no es miembro no hace nada.

| Operación con miembros    | Forma                                                                      |
| ------------------------- | -------------------------------------------------------------------------- |
| Quitar un miembro         | `remove` con la ruta `members[value eq "<id>"]`                            |
| Quitar varios miembros    | `remove` con la ruta `members` y un arreglo de objetos `{"value": "<id>"}` |
| Quitar todos los miembros | `remove` con la ruta `members` y sin valor                                 |
| Reemplazar la lista       | `replace` con la ruta `members` y un arreglo de valores                    |

Un valor de miembro que no es un `id` de Users de esta organización responde `400` con `scimType` `invalidValue`.

`DELETE` elimina el grupo de Probara y sus permisos de proyecto, de paneles y de consultas guardadas. La asignación de roles a los grupos no forma parte de este recurso: se configura en los ajustes SCIM del espacio de trabajo.

## Roles

La fuente de roles de la organización decide qué datos del proveedor de identidad fijan el rol de una persona.

| Fuente de roles        | El rol viene de                                                                                                                                                     | Cuando una persona no tiene valor     |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------- |
| `groups` (por defecto) | El grupo asignado con el número de prioridad más bajo. Entre los grupos que comparten ese número, gana el rol que concede más permisos.                             | El rol por defecto de la organización |
| `attribute`            | El valor `role` de la extensión de Probara, o los valores `roles` de Entra. Varios valores resuelven al rol que concede más permisos, y un empate va al slug menor. | El rol por defecto de la organización |
| `probara`              | Nada. SCIM fija el rol al aprovisionar a la persona, y las actualizaciones posteriores no lo cambian.                                                               | No aplica                             |

El rol por defecto es el que la política de inicio de sesión único de la organización da a los nuevos miembros. Es Lector hasta que lo cambies.

### Formas de rol

Con la fuente `attribute`, un rol llega en una de estas formas, en `POST`, `PUT` y `PATCH`:

```json
{
  "schemas": [
    "urn:ietf:params:scim:schemas:core:2.0:User",
    "urn:ietf:params:scim:schemas:extension:probara:2.0:User"
  ],
  "userName": "ada@example.com",
  "urn:ietf:params:scim:schemas:extension:probara:2.0:User": { "role": "member" }
}
```

```json
{ "roles": [{ "value": "member" }] }
```

Entra también puede enviar `roles` como un objeto de rol de aplicación codificado en JSON, como una cadena JSON del arreglo, o como una cadena simple. Cada forma resuelve al `value` de rol que contiene.

| Regla            | Comportamiento                                                                                              |
| ---------------- | ----------------------------------------------------------------------------------------------------------- |
| Valor            | Un slug de rol de la organización: `admin`, `member`, `viewer`, o el identificador de un rol personalizado. |
| `owner`          | Nunca se asigna. Con `attribute` responde `400` con `scimType` `invalidValue`.                              |
| Slug desconocido | Con `attribute` responde `400` con `scimType` `invalidValue`.                                               |
| Otras fuentes    | Con `groups` y `probara`, un valor de rol no cambia ningún rol y nunca rechaza la petición.                 |

En `PATCH`, la ruta de Probara es `urn:ietf:params:scim:schemas:extension:probara:2.0:User:role`, y `roles` es la ruta de Entra. `add` añade al rol guardado, `remove` lo borra y `replace` lo fija. Un filtro de valor sobre `roles` responde `400` con `scimType` `invalidPath`.

## Errores

Cada error usa el esquema de error SCIM, con `status` como cadena:

```json
{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"],
  "status": "409",
  "scimType": "uniqueness",
  "detail": "A user with this userName already exists in this organization."
}
```

Los textos `detail` se devuelven en inglés, tal como los produce la API. Compáralos sin traducir. El `detail` está escrito para el administrador, y un proveedor de identidad puede mostrarlo en su registro. `scimType` aparece solo donde la tabla lo indica.

| Estado | Cuándo                                                                                                            | `scimType`                                                                                                            |
| ------ | ----------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `400`  | Un cuerpo, filtro, ruta o valor mal formado, una negativa por ser propietario, o un cambio de dirección rechazado | `invalidSyntax`, `invalidFilter`, `invalidPath`, `invalidValue`, `noTarget` o `mutability`, cuando la tabla lo indica |
| `401`  | Un token ausente, desconocido, revocado o caducado                                                                | Ninguno                                                                                                               |
| `403`  | Falta el plan Enterprise, la organización está suspendida o programada para eliminación, o no hay puesto libre    | Ninguno                                                                                                               |
| `404`  | Un id, tipo de recurso, esquema o ruta desconocidos                                                               | Ninguno                                                                                                               |
| `405`  | Un método que la ruta no acepta. La cabecera `Allow` lista los métodos aceptados.                                 | Ninguno                                                                                                               |
| `409`  | Un `userName`, correo o nombre de grupo duplicado, o un nombre de grupo que ya tiene un grupo creado en Probara   | `uniqueness`                                                                                                          |
| `413`  | Un cuerpo mayor de 1 MB                                                                                           | Ninguno                                                                                                               |
| `415`  | Un cuerpo enviado con un tipo de medio distinto de `application/scim+json` o `application/json`                   | Ninguno                                                                                                               |
| `429`  | El token alcanzó su límite de peticiones. La cabecera `Retry-After` indica la espera en segundos.                 | Ninguno                                                                                                               |
| `500`  | Un fallo inesperado. Vuelve a enviar la petición.                                                                 | Ninguno                                                                                                               |
| `501`  | `/Bulk`, `/Me` o `/.search`                                                                                       | Ninguno                                                                                                               |

Las respuestas usan `application/scim+json; charset=utf-8`. Una petición con cuerpo debe usar `application/scim+json` o `application/json`, con o sin parámetros.

## Límites

| Límite                | Valor                                                                                                                  |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| Cuerpo de la petición | 1 MB. Un cuerpo mayor responde `413`.                                                                                  |
| Peticiones por token  | 250 cada 10 segundos, es decir, 25 por segundo. Por encima de ese límite, la respuesta es `429` con `Retry-After: 10`. |
| Tamaño de página      | Hasta 100 recursos por página                                                                                          |
| Caducidad del token   | Obligatoria, como máximo 365 días                                                                                      |
| Tokens activos        | Dos por organización                                                                                                   |

## Lista de comprobación

- [ ] `GET /ServiceProviderConfig` responde `200` con tu token.
- [ ] `POST /Users` responde `201` con cabecera `Location`, y una segunda creación con el mismo `userName` responde `409`.
- [ ] Un `PATCH` que pone `active` en `false` responde `200` y deja el registro visible en `GET /Users`.
- [ ] Una petición sin un token válido responde `401`.

## Siguiente paso

Lee la [guía de aprovisionamiento SCIM](https://docs.probara.net/es/guides/scim-provisioning/) para los pasos de configuración de Okta, Microsoft Entra ID y clientes SCIM 2.0 genéricos.
