mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
parent
330b9d17f9
commit
377fae55f7
1 changed files with 160 additions and 104 deletions
|
@ -9,7 +9,8 @@ import { SecurityService, SpacesService } from '../../../common/services';
|
|||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export default function featureControlsTests({ getService }: FtrProviderContext) {
|
||||
const supertest = getService('supertestWithoutAuth');
|
||||
const supertest = getService('supertest');
|
||||
const supertestWithoutAuth = getService('supertestWithoutAuth');
|
||||
const security: SecurityService = getService('security');
|
||||
const spaces: SpacesService = getService('spaces');
|
||||
const log = getService('log');
|
||||
|
@ -19,142 +20,240 @@ export default function featureControlsTests({ getService }: FtrProviderContext)
|
|||
|
||||
const expect404 = (result: any) => {
|
||||
expect(result.error).to.be(undefined);
|
||||
expect(result.response).not.to.be(undefined);
|
||||
expect(result.response).to.have.property('statusCode', 404);
|
||||
expect(result.response.statusCode).to.be(404);
|
||||
};
|
||||
|
||||
const expect200 = (result: any) => {
|
||||
expect(result.error).to.be(undefined);
|
||||
expect(result.response).not.to.be(undefined);
|
||||
expect(result.response).to.have.property('statusCode', 200);
|
||||
expect(result.response.statusCode).to.be(200);
|
||||
};
|
||||
|
||||
const endpoints = [
|
||||
interface Endpoint {
|
||||
req: {
|
||||
url: string;
|
||||
method?: 'get' | 'post' | 'delete';
|
||||
body?: any;
|
||||
};
|
||||
expectForbidden: (result: any) => void;
|
||||
expectResponse: (result: any) => void;
|
||||
}
|
||||
const endpoints: Endpoint[] = [
|
||||
{
|
||||
url: `/api/apm/services/foo/errors?start=${start}&end=${end}&uiFilters=%7B%7D&_debug=true`,
|
||||
req: { url: `/api/apm/services/foo/errors?start=${start}&end=${end}&uiFilters=%7B%7D` },
|
||||
expectForbidden: expect404,
|
||||
expectResponse: expect200,
|
||||
},
|
||||
{
|
||||
url: `/api/apm/services/foo/errors/bar?start=${start}&end=${end}&uiFilters=%7B%7D`,
|
||||
req: { url: `/api/apm/services/foo/errors/bar?start=${start}&end=${end}&uiFilters=%7B%7D` },
|
||||
expectForbidden: expect404,
|
||||
expectResponse: expect200,
|
||||
},
|
||||
{
|
||||
url: `/api/apm/services/foo/errors/distribution?start=${start}&end=${end}&groupId=bar&uiFilters=%7B%7D`,
|
||||
req: {
|
||||
url: `/api/apm/services/foo/errors/distribution?start=${start}&end=${end}&groupId=bar&uiFilters=%7B%7D`,
|
||||
},
|
||||
expectForbidden: expect404,
|
||||
expectResponse: expect200,
|
||||
},
|
||||
{
|
||||
url: `/api/apm/services/foo/errors/distribution?start=${start}&end=${end}&uiFilters=%7B%7D`,
|
||||
req: {
|
||||
url: `/api/apm/services/foo/errors/distribution?start=${start}&end=${end}&uiFilters=%7B%7D`,
|
||||
},
|
||||
expectForbidden: expect404,
|
||||
expectResponse: expect200,
|
||||
},
|
||||
{
|
||||
url: `/api/apm/services/foo/metrics/charts?start=${start}&end=${end}&agentName=cool-agent&uiFilters=%7B%7D`,
|
||||
req: {
|
||||
url: `/api/apm/services/foo/metrics/charts?start=${start}&end=${end}&agentName=cool-agent&uiFilters=%7B%7D`,
|
||||
},
|
||||
expectForbidden: expect404,
|
||||
expectResponse: expect200,
|
||||
},
|
||||
{
|
||||
url: `/api/apm/services?start=${start}&end=${end}&uiFilters=%7B%7D`,
|
||||
req: { url: `/api/apm/services?start=${start}&end=${end}&uiFilters=%7B%7D` },
|
||||
expectForbidden: expect404,
|
||||
expectResponse: expect200,
|
||||
},
|
||||
{
|
||||
url: `/api/apm/services/foo/agent_name?start=${start}&end=${end}`,
|
||||
req: { url: `/api/apm/services/foo/agent_name?start=${start}&end=${end}` },
|
||||
expectForbidden: expect404,
|
||||
expectResponse: expect200,
|
||||
},
|
||||
{
|
||||
url: `/api/apm/services/foo/transaction_types?start=${start}&end=${end}`,
|
||||
req: { url: `/api/apm/services/foo/transaction_types?start=${start}&end=${end}` },
|
||||
expectForbidden: expect404,
|
||||
expectResponse: expect200,
|
||||
},
|
||||
{
|
||||
url: `/api/apm/traces?start=${start}&end=${end}&uiFilters=%7B%7D`,
|
||||
req: { url: `/api/apm/traces?start=${start}&end=${end}&uiFilters=%7B%7D` },
|
||||
expectForbidden: expect404,
|
||||
expectResponse: expect200,
|
||||
},
|
||||
{
|
||||
url: `/api/apm/traces/foo?start=${start}&end=${end}`,
|
||||
req: { url: `/api/apm/traces/foo?start=${start}&end=${end}` },
|
||||
expectForbidden: expect404,
|
||||
expectResponse: expect200,
|
||||
},
|
||||
{
|
||||
url: `/api/apm/services/foo/transaction_groups?start=${start}&end=${end}&transactionType=bar&uiFilters=%7B%7D`,
|
||||
req: {
|
||||
url: `/api/apm/services/foo/transaction_groups?start=${start}&end=${end}&transactionType=bar&uiFilters=%7B%7D`,
|
||||
},
|
||||
expectForbidden: expect404,
|
||||
expectResponse: expect200,
|
||||
},
|
||||
{
|
||||
url: `/api/apm/services/foo/transaction_groups/charts?start=${start}&end=${end}&transactionType=bar&uiFilters=%7B%7D`,
|
||||
req: {
|
||||
url: `/api/apm/services/foo/transaction_groups/charts?start=${start}&end=${end}&transactionType=bar&uiFilters=%7B%7D`,
|
||||
},
|
||||
expectForbidden: expect404,
|
||||
expectResponse: expect200,
|
||||
},
|
||||
{
|
||||
url: `/api/apm/services/foo/transaction_groups/charts?start=${start}&end=${end}&uiFilters=%7B%7D`,
|
||||
req: {
|
||||
url: `/api/apm/services/foo/transaction_groups/charts?start=${start}&end=${end}&uiFilters=%7B%7D`,
|
||||
},
|
||||
expectForbidden: expect404,
|
||||
expectResponse: expect200,
|
||||
},
|
||||
{
|
||||
url: `/api/apm/services/foo/transaction_groups/charts?start=${start}&end=${end}&transactionType=bar&transactionName=baz&uiFilters=%7B%7D`,
|
||||
req: {
|
||||
url: `/api/apm/services/foo/transaction_groups/charts?start=${start}&end=${end}&transactionType=bar&transactionName=baz&uiFilters=%7B%7D`,
|
||||
},
|
||||
expectForbidden: expect404,
|
||||
expectResponse: expect200,
|
||||
},
|
||||
{
|
||||
url: `/api/apm/services/foo/transaction_groups/distribution?start=${start}&end=${end}&transactionType=bar&transactionName=baz&uiFilters=%7B%7D`,
|
||||
req: {
|
||||
url: `/api/apm/services/foo/transaction_groups/distribution?start=${start}&end=${end}&transactionType=bar&transactionName=baz&uiFilters=%7B%7D`,
|
||||
},
|
||||
expectForbidden: expect404,
|
||||
expectResponse: expect200,
|
||||
},
|
||||
{
|
||||
req: {
|
||||
method: 'post',
|
||||
url: `/api/apm/settings/agent-configuration/search`,
|
||||
body: { service: { name: 'test-service' } },
|
||||
},
|
||||
expectForbidden: expect404,
|
||||
expectResponse: expect200,
|
||||
},
|
||||
];
|
||||
|
||||
async function executeRequest(
|
||||
endpoint: string,
|
||||
const elasticsearchRole = {
|
||||
indices: [
|
||||
{ names: ['apm-*'], privileges: ['read', 'view_index_metadata'] },
|
||||
{ names: ['.apm-agent-configuration'], privileges: ['read', 'write', 'view_index_metadata'] },
|
||||
],
|
||||
};
|
||||
|
||||
async function executeAsUser(
|
||||
{ method = 'get', url, body }: Endpoint['req'],
|
||||
username: string,
|
||||
password: string,
|
||||
spaceId?: string
|
||||
) {
|
||||
const basePath = spaceId ? `/s/${spaceId}` : '';
|
||||
|
||||
return await supertest
|
||||
.get(`${basePath}${endpoint}`)
|
||||
let request = supertestWithoutAuth[method](`${basePath}${url}`);
|
||||
|
||||
// json body
|
||||
if (body) {
|
||||
request = request.send(body);
|
||||
}
|
||||
|
||||
return await request
|
||||
.auth(username, password)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.then((response: any) => ({ error: undefined, response }))
|
||||
.catch((error: any) => ({ error, response: undefined }));
|
||||
}
|
||||
|
||||
async function executeRequests(
|
||||
username: string,
|
||||
password: string,
|
||||
spaceId: string,
|
||||
expectation: 'forbidden' | 'response'
|
||||
) {
|
||||
async function executeAsAdmin({ method = 'get', url, body }: Endpoint['req'], spaceId?: string) {
|
||||
const basePath = spaceId ? `/s/${spaceId}` : '';
|
||||
|
||||
let request = supertest[method](`${basePath}${url}`);
|
||||
|
||||
// json body
|
||||
if (body) {
|
||||
request = request.send(body);
|
||||
}
|
||||
|
||||
const response = await request.set('kbn-xsrf', 'foo');
|
||||
|
||||
const { statusCode, req } = response;
|
||||
if (statusCode !== 200) {
|
||||
log.debug(`Endpoint: ${req.method} ${req.path}
|
||||
Status code: ${statusCode}
|
||||
Response: ${response.body.message}`);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
async function executeRequests({
|
||||
username,
|
||||
password,
|
||||
expectation,
|
||||
spaceId,
|
||||
}: {
|
||||
username: string;
|
||||
password: string;
|
||||
expectation: 'forbidden' | 'response';
|
||||
spaceId?: string;
|
||||
}) {
|
||||
for (const endpoint of endpoints) {
|
||||
log.debug(`hitting ${endpoint}`);
|
||||
const result = await executeRequest(endpoint.url, username, password, spaceId);
|
||||
if (expectation === 'forbidden') {
|
||||
endpoint.expectForbidden(result);
|
||||
} else {
|
||||
endpoint.expectResponse(result);
|
||||
log.debug(`hitting ${endpoint.req.url}`);
|
||||
const result = await executeAsUser(endpoint.req, username, password, spaceId);
|
||||
try {
|
||||
if (expectation === 'forbidden') {
|
||||
endpoint.expectForbidden(result);
|
||||
} else {
|
||||
endpoint.expectResponse(result);
|
||||
}
|
||||
} catch (e) {
|
||||
const { statusCode, body, req } = result.response;
|
||||
throw new Error(
|
||||
`Endpoint: ${req.method} ${req.path}
|
||||
Status code: ${statusCode}
|
||||
Response: ${body.message}
|
||||
|
||||
${e.message}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
describe('feature controls', () => {
|
||||
it(`APIs can't be accessed by apm-* read privileges role`, async () => {
|
||||
describe('apm feature controls', () => {
|
||||
let res: any;
|
||||
before(async () => {
|
||||
log.debug('creating agent configuration');
|
||||
res = await executeAsAdmin({
|
||||
method: 'post',
|
||||
url: '/api/apm/settings/agent-configuration/new',
|
||||
body: {
|
||||
service: { name: 'test-service' },
|
||||
settings: { transaction_sample_rate: 0.5 },
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
log.debug('deleting agent configuration');
|
||||
const configurationId = res.body._id;
|
||||
await executeAsAdmin({
|
||||
method: 'delete',
|
||||
url: `/api/apm/settings/agent-configuration/${configurationId}`,
|
||||
});
|
||||
});
|
||||
|
||||
it(`APIs can't be accessed by logstash_read user`, async () => {
|
||||
const username = 'logstash_read';
|
||||
const roleName = 'logstash_read';
|
||||
const password = `${username}-password`;
|
||||
try {
|
||||
await security.role.create(roleName, {
|
||||
elasticsearch: {
|
||||
indices: [
|
||||
{
|
||||
names: ['apm-*'],
|
||||
privileges: ['read', 'view_index_metadata'],
|
||||
},
|
||||
],
|
||||
},
|
||||
elasticsearch: elasticsearchRole,
|
||||
});
|
||||
|
||||
await security.user.create(username, {
|
||||
|
@ -163,33 +262,21 @@ export default function featureControlsTests({ getService }: FtrProviderContext)
|
|||
full_name: 'a kibana user',
|
||||
});
|
||||
|
||||
await executeRequests(username, password, '', 'forbidden');
|
||||
await executeRequests({ username, password, expectation: 'forbidden' });
|
||||
} finally {
|
||||
await security.role.delete(roleName);
|
||||
await security.user.delete(username);
|
||||
}
|
||||
});
|
||||
|
||||
it('APIs can be accessed global all with apm-* read privileges role', async () => {
|
||||
it('APIs can be accessed by global_all user', async () => {
|
||||
const username = 'global_all';
|
||||
const roleName = 'global_all';
|
||||
const password = `${username}-password`;
|
||||
try {
|
||||
await security.role.create(roleName, {
|
||||
elasticsearch: {
|
||||
indices: [
|
||||
{
|
||||
names: ['apm-*'],
|
||||
privileges: ['read', 'view_index_metadata'],
|
||||
},
|
||||
],
|
||||
},
|
||||
kibana: [
|
||||
{
|
||||
base: ['all'],
|
||||
spaces: ['*'],
|
||||
},
|
||||
],
|
||||
elasticsearch: elasticsearchRole,
|
||||
kibana: [{ base: ['all'], spaces: ['*'] }],
|
||||
});
|
||||
|
||||
await security.user.create(username, {
|
||||
|
@ -198,7 +285,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext)
|
|||
full_name: 'a kibana user',
|
||||
});
|
||||
|
||||
await executeRequests(username, password, '', 'response');
|
||||
await executeRequests({ username, password, expectation: 'response' });
|
||||
} finally {
|
||||
await security.role.delete(roleName);
|
||||
await security.user.delete(username);
|
||||
|
@ -206,28 +293,14 @@ export default function featureControlsTests({ getService }: FtrProviderContext)
|
|||
});
|
||||
|
||||
// this could be any role which doesn't have access to the APM feature
|
||||
it(`APIs can't be accessed by dashboard all with apm-* read privileges role`, async () => {
|
||||
it(`APIs can't be accessed by dashboard_all user`, async () => {
|
||||
const username = 'dashboard_all';
|
||||
const roleName = 'dashboard_all';
|
||||
const password = `${username}-password`;
|
||||
try {
|
||||
await security.role.create(roleName, {
|
||||
elasticsearch: {
|
||||
indices: [
|
||||
{
|
||||
names: ['apm-*'],
|
||||
privileges: ['read', 'view_index_metadata'],
|
||||
},
|
||||
],
|
||||
},
|
||||
kibana: [
|
||||
{
|
||||
feature: {
|
||||
dashboard: ['all'],
|
||||
},
|
||||
spaces: ['*'],
|
||||
},
|
||||
],
|
||||
elasticsearch: elasticsearchRole,
|
||||
kibana: [{ feature: { dashboard: ['all'] }, spaces: ['*'] }],
|
||||
});
|
||||
|
||||
await security.user.create(username, {
|
||||
|
@ -236,7 +309,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext)
|
|||
full_name: 'a kibana user',
|
||||
});
|
||||
|
||||
await executeRequests(username, password, '', 'forbidden');
|
||||
await executeRequests({ username, password, expectation: 'forbidden' });
|
||||
} finally {
|
||||
await security.role.delete(roleName);
|
||||
await security.user.delete(username);
|
||||
|
@ -264,27 +337,10 @@ export default function featureControlsTests({ getService }: FtrProviderContext)
|
|||
disabledFeatures: [],
|
||||
});
|
||||
await security.role.create(roleName, {
|
||||
elasticsearch: {
|
||||
indices: [
|
||||
{
|
||||
names: ['apm-*'],
|
||||
privileges: ['read', 'view_index_metadata'],
|
||||
},
|
||||
],
|
||||
},
|
||||
elasticsearch: elasticsearchRole,
|
||||
kibana: [
|
||||
{
|
||||
feature: {
|
||||
apm: ['read'],
|
||||
},
|
||||
spaces: [space1Id],
|
||||
},
|
||||
{
|
||||
feature: {
|
||||
dashboard: ['all'],
|
||||
},
|
||||
spaces: [space2Id],
|
||||
},
|
||||
{ feature: { apm: ['read'] }, spaces: [space1Id] },
|
||||
{ feature: { dashboard: ['all'] }, spaces: [space2Id] },
|
||||
],
|
||||
});
|
||||
await security.user.create(username, {
|
||||
|
@ -301,11 +357,11 @@ export default function featureControlsTests({ getService }: FtrProviderContext)
|
|||
});
|
||||
|
||||
it('user_1 can access APIs in space_1', async () => {
|
||||
await executeRequests(username, password, space1Id, 'response');
|
||||
await executeRequests({ username, password, expectation: 'response', spaceId: space1Id });
|
||||
});
|
||||
|
||||
it(`user_1 can't access APIs in space_2`, async () => {
|
||||
await executeRequests(username, password, space2Id, 'forbidden');
|
||||
await executeRequests({ username, password, expectation: 'forbidden', spaceId: space2Id });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue