Skip to content

Commit a4ef281

Browse files
[APM][9.0] Adding kibana upgrade deprecation warning apm_user removed (#200163)
Related to: elastic/elasticsearch#116712 Meta issue: #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 #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">
1 parent f6ac2cf commit a4ef281

File tree

11 files changed

+463
-218
lines changed

11 files changed

+463
-218
lines changed

x-pack/plugins/observability_solution/apm/server/deprecations/__snapshots__/apm_user_role.test.ts.snap

Lines changed: 37 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import type { GetDeprecationsContext, IScopedClusterClient, CoreSetup } from '@kbn/core/server';
9+
import { elasticsearchServiceMock, loggingSystemMock } from '@kbn/core/server/mocks';
10+
import { getDeprecationsInfo } from './apm_user_role';
11+
import { SecurityPluginSetup } from '@kbn/security-plugin/server';
12+
13+
let context: GetDeprecationsContext;
14+
let esClient: jest.Mocked<IScopedClusterClient>;
15+
const core = { docLinks: { version: 'main' } } as unknown as CoreSetup;
16+
const logger = loggingSystemMock.createLogger();
17+
const security = { license: { isEnabled: () => true } } as unknown as SecurityPluginSetup;
18+
19+
describe('apm_user deprecation', () => {
20+
beforeEach(async () => {
21+
esClient = elasticsearchServiceMock.createScopedClusterClient();
22+
esClient.asCurrentUser.security.getUser = jest.fn().mockResolvedValue({
23+
xyz: { username: 'normal_user', roles: ['data_analyst'] },
24+
});
25+
esClient.asCurrentUser.security.getRoleMapping = jest.fn().mockResolvedValue({});
26+
27+
context = { esClient } as unknown as GetDeprecationsContext;
28+
});
29+
30+
test('logs no deprecations when setup has no issues', async () => {
31+
expect(await getDeprecationsInfo(context, core, { logger, security })).toMatchInlineSnapshot(
32+
`Array []`
33+
);
34+
});
35+
36+
describe('users assigned to a removed role', () => {
37+
test('logs a deprecation when a user was found with a removed apm_user role', async () => {
38+
esClient.asCurrentUser.security.getUser = jest.fn().mockResolvedValue({
39+
foo: {
40+
username: 'foo',
41+
roles: ['kibana_admin', 'apm_user'],
42+
},
43+
});
44+
45+
expect(await getDeprecationsInfo(context, core, { logger, security })).toMatchSnapshot();
46+
});
47+
});
48+
49+
describe('roles mapped to a removed role', () => {
50+
test('logs a deprecation when a role was found that maps to the removed apm_user role', async () => {
51+
esClient.asCurrentUser.security.getRoleMapping = jest
52+
.fn()
53+
.mockResolvedValue({ dungeon_master: { roles: ['apm_user'] } });
54+
55+
expect(await getDeprecationsInfo(context, core, { logger, security })).toMatchSnapshot();
56+
});
57+
});
58+
59+
describe('check deprecations when security is disabled', () => {
60+
test('logs no deprecations', async () => {
61+
expect(
62+
await getDeprecationsInfo(context, core, { logger, security: undefined })
63+
).toMatchInlineSnapshot(`Array []`);
64+
});
65+
});
66+
67+
it('insufficient permissions', async () => {
68+
const permissionsError = new Error('you shall not pass');
69+
(permissionsError as unknown as { statusCode: number }).statusCode = 403;
70+
esClient.asCurrentUser.security.getUser = jest.fn().mockRejectedValue(permissionsError);
71+
esClient.asCurrentUser.security.getRoleMapping = jest.fn().mockRejectedValue(permissionsError);
72+
73+
expect(await getDeprecationsInfo(context, core, { logger, security })).toMatchInlineSnapshot(`
74+
Array [
75+
Object {
76+
"correctiveActions": Object {
77+
"manualSteps": Array [
78+
"Make sure you have a \\"manage_security\\" cluster privilege assigned.",
79+
],
80+
},
81+
"deprecationType": "feature",
82+
"documentationUrl": "https://www.elastic.co/guide/en/kibana/main/xpack-security.html#_required_permissions_7",
83+
"level": "fetch_error",
84+
"message": "You do not have enough permissions to fix this deprecation.",
85+
"title": "Check for users assigned the deprecated \\"apm_user\\" role",
86+
},
87+
Object {
88+
"correctiveActions": Object {
89+
"manualSteps": Array [
90+
"Make sure you have a \\"manage_security\\" cluster privilege assigned.",
91+
],
92+
},
93+
"deprecationType": "feature",
94+
"documentationUrl": "https://www.elastic.co/guide/en/kibana/main/xpack-security.html#_required_permissions_7",
95+
"level": "fetch_error",
96+
"message": "You do not have enough permissions to fix this deprecation.",
97+
"title": "Check for role mappings using the deprecated \\"apm_user\\" role",
98+
},
99+
]
100+
`);
101+
});
102+
});
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import {
9+
SecurityGetRoleMappingResponse,
10+
SecurityGetUserResponse,
11+
} from '@elastic/elasticsearch/lib/api/types';
12+
import type {
13+
CoreSetup,
14+
DeprecationsDetails,
15+
DocLinksServiceSetup,
16+
ElasticsearchClient,
17+
GetDeprecationsContext,
18+
} from '@kbn/core/server';
19+
import { i18n } from '@kbn/i18n';
20+
import type { DeprecationApmDeps } from '.';
21+
import { deprecations } from '../lib/deprecations';
22+
23+
const APM_USER_ROLE_NAME = 'apm_user';
24+
const getKibanaPrivilegesDocumentationUrl = (branch: string) => {
25+
return `https://www.elastic.co/guide/en/kibana/${branch}/kibana-privileges.html`;
26+
};
27+
28+
export async function getDeprecationsInfo(
29+
{ esClient }: GetDeprecationsContext,
30+
core: CoreSetup,
31+
apmDeps: DeprecationApmDeps
32+
) {
33+
const client = esClient.asCurrentUser;
34+
const { docLinks } = core;
35+
const { security } = apmDeps;
36+
37+
// Nothing to do if security is disabled
38+
if (!security?.license.isEnabled()) {
39+
return [];
40+
}
41+
42+
const [userDeprecations, roleMappingDeprecations] = await Promise.all([
43+
getUsersDeprecations(client, apmDeps, docLinks),
44+
getRoleMappingsDeprecations(client, apmDeps, docLinks),
45+
]);
46+
47+
return [...userDeprecations, ...roleMappingDeprecations];
48+
}
49+
50+
async function getUsersDeprecations(
51+
client: ElasticsearchClient,
52+
apmDeps: DeprecationApmDeps,
53+
docLinks: DocLinksServiceSetup
54+
): Promise<DeprecationsDetails[]> {
55+
const title = i18n.translate('xpack.apm.deprecations.apmUser.title', {
56+
defaultMessage: `Check for users assigned the deprecated "{apmUserRoleName}" role`,
57+
values: { apmUserRoleName: APM_USER_ROLE_NAME },
58+
});
59+
60+
let users: SecurityGetUserResponse;
61+
try {
62+
users = await client.security.getUser();
63+
} catch (err) {
64+
const { logger } = apmDeps;
65+
if (deprecations.getErrorStatusCode(err) === 403) {
66+
logger.warn(
67+
'Failed to retrieve users when checking for deprecations: the "read_security" or "manage_security" cluster privilege is required.'
68+
);
69+
} else {
70+
logger.error(
71+
`Failed to retrieve users when checking for deprecations, unexpected error: ${deprecations.getDetailedErrorMessage(
72+
err
73+
)}.`
74+
);
75+
}
76+
return deprecations.deprecationError(title, err, docLinks);
77+
}
78+
79+
const apmUsers = Object.values(users).flatMap((user) =>
80+
user.roles.find(hasApmUserRole) ? user.username : []
81+
);
82+
83+
if (apmUsers.length === 0) {
84+
return [];
85+
}
86+
87+
return [
88+
{
89+
title,
90+
message: i18n.translate('xpack.apm.deprecations.apmUser.description', {
91+
defaultMessage: `The "{apmUserRoleName}" role has been deprecated. Remove the "{apmUserRoleName}" role from affected users in this cluster including: {users}`,
92+
values: { apmUserRoleName: APM_USER_ROLE_NAME, users: apmUsers.join() },
93+
}),
94+
correctiveActions: {
95+
manualSteps: [
96+
i18n.translate('xpack.apm.deprecations.apmUser.manualStepOne', {
97+
defaultMessage: `Go to Management > Security > Users to find users with the "{apmUserRoleName}" role.`,
98+
values: { apmUserRoleName: APM_USER_ROLE_NAME },
99+
}),
100+
i18n.translate('xpack.apm.deprecations.apmUser.manualStepTwo', {
101+
defaultMessage:
102+
'Remove the "{apmUserRoleName}" role from all users and add the built-in "viewer" role.',
103+
values: { apmUserRoleName: APM_USER_ROLE_NAME },
104+
}),
105+
],
106+
},
107+
level: 'critical',
108+
deprecationType: 'feature',
109+
documentationUrl: getKibanaPrivilegesDocumentationUrl(docLinks.version),
110+
},
111+
];
112+
}
113+
114+
async function getRoleMappingsDeprecations(
115+
client: ElasticsearchClient,
116+
apmDeps: DeprecationApmDeps,
117+
docLinks: DocLinksServiceSetup
118+
): Promise<DeprecationsDetails[]> {
119+
const title = i18n.translate('xpack.apm.deprecations.apmUserRoleMappings.title', {
120+
defaultMessage: `Check for role mappings using the deprecated "{apmUserRoleName}" role`,
121+
values: { apmUserRoleName: APM_USER_ROLE_NAME },
122+
});
123+
124+
let roleMappings: SecurityGetRoleMappingResponse;
125+
try {
126+
roleMappings = await client.security.getRoleMapping();
127+
} catch (err) {
128+
const { logger } = apmDeps;
129+
if (deprecations.getErrorStatusCode(err) === 403) {
130+
logger.warn(
131+
'Failed to retrieve role mappings when checking for deprecations: the "manage_security" cluster privilege is required.'
132+
);
133+
} else {
134+
logger.error(
135+
`Failed to retrieve role mappings when checking for deprecations, unexpected error: ${deprecations.getDetailedErrorMessage(
136+
err
137+
)}.`
138+
);
139+
}
140+
return deprecations.deprecationError(title, err, docLinks);
141+
}
142+
143+
const roleMappingsWithApmUserRole = Object.entries(roleMappings).flatMap(([roleName, role]) =>
144+
role.roles?.find(hasApmUserRole) ? roleName : []
145+
);
146+
147+
if (roleMappingsWithApmUserRole.length === 0) {
148+
return [];
149+
}
150+
151+
return [
152+
{
153+
title,
154+
message: i18n.translate('xpack.apm.deprecations.apmUserRoleMappings.description', {
155+
defaultMessage: `The "{apmUserRoleName}" role has been deprecated. Remove the "{apmUserRoleName}" role from affected role mappings in this cluster including: {roles}`,
156+
values: {
157+
apmUserRoleName: APM_USER_ROLE_NAME,
158+
roles: roleMappingsWithApmUserRole.join(),
159+
},
160+
}),
161+
correctiveActions: {
162+
manualSteps: [
163+
i18n.translate('xpack.apm.deprecations.apmUserRoleMappings.manualStepOne', {
164+
defaultMessage: `Go to Management > Security > Role Mappings to find roles mappings with the "{apmUserRoleName}" role.`,
165+
values: { apmUserRoleName: APM_USER_ROLE_NAME },
166+
}),
167+
i18n.translate('xpack.apm.deprecations.apmUserRoleMappings.manualStepTwo', {
168+
defaultMessage:
169+
'Remove the "{apmUserRoleName}" role from all role mappings and add the built-in "viewer" role',
170+
values: { apmUserRoleName: APM_USER_ROLE_NAME },
171+
}),
172+
],
173+
},
174+
level: 'critical',
175+
deprecationType: 'feature',
176+
documentationUrl: getKibanaPrivilegesDocumentationUrl(docLinks.version),
177+
},
178+
];
179+
}
180+
181+
const hasApmUserRole = (role: string) => role === APM_USER_ROLE_NAME;

0 commit comments

Comments
 (0)