Load Capabilities from InjectedMetadata (#36710)

This commit is contained in:
Josh Dover 2019-05-21 13:58:07 -05:00 committed by GitHub
parent 1e25d1d5e5
commit d9c34caf95
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 251 additions and 185 deletions

View file

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [InjectedMetadataSetup](./kibana-plugin-public.injectedmetadatasetup.md) &gt; [getCapabilities](./kibana-plugin-public.injectedmetadatasetup.getcapabilities.md)
## InjectedMetadataSetup.getCapabilities property
<b>Signature:</b>
```typescript
getCapabilities: () => Capabilities;
```

View file

@ -17,6 +17,7 @@ export interface InjectedMetadataSetup
| Property | Type | Description |
| --- | --- | --- |
| [getBasePath](./kibana-plugin-public.injectedmetadatasetup.getbasepath.md) | <code>() =&gt; string</code> | |
| [getCapabilities](./kibana-plugin-public.injectedmetadatasetup.getcapabilities.md) | <code>() =&gt; Capabilities</code> | |
| [getCspConfig](./kibana-plugin-public.injectedmetadatasetup.getcspconfig.md) | <code>() =&gt; {`<p/>` warnLegacyBrowsers: boolean;`<p/>` }</code> | |
| [getInjectedVar](./kibana-plugin-public.injectedmetadatasetup.getinjectedvar.md) | <code>(name: string, defaultValue?: any) =&gt; unknown</code> | |
| [getInjectedVars](./kibana-plugin-public.injectedmetadatasetup.getinjectedvars.md) | <code>() =&gt; {`<p/>` [key: string]: unknown;`<p/>` }</code> | |

View file

@ -17,9 +17,6 @@
* under the License.
*/
// @ts-ignore
import fetchMock from 'fetch-mock/es5/client';
import { InjectedMetadataService } from '../../injected_metadata';
import { CapabilitiesService } from './capabilities_service';
import { basePathServiceMock } from '../../base_path/base_path_service.mock';
@ -29,74 +26,21 @@ describe('#start', () => {
basePath.addToPath.mockImplementation(str => str);
const injectedMetadata = new InjectedMetadataService({
injectedMetadata: {
vars: {
uiCapabilities: {
foo: { feature: true },
bar: { feature: true },
capabilities: {
catalogue: {},
management: {},
navLinks: {
app1: true,
app2: false,
},
foo: { feature: true },
bar: { feature: true },
},
} as any,
}).start();
const apps = [{ id: 'app1' }, { id: 'app2', capabilities: { app2: { feature: true } } }] as any;
beforeEach(() => {
fetchMock.post('/api/capabilities', (url: string, options: any) => ({
body: options.body,
status: 200,
}));
});
afterEach(() => {
fetchMock.restore();
});
it('calls backend API with merged capabilities', async () => {
const service = new CapabilitiesService();
await service.start({ apps, basePath, injectedMetadata });
expect(fetchMock.calls()).toMatchInlineSnapshot(`
Array [
Array [
"/api/capabilities",
Object {
"body": "{\\"capabilities\\":{\\"navLinks\\":{\\"app2\\":true,\\"app1\\":true},\\"management\\":{},\\"catalogue\\":{},\\"app2\\":{\\"feature\\":true}}}",
"credentials": "same-origin",
"headers": Object {
"kbn-xsrf": "xxx",
},
"method": "POST",
},
],
]
`);
});
it('returns capabilities from backend', async () => {
const service = new CapabilitiesService();
expect((await service.start({ apps, basePath, injectedMetadata })).capabilities)
.toMatchInlineSnapshot(`
Object {
"app2": Object {
"feature": true,
},
"catalogue": Object {},
"management": Object {},
"navLinks": Object {
"app1": true,
"app2": true,
},
}
`);
});
it('filters available apps based on returned navLinks', async () => {
fetchMock.post(
'/api/capabilities',
(url: string, options: any) => ({
body: JSON.stringify({ capabilities: { navLinks: { app1: true, app2: false } } }),
status: 200,
}),
{ overwriteRoutes: true }
);
const service = new CapabilitiesService();
expect((await service.start({ apps, basePath, injectedMetadata })).availableApps).toEqual([
{ id: 'app1' },

View file

@ -19,7 +19,6 @@
import { deepFreeze, RecursiveReadonly } from '../../utils/deep_freeze';
import { MixedApp } from '../application_service';
import { mergeCapabilities } from './merge_capabilities';
import { InjectedMetadataStart } from '../../injected_metadata';
import { BasePathStart } from '../../base_path';
@ -76,38 +75,7 @@ export interface CapabilitiesStart {
*/
export class CapabilitiesService {
public async start({ apps, basePath, injectedMetadata }: StartDeps): Promise<CapabilitiesStart> {
const mergedCapabilities = mergeCapabilities(
// Custom capabilites for new platform apps
...apps.filter(app => app.capabilities).map(app => app.capabilities!),
// Generate navLink capabilities for all apps
...apps.map(app => ({ navLinks: { [app.id]: true } }))
);
// NOTE: should replace `fetch` with browser HTTP service once it exists
const res = await fetch(basePath.addToPath('/api/capabilities'), {
method: 'POST',
body: JSON.stringify({ capabilities: mergedCapabilities }),
headers: {
'kbn-xsrf': 'xxx',
},
credentials: 'same-origin',
});
if (res.status === 401) {
return {
availableApps: [],
capabilities: deepFreeze({
navLinks: {},
management: {},
catalogue: {},
}),
};
} else if (res.status !== 200) {
throw new Error(`Capabilities check failed.`);
}
const body = await res.json();
const capabilities = deepFreeze(body.capabilities as Capabilities);
const capabilities = deepFreeze(injectedMetadata.getCapabilities());
const availableApps = apps.filter(app => capabilities.navLinks[app.id]);
return {

View file

@ -22,6 +22,7 @@ const createSetupContractMock = () => {
const setupContract: jest.Mocked<InjectedMetadataSetup> = {
getBasePath: jest.fn(),
getKibanaVersion: jest.fn(),
getCapabilities: jest.fn(),
getCspConfig: jest.fn(),
getLegacyMetadata: jest.fn(),
getPlugins: jest.fn(),
@ -29,6 +30,7 @@ const createSetupContractMock = () => {
getInjectedVars: jest.fn(),
getKibanaBuildNumber: jest.fn(),
};
setupContract.getCapabilities.mockReturnValue({} as any);
setupContract.getCspConfig.mockReturnValue({ warnLegacyBrowsers: true });
setupContract.getKibanaVersion.mockReturnValue('kibanaVersion');
setupContract.getLegacyMetadata.mockReturnValue({

View file

@ -21,6 +21,7 @@ import { get } from 'lodash';
import { DiscoveredPlugin, PluginName } from '../../server';
import { UiSettingsState } from '../ui_settings';
import { deepFreeze } from '../utils/deep_freeze';
import { Capabilities } from '..';
/** @public */
export interface LegacyNavLink {
@ -48,6 +49,7 @@ export interface InjectedMetadataParams {
id: PluginName;
plugin: DiscoveredPlugin;
}>;
capabilities: Capabilities;
legacyMetadata: {
app: unknown;
translations: unknown;
@ -97,6 +99,10 @@ export class InjectedMetadataService {
return this.state.version;
},
getCapabilities: () => {
return this.state.capabilities;
},
getCspConfig: () => {
return this.state.csp;
},
@ -133,6 +139,7 @@ export interface InjectedMetadataSetup {
getBasePath: () => string;
getKibanaBuildNumber: () => number;
getKibanaVersion: () => string;
getCapabilities: () => Capabilities;
getCspConfig: () => {
warnLegacyBrowsers: boolean;
};

View file

@ -221,6 +221,7 @@ export interface InjectedMetadataParams {
id: PluginName;
plugin: DiscoveredPlugin;
}>;
capabilities: Capabilities;
legacyMetadata: {
app: unknown;
translations: unknown;
@ -246,6 +247,8 @@ export interface InjectedMetadataSetup {
// (undocumented)
getBasePath: () => string;
// (undocumented)
getCapabilities: () => Capabilities;
// (undocumented)
getCspConfig: () => {
warnLegacyBrowsers: boolean;
};
@ -452,8 +455,8 @@ export interface UiSettingsState {
// Warnings were encountered during analysis:
//
// src/core/public/injected_metadata/injected_metadata_service.ts:48:7 - (ae-forgotten-export) The symbol "PluginName" needs to be exported by the entry point index.d.ts
// src/core/public/injected_metadata/injected_metadata_service.ts:49:7 - (ae-forgotten-export) The symbol "DiscoveredPlugin" needs to be exported by the entry point index.d.ts
// src/core/public/injected_metadata/injected_metadata_service.ts:49:7 - (ae-forgotten-export) The symbol "PluginName" needs to be exported by the entry point index.d.ts
// src/core/public/injected_metadata/injected_metadata_service.ts:50:7 - (ae-forgotten-export) The symbol "DiscoveredPlugin" needs to be exported by the entry point index.d.ts
// (No @packageDocumentation comment for this package)

View file

@ -100,6 +100,7 @@ const coreSystem = new CoreSystem({
csp: {
warnLegacyBrowsers: false,
},
capabilities: uiCapabilities,
uiPlugins: [],
vars: {
kbnIndex: '.kibana',

View file

@ -85,4 +85,15 @@ describe('capabilitiesMixin', () => {
expect(mockRegisterCapabilitiesRoute.mock.calls[0][2]).toEqual([mockModifier1, mockModifier2]);
});
it('exposes request#getCapabilities for retrieving legacy capabilities', async () => {
const kbnServer = getKbnServer();
jest.spyOn(server, 'decorate');
await capabilitiesMixin(kbnServer, server);
expect(server.decorate).toHaveBeenCalledWith(
'request',
'getCapabilities',
expect.any(Function)
);
});
});

View file

@ -23,6 +23,7 @@ import { Capabilities } from '../../../core/public';
import KbnServer from '../kbn_server';
import { registerCapabilitiesRoute } from './capabilities_route';
import { mergeCapabilities } from './merge_capabilities';
import { resolveCapabilities } from './resolve_capabilities';
export type CapabilitiesModifier = (
request: Request,
@ -48,6 +49,19 @@ export async function capabilitiesMixin(kbnServer: KbnServer, server: Server) {
))
);
server.decorate('request', 'getCapabilities', function() {
// Get legacy nav links
const navLinks = server.getUiNavLinks().reduce(
(acc, spec) => ({
...acc,
[spec._id]: true,
}),
{} as Record<string, boolean>
);
return resolveCapabilities(this, modifiers, defaultCapabilities, { navLinks });
});
registerCapabilitiesRoute(server, defaultCapabilities, modifiers);
});
}

View file

@ -20,9 +20,9 @@
import Joi from 'joi';
import { Server } from 'hapi';
import { CapabilitiesModifier } from '.';
import { Capabilities } from '../../../core/public';
import { mergeCapabilities } from './merge_capabilities';
import { CapabilitiesModifier } from './capabilities_mixin';
import { resolveCapabilities } from './resolve_capabilities';
export const registerCapabilitiesRoute = (
server: Server,
@ -40,15 +40,14 @@ export const registerCapabilitiesRoute = (
},
},
async handler(request) {
let { capabilities } = request.payload as { capabilities: Capabilities };
capabilities = mergeCapabilities({ ...defaultCapabilities }, capabilities);
for (const provider of modifiers) {
capabilities = await provider(request, capabilities);
}
const { capabilities } = request.payload as { capabilities: Capabilities };
return {
capabilities,
capabilities: await resolveCapabilities(
request,
modifiers,
defaultCapabilities,
capabilities
),
};
},
});

View file

@ -19,10 +19,10 @@
import { Capabilities } from '../../../core/public';
export const mergeCapabilities = (...sources: Capabilities[]): Capabilities =>
export const mergeCapabilities = (...sources: Array<Partial<Capabilities>>): Capabilities =>
sources.reduce(
(capabilities, source) => {
Object.entries(source).forEach(([key, value]) => {
(capabilities: Capabilities, source) => {
Object.entries(source).forEach(([key, value = {}]) => {
capabilities[key] = {
...value,
...capabilities[key],

View file

@ -0,0 +1,34 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { Request } from 'hapi';
import { Capabilities } from '../../../core/public';
import { mergeCapabilities } from './merge_capabilities';
import { CapabilitiesModifier } from './capabilities_mixin';
export const resolveCapabilities = (
request: Request,
modifiers: CapabilitiesModifier[],
...capabilities: Array<Partial<Capabilities>>
) =>
modifiers.reduce(
async (resolvedCaps, modifier) => modifier(request, await resolvedCaps),
Promise.resolve(mergeCapabilities(...capabilities))
);

View file

@ -39,6 +39,7 @@ import {
SavedObjectsSchema,
SavedObjectsManagement,
} from './saved_objects';
import { Capabilities } from '../../core/public';
export interface KibanaConfig {
get<T>(key: string): T;
@ -71,12 +72,14 @@ declare module 'hapi' {
scopedTutorialContextFactory: (...args: any[]) => any
) => void;
savedObjectsManagement(): SavedObjectsManagement;
getUiNavLinks(): Array<{ _id: string }>;
}
interface Request {
getSavedObjectsClient(): SavedObjectsClient;
getBasePath(): string;
getUiSettingsService(): any;
getCapabilities(): Promise<Capabilities>;
}
interface ResponseToolkit {

View file

@ -252,6 +252,8 @@ export function uiRenderMixin(kbnServer, server, config) {
uiPlugins,
legacyMetadata,
capabilities: await request.getCapabilities(),
},
});

View file

@ -50,7 +50,6 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions)
...xPackFunctionalTestsConfig.get('kbnTestServer.serverArgs'),
...disabledPlugins.map(key => `--xpack.${key}.enabled=false`),
`--plugin-path=${path.join(__dirname, 'fixtures', 'plugins', 'foo_plugin')}`,
'--optimize.enabled=false',
],
},
};

View file

@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import axios, { AxiosInstance } from 'axios';
import cheerio from 'cheerio';
import { UICapabilities } from 'ui/capabilities';
import { format as formatUrl } from 'url';
import util from 'util';
@ -42,21 +43,23 @@ export class UICapabilitiesService {
public async get({
credentials,
navLinks,
spaceId,
}: {
credentials?: BasicCredentials;
navLinks?: Record<string, boolean>;
spaceId?: string;
}): Promise<GetUICapabilitiesResult> {
const spaceUrlPrefix = spaceId ? `/s/${spaceId}` : '';
this.log.debug(`requesting ${spaceUrlPrefix}/api/capabilities to get the uiCapabilities`);
const requestOptions = credentials ? { auth: credentials } : {};
const response = await this.axios.post(
`${spaceUrlPrefix}/api/capabilities`,
{ capabilities: { navLinks } },
requestOptions
);
this.log.debug(`requesting ${spaceUrlPrefix}/app/kibana to parse the uiCapabilities`);
const requestHeaders = credentials
? {
Authorization: `Basic ${Buffer.from(
`${credentials.username}:${credentials.password}`
).toString('base64')}`,
}
: {};
const response = await this.axios.get(`${spaceUrlPrefix}/app/kibana`, {
headers: requestHeaders,
});
if (response.status === 302 && response.headers.location === '/') {
return {
@ -65,6 +68,13 @@ export class UICapabilitiesService {
};
}
if (response.status === 404) {
return {
success: false,
failureReason: GetUICapabilitiesFailureReason.NotFound,
};
}
if (response.status !== 200) {
throw new Error(
`Expected status code of 200, received ${response.status} ${
@ -73,10 +83,25 @@ export class UICapabilitiesService {
);
}
return {
success: true,
value: response.data.capabilities,
};
const dom = cheerio.load(response.data.toString());
const element = dom('kbn-injected-metadata');
if (!element) {
throw new Error('Unable to find "kbn-injected-metadata" element');
}
const dataAttrJson = element.attr('data');
try {
const dataAttr = JSON.parse(dataAttrJson);
return {
success: true,
value: dataAttr.capabilities as UICapabilities,
};
} catch (err) {
throw new Error(
`Unable to parse JSON from the kbn-injected-metadata data attribute: ${dataAttrJson}`
);
}
}
}

View file

@ -7,9 +7,11 @@
import expect from '@kbn/expect';
import { mapValues } from 'lodash';
import { KibanaFunctionalTestDefaultProviders } from '../../../types/providers';
import { UICapabilitiesService } from '../../common/services/ui_capabilities';
import {
GetUICapabilitiesFailureReason,
UICapabilitiesService,
} from '../../common/services/ui_capabilities';
import { UserAtSpaceScenarios } from '../scenarios';
import { assertDeeplyFalse } from '../../common/lib/assert_deeply_false';
// eslint-disable-next-line import/no-default-export
export default function catalogueTests({ getService }: KibanaFunctionalTestDefaultProviders) {
@ -24,12 +26,10 @@ export default function catalogueTests({ getService }: KibanaFunctionalTestDefau
credentials: { username: user.username, password: user.password },
spaceId: space.id,
});
expect(uiCapabilities.success).to.be(true);
expect(uiCapabilities.value).to.have.property('catalogue');
switch (scenario.id) {
case 'superuser at everything_space': {
expect(uiCapabilities.success).to.be(true);
expect(uiCapabilities.value).to.have.property('catalogue');
// everything is enabled
const expected = mapValues(uiCapabilities.value!.catalogue, () => true);
expect(uiCapabilities.value!.catalogue).to.eql(expected);
@ -41,6 +41,8 @@ export default function catalogueTests({ getService }: KibanaFunctionalTestDefau
case 'global_read at everything_space':
case 'dual_privileges_read at everything_space':
case 'everything_space_read at everything_space': {
expect(uiCapabilities.success).to.be(true);
expect(uiCapabilities.value).to.have.property('catalogue');
// everything except ml and monitoring is enabled
const expected = mapValues(
uiCapabilities.value!.catalogue,
@ -58,13 +60,16 @@ export default function catalogueTests({ getService }: KibanaFunctionalTestDefau
case 'dual_privileges_read at nothing_space':
case 'nothing_space_all at nothing_space':
case 'nothing_space_read at nothing_space': {
expect(uiCapabilities.success).to.be(true);
expect(uiCapabilities.value).to.have.property('catalogue');
// everything is disabled
const expected = mapValues(uiCapabilities.value!.catalogue, () => false);
expect(uiCapabilities.value!.catalogue).to.eql(expected);
break;
}
// if we don't have access at the space itself, all ui
// capabilities should be false
// if we don't have access at the space itself, we're
// redirected to the space selector and the ui capabilities
// are lagely irrelevant because they won't be consumed
case 'no_kibana_privileges at everything_space':
case 'no_kibana_privileges at nothing_space':
case 'legacy_all at everything_space':
@ -73,7 +78,10 @@ export default function catalogueTests({ getService }: KibanaFunctionalTestDefau
case 'everything_space_read at nothing_space':
case 'nothing_space_all at everything_space':
case 'nothing_space_read at everything_space':
assertDeeplyFalse(uiCapabilities.value!.catalogue);
expect(uiCapabilities.success).to.be(false);
expect(uiCapabilities.failureReason).to.be(
GetUICapabilitiesFailureReason.RedirectedToRoot
);
break;
default:
throw new UnreachableError(scenario);

View file

@ -6,9 +6,11 @@
import expect from '@kbn/expect';
import { KibanaFunctionalTestDefaultProviders } from '../../../types/providers';
import { UICapabilitiesService } from '../../common/services/ui_capabilities';
import {
GetUICapabilitiesFailureReason,
UICapabilitiesService,
} from '../../common/services/ui_capabilities';
import { UserAtSpaceScenarios } from '../scenarios';
import { assertDeeplyFalse } from '../../common/lib/assert_deeply_false';
// eslint-disable-next-line import/no-default-export
export default function fooTests({ getService }: KibanaFunctionalTestDefaultProviders) {
@ -23,14 +25,14 @@ export default function fooTests({ getService }: KibanaFunctionalTestDefaultProv
credentials: { username: user.username, password: user.password },
spaceId: space.id,
});
expect(uiCapabilities.success).to.be(true);
expect(uiCapabilities.value).to.have.property('foo');
switch (scenario.id) {
// these users have a read/write view
case 'superuser at everything_space':
case 'global_all at everything_space':
case 'dual_privileges_all at everything_space':
case 'everything_space_all at everything_space':
expect(uiCapabilities.success).to.be(true);
expect(uiCapabilities.value).to.have.property('foo');
expect(uiCapabilities.value!.foo).to.eql({
create: true,
edit: true,
@ -42,6 +44,8 @@ export default function fooTests({ getService }: KibanaFunctionalTestDefaultProv
case 'global_read at everything_space':
case 'dual_privileges_read at everything_space':
case 'everything_space_read at everything_space':
expect(uiCapabilities.success).to.be(true);
expect(uiCapabilities.value).to.have.property('foo');
expect(uiCapabilities.value!.foo).to.eql({
create: false,
edit: false,
@ -58,6 +62,8 @@ export default function fooTests({ getService }: KibanaFunctionalTestDefaultProv
case 'dual_privileges_read at nothing_space':
case 'nothing_space_all at nothing_space':
case 'nothing_space_read at nothing_space':
expect(uiCapabilities.success).to.be(true);
expect(uiCapabilities.value).to.have.property('foo');
expect(uiCapabilities.value!.foo).to.eql({
create: false,
edit: false,
@ -65,8 +71,9 @@ export default function fooTests({ getService }: KibanaFunctionalTestDefaultProv
show: false,
});
break;
// if we don't have access at the space itself, all ui
// capabilities should be false
// if we don't have access at the space itself, we're
// redirected to the space selector and the ui capabilities
// are largely irrelevant because they won't be consumed
case 'no_kibana_privileges at everything_space':
case 'no_kibana_privileges at nothing_space':
case 'legacy_all at everything_space':
@ -75,7 +82,10 @@ export default function fooTests({ getService }: KibanaFunctionalTestDefaultProv
case 'everything_space_read at nothing_space':
case 'nothing_space_all at everything_space':
case 'nothing_space_read at everything_space':
assertDeeplyFalse(uiCapabilities.value!.foo);
expect(uiCapabilities.success).to.be(false);
expect(uiCapabilities.failureReason).to.be(
GetUICapabilitiesFailureReason.RedirectedToRoot
);
break;
default:
throw new UnreachableError(scenario);

View file

@ -8,7 +8,10 @@ import expect from '@kbn/expect';
import { KibanaFunctionalTestDefaultProviders } from '../../../types/providers';
import { NavLinksBuilder } from '../../common/nav_links_builder';
import { FeaturesService } from '../../common/services';
import { UICapabilitiesService } from '../../common/services/ui_capabilities';
import {
GetUICapabilitiesFailureReason,
UICapabilitiesService,
} from '../../common/services/ui_capabilities';
import { UserAtSpaceScenarios } from '../scenarios';
// eslint-disable-next-line import/no-default-export
@ -29,13 +32,12 @@ export default function navLinksTests({ getService }: KibanaFunctionalTestDefaul
const uiCapabilities = await uiCapabilitiesService.get({
credentials: { username: user.username, password: user.password },
navLinks: navLinksBuilder.all(),
spaceId: space.id,
});
expect(uiCapabilities.success).to.be(true);
expect(uiCapabilities.value).to.have.property('navLinks');
switch (scenario.id) {
case 'superuser at everything_space':
expect(uiCapabilities.success).to.be(true);
expect(uiCapabilities.value).to.have.property('navLinks');
expect(uiCapabilities.value!.navLinks).to.eql(navLinksBuilder.all());
break;
case 'global_all at everything_space':
@ -44,6 +46,8 @@ export default function navLinksTests({ getService }: KibanaFunctionalTestDefaul
case 'global_read at everything_space':
case 'everything_space_all at everything_space':
case 'everything_space_read at everything_space':
expect(uiCapabilities.success).to.be(true);
expect(uiCapabilities.value).to.have.property('navLinks');
expect(uiCapabilities.value!.navLinks).to.eql(
navLinksBuilder.except('ml', 'monitoring')
);
@ -55,10 +59,10 @@ export default function navLinksTests({ getService }: KibanaFunctionalTestDefaul
case 'global_read at nothing_space':
case 'nothing_space_all at nothing_space':
case 'nothing_space_read at nothing_space':
expect(uiCapabilities.success).to.be(true);
expect(uiCapabilities.value).to.have.property('navLinks');
expect(uiCapabilities.value!.navLinks).to.eql(navLinksBuilder.only('management'));
break;
// these users have no access to any navLinks except management
// which is not a navLink that ever gets disabled.
case 'no_kibana_privileges at everything_space':
case 'no_kibana_privileges at nothing_space':
case 'legacy_all at everything_space':
@ -67,7 +71,10 @@ export default function navLinksTests({ getService }: KibanaFunctionalTestDefaul
case 'everything_space_read at nothing_space':
case 'nothing_space_all at everything_space':
case 'nothing_space_read at everything_space':
expect(uiCapabilities.value!.navLinks).to.eql(navLinksBuilder.only('management'));
expect(uiCapabilities.success).to.be(false);
expect(uiCapabilities.failureReason).to.be(
GetUICapabilitiesFailureReason.RedirectedToRoot
);
break;
default:
throw new UnreachableError(scenario);

View file

@ -7,9 +7,11 @@ import expect from '@kbn/expect';
import { mapValues } from 'lodash';
import { KibanaFunctionalTestDefaultProviders } from '../../../types/providers';
import { SavedObjectsManagementBuilder } from '../../common/saved_objects_management_builder';
import { UICapabilitiesService } from '../../common/services/ui_capabilities';
import {
UICapabilitiesService,
GetUICapabilitiesFailureReason,
} from '../../common/services/ui_capabilities';
import { UserAtSpaceScenarios } from '../scenarios';
import { assertDeeplyFalse } from '../../common/lib/assert_deeply_false';
const savedObjectsManagementBuilder = new SavedObjectsManagementBuilder(true);
@ -28,8 +30,6 @@ export default function savedObjectsManagementTests({
credentials: { username: user.username, password: user.password },
spaceId: space.id,
});
expect(uiCapabilities.success).to.be(true);
expect(uiCapabilities.value).to.have.property('savedObjectsManagement');
switch (scenario.id) {
case 'superuser at everything_space':
case 'global_all at everything_space':
@ -76,7 +76,10 @@ export default function savedObjectsManagementTests({
case 'everything_space_read at nothing_space':
case 'nothing_space_all at everything_space':
case 'nothing_space_read at everything_space':
assertDeeplyFalse(uiCapabilities.value!.savedObjectsManagement);
expect(uiCapabilities.success).to.be(false);
expect(uiCapabilities.failureReason).to.be(
GetUICapabilitiesFailureReason.RedirectedToRoot
);
break;
default:

View file

@ -7,9 +7,11 @@
import expect from '@kbn/expect';
import { mapValues } from 'lodash';
import { KibanaFunctionalTestDefaultProviders } from '../../../types/providers';
import { UICapabilitiesService } from '../../common/services/ui_capabilities';
import {
GetUICapabilitiesFailureReason,
UICapabilitiesService,
} from '../../common/services/ui_capabilities';
import { UserScenarios } from '../scenarios';
import { assertDeeplyFalse } from '../../common/lib/assert_deeply_false';
// eslint-disable-next-line import/no-default-export
export default function catalogueTests({ getService }: KibanaFunctionalTestDefaultProviders) {
@ -24,10 +26,10 @@ export default function catalogueTests({ getService }: KibanaFunctionalTestDefau
password: scenario.password,
},
});
expect(uiCapabilities.success).to.be(true);
expect(uiCapabilities.value).to.have.property('catalogue');
switch (scenario.username) {
case 'superuser': {
expect(uiCapabilities.success).to.be(true);
expect(uiCapabilities.value).to.have.property('catalogue');
// everything is enabled
const expected = mapValues(uiCapabilities.value!.catalogue, () => true);
expect(uiCapabilities.value!.catalogue).to.eql(expected);
@ -37,6 +39,8 @@ export default function catalogueTests({ getService }: KibanaFunctionalTestDefau
case 'read':
case 'dual_privileges_all':
case 'dual_privileges_read': {
expect(uiCapabilities.success).to.be(true);
expect(uiCapabilities.value).to.have.property('catalogue');
// everything except ml and monitoring is enabled
const expected = mapValues(
uiCapabilities.value!.catalogue,
@ -47,6 +51,8 @@ export default function catalogueTests({ getService }: KibanaFunctionalTestDefau
}
case 'foo_all':
case 'foo_read': {
expect(uiCapabilities.success).to.be(true);
expect(uiCapabilities.value).to.have.property('catalogue');
// only foo is enabled
const expected = mapValues(
uiCapabilities.value!.catalogue,
@ -55,10 +61,11 @@ export default function catalogueTests({ getService }: KibanaFunctionalTestDefau
expect(uiCapabilities.value!.catalogue).to.eql(expected);
break;
}
// these users have no access to any ui capabilities
// these users have no access to even get the ui capabilities
case 'legacy_all':
case 'no_kibana_privileges':
assertDeeplyFalse(uiCapabilities.value!.catalogue);
expect(uiCapabilities.success).to.be(false);
expect(uiCapabilities.failureReason).to.be(GetUICapabilitiesFailureReason.NotFound);
break;
default:
throw new UnreachableError(scenario);

View file

@ -6,9 +6,11 @@
import expect from '@kbn/expect';
import { KibanaFunctionalTestDefaultProviders } from '../../../types/providers';
import { UICapabilitiesService } from '../../common/services/ui_capabilities';
import {
GetUICapabilitiesFailureReason,
UICapabilitiesService,
} from '../../common/services/ui_capabilities';
import { UserScenarios } from '../scenarios';
import { assertDeeplyFalse } from '../../common/lib/assert_deeply_false';
// eslint-disable-next-line import/no-default-export
export default function fooTests({ getService }: KibanaFunctionalTestDefaultProviders) {
@ -23,16 +25,14 @@ export default function fooTests({ getService }: KibanaFunctionalTestDefaultProv
password: scenario.password,
},
});
expect(uiCapabilities.success).to.be(true);
expect(uiCapabilities.value).to.have.property('foo');
switch (scenario.username) {
// these users have a read/write view of Foo
case 'superuser':
case 'all':
case 'dual_privileges_all':
case 'foo_all':
expect(uiCapabilities.success).to.be(true);
expect(uiCapabilities.value).to.have.property('foo');
expect(uiCapabilities.value!.foo).to.eql({
create: true,
edit: true,
@ -44,6 +44,8 @@ export default function fooTests({ getService }: KibanaFunctionalTestDefaultProv
case 'read':
case 'dual_privileges_read':
case 'foo_read':
expect(uiCapabilities.success).to.be(true);
expect(uiCapabilities.value).to.have.property('foo');
expect(uiCapabilities.value!.foo).to.eql({
create: false,
edit: false,
@ -51,10 +53,11 @@ export default function fooTests({ getService }: KibanaFunctionalTestDefaultProv
show: true,
});
break;
// these users have no access to any ui capabilities
// these users have no access to even get the ui capabilities
case 'legacy_all':
case 'no_kibana_privileges':
assertDeeplyFalse(uiCapabilities.value!.foo);
expect(uiCapabilities.success).to.be(false);
expect(uiCapabilities.failureReason).to.be(GetUICapabilitiesFailureReason.NotFound);
break;
// all other users can't do anything with Foo
default:

View file

@ -8,7 +8,10 @@ import expect from '@kbn/expect';
import { KibanaFunctionalTestDefaultProviders } from '../../../types/providers';
import { NavLinksBuilder } from '../../common/nav_links_builder';
import { FeaturesService } from '../../common/services';
import { UICapabilitiesService } from '../../common/services/ui_capabilities';
import {
GetUICapabilitiesFailureReason,
UICapabilitiesService,
} from '../../common/services/ui_capabilities';
import { UserScenarios } from '../scenarios';
// eslint-disable-next-line import/no-default-export
@ -30,34 +33,35 @@ export default function navLinksTests({ getService }: KibanaFunctionalTestDefaul
username: scenario.username,
password: scenario.password,
},
navLinks: navLinksBuilder.all(),
});
expect(uiCapabilities.success).to.be(true);
expect(uiCapabilities.value).to.have.property('navLinks');
switch (scenario.username) {
case 'superuser':
expect(uiCapabilities.success).to.be(true);
expect(uiCapabilities.value).to.have.property('navLinks');
expect(uiCapabilities.value!.navLinks).to.eql(navLinksBuilder.all());
break;
case 'all':
case 'read':
case 'dual_privileges_all':
case 'dual_privileges_read':
expect(uiCapabilities.success).to.be(true);
expect(uiCapabilities.value).to.have.property('navLinks');
expect(uiCapabilities.value!.navLinks).to.eql(
navLinksBuilder.except('ml', 'monitoring')
);
break;
case 'foo_all':
case 'foo_read':
expect(uiCapabilities.success).to.be(true);
expect(uiCapabilities.value).to.have.property('navLinks');
expect(uiCapabilities.value!.navLinks).to.eql(
navLinksBuilder.only('management', 'foo')
);
break;
// these users have no access to any navLinks except management
// which is not a navLink that ever gets disabled.
case 'legacy_all':
case 'no_kibana_privileges':
expect(uiCapabilities.value!.navLinks).to.eql(navLinksBuilder.only('management'));
expect(uiCapabilities.success).to.be(false);
expect(uiCapabilities.failureReason).to.be(GetUICapabilitiesFailureReason.NotFound);
break;
default:
throw new UnreachableError(scenario);

View file

@ -8,9 +8,11 @@ import expect from '@kbn/expect';
import { mapValues } from 'lodash';
import { KibanaFunctionalTestDefaultProviders } from '../../../types/providers';
import { SavedObjectsManagementBuilder } from '../../common/saved_objects_management_builder';
import { UICapabilitiesService } from '../../common/services/ui_capabilities';
import {
UICapabilitiesService,
GetUICapabilitiesFailureReason,
} from '../../common/services/ui_capabilities';
import { UserScenarios } from '../scenarios';
import { assertDeeplyFalse } from '../../common/lib/assert_deeply_false';
const savedObjectsManagementBuilder = new SavedObjectsManagementBuilder(false);
@ -29,8 +31,6 @@ export default function savedObjectsManagementTests({
password: scenario.password,
},
});
expect(uiCapabilities.success).to.be(true);
expect(uiCapabilities.value).to.have.property('savedObjectsManagement');
switch (scenario.username) {
case 'superuser':
case 'all':
@ -53,6 +53,8 @@ export default function savedObjectsManagementTests({
break;
case 'foo_all':
case 'foo_read':
expect(uiCapabilities.success).to.be(true);
expect(uiCapabilities.value).to.have.property('savedObjectsManagement');
expect(uiCapabilities.value!.savedObjectsManagement).to.eql(
savedObjectsManagementBuilder.build({
all: [],
@ -64,7 +66,8 @@ export default function savedObjectsManagementTests({
// these users have no access to any ui capabilities
case 'legacy_all':
case 'no_kibana_privileges':
assertDeeplyFalse(uiCapabilities.value!.savedObjectsManagement);
expect(uiCapabilities.success).to.be(false);
expect(uiCapabilities.failureReason).to.be(GetUICapabilitiesFailureReason.NotFound);
break;
default:
throw new UnreachableError(scenario);

View file

@ -25,10 +25,7 @@ export default function navLinksTests({ getService }: KibanaFunctionalTestDefaul
SpaceScenarios.forEach(scenario => {
it(`${scenario.name}`, async () => {
const uiCapabilities = await uiCapabilitiesService.get({
navLinks: navLinksBuilder.all(),
spaceId: scenario.id,
});
const uiCapabilities = await uiCapabilitiesService.get({ spaceId: scenario.id });
switch (scenario.id) {
case 'everything_space':
expect(uiCapabilities.success).to.be(true);