Skip to content
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

feat(security): extend Feature definition to support explicit feature replacements #206660

Merged
merged 2 commits into from
Jan 16, 2025

Conversation

azasypkin
Copy link
Member

@azasypkin azasypkin commented Jan 14, 2025

Summary

Today, when a developer deprecates a feature and replaces its privileges with those of another feature, we reasonably assume that the new feature fully replaces the old one in all possible contexts - whether in role management UIs or in the Spaces feature toggles visibility UI. However, when deprecated privileges are replaced by the privileges of multiple features, such as in this case where the Discover/Dashboard/Maps feature privileges are replaced by the privileges of Discover_v2/Dashboard_v2/Maps_v2, respectively, and the privileges of the Saved Query Management feature, the choice is ambiguous.

Which of these features should be treated as the replacement for the deprecated feature in contexts that deal with entire features (like the Spaces feature toggles visibility UI) rather than individual privileges (like in role management UIs)? Should all referenced features be considered replacements? Or just a subset - or even a single feature? If so, which one? Currently, we treat all referenced features as replacements for the deprecated feature, which creates problems, as described in detail in this discussion.

This PR allows developers to customize this behavior by specifying which features Kibana should treat as direct successors to deprecated features in contexts that deal with whole features rather than individual privileges:

deps.features.registerKibanaFeature({
  deprecated: {
    notice: 'The feature is deprecated because … well, there’s a reason.',
    --> replacedBy: ['feature_id_v2'], <--
  },
  id: 'feature_id'
  name: `Case #4 feature ${suffix} (DEPRECATED)`,});

How to test

  1. Run test server
node scripts/functional_tests_server.js --config x-pack/test/security_api_integration/features.config.ts
  1. Execute the following request from the Dev Tools (case_4_feature_a is a deprecated feature that is replaced by multiple features and doesn't use deprecated.replacedBy)
PUT kbn:/api/spaces/space/default?overwrite=true
{
  "id":"default",
  "name":"Default",
  "description":"This is your default space!",
  "color":"#00bfb3",
  "disabledFeatures":["case_4_feature_a"],
  "_reserved":true,
  "imageUrl":"",
  "initials":"D"
}
  1. Observe that in response deprecated case_4_feature_a is replaced by two features (you can also check http://localhost:5620/app/management/kibana/spaces/edit/default to see how it's reflected in UI)
{
  "id": "default",
  "name": "Default",
  "description": "This is your default space!",
  "color": "#00bfb3",
  "initials": "D",
  "imageUrl": "",
  "disabledFeatures": [
    "case_4_feature_a_v2",
    "case_4_feature_c"
  ],
  "_reserved": true
}
  1. Execute the following request from the Dev Tools (case_4_feature_b is a deprecated feature that is replaced by multiple features, but uses deprecated.replacedBy to set the conceptual feature-successor)
PUT kbn:/api/spaces/space/default?overwrite=true
{
  "id":"default",
  "name":"Default",
  "description":"This is your default space!",
  "color":"#00bfb3",
  "disabledFeatures":["case_4_feature_b"],
  "_reserved":true,
  "imageUrl":"",
  "initials":"D"
}
  1. Observe that in response deprecated case_4_feature_b is replaced by a single feature (you can also check http://localhost:5620/app/management/kibana/spaces/edit/default to see how it's reflected in UI)
{
  "id": "default",
  "name": "Default",
  "description": "This is your default space!",
  "color": "#00bfb3",
  "initials": "D",
  "imageUrl": "",
  "disabledFeatures": [
    "case_4_feature_b_v2"
  ],
  "_reserved": true
}

Required by: #202863 (comment)

//cc @davismcphee

@azasypkin azasypkin added Feature:Security/Spaces Platform Security - Spaces feature release_note:skip Skip the PR/issue when compiling release notes Feature:Security/Authorization Platform Security - Authorization backport:prev-minor Backport to (8.x) the previous minor version (i.e. one version back from main) labels Jan 14, 2025
* `replacedBy` fields of the feature privileges. However, if the feature privileges are replaced by the privileges
* of multiple features, this behavior is not always desired and can be overridden here.
*/
replacedBy?: readonly string[];
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: I couldn't come up with a better name to describe what it's for, but open to suggestions...

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

Metrics [docs]

Public APIs missing comments

Total count of every public API that lacks a comment. Target amount is 0. Run node scripts/build_api_docs --plugin [yourplugin] --stats comments for more detailed information.

id before after diff
features 110 109 -1
Unknown metric groups

API count

id before after diff
features 270 273 +3

References to deprecated APIs

id before after diff
spaces 14 12 -2

/**
* Describes parameters used to retrieve all Kibana features (for public consumers).
*/
export interface GetKibanaFeaturesParams {
Copy link
Member Author

@azasypkin azasypkin Jan 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: Due to the way the start contract of the Feature plugin was defined, it was technically possible to pass any arguments to the getKibanaFeatures method, even though this was not reflected in the type definition:

getKibanaFeatures: this.featureRegistry.getAllKibanaFeatures.bind(this.featureRegistry),

This change explicitly defines the list of accepted arguments, ensuring that only omitDeprecated is exposed publicly:

getKibanaFeatures: (params) => this.featureRegistry.getAllKibanaFeatures(
  params && { omitDeprecated: params.omitDeprecated }
),

@@ -106,7 +106,10 @@ export function initSpacesOnPostAuthRequestInterceptor({
}
}

const allFeatures = features.getKibanaFeatures();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: getKibanaFeatures in setup contract is deprecated.

@azasypkin azasypkin marked this pull request as ready for review January 15, 2025 10:58
@azasypkin azasypkin requested review from a team as code owners January 15, 2025 10:58
Copy link
Member

@afharo afharo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Core changes LGTM. Code review only.

Copy link
Contributor

@davismcphee davismcphee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I merged this into my PR branch to test and confirmed it's working as expected. I can now deprecate the necessary features and choose which ones replace them separately from the deprecated privileges. Thanks for addressing this so quickly!

Without replacedBy: ['dashboard_v2']:
without

With replacedBy: ['dashboard_v2']:
with

Copy link
Contributor

@SiddharthMantri SiddharthMantri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! tested and synced with @azasypkin about the changes.

@azasypkin
Copy link
Member Author

@elasticmachine merge upstream

@azasypkin azasypkin enabled auto-merge (squash) January 16, 2025 09:53
@azasypkin azasypkin merged commit dd3ce0e into elastic:main Jan 16, 2025
9 checks passed
@kibanamachine
Copy link
Contributor

Starting backport for target branches: 8.x

https://github.com/elastic/kibana/actions/runs/12808033095

kibanamachine pushed a commit to kibanamachine/kibana that referenced this pull request Jan 16, 2025
…re replacements (elastic#206660)

## Summary

Today, when a developer deprecates a feature and replaces its privileges
with those of another feature, we reasonably assume that the new feature
fully replaces the old one in all possible contexts - whether in role
management UIs or in the Spaces feature toggles visibility UI. However,
when deprecated privileges are replaced by the privileges of multiple
features, such as in [this
case](elastic#202863 (comment))
where the Discover/Dashboard/Maps feature privileges are replaced by the
privileges of Discover_v2/Dashboard_v2/Maps_v2, respectively, **and**
the privileges of the Saved Query Management feature, the choice is
ambiguous.

Which of these features should be treated as the replacement for the
deprecated feature in contexts that deal with entire features (like the
Spaces feature toggles visibility UI) rather than individual privileges
(like in role management UIs)? Should all referenced features be
considered replacements? Or just a subset - or even a single feature? If
so, which one? Currently, we treat all referenced features as
replacements for the deprecated feature, which creates problems, as
described in detail in [this
discussion](elastic#202863 (comment)).

This PR allows developers to customize this behavior by specifying which
features Kibana should treat as direct successors to deprecated features
in contexts that deal with whole features rather than individual
privileges:

```ts
deps.features.registerKibanaFeature({
  deprecated: {
    notice: 'The feature is deprecated because … well, there’s a reason.',
    --> replacedBy: ['feature_id_v2'], <--
  },
  id: 'feature_id'
  name: `Case elastic#4 feature ${suffix} (DEPRECATED)`,
  …
});
```

## How to test

1. Run test server
```bash
node scripts/functional_tests_server.js --config x-pack/test/security_api_integration/features.config.ts
```

2. Execute the following request from the Dev Tools (`case_4_feature_a`
is a deprecated feature that is replaced by multiple features and
**doesn't use** `deprecated.replacedBy`)
```http
PUT kbn:/api/spaces/space/default?overwrite=true
{
  "id":"default",
  "name":"Default",
  "description":"This is your default space!",
  "color":"#00bfb3",
  "disabledFeatures":["case_4_feature_a"],
  "_reserved":true,
  "imageUrl":"",
  "initials":"D"
}
```

3. Observe that in response deprecated `case_4_feature_a` is replaced by
two features (you can also check
http://localhost:5620/app/management/kibana/spaces/edit/default to see
how it's reflected in UI)
```http
{
  "id": "default",
  "name": "Default",
  "description": "This is your default space!",
  "color": "#00bfb3",
  "initials": "D",
  "imageUrl": "",
  "disabledFeatures": [
    "case_4_feature_a_v2",
    "case_4_feature_c"
  ],
  "_reserved": true
}
```

4. Execute the following request from the Dev Tools (`case_4_feature_b`
is a deprecated feature that is replaced by multiple features, but
**uses** `deprecated.replacedBy` to set the conceptual
feature-successor)
```http
PUT kbn:/api/spaces/space/default?overwrite=true
{
  "id":"default",
  "name":"Default",
  "description":"This is your default space!",
  "color":"#00bfb3",
  "disabledFeatures":["case_4_feature_b"],
  "_reserved":true,
  "imageUrl":"",
  "initials":"D"
}
```

5. Observe that in response deprecated `case_4_feature_b` is replaced by
a single feature (you can also check
http://localhost:5620/app/management/kibana/spaces/edit/default to see
how it's reflected in UI)
```http
{
  "id": "default",
  "name": "Default",
  "description": "This is your default space!",
  "color": "#00bfb3",
  "initials": "D",
  "imageUrl": "",
  "disabledFeatures": [
    "case_4_feature_b_v2"
  ],
  "_reserved": true
}
```

__Required by:__
elastic#202863 (comment)

//cc @davismcphee

(cherry picked from commit dd3ce0e)
@kibanamachine
Copy link
Contributor

💚 All backports created successfully

Status Branch Result
8.x

Note: Successful backport PRs will be merged automatically after passing CI.

Questions ?

Please refer to the Backport tool documentation

kibanamachine added a commit that referenced this pull request Jan 16, 2025
…t explicit feature replacements (#206660) (#206925)

# Backport

This will backport the following commits from `main` to `8.x`:
- [feat(security): extend &#x60;Feature&#x60; definition to support
explicit feature replacements
(#206660)](#206660)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Aleh
Zasypkin","email":"[email protected]"},"sourceCommit":{"committedDate":"2025-01-16T11:35:32Z","message":"feat(security):
extend `Feature` definition to support explicit feature replacements
(#206660)\n\n## Summary\n\nToday, when a developer deprecates a feature
and replaces its privileges\nwith those of another feature, we
reasonably assume that the new feature\nfully replaces the old one in
all possible contexts - whether in role\nmanagement UIs or in the Spaces
feature toggles visibility UI. However,\nwhen deprecated privileges are
replaced by the privileges of multiple\nfeatures, such as in
[this\ncase](https://github.com/elastic/kibana/pull/202863#discussion_r1892672114)\nwhere
the Discover/Dashboard/Maps feature privileges are replaced by
the\nprivileges of Discover_v2/Dashboard_v2/Maps_v2, respectively,
**and**\nthe privileges of the Saved Query Management feature, the
choice is\nambiguous.\n\nWhich of these features should be treated as
the replacement for the\ndeprecated feature in contexts that deal with
entire features (like the\nSpaces feature toggles visibility UI) rather
than individual privileges\n(like in role management UIs)? Should all
referenced features be\nconsidered replacements? Or just a subset - or
even a single feature? If\nso, which one? Currently, we treat all
referenced features as\nreplacements for the deprecated feature, which
creates problems, as\ndescribed in detail in
[this\ndiscussion](https://github.com/elastic/kibana/pull/202863#discussion_r1892672114).\n\nThis
PR allows developers to customize this behavior by specifying
which\nfeatures Kibana should treat as direct successors to deprecated
features\nin contexts that deal with whole features rather than
individual\nprivileges:\n\n```ts\ndeps.features.registerKibanaFeature({\n
deprecated: {\n notice: 'The feature is deprecated because … well,
there’s a reason.',\n --> replacedBy: ['feature_id_v2'], <--\n },\n id:
'feature_id'\n name: `Case #4 feature ${suffix} (DEPRECATED)`,\n
…\n});\n```\n\n## How to test\n\n1. Run test server\n```bash\nnode
scripts/functional_tests_server.js --config
x-pack/test/security_api_integration/features.config.ts\n```\n\n2.
Execute the following request from the Dev Tools (`case_4_feature_a`\nis
a deprecated feature that is replaced by multiple features
and\n**doesn't use** `deprecated.replacedBy`)\n```http\nPUT
kbn:/api/spaces/space/default?overwrite=true\n{\n \"id\":\"default\",\n
\"name\":\"Default\",\n \"description\":\"This is your default
space!\",\n \"color\":\"#00bfb3\",\n
\"disabledFeatures\":[\"case_4_feature_a\"],\n \"_reserved\":true,\n
\"imageUrl\":\"\",\n \"initials\":\"D\"\n}\n```\n\n3. Observe that in
response deprecated `case_4_feature_a` is replaced by\ntwo features (you
can also
check\nhttp://localhost:5620/app/management/kibana/spaces/edit/default
to see\nhow it's reflected in UI)\n```http\n{\n \"id\": \"default\",\n
\"name\": \"Default\",\n \"description\": \"This is your default
space!\",\n \"color\": \"#00bfb3\",\n \"initials\": \"D\",\n
\"imageUrl\": \"\",\n \"disabledFeatures\": [\n
\"case_4_feature_a_v2\",\n \"case_4_feature_c\"\n ],\n \"_reserved\":
true\n}\n```\n\n4. Execute the following request from the Dev Tools
(`case_4_feature_b`\nis a deprecated feature that is replaced by
multiple features, but\n**uses** `deprecated.replacedBy` to set the
conceptual\nfeature-successor)\n```http\nPUT
kbn:/api/spaces/space/default?overwrite=true\n{\n \"id\":\"default\",\n
\"name\":\"Default\",\n \"description\":\"This is your default
space!\",\n \"color\":\"#00bfb3\",\n
\"disabledFeatures\":[\"case_4_feature_b\"],\n \"_reserved\":true,\n
\"imageUrl\":\"\",\n \"initials\":\"D\"\n}\n```\n\n5. Observe that in
response deprecated `case_4_feature_b` is replaced by\na single feature
(you can also
check\nhttp://localhost:5620/app/management/kibana/spaces/edit/default
to see\nhow it's reflected in UI)\n```http\n{\n \"id\": \"default\",\n
\"name\": \"Default\",\n \"description\": \"This is your default
space!\",\n \"color\": \"#00bfb3\",\n \"initials\": \"D\",\n
\"imageUrl\": \"\",\n \"disabledFeatures\": [\n
\"case_4_feature_b_v2\"\n ],\n \"_reserved\": true\n}\n```\n\n__Required
by:__\nhttps://github.com//pull/202863#discussion_r1892672114\n\n//cc
@davismcphee","sha":"dd3ce0e7f534279f48be8c125853c89aa92969e2","branchLabelMapping":{"^v9.0.0$":"main","^v8.18.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["Feature:Security/Spaces","release_note:skip","Feature:Security/Authorization","v9.0.0","backport:prev-minor"],"title":"feat(security):
extend `Feature` definition to support explicit feature
replacements","number":206660,"url":"https://github.com/elastic/kibana/pull/206660","mergeCommit":{"message":"feat(security):
extend `Feature` definition to support explicit feature replacements
(#206660)\n\n## Summary\n\nToday, when a developer deprecates a feature
and replaces its privileges\nwith those of another feature, we
reasonably assume that the new feature\nfully replaces the old one in
all possible contexts - whether in role\nmanagement UIs or in the Spaces
feature toggles visibility UI. However,\nwhen deprecated privileges are
replaced by the privileges of multiple\nfeatures, such as in
[this\ncase](https://github.com/elastic/kibana/pull/202863#discussion_r1892672114)\nwhere
the Discover/Dashboard/Maps feature privileges are replaced by
the\nprivileges of Discover_v2/Dashboard_v2/Maps_v2, respectively,
**and**\nthe privileges of the Saved Query Management feature, the
choice is\nambiguous.\n\nWhich of these features should be treated as
the replacement for the\ndeprecated feature in contexts that deal with
entire features (like the\nSpaces feature toggles visibility UI) rather
than individual privileges\n(like in role management UIs)? Should all
referenced features be\nconsidered replacements? Or just a subset - or
even a single feature? If\nso, which one? Currently, we treat all
referenced features as\nreplacements for the deprecated feature, which
creates problems, as\ndescribed in detail in
[this\ndiscussion](https://github.com/elastic/kibana/pull/202863#discussion_r1892672114).\n\nThis
PR allows developers to customize this behavior by specifying
which\nfeatures Kibana should treat as direct successors to deprecated
features\nin contexts that deal with whole features rather than
individual\nprivileges:\n\n```ts\ndeps.features.registerKibanaFeature({\n
deprecated: {\n notice: 'The feature is deprecated because … well,
there’s a reason.',\n --> replacedBy: ['feature_id_v2'], <--\n },\n id:
'feature_id'\n name: `Case #4 feature ${suffix} (DEPRECATED)`,\n
…\n});\n```\n\n## How to test\n\n1. Run test server\n```bash\nnode
scripts/functional_tests_server.js --config
x-pack/test/security_api_integration/features.config.ts\n```\n\n2.
Execute the following request from the Dev Tools (`case_4_feature_a`\nis
a deprecated feature that is replaced by multiple features
and\n**doesn't use** `deprecated.replacedBy`)\n```http\nPUT
kbn:/api/spaces/space/default?overwrite=true\n{\n \"id\":\"default\",\n
\"name\":\"Default\",\n \"description\":\"This is your default
space!\",\n \"color\":\"#00bfb3\",\n
\"disabledFeatures\":[\"case_4_feature_a\"],\n \"_reserved\":true,\n
\"imageUrl\":\"\",\n \"initials\":\"D\"\n}\n```\n\n3. Observe that in
response deprecated `case_4_feature_a` is replaced by\ntwo features (you
can also
check\nhttp://localhost:5620/app/management/kibana/spaces/edit/default
to see\nhow it's reflected in UI)\n```http\n{\n \"id\": \"default\",\n
\"name\": \"Default\",\n \"description\": \"This is your default
space!\",\n \"color\": \"#00bfb3\",\n \"initials\": \"D\",\n
\"imageUrl\": \"\",\n \"disabledFeatures\": [\n
\"case_4_feature_a_v2\",\n \"case_4_feature_c\"\n ],\n \"_reserved\":
true\n}\n```\n\n4. Execute the following request from the Dev Tools
(`case_4_feature_b`\nis a deprecated feature that is replaced by
multiple features, but\n**uses** `deprecated.replacedBy` to set the
conceptual\nfeature-successor)\n```http\nPUT
kbn:/api/spaces/space/default?overwrite=true\n{\n \"id\":\"default\",\n
\"name\":\"Default\",\n \"description\":\"This is your default
space!\",\n \"color\":\"#00bfb3\",\n
\"disabledFeatures\":[\"case_4_feature_b\"],\n \"_reserved\":true,\n
\"imageUrl\":\"\",\n \"initials\":\"D\"\n}\n```\n\n5. Observe that in
response deprecated `case_4_feature_b` is replaced by\na single feature
(you can also
check\nhttp://localhost:5620/app/management/kibana/spaces/edit/default
to see\nhow it's reflected in UI)\n```http\n{\n \"id\": \"default\",\n
\"name\": \"Default\",\n \"description\": \"This is your default
space!\",\n \"color\": \"#00bfb3\",\n \"initials\": \"D\",\n
\"imageUrl\": \"\",\n \"disabledFeatures\": [\n
\"case_4_feature_b_v2\"\n ],\n \"_reserved\": true\n}\n```\n\n__Required
by:__\nhttps://github.com//pull/202863#discussion_r1892672114\n\n//cc
@davismcphee","sha":"dd3ce0e7f534279f48be8c125853c89aa92969e2"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/206660","number":206660,"mergeCommit":{"message":"feat(security):
extend `Feature` definition to support explicit feature replacements
(#206660)\n\n## Summary\n\nToday, when a developer deprecates a feature
and replaces its privileges\nwith those of another feature, we
reasonably assume that the new feature\nfully replaces the old one in
all possible contexts - whether in role\nmanagement UIs or in the Spaces
feature toggles visibility UI. However,\nwhen deprecated privileges are
replaced by the privileges of multiple\nfeatures, such as in
[this\ncase](https://github.com/elastic/kibana/pull/202863#discussion_r1892672114)\nwhere
the Discover/Dashboard/Maps feature privileges are replaced by
the\nprivileges of Discover_v2/Dashboard_v2/Maps_v2, respectively,
**and**\nthe privileges of the Saved Query Management feature, the
choice is\nambiguous.\n\nWhich of these features should be treated as
the replacement for the\ndeprecated feature in contexts that deal with
entire features (like the\nSpaces feature toggles visibility UI) rather
than individual privileges\n(like in role management UIs)? Should all
referenced features be\nconsidered replacements? Or just a subset - or
even a single feature? If\nso, which one? Currently, we treat all
referenced features as\nreplacements for the deprecated feature, which
creates problems, as\ndescribed in detail in
[this\ndiscussion](https://github.com/elastic/kibana/pull/202863#discussion_r1892672114).\n\nThis
PR allows developers to customize this behavior by specifying
which\nfeatures Kibana should treat as direct successors to deprecated
features\nin contexts that deal with whole features rather than
individual\nprivileges:\n\n```ts\ndeps.features.registerKibanaFeature({\n
deprecated: {\n notice: 'The feature is deprecated because … well,
there’s a reason.',\n --> replacedBy: ['feature_id_v2'], <--\n },\n id:
'feature_id'\n name: `Case #4 feature ${suffix} (DEPRECATED)`,\n
…\n});\n```\n\n## How to test\n\n1. Run test server\n```bash\nnode
scripts/functional_tests_server.js --config
x-pack/test/security_api_integration/features.config.ts\n```\n\n2.
Execute the following request from the Dev Tools (`case_4_feature_a`\nis
a deprecated feature that is replaced by multiple features
and\n**doesn't use** `deprecated.replacedBy`)\n```http\nPUT
kbn:/api/spaces/space/default?overwrite=true\n{\n \"id\":\"default\",\n
\"name\":\"Default\",\n \"description\":\"This is your default
space!\",\n \"color\":\"#00bfb3\",\n
\"disabledFeatures\":[\"case_4_feature_a\"],\n \"_reserved\":true,\n
\"imageUrl\":\"\",\n \"initials\":\"D\"\n}\n```\n\n3. Observe that in
response deprecated `case_4_feature_a` is replaced by\ntwo features (you
can also
check\nhttp://localhost:5620/app/management/kibana/spaces/edit/default
to see\nhow it's reflected in UI)\n```http\n{\n \"id\": \"default\",\n
\"name\": \"Default\",\n \"description\": \"This is your default
space!\",\n \"color\": \"#00bfb3\",\n \"initials\": \"D\",\n
\"imageUrl\": \"\",\n \"disabledFeatures\": [\n
\"case_4_feature_a_v2\",\n \"case_4_feature_c\"\n ],\n \"_reserved\":
true\n}\n```\n\n4. Execute the following request from the Dev Tools
(`case_4_feature_b`\nis a deprecated feature that is replaced by
multiple features, but\n**uses** `deprecated.replacedBy` to set the
conceptual\nfeature-successor)\n```http\nPUT
kbn:/api/spaces/space/default?overwrite=true\n{\n \"id\":\"default\",\n
\"name\":\"Default\",\n \"description\":\"This is your default
space!\",\n \"color\":\"#00bfb3\",\n
\"disabledFeatures\":[\"case_4_feature_b\"],\n \"_reserved\":true,\n
\"imageUrl\":\"\",\n \"initials\":\"D\"\n}\n```\n\n5. Observe that in
response deprecated `case_4_feature_b` is replaced by\na single feature
(you can also
check\nhttp://localhost:5620/app/management/kibana/spaces/edit/default
to see\nhow it's reflected in UI)\n```http\n{\n \"id\": \"default\",\n
\"name\": \"Default\",\n \"description\": \"This is your default
space!\",\n \"color\": \"#00bfb3\",\n \"initials\": \"D\",\n
\"imageUrl\": \"\",\n \"disabledFeatures\": [\n
\"case_4_feature_b_v2\"\n ],\n \"_reserved\": true\n}\n```\n\n__Required
by:__\nhttps://github.com//pull/202863#discussion_r1892672114\n\n//cc
@davismcphee","sha":"dd3ce0e7f534279f48be8c125853c89aa92969e2"}}]}]
BACKPORT-->

Co-authored-by: Aleh Zasypkin <[email protected]>
@azasypkin azasypkin deleted the issue-xxx-features-replaced-by branch January 16, 2025 15:00
viduni94 pushed a commit to viduni94/kibana that referenced this pull request Jan 23, 2025
…re replacements (elastic#206660)

## Summary

Today, when a developer deprecates a feature and replaces its privileges
with those of another feature, we reasonably assume that the new feature
fully replaces the old one in all possible contexts - whether in role
management UIs or in the Spaces feature toggles visibility UI. However,
when deprecated privileges are replaced by the privileges of multiple
features, such as in [this
case](elastic#202863 (comment))
where the Discover/Dashboard/Maps feature privileges are replaced by the
privileges of Discover_v2/Dashboard_v2/Maps_v2, respectively, **and**
the privileges of the Saved Query Management feature, the choice is
ambiguous.

Which of these features should be treated as the replacement for the
deprecated feature in contexts that deal with entire features (like the
Spaces feature toggles visibility UI) rather than individual privileges
(like in role management UIs)? Should all referenced features be
considered replacements? Or just a subset - or even a single feature? If
so, which one? Currently, we treat all referenced features as
replacements for the deprecated feature, which creates problems, as
described in detail in [this
discussion](elastic#202863 (comment)).

This PR allows developers to customize this behavior by specifying which
features Kibana should treat as direct successors to deprecated features
in contexts that deal with whole features rather than individual
privileges:

```ts
deps.features.registerKibanaFeature({
  deprecated: {
    notice: 'The feature is deprecated because … well, there’s a reason.',
    --> replacedBy: ['feature_id_v2'], <--
  },
  id: 'feature_id'
  name: `Case elastic#4 feature ${suffix} (DEPRECATED)`,
  …
});
```

## How to test

1. Run test server
```bash
node scripts/functional_tests_server.js --config x-pack/test/security_api_integration/features.config.ts
```

2. Execute the following request from the Dev Tools (`case_4_feature_a`
is a deprecated feature that is replaced by multiple features and
**doesn't use** `deprecated.replacedBy`)
```http
PUT kbn:/api/spaces/space/default?overwrite=true
{
  "id":"default",
  "name":"Default",
  "description":"This is your default space!",
  "color":"#00bfb3",
  "disabledFeatures":["case_4_feature_a"],
  "_reserved":true,
  "imageUrl":"",
  "initials":"D"
}
```

3. Observe that in response deprecated `case_4_feature_a` is replaced by
two features (you can also check
http://localhost:5620/app/management/kibana/spaces/edit/default to see
how it's reflected in UI)
```http
{
  "id": "default",
  "name": "Default",
  "description": "This is your default space!",
  "color": "#00bfb3",
  "initials": "D",
  "imageUrl": "",
  "disabledFeatures": [
    "case_4_feature_a_v2",
    "case_4_feature_c"
  ],
  "_reserved": true
}
```

4. Execute the following request from the Dev Tools (`case_4_feature_b`
is a deprecated feature that is replaced by multiple features, but
**uses** `deprecated.replacedBy` to set the conceptual
feature-successor)
```http
PUT kbn:/api/spaces/space/default?overwrite=true
{
  "id":"default",
  "name":"Default",
  "description":"This is your default space!",
  "color":"#00bfb3",
  "disabledFeatures":["case_4_feature_b"],
  "_reserved":true,
  "imageUrl":"",
  "initials":"D"
}
```

5. Observe that in response deprecated `case_4_feature_b` is replaced by
a single feature (you can also check
http://localhost:5620/app/management/kibana/spaces/edit/default to see
how it's reflected in UI)
```http
{
  "id": "default",
  "name": "Default",
  "description": "This is your default space!",
  "color": "#00bfb3",
  "initials": "D",
  "imageUrl": "",
  "disabledFeatures": [
    "case_4_feature_b_v2"
  ],
  "_reserved": true
}
```

__Required by:__
elastic#202863 (comment)

//cc @davismcphee
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport:prev-minor Backport to (8.x) the previous minor version (i.e. one version back from main) Feature:Security/Authorization Platform Security - Authorization Feature:Security/Spaces Platform Security - Spaces feature release_note:skip Skip the PR/issue when compiling release notes v8.18.0 v9.0.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants