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

[Cases] [Security Solution] New cases subfeatures, add comments and reopen cases #194898

Open
wants to merge 61 commits into
base: main
Choose a base branch
from

Conversation

kqualters-elastic
Copy link
Contributor

@kqualters-elastic kqualters-elastic commented Oct 4, 2024

Summary

This pr adds 2 new sub feature permissions to the cases plugin in stack/security/observability, that behave as follows. The first is for controlling the ability to reopen cases. When Cases has the read permission, and the reopen permission is not enabled, users have permissions as before. When enabled, users can move cases from closed to open/in progress, but nothing else. If a user has all and this permission, they can do anything as before, if the option is unselected, they can change case properties, and change a case from open to anything, in progress to anything, but if the case is closed, are unable to reopen it.

The 2nd permission is 'Add comment'. When enabled and the user has case read permissions, users can add comments, but not make any other changes to the case. When the user has read and this deselected, read functions as before. When a user has this permission and cases is all, this functions as all. When they have all but this permission is deselected, the user can do everything normally, except add cases comments.

Checklist

@kqualters-elastic kqualters-elastic added v8.16.0 v9.0.0 release_note:feature Makes this part of the condensed release notes backport:version Backport to applied version labels labels Oct 7, 2024
@kqualters-elastic kqualters-elastic marked this pull request as ready for review October 7, 2024 02:30
@kqualters-elastic kqualters-elastic requested review from a team as code owners October 7, 2024 02:30
@botelastic botelastic bot added ci:project-deploy-observability Create an Observability project Team:obs-ux-management Observability Management User Experience Team labels Oct 7, 2024
@elasticmachine
Copy link
Contributor

Pinging @elastic/obs-ux-management-team (Team:obs-ux-management)

@kibana-ci

This comment was marked as outdated.

Copy link
Contributor

@jloleysens jloleysens left a comment

Choose a reason for hiding this comment

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

Focussed on features plugin and left a few nits that would be nice to address before merging.

Comment on lines 219 to 238
createComment?: readonly string[];
reopenCases?: readonly string[];
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: missing doc comments like other interface members

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will update, although I think the grammar for all of these doc blocks is a little unclear. Proposed change for all of them: "List of case owners whose users should have settings access when granted this privilege.", "List of case owners whose users should have comment creation access when granted this privilege." etc. cc @cnasikas

Copy link
Member

Choose a reason for hiding this comment

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

Sounds good to me. Also could you please add a comment on top of the update to mention that it does not include reopening a case?

@@ -151,6 +151,14 @@ function mergeWithSubFeatures(
mergedConfig.cases?.settings ?? [],
subFeaturePrivilege.cases?.settings ?? []
),
createComment: mergeArrays(
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: would you mind including these in the relevant unit test in x-pack/plugins/features/server/feature_privilege_iterator/feature_privilege_iterator.test.ts

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ya will update, although that seems like something that typescript should catch

Copy link
Member

Choose a reason for hiding this comment

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

+1 for adding unit tests.

Copy link
Contributor

Choose a reason for hiding this comment

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

updated

Copy link
Contributor

@cauemarcondes cauemarcondes left a comment

Choose a reason for hiding this comment

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

Obs shared LGTM

@azasypkin azasypkin self-requested a review October 7, 2024 11:15
Copy link
Contributor

@lgestc lgestc left a comment

Choose a reason for hiding this comment

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

looks okay, will also try to test it later today

Copy link
Member

@cnasikas cnasikas left a comment

Choose a reason for hiding this comment

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

Hey! I did a first pass of the PR focusing mostly on the backend code. I left some comments.

@@ -171,6 +171,8 @@ export const DELETE_CASES_CAPABILITY = 'delete_cases' as const;
export const PUSH_CASES_CAPABILITY = 'push_cases' as const;
export const CASES_SETTINGS_CAPABILITY = 'cases_settings' as const;
export const CASES_CONNECTORS_CAPABILITY = 'cases_connectors' as const;
export const REOPEN_CASES_CAPABILITY = 'reopen_cases' as const;
export const COMMENT_CASES_CAPABILITY = 'create_comment' as const;
Copy link
Member

Choose a reason for hiding this comment

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

super nit: COMMENT_CASES_CAPABILITY -> CREATE_COMMENT_CAPABILITY

Copy link
Contributor

Choose a reason for hiding this comment

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

Done

Comment on lines 219 to 238
createComment?: readonly string[];
reopenCases?: readonly string[];
Copy link
Member

Choose a reason for hiding this comment

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

Sounds good to me. Also could you please add a comment on top of the update to mention that it does not include reopening a case?

@@ -151,6 +151,14 @@ function mergeWithSubFeatures(
mergedConfig.cases?.settings ?? [],
subFeaturePrivilege.cases?.settings ?? []
),
createComment: mergeArrays(
Copy link
Member

Choose a reason for hiding this comment

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

+1 for adding unit tests.

@@ -148,6 +148,8 @@ export enum SecuritySubFeatureId {
export enum CasesSubFeatureId {
deleteCases = 'deleteCasesSubFeature',
casesSettings = 'casesSettingsSubFeature',
addComment = 'addCommentSubFeature',
Copy link
Member

Choose a reason for hiding this comment

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

nit: Let's stick with the createComment* terminology.

Copy link
Contributor

Choose a reason for hiding this comment

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

+1

Copy link
Contributor

Choose a reason for hiding this comment

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

done

Comment on lines 37 to 38
const createCommentOperations = ['createComment'] as const;
const reopenOperations = ['reopenCases'] as const;
Copy link
Member

Choose a reason for hiding this comment

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

nit: Because they are single operations I think it is better if we do const createComment = 'createComment' etc. Wdyt?

Copy link
Contributor

Choose a reason for hiding this comment

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

I would prefer actually if we left this as is for consistency in the getCasesPrivilege fn and the way allOperations is built. Also the naming paradigm operations maintains that consistency as well. Willing to make the change if you'd still like us to, but think this way is a bit simpler

@@ -51,15 +51,15 @@ describe('useStatusAction', () => {
},
Object {
"data-test-subj": "cases-bulk-action-status-in-progress",
"disabled": false,
Copy link
Member

Choose a reason for hiding this comment

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

Let's keep the test with disabled: false and then add some tests that test the disabled behavior.

Copy link
Contributor

Choose a reason for hiding this comment

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

Done

@@ -52,47 +52,6 @@ describe('useBulkActions', () => {
Object {
"id": 0,
"items": Array [
Object {
Copy link
Member

Choose a reason for hiding this comment

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

Let's keep the test without removing the actions and then add some tests that test the disabled behavior.

const canChangeStatus = useMemo(() => {
// User has full permissions
if (permissions.update && permissions.reopenCases) {
return false;
Copy link
Member

Choose a reason for hiding this comment

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

Should we return true?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yea, had to rethink this a bit

// User has full permissions
if (permissions.update && permissions.reopenCases) {
return false;
} else {
Copy link
Member

Choose a reason for hiding this comment

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

nit: What do you think of escaping early the if statements?

Copy link
Contributor

Choose a reason for hiding this comment

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

Rethought this file a bit. Hopefully it makes more sense now

import { useCasesContext } from '../cases_context/use_cases_context';
import { CaseStatuses } from '../../../common/types/domain';

export const useUserPermissions = ({ status }: { status?: CaseStatuses }) => {
Copy link
Member

Choose a reason for hiding this comment

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

nit: status -> statusToAuthorize

Copy link
Contributor

@dominiqueclarke dominiqueclarke left a comment

Choose a reason for hiding this comment

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

obs-ux-management changes LGTM

Copy link
Contributor

@semd semd left a comment

Choose a reason for hiding this comment

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

Security Threat Hunting Explore code LGTM

@@ -46,7 +46,7 @@ viewer:
- feature_siem.read
- feature_siem.read_alerts
- feature_siem.endpoint_list_read
- feature_securitySolutionCases.read
- feature_securitySolutionCasesV2.read
Copy link
Member

Choose a reason for hiding this comment

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

Are these new privileges for all these roles also updated in the elasticsearch-controller repo? The privileges in this file here should be a copy of those. If not, I'd suggest you create a PR on the other repo first, to update the privileges before merging this PR.

Copy link
Member

Choose a reason for hiding this comment

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

The privileges in roles.yml, security_roles.json, roles.yml and project_controller_security_roles.yml need to be in sync with the ones in elasticsearch-controller for our testing needs.

Copy link
Member

Choose a reason for hiding this comment

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

If not, I'd suggest you create a PR on the other repo first, to update the privileges before merging this PR.

As I explained in https://github.com/elastic/kibana/pull/194898/files#r1801715345, it might be easier to do it the other way around: first merge the PR, wait until the changes reach production, and then drop legacy privileges from the predefined roles. Alternatively, you could update the predefined roles first, but you’d need to keep both old and new privileges since we’ll have Kibana pods running on different versions simultaneously, and later update predefined roles once again to remove legacy privileges.

@azasypkin azasypkin self-requested a review November 4, 2024 13:50
Copy link
Member

@azasypkin azasypkin left a comment

Choose a reason for hiding this comment

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

LGTM from the AppEx Platform Security side (code review only). I’ve left a few minor suggestions and a few items I consider important to address or discuss before merging.

@@ -46,7 +46,7 @@ viewer:
- feature_siem.read
- feature_siem.read_alerts
- feature_siem.endpoint_list_read
- feature_securitySolutionCases.read
- feature_securitySolutionCasesV2.read
Copy link
Member

Choose a reason for hiding this comment

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

If not, I'd suggest you create a PR on the other repo first, to update the privileges before merging this PR.

As I explained in https://github.com/elastic/kibana/pull/194898/files#r1801715345, it might be easier to do it the other way around: first merge the PR, wait until the changes reach production, and then drop legacy privileges from the predefined roles. Alternatively, you could update the predefined roles first, but you’d need to keep both old and new privileges since we’ll have Kibana pods running on different versions simultaneously, and later update predefined roles once again to remove legacy privileges.

replacedBy: [
{
feature: CASES_FEATURE_ID_V2,
privileges: ['minimal_all', 'create_comment', 'case_reopen'],
Copy link
Member

Choose a reason for hiding this comment

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

Important

I believe we agreed in #194898 (comment) to use "extended" syntax to have separate definition for default and minimal privilege variants (for both all and read)?

catalogue: [APP_ID],
cases: [APP_ID],
privileges: {
all: {
api: apiTags.all,
app: [CASES_FEATURE_ID, 'kibana'],
app: [SECURITY_SOLUTION_CASES_APP_ID, 'kibana'],
Copy link
Member

Choose a reason for hiding this comment

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

Warning

Did you intentionally update app only in all and keep the old value for read?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

no, thanks for catching

api: apiTags.all,
id: 'case_reopen',
name: i18n.translate(
'securitySolutionPackages.features.featureRegistry.reopenCaseubFeatureDetails',
Copy link
Member

Choose a reason for hiding this comment

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

Tip

Typo

Suggested change
'securitySolutionPackages.features.featureRegistry.reopenCaseubFeatureDetails',
'securitySolutionPackages.features.featureRegistry.reopenCaseSubFeatureDetails',

Copy link
Contributor

Choose a reason for hiding this comment

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

@azasypkin I believe this has been addressed

};
const casesreopenCaseubFeature: SubFeatureConfig = {
name: i18n.translate(
'securitySolutionPackages.features.featureRegistry.reopenCaseubFeatureName',
Copy link
Member

Choose a reason for hiding this comment

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

Tip

Typo (here and in other plugins)

Suggested change
'securitySolutionPackages.features.featureRegistry.reopenCaseubFeatureName',
'securitySolutionPackages.features.featureRegistry.reopenCaseSubFeatureName',

Copy link
Contributor

Choose a reason for hiding this comment

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

I believe this has been addressed

@@ -0,0 +1,178 @@
/*
Copy link
Member

Choose a reason for hiding this comment

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

Note

/rant mode on
Having the sub-feature definitions separated from the feature definitions makes auditing ("what is granting what") so much harder!
/rant mode off

Copy link
Contributor

Choose a reason for hiding this comment

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

Yea, this decision predates this work, but completely understand 😅

read: [...filesSavedObjectTypes],
},
ui: casesCapabilities.all,
replacedBy: [
Copy link
Member

Choose a reason for hiding this comment

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

Important

Same comment here: I believe we agreed in #194898 (comment) to use "extended" syntax to have separate definition for default and minimal privilege variants (for both all and read)?

@@ -72,6 +72,8 @@ export default function catalogueTests({ getService }: FtrProviderContext) {
uiCapabilities.value!.catalogue,
Copy link
Member

Choose a reason for hiding this comment

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

Note

I believe you’ll be able to revert all changes in x-pack/test/ui_capabilities once I merge #198656. If I merge it before your PR, you’ll need to revert these changes; otherwise, I’ll revert them in my PR before merging.

Copy link
Member

Choose a reason for hiding this comment

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

#198656 is merged, can you please try to revert changes in x-pack/test/ui_capabilities and see if the tests pass?

uiCapabilities.value!.navLinks;
const {
kibana: _,
securitySolutionCases: __,
Copy link
Member

Choose a reason for hiding this comment

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

Should be addressed in #198656 and all changes in x-pack/test/ui_capabilities made in this PR can be reverted.

});
});

it('cases permissions are properly handled for deprecated privileges', async () => {
Copy link
Member

Choose a reason for hiding this comment

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

question: would it be possible to add another test that validates that a user with the deprecated all privilege (v1) can still reopen the case and comment on it, as well as a user with the non-deprecated all privilege? /cc @cnasikas

Copy link
Member

Choose a reason for hiding this comment

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

+1 to that. This is the most important scenario we would like to test.

Copy link
Member

@cnasikas cnasikas left a comment

Choose a reason for hiding this comment

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

I did another pass on the PR. I hope this is the final one 🙂. I noticed that there are unaddressed comments from the previous reviews. Also, the following files are missing unit tests:

  • x-pack/plugins/cases/server/authorization/audit_logger.ts
  • x-pack/plugins/cases/public/components/user_actions/index.tsx

We also missing integration tests for the reopen sub privilege like in x-pack/test/cases_api_integration/security_and_spaces/tests/trial/create_comment_sub_privilege.ts.

Finally, it would be nice if we could add some tests for the files (you cannot create files if you do not have the create comment subfeature privilege) in x-pack/test/api_integration/apis/cases/files.ts

@@ -40,5 +41,6 @@ export const getApiTags = (owner: Owner): CasesApiTags => {
read,
] as const,
delete: [deleteTag] as const,
createComment: [create, CREATE_COMMENT_API_TAG] as const,
Copy link
Member

Choose a reason for hiding this comment

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

We do not need the CREATE_COMMENT_API_TAG, only the create. The Cases API will perform its own authorization inside, so it is fine not to define any tags for the Cases APIs. For the File Cases API, though, which is not controlled by the cases code, we should have the create (tag only for files) as you have.

Suggested change
createComment: [create, CREATE_COMMENT_API_TAG] as const,
createComment: [create] as const,

})
).resolves.not.toThrow();

expect(mockLogger.log.mock.calls).toMatchSnapshot();
Copy link
Member

Choose a reason for hiding this comment

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

Could you please add a check that we call the checkPrivileges with the correct requiredPrivileges? You can do it expect(checkRequestReturningHasAllAsTrue.mock.calls[0]).toMatchInlineSnapshot(...). Also, I think is better to have two tests one for checking the operations and one for checking the logging.

}) {
const ownerMsg = owners.length <= 0 ? 'of any owner' : `with owners: "${owners.join(', ')}"`;
const operations = Array.isArray(operation) ? operation : [operation];
const operationVerbs = [...new Set(operations.map((op) => op.verbs.present))].join(', ');
const operationDocTypes = [...new Set(operations.map((op) => op.docType))].join(', ');
/**
* This will take the form:
* `Unauthorized to create case with owners: "securitySolution, observability"`
* `Unauthorized to access cases of any owner`
Copy link
Member

Choose a reason for hiding this comment

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

nit: Could we add an example for multiple operations?

@@ -96,6 +113,7 @@ export class ProductFeaturesService {

const casesProductFeaturesConfig = configurator.cases();
this.casesProductFeatures.setConfig(casesProductFeaturesConfig);
this.casesProductV2Features.setConfig(casesProductFeaturesConfig);
Copy link
Member

Choose a reason for hiding this comment

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

Is it ok if the two features have the same configuration (casesProductFeaturesConfig)? Not sure how the ProductFeaturesService works tbh 🙂.

@@ -488,6 +488,8 @@ soc_manager:
- application: "kibana-.kibana"
privileges:
- feature_ml.read
- feature_generalCases.all
- feature_generalCasesV2.all
Copy link
Member

Choose a reason for hiding this comment

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

generalCases and generalCasesV2 features are used for Cases in the stack management page. Stack cases are not available in the Security project. Do we need it here?

});
});

it('cases permissions are properly handled for deprecated privileges', async () => {
Copy link
Member

Choose a reason for hiding this comment

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

+1 to that. This is the most important scenario we would like to test.

Copy link
Member

@cnasikas cnasikas Nov 6, 2024

Choose a reason for hiding this comment

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

I think that the tests should be owned by ResponseOps and not the Kibana security team as solutions use the framework to deprecate their features. You could add tests in x-pack/test/api_integration/apis/cases/privileges.ts, create dedicated users (x-pack/test/api_integration/apis/cases/common/users.ts) and roles (x-pack/test/api_integration/apis/cases/common/roles.ts) for the new features, and use the old users for the old features. This way you could avoid creating roles on the fly and also test all solutions at the same time. Finally, you can use a lot of our utility functions exported from cases_api_integration/common/lib/api.

});
});

// Delete
Copy link
Member

Choose a reason for hiding this comment

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

nit: The test is for updating.

groupType: 'independent',
privileges: [
{
api: apiTags.all,
Copy link
Member

Choose a reason for hiding this comment

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

I think we should remove the tags here.

kibana: [
{
feature: {
securitySolutionFixture: ['minimal_all'],
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't this be cases_delete?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

no_delete right?

Copy link
Member

Choose a reason for hiding this comment

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

I think is better to keep the old privileges. This way we can be sure that the tests in x-pack/test/api_integration/apis/cases/privileges.ts still working as expected with the old deprecated features.

@cnasikas
Copy link
Member

cnasikas commented Nov 6, 2024

I also found a small bug. If I do not have access to reopen a case I can select the status dropdown but it is empty. I think we should not allow users to click and hide the caret (down arrow) when they do not have access to reopen.

Screenshot 2024-11-06 at 12 33 55 PM

@kqualters-elastic
Copy link
Contributor Author

kqualters-elastic commented Nov 7, 2024

@cnasikas for x-pack/plugins/cases/public/components/user_actions/index.tsx , there were no logic changes at all, merely moving some logic to a shared hook that has tests, and renamed a variable. Any new tests would be testing cases that did not exist before, and the existing tests passing without needing any changes should be proof enough of this.

@elasticmachine
Copy link
Contributor

elasticmachine commented Nov 14, 2024

⏳ Build in-progress, with failures

Failed CI Steps

Test Failures

  • [job] [logs] FTR Configs #9 / cases files successful requests users with all privileges scenario user: sec_all_user_api_int fileKind: securitySolutionFilesCases delete created file after test should list files
  • [job] [logs] FTR Configs #9 / cases files successful requests users with all privileges scenario user: sec_all_user_api_int fileKind: securitySolutionFilesCases delete created file after test should list files
  • [job] [logs] FTR Configs #80 / cases security and spaces enabled: basic Common delete_cases rbac files should delete a case when the user has access to delete the case and files
  • [job] [logs] FTR Configs #80 / cases security and spaces enabled: basic Common delete_cases rbac files should delete a case when the user has access to delete the case and files
  • [job] [logs] x-pack/test/cases_api_integration/security_and_spaces/config_trial.ts / cases security and spaces enabled: trial Common delete_cases rbac files should delete a case when the user has access to delete the case and files
  • [job] [logs] x-pack/test/cases_api_integration/security_and_spaces/config_trial.ts / cases security and spaces enabled: trial Common delete_cases rbac files should delete a case when the user has access to delete the case and files
  • [job] [logs] FTR Configs #63 / Screenshots - serverless observability UI response ops docs observability cases list view case detail screenshot
  • [job] [logs] FTR Configs #63 / Screenshots - serverless observability UI response ops docs observability cases list view case detail screenshot
  • [job] [logs] FTR Configs #62 / security APIs - Features deprecated features all deprecated features are known
  • [job] [logs] FTR Configs #62 / security APIs - Features deprecated features all deprecated features are known
  • [job] [logs] Jest Tests #1 / UserActionsList renders list correctly with isExpandable option

History

cc @adcoelho @cnasikas

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
apm:review backport:version Backport to applied version labels ci:cloud-deploy Create or update a Cloud deployment ci:cloud-persist-deployment Persist cloud deployment indefinitely ci:project-deploy-observability Create an Observability project release_note:feature Makes this part of the condensed release notes Team:obs-ux-management Observability Management User Experience Team v8.17.0 v9.0.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.