-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
Move base feature controls functionality from XPack Main plugin to a dedicated XPack Features plugin #44664
Conversation
Pinging @elastic/kibana-platform |
Pinging @elastic/kibana-security |
@@ -38,6 +38,7 @@ | |||
/src/legacy/server/saved_objects/ @elastic/kibana-platform | |||
/src/legacy/ui/public/saved_objects @elastic/kibana-platform | |||
/config/kibana.yml @elastic/kibana-platform | |||
/x-pack/plugins/features/ @elastic/kibana-platform |
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.
note: Platform team will own this plugin as soon as we merge it.
docs/api/features/get.asciidoc
Outdated
@@ -12,11 +12,11 @@ Note: You cannot access this endpoint via the Console in {kib}. | |||
===== Request | |||
|
|||
To retrieve all features, issue a GET request to the | |||
/api/features/v1 endpoint. | |||
/features/api endpoint. |
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.
note: it feels like not a big deal to change URL in a minor for this particular API.
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, this is a negligible change. Is the /${pluginId}/api
format a new standard? I know that endpoints have to be prefixed with the plugin id as part of the NP migration, but the /api
suffix feels strange to me. Reading this, I'd expect this to retrieve a feature with id api
, instead of retrieving all registered features.
We had a similar problem when we wrote the spaces api, but I'm not thrilled with what we did there either...
/api/spaces/space
retrieves all spaces
/api/spaces/space/foo
retrieves a space with id foo
.
We don't need the latter for this API call, since we don't support retrieving a single feature by id. If we follow the spaces pattern though (updating for the NP), we get something like:
/features/feature
...which isn't great either.
Maybe
/features/api/feature
?
I'm more than open to other suggestions
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.
Is the /${pluginId}/api format a new standard?
It's not clear yet, that's why I'd like to discuss it here and in #44620. I need to introduce NP routes in a couple PRs right now and was thinking whether we should separate API and view routes somehow, that's how this /api/
part appeared.
I'd expect this to retrieve a feature with id api, instead of retrieving all registered features.
Heh, that's a good point!
Maybe /features/api/feature?
Yeah, or something verbose like /features/api/all-features
, /features/api/feature/{id}
, still thinking, don't have good enough ideas yet....
I was also thinking about features/_api/
, _features/api/
, features.api/
to kind of stress on the difference between these two semantically different parts of the URL. We can also say that features/*
are always API calls and features/{views|app|pages}/*
are for {views|app|pages}
.
@restrry it's probably a good time to pick some API naming style that we can follow :) Did you guys had a chance to discuss that already?
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.
Once #44855 is merged I'll change URL from /api/features/v1
to /api/features
.
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.
Changed to /api/features
@@ -0,0 +1,8 @@ | |||
{ |
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.
note: we need to get access to the timelion.ui.enabled
setting from the Features plugin so introducing NP timelion plugin felt natural to me, but I'm open to any better ideas!
"id": "features", | ||
"version": "8.0.0", | ||
"kibanaVersion": "kibana", | ||
"optionalPlugins": ["timelion"], |
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.
note: just because we want to know value of timelion.ui.enabled
.
export class Plugin { | ||
private readonly logger: Logger; | ||
|
||
private legacyAPI?: LegacyAPI; |
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.
note: same approach we used in NP Security plugin, but happy to hear any better suggestions.
return deepFreeze({ | ||
registerFeature: featureRegistry.register.bind(featureRegistry), | ||
getFeatures: featureRegistry.getAll.bind(featureRegistry), | ||
getFeaturesUICapabilities: () => uiCapabilitiesForFeatures(featureRegistry.getAll()), |
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.
question: I expose this just because it's used in uiCapabilities
method of the XPack Main Plugin definition. Josh do we have anything like this in NP world so that I can use it here and don't expose to LP?
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.
On the server-side, the NP doesn't have anything related to UI capabilities yet.
The ApplicationService on the client-side does currently expose the injected ui capabilities for the current user. We will change this to fetch UI capabilities (#36319) based on the registered applications once security's HTTP interceptors are moved over.
Whenever the fetch endpoint for this is moved over to the New Platform, I expect the New Platform will need to expose a way for this plugin to inject additional capabilities. We can make that change at a later time.
"common.ui.savedObjects.howToSaveAsNewDescription": "Kibana の以前のバージョンでは、{savedObjectName} の名前を変更すると新しい名前でコピーが作成されました。今後この操作を行うには、「新規 {savedObjectName} として保存」を使用します。", | ||
"common.ui.savedObjects.overwriteRejectedDescription": "上書き確認が拒否されました", | ||
"common.ui.savedObjects.saveAsNewLabel": "新規 {savedObjectName} として保存", | ||
"common.ui.savedObjects.saveDuplicateRejectedDescription": "重複ファイルの保存確認が拒否されました", | ||
"kibana-react.savedObjects.saveModal.cancelButtonLabel": "キャンセル", |
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.
note: this has been automatically rearranged by i18n script.
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.
Would it be worthwhile to find/replace xpack.main.featureRegistry.*
with xpack.features.*
in the translated files, since we know that the translated text doesn't need to change? Or is that not advised?
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 never did this since translators are supposed to receive en.json for every release, and in this case these labels will be recognized as already translated even though IDs changed (so that we're not charged additionally). But let's double check with @Bamieh
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.
Currently we send a json diff file in english and one for every language we have, so renaming these ID's will be synced with translators. We started doing this to sync translations introduced in PRs as we used to ignore them.
I'd vote for renaming these ids as it will also help keep kibana translated even before we sync with the translators which only happens a few days before every FF release.
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.
Okay, will do that than. I usually advised against manual edits in files with translations and treated them almost as "auto-generated" as it's easy to make something wrong there :)
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.
Done.
if (!request.path.startsWith('/api/') || !mode.useRbacForRequest(request)) { | ||
// if the api doesn't include or end with "/api/" or we aren't using RBAC for this request, | ||
// just continue | ||
if ( |
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.
note: temp fix, we need to discuss what to do here with plugin-id prefixed NP API routes.
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'm trying to remember why we had the startsWith('/api/')
check to begin with...I don't think it's necessary for us to check for that path part in addition to the existence of the access:foo
tag. We should support securing any route with an appropriately configured access:
tag, even if it doesn't have /api/
within its path.
This may have just been a holdover from an earlier implementation for securing API endpoints - @kobelb do you remember differently?
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.
Reverted this change since we can keep /api/features
now.
💔 Build Failed |
💚 Build Succeeded |
@joshdover @legrego would really appreciate your feedback on this PR (will tag you for review)! |
ACK will review today/tomorrow |
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.
Made a first pass at the code today (mostly questions) -- I'll test this out some more tomorrow, but I wanted to give you something to look at before my day starts
docs/api/features/get.asciidoc
Outdated
@@ -12,11 +12,11 @@ Note: You cannot access this endpoint via the Console in {kib}. | |||
===== Request | |||
|
|||
To retrieve all features, issue a GET request to the | |||
/api/features/v1 endpoint. | |||
/features/api endpoint. |
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, this is a negligible change. Is the /${pluginId}/api
format a new standard? I know that endpoints have to be prefixed with the plugin id as part of the NP migration, but the /api
suffix feels strange to me. Reading this, I'd expect this to retrieve a feature with id api
, instead of retrieving all registered features.
We had a similar problem when we wrote the spaces api, but I'm not thrilled with what we did there either...
/api/spaces/space
retrieves all spaces
/api/spaces/space/foo
retrieves a space with id foo
.
We don't need the latter for this API call, since we don't support retrieving a single feature by id. If we follow the spaces pattern though (updating for the NP), we get something like:
/features/feature
...which isn't great either.
Maybe
/features/api/feature
?
I'm more than open to other suggestions
import { first } from 'rxjs/operators'; | ||
import { TypeOf } from '@kbn/config-schema'; | ||
import { PluginInitializerContext, RecursiveReadonly } from '../../../../src/core/server'; | ||
import { deepFreeze } from '../../../../src/core/utils'; |
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 didn't know we could import from src/core/utils
. Last time I tried within x-pack
, it was disallowed/not working. Is this something we support for x-pack plugins too, or is it just for OSS plugins?
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.
Is this something we support for x-pack plugins too, or is it just for OSS plugins?
We use it for Security plugin already, so it should be supported: https://github.com/elastic/kibana/blob/master/x-pack/plugins/security/server/plugin.ts#L16
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
import Joi from 'joi'; |
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.
Question: Is joi
still the validation framework of choice, or should we be using @kbn/config-schema
?
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 just kept what we had before, but I believe we should eventually migrate to @kbn/config-schema
assuming everything we have in these schemas is supported by @kbn/config-schema
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 I think it's fine if we change this later.
|
||
import Joi from 'joi'; | ||
|
||
// Each feature gets its own property on the UICapabilities object, |
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: I think this comment belongs with line 15 below.
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.
Ha, right, thanks!
} | ||
|
||
/** | ||
* Describes a set of APIs that is available in the legacy platform only and required by this plugin |
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
* Describes a set of APIs that is available in the legacy platform only and required by this plugin | |
* Describes a set of APIs that are available in the legacy platform only and required by this plugin |
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.
Fixed! Out of curiosity and to improve my grammar skills: does *set* .... that *is* ....
sound too unnatural comparing to set of *things* that *are*
? :)
Logger, | ||
PluginInitializerContext, | ||
RecursiveReadonly, | ||
} from '../../../../src/core/server'; |
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.
Should we be using the from 'src/core/server'
alias instead of the relative import?
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 believe I heard somewhere that we should use relative paths instead of this alias when I was working Security NP plugin since it's exactly same approach we'd use to import from other plugins, but maybe something has changed since then. @elastic/kibana-platform what would be the correct approach to import from the core?
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.
Ah ok, I'm interested to see what Platform says here. I'm ok with relative imports if that's the direction we want to go, but third-party plugins wouldn't be able to take advantage of that, right?
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 see the relevant discussion is still happening here: #40446 (comment)
registerLegacyAPI: (legacyAPI: LegacyAPI) => { | ||
this.legacyAPI = legacyAPI; | ||
|
||
// Register OSS features. |
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.
Are we registering OSS features here because we need the saved object types from the legacy API? It was a little confusing when I first read this, as it feels out of place right now. I'm OK leaving it as-is since it's temporary.
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.
Are we registering OSS features here because we need the saved object types from the legacy API?
Yep. And it's confusing, definitely agree. Initially I had additional setup contract registerOSSFeatures
method, but it was also confusing as "something" that is outside of Features control is supposed to initialize Features plugin at "some" point 🤷♂️
Hopefully we'll get SO on server side in NP soon and get rid of this hack.
"common.ui.savedObjects.howToSaveAsNewDescription": "Kibana の以前のバージョンでは、{savedObjectName} の名前を変更すると新しい名前でコピーが作成されました。今後この操作を行うには、「新規 {savedObjectName} として保存」を使用します。", | ||
"common.ui.savedObjects.overwriteRejectedDescription": "上書き確認が拒否されました", | ||
"common.ui.savedObjects.saveAsNewLabel": "新規 {savedObjectName} として保存", | ||
"common.ui.savedObjects.saveDuplicateRejectedDescription": "重複ファイルの保存確認が拒否されました", | ||
"kibana-react.savedObjects.saveModal.cancelButtonLabel": "キャンセル", |
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.
Would it be worthwhile to find/replace xpack.main.featureRegistry.*
with xpack.features.*
in the translated files, since we know that the translated text doesn't need to change? Or is that not advised?
// functions or removal of exports should be considered as a breaking change. Ideally we should | ||
// reduce number of such exports to zero and provide everything we want to expose via Setup/Start | ||
// run-time contracts. | ||
export { uiCapabilitiesRegex } from './feature_schema'; |
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.
For what it's worth, we should probably move uiCapabilitiesRegex
into src/core
, as UI Capabilities are a part of OSS now. You can leave it here for the time being, but if we're concerned about API surface, that's one way to reduce it 😄.
question: your comment about reducing these exports to zero doesn't include the types we export below, right? We can still export types from our plugins outside of the lifecycle methods?
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.
For what it's worth, we should probably move uiCapabilitiesRegex into src/core, as UI Capabilities are a part of OSS now. You can leave it here for the time being, but if we're concerned about API surface, that's one way to reduce it
Yeah, I tried to do that several times while working on this, but failed to do so every time 😆 I just can't find a proper place for it, it was kind of confusing that it sits in the core, but is not used there at all and it's not clear how exactly it's used while reading core code. If you can help me to find a justification I'll happily move that to the core 🙂
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.
question: your comment about reducing these exports to zero doesn't include the types we export below, right? We can still export types from our plugins outside of the lifecycle methods?
Correct!
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, I tried to do that several times while working on this, but failed to do so every time 😆 I just can't find a proper place for it, it was kind of confusing that it sits in the core, but is not used there at all and it's not clear how exactly it's used while reading core code. If you can help me to find a justification I'll happily move that to the core 🙂
It doesn't exist today, but I could see this being useful when we build the list of capabilities. Currently, plugins can provide any capabilities they'd like, even if they violate the regex. We're just trusting that they don't right now.
It's potentially also useful when we resolve capabilities. Core might want to prevent registered Capabilities Modifiers from returning a capability which violates the regex. In theory, the modifiers shouldn't be adding or removing any capabilities; rather, they should just be flipping the boolean flags...
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.
Ah, good to know, thanks. Yeah, it makes a lot of sense to have it in core then.
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.
Moved to the core.
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.
Nah, had to revert and move back to the Features
plugin. There is something weird happening when we require regex from ui/capabilities
in server code. It's probably expected, but I'll let platform team to handle that when they want to move that to core :)
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.
Thanks for trying!
@joshdover @legrego thanks for the feedback! I think I've handled everything you mentioned so far, so PR is ready for another pass. |
This comment has been minimized.
This comment has been minimized.
💔 Build Failed |
💚 Build Succeeded |
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.
Everything seems to be working great! Just a doc update needed from my perspective.
@@ -142,7 +142,7 @@ init(server) { | |||
const xpackMainPlugin = server.plugins.xpack_main; | |||
xpackMainPlugin.registerFeature({ | |||
id: 'dev_tools', | |||
name: i18n.translate('xpack.main.featureRegistry.devToolsFeatureName', { | |||
name: i18n.translate('xpack.features.devToolsFeatureName', { |
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 file has a few references to the legacy filepath that we'll want to update:
Registering a feature consists of the following fields. For more information, consult the {repo}blob/{branch}/x-pack/legacy/plugins/xpack_main/server/lib/feature_registry/feature_registry.ts[feature registry interface]. |
|{repo}blob/{branch}/x-pack/legacy/plugins/xpack_main/server/lib/feature_registry/feature_registry.ts[`FeatureWithAllOrReadPrivileges`]. |
For a full explanation of fields and options, consult the {repo}blob/{branch}/x-pack/legacy/plugins/xpack_main/server/lib/feature_registry/feature_registry.ts[feature registry interface]. |
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.
Good catch!
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.
LGTM on green CI. Thanks!
💔 Build Failed |
Looks like unrelated issue. |
retest |
💚 Build Succeeded |
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.
LGTM, thanks @azasypkin!
💚 Build Succeeded |
7.x/7.5.0: 6341714 |
We're moving Security Authorization system to the New Platform and it depends on the "features" functionality. To minimize a number of dependencies on the Legacy Platform plugins we're introducing "Features" plugin that will solely responsible for the base Feature Controls functionality.
NOTE: it's easier to review this PR commit by commit because of
git mv
. Not much of new code is written here, I just moved some code and split some large files into smaller ones.Blocked by: #44855