mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Security] Add message to login page (#51557)
* [Security] Add loginAssistanceMessage to login page * Fix tests * Fix login_page.test.tsx * Fix defaultValue * Render login assistance message independently of other messages and use EuiText instead of EuiCallOut * Use small text Co-Authored-By: Caroline Horn <549577+cchaos@users.noreply.github.com> * Flip order of message around
This commit is contained in:
parent
0557a40a9d
commit
e8e517475a
14 changed files with 188 additions and 38 deletions
|
@ -20,7 +20,7 @@ are enabled.
|
|||
Do not set this to `false`; it disables the login form, user and role management
|
||||
screens, and authorization using <<kibana-privileges>>. To disable
|
||||
{security-features} entirely, see
|
||||
{ref}/security-settings.html[{es} security settings].
|
||||
{ref}/security-settings.html[{es} security settings].
|
||||
|
||||
`xpack.security.audit.enabled`::
|
||||
Set to `true` to enable audit logging for security events. By default, it is set
|
||||
|
@ -40,7 +40,7 @@ An arbitrary string of 32 characters or more that is used to encrypt credentials
|
|||
in a cookie. It is crucial that this key is not exposed to users of {kib}. By
|
||||
default, a value is automatically generated in memory. If you use that default
|
||||
behavior, all sessions are invalidated when {kib} restarts.
|
||||
In addition, high-availability deployments of {kib} will behave unexpectedly
|
||||
In addition, high-availability deployments of {kib} will behave unexpectedly
|
||||
if this setting isn't the same for all instances of {kib}.
|
||||
|
||||
`xpack.security.secureCookies`::
|
||||
|
@ -53,3 +53,6 @@ routing requests through a load balancer or proxy).
|
|||
Sets the session duration (in milliseconds). By default, sessions stay active
|
||||
until the browser is closed. When this is set to an explicit timeout, closing the
|
||||
browser still requires the user to log back in to {kib}.
|
||||
|
||||
`xpack.security.loginAssistanceMessage`::
|
||||
Adds a message to the login screen. Useful for displaying information about maintenance windows, links to corporate sign up pages etc.
|
||||
|
|
|
@ -180,6 +180,7 @@ kibana_vars=(
|
|||
xpack.security.encryptionKey
|
||||
xpack.security.secureCookies
|
||||
xpack.security.sessionTimeout
|
||||
xpack.security.loginAssistanceMessage
|
||||
telemetry.enabled
|
||||
telemetry.sendUsageFrom
|
||||
)
|
||||
|
|
|
@ -31,6 +31,7 @@ export const security = (kibana) => new kibana.Plugin({
|
|||
encryptionKey: Joi.any().description('This key is handled in the new platform security plugin ONLY'),
|
||||
sessionTimeout: Joi.any().description('This key is handled in the new platform security plugin ONLY'),
|
||||
secureCookies: Joi.any().description('This key is handled in the new platform security plugin ONLY'),
|
||||
loginAssistanceMessage: Joi.string().default(),
|
||||
authorization: Joi.object({
|
||||
legacyFallback: Joi.object({
|
||||
enabled: Joi.boolean().default(true) // deprecated
|
||||
|
@ -147,7 +148,9 @@ export const security = (kibana) => new kibana.Plugin({
|
|||
|
||||
server.injectUiAppVars('login', () => {
|
||||
const { showLogin, allowLogin, layout = 'form' } = securityPlugin.__legacyCompat.license.getFeatures();
|
||||
const { loginAssistanceMessage } = securityPlugin.__legacyCompat.config;
|
||||
return {
|
||||
loginAssistanceMessage,
|
||||
loginState: {
|
||||
showLogin,
|
||||
allowLogin,
|
||||
|
|
|
@ -2,6 +2,20 @@
|
|||
|
||||
exports[`BasicLoginForm renders as expected 1`] = `
|
||||
<Fragment>
|
||||
<EuiText
|
||||
size="s"
|
||||
>
|
||||
<ReactMarkdown
|
||||
astPlugins={Array []}
|
||||
escapeHtml={true}
|
||||
plugins={Array []}
|
||||
rawSourcePos={false}
|
||||
renderers={Object {}}
|
||||
skipHtml={false}
|
||||
sourcePos={false}
|
||||
transformLinkUri={[Function]}
|
||||
/>
|
||||
</EuiText>
|
||||
<EuiPanel>
|
||||
<form
|
||||
onSubmit={[Function]}
|
||||
|
|
|
@ -50,6 +50,7 @@ describe('BasicLoginForm', () => {
|
|||
loginState={loginState}
|
||||
next={''}
|
||||
intl={null as any}
|
||||
loginAssistanceMessage=""
|
||||
/>
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
|
@ -68,6 +69,7 @@ describe('BasicLoginForm', () => {
|
|||
next={''}
|
||||
infoMessage={'Hey this is an info message'}
|
||||
intl={null as any}
|
||||
loginAssistanceMessage=""
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -86,6 +88,7 @@ describe('BasicLoginForm', () => {
|
|||
loginState={loginState}
|
||||
next={''}
|
||||
intl={null as any}
|
||||
loginAssistanceMessage=""
|
||||
/>
|
||||
);
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
import { EuiButton, EuiCallOut, EuiFieldText, EuiFormRow, EuiPanel, EuiSpacer } from '@elastic/eui';
|
||||
import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
||||
import React, { ChangeEvent, Component, FormEvent, Fragment, MouseEvent } from 'react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import { EuiText } from '@elastic/eui';
|
||||
import { LoginState } from '../../../../../common/login_state';
|
||||
|
||||
interface Props {
|
||||
|
@ -16,6 +18,7 @@ interface Props {
|
|||
loginState: LoginState;
|
||||
next: string;
|
||||
intl: InjectedIntl;
|
||||
loginAssistanceMessage: string;
|
||||
}
|
||||
|
||||
interface State {
|
||||
|
@ -38,6 +41,7 @@ class BasicLoginFormUI extends Component<Props, State> {
|
|||
public render() {
|
||||
return (
|
||||
<Fragment>
|
||||
{this.renderLoginAssistanceMessage()}
|
||||
{this.renderMessage()}
|
||||
<EuiPanel>
|
||||
<form onSubmit={this.submit}>
|
||||
|
@ -102,6 +106,16 @@ class BasicLoginFormUI extends Component<Props, State> {
|
|||
);
|
||||
}
|
||||
|
||||
private renderLoginAssistanceMessage = () => {
|
||||
return (
|
||||
<Fragment>
|
||||
<EuiText size="s">
|
||||
<ReactMarkdown>{this.props.loginAssistanceMessage}</ReactMarkdown>
|
||||
</EuiText>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
private renderMessage = () => {
|
||||
if (this.state.message) {
|
||||
return (
|
||||
|
@ -132,6 +146,7 @@ class BasicLoginFormUI extends Component<Props, State> {
|
|||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
|
|
|
@ -160,6 +160,88 @@ exports[`LoginPage disabled form states renders as expected when an unknown logi
|
|||
</div>
|
||||
`;
|
||||
|
||||
exports[`LoginPage disabled form states renders as expected when loginAssistanceMessage is set 1`] = `
|
||||
<div
|
||||
className="loginWelcome login-form"
|
||||
>
|
||||
<header
|
||||
className="loginWelcome__header"
|
||||
>
|
||||
<div
|
||||
className="loginWelcome__content eui-textCenter"
|
||||
>
|
||||
<EuiSpacer
|
||||
size="xxl"
|
||||
/>
|
||||
<span
|
||||
className="loginWelcome__logo"
|
||||
>
|
||||
<EuiIcon
|
||||
size="xxl"
|
||||
type="logoKibana"
|
||||
/>
|
||||
</span>
|
||||
<EuiTitle
|
||||
className="loginWelcome__title"
|
||||
size="l"
|
||||
>
|
||||
<h1>
|
||||
<FormattedMessage
|
||||
defaultMessage="Welcome to Kibana"
|
||||
id="xpack.security.loginPage.welcomeTitle"
|
||||
values={Object {}}
|
||||
/>
|
||||
</h1>
|
||||
</EuiTitle>
|
||||
<EuiText
|
||||
className="loginWelcome__subtitle"
|
||||
color="subdued"
|
||||
size="s"
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
defaultMessage="Your window into the Elastic Stack"
|
||||
id="xpack.security.loginPage.welcomeDescription"
|
||||
values={Object {}}
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
<EuiSpacer
|
||||
size="xl"
|
||||
/>
|
||||
</div>
|
||||
</header>
|
||||
<div
|
||||
className="loginWelcome__content loginWelcome-body"
|
||||
>
|
||||
<EuiFlexGroup
|
||||
gutterSize="l"
|
||||
>
|
||||
<EuiFlexItem>
|
||||
<InjectIntl(BasicLoginFormUI)
|
||||
http={
|
||||
Object {
|
||||
"post": [MockFunction],
|
||||
}
|
||||
}
|
||||
isSecureConnection={false}
|
||||
loginAssistanceMessage="This is an *important* message"
|
||||
loginState={
|
||||
Object {
|
||||
"allowLogin": true,
|
||||
"layout": "form",
|
||||
}
|
||||
}
|
||||
next=""
|
||||
requiresSecureConnection={false}
|
||||
window={Object {}}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`LoginPage disabled form states renders as expected when secure cookies are required but not present 1`] = `
|
||||
<div
|
||||
className="loginWelcome login-form"
|
||||
|
@ -385,6 +467,7 @@ exports[`LoginPage enabled form state renders as expected 1`] = `
|
|||
}
|
||||
}
|
||||
isSecureConnection={false}
|
||||
loginAssistanceMessage=""
|
||||
loginState={
|
||||
Object {
|
||||
"allowLogin": true,
|
||||
|
|
|
@ -46,6 +46,7 @@ describe('LoginPage', () => {
|
|||
loginState: createLoginState(),
|
||||
isSecureConnection: false,
|
||||
requiresSecureConnection: true,
|
||||
loginAssistanceMessage: '',
|
||||
};
|
||||
|
||||
expect(shallow(<LoginPage {...props} />)).toMatchSnapshot();
|
||||
|
@ -61,6 +62,7 @@ describe('LoginPage', () => {
|
|||
}),
|
||||
isSecureConnection: false,
|
||||
requiresSecureConnection: false,
|
||||
loginAssistanceMessage: '',
|
||||
};
|
||||
|
||||
expect(shallow(<LoginPage {...props} />)).toMatchSnapshot();
|
||||
|
@ -76,6 +78,7 @@ describe('LoginPage', () => {
|
|||
}),
|
||||
isSecureConnection: false,
|
||||
requiresSecureConnection: false,
|
||||
loginAssistanceMessage: '',
|
||||
};
|
||||
|
||||
expect(shallow(<LoginPage {...props} />)).toMatchSnapshot();
|
||||
|
@ -91,6 +94,21 @@ describe('LoginPage', () => {
|
|||
}),
|
||||
isSecureConnection: false,
|
||||
requiresSecureConnection: false,
|
||||
loginAssistanceMessage: '',
|
||||
};
|
||||
|
||||
expect(shallow(<LoginPage {...props} />)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders as expected when loginAssistanceMessage is set', () => {
|
||||
const props = {
|
||||
http: createMockHttp(),
|
||||
window: {},
|
||||
next: '',
|
||||
loginState: createLoginState(),
|
||||
isSecureConnection: false,
|
||||
requiresSecureConnection: false,
|
||||
loginAssistanceMessage: 'This is an *important* message',
|
||||
};
|
||||
|
||||
expect(shallow(<LoginPage {...props} />)).toMatchSnapshot();
|
||||
|
@ -106,6 +124,7 @@ describe('LoginPage', () => {
|
|||
loginState: createLoginState(),
|
||||
isSecureConnection: false,
|
||||
requiresSecureConnection: false,
|
||||
loginAssistanceMessage: '',
|
||||
};
|
||||
|
||||
expect(shallow(<LoginPage {...props} />)).toMatchSnapshot();
|
||||
|
|
|
@ -31,6 +31,7 @@ interface Props {
|
|||
loginState: LoginState;
|
||||
isSecureConnection: boolean;
|
||||
requiresSecureConnection: boolean;
|
||||
loginAssistanceMessage: string;
|
||||
}
|
||||
|
||||
export class LoginPage extends Component<Props, {}> {
|
||||
|
|
|
@ -39,7 +39,8 @@ interface AnyObject {
|
|||
$http: AnyObject,
|
||||
$window: AnyObject,
|
||||
secureCookies: boolean,
|
||||
loginState: LoginState
|
||||
loginState: LoginState,
|
||||
loginAssistanceMessage: string
|
||||
) => {
|
||||
const basePath = chrome.getBasePath();
|
||||
const next = parseNext($window.location.href, basePath);
|
||||
|
@ -59,6 +60,7 @@ interface AnyObject {
|
|||
loginState={loginState}
|
||||
isSecureConnection={isSecure}
|
||||
requiresSecureConnection={secureCookies}
|
||||
loginAssistanceMessage={loginAssistanceMessage}
|
||||
next={next}
|
||||
/>
|
||||
</I18nContext>,
|
||||
|
|
|
@ -13,45 +13,48 @@ import { createConfig$, ConfigSchema } from './config';
|
|||
describe('config schema', () => {
|
||||
it('generates proper defaults', () => {
|
||||
expect(ConfigSchema.validate({})).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"authc": Object {
|
||||
"providers": Array [
|
||||
"basic",
|
||||
],
|
||||
},
|
||||
"cookieName": "sid",
|
||||
"encryptionKey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"secureCookies": false,
|
||||
"sessionTimeout": null,
|
||||
}
|
||||
`);
|
||||
Object {
|
||||
"authc": Object {
|
||||
"providers": Array [
|
||||
"basic",
|
||||
],
|
||||
},
|
||||
"cookieName": "sid",
|
||||
"encryptionKey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"loginAssistanceMessage": "",
|
||||
"secureCookies": false,
|
||||
"sessionTimeout": null,
|
||||
}
|
||||
`);
|
||||
|
||||
expect(ConfigSchema.validate({}, { dist: false })).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"authc": Object {
|
||||
"providers": Array [
|
||||
"basic",
|
||||
],
|
||||
},
|
||||
"cookieName": "sid",
|
||||
"encryptionKey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"secureCookies": false,
|
||||
"sessionTimeout": null,
|
||||
}
|
||||
`);
|
||||
Object {
|
||||
"authc": Object {
|
||||
"providers": Array [
|
||||
"basic",
|
||||
],
|
||||
},
|
||||
"cookieName": "sid",
|
||||
"encryptionKey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"loginAssistanceMessage": "",
|
||||
"secureCookies": false,
|
||||
"sessionTimeout": null,
|
||||
}
|
||||
`);
|
||||
|
||||
expect(ConfigSchema.validate({}, { dist: true })).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"authc": Object {
|
||||
"providers": Array [
|
||||
"basic",
|
||||
],
|
||||
},
|
||||
"cookieName": "sid",
|
||||
"secureCookies": false,
|
||||
"sessionTimeout": null,
|
||||
}
|
||||
`);
|
||||
Object {
|
||||
"authc": Object {
|
||||
"providers": Array [
|
||||
"basic",
|
||||
],
|
||||
},
|
||||
"cookieName": "sid",
|
||||
"loginAssistanceMessage": "",
|
||||
"secureCookies": false,
|
||||
"sessionTimeout": null,
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should throw error if xpack.security.encryptionKey is less than 32 characters', () => {
|
||||
|
|
|
@ -26,6 +26,7 @@ const providerOptionsSchema = (providerType: string, optionsSchema: Type<any>) =
|
|||
|
||||
export const ConfigSchema = schema.object(
|
||||
{
|
||||
loginAssistanceMessage: schema.string({ defaultValue: '' }),
|
||||
cookieName: schema.string({ defaultValue: 'sid' }),
|
||||
encryptionKey: schema.conditional(
|
||||
schema.contextRef('dist'),
|
||||
|
|
|
@ -52,6 +52,7 @@ describe('Security Plugin', () => {
|
|||
],
|
||||
},
|
||||
"cookieName": "sid",
|
||||
"loginAssistanceMessage": undefined,
|
||||
"secureCookies": true,
|
||||
"sessionTimeout": 1500,
|
||||
},
|
||||
|
|
|
@ -205,6 +205,7 @@ export class Plugin {
|
|||
// We should stop exposing this config as soon as only new platform plugin consumes it. The only
|
||||
// exception may be `sessionTimeout` as other parts of the app may want to know it.
|
||||
config: {
|
||||
loginAssistanceMessage: config.loginAssistanceMessage,
|
||||
sessionTimeout: config.sessionTimeout,
|
||||
secureCookies: config.secureCookies,
|
||||
cookieName: config.cookieName,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue