From 8b0fa36d579429f9659d26f542d93b0e0ec9ff5c Mon Sep 17 00:00:00 2001 From: kobelb Date: Tue, 22 May 2018 09:31:30 -0400 Subject: [PATCH] Adding filters in find for authorized types --- .../saved_objects/service/lib/repository.js | 5 +- .../service/lib/repository.test.js | 9 +- .../service/lib/search_dsl/query_params.js | 14 +- .../lib/search_dsl/query_params.test.js | 228 +++++++++++++++++- .../service/lib/search_dsl/search_dsl.js | 5 +- .../service/lib/search_dsl/search_dsl.test.js | 8 +- .../service/saved_objects_client.js | 1 + .../secure_saved_objects_client.js | 6 + 8 files changed, 259 insertions(+), 17 deletions(-) diff --git a/src/server/saved_objects/service/lib/repository.js b/src/server/saved_objects/service/lib/repository.js index fb109f67401d8..87f1ff5f0c223 100644 --- a/src/server/saved_objects/service/lib/repository.js +++ b/src/server/saved_objects/service/lib/repository.js @@ -189,6 +189,7 @@ export class SavedObjectsRepository { * @property {string} [options.sortField] * @property {string} [options.sortOrder] * @property {Array} [options.fields] + * @property {Array} [options.filters] * @returns {promise} - { saved_objects: [{ id, type, version, attributes }], total, per_page, page } */ async find(options = {}) { @@ -201,6 +202,7 @@ export class SavedObjectsRepository { sortField, sortOrder, fields, + filters, } = options; if (searchFields && !Array.isArray(searchFields)) { @@ -224,7 +226,8 @@ export class SavedObjectsRepository { searchFields, type, sortField, - sortOrder + sortOrder, + filters, }) } }; diff --git a/src/server/saved_objects/service/lib/repository.test.js b/src/server/saved_objects/service/lib/repository.test.js index 2fb4a938a781e..67a510f6de082 100644 --- a/src/server/saved_objects/service/lib/repository.test.js +++ b/src/server/saved_objects/service/lib/repository.test.js @@ -366,13 +366,18 @@ describe('SavedObjectsRepository', () => { } }); - it('passes mappings, search, searchFields, type, sortField, and sortOrder to getSearchDsl', async () => { + it('passes mappings, search, searchFields, type, sortField, sortOrder and filters to getSearchDsl', async () => { const relevantOpts = { search: 'foo*', searchFields: ['foo'], type: 'bar', sortField: 'name', - sortOrder: 'desc' + sortOrder: 'desc', + filters: [{ + terms: { + type: ['foo', 'bar'] + } + }] }; await savedObjectsRepository.find(relevantOpts); diff --git a/src/server/saved_objects/service/lib/search_dsl/query_params.js b/src/server/saved_objects/service/lib/search_dsl/query_params.js index 3b29aa41b6d22..306fd4b1d6dbf 100644 --- a/src/server/saved_objects/service/lib/search_dsl/query_params.js +++ b/src/server/saved_objects/service/lib/search_dsl/query_params.js @@ -29,17 +29,13 @@ function getFieldsForTypes(searchFields, types) { * @param {Array} searchFields * @return {Object} */ -export function getQueryParams(mappings, type, search, searchFields) { - if (!type && !search) { - return {}; - } - - const bool = {}; +export function getQueryParams(mappings, type, search, searchFields, filters = []) { + const bool = { + filter: [...filters], + }; if (type) { - bool.filter = [ - { term: { type } } - ]; + bool.filter = [...bool.filter, { term: { type } }]; } if (search) { diff --git a/src/server/saved_objects/service/lib/search_dsl/query_params.test.js b/src/server/saved_objects/service/lib/search_dsl/query_params.test.js index 19316fd46a588..bbf52f3a05113 100644 --- a/src/server/saved_objects/service/lib/search_dsl/query_params.test.js +++ b/src/server/saved_objects/service/lib/search_dsl/query_params.test.js @@ -40,7 +40,13 @@ describe('searchDsl/queryParams', () => { describe('{}', () => { it('searches for everything', () => { expect(getQueryParams(MAPPINGS)) - .toEqual({}); + .toEqual({ + query: { + bool: { + filter: [] + } + } + }); }); }); @@ -61,12 +67,54 @@ describe('searchDsl/queryParams', () => { }); }); + describe('{type,filters}', () => { + it('includes filters and a term filter for type', () => { + expect(getQueryParams(MAPPINGS, 'saved', null, null, [{ terms: { foo: ['bar', 'baz' ] } }])) + .toEqual({ + query: { + bool: { + filter: [ + { terms: { foo: ['bar', 'baz' ] } }, + { + term: { type: 'saved' } + } + ] + } + } + }); + }); + }); + describe('{search}', () => { it('includes just a sqs query', () => { expect(getQueryParams(MAPPINGS, null, 'us*')) .toEqual({ query: { bool: { + filter: [], + must: [ + { + simple_query_string: { + query: 'us*', + all_fields: true + } + } + ] + } + } + }); + }); + }); + + describe('{search,filters}', () => { + it('includes filters and a sqs query', () => { + expect(getQueryParams(MAPPINGS, null, 'us*', null, [{ terms: { foo: ['bar', 'baz' ] } }])) + .toEqual({ + query: { + bool: { + filter: [ + { terms: { foo: ['bar', 'baz' ] } } + ], must: [ { simple_query_string: { @@ -104,12 +152,37 @@ describe('searchDsl/queryParams', () => { }); }); + describe('{type,search,filters}', () => { + it('includes bool with sqs query, filters and term filter for type', () => { + expect(getQueryParams(MAPPINGS, 'saved', 'y*', null, [{ terms: { foo: ['bar', 'baz' ] } }])) + .toEqual({ + query: { + bool: { + filter: [ + { terms: { foo: ['bar', 'baz' ] } }, + { term: { type: 'saved' } } + ], + must: [ + { + simple_query_string: { + query: 'y*', + all_fields: true + } + } + ] + } + } + }); + }); + }); + describe('{search,searchFields}', () => { it('includes all types for field', () => { expect(getQueryParams(MAPPINGS, null, 'y*', ['title'])) .toEqual({ query: { bool: { + filter: [], must: [ { simple_query_string: { @@ -131,6 +204,7 @@ describe('searchDsl/queryParams', () => { .toEqual({ query: { bool: { + filter: [], must: [ { simple_query_string: { @@ -152,6 +226,85 @@ describe('searchDsl/queryParams', () => { .toEqual({ query: { bool: { + filter: [], + must: [ + { + simple_query_string: { + query: 'y*', + fields: [ + 'type.title', + 'pending.title', + 'saved.title', + 'type.title.raw', + 'pending.title.raw', + 'saved.title.raw', + ] + } + } + ] + } + } + }); + }); + }); + + describe('{search,searchFields,filters}', () => { + it('specifies filters and includes all types for field', () => { + expect(getQueryParams(MAPPINGS, null, 'y*', ['title'], [{ terms: { foo: ['bar', 'baz' ] } }])) + .toEqual({ + query: { + bool: { + filter: [ + { terms: { foo: ['bar', 'baz' ] } }, + ], + must: [ + { + simple_query_string: { + query: 'y*', + fields: [ + 'type.title', + 'pending.title', + 'saved.title' + ] + } + } + ] + } + } + }); + }); + it('specifies filters and supports field boosting', () => { + expect(getQueryParams(MAPPINGS, null, 'y*', ['title^3'], [{ terms: { foo: ['bar', 'baz' ] } }])) + .toEqual({ + query: { + bool: { + filter: [ + { terms: { foo: ['bar', 'baz' ] } }, + ], + must: [ + { + simple_query_string: { + query: 'y*', + fields: [ + 'type.title^3', + 'pending.title^3', + 'saved.title^3' + ] + } + } + ] + } + } + }); + }); + it('specifies filters and supports field and multi-field', () => { + expect(getQueryParams(MAPPINGS, null, 'y*', ['title', 'title.raw'], [{ terms: { foo: ['bar', 'baz' ] } }])) + .toEqual({ + query: { + bool: { + filter: [ + { terms: { foo: ['bar', 'baz' ] } }, + ], must: [ { simple_query_string: { @@ -242,4 +395,77 @@ describe('searchDsl/queryParams', () => { }); }); }); + + describe('{type,search,searchFields,filters}', () => { + it('includes specified filters, type filter and sqs with field list', () => { + expect(getQueryParams(MAPPINGS, 'saved', 'y*', ['title'], [{ terms: { foo: ['bar', 'baz' ] } }])) + .toEqual({ + query: { + bool: { + filter: [ + { terms: { foo: ['bar', 'baz' ] } }, + { term: { type: 'saved' } } + ], + must: [ + { + simple_query_string: { + query: 'y*', + fields: [ + 'saved.title' + ] + } + } + ] + } + } + }); + }); + it('supports fields pointing to multi-fields', () => { + expect(getQueryParams(MAPPINGS, 'saved', 'y*', ['title.raw'], [{ terms: { foo: ['bar', 'baz' ] } }])) + .toEqual({ + query: { + bool: { + filter: [ + { terms: { foo: ['bar', 'baz' ] } }, + { term: { type: 'saved' } } + ], + must: [ + { + simple_query_string: { + query: 'y*', + fields: [ + 'saved.title.raw' + ] + } + } + ] + } + } + }); + }); + it('supports multiple search fields', () => { + expect(getQueryParams(MAPPINGS, 'saved', 'y*', ['title', 'title.raw'], [{ terms: { foo: ['bar', 'baz' ] } }])) + .toEqual({ + query: { + bool: { + filter: [ + { terms: { foo: ['bar', 'baz' ] } }, + { term: { type: 'saved' } } + ], + must: [ + { + simple_query_string: { + query: 'y*', + fields: [ + 'saved.title', + 'saved.title.raw' + ] + } + } + ] + } + } + }); + }); + }); }); diff --git a/src/server/saved_objects/service/lib/search_dsl/search_dsl.js b/src/server/saved_objects/service/lib/search_dsl/search_dsl.js index a76f91eb11b0b..fccddcf06fa8e 100644 --- a/src/server/saved_objects/service/lib/search_dsl/search_dsl.js +++ b/src/server/saved_objects/service/lib/search_dsl/search_dsl.js @@ -9,7 +9,8 @@ export function getSearchDsl(mappings, options = {}) { search, searchFields, sortField, - sortOrder + sortOrder, + filters, } = options; if (!type && sortField) { @@ -21,7 +22,7 @@ export function getSearchDsl(mappings, options = {}) { } return { - ...getQueryParams(mappings, type, search, searchFields), + ...getQueryParams(mappings, type, search, searchFields, filters), ...getSortingParams(mappings, type, sortField, sortOrder), }; } diff --git a/src/server/saved_objects/service/lib/search_dsl/search_dsl.test.js b/src/server/saved_objects/service/lib/search_dsl/search_dsl.test.js index 4a147aa7873a9..1102bb0ba58ec 100644 --- a/src/server/saved_objects/service/lib/search_dsl/search_dsl.test.js +++ b/src/server/saved_objects/service/lib/search_dsl/search_dsl.test.js @@ -27,13 +27,16 @@ describe('getSearchDsl', () => { }); describe('passes control', () => { - it('passes (mappings, type, search, searchFields) to getQueryParams', () => { + it('passes (mappings, type, search, searchFields, filters) to getQueryParams', () => { const spy = sandbox.spy(queryParamsNS, 'getQueryParams'); const mappings = { type: { properties: {} } }; const opts = { type: 'foo', search: 'bar', - searchFields: ['baz'] + searchFields: ['baz'], + filters: [ + { terms: { foo: ['bar', 'baz'] } } + ] }; getSearchDsl(mappings, opts); @@ -44,6 +47,7 @@ describe('getSearchDsl', () => { opts.type, opts.search, opts.searchFields, + opts.filters, ); }); diff --git a/src/server/saved_objects/service/saved_objects_client.js b/src/server/saved_objects/service/saved_objects_client.js index 396cdd07a42d2..51c1c02f5f074 100644 --- a/src/server/saved_objects/service/saved_objects_client.js +++ b/src/server/saved_objects/service/saved_objects_client.js @@ -123,6 +123,7 @@ export class SavedObjectsClient { * @property {string} [options.sortField] * @property {string} [options.sortOrder] * @property {Array} [options.fields] + * @property {Array} [options.filters] * @returns {promise} - { saved_objects: [{ id, type, version, attributes }], total, per_page, page } */ async find(options = {}) { diff --git a/x-pack/plugins/security/server/lib/saved_objects_client/secure_saved_objects_client.js b/x-pack/plugins/security/server/lib/saved_objects_client/secure_saved_objects_client.js index 6a1d963a8f5ad..ae73880f087ad 100644 --- a/x-pack/plugins/security/server/lib/saved_objects_client/secure_saved_objects_client.js +++ b/x-pack/plugins/security/server/lib/saved_objects_client/secure_saved_objects_client.js @@ -52,6 +52,12 @@ export class SecureSavedObjectsClient { throw this.errors.decorateForbiddenError(new Error(`Not authorized to search any types`)); } + options.filters = [...(options.filters || []), { + terms: { + 'type': authorizedTypes + } + }]; + return await this._repository.find(options); }