Onboard Dashboards saved objects and scope reads to a workspace - #18
Draft
DarshitChanpura wants to merge 6 commits into
Draft
DarshitChanpura wants to merge 6 commits into
DarshitChanpura wants to merge 6 commits into
Conversation
Dashboards is not an OpenSearch plugin, so nothing on the OpenSearch side can supply a ResourceSharingExtension for its saved objects. Register a built-in one on its behalf, declaring the workspace container type plus the shareable saved-object types over the Dashboards index. Off by default and gated twice: the extension is only registered when dashboards_onboarding.enabled is set, and a registered type is only enforced once it also appears in protected_types. Signed-off-by: Darshit Chanpura <dchanp@amazon.com>
Signed-off-by: Darshit Chanpura <dchanp@amazon.com>
A resource-access-levels.yml shipped inside this plugin is resolved by a fixed filename from each extension classloader, so it is ambiguous with a plugin own copy wherever both are visible on one classpath. Register the built-in levels directly instead and drop the file. Signed-off-by: Darshit Chanpura <dchanp@amazon.com>
Signed-off-by: Darshit Chanpura <dchanp@amazon.com>
loadExtensions read the inherited settings field, which is null when the security plugin is disabled, so every cluster start failed. Use the always-assigned pluginSettings field and null-guard it. Signed-off-by: Darshit Chanpura <dchanp@amazon.com>
Combines two pieces of workspace onboarding groundwork. Registers a built-in ResourceSharingExtension for Dashboards saved objects, since Dashboards is not an OpenSearch plugin and cannot supply one itself. Declares the workspace container type plus the shareable saved-object types. Off by default and additionally gated on protected_types. Adds optional request-scoped narrowing: when Dashboards forwards the workspace the UI is in, DLS scopes resource visibility to that single workspace, intersected with the user membership set so it can only narrow and never grant. Signed-off-by: Darshit Chanpura <dchanp@amazon.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Groundwork for bringing OpenSearch Dashboards workspaces under resource sharing. Two related pieces:
1. Register Dashboards saved objects. Dashboards is a Node.js application, so nothing on the OpenSearch side can implement
ResourceSharingExtensionfor its saved objects — which is why the SPI seams added earlier have no implementer. A built-in extension registers them from inside the security plugin instead. A separate plugin would also work, but a core module depending on the security SPI would not: the SPI itself builds against core, so that is a build cycle.Declares the
workspacecontainer type (which is what lets the write-path container fan-out resolve a workspace's own sharing record) plus the shareable saved-object types (dashboard,visualization,search,index-pattern). They all live in one index and carry their type in atypefield, so each is registered as its own resource type over that shared index. Member types declareworkspacesField("workspaces");workspacedeclares none, since it is a container rather than a member.Access levels are registered programmatically rather than via a
resource-access-levels.yml: that file is resolved per extension by a fixed filename from the classloader, so a copy inside this plugin would be ambiguous with a plugin's own copy wherever both are visible on one classpath.2. Optional request-scoped narrowing. Workspaces are membership-scoped, not request-scoped: DLS filters a resource's
workspacesfield against the user's entire accessible-workspace set, and that is the security floor. On top of that, when Dashboards forwards the workspace the UI is currently in as acurrentworkspaceheader, visibility narrows to that one workspace. The requested workspace is intersected with the user's trusted membership, so it can only narrow, never grant — a foreign or spoofed workspace narrows to empty.BackendRegistryreads the header next to the existing tenant handling and re-emits it server-side. It usesputPersistentrather thanputHeader, because the plugin's own searches run under a stashed system-subject context where only persistent entries survive — the same mechanism that carries the authenticatedUserto the DLS valve.Safety
The saved-object registration is gated twice, so it is inert unless explicitly opted into:
plugins.security.resource_sharing.dashboards_onboarding.enabledis set (defaultfalse).plugins.security.resource_sharing.protected_types.Testing
DashboardsResourceSharingExtensionTests— declared types and index;workspaceopting out of the workspaces field while members opt in; resolution throughResourcePluginInfoonce protected; non-protected types not resolving; an emptyprotected_typesleaving the resource-index set empty (so the index listener is not attached and no sharing index is created); mixed null/non-nullworkspacesFielddeclarations not tripping the per-index conflict check; and programmatic access-level registration including the derived level names and default.testCurrentWorkspaceHeaderNarrowsReadVisibility(live integration) — no header gives full membership and the resource is visible; the user's own workspace keeps it visible; a workspace the user is not a member of narrows to empty and hides it.What this does not do yet
ResourceAccessEvaluator.shouldEvaluateexplicitly excludesDocWriteRequest, and core'sDocRequest.type()returns"indices"rather than the saved-object type. There is a real design question here, since the resource-access path is deliberately async while the multi-tenancy handler that already authorizes raw saved-object writes is synchronous — happy to take direction before building it.resolveWorkspacesForUseris not overridden, so workspace-based visibility stays off until membership is derived from theworkspace:{id}sharing records.hasPermissiondenies when no sharing record exists, so existing saved objects need backfilling before any type is added toprotected_typesin a live cluster.currentworkspaceis a companion PR insecurity-dashboards-plugin, and the header must be listed inopensearch.requestHeadersWhitelist.Open question
Carry the current workspace on
UserlikerequestedTenant(survives to all nodes, cleaner downstream), or keep the persistent thread-context entry used here?Check List
--signoff