Skip to content

Commit

Permalink
Add authentication to apis
Browse files Browse the repository at this point in the history
  • Loading branch information
CohenIdo authored Mar 24, 2022
1 parent 96515f5 commit 546b82a
Show file tree
Hide file tree
Showing 11 changed files with 303 additions and 14 deletions.
3 changes: 2 additions & 1 deletion x-pack/plugins/cloud_security_posture/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import type {
CspServerPluginStart,
CspServerPluginSetupDeps,
CspServerPluginStartDeps,
CspRequestHandlerContext,
} from './types';
import { defineRoutes } from './routes';
import { cspRuleAssetType } from './saved_objects/cis_1_4_1/csp_rule_type';
Expand Down Expand Up @@ -55,7 +56,7 @@ export class CspPlugin

core.savedObjects.registerType(cspRuleAssetType);

const router = core.http.createRouter();
const router = core.http.createRouter<CspRequestHandlerContext>();

// Register server side APIs
defineRoutes(router, cspAppContext);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,18 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { httpServiceMock, loggingSystemMock, savedObjectsClientMock } from 'src/core/server/mocks';
import {
httpServerMock,
httpServiceMock,
loggingSystemMock,
savedObjectsClientMock,
} from 'src/core/server/mocks';
import {
ElasticsearchClientMock,
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
} from 'src/core/server/elasticsearch/client/mocks';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { KibanaRequest } from 'src/core/server/http/router/request';
import {
defineGetBenchmarksRoute,
benchmarksInputSchema,
Expand All @@ -14,6 +25,7 @@ import {
getAgentPolicies,
createBenchmarkEntry,
} from './benchmarks';

import { SavedObjectsClientContract } from 'src/core/server';
import {
createMockAgentPolicyService,
Expand All @@ -25,6 +37,17 @@ import { AgentPolicy } from '../../../../fleet/common';
import { CspAppService } from '../../lib/csp_app_services';
import { CspAppContext } from '../../plugin';

export const getMockCspContext = (mockEsClient: ElasticsearchClientMock): KibanaRequest => {
return {
core: {
elasticsearch: {
client: { asCurrentUser: mockEsClient },
},
},
fleet: { authz: { fleet: { all: false } } },
} as unknown as KibanaRequest;
};

function createMockAgentPolicy(props: Partial<AgentPolicy> = {}): AgentPolicy {
return {
id: 'some-uuid1',
Expand Down Expand Up @@ -66,6 +89,54 @@ describe('benchmarks API', () => {
expect(config.path).toEqual('/api/csp/benchmarks');
});

it('should accept to a user with fleet.all privilege', async () => {
const router = httpServiceMock.createRouter();
const cspAppContextService = new CspAppService();

const cspContext: CspAppContext = {
logger,
service: cspAppContextService,
};
defineGetBenchmarksRoute(router, cspContext);
const [_, handler] = router.get.mock.calls[0];

const mockContext = {
fleet: { authz: { fleet: { all: true } } },
} as unknown as KibanaRequest;

const mockResponse = httpServerMock.createResponseFactory();
const mockRequest = httpServerMock.createKibanaRequest();
const [context, req, res] = [mockContext, mockRequest, mockResponse];

await handler(context, req, res);

expect(res.forbidden).toHaveBeenCalledTimes(0);
});

it('should reject to a user without fleet.all privilege', async () => {
const router = httpServiceMock.createRouter();
const cspAppContextService = new CspAppService();

const cspContext: CspAppContext = {
logger,
service: cspAppContextService,
};
defineGetBenchmarksRoute(router, cspContext);
const [_, handler] = router.get.mock.calls[0];

const mockContext = {
fleet: { authz: { fleet: { all: false } } },
} as unknown as KibanaRequest;

const mockResponse = httpServerMock.createResponseFactory();
const mockRequest = httpServerMock.createKibanaRequest();
const [context, req, res] = [mockContext, mockRequest, mockResponse];

await handler(context, req, res);

expect(res.forbidden).toHaveBeenCalledTimes(1);
});

describe('test input schema', () => {
it('expect to find default values', async () => {
const validatedQuery = benchmarksInputSchema.validate({});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/
import { uniq, map } from 'lodash';
import type { IRouter, SavedObjectsClientContract } from 'src/core/server';
import type { SavedObjectsClientContract } from 'src/core/server';
import { schema as rt, TypeOf } from '@kbn/config-schema';
import { transformError } from '@kbn/securitysolution-es-utils';
import type {
Expand All @@ -23,6 +23,7 @@ import { BENCHMARKS_ROUTE_PATH, CIS_KUBERNETES_PACKAGE_NAME } from '../../../com
import { CspAppContext } from '../../plugin';
import type { Benchmark } from '../../../common/types';
import { isNonNullable } from '../../../common/utils/helpers';
import { CspRouter } from '../../types';

type BenchmarksQuerySchema = TypeOf<typeof benchmarksInputSchema>;

Expand Down Expand Up @@ -132,13 +133,17 @@ const createBenchmarks = (
.filter(isNonNullable);
});

export const defineGetBenchmarksRoute = (router: IRouter, cspContext: CspAppContext): void =>
export const defineGetBenchmarksRoute = (router: CspRouter, cspContext: CspAppContext): void =>
router.get(
{
path: BENCHMARKS_ROUTE_PATH,
validate: { query: benchmarksInputSchema },
},
async (context, request, response) => {
if (!context.fleet.authz.fleet.all) {
return response.forbidden();
}

try {
const soClient = context.core.savedObjects.client;
const { query } = request;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { httpServerMock, httpServiceMock, loggingSystemMock } from 'src/core/server/mocks';
import // eslint-disable-next-line @kbn/eslint/no-restricted-paths
'src/core/server/elasticsearch/client/mocks';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { KibanaRequest } from 'src/core/server/http/router/request';
import { defineGetComplianceDashboardRoute } from './compliance_dashboard';

import { CspAppService } from '../../lib/csp_app_services';
import { CspAppContext } from '../../plugin';

describe('compliance dashboard permissions API', () => {
let logger: ReturnType<typeof loggingSystemMock.createLogger>;

beforeEach(() => {
logger = loggingSystemMock.createLogger();
jest.clearAllMocks();
});

it('should accept to a user with fleet.all privilege', async () => {
const router = httpServiceMock.createRouter();
const cspAppContextService = new CspAppService();

const cspContext: CspAppContext = {
logger,
service: cspAppContextService,
};
defineGetComplianceDashboardRoute(router, cspContext);
const [_, handler] = router.get.mock.calls[0];

const mockContext = {
fleet: { authz: { fleet: { all: true } } },
} as unknown as KibanaRequest;

const mockResponse = httpServerMock.createResponseFactory();
const mockRequest = httpServerMock.createKibanaRequest();
const [context, req, res] = [mockContext, mockRequest, mockResponse];

await handler(context, req, res);

expect(res.forbidden).toHaveBeenCalledTimes(0);
});

it('should reject to a user without fleet.all privilege', async () => {
const router = httpServiceMock.createRouter();
const cspAppContextService = new CspAppService();

const cspContext: CspAppContext = {
logger,
service: cspAppContextService,
};
defineGetComplianceDashboardRoute(router, cspContext);
const [_, handler] = router.get.mock.calls[0];

const mockContext = {
fleet: { authz: { fleet: { all: true } } },
} as unknown as KibanaRequest;

const mockResponse = httpServerMock.createResponseFactory();
const mockRequest = httpServerMock.createKibanaRequest();
const [context, req, res] = [mockContext, mockRequest, mockResponse];

await handler(context, req, res);

expect(res.forbidden).toHaveBeenCalledTimes(0);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import type { ElasticsearchClient, IRouter } from 'src/core/server';
import type { ElasticsearchClient } from 'src/core/server';
import { transformError } from '@kbn/securitysolution-es-utils';
import type {
AggregationsMultiBucketAggregateBase as Aggregation,
Expand All @@ -19,6 +19,7 @@ import { CspAppContext } from '../../plugin';
import { getResourcesTypes } from './get_resources_types';
import { getClusters } from './get_clusters';
import { getStats } from './get_stats';
import { CspRouter } from '../../types';

export interface ClusterBucket {
ordered_top_hits: AggregationsTopHitsAggregate;
Expand Down Expand Up @@ -75,7 +76,7 @@ const getLatestCyclesIds = async (esClient: ElasticsearchClient): Promise<string

// TODO: Utilize ES "Point in Time" feature https://www.elastic.co/guide/en/elasticsearch/reference/current/point-in-time-api.html
export const defineGetComplianceDashboardRoute = (
router: IRouter,
router: CspRouter,
cspContext: CspAppContext
): void =>
router.get(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@
*/
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { elasticsearchClientMock } from 'src/core/server/elasticsearch/client/mocks';
import { savedObjectsClientMock, httpServiceMock, loggingSystemMock } from 'src/core/server/mocks';
import {
savedObjectsClientMock,
httpServiceMock,
loggingSystemMock,
httpServerMock,
} from 'src/core/server/mocks';
import {
convertRulesConfigToYaml,
createRulesConfig,
Expand All @@ -24,6 +29,7 @@ import { createPackagePolicyServiceMock } from '../../../../fleet/server/mocks';
import { cspRuleAssetSavedObjectType, CspRuleSchema } from '../../../common/schemas/csp_rule';
import {
ElasticsearchClient,
KibanaRequest,
SavedObjectsClientContract,
SavedObjectsFindResponse,
} from 'kibana/server';
Expand Down Expand Up @@ -55,6 +61,54 @@ describe('Update rules configuration API', () => {
expect(config.path).toEqual('/api/csp/update_rules_config');
});

it('should accept to a user with fleet.all privilege', async () => {
const router = httpServiceMock.createRouter();
const cspAppContextService = new CspAppService();

const cspContext: CspAppContext = {
logger,
service: cspAppContextService,
};
defineUpdateRulesConfigRoute(router, cspContext);
const [_, handler] = router.post.mock.calls[0];

const mockContext = {
fleet: { authz: { fleet: { all: true } } },
} as unknown as KibanaRequest;

const mockResponse = httpServerMock.createResponseFactory();
const mockRequest = httpServerMock.createKibanaRequest();
const [context, req, res] = [mockContext, mockRequest, mockResponse];

await handler(context, req, res);

expect(res.forbidden).toHaveBeenCalledTimes(0);
});

it('should reject to a user without fleet.all privilege', async () => {
const router = httpServiceMock.createRouter();
const cspAppContextService = new CspAppService();

const cspContext: CspAppContext = {
logger,
service: cspAppContextService,
};
defineUpdateRulesConfigRoute(router, cspContext);
const [_, handler] = router.post.mock.calls[0];

const mockContext = {
fleet: { authz: { fleet: { all: true } } },
} as unknown as KibanaRequest;

const mockResponse = httpServerMock.createResponseFactory();
const mockRequest = httpServerMock.createKibanaRequest();
const [context, req, res] = [mockContext, mockRequest, mockResponse];

await handler(context, req, res);

expect(res.forbidden).toHaveBeenCalledTimes(0);
});

it('validate getCspRules input parameters', async () => {
mockSoClient = savedObjectsClientMock.create();
mockSoClient.find.mockResolvedValueOnce({} as SavedObjectsFindResponse);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
*/
import type {
ElasticsearchClient,
IRouter,
SavedObjectsClientContract,
SavedObjectsFindResponse,
} from 'src/core/server';
Expand All @@ -24,6 +23,7 @@ import { CspRuleSchema, cspRuleAssetSavedObjectType } from '../../../common/sche
import { UPDATE_RULES_CONFIG_ROUTE_PATH } from '../../../common/constants';
import { CIS_KUBERNETES_PACKAGE_NAME } from '../../../common/constants';
import { PackagePolicyServiceInterface } from '../../../../fleet/server';
import { CspRouter } from '../../types';

export const getPackagePolicy = async (
soClient: SavedObjectsClientContract,
Expand Down Expand Up @@ -99,13 +99,17 @@ export const updatePackagePolicy = (
return packagePolicyService.update(soClient, esClient, packagePolicy.id, updatedPackagePolicy);
};

export const defineUpdateRulesConfigRoute = (router: IRouter, cspContext: CspAppContext): void =>
export const defineUpdateRulesConfigRoute = (router: CspRouter, cspContext: CspAppContext): void =>
router.post(
{
path: UPDATE_RULES_CONFIG_ROUTE_PATH,
validate: { query: configurationUpdateInputSchema },
},
async (context, request, response) => {
if (!context.fleet.authz.fleet.all) {
return response.forbidden();
}

try {
const esClient = context.core.elasticsearch.client.asCurrentUser;
const soClient = context.core.savedObjects.client;
Expand Down
Loading

0 comments on commit 546b82a

Please sign in to comment.