mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 18:51:07 -04:00
[Response Ops][Alerting] Add auth check to triggers_actions_ui config API (#158205)
This commit is contained in:
parent
2a97425f58
commit
3f3590ea15
18 changed files with 159 additions and 61 deletions
|
@ -709,7 +709,7 @@ When a user is granted the `all` role in the Alerting Framework, they will be ab
|
||||||
|
|
||||||
Finally, all users, whether they're granted any role or not, are privileged to call the following:
|
Finally, all users, whether they're granted any role or not, are privileged to call the following:
|
||||||
|
|
||||||
- `listAlertTypes`, but the output is limited to displaying the rule types the user is privileged to `get`.
|
- `listRuleTypes`, but the output is limited to displaying the rule types the user is privileged to `get`.
|
||||||
|
|
||||||
Attempting to execute any operation the user isn't privileged to execute will result in an Authorization error thrown by the RulesClient.
|
Attempting to execute any operation the user isn't privileged to execute will result in an Authorization error thrown by the RulesClient.
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ beforeEach(() => {
|
||||||
|
|
||||||
describe('healthRoute', () => {
|
describe('healthRoute', () => {
|
||||||
it('registers the route', async () => {
|
it('registers the route', async () => {
|
||||||
rulesClient.listAlertTypes.mockResolvedValueOnce(new Set(ruleTypes));
|
rulesClient.listRuleTypes.mockResolvedValueOnce(new Set(ruleTypes));
|
||||||
const router = httpServiceMock.createRouter();
|
const router = httpServiceMock.createRouter();
|
||||||
|
|
||||||
const licenseState = licenseStateMock.create();
|
const licenseState = licenseStateMock.create();
|
||||||
|
@ -85,7 +85,7 @@ describe('healthRoute', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('queries the usage api', async () => {
|
it('queries the usage api', async () => {
|
||||||
rulesClient.listAlertTypes.mockResolvedValueOnce(new Set(ruleTypes));
|
rulesClient.listRuleTypes.mockResolvedValueOnce(new Set(ruleTypes));
|
||||||
const router = httpServiceMock.createRouter();
|
const router = httpServiceMock.createRouter();
|
||||||
|
|
||||||
const licenseState = licenseStateMock.create();
|
const licenseState = licenseStateMock.create();
|
||||||
|
@ -109,7 +109,7 @@ describe('healthRoute', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws error when user does not have any access to any rule types', async () => {
|
it('throws error when user does not have any access to any rule types', async () => {
|
||||||
rulesClient.listAlertTypes.mockResolvedValueOnce(new Set());
|
rulesClient.listRuleTypes.mockResolvedValueOnce(new Set());
|
||||||
const router = httpServiceMock.createRouter();
|
const router = httpServiceMock.createRouter();
|
||||||
|
|
||||||
const licenseState = licenseStateMock.create();
|
const licenseState = licenseStateMock.create();
|
||||||
|
@ -135,7 +135,7 @@ describe('healthRoute', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('evaluates whether Encrypted Saved Objects is missing encryption key', async () => {
|
it('evaluates whether Encrypted Saved Objects is missing encryption key', async () => {
|
||||||
rulesClient.listAlertTypes.mockResolvedValueOnce(new Set(ruleTypes));
|
rulesClient.listRuleTypes.mockResolvedValueOnce(new Set(ruleTypes));
|
||||||
const router = httpServiceMock.createRouter();
|
const router = httpServiceMock.createRouter();
|
||||||
|
|
||||||
const licenseState = licenseStateMock.create();
|
const licenseState = licenseStateMock.create();
|
||||||
|
@ -176,7 +176,7 @@ describe('healthRoute', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when ES security status cannot be determined from license state, isSufficientlySecure should return false', async () => {
|
test('when ES security status cannot be determined from license state, isSufficientlySecure should return false', async () => {
|
||||||
rulesClient.listAlertTypes.mockResolvedValueOnce(new Set(ruleTypes));
|
rulesClient.listRuleTypes.mockResolvedValueOnce(new Set(ruleTypes));
|
||||||
const router = httpServiceMock.createRouter();
|
const router = httpServiceMock.createRouter();
|
||||||
const licenseState = licenseStateMock.create();
|
const licenseState = licenseStateMock.create();
|
||||||
const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup({ canEncrypt: true });
|
const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup({ canEncrypt: true });
|
||||||
|
@ -218,7 +218,7 @@ describe('healthRoute', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when ES security is disabled, isSufficientlySecure should return true', async () => {
|
test('when ES security is disabled, isSufficientlySecure should return true', async () => {
|
||||||
rulesClient.listAlertTypes.mockResolvedValueOnce(new Set(ruleTypes));
|
rulesClient.listRuleTypes.mockResolvedValueOnce(new Set(ruleTypes));
|
||||||
const router = httpServiceMock.createRouter();
|
const router = httpServiceMock.createRouter();
|
||||||
const licenseState = licenseStateMock.create();
|
const licenseState = licenseStateMock.create();
|
||||||
const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup({ canEncrypt: true });
|
const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup({ canEncrypt: true });
|
||||||
|
@ -260,7 +260,7 @@ describe('healthRoute', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when ES security is enabled but user cannot generate api keys, isSufficientlySecure should return false', async () => {
|
test('when ES security is enabled but user cannot generate api keys, isSufficientlySecure should return false', async () => {
|
||||||
rulesClient.listAlertTypes.mockResolvedValueOnce(new Set(ruleTypes));
|
rulesClient.listRuleTypes.mockResolvedValueOnce(new Set(ruleTypes));
|
||||||
const router = httpServiceMock.createRouter();
|
const router = httpServiceMock.createRouter();
|
||||||
const licenseState = licenseStateMock.create();
|
const licenseState = licenseStateMock.create();
|
||||||
const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup({ canEncrypt: true });
|
const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup({ canEncrypt: true });
|
||||||
|
@ -302,7 +302,7 @@ describe('healthRoute', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when ES security is enabled and user can generate api keys, isSufficientlySecure should return true', async () => {
|
test('when ES security is enabled and user can generate api keys, isSufficientlySecure should return true', async () => {
|
||||||
rulesClient.listAlertTypes.mockResolvedValueOnce(new Set(ruleTypes));
|
rulesClient.listRuleTypes.mockResolvedValueOnce(new Set(ruleTypes));
|
||||||
const router = httpServiceMock.createRouter();
|
const router = httpServiceMock.createRouter();
|
||||||
const licenseState = licenseStateMock.create();
|
const licenseState = licenseStateMock.create();
|
||||||
const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup({ canEncrypt: true });
|
const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup({ canEncrypt: true });
|
||||||
|
|
|
@ -47,7 +47,7 @@ export const healthRoute = (
|
||||||
try {
|
try {
|
||||||
const alertingContext = await context.alerting;
|
const alertingContext = await context.alerting;
|
||||||
// Verify that user has access to at least one rule type
|
// Verify that user has access to at least one rule type
|
||||||
const ruleTypes = Array.from(await alertingContext.getRulesClient().listAlertTypes());
|
const ruleTypes = Array.from(await alertingContext.getRulesClient().listRuleTypes());
|
||||||
if (ruleTypes.length > 0) {
|
if (ruleTypes.length > 0) {
|
||||||
const alertingFrameworkHealth = await alertingContext.getFrameworkHealth();
|
const alertingFrameworkHealth = await alertingContext.getFrameworkHealth();
|
||||||
|
|
||||||
|
|
|
@ -76,7 +76,7 @@ beforeEach(() => {
|
||||||
|
|
||||||
describe('healthRoute', () => {
|
describe('healthRoute', () => {
|
||||||
it('registers the route', async () => {
|
it('registers the route', async () => {
|
||||||
rulesClient.listAlertTypes.mockResolvedValueOnce(new Set(ruleTypes));
|
rulesClient.listRuleTypes.mockResolvedValueOnce(new Set(ruleTypes));
|
||||||
const router = httpServiceMock.createRouter();
|
const router = httpServiceMock.createRouter();
|
||||||
|
|
||||||
const licenseState = licenseStateMock.create();
|
const licenseState = licenseStateMock.create();
|
||||||
|
@ -89,7 +89,7 @@ describe('healthRoute', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws error when user does not have any access to any rule types', async () => {
|
it('throws error when user does not have any access to any rule types', async () => {
|
||||||
rulesClient.listAlertTypes.mockResolvedValueOnce(new Set());
|
rulesClient.listRuleTypes.mockResolvedValueOnce(new Set());
|
||||||
const router = httpServiceMock.createRouter();
|
const router = httpServiceMock.createRouter();
|
||||||
|
|
||||||
const licenseState = licenseStateMock.create();
|
const licenseState = licenseStateMock.create();
|
||||||
|
@ -115,7 +115,7 @@ describe('healthRoute', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('evaluates whether Encrypted Saved Objects is missing encryption key', async () => {
|
it('evaluates whether Encrypted Saved Objects is missing encryption key', async () => {
|
||||||
rulesClient.listAlertTypes.mockResolvedValueOnce(new Set(ruleTypes));
|
rulesClient.listRuleTypes.mockResolvedValueOnce(new Set(ruleTypes));
|
||||||
const router = httpServiceMock.createRouter();
|
const router = httpServiceMock.createRouter();
|
||||||
|
|
||||||
const licenseState = licenseStateMock.create();
|
const licenseState = licenseStateMock.create();
|
||||||
|
@ -172,7 +172,7 @@ describe('healthRoute', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when ES security status cannot be determined from license state, isSufficientlySecure should return false', async () => {
|
test('when ES security status cannot be determined from license state, isSufficientlySecure should return false', async () => {
|
||||||
rulesClient.listAlertTypes.mockResolvedValueOnce(new Set(ruleTypes));
|
rulesClient.listRuleTypes.mockResolvedValueOnce(new Set(ruleTypes));
|
||||||
const router = httpServiceMock.createRouter();
|
const router = httpServiceMock.createRouter();
|
||||||
const licenseState = licenseStateMock.create();
|
const licenseState = licenseStateMock.create();
|
||||||
const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup({ canEncrypt: true });
|
const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup({ canEncrypt: true });
|
||||||
|
@ -230,7 +230,7 @@ describe('healthRoute', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when ES security is disabled, isSufficientlySecure should return true', async () => {
|
test('when ES security is disabled, isSufficientlySecure should return true', async () => {
|
||||||
rulesClient.listAlertTypes.mockResolvedValueOnce(new Set(ruleTypes));
|
rulesClient.listRuleTypes.mockResolvedValueOnce(new Set(ruleTypes));
|
||||||
const router = httpServiceMock.createRouter();
|
const router = httpServiceMock.createRouter();
|
||||||
const licenseState = licenseStateMock.create();
|
const licenseState = licenseStateMock.create();
|
||||||
const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup({ canEncrypt: true });
|
const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup({ canEncrypt: true });
|
||||||
|
@ -288,7 +288,7 @@ describe('healthRoute', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when ES security is enabled but user cannot generate api keys, isSufficientlySecure should return false', async () => {
|
test('when ES security is enabled but user cannot generate api keys, isSufficientlySecure should return false', async () => {
|
||||||
rulesClient.listAlertTypes.mockResolvedValueOnce(new Set(ruleTypes));
|
rulesClient.listRuleTypes.mockResolvedValueOnce(new Set(ruleTypes));
|
||||||
const router = httpServiceMock.createRouter();
|
const router = httpServiceMock.createRouter();
|
||||||
const licenseState = licenseStateMock.create();
|
const licenseState = licenseStateMock.create();
|
||||||
const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup({ canEncrypt: true });
|
const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup({ canEncrypt: true });
|
||||||
|
@ -346,7 +346,7 @@ describe('healthRoute', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when ES security is enabled and user can generate api keys, isSufficientlySecure should return true', async () => {
|
test('when ES security is enabled and user can generate api keys, isSufficientlySecure should return true', async () => {
|
||||||
rulesClient.listAlertTypes.mockResolvedValueOnce(new Set(ruleTypes));
|
rulesClient.listRuleTypes.mockResolvedValueOnce(new Set(ruleTypes));
|
||||||
const router = httpServiceMock.createRouter();
|
const router = httpServiceMock.createRouter();
|
||||||
const licenseState = licenseStateMock.create();
|
const licenseState = licenseStateMock.create();
|
||||||
const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup({ canEncrypt: true });
|
const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup({ canEncrypt: true });
|
||||||
|
@ -404,7 +404,7 @@ describe('healthRoute', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should track every call', async () => {
|
it('should track every call', async () => {
|
||||||
rulesClient.listAlertTypes.mockResolvedValueOnce(new Set(ruleTypes));
|
rulesClient.listRuleTypes.mockResolvedValueOnce(new Set(ruleTypes));
|
||||||
const licenseState = licenseStateMock.create();
|
const licenseState = licenseStateMock.create();
|
||||||
const router = httpServiceMock.createRouter();
|
const router = httpServiceMock.createRouter();
|
||||||
const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup({ canEncrypt: true });
|
const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup({ canEncrypt: true });
|
||||||
|
|
|
@ -34,7 +34,7 @@ export function healthRoute(
|
||||||
try {
|
try {
|
||||||
const alertingContext = await context.alerting;
|
const alertingContext = await context.alerting;
|
||||||
// Verify that user has access to at least one rule type
|
// Verify that user has access to at least one rule type
|
||||||
const ruleTypes = Array.from(await alertingContext.getRulesClient().listAlertTypes());
|
const ruleTypes = Array.from(await alertingContext.getRulesClient().listRuleTypes());
|
||||||
if (ruleTypes.length > 0) {
|
if (ruleTypes.length > 0) {
|
||||||
const alertingFrameworkHealth = await alertingContext.getFrameworkHealth();
|
const alertingFrameworkHealth = await alertingContext.getFrameworkHealth();
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ describe('listAlertTypesRoute', () => {
|
||||||
enabledInLicense: true,
|
enabledInLicense: true,
|
||||||
} as RegistryAlertTypeWithAuth,
|
} as RegistryAlertTypeWithAuth,
|
||||||
];
|
];
|
||||||
rulesClient.listAlertTypes.mockResolvedValueOnce(new Set(listTypes));
|
rulesClient.listRuleTypes.mockResolvedValueOnce(new Set(listTypes));
|
||||||
|
|
||||||
const [context, req, res] = mockHandlerArguments({ rulesClient }, {}, ['ok']);
|
const [context, req, res] = mockHandlerArguments({ rulesClient }, {}, ['ok']);
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ describe('listAlertTypesRoute', () => {
|
||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
expect(rulesClient.listAlertTypes).toHaveBeenCalledTimes(1);
|
expect(rulesClient.listRuleTypes).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
expect(res.ok).toHaveBeenCalledWith({
|
expect(res.ok).toHaveBeenCalledWith({
|
||||||
body: listTypes,
|
body: listTypes,
|
||||||
|
@ -140,7 +140,7 @@ describe('listAlertTypesRoute', () => {
|
||||||
} as RegistryAlertTypeWithAuth,
|
} as RegistryAlertTypeWithAuth,
|
||||||
];
|
];
|
||||||
|
|
||||||
rulesClient.listAlertTypes.mockResolvedValueOnce(new Set(listTypes));
|
rulesClient.listRuleTypes.mockResolvedValueOnce(new Set(listTypes));
|
||||||
|
|
||||||
const [context, req, res] = mockHandlerArguments(
|
const [context, req, res] = mockHandlerArguments(
|
||||||
{ rulesClient },
|
{ rulesClient },
|
||||||
|
@ -193,7 +193,7 @@ describe('listAlertTypesRoute', () => {
|
||||||
} as RegistryAlertTypeWithAuth,
|
} as RegistryAlertTypeWithAuth,
|
||||||
];
|
];
|
||||||
|
|
||||||
rulesClient.listAlertTypes.mockResolvedValueOnce(new Set(listTypes));
|
rulesClient.listRuleTypes.mockResolvedValueOnce(new Set(listTypes));
|
||||||
|
|
||||||
const [context, req, res] = mockHandlerArguments(
|
const [context, req, res] = mockHandlerArguments(
|
||||||
{ rulesClient },
|
{ rulesClient },
|
||||||
|
@ -214,7 +214,7 @@ describe('listAlertTypesRoute', () => {
|
||||||
const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract();
|
const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract();
|
||||||
const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test');
|
const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test');
|
||||||
|
|
||||||
rulesClient.listAlertTypes.mockResolvedValueOnce(new Set([]));
|
rulesClient.listRuleTypes.mockResolvedValueOnce(new Set([]));
|
||||||
|
|
||||||
listAlertTypesRoute(router, licenseState, mockUsageCounter);
|
listAlertTypesRoute(router, licenseState, mockUsageCounter);
|
||||||
const [, handler] = router.get.mock.calls[0];
|
const [, handler] = router.get.mock.calls[0];
|
||||||
|
|
|
@ -29,7 +29,7 @@ export const listAlertTypesRoute = (
|
||||||
trackLegacyRouteUsage('listAlertTypes', usageCounter);
|
trackLegacyRouteUsage('listAlertTypes', usageCounter);
|
||||||
const alertingContext = await context.alerting;
|
const alertingContext = await context.alerting;
|
||||||
return res.ok({
|
return res.ok({
|
||||||
body: Array.from(await alertingContext.getRulesClient().listAlertTypes()),
|
body: Array.from(await alertingContext.getRulesClient().listRuleTypes()),
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
|
@ -90,7 +90,7 @@ describe('ruleTypesRoute', () => {
|
||||||
has_get_summarized_alerts: true,
|
has_get_summarized_alerts: true,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
rulesClient.listAlertTypes.mockResolvedValueOnce(new Set(listTypes));
|
rulesClient.listRuleTypes.mockResolvedValueOnce(new Set(listTypes));
|
||||||
|
|
||||||
const [context, req, res] = mockHandlerArguments({ rulesClient }, {}, ['ok']);
|
const [context, req, res] = mockHandlerArguments({ rulesClient }, {}, ['ok']);
|
||||||
|
|
||||||
|
@ -129,7 +129,7 @@ describe('ruleTypesRoute', () => {
|
||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
expect(rulesClient.listAlertTypes).toHaveBeenCalledTimes(1);
|
expect(rulesClient.listRuleTypes).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
expect(res.ok).toHaveBeenCalledWith({
|
expect(res.ok).toHaveBeenCalledWith({
|
||||||
body: expectedResult,
|
body: expectedResult,
|
||||||
|
@ -170,7 +170,7 @@ describe('ruleTypesRoute', () => {
|
||||||
} as RegistryAlertTypeWithAuth,
|
} as RegistryAlertTypeWithAuth,
|
||||||
];
|
];
|
||||||
|
|
||||||
rulesClient.listAlertTypes.mockResolvedValueOnce(new Set(listTypes));
|
rulesClient.listRuleTypes.mockResolvedValueOnce(new Set(listTypes));
|
||||||
|
|
||||||
const [context, req, res] = mockHandlerArguments(
|
const [context, req, res] = mockHandlerArguments(
|
||||||
{ rulesClient },
|
{ rulesClient },
|
||||||
|
@ -223,7 +223,7 @@ describe('ruleTypesRoute', () => {
|
||||||
} as RegistryAlertTypeWithAuth,
|
} as RegistryAlertTypeWithAuth,
|
||||||
];
|
];
|
||||||
|
|
||||||
rulesClient.listAlertTypes.mockResolvedValueOnce(new Set(listTypes));
|
rulesClient.listRuleTypes.mockResolvedValueOnce(new Set(listTypes));
|
||||||
|
|
||||||
const [context, req, res] = mockHandlerArguments(
|
const [context, req, res] = mockHandlerArguments(
|
||||||
{ rulesClient },
|
{ rulesClient },
|
||||||
|
|
|
@ -57,7 +57,7 @@ export const ruleTypesRoute = (
|
||||||
router.handleLegacyErrors(
|
router.handleLegacyErrors(
|
||||||
verifyAccessAndContext(licenseState, async function (context, req, res) {
|
verifyAccessAndContext(licenseState, async function (context, req, res) {
|
||||||
const rulesClient = (await context.alerting).getRulesClient();
|
const rulesClient = (await context.alerting).getRulesClient();
|
||||||
const ruleTypes = Array.from(await rulesClient.listAlertTypes());
|
const ruleTypes = Array.from(await rulesClient.listRuleTypes());
|
||||||
return res.ok({
|
return res.ok({
|
||||||
body: rewriteBodyRes(ruleTypes),
|
body: rewriteBodyRes(ruleTypes),
|
||||||
});
|
});
|
||||||
|
|
|
@ -27,7 +27,7 @@ const createRulesClientMock = () => {
|
||||||
unmuteAll: jest.fn(),
|
unmuteAll: jest.fn(),
|
||||||
muteInstance: jest.fn(),
|
muteInstance: jest.fn(),
|
||||||
unmuteInstance: jest.fn(),
|
unmuteInstance: jest.fn(),
|
||||||
listAlertTypes: jest.fn(),
|
listRuleTypes: jest.fn(),
|
||||||
getAlertSummary: jest.fn(),
|
getAlertSummary: jest.fn(),
|
||||||
getExecutionLogForRule: jest.fn(),
|
getExecutionLogForRule: jest.fn(),
|
||||||
getRuleExecutionKPI: jest.fn(),
|
getRuleExecutionKPI: jest.fn(),
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
import { WriteOperations, ReadOperations, AlertingAuthorizationEntity } from '../../authorization';
|
import { WriteOperations, ReadOperations, AlertingAuthorizationEntity } from '../../authorization';
|
||||||
import { RulesClientContext } from '../types';
|
import { RulesClientContext } from '../types';
|
||||||
|
|
||||||
export async function listAlertTypes(context: RulesClientContext) {
|
export async function listRuleTypes(context: RulesClientContext) {
|
||||||
return await context.authorization.filterByRuleTypeAuthorization(
|
return await context.authorization.filterByRuleTypeAuthorization(
|
||||||
context.ruleTypeRegistry.list(),
|
context.ruleTypeRegistry.list(),
|
||||||
[ReadOperations.Get, WriteOperations.Create],
|
[ReadOperations.Get, WriteOperations.Create],
|
|
@ -51,7 +51,7 @@ import { unmuteAll } from './methods/unmute_all';
|
||||||
import { muteInstance } from './methods/mute_instance';
|
import { muteInstance } from './methods/mute_instance';
|
||||||
import { unmuteInstance } from './methods/unmute_instance';
|
import { unmuteInstance } from './methods/unmute_instance';
|
||||||
import { runSoon } from './methods/run_soon';
|
import { runSoon } from './methods/run_soon';
|
||||||
import { listAlertTypes } from './methods/list_alert_types';
|
import { listRuleTypes } from './methods/list_rule_types';
|
||||||
import { getAlertFromRaw, GetAlertFromRawParams } from './lib/get_alert_from_raw';
|
import { getAlertFromRaw, GetAlertFromRawParams } from './lib/get_alert_from_raw';
|
||||||
|
|
||||||
export type ConstructorOptions = Omit<
|
export type ConstructorOptions = Omit<
|
||||||
|
@ -157,7 +157,7 @@ export class RulesClient {
|
||||||
|
|
||||||
public runSoon = (options: { id: string }) => runSoon(this.context, options);
|
public runSoon = (options: { id: string }) => runSoon(this.context, options);
|
||||||
|
|
||||||
public listAlertTypes = () => listAlertTypes(this.context);
|
public listRuleTypes = () => listRuleTypes(this.context);
|
||||||
|
|
||||||
public getSpaceId(): string | undefined {
|
public getSpaceId(): string | undefined {
|
||||||
return this.context.spaceId;
|
return this.context.spaceId;
|
||||||
|
|
|
@ -54,7 +54,7 @@ beforeEach(() => {
|
||||||
getBeforeSetup(rulesClientParams, taskManager, ruleTypeRegistry);
|
getBeforeSetup(rulesClientParams, taskManager, ruleTypeRegistry);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('listAlertTypes', () => {
|
describe('listRuleTypes', () => {
|
||||||
let rulesClient: RulesClient;
|
let rulesClient: RulesClient;
|
||||||
const alertingAlertType: RegistryRuleType = {
|
const alertingAlertType: RegistryRuleType = {
|
||||||
actionGroups: [],
|
actionGroups: [],
|
||||||
|
@ -100,7 +100,7 @@ describe('listAlertTypes', () => {
|
||||||
{ ...alertingAlertType, authorizedConsumers },
|
{ ...alertingAlertType, authorizedConsumers },
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
expect(await rulesClient.listAlertTypes()).toEqual(
|
expect(await rulesClient.listRuleTypes()).toEqual(
|
||||||
new Set([
|
new Set([
|
||||||
{ ...myAppAlertType, authorizedConsumers },
|
{ ...myAppAlertType, authorizedConsumers },
|
||||||
{ ...alertingAlertType, authorizedConsumers },
|
{ ...alertingAlertType, authorizedConsumers },
|
||||||
|
@ -157,7 +157,7 @@ describe('listAlertTypes', () => {
|
||||||
]);
|
]);
|
||||||
authorization.filterByRuleTypeAuthorization.mockResolvedValue(authorizedTypes);
|
authorization.filterByRuleTypeAuthorization.mockResolvedValue(authorizedTypes);
|
||||||
|
|
||||||
expect(await rulesClient.listAlertTypes()).toEqual(authorizedTypes);
|
expect(await rulesClient.listRuleTypes()).toEqual(authorizedTypes);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
|
@ -6,7 +6,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Logger, Plugin, CoreSetup, PluginInitializerContext } from '@kbn/core/server';
|
import { Logger, Plugin, CoreSetup, PluginInitializerContext } from '@kbn/core/server';
|
||||||
import { PluginSetupContract as AlertingPluginSetup } from '@kbn/alerting-plugin/server';
|
import {
|
||||||
|
PluginSetupContract as AlertingPluginSetup,
|
||||||
|
PluginStartContract as AlertingPluginStart,
|
||||||
|
} from '@kbn/alerting-plugin/server';
|
||||||
import { EncryptedSavedObjectsPluginSetup } from '@kbn/encrypted-saved-objects-plugin/server';
|
import { EncryptedSavedObjectsPluginSetup } from '@kbn/encrypted-saved-objects-plugin/server';
|
||||||
import { getService, register as registerDataService } from './data';
|
import { getService, register as registerDataService } from './data';
|
||||||
import { createHealthRoute, createConfigRoute } from './routes';
|
import { createHealthRoute, createConfigRoute } from './routes';
|
||||||
|
@ -21,6 +24,10 @@ interface PluginsSetup {
|
||||||
alerting: AlertingPluginSetup;
|
alerting: AlertingPluginSetup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface TriggersActionsPluginStart {
|
||||||
|
alerting: AlertingPluginStart;
|
||||||
|
}
|
||||||
|
|
||||||
export class TriggersActionsPlugin implements Plugin<void, PluginStartContract> {
|
export class TriggersActionsPlugin implements Plugin<void, PluginStartContract> {
|
||||||
private readonly logger: Logger;
|
private readonly logger: Logger;
|
||||||
private readonly data: PluginStartContract['data'];
|
private readonly data: PluginStartContract['data'];
|
||||||
|
@ -30,7 +37,7 @@ export class TriggersActionsPlugin implements Plugin<void, PluginStartContract>
|
||||||
this.data = getService();
|
this.data = getService();
|
||||||
}
|
}
|
||||||
|
|
||||||
public setup(core: CoreSetup, plugins: PluginsSetup): void {
|
public setup(core: CoreSetup<TriggersActionsPluginStart>, plugins: PluginsSetup): void {
|
||||||
const router = core.http.createRouter();
|
const router = core.http.createRouter();
|
||||||
registerDataService({
|
registerDataService({
|
||||||
logger: this.logger,
|
logger: this.logger,
|
||||||
|
@ -45,12 +52,16 @@ export class TriggersActionsPlugin implements Plugin<void, PluginStartContract>
|
||||||
BASE_TRIGGERS_ACTIONS_UI_API_PATH,
|
BASE_TRIGGERS_ACTIONS_UI_API_PATH,
|
||||||
plugins.alerting !== undefined
|
plugins.alerting !== undefined
|
||||||
);
|
);
|
||||||
createConfigRoute(
|
|
||||||
this.logger,
|
core.getStartServices().then(([_, pluginStart]) => {
|
||||||
router,
|
createConfigRoute({
|
||||||
BASE_TRIGGERS_ACTIONS_UI_API_PATH,
|
logger: this.logger,
|
||||||
plugins.alerting.getConfig
|
router,
|
||||||
);
|
baseRoute: BASE_TRIGGERS_ACTIONS_UI_API_PATH,
|
||||||
|
alertingConfig: plugins.alerting.getConfig,
|
||||||
|
getRulesClientWithRequest: pluginStart.alerting.getRulesClientWithRequest,
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public start(): PluginStartContract {
|
public start(): PluginStartContract {
|
||||||
|
|
|
@ -6,16 +6,55 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { httpServiceMock, httpServerMock, loggingSystemMock } from '@kbn/core/server/mocks';
|
import { httpServiceMock, httpServerMock, loggingSystemMock } from '@kbn/core/server/mocks';
|
||||||
|
import { rulesClientMock } from '@kbn/alerting-plugin/server/rules_client.mock';
|
||||||
import { createConfigRoute } from './config';
|
import { createConfigRoute } from './config';
|
||||||
|
import { RecoveredActionGroup } from '@kbn/alerting-plugin/common';
|
||||||
|
import { RegistryAlertTypeWithAuth } from '@kbn/alerting-plugin/server/authorization';
|
||||||
|
|
||||||
|
const ruleTypes = [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
name: 'name',
|
||||||
|
actionGroups: [
|
||||||
|
{
|
||||||
|
id: 'default',
|
||||||
|
name: 'Default',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
defaultActionGroupId: 'default',
|
||||||
|
minimumLicenseRequired: 'basic',
|
||||||
|
isExportable: true,
|
||||||
|
ruleTaskTimeout: '10m',
|
||||||
|
recoveryActionGroup: RecoveredActionGroup,
|
||||||
|
authorizedConsumers: {},
|
||||||
|
actionVariables: {
|
||||||
|
context: [],
|
||||||
|
state: [],
|
||||||
|
},
|
||||||
|
producer: 'test',
|
||||||
|
enabledInLicense: true,
|
||||||
|
minimumScheduleInterval: '1m',
|
||||||
|
defaultScheduleInterval: '10m',
|
||||||
|
} as unknown as RegistryAlertTypeWithAuth,
|
||||||
|
];
|
||||||
|
|
||||||
describe('createConfigRoute', () => {
|
describe('createConfigRoute', () => {
|
||||||
it('registers the route', async () => {
|
it('registers the route and returns config if user is authorized', async () => {
|
||||||
const router = httpServiceMock.createRouter();
|
const router = httpServiceMock.createRouter();
|
||||||
const logger = loggingSystemMock.create().get();
|
const logger = loggingSystemMock.create().get();
|
||||||
createConfigRoute(logger, router, `/internal/triggers_actions_ui`, () => ({
|
const mockRulesClient = rulesClientMock.create();
|
||||||
isUsingSecurity: true,
|
|
||||||
minimumScheduleInterval: { value: '1m', enforce: false },
|
mockRulesClient.listRuleTypes.mockResolvedValueOnce(new Set(ruleTypes));
|
||||||
}));
|
createConfigRoute({
|
||||||
|
logger,
|
||||||
|
router,
|
||||||
|
baseRoute: `/internal/triggers_actions_ui`,
|
||||||
|
alertingConfig: () => ({
|
||||||
|
isUsingSecurity: true,
|
||||||
|
minimumScheduleInterval: { value: '1m', enforce: false },
|
||||||
|
}),
|
||||||
|
getRulesClientWithRequest: () => mockRulesClient,
|
||||||
|
});
|
||||||
|
|
||||||
const [config, handler] = router.get.mock.calls[0];
|
const [config, handler] = router.get.mock.calls[0];
|
||||||
expect(config.path).toMatchInlineSnapshot(`"/internal/triggers_actions_ui/_config"`);
|
expect(config.path).toMatchInlineSnapshot(`"/internal/triggers_actions_ui/_config"`);
|
||||||
|
@ -28,4 +67,33 @@ describe('createConfigRoute', () => {
|
||||||
body: { isUsingSecurity: true, minimumScheduleInterval: { value: '1m', enforce: false } },
|
body: { isUsingSecurity: true, minimumScheduleInterval: { value: '1m', enforce: false } },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('registers the route and returns forbidden error if user does not have access to any alerting rules ', async () => {
|
||||||
|
const router = httpServiceMock.createRouter();
|
||||||
|
const logger = loggingSystemMock.create().get();
|
||||||
|
const mockRulesClient = rulesClientMock.create();
|
||||||
|
|
||||||
|
mockRulesClient.listRuleTypes.mockResolvedValueOnce(new Set());
|
||||||
|
createConfigRoute({
|
||||||
|
logger,
|
||||||
|
router,
|
||||||
|
baseRoute: `/internal/triggers_actions_ui`,
|
||||||
|
alertingConfig: () => ({
|
||||||
|
isUsingSecurity: true,
|
||||||
|
minimumScheduleInterval: { value: '1m', enforce: false },
|
||||||
|
}),
|
||||||
|
getRulesClientWithRequest: () => mockRulesClient,
|
||||||
|
});
|
||||||
|
|
||||||
|
const [config, handler] = router.get.mock.calls[0];
|
||||||
|
expect(config.path).toMatchInlineSnapshot(`"/internal/triggers_actions_ui/_config"`);
|
||||||
|
|
||||||
|
const mockResponse = httpServerMock.createResponseFactory();
|
||||||
|
await handler({}, httpServerMock.createKibanaRequest(), mockResponse);
|
||||||
|
|
||||||
|
expect(mockResponse.forbidden).toBeCalled();
|
||||||
|
expect(mockResponse.forbidden.mock.calls[0][0]).toEqual({
|
||||||
|
body: { message: 'Unauthorized to access config' },
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,15 +14,25 @@ import {
|
||||||
} from '@kbn/core/server';
|
} from '@kbn/core/server';
|
||||||
import { Logger } from '@kbn/core/server';
|
import { Logger } from '@kbn/core/server';
|
||||||
import { AlertingRulesConfig } from '@kbn/alerting-plugin/server';
|
import { AlertingRulesConfig } from '@kbn/alerting-plugin/server';
|
||||||
|
import { RulesClientApi } from '@kbn/alerting-plugin/server/types';
|
||||||
|
|
||||||
export function createConfigRoute(
|
export interface ConfigRouteOpts {
|
||||||
logger: Logger,
|
logger: Logger;
|
||||||
router: IRouter,
|
router: IRouter;
|
||||||
baseRoute: string,
|
baseRoute: string;
|
||||||
// config is a function because "isUsingSecurity" is pulled from the license
|
// alertingConfig is a function because "isUsingSecurity" is pulled from the license
|
||||||
// state which gets populated after plugin setup().
|
// state which gets populated after plugin setup().
|
||||||
config: () => AlertingRulesConfig
|
alertingConfig: () => AlertingRulesConfig;
|
||||||
) {
|
getRulesClientWithRequest: (request: KibanaRequest) => RulesClientApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createConfigRoute({
|
||||||
|
logger,
|
||||||
|
router,
|
||||||
|
baseRoute,
|
||||||
|
alertingConfig,
|
||||||
|
getRulesClientWithRequest,
|
||||||
|
}: ConfigRouteOpts) {
|
||||||
const path = `${baseRoute}/_config`;
|
const path = `${baseRoute}/_config`;
|
||||||
logger.debug(`registering triggers_actions_ui config route GET ${path}`);
|
logger.debug(`registering triggers_actions_ui config route GET ${path}`);
|
||||||
router.get(
|
router.get(
|
||||||
|
@ -33,10 +43,19 @@ export function createConfigRoute(
|
||||||
handler
|
handler
|
||||||
);
|
);
|
||||||
async function handler(
|
async function handler(
|
||||||
ctx: RequestHandlerContext,
|
_: RequestHandlerContext,
|
||||||
req: KibanaRequest<unknown, unknown, unknown>,
|
req: KibanaRequest<unknown, unknown, unknown>,
|
||||||
res: KibanaResponseFactory
|
res: KibanaResponseFactory
|
||||||
): Promise<IKibanaResponse> {
|
): Promise<IKibanaResponse> {
|
||||||
return res.ok({ body: config() });
|
// Check that user has access to at least one rule type
|
||||||
|
const rulesClient = getRulesClientWithRequest(req);
|
||||||
|
const ruleTypes = Array.from(await rulesClient.listRuleTypes());
|
||||||
|
if (ruleTypes.length > 0) {
|
||||||
|
return res.ok({ body: alertingConfig() });
|
||||||
|
} else {
|
||||||
|
return res.forbidden({
|
||||||
|
body: { message: `Unauthorized to access config` },
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ import { getUrlPrefix } from '../../../../common/lib/space_test_utils';
|
||||||
import { FtrProviderContext } from '../../../../common/ftr_provider_context';
|
import { FtrProviderContext } from '../../../../common/ftr_provider_context';
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default function listAlertTypes({ getService }: FtrProviderContext) {
|
export default function listRuleTypes({ getService }: FtrProviderContext) {
|
||||||
const supertestWithoutAuth = getService('supertestWithoutAuth');
|
const supertestWithoutAuth = getService('supertestWithoutAuth');
|
||||||
|
|
||||||
const expectedNoOpType = {
|
const expectedNoOpType = {
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { getUrlPrefix } from '../../../../common/lib/space_test_utils';
|
||||||
import { FtrProviderContext } from '../../../../common/ftr_provider_context';
|
import { FtrProviderContext } from '../../../../common/ftr_provider_context';
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default function listAlertTypes({ getService }: FtrProviderContext) {
|
export default function listRuleTypes({ getService }: FtrProviderContext) {
|
||||||
const supertest = getService('supertest');
|
const supertest = getService('supertest');
|
||||||
|
|
||||||
describe('rule_types', () => {
|
describe('rule_types', () => {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue