Skip to content

Commit

Permalink
Revert "[7.x] Omit runtime fields from FLS suggestions (#78330) (#79106
Browse files Browse the repository at this point in the history
…) (#79939)

Co-authored-by: spalger <[email protected]>
  • Loading branch information
Spencer and spalger authored Oct 7, 2020
1 parent 171c27f commit 1f58db6
Show file tree
Hide file tree
Showing 4 changed files with 9 additions and 160 deletions.
58 changes: 0 additions & 58 deletions x-pack/plugins/security/server/routes/indices/get_fields.test.ts

This file was deleted.

46 changes: 9 additions & 37 deletions x-pack/plugins/security/server/routes/indices/get_fields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,6 @@ import { schema } from '@kbn/config-schema';
import { RouteDefinitionParams } from '../index';
import { wrapIntoCustomErrorResponse } from '../../errors';

interface FieldMappingResponse {
[indexName: string]: {
mappings: {
[fieldName: string]: {
mapping: {
[fieldName: string]: {
type: string;
};
};
};
};
};
}

export function defineGetFieldsRoutes({ router, clusterClient }: RouteDefinitionParams) {
router.get(
{
Expand All @@ -37,35 +23,21 @@ export function defineGetFieldsRoutes({ router, clusterClient }: RouteDefinition
fields: '*',
allowNoIndices: false,
includeDefaults: true,
})) as FieldMappingResponse;
})) as Record<string, { mappings: Record<string, unknown> }>;

// The flow is the following (see response format at https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-get-field-mapping.html):
// 1. Iterate over all matched indices.
// 2. Extract all the field names from the `mappings` field of the particular index.
// 3. Collect and flatten the list of the field names, omitting any fields without mappings, and any runtime fields
// 3. Collect and flatten the list of the field names.
// 4. Use `Set` to get only unique field names.
const fields = Array.from(
new Set(
Object.values(indexMappings).flatMap((indexMapping) => {
return Object.keys(indexMapping.mappings).filter((fieldName) => {
const mappingValues = Object.values(indexMapping.mappings[fieldName].mapping);
const hasMapping = mappingValues.length > 0;

const isRuntimeField = hasMapping && mappingValues[0]?.type === 'runtime';

// fields without mappings are internal fields such as `_routing` and `_index`,
// and therefore don't make sense as autocomplete suggestions for FLS.

// Runtime fields are not securable via FLS.
// Administrators should instead secure access to the fields which derive this information.
return hasMapping && !isRuntimeField;
});
})
)
);

return response.ok({
body: fields,
body: Array.from(
new Set(
Object.values(indexMappings)
.map((indexMapping) => Object.keys(indexMapping.mappings))
.flat()
)
),
});
} catch (error) {
return response.customError(wrapIntoCustomErrorResponse(error));
Expand Down
58 changes: 0 additions & 58 deletions x-pack/test/api_integration/apis/security/index_fields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,10 @@
import expect from '@kbn/expect/expect.js';
import { FtrProviderContext } from '../../ftr_provider_context';

interface FLSFieldMappingResponse {
flstest: {
mappings: {
[fieldName: string]: {
mapping: {
[fieldName: string]: {
type: string;
};
};
};
};
};
}

export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
const es = getService('legacyEs');

describe('Index Fields', () => {
before(async () => {
await esArchiver.load('security/flstest/data');
});
after(async () => {
await esArchiver.unload('security/flstest/data');
});

describe('GET /internal/security/fields/{query}', () => {
it('should return a list of available index mapping fields', async () => {
await supertest
Expand All @@ -53,41 +30,6 @@ export default function ({ getService }: FtrProviderContext) {
sampleOfExpectedFields.forEach((field) => expect(response.body).to.contain(field));
});
});

it('should not include runtime fields', async () => {
// First, make sure the mapping actually includes a runtime field
const fieldMapping = (await es.indices.getFieldMapping({
index: 'flstest',
fields: '*',
includeDefaults: true,
})) as FLSFieldMappingResponse;

expect(Object.keys(fieldMapping.flstest.mappings)).to.contain('runtime_customer_ssn');
expect(
fieldMapping.flstest.mappings.runtime_customer_ssn.mapping.runtime_customer_ssn.type
).to.eql('runtime');

// Now, make sure it's not returned here
const { body: actualFields } = (await supertest
.get('/internal/security/fields/flstest')
.set('kbn-xsrf', 'xxx')
.send()
.expect(200)) as { body: string[] };

const expectedFields = [
'customer_ssn',
'customer_ssn.keyword',
'customer_region',
'customer_region.keyword',
'customer_name',
'customer_name.keyword',
];

actualFields.sort();
expectedFields.sort();

expect(actualFields).to.eql(expectedFields);
});
});
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,6 @@
}
},
"type": "text"
},
"runtime_customer_ssn": {
"type": "runtime",
"runtime_type": "keyword",
"script": {
"source": "emit(doc['customer_ssn'].value + ' calculated at runtime')"
}
}
}
},
Expand Down

0 comments on commit 1f58db6

Please sign in to comment.