Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added ability to create API keys #92610

Merged
merged 34 commits into from
Apr 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
1ff1e2c
Added ability to create API keys
thomheymann Feb 24, 2021
6c373bb
Remove hard coded colours
thomheymann Feb 25, 2021
4c2b9cc
Added unit tests
thomheymann Mar 2, 2021
65c4cd2
Fix linting errors
thomheymann Mar 3, 2021
d834d94
Display full base64 encoded API key
thomheymann Mar 7, 2021
3116000
Merge branch 'master' of github.com:elastic/kibana into security/crea…
thomheymann Mar 8, 2021
478cf74
Fix linting errors
thomheymann Mar 8, 2021
8830612
Fix more linting error and unit tests
thomheymann Mar 8, 2021
fc4a3c8
Added suggestions from code review
thomheymann Mar 16, 2021
43d4986
Merge branch 'master' of github.com:elastic/kibana into security/crea…
thomheymann Mar 16, 2021
59f874d
fix unit tests
thomheymann Mar 17, 2021
588f9e1
Merge branch 'master' of github.com:elastic/kibana into security/crea…
thomheymann Mar 17, 2021
5fcfc28
move code editor field into separate component
thomheymann Mar 18, 2021
60c8894
fixed tests
thomheymann Mar 18, 2021
0e501b1
fixed test
thomheymann Mar 18, 2021
6f3a472
Fixed functional tests
thomheymann Mar 22, 2021
acd81d0
Merge branch 'master' of github.com:elastic/kibana into security/crea…
thomheymann Mar 22, 2021
5ed5e67
replaced theme hook with eui import
thomheymann Mar 22, 2021
6857eb7
Revert to manual theme detection
thomheymann Mar 22, 2021
0d1a7a7
added storybook
thomheymann Mar 22, 2021
defb331
Merge branch 'master' into security/create-api-key
kibanamachine Mar 29, 2021
d044e09
Additional unit and functional tests
legrego Mar 29, 2021
9d14bc9
Merge branch 'master' of github.com:elastic/kibana into pr/thomheyman…
legrego Mar 29, 2021
62d433a
Merge branch 'master' of github.com:elastic/kibana into pr/thomheyman…
legrego Apr 1, 2021
fa1a6cd
Merge branch 'master' into security/create-api-key
kibanamachine Apr 2, 2021
d7556c3
Added suggestions from code review
thomheymann Apr 7, 2021
90af78b
Merge remote-tracking branch 'upstream/master' into security/create-a…
thomheymann Apr 7, 2021
d35ede4
Remove unused translations
thomheymann Apr 7, 2021
ffdcb7f
Merge branch 'master' into security/create-api-key
kibanamachine Apr 7, 2021
a3674a3
Updated docs and added detailed error description
thomheymann Apr 8, 2021
ad4c21e
Merge branch 'security/create-api-key' of github.com:thomheymann/kiba…
thomheymann Apr 8, 2021
5900f2e
Removed unused messages
thomheymann Apr 8, 2021
959f266
Updated unit test
thomheymann Apr 9, 2021
bfcd5d7
Merge branch 'master' into security/create-api-key
kibanamachine Apr 13, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
Binary file modified docs/user/security/api-keys/images/api-keys.png
100755 → 100644
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
57 changes: 15 additions & 42 deletions docs/user/security/api-keys/index.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@


API keys enable you to create secondary credentials so that you can send
requests on behalf of the user. Secondary credentials have
requests on behalf of a user. Secondary credentials have
the same or lower access rights.

For example, if you extract data from an {es} cluster on a daily
Expand All @@ -14,8 +14,7 @@ and then put the API credentials into a cron job.
Or, you might create API keys to automate ingestion of new data from
remote sources, without a live user interaction.

You can create API keys from the {kib} Console. To view and invalidate
API keys, open the main menu, then click *Stack Management > API Keys*.
To manage API keys, open the main menu, then click *Stack Management > API Keys*.

[role="screenshot"]
image:user/security/api-keys/images/api-keys.png["API Keys UI"]
Expand Down Expand Up @@ -46,58 +45,32 @@ cluster privileges to use API keys in {kib}. To manage roles, open the main menu
[float]
[[create-api-key]]
=== Create an API key
You can {ref}/security-api-create-api-key.html[create an API key] from
the {kib} Console. This example shows how to create an API key
to authenticate to a <<api, Kibana API>>.

[source,js]
POST /_security/api_key
{
"name": "kibana_api_key"
}

This creates an API key with the
name `kibana_api_key`. API key
names must be globally unique.
An expiration date is optional and follows
{ref}/common-options.html#time-units[{es} time unit format].
When an expiration is not provided, the API key does not expire.

The response should look something like this:

[source,js]
{
"id" : "XFcbCnIBnbwqt2o79G4q",
"name" : "kibana_api_key",
"api_key" : "FD6P5UA4QCWlZZQhYF3YGw"
}

Now, you can use the API key to request {kib} roles. You'll need to send a request with a
`Authorization` header with a value having the prefix `ApiKey` followed by the credentials,
where credentials is the base64 encoding of `id` and `api_key` joined by a colon. For example:

[source,js]

To create an API key, open the main menu, then click *Stack Management > API Keys > Create API key*.

[role="screenshot"]
image:user/security/api-keys/images/create-api-key.png["Create API Key UI"]

Once created, you can copy the API key (Base64 encoded) and use it to send requests to {es} on your behalf. For example:

[source,bash]
curl --location --request GET 'http://localhost:5601/api/security/role' \
--header 'Content-Type: application/json;charset=UTF-8' \
--header 'kbn-xsrf: true' \
--header 'Authorization: ApiKey aVZlLUMzSUJuYndxdDJvN0k1bU46aGxlYUpNS2lTa2FKeVZua1FnY1VEdw==' \

[float]
[[view-api-keys]]
=== View and invalidate API keys
The *API Keys* feature in Kibana lists your API keys, including the name, date created,
and expiration date. If an API key expires, its status changes from `Active` to `Expired`.
=== View and delete API keys

The *API Keys* feature in Kibana lists your API keys, including the name, date created, and status. If an API key expires, its status changes from `Active` to `Expired`.

If you have `manage_security` or `manage_api_key` permissions,
you can view the API keys of all users, and see which API key was
created by which user in which realm.
If you have only the `manage_own_api_key` permission, you see only a list of your own keys.

You can invalidate API keys individually or in bulk.
Invalidated keys are deleted in batch after seven days.

[role="screenshot"]
image:user/security/api-keys/images/api-key-invalidate.png["API Keys invalidate"]
You can delete API keys individually or in bulk.

You cannot modify an API key. If you need additional privileges,
you must create a new key with the desired configuration and invalidate the old key.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,25 @@ storiesOf('CodeEditor', module)
},
}
)
.add(
'transparent background',
() => (
<div>
<CodeEditor
languageId="plaintext"
height={250}
value="Hello!"
onChange={action('onChange')}
transparentBackground
/>
</div>
),
{
info: {
text: 'Plaintext Monaco Editor',
},
}
)
.add(
'custom log language',
() => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ test('editor mount setup', () => {
// Verify our mount callback will be called
expect(editorWillMount.mock.calls.length).toBe(1);

// Verify our theme will be setup
expect((monaco.editor.defineTheme as jest.Mock).mock.calls.length).toBe(1);
// Verify that both, default and transparent theme will be setup
expect((monaco.editor.defineTheme as jest.Mock).mock.calls.length).toBe(2);

// Verify our language features have been registered
expect((monaco.languages.onLanguage as jest.Mock).mock.calls.length).toBe(1);
Expand Down
44 changes: 35 additions & 9 deletions src/plugins/kibana_react/public/code_editor/code_editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,14 @@
import React from 'react';
import ReactResizeDetector from 'react-resize-detector';
import MonacoEditor from 'react-monaco-editor';

import { monaco } from '@kbn/monaco';

import { LIGHT_THEME, DARK_THEME } from './editor_theme';
import {
DARK_THEME,
LIGHT_THEME,
DARK_THEME_TRANSPARENT,
LIGHT_THEME_TRANSPARENT,
} from './editor_theme';

import './editor.scss';

Expand Down Expand Up @@ -86,6 +90,11 @@ export interface Props {
* Should the editor use the dark theme
*/
useDarkTheme?: boolean;

/**
* Should the editor use a transparent background
*/
transparentBackground?: boolean;
}

export class CodeEditor extends React.Component<Props, {}> {
Expand Down Expand Up @@ -132,8 +141,12 @@ export class CodeEditor extends React.Component<Props, {}> {
}
});

// Register the theme
// Register themes
monaco.editor.defineTheme('euiColors', this.props.useDarkTheme ? DARK_THEME : LIGHT_THEME);
monaco.editor.defineTheme(
'euiColorsTransparent',
this.props.useDarkTheme ? DARK_THEME_TRANSPARENT : LIGHT_THEME_TRANSPARENT
);
};

_editorDidMount = (editor: monaco.editor.IStandaloneCodeEditor, __monaco: unknown) => {
Expand All @@ -152,20 +165,33 @@ export class CodeEditor extends React.Component<Props, {}> {
const { languageId, value, onChange, width, height, options } = this.props;

return (
<React.Fragment>
<>
<MonacoEditor
theme="euiColors"
theme={this.props.transparentBackground ? 'euiColorsTransparent' : 'euiColors'}
language={languageId}
value={value}
onChange={onChange}
editorWillMount={this._editorWillMount}
editorDidMount={this._editorDidMount}
width={width}
height={height}
options={options}
editorWillMount={this._editorWillMount}
editorDidMount={this._editorDidMount}
options={{
renderLineHighlight: 'none',
scrollBeyondLastLine: false,
minimap: {
enabled: false,
},
scrollbar: {
useShadows: false,
},
wordBasedSuggestions: false,
wordWrap: 'on',
wrappingIndent: 'indent',
...options,
}}
/>
<ReactResizeDetector handleWidth handleHeight onResize={this._updateDimensions} />
</React.Fragment>
</>
);
}

Expand Down
9 changes: 6 additions & 3 deletions src/plugins/kibana_react/public/code_editor/editor_theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import lightTheme from '@elastic/eui/dist/eui_theme_light.json';

export function createTheme(
euiTheme: typeof darkTheme | typeof lightTheme,
selectionBackgroundColor: string
selectionBackgroundColor: string,
backgroundColor?: string
): monaco.editor.IStandaloneThemeData {
return {
base: 'vs',
Expand Down Expand Up @@ -87,7 +88,7 @@ export function createTheme(
],
colors: {
'editor.foreground': euiTheme.euiColorDarkestShade,
'editor.background': euiTheme.euiFormBackgroundColor,
'editor.background': backgroundColor ?? euiTheme.euiFormBackgroundColor,
'editorLineNumber.foreground': euiTheme.euiColorDarkShade,
'editorLineNumber.activeForeground': euiTheme.euiColorDarkShade,
'editorIndentGuide.background': euiTheme.euiColorLightShade,
Expand All @@ -105,5 +106,7 @@ export function createTheme(

const DARK_THEME = createTheme(darkTheme, '#343551');
const LIGHT_THEME = createTheme(lightTheme, '#E3E4ED');
const DARK_THEME_TRANSPARENT = createTheme(darkTheme, '#343551', '#00000000');
const LIGHT_THEME_TRANSPARENT = createTheme(lightTheme, '#E3E4ED', '#00000000');

export { DARK_THEME, LIGHT_THEME };
export { DARK_THEME, LIGHT_THEME, DARK_THEME_TRANSPARENT, LIGHT_THEME_TRANSPARENT };
58 changes: 54 additions & 4 deletions src/plugins/kibana_react/public/code_editor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,14 @@
*/

import React from 'react';
import { EuiDelayRender, EuiLoadingContent } from '@elastic/eui';
import {
EuiDelayRender,
EuiErrorBoundary,
EuiLoadingContent,
EuiFormControlLayout,
} from '@elastic/eui';
import darkTheme from '@elastic/eui/dist/eui_theme_dark.json';
import lightTheme from '@elastic/eui/dist/eui_theme_light.json';
import { useUiSetting } from '../ui_settings';
import type { Props } from './code_editor';

Expand All @@ -19,11 +26,54 @@ const Fallback = () => (
</EuiDelayRender>
);

/**
* Renders a Monaco code editor with EUI color theme.
*
* @see CodeEditorField to render a code editor in the same style as other EUI form fields.
*/
export const CodeEditor: React.FunctionComponent<Props> = (props) => {
const darkMode = useUiSetting<boolean>('theme:darkMode');
return (
<React.Suspense fallback={<Fallback />}>
<LazyBaseEditor {...props} useDarkTheme={darkMode} />
</React.Suspense>
<EuiErrorBoundary>
<React.Suspense fallback={<Fallback />}>
<LazyBaseEditor {...props} useDarkTheme={darkMode} />
</React.Suspense>
</EuiErrorBoundary>
);
};

/**
* Renders a Monaco code editor in the same style as other EUI form fields.
*/
export const CodeEditorField: React.FunctionComponent<Props> = (props) => {
const { width, height, options } = props;
const darkMode = useUiSetting<boolean>('theme:darkMode');
const theme = darkMode ? darkTheme : lightTheme;
const style = {
width,
height,
backgroundColor: options?.readOnly
? theme.euiFormBackgroundReadOnlyColor
: theme.euiFormBackgroundColor,
};

return (
<EuiErrorBoundary>
<React.Suspense
fallback={
<EuiFormControlLayout
append={<div hidden />}
style={{ ...style, padding: theme.paddingSizes.m }}
readOnly={options?.readOnly}
>
<Fallback />
</EuiFormControlLayout>
}
>
<EuiFormControlLayout append={<div hidden />} style={style} readOnly={options?.readOnly}>
<LazyBaseEditor {...props} useDarkTheme={darkMode} transparentBackground />
</EuiFormControlLayout>
</React.Suspense>
</EuiErrorBoundary>
);
};
4 changes: 4 additions & 0 deletions x-pack/plugins/security/common/model/api_key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* 2.0.
*/

import type { Role } from './role';

export interface ApiKey {
id: string;
name: string;
Expand All @@ -19,3 +21,5 @@ export interface ApiKeyToInvalidate {
id: string;
name: string;
}

export type ApiKeyRoleDescriptors = Record<string, Role['elasticsearch']>;
2 changes: 1 addition & 1 deletion x-pack/plugins/security/common/model/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

export { ApiKey, ApiKeyToInvalidate } from './api_key';
export { ApiKey, ApiKeyToInvalidate, ApiKeyRoleDescriptors } from './api_key';
export { User, EditUser, getUserDisplayName } from './user';
export { AuthenticatedUser, canUserChangePassword } from './authenticated_user';
export { AuthenticationProvider, shouldProviderUseLoginForm } from './authentication_provider';
Expand Down
20 changes: 18 additions & 2 deletions x-pack/plugins/security/public/components/breadcrumb.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import type { EuiBreadcrumb } from '@elastic/eui';
import type { FunctionComponent } from 'react';
import React, { createContext, useContext, useEffect, useRef } from 'react';

import type { ChromeStart } from 'src/core/public';

import { useKibana } from '../../../../../src/plugins/kibana_react/public';

interface BreadcrumbsContext {
Expand Down Expand Up @@ -81,8 +83,8 @@ export const BreadcrumbsProvider: FunctionComponent<BreadcrumbsProviderProps> =
if (onChange) {
onChange(breadcrumbs);
} else if (services.chrome) {
services.chrome.setBreadcrumbs(breadcrumbs);
services.chrome.docTitle.change(getDocTitle(breadcrumbs));
const setBreadcrumbs = createBreadcrumbsChangeHandler(services.chrome);
setBreadcrumbs(breadcrumbs);
}
};

Expand Down Expand Up @@ -138,3 +140,17 @@ export function getDocTitle(breadcrumbs: BreadcrumbProps[], maxBreadcrumbs = 2)
.reverse()
.map(({ text }) => text);
}

export function createBreadcrumbsChangeHandler(
chrome: Pick<ChromeStart, 'docTitle' | 'setBreadcrumbs'>,
setBreadcrumbs = chrome.setBreadcrumbs
) {
return (breadcrumbs: BreadcrumbProps[]) => {
setBreadcrumbs(breadcrumbs);
if (breadcrumbs.length === 0) {
chrome.docTitle.reset();
} else {
chrome.docTitle.change(getDocTitle(breadcrumbs));
}
};
}
Loading