Skip to content

Commit

Permalink
Pass params to system actions when getting kibana privileges
Browse files Browse the repository at this point in the history
  • Loading branch information
cnasikas committed Jul 9, 2023
1 parent 0020ee9 commit 356432c
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 13 deletions.
50 changes: 48 additions & 2 deletions x-pack/plugins/actions/server/action_type_registry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -699,7 +699,7 @@ describe('actionTypeRegistry', () => {
name: 'Cases',
minimumLicenseRequired: 'platinum',
supportedFeatureIds: ['alerting'],
kibanaPrivileges: ['test/create'],
getKibanaPrivileges: () => ['test/create'],
validate: {
config: { schema: schema.object({}) },
secrets: { schema: schema.object({}) },
Expand Down Expand Up @@ -754,15 +754,36 @@ describe('actionTypeRegistry', () => {
expect(result).toEqual([]);
});

it('should return an empty array if the action type is not a system action but registers kibana privileges', () => {
const registry = new ActionTypeRegistry(actionTypeRegistryParams);

registry.register({
id: 'foo',
name: 'Foo',
minimumLicenseRequired: 'basic',
supportedFeatureIds: ['alerting'],
validate: {
config: { schema: schema.object({}) },
secrets: { schema: schema.object({}) },
params: { schema: schema.object({}) },
},
executor,
});

const result = registry.getSystemActionKibanaPrivileges('foo');
expect(result).toEqual([]);
});

it('should return an empty array if the action type is not a system action but defines kibana privileges', () => {
const registry = new ActionTypeRegistry(actionTypeRegistryParams);
const getKibanaPrivileges = jest.fn().mockReturnValue(['test/create']);

registry.register({
id: 'foo',
name: 'Foo',
minimumLicenseRequired: 'basic',
supportedFeatureIds: ['alerting'],
kibanaPrivileges: ['test/create'],
getKibanaPrivileges,
validate: {
config: { schema: schema.object({}) },
secrets: { schema: schema.object({}) },
Expand All @@ -772,7 +793,32 @@ describe('actionTypeRegistry', () => {
});

const result = registry.getSystemActionKibanaPrivileges('foo');

expect(result).toEqual([]);
expect(getKibanaPrivileges).not.toHaveBeenCalled();
});

it('should pass the metadata correctly', () => {
const registry = new ActionTypeRegistry(actionTypeRegistryParams);
const getKibanaPrivileges = jest.fn().mockReturnValue(['test/create']);

registry.register({
id: '.cases',
name: 'Cases',
minimumLicenseRequired: 'platinum',
supportedFeatureIds: ['alerting'],
getKibanaPrivileges,
validate: {
config: { schema: schema.object({}) },
secrets: { schema: schema.object({}) },
params: { schema: schema.object({}) },
},
isSystemActionType: true,
executor,
});

registry.getSystemActionKibanaPrivileges('.cases', { foo: 'bar' });
expect(getKibanaPrivileges).toHaveBeenCalledWith({ metadata: { foo: 'bar' } });
});
});
});
7 changes: 5 additions & 2 deletions x-pack/plugins/actions/server/action_type_registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,14 +103,17 @@ export class ActionTypeRegistry {
/**
* Returns the kibana privileges of an action type
*/
public getSystemActionKibanaPrivileges(actionTypeId: string): string[] {
public getSystemActionKibanaPrivileges(
actionTypeId: string,
metadata?: Record<string, unknown>
): string[] {
const actionType = this.actionTypes.get(actionTypeId);

if (!actionType?.isSystemActionType) {
return [];
}

return actionType?.kibanaPrivileges ?? [];
return actionType?.getKibanaPrivileges?.({ metadata }) ?? [];
}

/**
Expand Down
70 changes: 68 additions & 2 deletions x-pack/plugins/actions/server/actions_client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2831,7 +2831,7 @@ describe('execute()', () => {
name: 'Cases',
minimumLicenseRequired: 'platinum',
supportedFeatureIds: ['alerting'],
kibanaPrivileges: ['test/create'],
getKibanaPrivileges: () => ['test/create'],
validate: {
config: { schema: schema.object({}) },
secrets: { schema: schema.object({}) },
Expand Down Expand Up @@ -2896,7 +2896,7 @@ describe('execute()', () => {
name: 'Cases',
minimumLicenseRequired: 'platinum',
supportedFeatureIds: ['alerting'],
kibanaPrivileges: ['test/create'],
getKibanaPrivileges: () => ['test/create'],
validate: {
config: { schema: schema.object({}) },
secrets: { schema: schema.object({}) },
Expand All @@ -2916,6 +2916,72 @@ describe('execute()', () => {
additionalPrivileges: [],
});
});

test('pass the params to the actionTypeRegistry when authorizing system actions', async () => {
(getAuthorizationModeBySource as jest.Mock).mockImplementationOnce(() => {
return AuthorizationMode.RBAC;
});

const getKibanaPrivileges = jest.fn().mockReturnValue(['test/create']);

actionsClient = new ActionsClient({
inMemoryConnectors: [
{
id: 'system-connector-.cases',
actionTypeId: '.cases',
name: 'System action: .cases',
config: {},
secrets: {},
isDeprecated: false,
isMissingSecrets: false,
isPreconfigured: false,
isSystemAction: true,
},
],
logger,
actionTypeRegistry,
unsecuredSavedObjectsClient,
scopedClusterClient,
kibanaIndices,
actionExecutor,
executionEnqueuer,
ephemeralExecutionEnqueuer,
bulkExecutionEnqueuer,
request,
authorization: authorization as unknown as ActionsAuthorization,
auditLogger,
usageCounter: mockUsageCounter,
connectorTokenClient,
getEventLogClient,
});

actionTypeRegistry.register({
id: '.cases',
name: 'Cases',
minimumLicenseRequired: 'platinum',
supportedFeatureIds: ['alerting'],
getKibanaPrivileges,
validate: {
config: { schema: schema.object({}) },
secrets: { schema: schema.object({}) },
params: { schema: schema.object({}) },
},
isSystemActionType: true,
executor,
});

await actionsClient.execute({
actionId: 'system-connector-.cases',
params: { foo: 'bar' },
});

expect(authorization.ensureAuthorized).toHaveBeenCalledWith({
operation: 'execute',
additionalPrivileges: ['test/create'],
});

expect(getKibanaPrivileges).toHaveBeenCalledWith({ metadata: { foo: 'bar' } });
});
});

test('calls the actionExecutor with the appropriate parameters', async () => {
Expand Down
9 changes: 6 additions & 3 deletions x-pack/plugins/actions/server/actions_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -718,13 +718,16 @@ export class ActionsClient {
return await this.unsecuredSavedObjectsClient.delete('action', id);
}

private getSystemActionKibanaPrivileges(actionId: string) {
private getSystemActionKibanaPrivileges(actionId: string, metadata?: Record<string, unknown>) {
const inMemoryConnector = this.inMemoryConnectors.find(
(connector) => connector.id === actionId
);

const additionalPrivileges = inMemoryConnector?.isSystemAction
? this.actionTypeRegistry.getSystemActionKibanaPrivileges(inMemoryConnector.actionTypeId)
? this.actionTypeRegistry.getSystemActionKibanaPrivileges(
inMemoryConnector.actionTypeId,
metadata
)
: [];

return additionalPrivileges;
Expand All @@ -742,7 +745,7 @@ export class ActionsClient {
(await getAuthorizationModeBySource(this.unsecuredSavedObjectsClient, source)) ===
AuthorizationMode.RBAC
) {
const additionalPrivileges = this.getSystemActionKibanaPrivileges(actionId);
const additionalPrivileges = this.getSystemActionKibanaPrivileges(actionId, params);
await this.authorization.ensureAuthorized({ operation: 'execute', additionalPrivileges });
} else {
trackLegacyRBACExemption('execute', this.usageCounter);
Expand Down
38 changes: 37 additions & 1 deletion x-pack/plugins/actions/server/lib/action_executor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -812,7 +812,7 @@ test('successfully authorize system actions', async () => {
name: 'Cases',
minimumLicenseRequired: 'platinum',
supportedFeatureIds: ['alerting'],
kibanaPrivileges: ['test/create'],
getKibanaPrivileges: () => ['test/create'],
isSystemActionType: true,
validate: {
config: { schema: schema.any() },
Expand All @@ -834,6 +834,42 @@ test('successfully authorize system actions', async () => {
});
});

test('pass the params to the actionTypeRegistry when authorizing system actions', async () => {
const actionType: jest.Mocked<ActionType> = {
id: '.cases',
name: 'Cases',
minimumLicenseRequired: 'platinum',
supportedFeatureIds: ['alerting'],
getKibanaPrivileges: () => ['test/create'],
isSystemActionType: true,
validate: {
config: { schema: schema.any() },
secrets: { schema: schema.any() },
params: { schema: schema.any() },
},
executor: jest.fn(),
};

actionTypeRegistry.get.mockReturnValueOnce(actionType);
actionTypeRegistry.isSystemActionType.mockReturnValueOnce(true);
actionTypeRegistry.getSystemActionKibanaPrivileges.mockReturnValueOnce(['test/create']);

await actionExecutor.execute({
...executeParams,
params: { foo: 'bar' },
actionId: 'system-connector-.cases',
});

expect(actionTypeRegistry.getSystemActionKibanaPrivileges).toHaveBeenCalledWith('.cases', {
foo: 'bar',
});

expect(authorizationMock.ensureAuthorized).toBeCalledWith({
operation: 'execute',
additionalPrivileges: ['test/create'],
});
});

test('does not authorize non system actions', async () => {
const actionType: jest.Mocked<ActionType> = {
id: 'test',
Expand Down
7 changes: 5 additions & 2 deletions x-pack/plugins/actions/server/lib/action_executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,11 @@ export class ActionExecutor {
*
*/
if (actionTypeRegistry.isSystemActionType(actionTypeId)) {
const additionalPrivileges =
actionTypeRegistry.getSystemActionKibanaPrivileges(actionTypeId);
const additionalPrivileges = actionTypeRegistry.getSystemActionKibanaPrivileges(
actionTypeId,
params
);

authorization.ensureAuthorized({ operation: 'execute', additionalPrivileges });
}

Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/actions/server/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ export interface ActionType<
* It only works with system actions and only when executing an action.
* For all other scenarios they will be ignored
*/
kibanaPrivileges?: string[];
getKibanaPrivileges?: (args?: { metadata?: Record<string, unknown> }) => string[];
renderParameterTemplates?: RenderParameterTemplates<Params>;
executor: ExecutorType<Config, Secrets, Params, ExecutorResultData>;
}
Expand Down

0 comments on commit 356432c

Please sign in to comment.