Skip to content

Commit

Permalink
Merge branch '7.x' into backport/7.x/pr-78403
Browse files Browse the repository at this point in the history
  • Loading branch information
elasticmachine authored Sep 25, 2020
2 parents 24b733b + d2610d0 commit 4f08f08
Show file tree
Hide file tree
Showing 25 changed files with 251 additions and 130 deletions.
10 changes: 10 additions & 0 deletions src/core/server/capabilities/capabilities_service.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
import type { PublicMethodsOf } from '@kbn/utility-types';
import { CapabilitiesService, CapabilitiesSetup, CapabilitiesStart } from './capabilities_service';
import { Capabilities } from './types';

const createSetupContractMock = () => {
const setupContract: jest.Mocked<CapabilitiesSetup> = {
Expand All @@ -34,6 +35,14 @@ const createStartContractMock = () => {
return setupContract;
};

const createCapabilitiesMock = (): Capabilities => {
return {
navLinks: {},
management: {},
catalogue: {},
};
};

type CapabilitiesServiceContract = PublicMethodsOf<CapabilitiesService>;
const createMock = () => {
const mocked: jest.Mocked<CapabilitiesServiceContract> = {
Expand All @@ -47,4 +56,5 @@ export const capabilitiesServiceMock = {
create: createMock,
createSetupContract: createSetupContractMock,
createStartContract: createStartContractMock,
createCapabilities: createCapabilitiesMock,
};
1 change: 1 addition & 0 deletions src/core/server/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export { metricsServiceMock } from './metrics/metrics_service.mock';
export { renderingMock } from './rendering/rendering_service.mock';
export { statusServiceMock } from './status/status_service.mock';
export { contextServiceMock } from './context/context_service.mock';
export { capabilitiesServiceMock } from './capabilities/capabilities_service.mock';

export function pluginInitializerContextConfigMock<T>(config: T) {
const globalConfig: SharedGlobalConfig = {
Expand Down
12 changes: 6 additions & 6 deletions x-pack/plugins/enterprise_search/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,11 @@ export class EnterpriseSearchPlugin implements Plugin {
category: DEFAULT_APP_CATEGORIES.enterpriseSearch,
mount: async (params: AppMountParameters) => {
const kibanaDeps = await this.getKibanaDeps(core, params);
const pluginData = this.getPluginData();

const { chrome, http } = kibanaDeps.core;
chrome.docTitle.change(ENTERPRISE_SEARCH_PLUGIN.NAME);

await this.getInitialData(http);
const pluginData = this.getPluginData();

const { renderApp } = await import('./applications');
const { EnterpriseSearch } = await import('./applications/enterprise_search');
Expand All @@ -80,11 +80,11 @@ export class EnterpriseSearchPlugin implements Plugin {
category: DEFAULT_APP_CATEGORIES.enterpriseSearch,
mount: async (params: AppMountParameters) => {
const kibanaDeps = await this.getKibanaDeps(core, params);
const pluginData = this.getPluginData();

const { chrome, http } = kibanaDeps.core;
chrome.docTitle.change(APP_SEARCH_PLUGIN.NAME);

await this.getInitialData(http);
const pluginData = this.getPluginData();

const { renderApp } = await import('./applications');
const { AppSearch } = await import('./applications/app_search');
Expand All @@ -101,11 +101,11 @@ export class EnterpriseSearchPlugin implements Plugin {
category: DEFAULT_APP_CATEGORIES.enterpriseSearch,
mount: async (params: AppMountParameters) => {
const kibanaDeps = await this.getKibanaDeps(core, params);
const pluginData = this.getPluginData();

const { chrome, http } = kibanaDeps.core;
chrome.docTitle.change(APP_SEARCH_PLUGIN.NAME);

await this.getInitialData(http);
const pluginData = this.getPluginData();

const { renderApp, renderHeaderActions } = await import('./applications');
const { WorkplaceSearch } = await import('./applications/workplace_search');
Expand Down
9 changes: 8 additions & 1 deletion x-pack/plugins/global_search/server/services/context.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { of } from 'rxjs';
import { Capabilities } from 'src/core/server';
import {
savedObjectsTypeRegistryMock,
savedObjectsClientMock,
elasticsearchServiceMock,
uiSettingsServiceMock,
capabilitiesServiceMock,
} from '../../../../../src/core/server/mocks';

const createContextMock = () => {
const createContextMock = (capabilities: Partial<Capabilities> = {}) => {
return {
core: {
savedObjects: {
Expand All @@ -26,6 +29,10 @@ const createContextMock = () => {
uiSettings: {
client: uiSettingsServiceMock.createClient(),
},
capabilities: of({
...capabilitiesServiceMock.createCapabilities(),
...capabilities,
} as Capabilities),
},
};
};
Expand Down
4 changes: 4 additions & 0 deletions x-pack/plugins/global_search/server/services/context.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,15 @@ describe('getContextFactory', () => {
expect(coreStart.uiSettings.asScopedToClient).toHaveBeenCalledTimes(1);
expect(coreStart.uiSettings.asScopedToClient).toHaveBeenCalledWith(soClient);

expect(coreStart.capabilities.resolveCapabilities).toHaveBeenCalledTimes(1);
expect(coreStart.capabilities.resolveCapabilities).toHaveBeenCalledWith(request);

expect(context).toEqual({
core: {
savedObjects: expect.any(Object),
elasticsearch: expect.any(Object),
uiSettings: expect.any(Object),
capabilities: expect.any(Object),
},
});
});
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/global_search/server/services/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { from } from 'rxjs';
import { CoreStart, KibanaRequest } from 'src/core/server';
import { GlobalSearchProviderContext } from '../types';

Expand All @@ -30,6 +31,7 @@ export const getContextFactory = (coreStart: CoreStart) => (
uiSettings: {
client: coreStart.uiSettings.asScopedToClient(soClient),
},
capabilities: from(coreStart.capabilities.resolveCapabilities(request)),
},
};
};
2 changes: 2 additions & 0 deletions x-pack/plugins/global_search/server/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
ILegacyScopedClusterClient,
IUiSettingsClient,
SavedObjectsClientContract,
Capabilities,
} from 'src/core/server';
import {
GlobalSearchBatchedResults,
Expand Down Expand Up @@ -52,6 +53,7 @@ export interface GlobalSearchProviderContext {
uiSettings: {
client: IUiSettingsClient;
};
capabilities: Observable<Capabilities>;
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { SavedObjectsFindResult, SavedObjectsType, SavedObjectTypeRegistry } from 'src/core/server';
import {
SavedObjectsFindResult,
SavedObjectsType,
SavedObjectTypeRegistry,
Capabilities,
} from 'src/core/server';
import { mapToResult, mapToResults } from './map_object_to_result';

const createType = (props: Partial<SavedObjectsType>): SavedObjectsType => {
Expand Down Expand Up @@ -111,18 +116,17 @@ describe('mapToResult', () => {

describe('mapToResults', () => {
let typeRegistry: SavedObjectTypeRegistry;
let capabilities: Capabilities;

beforeEach(() => {
typeRegistry = new SavedObjectTypeRegistry();
});

it('converts savedObjects to results', () => {
typeRegistry.registerType(
createType({
name: 'typeA',
management: {
defaultSearchField: 'title',
getInAppUrl: (obj) => ({ path: `/type-a/${obj.id}`, uiCapabilitiesPath: '' }),
getInAppUrl: (obj) => ({ path: `/type-a/${obj.id}`, uiCapabilitiesPath: 'test.typeA' }),
},
})
);
Expand All @@ -131,7 +135,7 @@ describe('mapToResults', () => {
name: 'typeB',
management: {
defaultSearchField: 'description',
getInAppUrl: (obj) => ({ path: `/type-b/${obj.id}`, uiCapabilitiesPath: 'foo' }),
getInAppUrl: (obj) => ({ path: `/type-b/${obj.id}`, uiCapabilitiesPath: 'test.typeB' }),
},
})
);
Expand All @@ -140,11 +144,37 @@ describe('mapToResults', () => {
name: 'typeC',
management: {
defaultSearchField: 'excerpt',
getInAppUrl: (obj) => ({ path: `/type-c/${obj.id}`, uiCapabilitiesPath: 'bar' }),
getInAppUrl: (obj) => ({ path: `/type-c/${obj.id}`, uiCapabilitiesPath: 'test.typeC' }),
},
})
);
typeRegistry.registerType(
createType({
name: 'inaccessibleType',
management: {
defaultSearchField: 'excerpt',
getInAppUrl: (obj) => ({
path: `/inaccessible-type/${obj.id}`,
uiCapabilitiesPath: 'test.inaccessibleType',
}),
},
})
);

capabilities = {
navLinks: {},
management: {},
catalogue: {},
test: {
typeA: true,
typeB: true,
typeC: true,
inacessibleType: false,
},
};
});

it('converts savedObjects to results', () => {
const results = [
createObject(
{
Expand Down Expand Up @@ -181,7 +211,7 @@ describe('mapToResults', () => {
),
];

expect(mapToResults(results, typeRegistry)).toEqual([
expect(mapToResults(results, typeRegistry, capabilities)).toEqual([
{
id: 'resultA',
title: 'titleA',
Expand All @@ -205,4 +235,41 @@ describe('mapToResults', () => {
},
]);
});

it('filters results without permissions', () => {
const results = [
createObject(
{
id: 'resultA',
type: 'typeA',
score: 100,
},
{
title: 'titleA',
field: 'noise',
}
),
createObject(
{
id: 'inaccessibleResult',
type: 'inaccessibleType',
score: 92,
},
{
excerpt: 'inaccessibleTitle',
title: 'inaccessible',
}
),
];

expect(mapToResults(results, typeRegistry, capabilities)).toEqual([
{
id: 'resultA',
title: 'titleA',
type: 'typeA',
url: '/type-a/resultA',
score: 100,
},
]);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,36 @@
* you may not use this file except in compliance with the Elastic License.
*/

import get from 'lodash/get';
import {
SavedObjectsType,
ISavedObjectTypeRegistry,
SavedObjectsFindResult,
Capabilities,
} from 'src/core/server';
import { GlobalSearchProviderResult } from '../../../../global_search/server';

export const mapToResults = (
objects: Array<SavedObjectsFindResult<unknown>>,
registry: ISavedObjectTypeRegistry
registry: ISavedObjectTypeRegistry,
capabilities: Capabilities
): GlobalSearchProviderResult[] => {
return objects.map((obj) => mapToResult(obj, registry.getType(obj.type)!));
return objects
.filter((obj) => isAccessible(obj, registry.getType(obj.type)!, capabilities))
.map((obj) => mapToResult(obj, registry.getType(obj.type)!));
};

const isAccessible = (
object: SavedObjectsFindResult<unknown>,
type: SavedObjectsType,
capabilities: Capabilities
): boolean => {
const { getInAppUrl } = type.management ?? {};
if (getInAppUrl === undefined) {
throw new Error('Trying to map an object from a type without management metadata');
}
const { uiCapabilitiesPath } = getInAppUrl(object);
return Boolean(get(capabilities, uiCapabilitiesPath) ?? false);
};

export const mapToResult = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,20 @@ const createFindResponse = (
total: results.length,
});

const createType = (props: Partial<SavedObjectsType>): SavedObjectsType => {
const createType = (
props: Pick<SavedObjectsType, 'name'> & Partial<SavedObjectsType>
): SavedObjectsType => {
return {
name: 'type',
hidden: false,
namespaceType: 'single',
mappings: { properties: {} },
...props,
management: {
defaultSearchField: 'field',
getInAppUrl: (obj) => ({ path: `/object/${obj.id}`, uiCapabilitiesPath: '' }),
getInAppUrl: (obj) => ({
path: `/object/${obj.id}`,
uiCapabilitiesPath: `types.${props.name}`,
}),
...props.management,
},
};
Expand Down Expand Up @@ -82,7 +86,7 @@ describe('savedObjectsResultProvider', () => {
name: 'typeA',
management: {
defaultSearchField: 'title',
getInAppUrl: (obj) => ({ path: `/type-a/${obj.id}`, uiCapabilitiesPath: '' }),
getInAppUrl: (obj) => ({ path: `/type-a/${obj.id}`, uiCapabilitiesPath: 'test.typeA' }),
},
})
);
Expand All @@ -91,12 +95,17 @@ describe('savedObjectsResultProvider', () => {
name: 'typeB',
management: {
defaultSearchField: 'description',
getInAppUrl: (obj) => ({ path: `/type-b/${obj.id}`, uiCapabilitiesPath: 'foo' }),
getInAppUrl: (obj) => ({ path: `/type-b/${obj.id}`, uiCapabilitiesPath: 'test.typeB' }),
},
})
);

context = globalSearchPluginMock.createProviderContext();
context = globalSearchPluginMock.createProviderContext({
test: {
typeA: true,
typeB: true,
},
});
context.core.savedObjects.client.find.mockResolvedValue(createFindResponse([]));
context.core.savedObjects.typeRegistry = registry as any;
});
Expand Down
Loading

0 comments on commit 4f08f08

Please sign in to comment.