mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Added scope field to features config. (#191634)
## Summary Kibana needs to more tightly control the set of visible features within a space, in order to support the new solution-based navigation. Added `scope` field to the features configuration. This enhancement is intended to prevent new features from appearing in Space Visibility Toggles. ### Checklist - [x] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios __Fixes: https://github.com/elastic/kibana/issues/191299__ ## Release Note Added `scope` field to the features configuration. This enhancement is intended to prevent new features from appearing in Space Visibility Toggles. --------- Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
303d8f27e4
commit
a71c9ba38a
74 changed files with 491 additions and 58 deletions
|
@ -59,6 +59,11 @@ of features within the management screens.
|
|||
|See <<example-3-discover,Example 3>>
|
||||
|The set of subfeatures that enables finer access control than the `all` and `read` feature privileges. These options are only available in the Gold subscription level and higher.
|
||||
|
||||
|`scope` (optional)
|
||||
|`string[]`
|
||||
|`["spaces", "security"]`
|
||||
| Default `security`. Scope identifies if feature should appear in both Spaces Visibility Toggles and Security Feature Privileges or only in Security Feature Privileges.
|
||||
|
||||
|===
|
||||
|
||||
==== Privilege definition
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
FeaturesPluginSetup,
|
||||
// PluginStartContract as FeaturesPluginStart,
|
||||
} from '@kbn/features-plugin/server';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
import { FEATURE_PRIVILEGES_PLUGIN_ID } from '../common';
|
||||
|
||||
export interface FeatureControlExampleDeps {
|
||||
|
@ -27,6 +28,7 @@ export class FeatureControlsPluginExample
|
|||
name: 'Feature Plugin Examples',
|
||||
category: DEFAULT_APP_CATEGORIES.management,
|
||||
app: ['FeaturePluginExample'],
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
privileges: {
|
||||
all: {
|
||||
app: ['FeaturePluginExample'],
|
||||
|
|
|
@ -17,6 +17,7 @@ import {
|
|||
DEFAULT_APP_CATEGORIES,
|
||||
} from '@kbn/core/server';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
import type { AIAssistantManagementSelectionConfig } from './config';
|
||||
import type {
|
||||
AIAssistantManagementSelectionPluginServerDependenciesSetup,
|
||||
|
@ -111,6 +112,7 @@ export class AIAssistantManagementSelectionPlugin
|
|||
order: 8600,
|
||||
app: [],
|
||||
category: DEFAULT_APP_CATEGORIES.management,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
management: {
|
||||
kibana: [
|
||||
'aiAssistantManagementSelection',
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
import type { KibanaFeatureConfig } from '@kbn/features-plugin/common';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { DEFAULT_APP_CATEGORIES } from '@kbn/core/server';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
import { PLUGIN_FEATURE, PLUGIN_ID } from '../common/constants';
|
||||
import { guideStateSavedObjectsType, pluginStateSavedObjectsType } from './saved_objects';
|
||||
|
||||
|
@ -19,6 +20,7 @@ export const GUIDED_ONBOARDING_FEATURE: KibanaFeatureConfig = {
|
|||
defaultMessage: 'Setup guides',
|
||||
}),
|
||||
category: DEFAULT_APP_CATEGORIES.management,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: [PLUGIN_ID],
|
||||
privileges: {
|
||||
all: {
|
||||
|
|
|
@ -12,6 +12,7 @@ import { DEFAULT_APP_CATEGORIES } from '@kbn/core-application-common';
|
|||
import { PluginSetupContract as AlertingSetup } from '@kbn/alerting-plugin/server';
|
||||
import { FeaturesPluginSetup } from '@kbn/features-plugin/server';
|
||||
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
import { ruleType as alwaysFiringRule } from './rule_types/always_firing';
|
||||
import { ruleType as peopleInSpaceRule } from './rule_types/astros';
|
||||
import { ruleType as patternRule } from './rule_types/pattern';
|
||||
|
@ -41,6 +42,7 @@ export class AlertingExamplePlugin implements Plugin<void, void, AlertingExample
|
|||
insightsAndAlerting: ['triggersActions'],
|
||||
},
|
||||
category: DEFAULT_APP_CATEGORIES.management,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
alerting: [alwaysFiringRule.id, peopleInSpaceRule.id, INDEX_THRESHOLD_ID],
|
||||
privileges: {
|
||||
all: {
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { DEFAULT_APP_CATEGORIES } from '@kbn/core-application-common';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
import { type BaseKibanaFeatureConfig } from '../types';
|
||||
import { APP_ID, ASSISTANT_FEATURE_ID } from '../constants';
|
||||
|
||||
|
@ -21,6 +22,7 @@ export const getAssistantBaseKibanaFeature = (): BaseKibanaFeatureConfig => ({
|
|||
),
|
||||
order: 1100,
|
||||
category: DEFAULT_APP_CATEGORIES.security,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: [ASSISTANT_FEATURE_ID, 'kibana'],
|
||||
catalogue: [APP_ID],
|
||||
minimumLicense: 'enterprise',
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import { DEFAULT_APP_CATEGORIES } from '@kbn/core-application-common';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
|
||||
import { APP_ID, ATTACK_DISCOVERY_FEATURE_ID } from '../constants';
|
||||
import { type BaseKibanaFeatureConfig } from '../types';
|
||||
|
@ -21,6 +22,7 @@ export const getAttackDiscoveryBaseKibanaFeature = (): BaseKibanaFeatureConfig =
|
|||
),
|
||||
order: 1100,
|
||||
category: DEFAULT_APP_CATEGORIES.security,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: [ATTACK_DISCOVERY_FEATURE_ID, 'kibana'],
|
||||
catalogue: [APP_ID],
|
||||
minimumLicense: 'enterprise',
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { DEFAULT_APP_CATEGORIES } from '@kbn/core-application-common';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
import type { BaseKibanaFeatureConfig } from '../types';
|
||||
import { APP_ID, CASES_FEATURE_ID } from '../constants';
|
||||
import type { CasesFeatureParams } from './types';
|
||||
|
@ -27,6 +28,7 @@ export const getCasesBaseKibanaFeature = ({
|
|||
),
|
||||
order: 1100,
|
||||
category: DEFAULT_APP_CATEGORIES.security,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: [CASES_FEATURE_ID, 'kibana'],
|
||||
catalogue: [APP_ID],
|
||||
cases: [APP_ID],
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
|
||||
import { DEFAULT_APP_CATEGORIES } from '@kbn/core-application-common';
|
||||
import {
|
||||
|
@ -52,6 +53,7 @@ export const getSecurityBaseKibanaFeature = ({
|
|||
),
|
||||
order: 1100,
|
||||
category: DEFAULT_APP_CATEGORIES.security,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: [APP_ID, CLOUD_POSTURE_APP_ID, CLOUD_DEFEND_APP_ID, 'kibana'],
|
||||
catalogue: [APP_ID],
|
||||
management: {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import { DEFAULT_APP_CATEGORIES } from '@kbn/core-application-common';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
import {
|
||||
ACTION_SAVED_OBJECT_TYPE,
|
||||
ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE,
|
||||
|
@ -28,6 +29,7 @@ export const ACTIONS_FEATURE = {
|
|||
defaultMessage: 'Actions and Connectors',
|
||||
}),
|
||||
category: DEFAULT_APP_CATEGORIES.management,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: [],
|
||||
order: FEATURE_ORDER,
|
||||
management: {
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { KibanaFeatureConfig } from '@kbn/features-plugin/common';
|
||||
import { DEFAULT_APP_CATEGORIES } from '@kbn/core/server';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
import {
|
||||
MAINTENANCE_WINDOW_FEATURE_ID,
|
||||
MAINTENANCE_WINDOW_API_PRIVILEGES,
|
||||
|
@ -20,6 +21,7 @@ export const maintenanceWindowFeature: KibanaFeatureConfig = {
|
|||
defaultMessage: 'Maintenance Windows',
|
||||
}),
|
||||
category: DEFAULT_APP_CATEGORIES.management,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: [],
|
||||
management: {
|
||||
insightsAndAlerting: ['maintenanceWindows'],
|
||||
|
|
|
@ -16,6 +16,7 @@ test('returns rule settings feature with query delay subfeature if serverless',
|
|||
label: 'Management',
|
||||
order: 5000,
|
||||
},
|
||||
scope: ['spaces', 'security'],
|
||||
id: 'rulesSettings',
|
||||
management: {
|
||||
insightsAndAlerting: ['triggersActions'],
|
||||
|
@ -125,6 +126,7 @@ test('returns rule settings feature without query delay subfeature if not server
|
|||
label: 'Management',
|
||||
order: 5000,
|
||||
},
|
||||
scope: ['spaces', 'security'],
|
||||
id: 'rulesSettings',
|
||||
management: {
|
||||
insightsAndAlerting: ['triggersActions'],
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { KibanaFeatureConfig } from '@kbn/features-plugin/common';
|
||||
import { DEFAULT_APP_CATEGORIES } from '@kbn/core/server';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
import {
|
||||
RULES_SETTINGS_FEATURE_ID,
|
||||
READ_FLAPPING_SETTINGS_SUB_FEATURE_ID,
|
||||
|
@ -25,6 +26,7 @@ export function getRulesSettingsFeature(isServerless: boolean): KibanaFeatureCon
|
|||
defaultMessage: 'Rules Settings',
|
||||
}),
|
||||
category: DEFAULT_APP_CATEGORIES.management,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: [],
|
||||
management: {
|
||||
insightsAndAlerting: ['triggersActions'],
|
||||
|
|
|
@ -78,6 +78,10 @@ it('Provides a feature declaration ', () => {
|
|||
],
|
||||
},
|
||||
},
|
||||
"scope": Array [
|
||||
"spaces",
|
||||
"security",
|
||||
],
|
||||
"subFeatures": Array [],
|
||||
}
|
||||
`);
|
||||
|
@ -152,6 +156,10 @@ it(`Calls on Reporting whether to include Generate PDF as a sub-feature`, () =>
|
|||
],
|
||||
},
|
||||
},
|
||||
"scope": Array [
|
||||
"spaces",
|
||||
"security",
|
||||
],
|
||||
"subFeatures": Array [
|
||||
Object {
|
||||
"name": "Reporting",
|
||||
|
|
|
@ -9,6 +9,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import { DEFAULT_APP_CATEGORIES } from '@kbn/core/server';
|
||||
import { KibanaFeatureConfig } from '@kbn/features-plugin/common';
|
||||
import { ReportingStart } from '@kbn/reporting-plugin/server/types';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
|
||||
/*
|
||||
* Register Canvas as a Kibana feature,
|
||||
|
@ -22,6 +23,7 @@ export function getCanvasFeature(plugins: { reporting?: ReportingStart }): Kiban
|
|||
name: 'Canvas',
|
||||
order: 300,
|
||||
category: DEFAULT_APP_CATEGORIES.kibana,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: ['canvas', 'kibana'],
|
||||
management: {
|
||||
...(includeReporting ? { insightsAndAlerting: ['reporting'] } : {}),
|
||||
|
|
|
@ -11,6 +11,7 @@ import type { KibanaFeatureConfig } from '@kbn/features-plugin/common';
|
|||
import { hiddenTypes as filesSavedObjectTypes } from '@kbn/files-plugin/server/saved_objects';
|
||||
import { DEFAULT_APP_CATEGORIES } from '@kbn/core/server';
|
||||
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
import { APP_ID, FEATURE_ID } from '../common/constants';
|
||||
import { createUICapabilities, getApiTags } from '../common';
|
||||
|
||||
|
@ -32,6 +33,7 @@ export const getCasesKibanaFeature = (): KibanaFeatureConfig => {
|
|||
defaultMessage: 'Cases',
|
||||
}),
|
||||
category: DEFAULT_APP_CATEGORIES.management,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: [],
|
||||
order: FEATURE_ORDER,
|
||||
management: {
|
||||
|
|
|
@ -19,6 +19,7 @@ import {
|
|||
import { CustomIntegrationsPluginSetup } from '@kbn/custom-integrations-plugin/server';
|
||||
import { DataPluginStart } from '@kbn/data-plugin/server/plugin';
|
||||
import { ENTERPRISE_SEARCH_APP_ID } from '@kbn/deeplinks-search';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
import { FeaturesPluginSetup } from '@kbn/features-plugin/server';
|
||||
import { GlobalSearchPluginSetup } from '@kbn/global-search-plugin/server';
|
||||
import type { GuidedOnboardingPluginSetup } from '@kbn/guided-onboarding-plugin/server';
|
||||
|
@ -202,6 +203,7 @@ export class EnterpriseSearchPlugin implements Plugin {
|
|||
name: SEARCH_PRODUCT_NAME,
|
||||
order: 0,
|
||||
category: DEFAULT_APP_CATEGORIES.enterpriseSearch,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: ['kibana', ...PLUGIN_IDS],
|
||||
catalogue: PLUGIN_IDS,
|
||||
privileges: {
|
||||
|
|
|
@ -10,7 +10,7 @@ export type { FeatureKibanaPrivileges } from './feature_kibana_privileges';
|
|||
export type { ElasticsearchFeatureConfig } from './elasticsearch_feature';
|
||||
export { ElasticsearchFeature } from './elasticsearch_feature';
|
||||
export type { KibanaFeatureConfig } from './kibana_feature';
|
||||
export { KibanaFeature } from './kibana_feature';
|
||||
export { KibanaFeature, KibanaFeatureScope } from './kibana_feature';
|
||||
export type {
|
||||
SubFeatureConfig,
|
||||
SubFeaturePrivilegeConfig,
|
||||
|
|
|
@ -12,6 +12,16 @@ import { FeatureKibanaPrivileges } from './feature_kibana_privileges';
|
|||
import { SubFeatureConfig, SubFeature as KibanaSubFeature } from './sub_feature';
|
||||
import { ReservedKibanaPrivilege } from './reserved_kibana_privilege';
|
||||
|
||||
/**
|
||||
* Enum for allowed feature scope values.
|
||||
* security - The feature is available in Security Feature Privileges.
|
||||
* spaces - The feature is available in the Spaces Visibility Toggles.
|
||||
*/
|
||||
export enum KibanaFeatureScope {
|
||||
Security = 'security',
|
||||
Spaces = 'spaces',
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for registering a feature.
|
||||
* Feature registration allows plugins to hide their applications with spaces,
|
||||
|
@ -149,6 +159,11 @@ export interface KibanaFeatureConfig {
|
|||
* are visible.
|
||||
*/
|
||||
hidden?: boolean;
|
||||
|
||||
/**
|
||||
* Indicates whether the feature is available in Security Feature Privileges and the Spaces Visibility Toggles.
|
||||
*/
|
||||
scope?: readonly KibanaFeatureScope[];
|
||||
}
|
||||
|
||||
export class KibanaFeature {
|
||||
|
@ -220,6 +235,10 @@ export class KibanaFeature {
|
|||
return this.config.reserved;
|
||||
}
|
||||
|
||||
public get scope() {
|
||||
return this.config.scope;
|
||||
}
|
||||
|
||||
public toRaw() {
|
||||
return { ...this.config } as KibanaFeatureConfig;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ export type {
|
|||
SubFeatureConfig,
|
||||
SubFeaturePrivilegeConfig,
|
||||
} from '../common';
|
||||
export { KibanaFeature } from '../common';
|
||||
export { KibanaFeature, KibanaFeatureScope } from '../common';
|
||||
|
||||
export type { FeaturesPluginSetup, FeaturesPluginStart } from './plugin';
|
||||
|
||||
|
|
|
@ -200,6 +200,25 @@ describe('FeatureRegistry', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('requires only a valid scope registered', () => {
|
||||
const feature: KibanaFeatureConfig = {
|
||||
id: 'test-feature',
|
||||
name: 'Test Feature',
|
||||
app: [],
|
||||
category: { id: 'foo', label: 'foo' },
|
||||
privileges: null,
|
||||
// @ts-expect-error
|
||||
scope: ['foo', 'bar'],
|
||||
};
|
||||
|
||||
const featureRegistry = new FeatureRegistry();
|
||||
expect(() =>
|
||||
featureRegistry.registerKibanaFeature(feature)
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Feature test-feature has unknown scope entries: foo, bar"`
|
||||
);
|
||||
});
|
||||
|
||||
it(`requires a value for privileges`, () => {
|
||||
const feature: KibanaFeatureConfig = {
|
||||
id: 'test-feature',
|
||||
|
|
|
@ -15,6 +15,7 @@ import {
|
|||
ElasticsearchFeatureConfig,
|
||||
ElasticsearchFeature,
|
||||
SubFeaturePrivilegeConfig,
|
||||
KibanaFeatureScope,
|
||||
} from '../common';
|
||||
import { validateKibanaFeature, validateElasticsearchFeature } from './feature_schema';
|
||||
import type { ConfigOverridesType } from './config';
|
||||
|
@ -41,6 +42,10 @@ export class FeatureRegistry {
|
|||
throw new Error(`Feature with id ${feature.id} is already registered.`);
|
||||
}
|
||||
|
||||
if (!feature.scope) {
|
||||
feature.scope = [KibanaFeatureScope.Security];
|
||||
}
|
||||
|
||||
const featureCopy = cloneDeep(feature);
|
||||
|
||||
this.kibanaFeatures[feature.id] = applyAutomaticPrivilegeGrants(featureCopy);
|
||||
|
|
|
@ -9,7 +9,7 @@ import { schema } from '@kbn/config-schema';
|
|||
|
||||
import { difference } from 'lodash';
|
||||
import { Capabilities as UICapabilities } from '@kbn/core/server';
|
||||
import { KibanaFeatureConfig } from '../common';
|
||||
import { KibanaFeatureConfig, KibanaFeatureScope } from '../common';
|
||||
import { FeatureKibanaPrivileges, ElasticsearchFeatureConfig } from '.';
|
||||
|
||||
// Each feature gets its own property on the UICapabilities object,
|
||||
|
@ -202,6 +202,7 @@ const kibanaFeatureSchema = schema.object({
|
|||
}),
|
||||
name: schema.string(),
|
||||
category: appCategorySchema,
|
||||
scope: schema.maybe(schema.arrayOf(schema.string(), { minSize: 1 })),
|
||||
description: schema.maybe(schema.string()),
|
||||
order: schema.maybe(schema.number()),
|
||||
excludeFromBasePrivileges: schema.maybe(schema.boolean()),
|
||||
|
@ -211,13 +212,23 @@ const kibanaFeatureSchema = schema.object({
|
|||
catalogue: schema.maybe(catalogueSchema),
|
||||
alerting: schema.maybe(alertingSchema),
|
||||
cases: schema.maybe(casesSchema),
|
||||
privileges: schema.oneOf([
|
||||
schema.literal(null),
|
||||
schema.object({
|
||||
all: schema.maybe(kibanaPrivilegeSchema),
|
||||
read: schema.maybe(kibanaPrivilegeSchema),
|
||||
// Features registered only for the spaces scope should not have a `privileges` property.
|
||||
// Such features are applicable only to the Spaces Visibility Toggles
|
||||
privileges: schema.conditional(
|
||||
schema.siblingRef('scope'),
|
||||
schema.arrayOf(schema.literal('spaces'), {
|
||||
minSize: 1,
|
||||
maxSize: 1,
|
||||
}),
|
||||
]),
|
||||
schema.literal(null),
|
||||
schema.oneOf([
|
||||
schema.literal(null),
|
||||
schema.object({
|
||||
all: schema.maybe(kibanaPrivilegeSchema),
|
||||
read: schema.maybe(kibanaPrivilegeSchema),
|
||||
}),
|
||||
])
|
||||
),
|
||||
subFeatures: schema.maybe(
|
||||
schema.conditional(
|
||||
schema.siblingRef('privileges'),
|
||||
|
@ -275,6 +286,14 @@ const elasticsearchFeatureSchema = schema.object({
|
|||
export function validateKibanaFeature(feature: KibanaFeatureConfig) {
|
||||
kibanaFeatureSchema.validate(feature);
|
||||
|
||||
const unknownScopesEntries = difference(feature.scope ?? [], Object.values(KibanaFeatureScope));
|
||||
|
||||
if (unknownScopesEntries.length) {
|
||||
throw new Error(
|
||||
`Feature ${feature.id} has unknown scope entries: ${unknownScopesEntries.join(', ')}`
|
||||
);
|
||||
}
|
||||
|
||||
// the following validation can't be enforced by the Joi schema, since it'd require us looking "up" the object graph for the list of valid value, which they explicitly forbid.
|
||||
const { app = [], management = {}, catalogue = [], alerting = [], cases = [] } = feature;
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { DEFAULT_APP_CATEGORIES } from '@kbn/core/server';
|
||||
import { KibanaFeatureScope } from '../common';
|
||||
import type { KibanaFeatureConfig, SubFeatureConfig } from '../common';
|
||||
|
||||
export interface BuildOSSFeaturesParams {
|
||||
|
@ -30,6 +31,7 @@ export const buildOSSFeatures = ({
|
|||
},
|
||||
order: 100,
|
||||
category: DEFAULT_APP_CATEGORIES.kibana,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: ['discover', 'kibana'],
|
||||
catalogue: ['discover'],
|
||||
privileges: {
|
||||
|
@ -125,6 +127,7 @@ export const buildOSSFeatures = ({
|
|||
},
|
||||
order: 700,
|
||||
category: DEFAULT_APP_CATEGORIES.kibana,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: ['visualize', 'lens', 'kibana'],
|
||||
catalogue: ['visualize'],
|
||||
privileges: {
|
||||
|
@ -189,6 +192,7 @@ export const buildOSSFeatures = ({
|
|||
},
|
||||
order: 200,
|
||||
category: DEFAULT_APP_CATEGORIES.kibana,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: ['dashboards', 'kibana'],
|
||||
catalogue: ['dashboard'],
|
||||
privileges: {
|
||||
|
@ -302,6 +306,7 @@ export const buildOSSFeatures = ({
|
|||
}),
|
||||
order: 1300,
|
||||
category: DEFAULT_APP_CATEGORIES.management,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: ['dev_tools', 'kibana'],
|
||||
catalogue: ['console', 'searchprofiler', 'grokdebugger'],
|
||||
privileges: {
|
||||
|
@ -338,6 +343,7 @@ export const buildOSSFeatures = ({
|
|||
}),
|
||||
order: 1500,
|
||||
category: DEFAULT_APP_CATEGORIES.management,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: ['kibana'],
|
||||
catalogue: ['advanced_settings'],
|
||||
management: {
|
||||
|
@ -377,6 +383,7 @@ export const buildOSSFeatures = ({
|
|||
}),
|
||||
order: 1600,
|
||||
category: DEFAULT_APP_CATEGORIES.management,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: ['kibana'],
|
||||
catalogue: ['indexPatterns'],
|
||||
management: {
|
||||
|
@ -416,6 +423,7 @@ export const buildOSSFeatures = ({
|
|||
}),
|
||||
order: 1600,
|
||||
category: DEFAULT_APP_CATEGORIES.management,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: ['kibana'],
|
||||
catalogue: [],
|
||||
management: {
|
||||
|
@ -455,6 +463,7 @@ export const buildOSSFeatures = ({
|
|||
}),
|
||||
order: 1600,
|
||||
category: DEFAULT_APP_CATEGORIES.management,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: ['kibana'],
|
||||
catalogue: [],
|
||||
privilegesTooltip: i18n.translate('xpack.features.filesSharedImagesPrivilegesTooltip', {
|
||||
|
@ -488,6 +497,7 @@ export const buildOSSFeatures = ({
|
|||
}),
|
||||
order: 1700,
|
||||
category: DEFAULT_APP_CATEGORIES.management,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: ['kibana'],
|
||||
catalogue: ['saved_objects'],
|
||||
management: {
|
||||
|
@ -529,6 +539,7 @@ export const buildOSSFeatures = ({
|
|||
}),
|
||||
order: 1750,
|
||||
category: DEFAULT_APP_CATEGORIES.management,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: ['kibana'],
|
||||
catalogue: [],
|
||||
privilegesTooltip: i18n.translate('xpack.features.savedQueryManagementTooltip', {
|
||||
|
|
|
@ -188,6 +188,9 @@ describe('Features Plugin', () => {
|
|||
"ui": Array [],
|
||||
},
|
||||
},
|
||||
"scope": Array [
|
||||
"security",
|
||||
],
|
||||
},
|
||||
"subFeatures": Array [],
|
||||
}
|
||||
|
|
|
@ -55,6 +55,7 @@ import type { SpacesPluginStart } from '@kbn/spaces-plugin/server';
|
|||
import type { SavedObjectTaggingStart } from '@kbn/saved-objects-tagging-plugin/server';
|
||||
|
||||
import { SECURITY_EXTENSION_ID } from '@kbn/core-saved-objects-server';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
|
||||
import type { FleetConfigType } from '../common/types';
|
||||
import type { FleetAuthz } from '../common';
|
||||
|
@ -317,6 +318,7 @@ export class FleetPlugin
|
|||
id: `fleetv2`,
|
||||
name: 'Fleet',
|
||||
category: DEFAULT_APP_CATEGORIES.management,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: [PLUGIN_ID],
|
||||
catalogue: ['fleet'],
|
||||
privilegesTooltip: i18n.translate('xpack.fleet.serverPlugin.privilegesTooltip', {
|
||||
|
@ -479,6 +481,7 @@ export class FleetPlugin
|
|||
id: 'fleet', // for BWC
|
||||
name: 'Integrations',
|
||||
category: DEFAULT_APP_CATEGORIES.management,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: [INTEGRATIONS_PLUGIN_ID],
|
||||
catalogue: ['fleet'],
|
||||
privileges: {
|
||||
|
|
|
@ -12,6 +12,7 @@ import { LicensingPluginSetup, LicensingPluginStart } from '@kbn/licensing-plugi
|
|||
import { HomeServerPluginSetup } from '@kbn/home-plugin/server';
|
||||
import { FeaturesPluginSetup } from '@kbn/features-plugin/server';
|
||||
import { ContentManagementServerSetup } from '@kbn/content-management-plugin/server';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
import { LicenseState } from './lib/license_state';
|
||||
import { registerSearchRoute } from './routes/search';
|
||||
import { registerExploreRoute } from './routes/explore';
|
||||
|
@ -68,6 +69,7 @@ export class GraphPlugin implements Plugin {
|
|||
}),
|
||||
order: 600,
|
||||
category: DEFAULT_APP_CATEGORIES.kibana,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: ['graph', 'kibana'],
|
||||
catalogue: ['graph'],
|
||||
minimumLicense: 'platinum',
|
||||
|
|
|
@ -18,6 +18,7 @@ import { HomeServerPluginSetup } from '@kbn/home-plugin/server';
|
|||
import { DataViewPersistableStateService } from '@kbn/data-views-plugin/common';
|
||||
import type { EMSSettings } from '@kbn/maps-ems-plugin/server';
|
||||
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
import { CONTENT_ID, LATEST_VERSION } from '../common/content_management';
|
||||
import { getEcommerceSavedObjects } from './sample_data/ecommerce_saved_objects';
|
||||
import { getFlightsSavedObjects } from './sample_data/flights_saved_objects';
|
||||
|
@ -175,6 +176,7 @@ export class MapsPlugin implements Plugin {
|
|||
}),
|
||||
order: 400,
|
||||
category: DEFAULT_APP_CATEGORIES.kibana,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: [APP_ID, 'kibana'],
|
||||
catalogue: [APP_ID],
|
||||
privileges: {
|
||||
|
|
|
@ -25,6 +25,7 @@ import type { SpacesPluginSetup } from '@kbn/spaces-plugin/server';
|
|||
import type { FieldFormatsStart } from '@kbn/field-formats-plugin/server';
|
||||
import type { HomeServerPluginSetup } from '@kbn/home-plugin/server';
|
||||
import type { CasesServerSetup } from '@kbn/cases-plugin/server';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
import type { PluginsSetup, PluginsStart, RouteInitialization } from './types';
|
||||
import type { MlCapabilities } from '../common/types/capabilities';
|
||||
import { notificationsRoutes } from './routes/notifications';
|
||||
|
@ -129,6 +130,7 @@ export class MlServerPlugin
|
|||
}),
|
||||
order: 500,
|
||||
category: DEFAULT_APP_CATEGORIES.kibana,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: [PLUGIN_ID, 'kibana'],
|
||||
catalogue: [PLUGIN_ID, `${PLUGIN_ID}_file_data_visualizer`],
|
||||
privilegesTooltip: i18n.translate('xpack.ml.featureRegistry.privilegesTooltip', {
|
||||
|
|
|
@ -23,6 +23,7 @@ import {
|
|||
import { get } from 'lodash';
|
||||
import { DEFAULT_APP_CATEGORIES } from '@kbn/core/server';
|
||||
import { RouteMethod } from '@kbn/core/server';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
import {
|
||||
KIBANA_MONITORING_LOGGING_TAG,
|
||||
KIBANA_STATS_TYPE_MONITORING,
|
||||
|
@ -272,6 +273,7 @@ export class MonitoringPlugin
|
|||
defaultMessage: 'Stack Monitoring',
|
||||
}),
|
||||
category: DEFAULT_APP_CATEGORIES.management,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: ['monitoring', 'kibana'],
|
||||
catalogue: ['monitoring'],
|
||||
privileges: null,
|
||||
|
|
|
@ -15,6 +15,7 @@ import {
|
|||
|
||||
import { APM_INDEX_SETTINGS_SAVED_OBJECT_TYPE } from '@kbn/apm-data-access-plugin/server/saved_objects/apm_indices';
|
||||
import { ApmRuleType } from '@kbn/rule-data-utils';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
import { APM_SERVER_FEATURE_ID } from '../common/rules/apm_rule_types';
|
||||
|
||||
const ruleTypes = Object.values(ApmRuleType);
|
||||
|
@ -26,6 +27,7 @@ export const APM_FEATURE = {
|
|||
}),
|
||||
order: 900,
|
||||
category: DEFAULT_APP_CATEGORIES.observability,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: [APM_SERVER_FEATURE_ID, 'ux', 'kibana'],
|
||||
catalogue: [APM_SERVER_FEATURE_ID],
|
||||
management: {
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
} from '@kbn/rule-data-utils';
|
||||
import { ES_QUERY_ID } from '@kbn/rule-data-utils';
|
||||
import { metricsDataSourceSavedObjectName } from '@kbn/metrics-data-access-plugin/server';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
import { LOG_DOCUMENT_COUNT_RULE_TYPE_ID } from '../common/alerting/logs/log_threshold/types';
|
||||
import {
|
||||
METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID,
|
||||
|
@ -37,6 +38,7 @@ export const METRICS_FEATURE = {
|
|||
}),
|
||||
order: 800,
|
||||
category: DEFAULT_APP_CATEGORIES.observability,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: ['infra', 'metrics', 'kibana'],
|
||||
catalogue: ['infraops', 'metrics'],
|
||||
management: {
|
||||
|
@ -103,6 +105,7 @@ export const LOGS_FEATURE = {
|
|||
}),
|
||||
order: 700,
|
||||
category: DEFAULT_APP_CATEGORIES.observability,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: ['infra', 'logs', 'kibana'],
|
||||
catalogue: ['infralogging', 'logs'],
|
||||
management: {
|
||||
|
|
|
@ -37,6 +37,7 @@ import { SharePluginSetup } from '@kbn/share-plugin/server';
|
|||
import { SpacesPluginSetup, SpacesPluginStart } from '@kbn/spaces-plugin/server';
|
||||
import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server';
|
||||
import { DataViewsServerPluginStart } from '@kbn/data-views-plugin/server';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
import { ObservabilityConfig } from '.';
|
||||
import { casesFeatureId, observabilityFeatureId } from '../common';
|
||||
import {
|
||||
|
@ -112,6 +113,7 @@ export class ObservabilityPlugin implements Plugin<ObservabilityPluginSetup> {
|
|||
}),
|
||||
order: 1100,
|
||||
category: DEFAULT_APP_CATEGORIES.observability,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: [casesFeatureId, 'kibana'],
|
||||
catalogue: [observabilityFeatureId],
|
||||
cases: [observabilityFeatureId],
|
||||
|
@ -235,6 +237,7 @@ export class ObservabilityPlugin implements Plugin<ObservabilityPluginSetup> {
|
|||
}),
|
||||
order: 1000,
|
||||
category: DEFAULT_APP_CATEGORIES.observability,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: [observabilityFeatureId],
|
||||
catalogue: [observabilityFeatureId],
|
||||
alerting: o11yRuleTypes,
|
||||
|
|
|
@ -20,6 +20,7 @@ import {
|
|||
ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE,
|
||||
} from '@kbn/actions-plugin/server/constants/saved_objects';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
import { OBSERVABILITY_AI_ASSISTANT_FEATURE_ID } from '../common/feature';
|
||||
import type { ObservabilityAIAssistantConfig } from './config';
|
||||
import { registerServerRoutes } from './routes/register_routes';
|
||||
|
@ -69,6 +70,7 @@ export class ObservabilityAIAssistantPlugin
|
|||
}),
|
||||
order: 8600,
|
||||
category: DEFAULT_APP_CATEGORIES.observability,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: [OBSERVABILITY_AI_ASSISTANT_FEATURE_ID, 'kibana'],
|
||||
catalogue: [OBSERVABILITY_AI_ASSISTANT_FEATURE_ID],
|
||||
minimumLicense: 'enterprise',
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { DEFAULT_APP_CATEGORIES } from '@kbn/core/server';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
|
||||
export const PROFILING_SERVER_FEATURE_ID = 'profiling';
|
||||
|
||||
|
@ -17,6 +18,7 @@ export const PROFILING_FEATURE = {
|
|||
}),
|
||||
order: 1200,
|
||||
category: DEFAULT_APP_CATEGORIES.observability,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: [PROFILING_SERVER_FEATURE_ID, 'ux', 'kibana'],
|
||||
// see x-pack/plugins/features/common/feature_kibana_privileges.ts
|
||||
privileges: {
|
||||
|
|
|
@ -33,6 +33,7 @@ import { SpacesPluginSetup, SpacesPluginStart } from '@kbn/spaces-plugin/server'
|
|||
import { AlertsLocatorDefinition } from '@kbn/observability-plugin/common';
|
||||
import { SLO_BURN_RATE_RULE_TYPE_ID } from '@kbn/rule-data-utils';
|
||||
import { sloFeatureId } from '@kbn/observability-plugin/common';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
import { registerSloUsageCollector } from './lib/collectors/register';
|
||||
import { SloOrphanSummaryCleanupTask } from './services/tasks/orphan_summary_cleanup_task';
|
||||
import { slo, SO_SLO_TYPE } from './saved_objects';
|
||||
|
@ -88,6 +89,7 @@ export class SloPlugin implements Plugin<SloPluginSetup> {
|
|||
}),
|
||||
order: 1200,
|
||||
category: DEFAULT_APP_CATEGORIES.observability,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: [sloFeatureId, 'kibana'],
|
||||
catalogue: [sloFeatureId, 'observability'],
|
||||
alerting: sloRuleTypes,
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
SubFeaturePrivilegeGroupConfig,
|
||||
SubFeaturePrivilegeGroupType,
|
||||
} from '@kbn/features-plugin/common';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
import { syntheticsMonitorType, syntheticsParamType } from '../common/types/saved_objects';
|
||||
import { SYNTHETICS_RULE_TYPES } from '../common/constants/synthetics_alerts';
|
||||
import { privateLocationsSavedObjectName } from '../common/saved_objects/private_locations';
|
||||
|
@ -55,6 +56,7 @@ export const syntheticsFeature = {
|
|||
category: DEFAULT_APP_CATEGORIES.observability,
|
||||
app: ['uptime', 'kibana', 'synthetics'],
|
||||
catalogue: ['uptime'],
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
management: {
|
||||
insightsAndAlerting: ['triggersActions'],
|
||||
},
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { DEFAULT_APP_CATEGORIES } from '@kbn/core/server';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
import {
|
||||
packSavedObjectType,
|
||||
packAssetSavedObjectType,
|
||||
|
@ -22,6 +23,7 @@ export const registerFeatures = (features: SetupPlugins['features']) => {
|
|||
defaultMessage: 'Osquery',
|
||||
}),
|
||||
category: DEFAULT_APP_CATEGORIES.management,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: [PLUGIN_ID, 'kibana'],
|
||||
catalogue: [PLUGIN_ID],
|
||||
order: 2300,
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import { DEFAULT_APP_CATEGORIES, type Logger } from '@kbn/core/server';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { FeaturesPluginSetup } from '@kbn/features-plugin/server';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
|
||||
interface FeatureRegistrationOpts {
|
||||
features: FeaturesPluginSetup;
|
||||
|
@ -37,6 +38,7 @@ export function registerFeatures({
|
|||
defaultMessage: 'Reporting',
|
||||
}),
|
||||
category: DEFAULT_APP_CATEGORIES.management,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: [],
|
||||
privileges: {
|
||||
all: { savedObject: { all: [], read: [] }, ui: [] },
|
||||
|
|
|
@ -186,6 +186,7 @@ describe('Reporting Plugin', () => {
|
|||
id: 'reporting',
|
||||
name: 'Reporting',
|
||||
category: DEFAULT_APP_CATEGORIES.management,
|
||||
scope: ['spaces', 'security'],
|
||||
app: [],
|
||||
privileges: {
|
||||
all: { savedObject: { all: [], read: [] }, ui: [] },
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { DEFAULT_APP_CATEGORIES } from '@kbn/core/server';
|
||||
import { KibanaFeatureConfig } from '@kbn/features-plugin/server';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
import { tagSavedObjectTypeName, tagManagementSectionId, tagFeatureId } from '../common/constants';
|
||||
|
||||
export const savedObjectsTaggingFeature: KibanaFeatureConfig = {
|
||||
|
@ -16,6 +17,7 @@ export const savedObjectsTaggingFeature: KibanaFeatureConfig = {
|
|||
defaultMessage: 'Tag Management',
|
||||
}),
|
||||
category: DEFAULT_APP_CATEGORIES.management,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
order: 1800,
|
||||
app: [],
|
||||
management: {
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
Plugin,
|
||||
PluginInitializerContext,
|
||||
} from '@kbn/core/server';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
import { defineRoutes } from './routes';
|
||||
import {
|
||||
SearchInferenceEndpointsPluginSetup,
|
||||
|
@ -56,6 +57,7 @@ export class SearchInferenceEndpointsPlugin
|
|||
order: 0,
|
||||
category: DEFAULT_APP_CATEGORIES.enterpriseSearch,
|
||||
app: ['kibana', PLUGIN_ID],
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
catalogue: [PLUGIN_ID],
|
||||
privileges: {
|
||||
all: {
|
||||
|
|
|
@ -55,6 +55,10 @@ exports[`EnabledFeatures renders as expected 1`] = `
|
|||
"id": "feature-1",
|
||||
"name": "Feature 1",
|
||||
"privileges": null,
|
||||
"scope": Array [
|
||||
"spaces",
|
||||
"security",
|
||||
],
|
||||
},
|
||||
Object {
|
||||
"app": Array [],
|
||||
|
@ -67,6 +71,10 @@ exports[`EnabledFeatures renders as expected 1`] = `
|
|||
"id": "feature-2",
|
||||
"name": "Feature 2",
|
||||
"privileges": null,
|
||||
"scope": Array [
|
||||
"spaces",
|
||||
"security",
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import type { EuiCheckboxProps } from '@elastic/eui';
|
|||
import React from 'react';
|
||||
|
||||
import { DEFAULT_APP_CATEGORIES } from '@kbn/core/public';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
import type { KibanaFeatureConfig } from '@kbn/features-plugin/public';
|
||||
import { findTestSubject, mountWithIntl, nextTick, shallowWithIntl } from '@kbn/test-jest-helpers';
|
||||
|
||||
|
@ -18,6 +19,7 @@ const features: KibanaFeatureConfig[] = [
|
|||
{
|
||||
id: 'feature-1',
|
||||
name: 'Feature 1',
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: [],
|
||||
category: DEFAULT_APP_CATEGORIES.kibana,
|
||||
privileges: null,
|
||||
|
@ -25,6 +27,7 @@ const features: KibanaFeatureConfig[] = [
|
|||
{
|
||||
id: 'feature-2',
|
||||
name: 'Feature 2',
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: [],
|
||||
category: DEFAULT_APP_CATEGORIES.kibana,
|
||||
privileges: null,
|
||||
|
|
|
@ -20,11 +20,13 @@ const features = [
|
|||
id: 'feature_1',
|
||||
name: 'Feature 1',
|
||||
app: [],
|
||||
scope: ['spaces', 'security'],
|
||||
},
|
||||
{
|
||||
id: 'feature_2',
|
||||
name: 'Feature 2',
|
||||
app: ['feature2'],
|
||||
scope: ['spaces', 'security'],
|
||||
catalogue: ['feature2Entry'],
|
||||
management: {
|
||||
kibana: ['somethingElse'],
|
||||
|
@ -44,6 +46,7 @@ const features = [
|
|||
id: 'feature_3',
|
||||
name: 'Feature 3',
|
||||
app: ['feature3_app'],
|
||||
scope: ['spaces', 'security'],
|
||||
catalogue: ['feature3Entry'],
|
||||
management: {
|
||||
kibana: ['indices'],
|
||||
|
@ -64,6 +67,7 @@ const features = [
|
|||
id: 'feature_4',
|
||||
name: 'Feature 4',
|
||||
app: ['feature3', 'feature3_app'],
|
||||
scope: ['spaces', 'security'],
|
||||
catalogue: ['feature3Entry'],
|
||||
management: {
|
||||
kibana: ['indices'],
|
||||
|
|
|
@ -117,7 +117,7 @@ describe('Spaces plugin', () => {
|
|||
|
||||
const coreStart = coreMock.createStart();
|
||||
|
||||
const spacesStart = plugin.start(coreStart);
|
||||
const spacesStart = plugin.start(coreStart, { features: featuresPluginMock.createStart() });
|
||||
expect(spacesStart).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"hasOnlyDefaultSpace$": Observable {
|
||||
|
@ -154,7 +154,7 @@ describe('Spaces plugin', () => {
|
|||
|
||||
const spacesSetup = plugin.setup(core, { features, licensing, usageCollection });
|
||||
const coreStart = coreMock.createStart();
|
||||
const spacesStart = plugin.start(coreStart);
|
||||
const spacesStart = plugin.start(coreStart, { features: featuresPluginMock.createStart() });
|
||||
|
||||
await expect(firstValueFrom(spacesSetup.hasOnlyDefaultSpace$)).resolves.toEqual(true);
|
||||
await expect(firstValueFrom(spacesStart.hasOnlyDefaultSpace$)).resolves.toEqual(true);
|
||||
|
@ -172,7 +172,7 @@ describe('Spaces plugin', () => {
|
|||
|
||||
const spacesSetup = plugin.setup(core, { features, licensing, usageCollection });
|
||||
const coreStart = coreMock.createStart();
|
||||
const spacesStart = plugin.start(coreStart);
|
||||
const spacesStart = plugin.start(coreStart, { features: featuresPluginMock.createStart() });
|
||||
|
||||
await expect(firstValueFrom(spacesSetup.hasOnlyDefaultSpace$)).resolves.toEqual(false);
|
||||
await expect(firstValueFrom(spacesStart.hasOnlyDefaultSpace$)).resolves.toEqual(false);
|
||||
|
|
|
@ -232,8 +232,8 @@ export class SpacesPlugin
|
|||
};
|
||||
}
|
||||
|
||||
public start(core: CoreStart) {
|
||||
const spacesClientStart = this.spacesClientService.start(core);
|
||||
public start(core: CoreStart, plugins: PluginsStart) {
|
||||
const spacesClientStart = this.spacesClientService.start(core, plugins.features);
|
||||
|
||||
this.spacesServiceStart = this.spacesService.start({
|
||||
basePath: core.http.basePath,
|
||||
|
|
|
@ -17,6 +17,7 @@ import {
|
|||
loggingSystemMock,
|
||||
} from '@kbn/core/server/mocks';
|
||||
import { SPACES_EXTENSION_ID } from '@kbn/core-saved-objects-server';
|
||||
import { featuresPluginMock } from '@kbn/features-plugin/server/mocks';
|
||||
|
||||
import { initCopyToSpacesApi } from './copy_to_space';
|
||||
import { spacesConfig } from '../../../lib/__fixtures__';
|
||||
|
@ -72,7 +73,7 @@ describe('copy to space', () => {
|
|||
usageStatsServiceMock.createSetupContract(usageStatsClient)
|
||||
);
|
||||
|
||||
const clientServiceStart = clientService.start(coreStart);
|
||||
const clientServiceStart = clientService.start(coreStart, featuresPluginMock.createStart());
|
||||
|
||||
const spacesServiceStart = service.start({
|
||||
basePath: coreStart.http.basePath,
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
httpServiceMock,
|
||||
loggingSystemMock,
|
||||
} from '@kbn/core/server/mocks';
|
||||
import { featuresPluginMock } from '@kbn/features-plugin/server/mocks';
|
||||
|
||||
import { initDeleteSpacesApi } from './delete';
|
||||
import { spacesConfig } from '../../../lib/__fixtures__';
|
||||
|
@ -54,7 +55,7 @@ describe('Spaces Public API', () => {
|
|||
|
||||
const usageStatsServicePromise = Promise.resolve(usageStatsServiceMock.createSetupContract());
|
||||
|
||||
const clientServiceStart = clientService.start(coreStart);
|
||||
const clientServiceStart = clientService.start(coreStart, featuresPluginMock.createStart());
|
||||
|
||||
const spacesServiceStart = service.start({
|
||||
basePath: coreStart.http.basePath,
|
||||
|
|
|
@ -15,6 +15,7 @@ import {
|
|||
httpServiceMock,
|
||||
loggingSystemMock,
|
||||
} from '@kbn/core/server/mocks';
|
||||
import { featuresPluginMock } from '@kbn/features-plugin/server/mocks';
|
||||
|
||||
import { initDisableLegacyUrlAliasesApi } from './disable_legacy_url_aliases';
|
||||
import { spacesConfig } from '../../../lib/__fixtures__';
|
||||
|
@ -57,7 +58,7 @@ describe('_disable_legacy_url_aliases', () => {
|
|||
usageStatsServiceMock.createSetupContract(usageStatsClient)
|
||||
);
|
||||
|
||||
const clientServiceStart = clientService.start(coreStart);
|
||||
const clientServiceStart = clientService.start(coreStart, featuresPluginMock.createStart());
|
||||
|
||||
const spacesServiceStart = service.start({
|
||||
basePath: coreStart.http.basePath,
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
httpServiceMock,
|
||||
loggingSystemMock,
|
||||
} from '@kbn/core/server/mocks';
|
||||
import { featuresPluginMock } from '@kbn/features-plugin/server/mocks';
|
||||
|
||||
import { initGetSpaceApi } from './get';
|
||||
import { spacesConfig } from '../../../lib/__fixtures__';
|
||||
|
@ -53,7 +54,7 @@ describe('GET space', () => {
|
|||
|
||||
const usageStatsServicePromise = Promise.resolve(usageStatsServiceMock.createSetupContract());
|
||||
|
||||
const clientServiceStart = clientService.start(coreStart);
|
||||
const clientServiceStart = clientService.start(coreStart, featuresPluginMock.createStart());
|
||||
|
||||
const spacesServiceStart = service.start({
|
||||
basePath: coreStart.http.basePath,
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
loggingSystemMock,
|
||||
} from '@kbn/core/server/mocks';
|
||||
import { getRequestValidation } from '@kbn/core-http-server';
|
||||
import { featuresPluginMock } from '@kbn/features-plugin/server/mocks';
|
||||
|
||||
import { initGetAllSpacesApi } from './get_all';
|
||||
import { spacesConfig } from '../../../lib/__fixtures__';
|
||||
|
@ -55,7 +56,7 @@ describe('GET /spaces/space', () => {
|
|||
|
||||
const usageStatsServicePromise = Promise.resolve(usageStatsServiceMock.createSetupContract());
|
||||
|
||||
const clientServiceStart = clientService.start(coreStart);
|
||||
const clientServiceStart = clientService.start(coreStart, featuresPluginMock.createStart());
|
||||
|
||||
const spacesServiceStart = service.start({
|
||||
basePath: coreStart.http.basePath,
|
||||
|
|
|
@ -15,6 +15,7 @@ import {
|
|||
httpServiceMock,
|
||||
loggingSystemMock,
|
||||
} from '@kbn/core/server/mocks';
|
||||
import { featuresPluginMock } from '@kbn/features-plugin/server/mocks';
|
||||
|
||||
import { initGetShareableReferencesApi } from './get_shareable_references';
|
||||
import { spacesConfig } from '../../../lib/__fixtures__';
|
||||
|
@ -54,7 +55,7 @@ describe('get shareable references', () => {
|
|||
|
||||
const usageStatsServicePromise = Promise.resolve(usageStatsServiceMock.createSetupContract());
|
||||
|
||||
const clientServiceStart = clientService.start(coreStart);
|
||||
const clientServiceStart = clientService.start(coreStart, featuresPluginMock.createStart());
|
||||
|
||||
const spacesServiceStart = service.start({
|
||||
basePath: coreStart.http.basePath,
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
httpServiceMock,
|
||||
loggingSystemMock,
|
||||
} from '@kbn/core/server/mocks';
|
||||
import { featuresPluginMock } from '@kbn/features-plugin/server/mocks';
|
||||
|
||||
import { initPostSpacesApi } from './post';
|
||||
import { spacesConfig } from '../../../lib/__fixtures__';
|
||||
|
@ -52,9 +53,13 @@ describe('Spaces Public API', () => {
|
|||
basePath: httpService.basePath,
|
||||
});
|
||||
|
||||
const featuresPluginMockStart = featuresPluginMock.createStart();
|
||||
|
||||
featuresPluginMockStart.getKibanaFeatures.mockReturnValue([]);
|
||||
|
||||
const usageStatsServicePromise = Promise.resolve(usageStatsServiceMock.createSetupContract());
|
||||
|
||||
const clientServiceStart = clientService.start(coreStart);
|
||||
const clientServiceStart = clientService.start(coreStart, featuresPluginMockStart);
|
||||
|
||||
const spacesServiceStart = service.start({
|
||||
basePath: coreStart.http.basePath,
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
httpServiceMock,
|
||||
loggingSystemMock,
|
||||
} from '@kbn/core/server/mocks';
|
||||
import { featuresPluginMock } from '@kbn/features-plugin/server/mocks';
|
||||
|
||||
import { initPutSpacesApi } from './put';
|
||||
import { spacesConfig } from '../../../lib/__fixtures__';
|
||||
|
@ -52,9 +53,13 @@ describe('PUT /api/spaces/space', () => {
|
|||
basePath: httpService.basePath,
|
||||
});
|
||||
|
||||
const featuresPluginMockStart = featuresPluginMock.createStart();
|
||||
|
||||
featuresPluginMockStart.getKibanaFeatures.mockReturnValue([]);
|
||||
|
||||
const usageStatsServicePromise = Promise.resolve(usageStatsServiceMock.createSetupContract());
|
||||
|
||||
const clientServiceStart = clientService.start(coreStart);
|
||||
const clientServiceStart = clientService.start(coreStart, featuresPluginMockStart);
|
||||
|
||||
const spacesServiceStart = service.start({
|
||||
basePath: coreStart.http.basePath,
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
httpServiceMock,
|
||||
loggingSystemMock,
|
||||
} from '@kbn/core/server/mocks';
|
||||
import { featuresPluginMock } from '@kbn/features-plugin/server/mocks';
|
||||
|
||||
import { initUpdateObjectsSpacesApi } from './update_objects_spaces';
|
||||
import { spacesConfig } from '../../../lib/__fixtures__';
|
||||
|
@ -55,7 +56,7 @@ describe('update_objects_spaces', () => {
|
|||
|
||||
const usageStatsServicePromise = Promise.resolve(usageStatsServiceMock.createSetupContract());
|
||||
|
||||
const clientServiceStart = clientService.start(coreStart);
|
||||
const clientServiceStart = clientService.start(coreStart, featuresPluginMock.createStart());
|
||||
|
||||
const spacesServiceStart = service.start({
|
||||
basePath: coreStart.http.basePath,
|
||||
|
|
|
@ -17,6 +17,7 @@ import {
|
|||
savedObjectsClientMock,
|
||||
savedObjectsTypeRegistryMock,
|
||||
} from '@kbn/core/server/mocks';
|
||||
import { featuresPluginMock } from '@kbn/features-plugin/server/mocks';
|
||||
|
||||
import type { SpaceContentTypeSummaryItem } from './get_content_summary';
|
||||
import { initGetSpaceContentSummaryApi } from './get_content_summary';
|
||||
|
@ -81,7 +82,7 @@ describe('GET /internal/spaces/{spaceId}/content_summary', () => {
|
|||
basePath: httpService.basePath,
|
||||
});
|
||||
|
||||
const clientServiceStart = clientService.start(coreStart);
|
||||
const clientServiceStart = clientService.start(coreStart, featuresPluginMock.createStart());
|
||||
|
||||
const spacesServiceStart = service.start({
|
||||
basePath: coreStart.http.basePath,
|
||||
|
|
|
@ -10,6 +10,7 @@ import * as Rx from 'rxjs';
|
|||
import type { RouteValidatorConfig } from '@kbn/core/server';
|
||||
import { kibanaResponseFactory } from '@kbn/core/server';
|
||||
import { coreMock, httpServerMock, httpServiceMock } from '@kbn/core/server/mocks';
|
||||
import { featuresPluginMock } from '@kbn/features-plugin/server/mocks';
|
||||
|
||||
import { initSetSolutionSpaceApi } from './set_solution_space';
|
||||
import { spacesConfig } from '../../../lib/__fixtures__';
|
||||
|
@ -43,7 +44,7 @@ describe('PUT /internal/spaces/space/{id}/solution', () => {
|
|||
basePath: httpService.basePath,
|
||||
});
|
||||
|
||||
const clientServiceStart = clientService.start(coreStart);
|
||||
const clientServiceStart = clientService.start(coreStart, featuresPluginMock.createStart());
|
||||
|
||||
const spacesServiceStart = service.start({
|
||||
basePath: coreStart.http.basePath,
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
|
||||
import { savedObjectsRepositoryMock } from '@kbn/core/server/mocks';
|
||||
import type { SavedObject } from '@kbn/core-saved-objects-server';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
import { KibanaFeature } from '@kbn/features-plugin/server';
|
||||
import { featuresPluginMock } from '@kbn/features-plugin/server/mocks';
|
||||
|
||||
import { SpacesClient } from './spaces_client';
|
||||
import type { GetAllSpacesPurpose, Space } from '../../common';
|
||||
|
@ -113,7 +116,8 @@ describe('#getAll', () => {
|
|||
mockConfig,
|
||||
mockCallWithRequestRepository,
|
||||
[],
|
||||
'traditional'
|
||||
'traditional',
|
||||
featuresPluginMock.createStart()
|
||||
);
|
||||
const actualSpaces = await client.getAll();
|
||||
|
||||
|
@ -140,7 +144,8 @@ describe('#getAll', () => {
|
|||
mockConfig,
|
||||
mockCallWithRequestRepository,
|
||||
[],
|
||||
'serverless'
|
||||
'serverless',
|
||||
featuresPluginMock.createStart()
|
||||
);
|
||||
const [actualSpace] = await client.getAll();
|
||||
const [{ solution, ...expectedSpace }] = expectedSpaces;
|
||||
|
@ -164,7 +169,8 @@ describe('#getAll', () => {
|
|||
mockConfig,
|
||||
mockCallWithRequestRepository,
|
||||
[],
|
||||
'traditional'
|
||||
'traditional',
|
||||
featuresPluginMock.createStart()
|
||||
);
|
||||
await expect(
|
||||
client.getAll({ purpose: 'invalid_purpose' as GetAllSpacesPurpose })
|
||||
|
@ -211,7 +217,8 @@ describe('#get', () => {
|
|||
mockConfig,
|
||||
mockCallWithRequestRepository,
|
||||
[],
|
||||
'traditional'
|
||||
'traditional',
|
||||
featuresPluginMock.createStart()
|
||||
);
|
||||
const id = savedObject.id;
|
||||
const actualSpace = await client.get(id);
|
||||
|
@ -234,7 +241,8 @@ describe('#get', () => {
|
|||
mockConfig,
|
||||
mockCallWithRequestRepository,
|
||||
[],
|
||||
'serverless'
|
||||
'serverless',
|
||||
featuresPluginMock.createStart()
|
||||
);
|
||||
const id = savedObject.id;
|
||||
const actualSpace = await client.get(id);
|
||||
|
@ -257,7 +265,8 @@ describe('#get', () => {
|
|||
mockConfig,
|
||||
mockCallWithRequestRepository,
|
||||
[],
|
||||
'traditional'
|
||||
'traditional',
|
||||
featuresPluginMock.createStart()
|
||||
);
|
||||
const id = savedObject.id;
|
||||
const actualSpace = await client.get(id);
|
||||
|
@ -320,7 +329,8 @@ describe('#create', () => {
|
|||
mockConfig,
|
||||
mockCallWithRequestRepository,
|
||||
[],
|
||||
'traditional'
|
||||
'traditional',
|
||||
featuresPluginMock.createStart()
|
||||
);
|
||||
|
||||
const actualSpace = await client.create(spaceToCreate);
|
||||
|
@ -336,6 +346,60 @@ describe('#create', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test(`throws bad request when creating space with disabled features`, async () => {
|
||||
const maxSpaces = 5;
|
||||
const mockDebugLogger = createMockDebugLogger();
|
||||
const mockConfig = createMockConfig();
|
||||
const mockCallWithRequestRepository = savedObjectsRepositoryMock.create();
|
||||
mockCallWithRequestRepository.create.mockResolvedValue(savedObject);
|
||||
mockCallWithRequestRepository.find.mockResolvedValue({
|
||||
total: maxSpaces - 1,
|
||||
} as any);
|
||||
const featuresMock = featuresPluginMock.createStart();
|
||||
|
||||
featuresMock.getKibanaFeatures.mockReturnValue([
|
||||
new KibanaFeature({
|
||||
id: 'feature-1',
|
||||
name: 'KibanaFeature',
|
||||
app: [],
|
||||
category: { id: 'foo', label: 'foo' },
|
||||
scope: [KibanaFeatureScope.Security],
|
||||
privileges: {
|
||||
all: {
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [],
|
||||
},
|
||||
ui: ['foo'],
|
||||
},
|
||||
read: {
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [],
|
||||
},
|
||||
ui: ['foo'],
|
||||
},
|
||||
},
|
||||
subFeatures: [],
|
||||
}),
|
||||
]);
|
||||
|
||||
const client = new SpacesClient(
|
||||
mockDebugLogger,
|
||||
mockConfig,
|
||||
mockCallWithRequestRepository,
|
||||
[],
|
||||
'traditional',
|
||||
featuresMock
|
||||
);
|
||||
|
||||
await expect(
|
||||
client.create({ ...spaceToCreate, disabledFeatures: ['feature-1'] })
|
||||
).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
`"Unable to create Space, one or more disabledFeatures do not have the required space scope"`
|
||||
);
|
||||
});
|
||||
|
||||
test(`throws bad request when we are at the maximum number of spaces`, async () => {
|
||||
const maxSpaces = 5;
|
||||
const mockDebugLogger = createMockDebugLogger();
|
||||
|
@ -357,7 +421,8 @@ describe('#create', () => {
|
|||
mockConfig,
|
||||
mockCallWithRequestRepository,
|
||||
[],
|
||||
'traditional'
|
||||
'traditional',
|
||||
featuresPluginMock.createStart()
|
||||
);
|
||||
|
||||
await expect(client.create(spaceToCreate)).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
|
@ -393,7 +458,8 @@ describe('#create', () => {
|
|||
mockConfig,
|
||||
mockCallWithRequestRepository,
|
||||
[],
|
||||
'serverless'
|
||||
'serverless',
|
||||
featuresPluginMock.createStart()
|
||||
);
|
||||
|
||||
await expect(
|
||||
|
@ -440,7 +506,8 @@ describe('#create', () => {
|
|||
mockConfig,
|
||||
mockCallWithRequestRepository,
|
||||
[],
|
||||
'traditional'
|
||||
'traditional',
|
||||
featuresPluginMock.createStart()
|
||||
);
|
||||
|
||||
const actualSpace = await client.create({ ...spaceToCreate, solution: 'es' });
|
||||
|
@ -483,7 +550,8 @@ describe('#create', () => {
|
|||
mockConfig,
|
||||
mockCallWithRequestRepository,
|
||||
[],
|
||||
'traditional'
|
||||
'traditional',
|
||||
featuresPluginMock.createStart()
|
||||
);
|
||||
|
||||
const actualSpace = await client.create(spaceToCreate);
|
||||
|
@ -520,7 +588,8 @@ describe('#create', () => {
|
|||
mockConfig,
|
||||
mockCallWithRequestRepository,
|
||||
[],
|
||||
'traditional'
|
||||
'traditional',
|
||||
featuresPluginMock.createStart()
|
||||
);
|
||||
|
||||
await expect(
|
||||
|
@ -560,7 +629,8 @@ describe('#create', () => {
|
|||
mockConfig,
|
||||
mockCallWithRequestRepository,
|
||||
[],
|
||||
'traditional'
|
||||
'traditional',
|
||||
featuresPluginMock.createStart()
|
||||
);
|
||||
|
||||
await expect(
|
||||
|
@ -624,7 +694,8 @@ describe('#update', () => {
|
|||
mockConfig,
|
||||
mockCallWithRequestRepository,
|
||||
[],
|
||||
'traditional'
|
||||
'traditional',
|
||||
featuresPluginMock.createStart()
|
||||
);
|
||||
const id = savedObject.id;
|
||||
const actualSpace = await client.update(id, spaceToUpdate);
|
||||
|
@ -634,6 +705,56 @@ describe('#update', () => {
|
|||
expect(mockCallWithRequestRepository.get).toHaveBeenCalledWith('space', id);
|
||||
});
|
||||
|
||||
test(`throws bad request when creating space with disabled features`, async () => {
|
||||
const mockDebugLogger = createMockDebugLogger();
|
||||
const mockConfig = createMockConfig();
|
||||
const mockCallWithRequestRepository = savedObjectsRepositoryMock.create();
|
||||
mockCallWithRequestRepository.get.mockResolvedValue(savedObject);
|
||||
const featuresMock = featuresPluginMock.createStart();
|
||||
|
||||
featuresMock.getKibanaFeatures.mockReturnValue([
|
||||
new KibanaFeature({
|
||||
id: 'feature-1',
|
||||
name: 'KibanaFeature',
|
||||
app: [],
|
||||
category: { id: 'foo', label: 'foo' },
|
||||
scope: [KibanaFeatureScope.Security],
|
||||
privileges: {
|
||||
all: {
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [],
|
||||
},
|
||||
ui: ['foo'],
|
||||
},
|
||||
read: {
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [],
|
||||
},
|
||||
ui: ['foo'],
|
||||
},
|
||||
},
|
||||
subFeatures: [],
|
||||
}),
|
||||
]);
|
||||
|
||||
const client = new SpacesClient(
|
||||
mockDebugLogger,
|
||||
mockConfig,
|
||||
mockCallWithRequestRepository,
|
||||
[],
|
||||
'traditional',
|
||||
featuresMock
|
||||
);
|
||||
|
||||
await expect(
|
||||
client.create({ ...spaceToUpdate, disabledFeatures: ['feature-1'] })
|
||||
).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
`"Cannot destructure property 'total' of '(intermediate value)' as it is undefined."`
|
||||
);
|
||||
});
|
||||
|
||||
test('throws bad request when solution property is provided in serverless build', async () => {
|
||||
const mockDebugLogger = createMockDebugLogger();
|
||||
const mockConfig = createMockConfig();
|
||||
|
@ -645,7 +766,8 @@ describe('#update', () => {
|
|||
mockConfig,
|
||||
mockCallWithRequestRepository,
|
||||
[],
|
||||
'serverless'
|
||||
'serverless',
|
||||
featuresPluginMock.createStart()
|
||||
);
|
||||
const id = savedObject.id;
|
||||
|
||||
|
@ -677,7 +799,8 @@ describe('#update', () => {
|
|||
mockConfig,
|
||||
mockCallWithRequestRepository,
|
||||
[],
|
||||
'traditional'
|
||||
'traditional',
|
||||
featuresPluginMock.createStart()
|
||||
);
|
||||
const id = savedObject.id;
|
||||
|
||||
|
@ -703,7 +826,8 @@ describe('#update', () => {
|
|||
mockConfig,
|
||||
mockCallWithRequestRepository,
|
||||
[],
|
||||
'traditional'
|
||||
'traditional',
|
||||
featuresPluginMock.createStart()
|
||||
);
|
||||
const id = savedObject.id;
|
||||
await client.update(id, { ...spaceToUpdate, solution: 'es' });
|
||||
|
@ -732,7 +856,8 @@ describe('#update', () => {
|
|||
mockConfig,
|
||||
mockCallWithRequestRepository,
|
||||
[],
|
||||
'traditional'
|
||||
'traditional',
|
||||
featuresPluginMock.createStart()
|
||||
);
|
||||
const id = savedObject.id;
|
||||
const actualSpace = await client.update(id, spaceToUpdate);
|
||||
|
@ -758,7 +883,8 @@ describe('#update', () => {
|
|||
mockConfig,
|
||||
mockCallWithRequestRepository,
|
||||
[],
|
||||
'traditional'
|
||||
'traditional',
|
||||
featuresPluginMock.createStart()
|
||||
);
|
||||
const id = savedObject.id;
|
||||
|
||||
|
@ -790,7 +916,8 @@ describe('#update', () => {
|
|||
mockConfig,
|
||||
mockCallWithRequestRepository,
|
||||
[],
|
||||
'traditional'
|
||||
'traditional',
|
||||
featuresPluginMock.createStart()
|
||||
);
|
||||
const id = savedObject.id;
|
||||
|
||||
|
@ -843,7 +970,8 @@ describe('#delete', () => {
|
|||
mockConfig,
|
||||
mockCallWithRequestRepository,
|
||||
[],
|
||||
'traditional'
|
||||
'traditional',
|
||||
featuresPluginMock.createStart()
|
||||
);
|
||||
|
||||
await expect(client.delete(id)).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
|
@ -864,7 +992,8 @@ describe('#delete', () => {
|
|||
mockConfig,
|
||||
mockCallWithRequestRepository,
|
||||
[],
|
||||
'traditional'
|
||||
'traditional',
|
||||
featuresPluginMock.createStart()
|
||||
);
|
||||
|
||||
await client.delete(id);
|
||||
|
@ -886,7 +1015,8 @@ describe('#disableLegacyUrlAliases', () => {
|
|||
mockConfig,
|
||||
mockCallWithRequestRepository,
|
||||
[],
|
||||
'traditional'
|
||||
'traditional',
|
||||
featuresPluginMock.createStart()
|
||||
);
|
||||
const aliases = [
|
||||
{ targetSpace: 'space1', targetType: 'foo', sourceId: '123' },
|
||||
|
|
|
@ -14,6 +14,8 @@ import type {
|
|||
SavedObject,
|
||||
} from '@kbn/core/server';
|
||||
import type { LegacyUrlAliasTarget } from '@kbn/core-saved-objects-common';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
import type { FeaturesPluginStart } from '@kbn/features-plugin/server';
|
||||
|
||||
import { isReservedSpace } from '../../common';
|
||||
import type { spaceV1 as v1 } from '../../common';
|
||||
|
@ -88,7 +90,8 @@ export class SpacesClient implements ISpacesClient {
|
|||
private readonly config: ConfigType,
|
||||
private readonly repository: ISavedObjectsRepository,
|
||||
private readonly nonGlobalTypeNames: string[],
|
||||
private readonly buildFlavour: BuildFlavor
|
||||
private readonly buildFlavour: BuildFlavor,
|
||||
private readonly features: FeaturesPluginStart
|
||||
) {
|
||||
this.isServerless = this.buildFlavour === 'serverless';
|
||||
}
|
||||
|
@ -150,6 +153,8 @@ export class SpacesClient implements ISpacesClient {
|
|||
throw Boom.badRequest('Unable to create Space, solution property cannot be empty');
|
||||
}
|
||||
|
||||
this.validateDisabledFeatures(space);
|
||||
|
||||
this.debugLogger(`SpacesClient.create(), using RBAC. Attempting to create space`);
|
||||
|
||||
const id = space.id;
|
||||
|
@ -183,6 +188,8 @@ export class SpacesClient implements ISpacesClient {
|
|||
throw Boom.badRequest('Unable to update Space, solution property cannot be empty');
|
||||
}
|
||||
|
||||
this.validateDisabledFeatures(space);
|
||||
|
||||
const attributes = this.generateSpaceAttributes(space);
|
||||
await this.repository.update('space', id, attributes);
|
||||
const updatedSavedObject = await this.repository.get('space', id);
|
||||
|
@ -216,6 +223,28 @@ export class SpacesClient implements ISpacesClient {
|
|||
await this.repository.bulkUpdate(objectsToUpdate);
|
||||
}
|
||||
|
||||
private validateDisabledFeatures = (space: v1.Space) => {
|
||||
if (!space.disabledFeatures.length || this.isServerless) {
|
||||
return;
|
||||
}
|
||||
|
||||
const kibanaFeatures = this.features.getKibanaFeatures();
|
||||
|
||||
if (
|
||||
space.disabledFeatures.some((feature) => {
|
||||
const disabledKibanaFeature = kibanaFeatures.find((f) => f.id === feature);
|
||||
|
||||
return (
|
||||
disabledKibanaFeature && !disabledKibanaFeature.scope?.includes(KibanaFeatureScope.Spaces)
|
||||
);
|
||||
})
|
||||
) {
|
||||
throw Boom.badRequest(
|
||||
'Unable to create Space, one or more disabledFeatures do not have the required space scope'
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
private transformSavedObjectToSpace = (savedObject: SavedObject<any>): v1.Space => {
|
||||
return {
|
||||
id: savedObject.id,
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import * as Rx from 'rxjs';
|
||||
|
||||
import { coreMock, httpServerMock } from '@kbn/core/server/mocks';
|
||||
import { featuresPluginMock } from '@kbn/features-plugin/server/mocks';
|
||||
|
||||
import type { ISpacesClient } from './spaces_client';
|
||||
import { SpacesClient } from './spaces_client';
|
||||
|
@ -49,7 +50,7 @@ describe('SpacesClientService', () => {
|
|||
const service = new SpacesClientService(debugLogger, 'traditional');
|
||||
service.setup({ config$: new Rx.Observable<ConfigType>() });
|
||||
const coreStart = coreMock.createStart();
|
||||
const start = service.start(coreStart);
|
||||
const start = service.start(coreStart, featuresPluginMock.createStart());
|
||||
|
||||
const request = httpServerMock.createKibanaRequest();
|
||||
|
||||
|
@ -64,7 +65,7 @@ describe('SpacesClientService', () => {
|
|||
service.setup({ config$: Rx.of(spacesConfig) });
|
||||
|
||||
const coreStart = coreMock.createStart();
|
||||
const start = service.start(coreStart);
|
||||
const start = service.start(coreStart, featuresPluginMock.createStart());
|
||||
|
||||
const request = httpServerMock.createKibanaRequest();
|
||||
const client = start.createSpacesClient(request);
|
||||
|
@ -85,7 +86,7 @@ describe('SpacesClientService', () => {
|
|||
setup.setClientRepositoryFactory(customRepositoryFactory);
|
||||
|
||||
const coreStart = coreMock.createStart();
|
||||
const start = service.start(coreStart);
|
||||
const start = service.start(coreStart, featuresPluginMock.createStart());
|
||||
|
||||
const request = httpServerMock.createKibanaRequest();
|
||||
const client = start.createSpacesClient(request);
|
||||
|
@ -107,7 +108,7 @@ describe('SpacesClientService', () => {
|
|||
setup.registerClientWrapper(clientWrapper);
|
||||
|
||||
const coreStart = coreMock.createStart();
|
||||
const start = service.start(coreStart);
|
||||
const start = service.start(coreStart, featuresPluginMock.createStart());
|
||||
|
||||
const request = httpServerMock.createKibanaRequest();
|
||||
const client = start.createSpacesClient(request);
|
||||
|
@ -135,7 +136,7 @@ describe('SpacesClientService', () => {
|
|||
setup.registerClientWrapper(clientWrapper);
|
||||
|
||||
const coreStart = coreMock.createStart();
|
||||
const start = service.start(coreStart);
|
||||
const start = service.start(coreStart, featuresPluginMock.createStart());
|
||||
|
||||
const request = httpServerMock.createKibanaRequest();
|
||||
const client = start.createSpacesClient(request);
|
||||
|
|
|
@ -14,6 +14,7 @@ import type {
|
|||
KibanaRequest,
|
||||
SavedObjectsServiceStart,
|
||||
} from '@kbn/core/server';
|
||||
import type { FeaturesPluginStart } from '@kbn/features-plugin/server';
|
||||
|
||||
import type { ISpacesClient } from './spaces_client';
|
||||
import { SpacesClient } from './spaces_client';
|
||||
|
@ -99,7 +100,7 @@ export class SpacesClientService {
|
|||
};
|
||||
}
|
||||
|
||||
public start(coreStart: CoreStart): SpacesClientServiceStart {
|
||||
public start(coreStart: CoreStart, features: FeaturesPluginStart): SpacesClientServiceStart {
|
||||
const nonGlobalTypes = coreStart.savedObjects
|
||||
.getTypeRegistry()
|
||||
.getAllTypes()
|
||||
|
@ -122,7 +123,8 @@ export class SpacesClientService {
|
|||
this.config,
|
||||
this.repositoryFactory!(request, coreStart.savedObjects),
|
||||
nonGlobalTypeNames,
|
||||
this.buildFlavour
|
||||
this.buildFlavour,
|
||||
features
|
||||
);
|
||||
if (this.clientWrapper) {
|
||||
return this.clientWrapper(request, baseClient);
|
||||
|
|
|
@ -10,13 +10,13 @@ import * as Rx from 'rxjs';
|
|||
import type { HttpServiceSetup, KibanaRequest, SavedObjectsRepository } from '@kbn/core/server';
|
||||
import { SavedObjectsErrorHelpers } from '@kbn/core/server';
|
||||
import { coreMock, httpServerMock } from '@kbn/core/server/mocks';
|
||||
import { featuresPluginMock } from '@kbn/features-plugin/server/mocks';
|
||||
|
||||
import { SpacesService } from './spaces_service';
|
||||
import { DEFAULT_SPACE_ID } from '../../common/constants';
|
||||
import { getSpaceIdFromPath } from '../../common/lib/spaces_url_parser';
|
||||
import { spacesConfig } from '../lib/__fixtures__';
|
||||
import { SpacesClientService } from '../spaces_client';
|
||||
|
||||
const createService = (serverBasePath: string = '') => {
|
||||
const spacesService = new SpacesService();
|
||||
|
||||
|
@ -74,7 +74,10 @@ const createService = (serverBasePath: string = '') => {
|
|||
config$: Rx.of(spacesConfig),
|
||||
});
|
||||
|
||||
const spacesClientServiceStart = spacesClientService.start(coreStart);
|
||||
const spacesClientServiceStart = spacesClientService.start(
|
||||
coreStart,
|
||||
featuresPluginMock.createStart()
|
||||
);
|
||||
|
||||
const spacesServiceStart = spacesService.start({
|
||||
basePath: coreStart.http.basePath,
|
||||
|
|
|
@ -11,6 +11,7 @@ import { DEFAULT_APP_CATEGORIES } from '@kbn/core/server';
|
|||
import { TRANSFORM_RULE_TYPE } from '@kbn/transform-plugin/common';
|
||||
import { STACK_ALERTS_FEATURE_ID } from '@kbn/rule-data-utils';
|
||||
import { ES_QUERY_ID as ElasticsearchQuery } from '@kbn/rule-data-utils';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
import { ID as IndexThreshold } from './rule_types/index_threshold/rule_type';
|
||||
import { GEO_CONTAINMENT_ID as GeoContainment } from './rule_types/geo_containment';
|
||||
|
||||
|
@ -23,6 +24,7 @@ export const BUILT_IN_ALERTS_FEATURE: KibanaFeatureConfig = {
|
|||
}),
|
||||
app: [],
|
||||
category: DEFAULT_APP_CATEGORIES.management,
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
management: {
|
||||
insightsAndAlerting: ['triggersActions'],
|
||||
},
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
PluginStartContract as ActionsPluginStartContract,
|
||||
} from '@kbn/actions-plugin/server/plugin';
|
||||
import { ActionType } from '@kbn/actions-plugin/server';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
import { initPlugin as initPagerduty } from './pagerduty_simulation';
|
||||
import { initPlugin as initSwimlane } from './swimlane_simulation';
|
||||
import { initPlugin as initServiceNow } from './servicenow_simulation';
|
||||
|
@ -127,6 +128,7 @@ export class FixturePlugin implements Plugin<void, void, FixtureSetupDeps, Fixtu
|
|||
name: 'actionsSimulators',
|
||||
app: ['actions', 'kibana'],
|
||||
category: { id: 'foo', label: 'foo' },
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
privileges: {
|
||||
all: {
|
||||
app: ['actions', 'kibana'],
|
||||
|
|
|
@ -25,6 +25,7 @@ import { RuleRegistryPluginSetupContract } from '@kbn/rule-registry-plugin/serve
|
|||
import { IEventLogClientService } from '@kbn/event-log-plugin/server';
|
||||
import { NotificationsPluginStart } from '@kbn/notifications-plugin/server';
|
||||
import { RULE_SAVED_OBJECT_TYPE } from '@kbn/alerting-plugin/server';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
import { defineRoutes } from './routes';
|
||||
import { defineActionTypes } from './action_types';
|
||||
import { defineRuleTypes } from './rule_types';
|
||||
|
@ -71,6 +72,7 @@ export class FixturePlugin implements Plugin<void, void, FixtureSetupDeps, Fixtu
|
|||
name: 'Alerts',
|
||||
app: ['alerts', 'kibana'],
|
||||
category: { id: 'foo', label: 'foo' },
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
alerting: [
|
||||
'test.always-firing',
|
||||
'test.cumulative-firing',
|
||||
|
|
|
@ -11,6 +11,7 @@ import { PluginSetupContract as AlertingPluginSetup } from '@kbn/alerting-plugin
|
|||
import { EncryptedSavedObjectsPluginStart } from '@kbn/encrypted-saved-objects-plugin/server';
|
||||
import { FeaturesPluginSetup } from '@kbn/features-plugin/server';
|
||||
import { RULE_SAVED_OBJECT_TYPE } from '@kbn/alerting-plugin/server';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
import { defineAlertTypes } from './alert_types';
|
||||
|
||||
export interface FixtureSetupDeps {
|
||||
|
@ -30,6 +31,7 @@ export class FixturePlugin implements Plugin<void, void, FixtureSetupDeps, Fixtu
|
|||
name: 'AlertRestricted',
|
||||
app: ['alerts', 'kibana'],
|
||||
category: { id: 'foo', label: 'foo' },
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
alerting: ['test.restricted-noop', 'test.unrestricted-noop', 'test.noop'],
|
||||
privileges: {
|
||||
all: {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import expect from '@kbn/expect';
|
||||
import { KibanaFeature } from '@kbn/features-plugin/server';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
import { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
|
@ -136,6 +137,63 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
].sort()
|
||||
);
|
||||
});
|
||||
|
||||
it('should return a full feature set with correct scope', async () => {
|
||||
const { body } = await supertest.get('/api/features').expect(200);
|
||||
expect(body).to.be.an(Array);
|
||||
|
||||
const scopeAgnosticFeatures = [
|
||||
'discover',
|
||||
'visualize',
|
||||
'dashboard',
|
||||
'dev_tools',
|
||||
'actions',
|
||||
'enterpriseSearch',
|
||||
'filesManagement',
|
||||
'filesSharedImage',
|
||||
'advancedSettings',
|
||||
'aiAssistantManagementSelection',
|
||||
'indexPatterns',
|
||||
'graph',
|
||||
'guidedOnboardingFeature',
|
||||
'monitoring',
|
||||
'observabilityAIAssistant',
|
||||
'observabilityCases',
|
||||
'savedObjectsManagement',
|
||||
'savedQueryManagement',
|
||||
'savedObjectsTagging',
|
||||
'ml',
|
||||
'apm',
|
||||
'stackAlerts',
|
||||
'canvas',
|
||||
'generalCases',
|
||||
'infrastructure',
|
||||
'logs',
|
||||
'maintenanceWindow',
|
||||
'maps',
|
||||
'osquery',
|
||||
'rulesSettings',
|
||||
'uptime',
|
||||
'searchInferenceEndpoints',
|
||||
'siem',
|
||||
'slo',
|
||||
'securitySolutionAssistant',
|
||||
'securitySolutionAttackDiscovery',
|
||||
'securitySolutionCases',
|
||||
'fleet',
|
||||
'fleetv2',
|
||||
];
|
||||
|
||||
const features = body.filter(
|
||||
(f: KibanaFeature) =>
|
||||
f.scope?.includes(KibanaFeatureScope.Spaces) &&
|
||||
f.scope?.includes(KibanaFeatureScope.Security)
|
||||
);
|
||||
|
||||
expect(features.every((f: KibanaFeature) => scopeAgnosticFeatures.includes(f.id))).to.be(
|
||||
true
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import { SecurityPluginStart } from '@kbn/security-plugin/server';
|
|||
import type { CasesServerStart, CasesServerSetup } from '@kbn/cases-plugin/server';
|
||||
import { FilesSetup } from '@kbn/files-plugin/server';
|
||||
import { PluginStartContract as ActionsPluginsStart } from '@kbn/actions-plugin/server/plugin';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
import { getPersistableStateAttachment } from './attachments/persistable_state';
|
||||
import { getExternalReferenceAttachment } from './attachments/external_reference';
|
||||
import { registerRoutes } from './routes';
|
||||
|
@ -52,6 +53,7 @@ export class FixturePlugin implements Plugin<void, void, FixtureSetupDeps, Fixtu
|
|||
name: 'TestNoCasesConnectorFixture',
|
||||
app: ['kibana'],
|
||||
category: { id: 'cases-fixtures', label: 'Cases Fixtures' },
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
cases: ['testNoCasesConnectorFixture'],
|
||||
privileges: {
|
||||
all: {
|
||||
|
|
|
@ -10,6 +10,7 @@ import { hiddenTypes as filesSavedObjectTypes } from '@kbn/files-plugin/server/s
|
|||
import { FeaturesPluginSetup } from '@kbn/features-plugin/server';
|
||||
import { SpacesPluginStart } from '@kbn/spaces-plugin/server';
|
||||
import { SecurityPluginStart } from '@kbn/security-plugin/server';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
|
||||
export interface FixtureSetupDeps {
|
||||
features: FeaturesPluginSetup;
|
||||
|
@ -28,6 +29,7 @@ export class FixturePlugin implements Plugin<void, void, FixtureSetupDeps, Fixtu
|
|||
name: 'ObservabilityFixture',
|
||||
app: ['kibana'],
|
||||
category: { id: 'cases-fixtures', label: 'Cases Fixtures' },
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
cases: ['observabilityFixture'],
|
||||
privileges: {
|
||||
all: {
|
||||
|
|
|
@ -10,6 +10,7 @@ import { hiddenTypes as filesSavedObjectTypes } from '@kbn/files-plugin/server/s
|
|||
import { FeaturesPluginSetup } from '@kbn/features-plugin/server';
|
||||
import { SpacesPluginStart } from '@kbn/spaces-plugin/server';
|
||||
import { SecurityPluginStart } from '@kbn/security-plugin/server';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
|
||||
export interface FixtureSetupDeps {
|
||||
features: FeaturesPluginSetup;
|
||||
|
@ -36,6 +37,7 @@ export class FixturePlugin implements Plugin<void, void, FixtureSetupDeps, Fixtu
|
|||
name: 'SecuritySolutionFixture',
|
||||
app: ['kibana'],
|
||||
category: { id: 'cases-fixtures', label: 'Cases Fixtures' },
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
cases: ['securitySolutionFixture'],
|
||||
privileges: {
|
||||
all: {
|
||||
|
@ -121,6 +123,7 @@ export class FixturePlugin implements Plugin<void, void, FixtureSetupDeps, Fixtu
|
|||
name: 'TestDisabledFixture',
|
||||
app: ['kibana'],
|
||||
category: { id: 'cases-fixtures', label: 'Cases Fixtures' },
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
// testDisabledFixture is disabled in space1
|
||||
cases: ['testDisabledFixture'],
|
||||
privileges: {
|
||||
|
|
|
@ -12,6 +12,7 @@ import { EncryptedSavedObjectsPluginStart } from '@kbn/encrypted-saved-objects-p
|
|||
import { FeaturesPluginSetup } from '@kbn/features-plugin/server';
|
||||
import { SpacesPluginStart } from '@kbn/spaces-plugin/server';
|
||||
import { SecurityPluginStart } from '@kbn/security-plugin/server';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
|
||||
export interface FixtureSetupDeps {
|
||||
features: FeaturesPluginSetup;
|
||||
|
@ -33,6 +34,7 @@ export class FixturePlugin implements Plugin<void, void, FixtureSetupDeps, Fixtu
|
|||
name: 'Alerts',
|
||||
app: ['alerts', 'kibana'],
|
||||
category: { id: 'foo', label: 'foo' },
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
alerting: ['test.executionContext'],
|
||||
privileges: {
|
||||
all: {
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
RuleTypeParams,
|
||||
} from '@kbn/alerting-plugin/server';
|
||||
import { FeaturesPluginSetup } from '@kbn/features-plugin/server';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
|
||||
// this plugin's dependendencies
|
||||
export interface AlertingExampleDeps {
|
||||
|
@ -116,6 +117,7 @@ export class AlertingFixturePlugin implements Plugin<void, void, AlertingExample
|
|||
name: 'alerting_fixture',
|
||||
app: [],
|
||||
category: { id: 'foo', label: 'foo' },
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
alerting: ['test.always-firing', 'test.noop', 'test.failing'],
|
||||
privileges: {
|
||||
all: {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import { CoreSetup, Plugin } from '@kbn/core/server';
|
||||
import { FeaturesPluginSetup } from '@kbn/features-plugin/server';
|
||||
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
|
||||
|
||||
export const plugin = async () => new FooPlugin();
|
||||
|
||||
|
@ -20,6 +21,7 @@ class FooPlugin implements Plugin {
|
|||
id: 'foo',
|
||||
name: 'Foo',
|
||||
category: { id: 'foo', label: 'foo' },
|
||||
scope: [KibanaFeatureScope.Spaces, KibanaFeatureScope.Security],
|
||||
app: ['foo_plugin', 'kibana'],
|
||||
catalogue: ['foo'],
|
||||
privileges: {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue