Skip to content

Commit

Permalink
Disable alerting APIs when ESO plugin is using an ephemeral encryptio…
Browse files Browse the repository at this point in the history
…n key
  • Loading branch information
mikecote committed Feb 3, 2020
1 parent e28e149 commit 4b511a8
Show file tree
Hide file tree
Showing 5 changed files with 196 additions and 2 deletions.
147 changes: 147 additions & 0 deletions x-pack/legacy/plugins/alerting/server/alerts_client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const alertsClientParams = {
invalidateAPIKey: jest.fn(),
logger: loggingServiceMock.create().get(),
encryptedSavedObjectsPlugin: encryptedSavedObjects,
isESOUsingEphemeralEncryptionKey: false,
};

beforeEach(() => {
Expand Down Expand Up @@ -845,6 +846,17 @@ describe('create()', () => {
}
);
});

test('throws error if ESO is using an ephemeral encryption key', async () => {
const data = getMockData();
const alertsClient = new AlertsClient({
...alertsClientParams,
isESOUsingEphemeralEncryptionKey: true,
});
await expect(alertsClient.create({ data })).rejects.toThrowErrorMatchingInlineSnapshot(
`"Unable to perform action due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml"`
);
});
});

describe('enable()', () => {
Expand Down Expand Up @@ -1026,6 +1038,16 @@ describe('enable()', () => {
'Failed to invalidate API Key: Fail'
);
});

test('throws error if ESO is using an ephemeral encryption key', async () => {
const alertsClient = new AlertsClient({
...alertsClientParams,
isESOUsingEphemeralEncryptionKey: true,
});
await expect(alertsClient.enable({ id: '1' })).rejects.toThrowErrorMatchingInlineSnapshot(
`"Unable to perform action due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml"`
);
});
});

describe('disable()', () => {
Expand Down Expand Up @@ -1082,6 +1104,16 @@ describe('disable()', () => {
expect(savedObjectsClient.update).toHaveBeenCalledTimes(0);
expect(taskManager.remove).toHaveBeenCalledTimes(0);
});

test('throws error if ESO is using an ephemeral encryption key', async () => {
const alertsClient = new AlertsClient({
...alertsClientParams,
isESOUsingEphemeralEncryptionKey: true,
});
await expect(alertsClient.disable({ id: '1' })).rejects.toThrowErrorMatchingInlineSnapshot(
`"Unable to perform action due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml"`
);
});
});

describe('muteAll()', () => {
Expand All @@ -1103,6 +1135,16 @@ describe('muteAll()', () => {
updatedBy: 'elastic',
});
});

test('throws error if ESO is using an ephemeral encryption key', async () => {
const alertsClient = new AlertsClient({
...alertsClientParams,
isESOUsingEphemeralEncryptionKey: true,
});
await expect(alertsClient.muteAll({ id: '1' })).rejects.toThrowErrorMatchingInlineSnapshot(
`"Unable to perform action due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml"`
);
});
});

describe('unmuteAll()', () => {
Expand All @@ -1124,6 +1166,16 @@ describe('unmuteAll()', () => {
updatedBy: 'elastic',
});
});

test('throws error if ESO is using an ephemeral encryption key', async () => {
const alertsClient = new AlertsClient({
...alertsClientParams,
isESOUsingEphemeralEncryptionKey: true,
});
await expect(alertsClient.unmuteAll({ id: '1' })).rejects.toThrowErrorMatchingInlineSnapshot(
`"Unable to perform action due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml"`
);
});
});

describe('muteInstance()', () => {
Expand Down Expand Up @@ -1193,6 +1245,18 @@ describe('muteInstance()', () => {
await alertsClient.muteInstance({ alertId: '1', alertInstanceId: '2' });
expect(savedObjectsClient.update).not.toHaveBeenCalled();
});

test('throws error if ESO is using an ephemeral encryption key', async () => {
const alertsClient = new AlertsClient({
...alertsClientParams,
isESOUsingEphemeralEncryptionKey: true,
});
await expect(
alertsClient.muteInstance({ alertId: '1', alertInstanceId: '2' })
).rejects.toThrowErrorMatchingInlineSnapshot(
`"Unable to perform action due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml"`
);
});
});

describe('unmuteInstance()', () => {
Expand Down Expand Up @@ -1262,6 +1326,18 @@ describe('unmuteInstance()', () => {
await alertsClient.unmuteInstance({ alertId: '1', alertInstanceId: '2' });
expect(savedObjectsClient.update).not.toHaveBeenCalled();
});

test('throws error if ESO is using an ephemeral encryption key', async () => {
const alertsClient = new AlertsClient({
...alertsClientParams,
isESOUsingEphemeralEncryptionKey: true,
});
await expect(
alertsClient.unmuteInstance({ alertId: '1', alertInstanceId: '2' })
).rejects.toThrowErrorMatchingInlineSnapshot(
`"Unable to perform action due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml"`
);
});
});

describe('get()', () => {
Expand Down Expand Up @@ -1354,6 +1430,16 @@ describe('get()', () => {
`"Reference action_0 not found"`
);
});

test('throws error if ESO is using an ephemeral encryption key', async () => {
const alertsClient = new AlertsClient({
...alertsClientParams,
isESOUsingEphemeralEncryptionKey: true,
});
await expect(alertsClient.get({ id: '1' })).rejects.toThrowErrorMatchingInlineSnapshot(
`"Unable to perform action due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml"`
);
});
});

describe('find()', () => {
Expand Down Expand Up @@ -1433,6 +1519,16 @@ describe('find()', () => {
]
`);
});

test('throws error if ESO is using an ephemeral encryption key', async () => {
const alertsClient = new AlertsClient({
...alertsClientParams,
isESOUsingEphemeralEncryptionKey: true,
});
await expect(alertsClient.find()).rejects.toThrowErrorMatchingInlineSnapshot(
`"Unable to perform action due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml"`
);
});
});

describe('delete()', () => {
Expand Down Expand Up @@ -1527,6 +1623,16 @@ describe('delete()', () => {
'Failed to invalidate API Key: Fail'
);
});

test('throws error if ESO is using an ephemeral encryption key', async () => {
const alertsClient = new AlertsClient({
...alertsClientParams,
isESOUsingEphemeralEncryptionKey: true,
});
await expect(alertsClient.delete({ id: '1' })).rejects.toThrowErrorMatchingInlineSnapshot(
`"Unable to perform action due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml"`
);
});
});

describe('update()', () => {
Expand Down Expand Up @@ -2163,6 +2269,37 @@ describe('update()', () => {
);
});

test('throws error if ESO is using an ephemeral encryption key', async () => {
const alertsClient = new AlertsClient({
...alertsClientParams,
isESOUsingEphemeralEncryptionKey: true,
});
await expect(
alertsClient.update({
id: '1',
data: {
schedule: { interval: '10s' },
name: 'abc',
tags: ['foo'],
params: {
bar: true,
},
actions: [
{
group: 'default',
id: '1',
params: {
foo: true,
},
},
],
},
})
).rejects.toThrowErrorMatchingInlineSnapshot(
`"Unable to perform action due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml"`
);
});

describe('updating an alert schedule', () => {
function mockApiCalls(
alertId: string,
Expand Down Expand Up @@ -2446,4 +2583,14 @@ describe('updateApiKey()', () => {
'Failed to invalidate API Key: Fail'
);
});

test('throws error if ESO is using an ephemeral encryption key', async () => {
const alertsClient = new AlertsClient({
...alertsClientParams,
isESOUsingEphemeralEncryptionKey: true,
});
await expect(alertsClient.updateApiKey({ id: '1' })).rejects.toThrowErrorMatchingInlineSnapshot(
`"Unable to perform action due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml"`
);
});
});
41 changes: 40 additions & 1 deletion x-pack/legacy/plugins/alerting/server/alerts_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ interface ConstructorOptions {
savedObjectsClient: SavedObjectsClientContract;
alertTypeRegistry: AlertTypeRegistry;
encryptedSavedObjectsPlugin: EncryptedSavedObjectsPluginStart;
isESOUsingEphemeralEncryptionKey: boolean;
spaceId?: string;
namespace?: string;
getUserName: () => Promise<string | null>;
Expand Down Expand Up @@ -120,7 +121,8 @@ export class AlertsClient {
private readonly invalidateAPIKey: (
params: InvalidateAPIKeyParams
) => Promise<InvalidateAPIKeyResult>;
encryptedSavedObjectsPlugin: EncryptedSavedObjectsPluginStart;
private readonly encryptedSavedObjectsPlugin: EncryptedSavedObjectsPluginStart;
private readonly isESOUsingEphemeralEncryptionKey: boolean;

constructor({
alertTypeRegistry,
Expand All @@ -133,6 +135,7 @@ export class AlertsClient {
createAPIKey,
invalidateAPIKey,
encryptedSavedObjectsPlugin,
isESOUsingEphemeralEncryptionKey,
}: ConstructorOptions) {
this.logger = logger;
this.getUserName = getUserName;
Expand All @@ -144,9 +147,12 @@ export class AlertsClient {
this.createAPIKey = createAPIKey;
this.invalidateAPIKey = invalidateAPIKey;
this.encryptedSavedObjectsPlugin = encryptedSavedObjectsPlugin;
this.isESOUsingEphemeralEncryptionKey = isESOUsingEphemeralEncryptionKey;
}

public async create({ data, options }: CreateOptions): Promise<Alert> {
this.throwIfESOUsingEphemeralEncryptionKey();

// Throws an error if alert type isn't registered
const alertType = this.alertTypeRegistry.get(data.alertTypeId);
const validatedAlertTypeParams = validateAlertTypeParams(alertType, data.params);
Expand Down Expand Up @@ -200,11 +206,15 @@ export class AlertsClient {
}

public async get({ id }: { id: string }): Promise<SanitizedAlert> {
this.throwIfESOUsingEphemeralEncryptionKey();

const result = await this.savedObjectsClient.get('alert', id);
return this.getAlertFromRaw(result.id, result.attributes, result.updated_at, result.references);
}

public async find({ options = {} }: FindOptions = {}): Promise<FindResult> {
this.throwIfESOUsingEphemeralEncryptionKey();

const {
page,
per_page: perPage,
Expand All @@ -226,6 +236,8 @@ export class AlertsClient {
}

public async delete({ id }: { id: string }) {
this.throwIfESOUsingEphemeralEncryptionKey();

const decryptedAlertSavedObject = await this.encryptedSavedObjectsPlugin.getDecryptedAsInternalUser<
RawAlert
>('alert', id, { namespace: this.namespace });
Expand All @@ -238,6 +250,8 @@ export class AlertsClient {
}

public async update({ id, data }: UpdateOptions): Promise<PartialAlert> {
this.throwIfESOUsingEphemeralEncryptionKey();

const decryptedAlertSavedObject = await this.encryptedSavedObjectsPlugin.getDecryptedAsInternalUser<
RawAlert
>('alert', id, { namespace: this.namespace });
Expand Down Expand Up @@ -314,6 +328,8 @@ export class AlertsClient {
}

public async updateApiKey({ id }: { id: string }) {
this.throwIfESOUsingEphemeralEncryptionKey();

const {
version,
attributes,
Expand Down Expand Up @@ -355,6 +371,8 @@ export class AlertsClient {
}

public async enable({ id }: { id: string }) {
this.throwIfESOUsingEphemeralEncryptionKey();

const {
version,
attributes,
Expand Down Expand Up @@ -382,6 +400,8 @@ export class AlertsClient {
}

public async disable({ id }: { id: string }) {
this.throwIfESOUsingEphemeralEncryptionKey();

const { attributes, version } = await this.savedObjectsClient.get('alert', id);
if (attributes.enabled === true) {
await this.savedObjectsClient.update(
Expand All @@ -402,6 +422,8 @@ export class AlertsClient {
}

public async muteAll({ id }: { id: string }) {
this.throwIfESOUsingEphemeralEncryptionKey();

await this.savedObjectsClient.update('alert', id, {
muteAll: true,
mutedInstanceIds: [],
Expand All @@ -410,6 +432,8 @@ export class AlertsClient {
}

public async unmuteAll({ id }: { id: string }) {
this.throwIfESOUsingEphemeralEncryptionKey();

await this.savedObjectsClient.update('alert', id, {
muteAll: false,
mutedInstanceIds: [],
Expand All @@ -424,6 +448,8 @@ export class AlertsClient {
alertId: string;
alertInstanceId: string;
}) {
this.throwIfESOUsingEphemeralEncryptionKey();

const { attributes, version } = await this.savedObjectsClient.get('alert', alertId);
const mutedInstanceIds = attributes.mutedInstanceIds || [];
if (!attributes.muteAll && !mutedInstanceIds.includes(alertInstanceId)) {
Expand All @@ -447,6 +473,8 @@ export class AlertsClient {
alertId: string;
alertInstanceId: string;
}) {
this.throwIfESOUsingEphemeralEncryptionKey();

const { attributes, version } = await this.savedObjectsClient.get('alert', alertId);
const mutedInstanceIds = attributes.mutedInstanceIds || [];
if (!attributes.muteAll && mutedInstanceIds.includes(alertInstanceId)) {
Expand Down Expand Up @@ -545,6 +573,17 @@ export class AlertsClient {
}
}

/**
* Function to prevent using alerts when the encryption key changes on Kibana restart.
*/
private throwIfESOUsingEphemeralEncryptionKey() {
if (this.isESOUsingEphemeralEncryptionKey) {
throw new Error(
`Unable to perform action due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml`
);
}
}

private async denormalizeActions(
alertActions: NormalizedAlertAction[]
): Promise<{ actions: RawAlert['actions']; references: SavedObjectReference[] }> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const alertsClientFactoryParams: jest.Mocked<ConstructorOpts> = {
getSpaceId: jest.fn(),
spaceIdToNamespace: jest.fn(),
encryptedSavedObjectsPlugin: encryptedSavedObjectsMock.createStart(),
isESOUsingEphemeralEncryptionKey: false,
};
const fakeRequest: Request = {
headers: {},
Expand Down
Loading

0 comments on commit 4b511a8

Please sign in to comment.