Skip to content

Commit

Permalink
Introduce Login Selector functionality.
Browse files Browse the repository at this point in the history
  • Loading branch information
azasypkin committed Mar 10, 2020
1 parent ec1f46b commit f442437
Show file tree
Hide file tree
Showing 34 changed files with 822 additions and 401 deletions.
12 changes: 8 additions & 4 deletions packages/kbn-config-schema/src/internals/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,8 @@ export const internals = Joi.extend([
for (const [entryKey, entryValue] of value) {
const { value: validatedEntryKey, error: keyError } = Joi.validate(
entryKey,
params.key
params.key,
{ presence: 'required' }
);

if (keyError) {
Expand All @@ -323,7 +324,8 @@ export const internals = Joi.extend([

const { value: validatedEntryValue, error: valueError } = Joi.validate(
entryValue,
params.value
params.value,
{ presence: 'required' }
);

if (valueError) {
Expand Down Expand Up @@ -374,7 +376,8 @@ export const internals = Joi.extend([
for (const [entryKey, entryValue] of Object.entries(value)) {
const { value: validatedEntryKey, error: keyError } = Joi.validate(
entryKey,
params.key
params.key,
{ presence: 'required' }
);

if (keyError) {
Expand All @@ -383,7 +386,8 @@ export const internals = Joi.extend([

const { value: validatedEntryValue, error: valueError } = Joi.validate(
entryValue,
params.value
params.value,
{ presence: 'required' }
);

if (valueError) {
Expand Down
5 changes: 1 addition & 4 deletions x-pack/legacy/plugins/security/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,7 @@ export const security = (kibana: Record<string, any>) =>
uiExports: {
hacks: ['plugins/security/hacks/legacy'],
injectDefaultVars: (server: Server) => {
return {
secureCookies: getSecurityPluginSetup(server).__legacyCompat.config.secureCookies,
enableSpaceAwarePrivileges: server.config().get('xpack.spaces.enabled'),
};
return { enableSpaceAwarePrivileges: server.config().get('xpack.spaces.enabled') };
},
},

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,17 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { LoginLayout } from '../../../common/licensing';
import { LoginLayout } from './licensing';

interface LoginSelector {
enabled: boolean;
providers: Array<{ type: string; name: string; options: { description: string; order: number } }>;
}

export interface LoginState {
layout: LoginLayout;
allowLogin: boolean;
showLoginForm: boolean;
requiresSecureConnection: boolean;
selector: LoginSelector;
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,7 @@
max-width: 700px;
}
}

.loginWelcome__selectorButton {
margin-bottom: $euiSize;
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ export const loginApp = Object.freeze({
http: coreStart.http,
fatalErrors: coreStart.fatalErrors,
loginAssistanceMessage: config.loginAssistanceMessage,
requiresSecureConnection: coreStart.injectedMetadata.getInjectedVar(
'secureCookies'
) as boolean,
});
},
});
Expand Down
219 changes: 140 additions & 79 deletions x-pack/plugins/security/public/authentication/login/login_page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,17 @@ import ReactDOM from 'react-dom';
import classNames from 'classnames';
import { BehaviorSubject } from 'rxjs';
import { parse } from 'url';
import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';
import { EuiButton, EuiIcon, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { CoreStart, FatalErrorsStart, HttpStart } from 'src/core/public';
import { LoginLayout } from '../../../common/licensing';
import { LoginState } from '../../../common/login_state';
import { BasicLoginForm, DisabledLoginForm } from './components';
import { LoginState } from './login_state';

interface Props {
http: HttpStart;
fatalErrors: FatalErrorsStart;
loginAssistanceMessage: string;
requiresSecureConnection: boolean;
}

interface State {
Expand Down Expand Up @@ -67,12 +65,10 @@ export class LoginPage extends Component<Props, State> {
}

const isSecureConnection = !!window.location.protocol.match(/^https/);
const { allowLogin, layout } = loginState;
const { allowLogin, layout, requiresSecureConnection } = loginState;

const loginIsSupported =
this.props.requiresSecureConnection && !isSecureConnection
? false
: allowLogin && layout === 'form';
requiresSecureConnection && !isSecureConnection ? false : allowLogin && layout === 'form';

const contentHeaderClasses = classNames('loginWelcome__content', 'eui-textCenter', {
['loginWelcome__contentDisabledForm']: !loginIsSupported,
Expand Down Expand Up @@ -110,22 +106,40 @@ export class LoginPage extends Component<Props, State> {
</div>
</header>
<div className={contentBodyClasses}>
<EuiFlexGroup gutterSize="l">
<EuiFlexItem>{this.getLoginForm({ isSecureConnection, layout })}</EuiFlexItem>
</EuiFlexGroup>
{this.getLoginForm({ ...loginState, isSecureConnection })}
</div>
</div>
);
}

private getLoginForm = ({
isSecureConnection,
layout,
}: {
isSecureConnection: boolean;
layout: LoginLayout;
}) => {
if (this.props.requiresSecureConnection && !isSecureConnection) {
requiresSecureConnection,
isSecureConnection,
selector,
showLoginForm,
}: LoginState & { isSecureConnection: boolean }) => {
const showLoginSelector = selector.providers.length > 0;
if (!showLoginSelector && !showLoginForm) {
return (
<DisabledLoginForm
title={
<FormattedMessage
id="xpack.security.loginPage.noLoginMethodsAvailableTitle"
defaultMessage="Login is disabled."
/>
}
message={
<FormattedMessage
id="xpack.security.loginPage.noLoginMethodsAvailableMessage"
defaultMessage="Contact your system administrator."
/>
}
/>
);
}

if (requiresSecureConnection && !isSecureConnection) {
return (
<DisabledLoginForm
title={
Expand All @@ -144,69 +158,116 @@ export class LoginPage extends Component<Props, State> {
);
}

switch (layout) {
case 'form':
return (
<BasicLoginForm
http={this.props.http}
infoMessage={infoMessageMap.get(
parse(window.location.href, true).query.msg?.toString()
)}
loginAssistanceMessage={this.props.loginAssistanceMessage}
/>
);
case 'error-es-unavailable':
return (
<DisabledLoginForm
title={
<FormattedMessage
id="xpack.security.loginPage.esUnavailableTitle"
defaultMessage="Cannot connect to the Elasticsearch cluster"
/>
}
message={
<FormattedMessage
id="xpack.security.loginPage.esUnavailableMessage"
defaultMessage="See the Kibana logs for details and try reloading the page."
/>
}
/>
);
case 'error-xpack-unavailable':
return (
<DisabledLoginForm
title={
<FormattedMessage
id="xpack.security.loginPage.xpackUnavailableTitle"
defaultMessage="Cannot connect to the Elasticsearch cluster currently configured for Kibana."
/>
}
message={
<FormattedMessage
id="xpack.security.loginPage.xpackUnavailableMessage"
defaultMessage="To use the full set of free features in this distribution of Kibana, please update Elasticsearch to the default distribution."
/>
}
/>
);
default:
return (
<DisabledLoginForm
title={
<FormattedMessage
id="xpack.security.loginPage.unknownLayoutTitle"
defaultMessage="Unsupported login form layout."
/>
}
message={
<FormattedMessage
id="xpack.security.loginPage.unknownLayoutMessage"
defaultMessage="Refer to the Kibana logs for more details and refresh to try again."
/>
}
/>
);
if (layout === 'error-es-unavailable') {
return (
<DisabledLoginForm
title={
<FormattedMessage
id="xpack.security.loginPage.esUnavailableTitle"
defaultMessage="Cannot connect to the Elasticsearch cluster"
/>
}
message={
<FormattedMessage
id="xpack.security.loginPage.esUnavailableMessage"
defaultMessage="See the Kibana logs for details and try reloading the page."
/>
}
/>
);
}

if (layout === 'error-xpack-unavailable') {
return (
<DisabledLoginForm
title={
<FormattedMessage
id="xpack.security.loginPage.xpackUnavailableTitle"
defaultMessage="Cannot connect to the Elasticsearch cluster currently configured for Kibana."
/>
}
message={
<FormattedMessage
id="xpack.security.loginPage.xpackUnavailableMessage"
defaultMessage="To use the full set of free features in this distribution of Kibana, please update Elasticsearch to the default distribution."
/>
}
/>
);
}

if (layout !== 'form') {
return (
<DisabledLoginForm
title={
<FormattedMessage
id="xpack.security.loginPage.unknownLayoutTitle"
defaultMessage="Unsupported login form layout."
/>
}
message={
<FormattedMessage
id="xpack.security.loginPage.unknownLayoutMessage"
defaultMessage="Refer to the Kibana logs for more details and refresh to try again."
/>
}
/>
);
}

const loginSelector =
showLoginSelector &&
selector.providers.map((provider, index) => (
<EuiButton
key={index}
className="loginWelcome__selectorButton"
iconType="user"
fullWidth={true}
onClick={() => this.login(provider.type, provider.name)}
>
{provider.options.description}
</EuiButton>
));

const loginSelectorAndLoginFormSeparator = showLoginSelector && showLoginForm && (
<>
<EuiText textAlign="center" color="subdued">
―――&nbsp;&nbsp;
<FormattedMessage id="xpack.security.loginPage.loginSelectorOR" defaultMessage="OR" />
&nbsp;&nbsp;―――
</EuiText>
<EuiSpacer size="m" />
</>
);

const loginForm = showLoginForm && (
<BasicLoginForm
http={this.props.http}
infoMessage={infoMessageMap.get(parse(window.location.href, true).query.msg?.toString())}
loginAssistanceMessage={this.props.loginAssistanceMessage}
/>
);

return (
<>
{loginSelector}
{loginSelectorAndLoginFormSeparator}
{loginForm}
</>
);
};

private login = (providerType: string, providerName: string) => {
const query = parse(window.location.href, true).query;
const next =
Array.isArray(query.next) && query.next.length > 0 ? query.next[0] : (query.next as string);
const queryString = next ? `?next=${encodeURIComponent(next)}${window.location.hash}` : '';

window.location.href = `${
this.props.http.basePath.serverBasePath
}/internal/security/login/${encodeURIComponent(providerType)}/${encodeURIComponent(
providerName
)}${queryString}`;
};
}

Expand Down
Loading

0 comments on commit f442437

Please sign in to comment.