Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[8.x] feat(security): extend `Feature` definition to suppor…
…t explicit feature replacements (#206660) (#206925) # Backport This will backport the following commits from `main` to `8.x`: - [feat(security): extend `Feature` 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]>
- Loading branch information