RAG sobre SharePoint, Confluence y Google Drive: arquitectura permissions-first
El problema difícil del RAG empresarial no es la calidad del retrieval, son los permisos: el asistente nunca debe mostrar un documento a un usuario que no tiene permiso para verlo. El embedding elimina la access control list, así que un pipeline ingenuo del tipo "embebe todo, recupera top-k" hace match por similitud, no por autorización, y filtra datos. La solución es arquitectónica. Aplique el acceso en la capa de retrieval, no en el prompt: adjunte la ACL de cada documento fuente como metadato versionado y filtrable en cada chunk, y filtre la búsqueda vectorial por la identidad y las pertenencias a grupos del usuario que pregunta antes del retrieval, de modo que el modelo solo vea chunks autorizados. Propague la identidad del usuario final, no una cuenta de servicio; almacene IDs de grupo, no IDs de usuario; deje que deny prevalezca sobre grant; y mantenga las ACL frescas, porque un permiso revocado que el índice aún no ha registrado es una fuga en activo.
Este es el complemento de implementación de nuestra checklist de production-readiness para RAG, que señala los permisos por usuario como el problema de seguridad difícil. Esta entrada explica cómo resolverlo de verdad. Las funciones específicas de proveedor y las de preview cambian rápido; vuelva a revisar la documentación enlazada antes de construir.
¿Está construyendo un asistente RAG sobre sus almacenes de documentos?
Reserve una consulta gratuitaPor qué los permisos son la parte difícil
El modo de fallo en una frase: el modelo compone una respuesta fluida basada en un documento que el usuario que pregunta nunca tuvo permiso para ver, porque el retrieval hizo match por similitud semántica, no por autorización. Una vez que un documento se trocea en chunks y se embebe en un índice compartido, los propietarios, niveles de acceso y etiquetas de sensibilidad del sistema fuente no viajan con el vector a menos que usted los lleve deliberadamente. Un vector store no tiene noción de quién pregunta.
Microsoft 365 Copilot es el caso real canónico. No rompe los permisos; los respeta. El problema es que vuelve trivialmente localizable contenido que ya estaba sobrecompartido pero era difícil de encontrar, de modo que un over-permissioning latente que nadie advirtió se convierte en una consulta de un solo prompt. La propia mitigación de Microsoft es reveladora: herramientas que sacan el contenido riesgoso del alcance del asistente (restricted content discovery, informes de data-access governance) en lugar de pedirle al modelo que se comporte.
Lo cual es el punto clave: decirle al modelo que no revele un documento no es un control. La guía de OWASP sobre divulgación de información sensible afirma con claridad que las restricciones del system prompt pueden no respetarse y pueden eludirse mediante prompt injection. La prompt injection es el riesgo número uno de los LLM, y puede llegar de forma indirecta, oculta dentro de un documento recuperado. Investigadores han demostrado una evasión casi total de los guardrails de producción, y una cadena de prompt injection de 2026 en la búsqueda empresarial de Copilot funcionó precisamente porque una salvaguarda de leak-prevention solo se aplicaba a la primera petición. La frontera tiene que ser la arquitectura, no el prompt.
El principio: aplicar en la capa de retrieval
El vector store nunca debe devolver un documento no autorizado en primer lugar, y el modelo nunca debe procesarlo. Eso se logra etiquetando cada chunk con metadatos ACL normalizados y versionados en el momento del indexado e incluyendo un filtro de permisos en la consulta en el momento del retrieval. Filtrar después del retrieval, en la capa de aplicación o en el prompt, falla de dos formas: desperdicia la ventana de contexto trayendo documentos solo para descartarlos, y si la lógica de la app tiene un bug o se elude con una injection, los documentos pasan igual. El post-filtrado además rompe silenciosamente el recall, porque quitar los chunks no autorizados de un conjunto top-k puede dejar al modelo sin nada cuando los resultados autorizados quedaban justo fuera de k. Pre-filtre en la búsqueda, o sobre-recupere varias veces k y luego filtre.
Modelos de permisos por fuente
Cada fuente tiene su propio modelo de ACL y sus propias trampas. Usted hace la ingesta con una identidad elevada que puede leer todo el contenido y sus permisos, normaliza cada fuente a la misma forma y resuelve a IDs de grupo.
| Fuente | Cómo funcionan los permisos | La trampa a manejar |
|---|---|---|
| SharePoint / OneDrive (Microsoft Graph) | Los permisos cascadean del site a la biblioteca al elemento; lea las ACL vía el endpoint de permisos del elemento usando grantedToV2 | Compartir un único archivo rompe en silencio la herencia; "Limited Access" es fontanería, no una concesión de lectura; las etiquetas de sensibilidad añaden una segunda barrera (el usuario necesita los derechos EXTRACT y VIEW, no solo la ACL de SharePoint) |
| Confluence | El acceso de lectura efectivo es la intersección del permiso de space y la restricción de página; las restricciones de vista se heredan a las páginas hijas | La API de restricciones de Cloud no devuelve las restricciones heredadas, así que debe recorrer cada ancestro y unir sus restricciones de lectura o filtrará páginas hijas restringidas |
| Google Drive | Seis roles, tipos de grantee user/group/domain/anyone; lea las ACL vía la lista de permisos con permissionDetails para la herencia | Los shared drives son estrictamente expansivos (un hijo no puede tener menos acceso que su padre); un cambio de permiso en el padre no registra ninguna entrada en el change-log del hijo, así que debe re-propagar |
Un detalle que muerde a los equipos: contra Microsoft Graph, programe contra grantedToV2 y grantedToIdentitiesV2; los campos grantedTo más antiguos están deprecados. Y la visibilidad de los permisos está ella misma access-trimmed, así que un crawl completo de ACL necesita una application identity con los scopes read-all correctos, no un token de usuario delegado.
Patrones de implementación que aguantan
- Almacene IDs de grupo en el chunk, no IDs de usuario. Menor cardinalidad, y un cambio de pertenencia no requiere reindexar, solo resolución de grupos en tiempo de consulta. Resuelva la pertenencia transitiva y anidada del lado del principal.
- Recupere con la identidad del usuario final. Haga la ingesta con una identidad de servicio, pero filtre en tiempo de consulta con la identidad y los group claims del usuario final, propagados vía OAuth on-behalf-of. Use el object ID inmutable y los object IDs de grupo para la autorización, nunca un display name mutable. On-behalf-of solo funciona para usuarios, no para service principals, así que una cadena solo de cuenta de servicio no puede hacer trimming por usuario.
- Pre-filtre en la consulta ANN con un test de pertenencia a conjunto. Use una función de conjunto (el equivalente en su vector store a search.in o un filtro $in) sobre los principals permitidos del usuario; una cadena larga de ORs de igualdad es segundos más lenta. Deny prevalece sobre grant.
- Maneje el groups-overage de Entra. Si un usuario está en más de unos 200 grupos, el identity token descarta el groups claim y emite un puntero de overage; la capa de retrieval debe detectarlo y traer la lista completa, o los usuarios con muchos grupos quedan silenciosamente sub-filtrados.
- pgvector con Postgres row-level security es un primitivo limpio: una select policy añade un predicado de permisos a cada consulta, incluida la búsqueda por similitud, así que es imposible eludirlo en el código de la app. Vigile la latencia, porque la policy puede pelearse con el índice aproximado; mitigue con índices parciales por tenant o particionado, y fije la variable de identidad por petición en su connection pooler.
Hay una bifurcación genuina en las recomendaciones que conviene nombrar. Un bando (incluido AWS) sostiene que el filtrado de metadatos en tiempo de consulta por sí solo no basta y que debería volver a verificar la autorización contra la fuente en el retrieval, porque los metadatos sincronizados se quedan obsoletos. El otro (incluidos los patrones de referencia de Microsoft) sincroniza las ACL al índice y las aplica con el token del usuario en tiempo de consulta, por throughput. Es un trade-off real: frescura frente a latencia. Elija según lo sensibles que sean los datos.
El problema de frescura que nadie presupuesta
Cuando los permisos se materializan en la ingesta, una revocación del lado de la fuente no se propaga automáticamente, así que un usuario revocado puede seguir viendo un documento en el índice hasta la siguiente sincronización. Hay dos ventanas de fuga: la ventana de sync entre un cambio en la fuente y la actualización del índice, y el punto ciego del scope heredado, donde una herramienta refresca las ACL de elementos con permisos únicos pero pierde los cambios que vinieron de un scope padre. SharePoint necesita un resync explícito de permisos para los cambios heredados; los cambios en el padre de un shared drive de Google Drive no dejan entrada en el change-log del hijo. Use los change feeds de la fuente (la Changes API de Google Drive se dispara con cambios de permisos, no solo con ediciones) y, o bien materialice más rápido con webhooks, o bien haga una verificación en vivo de última milla, según la bifurcación anterior.
El borrado es la otra mitad. El derecho de supresión significa borrar en cada capa: el objeto fuente, cada chunk derivado y cada embedding, más las cachés. Diseñe los IDs de chunk de antemano para que borrar un documento y todos sus chunks sea una operación barata. Y trate el embedding mismo como dato personal: la investigación sobre embedding-inversion ha reconstruido la gran mayoría de los textos fuente cortos solo a partir del vector, así que un borrado que deja atrás el embedding no ha olvidado nada en realidad.
GDPR y residencia
El RAG permissions-first mapea limpiamente sobre el GDPR. La minimización de datos desaconseja vectorizar en masa todo lo alcanzable; recupere y embeba solo lo que el asistente necesita. El principio de responsabilidad proactiva se sirve mejor con un artefacto concreto: un log de auditoría de retrieval por consulta que registre qué documentos se mostraron a qué identidad para qué consulta, lo que además le permite investigar a posteriori una sospecha de fuga por ACL obsoleta. Y dónde residen los datos sigue importando, así que enrute la inferencia a una región de la UE y firme un acuerdo de tratamiento de datos con el proveedor del modelo, algo que cubrimos en residencia de datos en la UE para apps de IA en 2026.
La arquitectura de referencia, y los anti-patrones
De extremo a extremo: haga la ingesta de cada fuente con una identidad que pueda leer todo el contenido y las ACL; extraiga la ACL efectiva por elemento (respetando la herencia rota, recorriendo los ancestros de Confluence, leyendo la herencia de Drive); trocee, embeba y adjunte la ACL normalizada y versionada como metadato filtrable en cada chunk; recupere con la identidad del usuario final y un filtro de permisos obligatorio; genere solo a partir de chunks autorizados; registre cada decisión; y ejecute un bucle de frescura a partir de los change feeds de la fuente con resync explícito para los cambios heredados y borrados en cascada hasta chunks y embeddings.
Los anti-patrones, la mayoría de los cuales son incidentes reales esperando a ocurrir: un único índice compartido sin filtro de permisos; filtrar después del retrieval o en el prompt; confiar en que el modelo se autocensure; recuperar con una identidad de cuenta de servicio, lo que mata el trimming por usuario; almacenar IDs por usuario en lugar de IDs de grupo; tratar el "Limited Access" de SharePoint como una concesión de lectura; leer las restricciones de Confluence sin recorrer los ancestros; espejar solo las ACL de SharePoint ignorando el cifrado por etiqueta; actualizar solo en crawls completos de modo que las revocaciones se demoran; y borrar el documento fuente pero dejar el embedding.

"El modelo no es tu control de acceso, y el prompt no es tu frontera de seguridad. Si un usuario no tiene permiso para leer un documento, el vector store nunca debe devolverlo. Todo lo demás, las etiquetas, la frescura, el log de auditoría, está al servicio de esa única regla."
Preguntas frecuentes
¿Cómo se aplican los permisos en RAG?
¿RAG respeta los permisos de SharePoint?
¿Qué es el security trimming en RAG?
¿Cómo se implementan permisos por usuario en RAG?
¿Debo almacenar IDs de usuario o IDs de grupo en cada chunk?
¿Cómo mantengo los permisos frescos para no filtrar tras una revocación?
¿Puedo simplemente decirle al modelo que no revele documentos restringidos?
¿Puede pgvector aplicar acceso por usuario?
¿Son los embeddings de documentos datos personales bajo el GDPR?
¿En qué se diferencia la ingesta de permisos de Confluence?
Reflexiones finales
El RAG empresarial sobre sus propios almacenes de documentos vive o muere por una regla: si un usuario no puede leer un documento, el retriever nunca debe devolverlo, y el modelo nunca debe verlo. El embedding elimina la ACL, no se puede confiar en que el prompt la vuelva a poner, y la ventana de fuga tras una revocación es real.
Así que construya permissions-first. Lleve la ACL de cada fuente como metadato versionado a cada chunk, recupere con la identidad del usuario final y un filtro obligatorio, mantenga las ACL frescas desde los change feeds de la fuente, trate el embedding como dato personal y registre qué mostró el asistente y a quién. Acierte con esa arquitectura y el resto de RAG es la parte fácil.
¿Quiere un RAG permissions-first construido sobre su SharePoint, Confluence o Drive?
Reserve una consulta gratuita