* More telemetry * Fix tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
616593c33c
commit
93f5cac21a
|
@ -20,6 +20,7 @@ import { licenseStateMock } from './lib/license_state.mock';
|
|||
import { licensingMock } from '../../licensing/server/mocks';
|
||||
import { httpServerMock } from '../../../../src/core/server/mocks';
|
||||
import { auditServiceMock } from '../../security/server/audit/index.mock';
|
||||
import { usageCountersServiceMock } from 'src/plugins/usage_collection/server/usage_counters/usage_counters_service.mock';
|
||||
|
||||
import {
|
||||
elasticsearchServiceMock,
|
||||
|
@ -28,7 +29,12 @@ import {
|
|||
import { actionExecutorMock } from './lib/action_executor.mock';
|
||||
import uuid from 'uuid';
|
||||
import { ActionsAuthorization } from './authorization/actions_authorization';
|
||||
import {
|
||||
getAuthorizationModeBySource,
|
||||
AuthorizationMode,
|
||||
} from './authorization/get_authorization_mode_by_source';
|
||||
import { actionsAuthorizationMock } from './authorization/actions_authorization.mock';
|
||||
import { trackLegacyRBACExemption } from './lib/track_legacy_rbac_exemption';
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { elasticsearchClientMock } from '../../../../src/core/server/elasticsearch/client/mocks';
|
||||
|
||||
|
@ -38,6 +44,22 @@ jest.mock('../../../../src/core/server/saved_objects/service/lib/utils', () => (
|
|||
},
|
||||
}));
|
||||
|
||||
jest.mock('./lib/track_legacy_rbac_exemption', () => ({
|
||||
trackLegacyRBACExemption: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('./authorization/get_authorization_mode_by_source', () => {
|
||||
return {
|
||||
getAuthorizationModeBySource: jest.fn(() => {
|
||||
return 1;
|
||||
}),
|
||||
AuthorizationMode: {
|
||||
Legacy: 0,
|
||||
RBAC: 1,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const defaultKibanaIndex = '.kibana';
|
||||
const unsecuredSavedObjectsClient = savedObjectsClientMock.create();
|
||||
const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient();
|
||||
|
@ -47,6 +69,8 @@ const executionEnqueuer = jest.fn();
|
|||
const ephemeralExecutionEnqueuer = jest.fn();
|
||||
const request = httpServerMock.createKibanaRequest();
|
||||
const auditLogger = auditServiceMock.create().asScoped(request);
|
||||
const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract();
|
||||
const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test');
|
||||
|
||||
const mockTaskManager = taskManagerMock.createSetup();
|
||||
|
||||
|
@ -82,6 +106,7 @@ beforeEach(() => {
|
|||
request,
|
||||
authorization: authorization as unknown as ActionsAuthorization,
|
||||
auditLogger,
|
||||
usageCounter: mockUsageCounter,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1640,6 +1665,9 @@ describe('update()', () => {
|
|||
describe('execute()', () => {
|
||||
describe('authorization', () => {
|
||||
test('ensures user is authorised to excecute actions', async () => {
|
||||
(getAuthorizationModeBySource as jest.Mock).mockImplementationOnce(() => {
|
||||
return AuthorizationMode.RBAC;
|
||||
});
|
||||
await actionsClient.execute({
|
||||
actionId: 'action-id',
|
||||
params: {
|
||||
|
@ -1650,6 +1678,9 @@ describe('execute()', () => {
|
|||
});
|
||||
|
||||
test('throws when user is not authorised to create the type of action', async () => {
|
||||
(getAuthorizationModeBySource as jest.Mock).mockImplementationOnce(() => {
|
||||
return AuthorizationMode.RBAC;
|
||||
});
|
||||
authorization.ensureAuthorized.mockRejectedValue(
|
||||
new Error(`Unauthorized to execute all actions`)
|
||||
);
|
||||
|
@ -1665,6 +1696,21 @@ describe('execute()', () => {
|
|||
|
||||
expect(authorization.ensureAuthorized).toHaveBeenCalledWith('execute');
|
||||
});
|
||||
|
||||
test('tracks legacy RBAC', async () => {
|
||||
(getAuthorizationModeBySource as jest.Mock).mockImplementationOnce(() => {
|
||||
return AuthorizationMode.Legacy;
|
||||
});
|
||||
|
||||
await actionsClient.execute({
|
||||
actionId: 'action-id',
|
||||
params: {
|
||||
name: 'my name',
|
||||
},
|
||||
});
|
||||
|
||||
expect(trackLegacyRBACExemption as jest.Mock).toBeCalledWith('execute', mockUsageCounter);
|
||||
});
|
||||
});
|
||||
|
||||
test('calls the actionExecutor with the appropriate parameters', async () => {
|
||||
|
@ -1756,6 +1802,9 @@ describe('execute()', () => {
|
|||
describe('enqueueExecution()', () => {
|
||||
describe('authorization', () => {
|
||||
test('ensures user is authorised to excecute actions', async () => {
|
||||
(getAuthorizationModeBySource as jest.Mock).mockImplementationOnce(() => {
|
||||
return AuthorizationMode.RBAC;
|
||||
});
|
||||
await actionsClient.enqueueExecution({
|
||||
id: uuid.v4(),
|
||||
params: {},
|
||||
|
@ -1766,6 +1815,9 @@ describe('enqueueExecution()', () => {
|
|||
});
|
||||
|
||||
test('throws when user is not authorised to create the type of action', async () => {
|
||||
(getAuthorizationModeBySource as jest.Mock).mockImplementationOnce(() => {
|
||||
return AuthorizationMode.RBAC;
|
||||
});
|
||||
authorization.ensureAuthorized.mockRejectedValue(
|
||||
new Error(`Unauthorized to execute all actions`)
|
||||
);
|
||||
|
@ -1781,6 +1833,24 @@ describe('enqueueExecution()', () => {
|
|||
|
||||
expect(authorization.ensureAuthorized).toHaveBeenCalledWith('execute');
|
||||
});
|
||||
|
||||
test('tracks legacy RBAC', async () => {
|
||||
(getAuthorizationModeBySource as jest.Mock).mockImplementationOnce(() => {
|
||||
return AuthorizationMode.Legacy;
|
||||
});
|
||||
|
||||
await actionsClient.enqueueExecution({
|
||||
id: uuid.v4(),
|
||||
params: {},
|
||||
spaceId: 'default',
|
||||
apiKey: null,
|
||||
});
|
||||
|
||||
expect(trackLegacyRBACExemption as jest.Mock).toBeCalledWith(
|
||||
'enqueueExecution',
|
||||
mockUsageCounter
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test('calls the executionEnqueuer with the appropriate parameters', async () => {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import Boom from '@hapi/boom';
|
||||
import type { estypes } from '@elastic/elasticsearch';
|
||||
import { UsageCounter } from 'src/plugins/usage_collection/server';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { omitBy, isUndefined } from 'lodash';
|
||||
|
@ -42,6 +43,7 @@ import {
|
|||
} from './authorization/get_authorization_mode_by_source';
|
||||
import { connectorAuditEvent, ConnectorAuditAction } from './lib/audit_events';
|
||||
import { RunNowResult } from '../../task_manager/server';
|
||||
import { trackLegacyRBACExemption } from './lib/track_legacy_rbac_exemption';
|
||||
|
||||
// We are assuming there won't be many actions. This is why we will load
|
||||
// all the actions in advance and assume the total count to not go over 10000.
|
||||
|
@ -74,6 +76,7 @@ interface ConstructorOptions {
|
|||
request: KibanaRequest;
|
||||
authorization: ActionsAuthorization;
|
||||
auditLogger?: AuditLogger;
|
||||
usageCounter?: UsageCounter;
|
||||
}
|
||||
|
||||
export interface UpdateOptions {
|
||||
|
@ -93,6 +96,7 @@ export class ActionsClient {
|
|||
private readonly executionEnqueuer: ExecutionEnqueuer<void>;
|
||||
private readonly ephemeralExecutionEnqueuer: ExecutionEnqueuer<RunNowResult>;
|
||||
private readonly auditLogger?: AuditLogger;
|
||||
private readonly usageCounter?: UsageCounter;
|
||||
|
||||
constructor({
|
||||
actionTypeRegistry,
|
||||
|
@ -106,6 +110,7 @@ export class ActionsClient {
|
|||
request,
|
||||
authorization,
|
||||
auditLogger,
|
||||
usageCounter,
|
||||
}: ConstructorOptions) {
|
||||
this.actionTypeRegistry = actionTypeRegistry;
|
||||
this.unsecuredSavedObjectsClient = unsecuredSavedObjectsClient;
|
||||
|
@ -118,6 +123,7 @@ export class ActionsClient {
|
|||
this.request = request;
|
||||
this.authorization = authorization;
|
||||
this.auditLogger = auditLogger;
|
||||
this.usageCounter = usageCounter;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -478,6 +484,8 @@ export class ActionsClient {
|
|||
AuthorizationMode.RBAC
|
||||
) {
|
||||
await this.authorization.ensureAuthorized('execute');
|
||||
} else {
|
||||
trackLegacyRBACExemption('execute', this.usageCounter);
|
||||
}
|
||||
return this.actionExecutor.execute({
|
||||
actionId,
|
||||
|
@ -495,6 +503,8 @@ export class ActionsClient {
|
|||
AuthorizationMode.RBAC
|
||||
) {
|
||||
await this.authorization.ensureAuthorized('execute');
|
||||
} else {
|
||||
trackLegacyRBACExemption('enqueueExecution', this.usageCounter);
|
||||
}
|
||||
return this.executionEnqueuer(this.unsecuredSavedObjectsClient, options);
|
||||
}
|
||||
|
@ -506,6 +516,8 @@ export class ActionsClient {
|
|||
AuthorizationMode.RBAC
|
||||
) {
|
||||
await this.authorization.ensureAuthorized('execute');
|
||||
} else {
|
||||
trackLegacyRBACExemption('ephemeralEnqueuedExecution', this.usageCounter);
|
||||
}
|
||||
return this.ephemeralExecutionEnqueuer(this.unsecuredSavedObjectsClient, options);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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 { usageCountersServiceMock } from 'src/plugins/usage_collection/server/usage_counters/usage_counters_service.mock';
|
||||
import { trackLegacyRBACExemption } from './track_legacy_rbac_exemption';
|
||||
|
||||
describe('trackLegacyRBACExemption', () => {
|
||||
it('should call `usageCounter.incrementCounter`', () => {
|
||||
const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract();
|
||||
const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test');
|
||||
|
||||
trackLegacyRBACExemption('test', mockUsageCounter);
|
||||
expect(mockUsageCounter.incrementCounter).toHaveBeenCalledWith({
|
||||
counterName: `source_test`,
|
||||
counterType: 'legacyRBACExemption',
|
||||
incrementBy: 1,
|
||||
});
|
||||
});
|
||||
|
||||
it('should do nothing if no usage counter is provided', () => {
|
||||
let err;
|
||||
try {
|
||||
trackLegacyRBACExemption('test', undefined);
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
expect(err).toBeUndefined();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* 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 { UsageCounter } from 'src/plugins/usage_collection/server';
|
||||
|
||||
export function trackLegacyRBACExemption(source: string, usageCounter?: UsageCounter) {
|
||||
if (usageCounter) {
|
||||
usageCounter.incrementCounter({
|
||||
counterName: `source_${source}`,
|
||||
counterType: 'legacyRBACExemption',
|
||||
incrementBy: 1,
|
||||
});
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import type { PublicMethodsOf } from '@kbn/utility-types';
|
||||
import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
|
||||
import { UsageCollectionSetup, UsageCounter } from 'src/plugins/usage_collection/server';
|
||||
import {
|
||||
PluginInitializerContext,
|
||||
Plugin,
|
||||
|
@ -151,6 +151,7 @@ export class ActionsPlugin implements Plugin<PluginSetupContract, PluginStartCon
|
|||
private eventLogService?: IEventLogService;
|
||||
private eventLogger?: IEventLogger;
|
||||
private isESOCanEncrypt?: boolean;
|
||||
private usageCounter?: UsageCounter;
|
||||
private readonly telemetryLogger: Logger;
|
||||
private readonly preconfiguredActions: PreConfiguredAction[];
|
||||
private readonly kibanaIndexConfig: { kibana: { index: string } };
|
||||
|
@ -264,13 +265,13 @@ export class ActionsPlugin implements Plugin<PluginSetupContract, PluginStartCon
|
|||
}
|
||||
|
||||
// Usage counter for telemetry
|
||||
const usageCounter = plugins.usageCollection?.createUsageCounter(ACTIONS_FEATURE_ID);
|
||||
this.usageCounter = plugins.usageCollection?.createUsageCounter(ACTIONS_FEATURE_ID);
|
||||
|
||||
// Routes
|
||||
defineRoutes(
|
||||
core.http.createRouter<ActionsRequestHandlerContext>(),
|
||||
this.licenseState,
|
||||
usageCounter
|
||||
this.usageCounter
|
||||
);
|
||||
|
||||
// Cleanup failed execution task definition
|
||||
|
@ -367,6 +368,7 @@ export class ActionsPlugin implements Plugin<PluginSetupContract, PluginStartCon
|
|||
preconfiguredActions,
|
||||
}),
|
||||
auditLogger: this.security?.audit.asScoped(request),
|
||||
usageCounter: this.usageCounter,
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -497,6 +499,7 @@ export class ActionsPlugin implements Plugin<PluginSetupContract, PluginStartCon
|
|||
actionExecutor,
|
||||
instantiateAuthorization,
|
||||
security,
|
||||
usageCounter,
|
||||
} = this;
|
||||
|
||||
return async function actionsRouteHandlerContext(context, request) {
|
||||
|
@ -533,6 +536,7 @@ export class ActionsPlugin implements Plugin<PluginSetupContract, PluginStartCon
|
|||
preconfiguredActions,
|
||||
}),
|
||||
auditLogger: security?.audit.asScoped(request),
|
||||
usageCounter,
|
||||
});
|
||||
},
|
||||
listTypes: actionTypeRegistry!.list.bind(actionTypeRegistry!),
|
||||
|
|
Loading…
Reference in a new issue