Skip to content

Commit

Permalink
[Alerting] Refactor alerts authorization client (elastic#99078)
Browse files Browse the repository at this point in the history
* WIP - creating alerting authorization client factory and exposing authorization client on plugin start contract

* Updating alerting feature privilege builder to handle different alerting types

* Passing in alerting authorization type to AlertingActions class string builder

* Passing in authorization type in each function call

* Passing in exempt consumer ids. Adding authorization type to audit logger

* Changing alertType to ruleType

* Changing alertType to ruleType

* Updating unit tests

* Updating unit tests

* Passing field names into authorization query builder. Adding kql/es dsl option

* Converting to es query if requested

* Fixing functional tests

* Removing ability to specify feature privilege name in constructor

* Fixing some types and tests

* Consolidating alerting authorization kuery filter options

* Cleanup and tests

* Cleanup and tests

* Throwing error when AlertingAuthorizationClientFactory is not defined

* Renaming authorizationType to entity

* Renaming AlertsAuthorization to AlertingAuthorization

* Fixing unit tests

* Updating privilege string terminology

* Updating privilege string terminology

Co-authored-by: Kibana Machine <[email protected]>
  • Loading branch information
ymao1 and kibanamachine committed May 18, 2021
1 parent 9768ee2 commit 8049d95
Show file tree
Hide file tree
Showing 50 changed files with 2,456 additions and 1,948 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* 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 { PublicMethodsOf } from '@kbn/utility-types';
import { AlertingAuthorizationClientFactory } from './alerting_authorization_client_factory';

const creatAlertingAuthorizationClientFactoryMock = () => {
const mocked: jest.Mocked<PublicMethodsOf<AlertingAuthorizationClientFactory>> = {
create: jest.fn(),
initialize: jest.fn(),
};
return mocked;
};

export const alertingAuthorizationClientFactoryMock = {
createFactory: creatAlertingAuthorizationClientFactoryMock,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
* 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 { Request } from '@hapi/hapi';
import { alertTypeRegistryMock } from './alert_type_registry.mock';
import { KibanaRequest } from '../../../../src/core/server';
import { savedObjectsClientMock } from '../../../../src/core/server/mocks';
import { securityMock } from '../../security/server/mocks';
import { ALERTS_FEATURE_ID } from '../common';
import {
AlertingAuthorizationClientFactory,
AlertingAuthorizationClientFactoryOpts,
} from './alerting_authorization_client_factory';
import { featuresPluginMock } from '../../features/server/mocks';

jest.mock('./authorization/alerting_authorization');
jest.mock('./authorization/audit_logger');

const savedObjectsClient = savedObjectsClientMock.create();
const features = featuresPluginMock.createStart();

const securityPluginSetup = securityMock.createSetup();
const securityPluginStart = securityMock.createStart();

const alertingAuthorizationClientFactoryParams: jest.Mocked<AlertingAuthorizationClientFactoryOpts> = {
alertTypeRegistry: alertTypeRegistryMock.create(),
getSpace: jest.fn(),
features,
};

const fakeRequest = ({
app: {},
headers: {},
getBasePath: () => '',
path: '/',
route: { settings: {} },
url: {
href: '/',
},
raw: {
req: {
url: '/',
},
},
getSavedObjectsClient: () => savedObjectsClient,
} as unknown) as Request;

beforeEach(() => {
jest.resetAllMocks();
});

test('creates an alerting authorization client with proper constructor arguments when security is enabled', async () => {
const factory = new AlertingAuthorizationClientFactory();
factory.initialize({
securityPluginSetup,
securityPluginStart,
...alertingAuthorizationClientFactoryParams,
});
const request = KibanaRequest.from(fakeRequest);
const { AlertingAuthorizationAuditLogger } = jest.requireMock('./authorization/audit_logger');

factory.create(request);

const { AlertingAuthorization } = jest.requireMock('./authorization/alerting_authorization');
expect(AlertingAuthorization).toHaveBeenCalledWith({
request,
authorization: securityPluginStart.authz,
alertTypeRegistry: alertingAuthorizationClientFactoryParams.alertTypeRegistry,
features: alertingAuthorizationClientFactoryParams.features,
auditLogger: expect.any(AlertingAuthorizationAuditLogger),
getSpace: expect.any(Function),
exemptConsumerIds: [],
});

expect(AlertingAuthorizationAuditLogger).toHaveBeenCalled();
expect(securityPluginSetup.audit.getLogger).toHaveBeenCalledWith(ALERTS_FEATURE_ID);
});

test('creates an alerting authorization client with proper constructor arguments when exemptConsumerIds are specified', async () => {
const factory = new AlertingAuthorizationClientFactory();
factory.initialize({
securityPluginSetup,
securityPluginStart,
...alertingAuthorizationClientFactoryParams,
});
const request = KibanaRequest.from(fakeRequest);
const { AlertingAuthorizationAuditLogger } = jest.requireMock('./authorization/audit_logger');

factory.create(request, ['exemptConsumerA', 'exemptConsumerB']);

const { AlertingAuthorization } = jest.requireMock('./authorization/alerting_authorization');
expect(AlertingAuthorization).toHaveBeenCalledWith({
request,
authorization: securityPluginStart.authz,
alertTypeRegistry: alertingAuthorizationClientFactoryParams.alertTypeRegistry,
features: alertingAuthorizationClientFactoryParams.features,
auditLogger: expect.any(AlertingAuthorizationAuditLogger),
getSpace: expect.any(Function),
exemptConsumerIds: ['exemptConsumerA', 'exemptConsumerB'],
});

expect(AlertingAuthorizationAuditLogger).toHaveBeenCalled();
expect(securityPluginSetup.audit.getLogger).toHaveBeenCalledWith(ALERTS_FEATURE_ID);
});

test('creates an alerting authorization client with proper constructor arguments', async () => {
const factory = new AlertingAuthorizationClientFactory();
factory.initialize(alertingAuthorizationClientFactoryParams);
const request = KibanaRequest.from(fakeRequest);
const { AlertingAuthorizationAuditLogger } = jest.requireMock('./authorization/audit_logger');

factory.create(request);

const { AlertingAuthorization } = jest.requireMock('./authorization/alerting_authorization');
expect(AlertingAuthorization).toHaveBeenCalledWith({
request,
alertTypeRegistry: alertingAuthorizationClientFactoryParams.alertTypeRegistry,
features: alertingAuthorizationClientFactoryParams.features,
auditLogger: expect.any(AlertingAuthorizationAuditLogger),
getSpace: expect.any(Function),
exemptConsumerIds: [],
});

expect(AlertingAuthorizationAuditLogger).toHaveBeenCalled();
expect(securityPluginSetup.audit.getLogger).not.toHaveBeenCalled();
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* 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 { KibanaRequest } from 'src/core/server';
import { ALERTS_FEATURE_ID } from '../common';
import { AlertTypeRegistry } from './types';
import { SecurityPluginSetup, SecurityPluginStart } from '../../security/server';
import { PluginStartContract as FeaturesPluginStart } from '../../features/server';
import { AlertingAuthorization } from './authorization/alerting_authorization';
import { AlertingAuthorizationAuditLogger } from './authorization/audit_logger';
import { Space } from '../../spaces/server';

export interface AlertingAuthorizationClientFactoryOpts {
alertTypeRegistry: AlertTypeRegistry;
securityPluginSetup?: SecurityPluginSetup;
securityPluginStart?: SecurityPluginStart;
getSpace: (request: KibanaRequest) => Promise<Space | undefined>;
features: FeaturesPluginStart;
}

export class AlertingAuthorizationClientFactory {
private isInitialized = false;
private alertTypeRegistry!: AlertTypeRegistry;
private securityPluginStart?: SecurityPluginStart;
private securityPluginSetup?: SecurityPluginSetup;
private features!: FeaturesPluginStart;
private getSpace!: (request: KibanaRequest) => Promise<Space | undefined>;

public initialize(options: AlertingAuthorizationClientFactoryOpts) {
if (this.isInitialized) {
throw new Error('AlertingAuthorizationClientFactory already initialized');
}
this.isInitialized = true;
this.getSpace = options.getSpace;
this.alertTypeRegistry = options.alertTypeRegistry;
this.securityPluginSetup = options.securityPluginSetup;
this.securityPluginStart = options.securityPluginStart;
this.features = options.features;
}

public create(request: KibanaRequest, exemptConsumerIds: string[] = []): AlertingAuthorization {
const { securityPluginSetup, securityPluginStart, features } = this;
return new AlertingAuthorization({
authorization: securityPluginStart?.authz,
request,
getSpace: this.getSpace,
alertTypeRegistry: this.alertTypeRegistry,
features: features!,
auditLogger: new AlertingAuthorizationAuditLogger(
securityPluginSetup?.audit.getLogger(ALERTS_FEATURE_ID)
),
exemptConsumerIds,
});
}
}
Loading

0 comments on commit 8049d95

Please sign in to comment.