mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 10:40:07 -04:00
Developer documentation for designing feature privileges (#166716)
## Summary Closes #162263 Introduces a new `Feature Privileges` section to the developer documentation. The documentation includes a tutorial on how to control access to features of plugin being developed. Introduces a few sections: - Controlling access to UI features - Controlling access to server side APIs - Documentation for configuration options ## Testing To build this locally, run ./scripts/dev_docs from a local checkout of this PR. A server will eventually start on http://localhost:3000 where you can preview the changes. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
22fca89861
commit
de0e1eb0d4
14 changed files with 730 additions and 0 deletions
9
examples/feature_control_examples/common/index.ts
Normal file
9
examples/feature_control_examples/common/index.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export const FEATURE_PRIVILEGES_PLUGIN_ID = 'featurePrivilegesPluginExample';
|
13
examples/feature_control_examples/kibana.jsonc
Normal file
13
examples/feature_control_examples/kibana.jsonc
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"type": "plugin",
|
||||
"id": "@kbn/feature-controls-examples-plugin",
|
||||
"owner": "@elastic/kibana-security",
|
||||
"description": "Demo of how to implement feature controls",
|
||||
"plugin": {
|
||||
"id": "featureControlsExamples",
|
||||
"server": true,
|
||||
"browser": true,
|
||||
"requiredBundles": ["kibanaReact"],
|
||||
"requiredPlugins": ["developerExamples", "security", "spaces", "features"]
|
||||
}
|
||||
}
|
62
examples/feature_control_examples/public/app.tsx
Normal file
62
examples/feature_control_examples/public/app.tsx
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { EuiButton, EuiHealth, EuiPageTemplate, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';
|
||||
import type { CoreStart } from '@kbn/core/public';
|
||||
import { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { FEATURE_PRIVILEGES_PLUGIN_ID } from '../common';
|
||||
|
||||
export const MyPluginComponent: React.FC = () => {
|
||||
const [time, setTime] = useState('');
|
||||
const kibana = useKibana<CoreStart>();
|
||||
|
||||
const fetchData = async () => {
|
||||
const response = await fetch('/internal/my_plugin/read');
|
||||
const data = await response.json();
|
||||
|
||||
// console.log(data2);
|
||||
setTime(data.time);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<EuiPageTemplate>
|
||||
<EuiPageTemplate.Section grow={false} color="subdued" bottomBorder="extended">
|
||||
<EuiTitle size="l">
|
||||
<h1>Feature Privileges Example</h1>
|
||||
</EuiTitle>
|
||||
</EuiPageTemplate.Section>
|
||||
<EuiPageTemplate.Section grow={false} color="subdued" bottomBorder="extended">
|
||||
<EuiText>
|
||||
<p>Server Time: {time}</p>
|
||||
</EuiText>
|
||||
<EuiButton onClick={fetchData}>Refresh (Super user only)</EuiButton>
|
||||
</EuiPageTemplate.Section>
|
||||
<EuiPageTemplate.Section grow={false} color="subdued" bottomBorder="extended">
|
||||
<EuiText>
|
||||
<p>Your privileges</p>
|
||||
</EuiText>
|
||||
<EuiSpacer />
|
||||
{Object.entries(
|
||||
kibana.services.application!.capabilities[FEATURE_PRIVILEGES_PLUGIN_ID]
|
||||
).map(([capability, value]) => {
|
||||
return value === true ? (
|
||||
<div key={capability}>
|
||||
<EuiHealth color="success">{capability}</EuiHealth>
|
||||
<EuiSpacer />
|
||||
</div>
|
||||
) : null;
|
||||
})}
|
||||
</EuiPageTemplate.Section>
|
||||
</EuiPageTemplate>
|
||||
);
|
||||
};
|
59
examples/feature_control_examples/public/index.tsx
Normal file
59
examples/feature_control_examples/public/index.tsx
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { AppMountParameters, CoreSetup, CoreStart, Plugin } from '@kbn/core/public';
|
||||
import { DeveloperExamplesSetup } from '@kbn/developer-examples-plugin/public';
|
||||
import { SecurityPluginSetup, SecurityPluginStart } from '@kbn/security-plugin/public';
|
||||
import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template';
|
||||
import type { FeaturesPluginSetup } from '@kbn/features-plugin/public';
|
||||
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
|
||||
import { MyPluginComponent } from './app';
|
||||
|
||||
interface SetupDeps {
|
||||
developerExamples: DeveloperExamplesSetup;
|
||||
security: SecurityPluginSetup;
|
||||
features: FeaturesPluginSetup;
|
||||
}
|
||||
|
||||
interface StartDeps {
|
||||
security: SecurityPluginStart;
|
||||
}
|
||||
|
||||
export class FeatureControlsPluginExample implements Plugin<void, void, SetupDeps, StartDeps> {
|
||||
public setup(coreSetup: CoreSetup<StartDeps>, deps: SetupDeps) {
|
||||
coreSetup.application.register({
|
||||
id: 'featureControlsExamples',
|
||||
title: 'FeatureControlExamples',
|
||||
async mount({ element }: AppMountParameters) {
|
||||
const [coreStart] = await coreSetup.getStartServices();
|
||||
ReactDOM.render(
|
||||
<KibanaPageTemplate>
|
||||
<KibanaContextProvider services={{ ...coreStart, ...deps }}>
|
||||
<MyPluginComponent />
|
||||
</KibanaContextProvider>
|
||||
</KibanaPageTemplate>,
|
||||
element
|
||||
);
|
||||
return () => ReactDOM.unmountComponentAtNode(element);
|
||||
},
|
||||
});
|
||||
deps.developerExamples.register({
|
||||
appId: 'featureControlsExamples',
|
||||
title: 'Feature Control Examples',
|
||||
description: 'Demo of how to implement Feature Controls',
|
||||
});
|
||||
}
|
||||
|
||||
public start(core: CoreStart, deps: StartDeps) {
|
||||
return {};
|
||||
}
|
||||
|
||||
public stop() {}
|
||||
}
|
||||
export const plugin = () => new FeatureControlsPluginExample();
|
11
examples/feature_control_examples/server/index.ts
Normal file
11
examples/feature_control_examples/server/index.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
import { PluginInitializer } from '@kbn/core/server';
|
||||
import { FeatureControlsPluginExample } from './plugin';
|
||||
|
||||
export const plugin: PluginInitializer<void, void> = () => new FeatureControlsPluginExample();
|
90
examples/feature_control_examples/server/plugin.ts
Normal file
90
examples/feature_control_examples/server/plugin.ts
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { CoreSetup, DEFAULT_APP_CATEGORIES, Plugin } from '@kbn/core/server';
|
||||
import {
|
||||
PluginSetupContract as FeaturesPluginSetup,
|
||||
// PluginStartContract as FeaturesPluginStart,
|
||||
} from '@kbn/features-plugin/server';
|
||||
import { FEATURE_PRIVILEGES_PLUGIN_ID } from '../common';
|
||||
|
||||
export interface FeatureControlExampleDeps {
|
||||
features: FeaturesPluginSetup;
|
||||
}
|
||||
|
||||
export class FeatureControlsPluginExample
|
||||
implements Plugin<void, void, any, FeatureControlExampleDeps>
|
||||
{
|
||||
public setup(core: CoreSetup, { features }: FeatureControlExampleDeps) {
|
||||
features.registerKibanaFeature({
|
||||
id: FEATURE_PRIVILEGES_PLUGIN_ID,
|
||||
name: 'Feature Plugin Examples',
|
||||
category: DEFAULT_APP_CATEGORIES.management,
|
||||
app: ['FeaturePluginExample'],
|
||||
privileges: {
|
||||
all: {
|
||||
app: ['FeaturePluginExample'],
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [],
|
||||
},
|
||||
api: ['my_closed_example_api'],
|
||||
ui: ['view', 'create', 'edit', 'delete', 'assign'],
|
||||
},
|
||||
read: {
|
||||
app: ['FeaturePluginExample'],
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: ['tag'],
|
||||
},
|
||||
api: [],
|
||||
ui: ['view'],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const router = core.http.createRouter();
|
||||
router.get(
|
||||
{
|
||||
path: '/internal/my_plugin/read',
|
||||
validate: false,
|
||||
},
|
||||
async (context, request, response) => {
|
||||
return response.ok({
|
||||
body: {
|
||||
time: new Date().toISOString(),
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
router.get(
|
||||
{
|
||||
path: '/internal/my_plugin/sensitive_action',
|
||||
validate: false,
|
||||
options: {
|
||||
tags: ['access:my_closed_example_api'],
|
||||
},
|
||||
},
|
||||
async (context, request, response) => {
|
||||
return response.ok({
|
||||
body: {
|
||||
time: new Date().toISOString(),
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
start() {
|
||||
return {};
|
||||
}
|
||||
|
||||
stop() {
|
||||
return {};
|
||||
}
|
||||
}
|
23
examples/feature_control_examples/tsconfig.json
Normal file
23
examples/feature_control_examples/tsconfig.json
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "target/types"
|
||||
},
|
||||
"include": [
|
||||
"index.ts",
|
||||
"common/**/*.ts",
|
||||
"public/**/*.ts",
|
||||
"public/**/*.tsx",
|
||||
"server/**/*.ts",
|
||||
"../../typings/**/*"
|
||||
],
|
||||
"exclude": ["target/**/*"],
|
||||
"kbn_references": [
|
||||
"@kbn/core",
|
||||
"@kbn/security-plugin",
|
||||
"@kbn/developer-examples-plugin",
|
||||
"@kbn/shared-ux-page-kibana-template",
|
||||
"@kbn/features-plugin",
|
||||
"@kbn/kibana-react-plugin"
|
||||
]
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue