-
Notifications
You must be signed in to change notification settings - Fork 8.2k
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
Security - Reorder Saved Objects Client wrappers #38444
Conversation
@@ -26,6 +27,10 @@ export function createSpacesService(server: any): SpacesService { | |||
return spaceId; | |||
} | |||
|
|||
function setSpaceId(request: any, spaceId: string) { |
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 can remove this, as it's not used yet, but this is a potential solution for allowing Spaces to dictate which space id should be considered the "current" space id for features such as #37286
If we decide to keep in this PR, then I just need to write a few more tests before merging.
This has the benefit of not modifying the base path for the in-flight request, so request.getBasePath()
/ http.getBasePathFor(request)
will not be impacted by calls to setSpaceId
, which reduces the likelihood of side-effects as a result of changing the space id mid-request.
One tradeoff is that this necessitates having a cache here (or elsewhere) to store this information, which we had planned to remove in #35429
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.
Hmm, I always figured this would just be done for "copy to space" by allowing "consumers" to specify the namespace
in the options. Otherwise, we can't use the SOC instances for different spaces concurrently and we have do so serially.
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.
Hmm, I always figured this would just be done for "copy to space" by allowing "consumers" to specify the namespace in the options. Otherwise, we can't use the SOC instances for different spaces concurrently and we have do so serially.
Yep, that's the other route we can take! I started down that path in this PR, but ended up reverting the required changes for now.
I'm happy to take either approach, I just wanted to present options. I'll revert this change in this PR, and re-introduce the ability for the spaces wrapper to accept a namespace from consumers.
💚 Build Succeeded |
type, | ||
}); | ||
expect(mockErrors.decorateGeneralError).toHaveBeenCalledTimes(1); | ||
expect(mockAuditLogger.savedObjectsAuthorizationFailure).not.toHaveBeenCalled(); |
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 decided (for now) to keep the audit logging assertions as part of this test suite. We're technically testing an implementation detail of a dependency now, but I feel like it's important to verify that it works from the service level, and the Saved Object Client is as close to an API as we can get without mocking out even more services.
We have audit logging tests as part of https://github.com/elastic/kibana/blob/764322cc7f3a6f3b082a0044af151e1b2a1da21f/x-pack/plugins/security/server/lib/authorization/saved_objects/check_saved_objects_privileges.test.ts, so I don't have a strong opposition to removing the checks here if we want to simplify this test suite a bit.
|
||
try { | ||
if (deps.spacesEnabled) { | ||
const spaceId = namespaceToSpaceId(namespace); |
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.
🆕 no longer relies on the spaces service to get the current space id!
💚 Build Succeeded |
private async ensureAuthorized( | ||
typeOrTypes: string | string[] | undefined, | ||
action: SavedObjectsOperation, | ||
namespace: string | undefined, |
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.
The diff isn't very helpful since this was converted to TypeScript, but the only material difference in this file is the introduction of this namespace
parameter, and the swapping of checkPrivilegesDynamicallyWithRequest
with checkSavedObjectsPrivileges
.
💚 Build Succeeded |
Pinging @elastic/kibana-security |
ACK: will review next |
* Using SavedObjectsClientContract['errors'] instead of any for errors * Moving security's SOC wrapper to authorization/saved_objects * Renaming 'action' to operation * Renaming checkSavedObjectsPrivileges to ensureSavedObjectsPrivileges We use "check" elsewhere when returning a "check privileges result", and we use "ensure" elsewhere when an Error will be thrown if the user doesn't have any privileges. * Adding "general error" tests for ensure saved object privileges * Removing general error tests from wrapper * Change the SecureSavedObjectsClientWrapper tests to be strictly white-box
This reverts commit c98c1cd.
💚 Build Succeeded |
retest |
💚 Build Succeeded |
Having to augment the |
@legrego the only reason for putting the It doesn't affect the resulting documentation so I'd be happy to live with it the way it was. I haven't had a chance to play around with it, but it might also be a bug intstead of just a limitation in |
If we could get away with not changing the OSS definition of the saved object namespace, that'd make me feel much better about this implementation. That'd allow us to change the "asserts" within the repository itself to ensure the namespace is either undefined or a string, without explicitly ensure that it's "not a symbol". Apologies if I'm being pedantic here. |
Yep, that was the only reason I made this change.
sure I'll revert. |
💔 Build Failed |
💔 Build Failed^ unrelated CI failure |
💔 Build Failed |
💔 Build Failed |
💚 Build Succeeded |
currentSpaceId: string | ||
): string | undefined { | ||
if (operationOptions.namespace) { | ||
if (operationOptions.namespace === DEFAULT_SPACE_NAMESPACE) { |
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.
Who writes DEFAULT_SPACE_NAMESPACE
in namespace field? If it serializable a symbol cannot be used. Can we use a special string instead? What is the benefit of using a separate type?
DEFAULT_SPACE_ID
exists here only for backward compatibility?
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.
Who writes DEFAULT_SPACE_NAMESPACE in namespace field? If it serializable a symbol cannot be used. Can we use a special string instead? What is the benefit of using a separate type?
Consumers of the Saved Objects Client can specify this for any operation they wish to perform explicitly against the default space. You bring up a good point about serializability that I hadn't considered -- @mikecote, do you foresee this being problematic for alerting? How are you planning/hoping to record the space/namespace which the alert should run in? Are you storing the space id, and translating to namespace when required, storing the namespace directly, or storing the base path and translating?
DEFAULT_SPACE_ID exists here only for backward compatibility?
DEFAULT_SPACE_ID
is mostly for backwards compatibility, but it also serves as a way to allow the spaces plugin to be completely disabled/re-enabled at any time, without breaking Kibana.
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.
How are you planning/hoping to record the space/namespace which the alert should run in? Are you storing the space id, and translating to namespace when required, storing the namespace directly, or storing the base path and translating?
This will be a conversation I'll have to have with you when we do the security phase. Right now we store the basePath
and create clients with that. Though there may be a few places that forget to store the space and assumes the default space.
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 will be a conversation I'll have to have with you when we do the security phase. Right now we store the basePath and create clients with that. Though there may be a few places that forget to store the space and assumes the default space.
Ok, then this shouldn't be an issue for alerting then.
In general, specifying a custom namespace when using the saved objects client should be a very rare event. I consider the namespace
field an implementation detail of the space plugin, even though it is an OSS concept. If the spaces plugin exposes first-class methods for translating a space id to a namespace (and vice-versa), then I don't foresee Symbols being problematic here.
Can we use a special string instead? What is the benefit of using a separate type?
A special string would have to include some character that is disallowed as part of a space id, in order to guarantee that we never have a collision. It's certainly doable, so long as we don't change the set of allowed characters in the future.
A symbol makes it painfully clear that we're dealing with a special case, as opposed to a mis-typed space id or similar.
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 consider the namespace field an implementation detail of the space plugin, even though it is an OSS concept.
ok, agree. In this case symbol is internal detail and not a part of public contract. why it leaks to sibling plugins? https://github.com/elastic/kibana/pull/38444/files#diff-9b008eb2578ead2253f9faf64b262f0eR79
If the spaces plugin exposes first-class methods for translating a space id to a namespace (and vice-versa), then I don't foresee Symbols being problematic here.
with this API can we have separate 2 types. don't we?
type SavedObjectsNamespace = string | undefined;
in OSS
type SavedObjectsNamespaceInternal = string | undefined | symbol;
in x-pack/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.
why it leaks to sibling plugins?
The type definition is declared in x-pack/types
, which is shared by all x-pack plugins:
kibana/x-pack/typings/core.d.ts
Lines 8 to 13 in e2796ff
declare module 'src/core/server/saved_objects' { | |
type SavedObjectsNamespace = string | undefined | symbol; | |
interface SavedObjectsBaseOptions { | |
namespace?: SavedObjectsNamespace; | |
} | |
} |
with this API can we have separate 2 types. don't we?
type SavedObjectsNamespace = string | undefined; in OSS
type SavedObjectsNamespaceInternal = string | undefined | symbol; in x-pack/spaces.
I don't know if it's really internal though. The wrapped saved objects client has a public api that expects either string | undefined | symbol
, and this is consumable by both first and third-party plugins.
Plus, having a different name for this vs the OSS version makes overriding the type very difficult, or maybe even impossible (I haven't tried yet). The current override (above) is rather straightforward.
💚 Build Succeeded |
Abandoned in favor of #40275 |
Summary
Previously, the wrapped saved objects client worked in the following manner:
namespace
to authorize against from thespacesService.getSpaceId()
function exposed from the Spaces plugin.namespace
attribute which is used by the base Saved Objects Client/Repository for the actual operation.With this change, the order of the wrappers has been switched, so now the wrapped saved objects client works in the following manner:
namespace
attribute for use by downstream wrappers, as well as the base Saved Objects Client/Repository for the actual operation.namespace
provided by the Spaces plugin's wrapper. This has a side benefit of removing the hard dependency between the security plugin's wrapper and the spaces plugin.This also further abstracts the authorization logic from the client wrapper. Previously, the wrapper used
checkPrivilegesDynamicallyWithRequest
, which was responsible for using the correct authz method based on whether the Spaces plugin was enabled or not.We've introduced a new
checkSavedObjectsPrivileges
function, which behaves in much the same way ascheckPrivilegesDynamicallyWithRequest
, but it's designed to work specifically with thenamespace
, converting to a space id when required.