forked from elastic/kibana
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[APM][9.0] Adding kibana upgrade deprecation warning apm_user removed (…
…elastic#200163) Related to: elastic/elasticsearch#116712 Meta issue: elastic#116760 The apm_user role was elastic/elasticsearch#68749 in 7.13 and was supposed to be removed in 8.0. All mentions of apm_user role were finally removed in elastic#132790. This PR adds some deprecation steps for users are using the `apm_user`. <img width="494" alt="Screenshot 2024-11-18 at 14 10 08" src="https://github.com/user-attachments/assets/e04fff63-b56e-4a74-93ad-a87884f9a8a6"> <img width="1128" alt="Screenshot 2024-11-18 at 14 10 18" src="https://github.com/user-attachments/assets/de67afcf-8bd5-4896-815a-4e1adc730681"> <img width="1241" alt="Screenshot 2024-11-14 at 13 12 01" src="https://github.com/user-attachments/assets/e94b0367-b459-45b4-923e-1de7a095b6b8">
- Loading branch information
1 parent
862f5f3
commit 8c1485b
Showing
11 changed files
with
463 additions
and
218 deletions.
There are no files selected for viewing
37 changes: 37 additions & 0 deletions
37
...s/observability_solution/apm/server/deprecations/__snapshots__/apm_user_role.test.ts.snap
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
102 changes: 102 additions & 0 deletions
102
x-pack/plugins/observability_solution/apm/server/deprecations/apm_user_role.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
import type { GetDeprecationsContext, IScopedClusterClient, CoreSetup } from '@kbn/core/server'; | ||
import { elasticsearchServiceMock, loggingSystemMock } from '@kbn/core/server/mocks'; | ||
import { getDeprecationsInfo } from './apm_user_role'; | ||
import { SecurityPluginSetup } from '@kbn/security-plugin/server'; | ||
|
||
let context: GetDeprecationsContext; | ||
let esClient: jest.Mocked<IScopedClusterClient>; | ||
const core = { docLinks: { version: 'main' } } as unknown as CoreSetup; | ||
const logger = loggingSystemMock.createLogger(); | ||
const security = { license: { isEnabled: () => true } } as unknown as SecurityPluginSetup; | ||
|
||
describe('apm_user deprecation', () => { | ||
beforeEach(async () => { | ||
esClient = elasticsearchServiceMock.createScopedClusterClient(); | ||
esClient.asCurrentUser.security.getUser = jest.fn().mockResolvedValue({ | ||
xyz: { username: 'normal_user', roles: ['data_analyst'] }, | ||
}); | ||
esClient.asCurrentUser.security.getRoleMapping = jest.fn().mockResolvedValue({}); | ||
|
||
context = { esClient } as unknown as GetDeprecationsContext; | ||
}); | ||
|
||
test('logs no deprecations when setup has no issues', async () => { | ||
expect(await getDeprecationsInfo(context, core, { logger, security })).toMatchInlineSnapshot( | ||
`Array []` | ||
); | ||
}); | ||
|
||
describe('users assigned to a removed role', () => { | ||
test('logs a deprecation when a user was found with a removed apm_user role', async () => { | ||
esClient.asCurrentUser.security.getUser = jest.fn().mockResolvedValue({ | ||
foo: { | ||
username: 'foo', | ||
roles: ['kibana_admin', 'apm_user'], | ||
}, | ||
}); | ||
|
||
expect(await getDeprecationsInfo(context, core, { logger, security })).toMatchSnapshot(); | ||
}); | ||
}); | ||
|
||
describe('roles mapped to a removed role', () => { | ||
test('logs a deprecation when a role was found that maps to the removed apm_user role', async () => { | ||
esClient.asCurrentUser.security.getRoleMapping = jest | ||
.fn() | ||
.mockResolvedValue({ dungeon_master: { roles: ['apm_user'] } }); | ||
|
||
expect(await getDeprecationsInfo(context, core, { logger, security })).toMatchSnapshot(); | ||
}); | ||
}); | ||
|
||
describe('check deprecations when security is disabled', () => { | ||
test('logs no deprecations', async () => { | ||
expect( | ||
await getDeprecationsInfo(context, core, { logger, security: undefined }) | ||
).toMatchInlineSnapshot(`Array []`); | ||
}); | ||
}); | ||
|
||
it('insufficient permissions', async () => { | ||
const permissionsError = new Error('you shall not pass'); | ||
(permissionsError as unknown as { statusCode: number }).statusCode = 403; | ||
esClient.asCurrentUser.security.getUser = jest.fn().mockRejectedValue(permissionsError); | ||
esClient.asCurrentUser.security.getRoleMapping = jest.fn().mockRejectedValue(permissionsError); | ||
|
||
expect(await getDeprecationsInfo(context, core, { logger, security })).toMatchInlineSnapshot(` | ||
Array [ | ||
Object { | ||
"correctiveActions": Object { | ||
"manualSteps": Array [ | ||
"Make sure you have a \\"manage_security\\" cluster privilege assigned.", | ||
], | ||
}, | ||
"deprecationType": "feature", | ||
"documentationUrl": "https://www.elastic.co/guide/en/kibana/main/xpack-security.html#_required_permissions_7", | ||
"level": "fetch_error", | ||
"message": "You do not have enough permissions to fix this deprecation.", | ||
"title": "Check for users assigned the deprecated \\"apm_user\\" role", | ||
}, | ||
Object { | ||
"correctiveActions": Object { | ||
"manualSteps": Array [ | ||
"Make sure you have a \\"manage_security\\" cluster privilege assigned.", | ||
], | ||
}, | ||
"deprecationType": "feature", | ||
"documentationUrl": "https://www.elastic.co/guide/en/kibana/main/xpack-security.html#_required_permissions_7", | ||
"level": "fetch_error", | ||
"message": "You do not have enough permissions to fix this deprecation.", | ||
"title": "Check for role mappings using the deprecated \\"apm_user\\" role", | ||
}, | ||
] | ||
`); | ||
}); | ||
}); |
181 changes: 181 additions & 0 deletions
181
x-pack/plugins/observability_solution/apm/server/deprecations/apm_user_role.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
import { | ||
SecurityGetRoleMappingResponse, | ||
SecurityGetUserResponse, | ||
} from '@elastic/elasticsearch/lib/api/types'; | ||
import type { | ||
CoreSetup, | ||
DeprecationsDetails, | ||
DocLinksServiceSetup, | ||
ElasticsearchClient, | ||
GetDeprecationsContext, | ||
} from '@kbn/core/server'; | ||
import { i18n } from '@kbn/i18n'; | ||
import type { DeprecationApmDeps } from '.'; | ||
import { deprecations } from '../lib/deprecations'; | ||
|
||
const APM_USER_ROLE_NAME = 'apm_user'; | ||
const getKibanaPrivilegesDocumentationUrl = (branch: string) => { | ||
return `https://www.elastic.co/guide/en/kibana/${branch}/kibana-privileges.html`; | ||
}; | ||
|
||
export async function getDeprecationsInfo( | ||
{ esClient }: GetDeprecationsContext, | ||
core: CoreSetup, | ||
apmDeps: DeprecationApmDeps | ||
) { | ||
const client = esClient.asCurrentUser; | ||
const { docLinks } = core; | ||
const { security } = apmDeps; | ||
|
||
// Nothing to do if security is disabled | ||
if (!security?.license.isEnabled()) { | ||
return []; | ||
} | ||
|
||
const [userDeprecations, roleMappingDeprecations] = await Promise.all([ | ||
getUsersDeprecations(client, apmDeps, docLinks), | ||
getRoleMappingsDeprecations(client, apmDeps, docLinks), | ||
]); | ||
|
||
return [...userDeprecations, ...roleMappingDeprecations]; | ||
} | ||
|
||
async function getUsersDeprecations( | ||
client: ElasticsearchClient, | ||
apmDeps: DeprecationApmDeps, | ||
docLinks: DocLinksServiceSetup | ||
): Promise<DeprecationsDetails[]> { | ||
const title = i18n.translate('xpack.apm.deprecations.apmUser.title', { | ||
defaultMessage: `Check for users assigned the deprecated "{apmUserRoleName}" role`, | ||
values: { apmUserRoleName: APM_USER_ROLE_NAME }, | ||
}); | ||
|
||
let users: SecurityGetUserResponse; | ||
try { | ||
users = await client.security.getUser(); | ||
} catch (err) { | ||
const { logger } = apmDeps; | ||
if (deprecations.getErrorStatusCode(err) === 403) { | ||
logger.warn( | ||
'Failed to retrieve users when checking for deprecations: the "read_security" or "manage_security" cluster privilege is required.' | ||
); | ||
} else { | ||
logger.error( | ||
`Failed to retrieve users when checking for deprecations, unexpected error: ${deprecations.getDetailedErrorMessage( | ||
err | ||
)}.` | ||
); | ||
} | ||
return deprecations.deprecationError(title, err, docLinks); | ||
} | ||
|
||
const apmUsers = Object.values(users).flatMap((user) => | ||
user.roles.find(hasApmUserRole) ? user.username : [] | ||
); | ||
|
||
if (apmUsers.length === 0) { | ||
return []; | ||
} | ||
|
||
return [ | ||
{ | ||
title, | ||
message: i18n.translate('xpack.apm.deprecations.apmUser.description', { | ||
defaultMessage: `The "{apmUserRoleName}" role has been deprecated. Remove the "{apmUserRoleName}" role from affected users in this cluster including: {users}`, | ||
values: { apmUserRoleName: APM_USER_ROLE_NAME, users: apmUsers.join() }, | ||
}), | ||
correctiveActions: { | ||
manualSteps: [ | ||
i18n.translate('xpack.apm.deprecations.apmUser.manualStepOne', { | ||
defaultMessage: `Go to Management > Security > Users to find users with the "{apmUserRoleName}" role.`, | ||
values: { apmUserRoleName: APM_USER_ROLE_NAME }, | ||
}), | ||
i18n.translate('xpack.apm.deprecations.apmUser.manualStepTwo', { | ||
defaultMessage: | ||
'Remove the "{apmUserRoleName}" role from all users and add the built-in "viewer" role.', | ||
values: { apmUserRoleName: APM_USER_ROLE_NAME }, | ||
}), | ||
], | ||
}, | ||
level: 'critical', | ||
deprecationType: 'feature', | ||
documentationUrl: getKibanaPrivilegesDocumentationUrl(docLinks.version), | ||
}, | ||
]; | ||
} | ||
|
||
async function getRoleMappingsDeprecations( | ||
client: ElasticsearchClient, | ||
apmDeps: DeprecationApmDeps, | ||
docLinks: DocLinksServiceSetup | ||
): Promise<DeprecationsDetails[]> { | ||
const title = i18n.translate('xpack.apm.deprecations.apmUserRoleMappings.title', { | ||
defaultMessage: `Check for role mappings using the deprecated "{apmUserRoleName}" role`, | ||
values: { apmUserRoleName: APM_USER_ROLE_NAME }, | ||
}); | ||
|
||
let roleMappings: SecurityGetRoleMappingResponse; | ||
try { | ||
roleMappings = await client.security.getRoleMapping(); | ||
} catch (err) { | ||
const { logger } = apmDeps; | ||
if (deprecations.getErrorStatusCode(err) === 403) { | ||
logger.warn( | ||
'Failed to retrieve role mappings when checking for deprecations: the "manage_security" cluster privilege is required.' | ||
); | ||
} else { | ||
logger.error( | ||
`Failed to retrieve role mappings when checking for deprecations, unexpected error: ${deprecations.getDetailedErrorMessage( | ||
err | ||
)}.` | ||
); | ||
} | ||
return deprecations.deprecationError(title, err, docLinks); | ||
} | ||
|
||
const roleMappingsWithApmUserRole = Object.entries(roleMappings).flatMap(([roleName, role]) => | ||
role.roles?.find(hasApmUserRole) ? roleName : [] | ||
); | ||
|
||
if (roleMappingsWithApmUserRole.length === 0) { | ||
return []; | ||
} | ||
|
||
return [ | ||
{ | ||
title, | ||
message: i18n.translate('xpack.apm.deprecations.apmUserRoleMappings.description', { | ||
defaultMessage: `The "{apmUserRoleName}" role has been deprecated. Remove the "{apmUserRoleName}" role from affected role mappings in this cluster including: {roles}`, | ||
values: { | ||
apmUserRoleName: APM_USER_ROLE_NAME, | ||
roles: roleMappingsWithApmUserRole.join(), | ||
}, | ||
}), | ||
correctiveActions: { | ||
manualSteps: [ | ||
i18n.translate('xpack.apm.deprecations.apmUserRoleMappings.manualStepOne', { | ||
defaultMessage: `Go to Management > Security > Role Mappings to find roles mappings with the "{apmUserRoleName}" role.`, | ||
values: { apmUserRoleName: APM_USER_ROLE_NAME }, | ||
}), | ||
i18n.translate('xpack.apm.deprecations.apmUserRoleMappings.manualStepTwo', { | ||
defaultMessage: | ||
'Remove the "{apmUserRoleName}" role from all role mappings and add the built-in "viewer" role', | ||
values: { apmUserRoleName: APM_USER_ROLE_NAME }, | ||
}), | ||
], | ||
}, | ||
level: 'critical', | ||
deprecationType: 'feature', | ||
documentationUrl: getKibanaPrivilegesDocumentationUrl(docLinks.version), | ||
}, | ||
]; | ||
} | ||
|
||
const hasApmUserRole = (role: string) => role === APM_USER_ROLE_NAME; |
Oops, something went wrong.