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

[ML] Adds option to Reauthorize transform in Management page #154736

Merged
merged 24 commits into from
Apr 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
707b75b
Update logic for needs authorization check
qn895 Apr 10, 2023
ec5a456
Update types
qn895 Apr 10, 2023
2aa006d
[ML] Update reauthorization
qn895 Apr 11, 2023
db82d60
[ML] Fix disabled messages for reauthorize, delete, reset
qn895 Apr 11, 2023
fc6088d
[ML] Revert change to delete and reset action
qn895 Apr 11, 2023
d415f40
Merge remote-tracking branch 'upstream/main' into ml-transform-manage…
qn895 Apr 11, 2023
f5319b2
[ML] Fix i18n
qn895 Apr 12, 2023
f94acda
[ML] Fix i18n and messaging
qn895 Apr 12, 2023
5f43e8d
[ML] Fix i18n and messaging
qn895 Apr 12, 2023
970ee8f
Merge branch 'ml-transform-management-reauthorize' of https://github.…
qn895 Apr 12, 2023
84b9ad3
Merge branch 'main' into ml-transform-management-reauthorize
kibanamachine Apr 12, 2023
0cea051
Merge remote-tracking branch 'upstream/main' into ml-transform-manage…
qn895 Apr 15, 2023
3f64b1f
Fix messaging and is populated obj
qn895 Apr 15, 2023
e8d16e7
Add new reauthorize transforms tests
qn895 Apr 16, 2023
9e73407
Merge branch 'ml-transform-management-reauthorize' of https://github.…
qn895 Apr 17, 2023
c44929b
Remove only
qn895 Apr 17, 2023
bc40330
Refactor createTransform to have headers/deferValidation options. Rem…
qn895 Apr 17, 2023
1a8969a
Update texts
qn895 Apr 17, 2023
a1b0374
Merge remote-tracking branch 'upstream/main' into ml-transform-manage…
qn895 Apr 19, 2023
1775dd0
Update messaging
qn895 Apr 19, 2023
5329239
Add order sequential install
qn895 Apr 19, 2023
b539864
Update translations
qn895 Apr 20, 2023
230e386
Handle case where security is not enabled
qn895 Apr 20, 2023
6d9a641
Merge remote-tracking branch 'upstream/main' into ml-transform-manage…
qn895 Apr 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* 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 { TypeOf } from '@kbn/config-schema';

import { transformIdsSchema, CommonResponseStatusSchema } from './common';

export const reauthorizeTransformsRequestSchema = transformIdsSchema;
export type ReauthorizeTransformsRequestSchema = TypeOf<typeof reauthorizeTransformsRequestSchema>;
export type ReauthorizeTransformsResponseSchema = CommonResponseStatusSchema;
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ describe('has_privilege_factory', () => {
canDeleteTransform: true,
canGetTransform: true,
canPreviewTransform: true,
canReauthorizeTransform: true,
canResetTransform: true,
canScheduleNowTransform: true,
canStartStopTransform: true,
Expand All @@ -96,6 +97,7 @@ describe('has_privilege_factory', () => {
canDeleteTransform: false,
canGetTransform: true,
canPreviewTransform: false,
canReauthorizeTransform: false,
canResetTransform: false,
canScheduleNowTransform: false,
canStartStopTransform: false,
Expand Down Expand Up @@ -124,6 +126,7 @@ describe('has_privilege_factory', () => {
canGetTransform: false,
canPreviewTransform: false,
canResetTransform: false,
canReauthorizeTransform: false,
canScheduleNowTransform: false,
canStartStopTransform: false,
canUseTransformAlerts: false,
Expand Down
14 changes: 14 additions & 0 deletions x-pack/plugins/transform/common/privilege/has_privilege_factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface TransformCapabilities {
canDeleteTransform: boolean;
canPreviewTransform: boolean;
canCreateTransform: boolean;
canReauthorizeTransform: boolean;
canScheduleNowTransform: boolean;
canStartStopTransform: boolean;
canCreateTransformAlerts: boolean;
Expand All @@ -30,6 +31,7 @@ export const INITIAL_CAPABILITIES = Object.freeze<Capabilities>({
canDeleteTransform: false,
canPreviewTransform: false,
canCreateTransform: false,
canReauthorizeTransform: false,
canScheduleNowTransform: false,
canStartStopTransform: false,
canCreateTransformAlerts: false,
Expand Down Expand Up @@ -130,6 +132,8 @@ export const getPrivilegesAndCapabilities = (

capabilities.canScheduleNowTransform = capabilities.canStartStopTransform;

capabilities.canReauthorizeTransform = capabilities.canStartStopTransform;

return { privileges: privilegesResult, capabilities };
};
// create the text for button's tooltips if the user
Expand Down Expand Up @@ -170,6 +174,16 @@ export function createCapabilityFailureMessage(
}
);
break;

case 'canReauthorizeTransform':
message = i18n.translate(
'xpack.transform.capability.noPermission.reauthorizeTransformTooltip',
{
defaultMessage: 'You do not have permission to reauthorize transforms.',
}
);
break;

case 'canDeleteTransform':
message = i18n.translate('xpack.transform.capability.noPermission.deleteTransformTooltip', {
defaultMessage: 'You do not have permission to delete transforms.',
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/transform/common/types/transform_stats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { type TransformHealth, type TransformState, TRANSFORM_STATE } from '../c
import { TransformId } from './transform';

export interface TransformHealthIssue {
type: string;
issue: string;
details?: string;
count: number;
Expand Down
41 changes: 41 additions & 0 deletions x-pack/plugins/transform/common/utils/transform_api_key.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* 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 { GrantAPIKeyResult } from '@kbn/security-plugin/server';
import type { SecurityCreateApiKeyResponse } from '@elastic/elasticsearch/lib/api/types';
import { isPopulatedObject } from '@kbn/ml-is-populated-object';

export interface TransformAPIKey extends GrantAPIKeyResult {
/**
* Generated encoded API key used for headers
*/
encoded: string;
}

export interface SecondaryAuthorizationHeader {
headers?: { 'es-secondary-authorization': string | string[] };
}

export function isTransformApiKey(arg: any): arg is TransformAPIKey {
return isPopulatedObject(arg, ['api_key', 'encoded']) && typeof arg.encoded === 'string';
}

export function generateTransformSecondaryAuthHeaders(
apiKeyWithCurrentUserPermission:
| GrantAPIKeyResult
| null
| undefined
| SecurityCreateApiKeyResponse
): SecondaryAuthorizationHeader | undefined {
return isTransformApiKey(apiKeyWithCurrentUserPermission)
? {
headers: {
'es-secondary-authorization': `ApiKey ${apiKeyWithCurrentUserPermission.encoded}`,
},
}
: undefined;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* 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 { isPopulatedObject } from '@kbn/ml-is-populated-object';
import type { TransformHealthIssue } from '../../../common/types/transform_stats';
import { TRANSFORM_HEALTH } from '../../../common/constants';
import type { TransformListRow } from './transform_list';

export const needsReauthorization = (transform: Partial<TransformListRow>) => {
return (
isPopulatedObject(transform.config?.authorization, ['api_key']) &&
isPopulatedObject(transform.stats) &&
transform.stats.health.status === TRANSFORM_HEALTH.red &&
transform.stats.health.issues?.find(
(issue) => (issue as TransformHealthIssue).issue === 'Privileges check failed'
) !== undefined
);
};
16 changes: 16 additions & 0 deletions x-pack/plugins/transform/public/app/hooks/use_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ import type { IHttpFetchError } from '@kbn/core-http-browser';

import { KBN_FIELD_TYPES } from '@kbn/field-types';

import {
ReauthorizeTransformsRequestSchema,
ReauthorizeTransformsResponseSchema,
} from '../../../common/api_schemas/reauthorize_transforms';
import type { GetTransformsAuditMessagesResponseSchema } from '../../../common/api_schemas/audit_messages';
import type {
DeleteTransformsRequestSchema,
Expand Down Expand Up @@ -166,6 +170,18 @@ export const useApi = () => {
return e;
}
},
async reauthorizeTransforms(
reqBody: ReauthorizeTransformsRequestSchema
): Promise<ReauthorizeTransformsResponseSchema | IHttpFetchError> {
try {
return await http.post(`${API_BASE_PATH}reauthorize_transforms`, {
body: JSON.stringify(reqBody),
});
} catch (e) {
return e;
}
},

async resetTransforms(
reqBody: ResetTransformsRequestSchema
): Promise<ResetTransformsResponseSchema | IHttpFetchError> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* 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 React from 'react';

import { i18n } from '@kbn/i18n';

import { toMountPoint } from '@kbn/kibana-react-plugin/public';

import type { StartTransformsRequestSchema } from '../../../common/api_schemas/start_transforms';
import { isStartTransformsResponseSchema } from '../../../common/api_schemas/type_guards';

import { getErrorMessage } from '../../../common/utils/errors';

import { useAppDependencies, useToastNotifications } from '../app_dependencies';
import { refreshTransformList$, REFRESH_TRANSFORM_LIST_STATE } from '../common';
import { ToastNotificationText } from '../components';

import { useApi } from './use_api';

export const useReauthorizeTransforms = () => {
const { overlays, theme } = useAppDependencies();
const toastNotifications = useToastNotifications();
const api = useApi();

return async (transformsInfo: StartTransformsRequestSchema) => {
const results = await api.reauthorizeTransforms(transformsInfo);

if (!isStartTransformsResponseSchema(results)) {
toastNotifications.addDanger({
title: i18n.translate(
'xpack.transform.stepCreateForm.reauthorizeTransformResponseSchemaErrorMessage',
{
defaultMessage: 'An error occurred calling the reauthorize transforms request.',
}
),
text: toMountPoint(
<ToastNotificationText
overlays={overlays}
theme={theme}
text={getErrorMessage(results)}
/>,
{ theme$: theme.theme$ }
),
});
return;
}

for (const transformId in results) {
// hasOwnProperty check to ensure only properties on object itself, and not its prototypes
if (results.hasOwnProperty(transformId)) {
const result = results[transformId];
if (result.success === true) {
toastNotifications.addSuccess(
i18n.translate('xpack.transform.transformList.reauthorizeTransformSuccessMessage', {
defaultMessage: 'Request to reauthorize transform {transformId} acknowledged.',
values: { transformId },
})
);
} else {
toastNotifications.addError(new Error(JSON.stringify(result.error!.caused_by, null, 2)), {
title: i18n.translate(
'xpack.transform.transformList.reauthorizeTransformErrorMessage',
{
defaultMessage: 'An error occurred reauthorizing the transform {transformId}',
values: { transformId },
}
),
toastMessage: result.error!.reason,
});
}
}
}

refreshTransformList$.next(REFRESH_TRANSFORM_LIST_STATE.REFRESH);
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* 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.
*/

export { useReauthorizeAction } from './use_reauthorize_action';
export { ReauthorizeActionModal } from './reauthorize_action_modal';
export { isReauthorizeActionDisabled, ReauthorizeActionName } from './reauthorize_action_name';
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* 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 React, { FC } from 'react';
import { i18n } from '@kbn/i18n';
import { EUI_MODAL_CONFIRM_BUTTON, EuiConfirmModal } from '@elastic/eui';
import type { ReauthorizeAction } from './use_reauthorize_action';

export const ReauthorizeActionModal: FC<ReauthorizeAction> = ({
closeModal,
items,
reauthorizeAndCloseModal,
}) => {
const isBulkAction = items.length > 1;

const bulkReauthorizeModalTitle = i18n.translate(
'xpack.transform.transformList.bulkReauthorizeModalTitle',
{
defaultMessage: 'Reauthorize {count} {count, plural, one {transform} other {transforms}}?',
values: { count: items && items.length },
}
);
const reauthorizeModalTitle = i18n.translate(
'xpack.transform.transformList.reauthorizeModalTitle',
{
defaultMessage: 'Reauthorize {transformId}?',
values: { transformId: items[0] && items[0].config.id },
}
);

return (
<EuiConfirmModal
data-test-subj="transformReauthorizeModal"
title={isBulkAction === true ? bulkReauthorizeModalTitle : reauthorizeModalTitle}
onCancel={closeModal}
onConfirm={reauthorizeAndCloseModal}
cancelButtonText={i18n.translate(
'xpack.transform.transformList.reauthorizeModalCancelButton',
{
defaultMessage: 'Cancel',
}
)}
confirmButtonText={i18n.translate(
'xpack.transform.transformList.reauthorizeModalConfirmButton',
{
defaultMessage: 'Reauthorize',
}
)}
defaultFocusedButton={EUI_MODAL_CONFIRM_BUTTON}
buttonColor="primary"
>
<p>
{i18n.translate('xpack.transform.transformList.reauthorizeModalBody', {
defaultMessage:
'Your current roles are used to update and start the transform. Starting a transform increases search and indexing load in your cluster. If excessive load is experienced, stop the transform.',
})}
</p>
</EuiConfirmModal>
);
};
Loading