-
Notifications
You must be signed in to change notification settings - Fork 8.5k
Add multiple namespaces support to PIT search and finder #109062
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add multiple namespaces support to PIT search and finder #109062
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a rough draft of what I think the implementation should look like given the info provided in #99044.
A question I have regarding the issue's description is regarding this point:
soClient.createPointInTimeFinder will need to ensure the typeToNamespacesMap is handed off when calling openPointInTimeForType
from my current understanding, createPointInTimeFinder would remain unchanged, as the logic change would be handled in the internal calls of PointInTimeFinder to client.openPointInTimeForType which would leverage the security wrapper's modifications? Could you confirm this?
@jportner @lukeelmers could you take a look and tell me if this seems like the correct approach?
| * @public | ||
| */ | ||
| export interface SavedObjectsOpenPointInTimeOptions extends SavedObjectsBaseOptions { | ||
| export interface SavedObjectsOpenPointInTimeOptions { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We no longer extend SavedObjectsBaseOptions to remove the namespace option, as this new namespaces option was added.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See my comment in #109062 (review), I think we should leave the SavedObjectsOpenPointInTimeOptions options as-is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with your approach, so typeToNamespacesMap is no longer necessary here and I will remove it. However SavedObjectsOpenPointInTimeOptions had a namespace?: string option due to this extension, that needs to be changed to a namespaces?: string[], for the security wrapper to use it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I was going too fast, yep agreed
x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts
Show resolved
Hide resolved
| throw error; | ||
| throw SavedObjectsErrorHelpers.decorateForbiddenError(new Error(status)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the same scenario, the find wrapper returns SavedObjectsUtils.createEmptyFindResponse, however this is not possible for openPointInTimeForType, as the expected return type is a SavedObjectsOpenPointInTimeResponse. I'm throwing a forbidden error in that case, but really not sure this is the correct error type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW this makes sense to me: If you are opening a PIT against a type & you don't have permissions to access that type's backing index, I'd expect a forbidden error. But let's see what @jportner thinks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, the forbidden error seems like the best approach here. Note that if you are only authorized to access a type in space A, and you attempt to open a PIT for that type only in space B, you'll run into a forbidden error. But that seems acceptable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you are only authorized to access a type in space A, and you attempt to open a PIT for that type only in space B, you'll run into a forbidden error. But that seems acceptable.
I would even think that this is expected and not just acceptable? OTOH that's right that it differs from the find behavior that just returns an empty find response without throwing in the same situation.
The alternative would be to call baseClient.openPointInTimeForType with an empty list of types, and have it return a stubbed PIT finder impl that returns no results in that case, but that feels a little over-complicated to me?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hum, actually we can't do that, as it would need to be done in createPointInTimeFinder, so it doesn't solve the issue when calling openPointInTimeForType directly. So throwing here seems the only viable option anyway
x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts
Outdated
Show resolved
Hide resolved
x-pack/plugins/spaces/server/saved_objects/spaces_saved_objects_client.ts
Show resolved
Hide resolved
lukeelmers
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall this makes sense to me, but will defer to @jportner to make sure the behavior is what we'd expect here.
from my current understanding, createPointInTimeFinder would remain unchanged, as the logic change would be handled in the internal calls of PointInTimeFinder to client.openPointInTimeForType which would leverage the security wrapper's modifications? Could you confirm this?
Yes, this sounds correct to me as the typeToNamespacesMap would be handled by the wrapper. That issue description was written without doing an actual POC, so most likely I had overlooked this consideration.
x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts
Show resolved
Hide resolved
| throw error; | ||
| throw SavedObjectsErrorHelpers.decorateForbiddenError(new Error(status)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW this makes sense to me: If you are opening a PIT against a type & you don't have permissions to access that type's backing index, I'd expect a forbidden error. But let's see what @jportner thinks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with most of the approach, with one exception:
In the SSOCW, in the openPointInTimeForType function, I don't think we need to pass typeToNamespacesMap down to the base client at all. When the user is partially or fully authorized, we should just pass down the types that the user can access.
The repository just needs to know what types the user is authorized to access in some space so that it can open the PIT against the appropriate indices for those types.
Re-reading the issue, Luke suggested this as an option in #99044 (comment):
- Don't implement
typeToNamespaceMapinopenPointInTimeForType, and simply open the PIT against the indices for all allowed types in the request. If permissions change later, the correct namespace filters should still be applied infind. The tradeoff is that you are allowing the PIT to be open against some types which could potentially not be allowed in any namespaces, however as you aren't able to query those withfind, this may be fine.
I think this approach is simpler and doesn't compromise security. See my suggestion below: #109062 (comment)
x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts
Outdated
Show resolved
Hide resolved
x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts
Show resolved
Hide resolved
pgayvallet
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
self-review (in addition to the previous, non-closed points)
| * @returns {promise} - { saved_objects: [{ id, type, version, attributes }], total, per_page, page } | ||
| */ | ||
| public async find<T = unknown, A = unknown>(options: SavedObjectsFindOptions) { | ||
| throwErrorIfNamespaceSpecified(options); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure why this was there, there isn't a namespace property on SavedObjectsFindOptions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dead code! thanks for removing it
| if (id) { | ||
| // "bulkGet" was unauthorized, which returns a forbidden error | ||
| await expectSavedObjectForbiddenBulkGet(type)(response); | ||
| } else { | ||
| expect(response.body).to.eql({ | ||
| statusCode: 403, | ||
| error: 'Forbidden', | ||
| message: `unauthorized`, | ||
| }); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Previously, per-id unauthorized were returning a 403 (due to bulkGet returning an error) and per-type were returning an empty export instead.
This PR has the side effect to change that, to now returns a 403 for per-type export when the user don't have the permissions for any of the types.
Overall, I think this is an improvement in term of consistency.
|
Pinging @elastic/kibana-core (Team:Core) |
| */ | ||
| preference?: string; | ||
| /** | ||
| * An optional list of namespaces to be used by the PIT. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Our separation between core / security makes documentation really hard here. But in the absence of another good place to write documentation it feels like we should maybe add some details about how this option behaves when the spaces plugin is enabled? Otherwise it's very difficult to figure out how to use this.
| * An optional list of namespaces to be used by the PIT. | |
| * An optional list of namespaces to be used by the PIT. | |
| * | |
| * When the spaces plugin is enabled: | |
| *. - this will default to the user's current space as determined by the URL | |
| * - if namespaces is specified the user's current space will be ignored | |
| * - `['*']` will search across all available spaces |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, that's fair. I can't think of any better place...
lukeelmers
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes here all LGTM, thanks for taking this one on 🚀
jportner
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor nits below, otherwise LGTM, thanks for doing this!
| type: string | string[], | ||
| options: SavedObjectsOpenPointInTimeOptions = {} | ||
| ) { | ||
| throwErrorIfNamespaceSpecified(options); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can get rid of this too
| }); | ||
|
|
||
| test(`supplements options with the current namespace`, async () => { | ||
| test(`translates options.namespace: ['*']`, async () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit
| test(`translates options.namespace: ['*']`, async () => { | |
| test(`translates options.namespaces: ['*']`, async () => { |
| foo: 'bar', | ||
| namespace: currentSpace.expectedNamespace, | ||
| keepAlive: '2m', | ||
| namespaces: [currentSpace.expectedNamespace ?? 'default'], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit
| namespaces: [currentSpace.expectedNamespace ?? 'default'], | |
| namespaces: [currentSpace.expectedNamespace ?? DEFAULT_SPACE_ID], |
💚 Build SucceededMetrics [docs]History
To update your PR or re-run it, just comment with: |
💚 Backport successful
This backport PR will be merged automatically after passing CI. |
…109603) * initial modifications * change approach for openPointInTime and add tests for spaces wrapper changes * fix and add security wrapper tests * fix export security FTR tests * update generated doc * add tests for PIT finder * NIT * improve doc * nits Co-authored-by: Pierre Gayvallet <pierre.gayvallet@gmail.com>
Summary
Fix #99044
Add multi-namespaces support to
openPointInTimeForTypeandcreatePointInTimeFinderChecklist