fix(sharepoint): add compact site-group DLS mode to reduce ACL memory - #4396
fix(sharepoint): add compact site-group DLS mode to reduce ACL memory#4396Jan-Kazlouski-elastic wants to merge 16 commits into
Conversation
Stop expanding large SharePoint site groups onto every document when expand_site_group_members is disabled; store site_group tokens on docs and resolve membership on identity docs during ACL sync instead.
…mpact DLS Resolve site group membership on identity docs using _access_control_for_member tokens (including nested Entra groups) and restore streaming ACL sync in legacy mode.
Build a token-to-site_group index before user enumeration so identity docs are yielded one at a time, and apply EEEU site groups only to non-guest users.
…ot be written Centralize site-group ACL resolution: use compact tokens when site_id is available, otherwise expand members so permissions are never silently dropped. Also handle role assignments that only include Member.Users without a group id.
| self._logger.info( | ||
| "Compact site-group DLS enabled: building site group membership index" | ||
| ) | ||
|
|
There was a problem hiding this comment.
This may end up being expensive. The call to self.site_collections() means we get it again. We go through the list twice. Not sure if there are Sharepoint instances with lots of these. Same with self.sites()
There was a problem hiding this comment.
Content and ACL sync run as separate jobs, so we can't share an in-memory cache across them. It's a deliberate tradeoff: one extra crawl per ACL sync vs expanding every site group member onto every document.
| if user_doc: | ||
| yield user_doc | ||
| return | ||
|
|
There was a problem hiding this comment.
Any worries the acl_token map gets big?
There was a problem hiding this comment.
5k-member group on 10k docs - expand mode puts ~50M ACL entries on documents, while compact mode puts 10k tokens on docs plus a ~5k-entry sync map. Much smaller.
| continue | ||
|
|
||
| site_group_token = _prefix_site_group(site_id, group_id) | ||
| async for member in self.client.site_groups_users( |
There was a problem hiding this comment.
Should we call our self.site_group_users so we can use the cached version?
There was a problem hiding this comment.
Switched the index builder to use self.site_group_users() so it goes through site_group_cache.
| group_id = role_assignment.get("PrincipalId") | ||
| if group_id is not None: | ||
| access_control.extend( | ||
| await self._site_group_access_control( |
There was a problem hiding this comment.
None may be fragile here, since we use it downstream in self.site_group_users
There was a problem hiding this comment.
site_web_url is now passed from all role assignment call sites, so fallback expansion has a real URL when needed.
…t DLS Use site_group_users in the compact ACL index builder and thread site_web_url through role assignment resolution for safe fallback.
## Closes elastic/chat-program#49 Part of elastic/chat-program#47. ServiceNow DLS expands every table read role into individual `user_id:` / `user:` / `email:` entries on each content document (and attachment). For large tenants — especially when a table ACL includes the `public` role — that can mean hundreds of thousands of ACL entries per document and OOM Elasticsearch during `_reindex`. This PR adds compact DLS, **opt-in** via `expand_role_members=false` (default `true` preserves existing behavior). When compact mode is enabled: - Content documents store compact `role_id:<sys_id>` tokens instead of every role member - Identity docs include role memberships from `sys_user_has_role` so DLS term overlap still works - Tables with a `public` read role omit `_allow_access_control` (world-readable via existing DLS `must_not exists`) - Tables where read roles cannot be resolved omit `_allow_access_control` (same as public tables; world-readable via existing DLS `must_not exists`) Also fixes advanced sync rules to resolve ACL per table instead of stamping an empty ACL on every batch. Changing the setting requires a full content sync and access control sync. ## Checklists #### Pre-Review Checklist - [x] this PR does NOT contain credentials of any kind, such as API keys or username/passwords (double check `config.yml.example`) - [x] this PR has a meaningful title - [x] this PR links to all relevant github issues that it fixes or partially addresses - [x] if there is no GH issue, please create it. Each PR should have a link to an issue - [x] this PR has a thorough description - [x] Covered the changes with automated tests - [x] Tested the changes locally - [ ] Added a label for each target release version (example: `v7.13.2`, `v7.14.0`, `v8.0.0`) - [ ] For bugfixes: backport safely to all minor branches still receiving patch releases - [ ] Considered corresponding documentation changes - [ ] Contributed any configuration settings changes to the configuration reference - [ ] if you added or changed Rich Configurable Fields for a Native Connector, you made a corresponding PR in [Kibana](https://github.com/elastic/kibana/blob/main/packages/kbn-search-connectors/types/native_connectors.ts) #### Changes Requiring Extra Attention - [x] Security-related changes (encryption, TLS, SSRF, etc) - [ ] New external service dependencies added. ## Related Pull Requests * #4396 — SharePoint Online compact site-group DLS (same tracking issue) * Kibana `native_connectors.ts` RCF for `expand_role_members` — to be opened separately ## Release Note ServiceNow document-level security can store compact role tokens on content documents instead of expanding every role member onto each record, reducing memory use for large tenants. Disable **Expand role members** to enable compact mode. The default preserves the previous per-user expansion behavior. Changing the setting requires a full content sync and access control sync. --------- Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
E2E validation: SPO compact site-group DLSVerdict: Pass. Setup
Results
DLSSimulated via standard connector
Gaps
No blocking issues found. |
…3645) (#4437) ## Summary With DLS enabled, unpublished SharePoint Online pages were still returned to users with view access. SharePoint does not update a page's ACLs when it is unpublished, so the connector kept the old view-only permissions on the indexed document. This detects draft vs published state from the page version string and restricts `_allow_access_control` on unpublished pages to owners/editors only. A `published` field is also exposed on `site_page` documents. Closes #3645 ## Changes - Detect published state from `OData__UIVersionString` (major version = published, minor version = draft) - For unpublished pages, restrict ACLs to owners/editors: - unique per-page permissions: only edit-or-higher role members - inherited site permissions: use a site editors ACL set instead of the full site ACL - Add `require_edit_access` to `_get_access_control_from_role_assignment` - Add `EDIT_ITEM_MASK` / `EDIT_ROLE_TYPES` constants - Add `published` field on `site_page` documents - Debug log when an unpublished page ACL is restricted ## Testing - Unit tests: version parsing, edit-only role filtering, unpublished/published ACL behavior (unique + inherited permissions). `209 passed` in `test_sharepoint_online.py`. - E2E: not run locally (requires live SharePoint Online tenant with DLS). ## Notes for reviewers - Published detection defaults to `published=True` when version info is missing, to avoid over-restricting access. - **Merge note with #4396:** both PRs touch `datasource.py` and `test_sharepoint_online.py`. A test merge shows one mechanical conflict in `_get_access_control_from_role_assignment` (this PR adds `require_edit_access`, #4396 adds `site_id`). Tests auto-merge. Resolution is to keep both optional parameters and pass both from `site_pages`. - Consider backport labels if needed. --------- Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
Closes https://github.com/elastic/chat-program/issues/48
Part of https://github.com/elastic/chat-program/issues/47.
SharePoint Online DLS expands every site group member onto each document's
_allow_access_controlarray. Large site groups can produce multi-megabyte ACLs per document and OOM Elasticsearch during_reindex.This PR adds
expand_site_group_members(defaulttrueto preserve existing behavior). When disabled (compact mode):site_group:<site_id>:<group_id>token instead of every site group membersite_id), the connector falls back to expanding site group members and logs a warningChanging the setting requires a full content sync and access control sync.
Checklists
Pre-Review Checklist
config.yml.example)v7.13.2,v7.14.0,v8.0.0)Changes Requiring Extra Attention
Related Pull Requests
native_connectors.tsRCF forexpand_site_group_members— to be opened separatelyRelease Note
SharePoint Online document-level security can store compact site-group tokens on documents instead of expanding every site group member onto each record, reducing memory use for large site groups. Disable Expand site group members to enable compact mode. The default preserves the previous behavior. Changing the setting requires a full content sync and access control sync.