[8.12] [Upgrade Assistant] Add missing cluster privilege check (#179033) (#179371)

# Backport

This will backport the following commits from `main` to `8.12`:
- [[Upgrade Assistant] Add missing cluster privilege check
(#179033)](https://github.com/elastic/kibana/pull/179033)

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

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

<!--BACKPORT [{"author":{"name":"Ignacio
Rivas","email":"rivasign@gmail.com"},"sourceCommit":{"committedDate":"2024-03-25T17:06:22Z","message":"[Upgrade
Assistant] Add missing cluster privilege check
(#179033)","sha":"4961e52bdfc39f59ac9ce3f767204c3be537ba45","branchLabelMapping":{"^v8.14.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["Team:Kibana
Management","release_note:skip","Feature:Upgrade
Assistant","v8.14.0","v8.12.3","v8.13.1"],"title":"[Upgrade Assistant]
Add missing cluster privilege
check","number":179033,"url":"https://github.com/elastic/kibana/pull/179033","mergeCommit":{"message":"[Upgrade
Assistant] Add missing cluster privilege check
(#179033)","sha":"4961e52bdfc39f59ac9ce3f767204c3be537ba45"}},"sourceBranch":"main","suggestedTargetBranches":["8.12","8.13"],"targetPullRequestStates":[{"branch":"main","label":"v8.14.0","branchLabelMappingKey":"^v8.14.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/179033","number":179033,"mergeCommit":{"message":"[Upgrade
Assistant] Add missing cluster privilege check
(#179033)","sha":"4961e52bdfc39f59ac9ce3f767204c3be537ba45"}},{"branch":"8.12","label":"v8.12.3","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"8.13","label":"v8.13.1","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Ignacio Rivas <rivasign@gmail.com>
This commit is contained in:
Kibana Machine 2024-03-25 14:34:41 -04:00 committed by GitHub
parent 8220de3a3e
commit 8716575154
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 191 additions and 42 deletions

View file

@ -41331,7 +41331,6 @@
"xpack.upgradeAssistant.overview.cloudBackup.hasSnapshotMessage": "Dernier snapshot créé le {lastBackupTime}.",
"xpack.upgradeAssistant.overview.deprecationLogs.deniedPrivilegeDescription": "Les logs de déclassement continueront à être indexés, mais vous ne pourrez pas les analyser tant que vous ne disposerez pas {privilegesCount, plural, one {du privilège de lecture d''index} many {des privilèges de lecture d''index} other {des privilèges de lecture d''index}} pour : {missingPrivileges}",
"xpack.upgradeAssistant.overview.logsStep.countDescription": "Vous avez {deprecationCount, plural, =0 {non} one {{deprecationCount}} many {{deprecationCount}} other {{deprecationCount}}} {deprecationCount, plural, one {problème} many {problèmes} other {problèmes}} de déclassement depuis {checkpoint}.",
"xpack.upgradeAssistant.overview.logsStep.missingPrivilegesDescription": "Les logs de déclassement continueront à être indexés, mais vous ne pourrez pas les analyser tant que vous ne disposerez pas {privilegesCount, plural, one {du privilège de lecture d''index} many {des privilèges de lecture d''index} other {des privilèges de lecture d''index}} pour : {missingPrivileges}",
"xpack.upgradeAssistant.overview.systemIndices.body": "Préparez les index système qui stockent des informations internes pour la mise à niveau. Cette préparation est requise uniquement lors des mises à jour des versions majeures. Tous les {hiddenIndicesLink} devant être réindexés seront affichés à l'étape suivante.",
"xpack.upgradeAssistant.overview.systemIndices.migrationFailedBody": "Une erreur s'est produite lors de la migration des index système pour {feature} : {failureCause}",
"xpack.upgradeAssistant.overview.verifyChanges.calloutTitle": "{warningsCount, plural, =0 {Non} one {{warningsCount}} many {{warningsCount}} other {{warningsCount}}} {warningsCount, plural, one {problème} many {problèmes} other {problèmes}} de déclassement depuis {previousCheck}",

View file

@ -41321,7 +41321,6 @@
"xpack.upgradeAssistant.overview.cloudBackup.hasSnapshotMessage": "前回のスナップショットは{lastBackupTime}に作成されました。",
"xpack.upgradeAssistant.overview.deprecationLogs.deniedPrivilegeDescription": "廃止予定ログのインデックスは続行されますが、次の読み取りインデックス{privilegesCount, plural, other {権限}}が付与されるまでは分析できません:{missingPrivileges}",
"xpack.upgradeAssistant.overview.logsStep.countDescription": "{checkpoint}以降に{deprecationCount, plural, =0 {いいえ} other {{deprecationCount}}}個の廃止予定の{deprecationCount, plural, other {問題}}があります。",
"xpack.upgradeAssistant.overview.logsStep.missingPrivilegesDescription": "廃止予定ログのインデックスは続行されますが、次の読み取りインデックス{privilegesCount, plural, other {権限}}が付与されるまでは分析できません:{missingPrivileges}",
"xpack.upgradeAssistant.overview.systemIndices.body": "アップグレードの内部情報を格納するシステムインデックスを準備します。これはメジャーバージョンアップグレード中にのみ必要です。インデックスを再作成する必要があるすべての{hiddenIndicesLink}は次のステップで表示されます。",
"xpack.upgradeAssistant.overview.systemIndices.migrationFailedBody": "{feature}のシステムインデックスの移行中にエラーが発生しました:{failureCause}",
"xpack.upgradeAssistant.overview.verifyChanges.calloutTitle": "{previousCheck}以降の{warningsCount, plural, =0 {いいえ} other {{warningsCount}}}廃止予定の{warningsCount, plural, other {問題}}",

View file

@ -41315,7 +41315,6 @@
"xpack.upgradeAssistant.overview.cloudBackup.hasSnapshotMessage": "上次创建快照的时间为 {lastBackupTime}。",
"xpack.upgradeAssistant.overview.deprecationLogs.deniedPrivilegeDescription": "将继续索引弃用日志,但您无法分析这些日志,直到您具有索引读取{privilegesCount, plural, other {权限}}{missingPrivileges}",
"xpack.upgradeAssistant.overview.logsStep.countDescription": "自 {checkpoint} 以来出现 {deprecationCount, plural, =0 {否} other {{deprecationCount}}} 个弃用{deprecationCount, plural, other {问题}}。",
"xpack.upgradeAssistant.overview.logsStep.missingPrivilegesDescription": "将继续索引弃用日志,但您无法分析这些日志,直到您具有索引读取{privilegesCount, plural, other {权限}}{missingPrivileges}",
"xpack.upgradeAssistant.overview.systemIndices.body": "为升级准备存储内部信息的系统索引。仅在主要版本升级期间需要此项。将在下一步中显示任何需要重新索引的{hiddenIndicesLink}。",
"xpack.upgradeAssistant.overview.systemIndices.migrationFailedBody": "迁移 {feature} 的系统索引时出错:{failureCause}",
"xpack.upgradeAssistant.overview.verifyChanges.calloutTitle": "自 {previousCheck} 以来出现 {warningsCount, plural, =0 {否} other {{warningsCount}}} 个弃用{warningsCount, plural, other {问题}}",

View file

@ -8,6 +8,7 @@
import { act } from 'react-dom/test-utils';
import { deprecationsServiceMock } from '@kbn/core/public/mocks';
import { APP_LOGS_COUNT_CLUSTER_PRIVILEGES } from '../../../../common/constants';
import { setupEnvironment } from '../../helpers';
import { kibanaDeprecationsServiceHelpers } from '../service.mock';
import { KibanaTestBed, setupKibanaPage } from '../kibana_deprecations.helpers';
@ -64,6 +65,13 @@ describe('Kibana deprecations - Deprecations table - Error handling', () => {
deprecations: deprecationService,
},
},
privileges: {
hasAllPrivileges: true,
missingPrivileges: {
cluster: [...APP_LOGS_COUNT_CLUSTER_PRIVILEGES],
index: [],
},
},
});
});
@ -72,9 +80,14 @@ describe('Kibana deprecations - Deprecations table - Error handling', () => {
component.update();
expect(exists('kibanaDeprecationErrors')).toBe(true);
// Should contain error about failed deprecations
expect(find('kibanaDeprecationErrors').text()).toContain(
'Failed to get deprecation issues for these plugins: failed_plugin_id_1, failed_plugin_id_2.'
);
// Should contain error about missing privilege
expect(find('kibanaDeprecationErrors').text()).toContain(
'Certain issues might be missing due to missing cluster privilege for: manage_security'
);
});
test('handles request error', async () => {

View file

@ -6,7 +6,10 @@
*/
import { act } from 'react-dom/test-utils';
import { DEPRECATION_LOGS_INDEX } from '../../../../common/constants';
import {
DEPRECATION_LOGS_INDEX,
APP_LOGS_COUNT_CLUSTER_PRIVILEGES,
} from '../../../../common/constants';
import { setupEnvironment } from '../../helpers';
import { OverviewTestBed, setupOverviewPage } from '../overview.helpers';
@ -134,27 +137,44 @@ describe('Overview - Logs Step', () => {
isDeprecationLogIndexingEnabled: true,
isDeprecationLoggingEnabled: true,
});
});
test('warns the user of missing index privileges', async () => {
await act(async () => {
testBed = await setupOverviewPage(httpSetup, {
privileges: {
hasAllPrivileges: true,
missingPrivileges: {
cluster: [],
index: [DEPRECATION_LOGS_INDEX],
},
},
});
});
const { component } = testBed;
const { component, exists } = testBed;
component.update();
expect(exists('missingIndexPrivilegesCallout')).toBe(true);
});
test('warns the user of missing index privileges', () => {
const { exists } = testBed;
test('warns the user of missing cluster privileges', async () => {
await act(async () => {
testBed = await setupOverviewPage(httpSetup, {
privileges: {
hasAllPrivileges: true,
missingPrivileges: {
cluster: [...APP_LOGS_COUNT_CLUSTER_PRIVILEGES],
index: [],
},
},
});
});
expect(exists('missingPrivilegesCallout')).toBe(true);
const { component, exists } = testBed;
component.update();
expect(exists('missingClusterPrivilegesCallout')).toBe(true);
});
});
});

View file

@ -41,3 +41,6 @@ export const APPS_WITH_DEPRECATION_LOGS = [
// The field that will indicate which elastic product generated the deprecation log
export const DEPRECATION_LOGS_ORIGIN_FIELD = 'elasticsearch.elastic_product_origin';
export const APP_LOGS_COUNT_INDEX_PRIVILEGES = ['read', 'view_index_metadata'];
export const APP_LOGS_COUNT_CLUSTER_PRIVILEGES = ['manage_security'];

View file

@ -8,12 +8,19 @@
import React, { useEffect, useState, useCallback, useMemo } from 'react';
import { v4 as uuidv4 } from 'uuid';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import { EuiPageHeader, EuiSpacer, EuiCallOut } from '@elastic/eui';
import { EuiCode, EuiPageHeader, EuiSpacer, EuiCallOut } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { METRIC_TYPE } from '@kbn/analytics';
import { FormattedMessage } from '@kbn/i18n-react';
import type { DomainDeprecationDetails } from '@kbn/core/public';
import { SectionLoading, GlobalFlyout } from '../../../shared_imports';
import {
WithPrivileges,
MissingPrivileges,
SectionLoading,
GlobalFlyout,
} from '../../../shared_imports';
import { APP_LOGS_COUNT_CLUSTER_PRIVILEGES } from '../../../../common/constants';
import { useAppContext } from '../../app_context';
import { uiMetricService, UIM_KIBANA_DEPRECATIONS_PAGE_LOAD } from '../../lib/ui_metric';
import { DeprecationsPageLoadingError, NoDeprecationsPrompt, DeprecationCount } from '../shared';
@ -56,6 +63,18 @@ const i18nTexts = {
pluginIds: pluginIds.join(', '),
},
}),
missingPermissionDescription: (privilegesMissing: MissingPrivileges) => (
<FormattedMessage
id="xpack.upgradeAssistant.overview.logsStep.missingPermissionDescription"
defaultMessage="Certain issues might be missing due to missing cluster {privilegesCount, plural, one {privilege} other {privileges}} for: {missingPrivileges}."
values={{
missingPrivileges: (
<EuiCode transparentBackground={true}>{privilegesMissing?.cluster?.join(', ')}</EuiCode>
),
privilegesCount: privilegesMissing?.cluster?.length,
}}
/>
),
};
export interface DeprecationResolutionState {
@ -87,7 +106,17 @@ const getDeprecationCountByLevel = (deprecations: KibanaDeprecationDetails[]) =>
};
};
export const KibanaDeprecations = withRouter(({ history }: RouteComponentProps) => {
interface KibanaDeprecationsListProps {
history: RouteComponentProps['history'];
hasPrivileges: boolean;
privilegesMissing: MissingPrivileges;
}
export const KibanaDeprecationsList = ({
history,
hasPrivileges,
privilegesMissing,
}: KibanaDeprecationsListProps) => {
const [kibanaDeprecations, setKibanaDeprecations] = useState<
KibanaDeprecationDetails[] | undefined
>(undefined);
@ -256,7 +285,7 @@ export const KibanaDeprecations = withRouter(({ history }: RouteComponentProps)
<EuiSpacer size="l" />
{kibanaDeprecationErrors.length > 0 && (
{(!hasPrivileges || kibanaDeprecationErrors.length > 0) && (
<>
<EuiCallOut
title={i18nTexts.kibanaDeprecationErrorTitle}
@ -264,7 +293,13 @@ export const KibanaDeprecations = withRouter(({ history }: RouteComponentProps)
iconType="warning"
data-test-subj="kibanaDeprecationErrors"
>
<p>{i18nTexts.getKibanaDeprecationErrorDescription(kibanaDeprecationErrors)}</p>
<>
{!hasPrivileges && <p>{i18nTexts.missingPermissionDescription(privilegesMissing)}</p>}
{kibanaDeprecationErrors.length > 0 && (
<p>{i18nTexts.getKibanaDeprecationErrorDescription(kibanaDeprecationErrors)}</p>
)}
</>
</EuiCallOut>
<EuiSpacer />
@ -279,4 +314,22 @@ export const KibanaDeprecations = withRouter(({ history }: RouteComponentProps)
/>
</div>
);
};
export const KibanaDeprecations = withRouter(({ history }: RouteComponentProps) => {
const requiredPrivileges = APP_LOGS_COUNT_CLUSTER_PRIVILEGES.map(
(privilege) => `cluster.${privilege}`
);
return (
<WithPrivileges privileges={requiredPrivileges}>
{({ hasPrivileges, isLoading, privilegesMissing }) => (
<KibanaDeprecationsList
history={history}
hasPrivileges={!isLoading && hasPrivileges}
privilegesMissing={privilegesMissing}
/>
)}
</WithPrivileges>
);
});

View file

@ -12,7 +12,11 @@ import { i18n } from '@kbn/i18n';
import { FormattedDate, FormattedTime, FormattedMessage } from '@kbn/i18n-react';
import type { EuiStepProps } from '@elastic/eui/src/components/steps/step';
import { DEPRECATION_LOGS_INDEX } from '../../../../../common/constants';
import {
DEPRECATION_LOGS_INDEX,
APP_LOGS_COUNT_CLUSTER_PRIVILEGES,
APP_LOGS_COUNT_INDEX_PRIVILEGES,
} from '../../../../../common/constants';
import { WithPrivileges, MissingPrivileges } from '../../../../shared_imports';
import { useAppContext } from '../../../app_context';
import { loadLogsCheckpoint } from '../../../lib/logs_checkpoint';
@ -53,21 +57,39 @@ const i18nTexts = {
}}
/>
),
missingPrivilegesTitle: i18n.translate(
missingIndexPrivilegesTitle: i18n.translate(
'xpack.upgradeAssistant.overview.logsStep.missingPrivilegesTitle',
{
defaultMessage: 'You require index privileges to analyze the deprecation logs',
}
),
missingPrivilegesDescription: (privilegesMissing: MissingPrivileges) => (
missingIndexPrivilegesDescription: (privilegesMissing: MissingPrivileges) => (
<FormattedMessage
id="xpack.upgradeAssistant.overview.logsStep.missingPrivilegesDescription"
defaultMessage="The deprecation logs will continue to be indexed, but you won't be able to analyze them until you have the read index {privilegesCount, plural, one {privilege} other {privileges}} for: {missingPrivileges}"
defaultMessage="The deprecation logs will continue to be indexed, but you won't be able to analyze them until you have the {requiredPrivileges} index privileges for: {missingPrivileges}"
values={{
requiredPrivileges: <i>{APP_LOGS_COUNT_INDEX_PRIVILEGES.join(', ')}</i>,
missingPrivileges: (
<EuiCode transparentBackground={true}>{privilegesMissing?.index?.join(', ')}</EuiCode>
),
privilegesCount: privilegesMissing?.index?.length,
}}
/>
),
missingClusterPrivilegesTitle: i18n.translate(
'xpack.upgradeAssistant.overview.logsStep.missingClusterPrivilegesTitle',
{
defaultMessage: 'You require cluster privileges to analyze the deprecation logs',
}
),
missingClusterPrivilegesDescription: (privilegesMissing: MissingPrivileges) => (
<FormattedMessage
id="xpack.upgradeAssistant.overview.logsStep.missingClusterPrivilegesDescription"
defaultMessage="The deprecation logs will continue to be indexed, but you won't be able to analyze them until you have the cluster {privilegesCount, plural, one {privilege} other {privileges}} for: {missingPrivileges}"
values={{
missingPrivileges: (
<EuiCode transparentBackground={true}>{privilegesMissing?.cluster?.join(', ')}</EuiCode>
),
privilegesCount: privilegesMissing?.cluster?.length,
}}
/>
),
@ -132,14 +154,29 @@ const LogsStep = ({
<EuiSpacer />
<EuiCallOut
iconType="help"
color="warning"
title={i18nTexts.missingPrivilegesTitle}
data-test-subj="missingPrivilegesCallout"
>
<p>{i18nTexts.missingPrivilegesDescription(privilegesMissing)}</p>
</EuiCallOut>
{privilegesMissing.cluster && (
<EuiCallOut
iconType="help"
color="warning"
title={i18nTexts.missingClusterPrivilegesTitle}
data-test-subj="missingClusterPrivilegesCallout"
>
<p>{i18nTexts.missingClusterPrivilegesDescription(privilegesMissing)}</p>
</EuiCallOut>
)}
{privilegesMissing.cluster && privilegesMissing.index && <EuiSpacer />}
{privilegesMissing.index && (
<EuiCallOut
iconType="help"
color="warning"
title={i18nTexts.missingIndexPrivilegesTitle}
data-test-subj="missingIndexPrivilegesCallout"
>
<p>{i18nTexts.missingIndexPrivilegesDescription(privilegesMissing)}</p>
</EuiCallOut>
)}
</>
);
}
@ -216,12 +253,17 @@ export const getLogsStep = ({
}: OverviewStepProps & CustomProps): EuiStepProps => {
const status = isComplete ? 'complete' : 'incomplete';
const requiredPrivileges = [
`index.${DEPRECATION_LOGS_INDEX}`,
...APP_LOGS_COUNT_CLUSTER_PRIVILEGES.map((privilege) => `cluster.${privilege}`),
];
return {
status,
title: i18nTexts.logsStepTitle,
'data-test-subj': `logsStep-${status}`,
children: (
<WithPrivileges privileges={`index.${DEPRECATION_LOGS_INDEX}`}>
<WithPrivileges privileges={requiredPrivileges}>
{({ hasPrivileges, isLoading, privilegesMissing }) => (
<LogsStep
setIsComplete={setIsComplete}

View file

@ -5,12 +5,17 @@
* 2.0.
*/
import { API_BASE_PATH, DEPRECATION_LOGS_INDEX } from '../../common/constants';
import {
API_BASE_PATH,
DEPRECATION_LOGS_INDEX,
APP_LOGS_COUNT_INDEX_PRIVILEGES,
APP_LOGS_COUNT_CLUSTER_PRIVILEGES,
} from '../../common/constants';
import { versionCheckHandlerWrapper } from '../lib/es_version_precheck';
import { Privileges } from '../shared_imports';
import { RouteDependencies } from '../types';
const extractMissingPrivileges = (
const extractMissingIndexPrivileges = (
privilegesObject: { [key: string]: Record<string, boolean> } = {}
): string[] =>
Object.keys(privilegesObject).reduce((privileges: string[], privilegeName: string): string[] => {
@ -20,6 +25,16 @@ const extractMissingPrivileges = (
return privileges;
}, []);
const extractMissingClusterPrivileges = (
privilegesObject: { [key: string]: boolean } = {}
): string[] =>
Object.keys(privilegesObject).reduce((privileges: string[], privilegeName: string): string[] => {
if (!privilegesObject[privilegeName]) {
privileges.push(privilegeName);
}
return privileges;
}, []);
export function registerAppRoutes({
router,
lib: { handleEsError },
@ -38,6 +53,7 @@ export function registerAppRoutes({
hasAllPrivileges: true,
missingPrivileges: {
index: [],
cluster: [],
},
};
@ -46,20 +62,25 @@ export function registerAppRoutes({
}
try {
const { has_all_requested: hasAllPrivileges, index } =
await client.asCurrentUser.security.hasPrivileges({
body: {
index: [
{
names: [DEPRECATION_LOGS_INDEX],
privileges: ['read'],
},
],
},
});
const {
has_all_requested: hasAllPrivileges,
index,
cluster,
} = await client.asCurrentUser.security.hasPrivileges({
body: {
cluster: APP_LOGS_COUNT_CLUSTER_PRIVILEGES,
index: [
{
names: [DEPRECATION_LOGS_INDEX],
privileges: APP_LOGS_COUNT_INDEX_PRIVILEGES,
},
],
},
});
if (!hasAllPrivileges) {
privilegesResult.missingPrivileges.index = extractMissingPrivileges(index);
privilegesResult.missingPrivileges.index = extractMissingIndexPrivileges(index);
privilegesResult.missingPrivileges.cluster = extractMissingClusterPrivileges(cluster);
}
privilegesResult.hasAllPrivileges = hasAllPrivileges;