Skip to content

Commit ca1976b

Browse files
Invalidate alert API Key when generating a new one (#53732)
* Initial work to auto cleanup old API keys * Fix ESLint error * Rename confusing variables * Add test to ensure thrown errors are swallowed * Add more tests Co-authored-by: Elastic Machine <[email protected]>
1 parent 851f37d commit ca1976b

File tree

6 files changed

+352
-48
lines changed

6 files changed

+352
-48
lines changed

x-pack/legacy/plugins/alerting/server/alerts_client.test.ts

Lines changed: 214 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,33 @@ import { alertTypeRegistryMock } from './alert_type_registry.mock';
1212
import { TaskStatus } from '../../task_manager/server';
1313
import { IntervalSchedule } from './types';
1414
import { resolvable } from './test_utils';
15+
import { encryptedSavedObjectsMock } from '../../../../plugins/encrypted_saved_objects/server/mocks';
1516

1617
const taskManager = taskManagerMock.create();
1718
const alertTypeRegistry = alertTypeRegistryMock.create();
1819
const savedObjectsClient = savedObjectsClientMock.create();
20+
const encryptedSavedObjects = encryptedSavedObjectsMock.createStart();
1921

2022
const alertsClientParams = {
2123
taskManager,
2224
alertTypeRegistry,
2325
savedObjectsClient,
2426
spaceId: 'default',
27+
namespace: 'default',
2528
getUserName: jest.fn(),
2629
createAPIKey: jest.fn(),
30+
invalidateAPIKey: jest.fn(),
2731
logger: loggingServiceMock.create().get(),
32+
encryptedSavedObjectsPlugin: encryptedSavedObjects,
2833
};
2934

3035
beforeEach(() => {
3136
jest.resetAllMocks();
32-
alertsClientParams.createAPIKey.mockResolvedValue({ created: false });
37+
alertsClientParams.createAPIKey.mockResolvedValue({ apiKeysEnabled: false });
38+
alertsClientParams.invalidateAPIKey.mockResolvedValue({
39+
apiKeysEnabled: true,
40+
result: { error_count: 0 },
41+
});
3342
alertsClientParams.getUserName.mockResolvedValue('elastic');
3443
taskManager.runNow.mockResolvedValue({ id: '' });
3544
});
@@ -725,7 +734,7 @@ describe('create()', () => {
725734
async executor() {},
726735
});
727736
alertsClientParams.createAPIKey.mockResolvedValueOnce({
728-
created: true,
737+
apiKeysEnabled: true,
729738
result: { id: '123', api_key: 'abc' },
730739
});
731740
savedObjectsClient.bulkGet.mockResolvedValueOnce({
@@ -841,7 +850,7 @@ describe('create()', () => {
841850
describe('enable()', () => {
842851
test('enables an alert', async () => {
843852
const alertsClient = new AlertsClient(alertsClientParams);
844-
savedObjectsClient.get.mockResolvedValueOnce({
853+
encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValueOnce({
845854
id: '1',
846855
type: 'alert',
847856
attributes: {
@@ -900,7 +909,7 @@ describe('enable()', () => {
900909

901910
test(`doesn't enable already enabled alerts`, async () => {
902911
const alertsClient = new AlertsClient(alertsClientParams);
903-
savedObjectsClient.get.mockResolvedValueOnce({
912+
encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValueOnce({
904913
id: '1',
905914
type: 'alert',
906915
attributes: {
@@ -918,7 +927,7 @@ describe('enable()', () => {
918927

919928
test('calls the API key function', async () => {
920929
const alertsClient = new AlertsClient(alertsClientParams);
921-
savedObjectsClient.get.mockResolvedValueOnce({
930+
encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValueOnce({
922931
id: '1',
923932
type: 'alert',
924933
attributes: {
@@ -943,7 +952,7 @@ describe('enable()', () => {
943952
ownerId: null,
944953
});
945954
alertsClientParams.createAPIKey.mockResolvedValueOnce({
946-
created: true,
955+
apiKeysEnabled: true,
947956
result: { id: '123', api_key: 'abc' },
948957
});
949958

@@ -978,6 +987,41 @@ describe('enable()', () => {
978987
scope: ['alerting'],
979988
});
980989
});
990+
991+
test('swallows error when invalidate API key throws', async () => {
992+
const alertsClient = new AlertsClient(alertsClientParams);
993+
alertsClientParams.invalidateAPIKey.mockRejectedValueOnce(new Error('Fail'));
994+
encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValueOnce({
995+
id: '1',
996+
type: 'alert',
997+
attributes: {
998+
schedule: { interval: '10s' },
999+
alertTypeId: '2',
1000+
enabled: false,
1001+
apiKey: Buffer.from('123:abc').toString('base64'),
1002+
},
1003+
version: '123',
1004+
references: [],
1005+
});
1006+
taskManager.schedule.mockResolvedValueOnce({
1007+
id: 'task-123',
1008+
scheduledAt: new Date(),
1009+
attempts: 0,
1010+
status: TaskStatus.Idle,
1011+
runAt: new Date(),
1012+
state: {},
1013+
params: {},
1014+
taskType: '',
1015+
startedAt: null,
1016+
retryAt: null,
1017+
ownerId: null,
1018+
});
1019+
1020+
await alertsClient.enable({ id: '1' });
1021+
expect(alertsClientParams.logger.error).toHaveBeenCalledWith(
1022+
'Failed to invalidate API Key: Fail'
1023+
);
1024+
});
9811025
});
9821026

9831027
describe('disable()', () => {
@@ -1390,7 +1434,7 @@ describe('find()', () => {
13901434
describe('delete()', () => {
13911435
test('successfully removes an alert', async () => {
13921436
const alertsClient = new AlertsClient(alertsClientParams);
1393-
savedObjectsClient.get.mockResolvedValueOnce({
1437+
encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValueOnce({
13941438
id: '1',
13951439
type: 'alert',
13961440
attributes: {
@@ -1437,6 +1481,48 @@ describe('delete()', () => {
14371481
]
14381482
`);
14391483
});
1484+
1485+
test('swallows error when invalidate API key throws', async () => {
1486+
const alertsClient = new AlertsClient(alertsClientParams);
1487+
alertsClientParams.invalidateAPIKey.mockRejectedValueOnce(new Error('Fail'));
1488+
encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValueOnce({
1489+
id: '1',
1490+
type: 'alert',
1491+
attributes: {
1492+
alertTypeId: '123',
1493+
schedule: { interval: '10s' },
1494+
params: {
1495+
bar: true,
1496+
},
1497+
apiKey: Buffer.from('123:abc').toString('base64'),
1498+
scheduledTaskId: 'task-123',
1499+
actions: [
1500+
{
1501+
group: 'default',
1502+
actionRef: 'action_0',
1503+
params: {
1504+
foo: true,
1505+
},
1506+
},
1507+
],
1508+
},
1509+
references: [
1510+
{
1511+
name: 'action_0',
1512+
type: 'action',
1513+
id: '1',
1514+
},
1515+
],
1516+
});
1517+
savedObjectsClient.delete.mockResolvedValueOnce({
1518+
success: true,
1519+
});
1520+
1521+
await alertsClient.delete({ id: '1' });
1522+
expect(alertsClientParams.logger.error).toHaveBeenCalledWith(
1523+
'Failed to invalidate API Key: Fail'
1524+
);
1525+
});
14401526
});
14411527

14421528
describe('update()', () => {
@@ -1448,7 +1534,7 @@ describe('update()', () => {
14481534
actionGroups: ['default'],
14491535
async executor() {},
14501536
});
1451-
savedObjectsClient.get.mockResolvedValueOnce({
1537+
encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValueOnce({
14521538
id: '1',
14531539
type: 'alert',
14541540
attributes: {
@@ -1603,7 +1689,7 @@ describe('update()', () => {
16031689
actionGroups: ['default'],
16041690
async executor() {},
16051691
});
1606-
savedObjectsClient.get.mockResolvedValueOnce({
1692+
encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValueOnce({
16071693
id: '1',
16081694
type: 'alert',
16091695
attributes: {
@@ -1786,7 +1872,7 @@ describe('update()', () => {
17861872
actionGroups: ['default'],
17871873
async executor() {},
17881874
});
1789-
savedObjectsClient.get.mockResolvedValueOnce({
1875+
encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValueOnce({
17901876
id: '1',
17911877
type: 'alert',
17921878
attributes: {
@@ -1810,7 +1896,7 @@ describe('update()', () => {
18101896
],
18111897
});
18121898
alertsClientParams.createAPIKey.mockResolvedValueOnce({
1813-
created: true,
1899+
apiKeysEnabled: true,
18141900
result: { id: '123', api_key: 'abc' },
18151901
});
18161902
savedObjectsClient.update.mockResolvedValueOnce({
@@ -1952,7 +2038,7 @@ describe('update()', () => {
19522038
},
19532039
async executor() {},
19542040
});
1955-
savedObjectsClient.get.mockResolvedValueOnce({
2041+
encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValueOnce({
19562042
id: '1',
19572043
type: 'alert',
19582044
attributes: {
@@ -1986,6 +2072,93 @@ describe('update()', () => {
19862072
);
19872073
});
19882074

2075+
it('swallows error when invalidate API key throws', async () => {
2076+
const alertsClient = new AlertsClient(alertsClientParams);
2077+
alertsClientParams.invalidateAPIKey.mockRejectedValueOnce(new Error('Fail'));
2078+
alertTypeRegistry.get.mockReturnValueOnce({
2079+
id: '123',
2080+
name: 'Test',
2081+
actionGroups: ['default'],
2082+
async executor() {},
2083+
});
2084+
encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValueOnce({
2085+
id: '1',
2086+
type: 'alert',
2087+
attributes: {
2088+
enabled: true,
2089+
alertTypeId: '123',
2090+
scheduledTaskId: 'task-123',
2091+
apiKey: Buffer.from('123:abc').toString('base64'),
2092+
},
2093+
references: [],
2094+
version: '123',
2095+
});
2096+
savedObjectsClient.bulkGet.mockResolvedValueOnce({
2097+
saved_objects: [
2098+
{
2099+
id: '1',
2100+
type: 'action',
2101+
attributes: {
2102+
actionTypeId: 'test',
2103+
},
2104+
references: [],
2105+
},
2106+
],
2107+
});
2108+
savedObjectsClient.update.mockResolvedValueOnce({
2109+
id: '1',
2110+
type: 'alert',
2111+
attributes: {
2112+
enabled: true,
2113+
schedule: { interval: '10s' },
2114+
params: {
2115+
bar: true,
2116+
},
2117+
actions: [
2118+
{
2119+
group: 'default',
2120+
actionRef: 'action_0',
2121+
actionTypeId: 'test',
2122+
params: {
2123+
foo: true,
2124+
},
2125+
},
2126+
],
2127+
scheduledTaskId: 'task-123',
2128+
},
2129+
references: [
2130+
{
2131+
name: 'action_0',
2132+
type: 'action',
2133+
id: '1',
2134+
},
2135+
],
2136+
});
2137+
await alertsClient.update({
2138+
id: '1',
2139+
data: {
2140+
schedule: { interval: '10s' },
2141+
name: 'abc',
2142+
tags: ['foo'],
2143+
params: {
2144+
bar: true,
2145+
},
2146+
actions: [
2147+
{
2148+
group: 'default',
2149+
id: '1',
2150+
params: {
2151+
foo: true,
2152+
},
2153+
},
2154+
],
2155+
},
2156+
});
2157+
expect(alertsClientParams.logger.error).toHaveBeenCalledWith(
2158+
'Failed to invalidate API Key: Fail'
2159+
);
2160+
});
2161+
19892162
describe('updating an alert schedule', () => {
19902163
function mockApiCalls(
19912164
alertId: string,
@@ -2012,7 +2185,7 @@ describe('update()', () => {
20122185
},
20132186
],
20142187
});
2015-
savedObjectsClient.get.mockResolvedValueOnce({
2188+
encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValueOnce({
20162189
id: alertId,
20172190
type: 'alert',
20182191
attributes: {
@@ -2212,7 +2385,7 @@ describe('update()', () => {
22122385
describe('updateApiKey()', () => {
22132386
test('updates the API key for the alert', async () => {
22142387
const alertsClient = new AlertsClient(alertsClientParams);
2215-
savedObjectsClient.get.mockResolvedValueOnce({
2388+
encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValueOnce({
22162389
id: '1',
22172390
type: 'alert',
22182391
attributes: {
@@ -2224,7 +2397,7 @@ describe('updateApiKey()', () => {
22242397
references: [],
22252398
});
22262399
alertsClientParams.createAPIKey.mockResolvedValueOnce({
2227-
created: true,
2400+
apiKeysEnabled: true,
22282401
result: { id: '123', api_key: 'abc' },
22292402
});
22302403

@@ -2243,4 +2416,30 @@ describe('updateApiKey()', () => {
22432416
{ version: '123' }
22442417
);
22452418
});
2419+
2420+
test('swallows error when invalidate API key throws', async () => {
2421+
const alertsClient = new AlertsClient(alertsClientParams);
2422+
alertsClientParams.invalidateAPIKey.mockRejectedValue(new Error('Fail'));
2423+
encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValueOnce({
2424+
id: '1',
2425+
type: 'alert',
2426+
attributes: {
2427+
schedule: { interval: '10s' },
2428+
alertTypeId: '2',
2429+
enabled: true,
2430+
apiKey: Buffer.from('123:abc').toString('base64'),
2431+
},
2432+
version: '123',
2433+
references: [],
2434+
});
2435+
alertsClientParams.createAPIKey.mockResolvedValueOnce({
2436+
apiKeysEnabled: true,
2437+
result: { id: '123', api_key: 'abc' },
2438+
});
2439+
2440+
await alertsClient.updateApiKey({ id: '1' });
2441+
expect(alertsClientParams.logger.error).toHaveBeenCalledWith(
2442+
'Failed to invalidate API Key: Fail'
2443+
);
2444+
});
22462445
});

0 commit comments

Comments
 (0)