Skip to content

Commit

Permalink
Assert user has expected privielges on source index during reset
Browse files Browse the repository at this point in the history
  • Loading branch information
kdelemme committed Nov 6, 2024
1 parent ba6b898 commit cb992ae
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 36 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
* 2.0.
*/

import { ElasticsearchClient } from '@kbn/core/server';
import { SecurityHasPrivilegesResponse } from '@elastic/elasticsearch/lib/api/types';
import {
ElasticsearchClientMock,
elasticsearchServiceMock,
httpServiceMock,
loggingSystemMock,
ScopedClusterClientMock,
} from '@kbn/core/server/mocks';
import { MockedLogger } from '@kbn/logging-mocks';

import { SLO_MODEL_VERSION } from '../../common/constants';
import { createSLO } from './fixtures/slo';
import {
Expand All @@ -31,7 +31,7 @@ describe('ResetSLO', () => {
let mockRepository: jest.Mocked<SLORepository>;
let mockTransformManager: jest.Mocked<TransformManager>;
let mockSummaryTransformManager: jest.Mocked<TransformManager>;
let mockEsClient: jest.Mocked<ElasticsearchClient>;
let mockEsClient: ElasticsearchClientMock;
let mockScopedClusterClient: ScopedClusterClientMock;
let loggerMock: jest.Mocked<MockedLogger>;
let resetSLO: ResetSLO;
Expand Down Expand Up @@ -60,37 +60,62 @@ describe('ResetSLO', () => {
jest.useRealTimers();
});

it('resets all associated resources', async () => {
const slo = createSLO({ id: 'irrelevant', version: 1 });
mockRepository.findById.mockResolvedValueOnce(slo);
mockRepository.update.mockImplementation((v) => Promise.resolve(v));
describe('happy path', () => {
beforeEach(() => {
mockEsClient.security.hasPrivileges.mockResolvedValue({
has_all_requested: true,
} as SecurityHasPrivilegesResponse);
});

it('resets all associated resources', async () => {
const slo = createSLO({ id: 'irrelevant', version: 1 });
mockRepository.findById.mockResolvedValueOnce(slo);
mockRepository.update.mockImplementation((v) => Promise.resolve(v));

await resetSLO.execute(slo.id);

// delete existing resources and data
expect(mockSummaryTransformManager.stop).toMatchSnapshot();
expect(mockSummaryTransformManager.uninstall).toMatchSnapshot();

await resetSLO.execute(slo.id);
expect(mockTransformManager.stop).toMatchSnapshot();
expect(mockTransformManager.uninstall).toMatchSnapshot();

// delete existing resources and data
expect(mockSummaryTransformManager.stop).toMatchSnapshot();
expect(mockSummaryTransformManager.uninstall).toMatchSnapshot();
expect(mockEsClient.deleteByQuery).toMatchSnapshot();

expect(mockTransformManager.stop).toMatchSnapshot();
expect(mockTransformManager.uninstall).toMatchSnapshot();
// install resources
expect(mockSummaryTransformManager.install).toMatchSnapshot();
expect(mockSummaryTransformManager.start).toMatchSnapshot();

expect(mockEsClient.deleteByQuery).toMatchSnapshot();
expect(mockScopedClusterClient.asSecondaryAuthUser.ingest.putPipeline).toMatchSnapshot();

// install resources
expect(mockSummaryTransformManager.install).toMatchSnapshot();
expect(mockSummaryTransformManager.start).toMatchSnapshot();
expect(mockTransformManager.install).toMatchSnapshot();
expect(mockTransformManager.start).toMatchSnapshot();

expect(mockScopedClusterClient.asSecondaryAuthUser.ingest.putPipeline).toMatchSnapshot();
expect(mockEsClient.index).toMatchSnapshot();

expect(mockTransformManager.install).toMatchSnapshot();
expect(mockTransformManager.start).toMatchSnapshot();
expect(mockRepository.update).toHaveBeenCalledWith({
...slo,
version: SLO_MODEL_VERSION,
updatedAt: expect.anything(),
});
});
});

describe('unhappy path', () => {
beforeEach(() => {
mockEsClient.security.hasPrivileges.mockResolvedValue({
has_all_requested: false,
} as SecurityHasPrivilegesResponse);
});

expect(mockEsClient.index).toMatchSnapshot();
it('throws a SecurityException error when the user does not have the required privileges', async () => {
const slo = createSLO({ id: 'irrelevant', version: 1 });
mockRepository.findById.mockResolvedValueOnce(slo);

expect(mockRepository.update).toHaveBeenCalledWith({
...slo,
version: SLO_MODEL_VERSION,
updatedAt: expect.anything(),
await expect(resetSLO.execute(slo.id)).rejects.toThrowError(
"Missing ['read', 'view_index_metadata'] privileges on the source index [metrics-apm*]"
);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import { retryTransientEsErrors } from '../utils/retry';
import { SLORepository } from './slo_repository';
import { createTempSummaryDocument } from './summary_transform_generator/helpers/create_temp_summary';
import { TransformManager } from './transform_manager';
import { SLODefinition } from '../domain/models';
import { SecurityException } from '../errors';

export class ResetSLO {
constructor(
Expand All @@ -39,6 +41,8 @@ export class ResetSLO {
public async execute(sloId: string) {
const slo = await this.repository.findById(sloId);

await this.assertExpectedIndicatorSourceIndexPrivileges(slo);

const summaryTransformId = getSLOSummaryTransformId(slo.id, slo.revision);
await this.summaryTransformManager.stop(summaryTransformId);
await this.summaryTransformManager.uninstall(summaryTransformId);
Expand Down Expand Up @@ -113,6 +117,17 @@ export class ResetSLO {
return resetSLOResponseSchema.encode(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}]`
);
}
}

/**
* Deleting all SLI rollup data matching the sloId. All revision will be deleted in case of
* residual documents.
Expand Down

0 comments on commit cb992ae

Please sign in to comment.