Skip to content

Commit

Permalink
Assert user has expected privileges on source index during update
Browse files Browse the repository at this point in the history
  • Loading branch information
kdelemme committed Nov 6, 2024
1 parent 36bef65 commit ba6b898
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
* 2.0.
*/

import { ElasticsearchClient } from '@kbn/core/server';
import {
ElasticsearchClientMock,
elasticsearchServiceMock,
httpServiceMock,
loggingSystemMock,
Expand All @@ -16,6 +16,7 @@ import { MockedLogger } from '@kbn/logging-mocks';
import { UpdateSLOParams } from '@kbn/slo-schema';
import { cloneDeep, omit, pick } from 'lodash';

import { SecurityHasPrivilegesResponse } from '@elastic/elasticsearch/lib/api/types';
import {
getSLOSummaryTransformId,
getSLOTransformId,
Expand All @@ -42,7 +43,7 @@ import { UpdateSLO } from './update_slo';
describe('UpdateSLO', () => {
let mockRepository: jest.Mocked<SLORepository>;
let mockTransformManager: jest.Mocked<TransformManager>;
let mockEsClient: jest.Mocked<ElasticsearchClient>;
let mockEsClient: ElasticsearchClientMock;
let mockScopedClusterClient: ScopedClusterClientMock;
let mockLogger: jest.Mocked<MockedLogger>;
let mockSummaryTransformManager: jest.Mocked<TransformManager>;
Expand All @@ -69,6 +70,8 @@ describe('UpdateSLO', () => {

describe('when the update payload does not change the original SLO', () => {
function expectNoCallsToAnyMocks() {
expect(mockEsClient.security.hasPrivileges).not.toBeCalled();

expect(mockTransformManager.stop).not.toBeCalled();
expect(mockTransformManager.uninstall).not.toBeCalled();
expect(mockTransformManager.install).not.toBeCalled();
Expand Down Expand Up @@ -192,6 +195,12 @@ describe('UpdateSLO', () => {
});

describe('handles breaking changes', () => {
beforeEach(() => {
mockEsClient.security.hasPrivileges.mockResolvedValue({
has_all_requested: true,
} as SecurityHasPrivilegesResponse);
});

it('consideres a settings change as a breaking change', async () => {
const slo = createSLO();
mockRepository.findById.mockResolvedValueOnce(slo);
Expand Down Expand Up @@ -302,6 +311,32 @@ describe('UpdateSLO', () => {
});

describe('when error happens during the update', () => {
beforeEach(() => {
mockEsClient.security.hasPrivileges.mockResolvedValue({
has_all_requested: true,
} as SecurityHasPrivilegesResponse);
});

it('throws a SecurityException error when the user does not have the required privileges on the source index', async () => {
mockEsClient.security.hasPrivileges.mockResolvedValue({
has_all_requested: false,
} as SecurityHasPrivilegesResponse);

const originalSlo = createSLO({
id: 'original-id',
indicator: createAPMTransactionErrorRateIndicator(),
});
mockRepository.findById.mockResolvedValueOnce(originalSlo);

const newIndicator = createAPMTransactionErrorRateIndicator({ index: 'new-index-*' });

await expect(
updateSLO.execute(originalSlo.id, { indicator: newIndicator })
).rejects.toThrowError(
"Missing ['read', 'view_index_metadata'] privileges on the source index [new-index-*]"
);
});

it('restores the previous SLO definition when updated summary transform install fails', async () => {
const originalSlo = createSLO({
id: 'original-id',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,9 @@ export class UpdateSLO {

validateSLO(updatedSlo);

const rollbackOperations = [];
await this.assertExpectedIndicatorSourceIndexPrivileges(updatedSlo);

const rollbackOperations = [];
await this.repository.update(updatedSlo);
rollbackOperations.push(() => this.repository.update(originalSlo));

Expand Down Expand Up @@ -202,6 +203,17 @@ export class UpdateSLO {
return this.toResponse(updatedSlo);
}

private async assertExpectedIndicatorSourceIndexPrivileges(slo: SLODefinition) {
const privileges = await this.esClient.security.hasPrivileges({
index: [{ names: slo.indicator.params.index, privileges: ['read', 'view_index_metadata'] }],
});
if (!privileges.has_all_requested) {
throw new SecurityException(
`Missing ['read', 'view_index_metadata'] privileges on the source index [${slo.indicator.params.index}]`
);
}
}

private async deleteOriginalSLO(originalSlo: SLODefinition) {
try {
const originalRollupTransformId = getSLOTransformId(originalSlo.id, originalSlo.revision);
Expand Down

0 comments on commit ba6b898

Please sign in to comment.