Skip to content

Commit

Permalink
[security] Improve communication for ES/X-Pack being unavailable (#21124
Browse files Browse the repository at this point in the history
) (#21183)

Previously if Elasticsearch was unavailable in the Kibana default distribution,
you would be prompted with a disabled login screen stating "Login is currently
disabled. Administrators should consult the Kibana logs for more details". This
was rather confusing for users who have a Basic license.

This now provides the user with a screen providing only the required
messaging.

Additionally, if you were using Kibana with a Basic license with an OSS
distribution of Elasticsearch, you would see the same disabled login
screen as mentioned previously.

This also separates the messaging there to provide clear details for the
user to resolve the issue.
  • Loading branch information
tylersmalley authored Jul 25, 2018
1 parent 928e6f0 commit 390d9bc
Show file tree
Hide file tree
Showing 8 changed files with 70 additions and 11 deletions.
5 changes: 3 additions & 2 deletions x-pack/plugins/security/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,13 +156,14 @@ export const security = (kibana) => new kibana.Plugin({
initLogoutView(server);

server.injectUiAppVars('login', () => {
const { showLogin, loginMessage, allowLogin } = xpackInfo.feature(plugin.id).getLicenseCheckResults() || {};
const { showLogin, loginMessage, allowLogin, layout = 'form' } = xpackInfo.feature(plugin.id).getLicenseCheckResults() || {};

return {
loginState: {
showLogin,
allowLogin,
loginMessage
loginMessage,
layout,
}
};
});
Expand Down
13 changes: 12 additions & 1 deletion x-pack/plugins/security/public/views/login/login.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<div class="kibanaWelcomeLogo"></div>
</div>

<div class="form-container">
<div class="form-container" ng-if="login.layout === 'form'">
<form class="login-form" ng-submit="login.submit(username, password)">
<div ng-show="login.error" class="form-group error-message">
<label class="control-label" data-test-subj="loginErrorMessage" >Oops! Error. Try again.</label>
Expand Down Expand Up @@ -44,4 +44,15 @@
</form>
</div>

<div class="euiText loginErrorEsUnavailable" ng-if="login.layout === 'error-es-unavailable'">
<p class="euiTitle euiTitle--medium euiTextColor euiTextColor--danger">Cannot connect to the Elasticsearch cluster currently configured for Kibana.</p>
<p>Refer to the Kibana logs for more details and refresh to try again.</p>
</div>

<div class="euiText loginErrorXpackUnavailable" ng-if="login.layout === 'error-xpack-unavailable'">
<p class="euiTitle euiTitle--medium euiTextColor euiTextColor--danger">Cannot connect to an Elasticsearch cluster running the OSS distribution from an instance of Kibana that has a Basic license or above.</p>
<p>Upgrade Elasticsearch to the default distribution, or use the OSS version of Kibana.</p>
<p>Refresh to try again.</p>
</div>

</div>
6 changes: 2 additions & 4 deletions x-pack/plugins/security/public/views/login/login.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,9 @@ chrome
const self = this;

function setupScope() {
const defaultLoginMessage = 'Login is currently disabled because the license could not be determined. '
+ 'Please check that Elasticsearch is running, then refresh this page.';

self.layout = loginState.layout;
self.allowLogin = loginState.allowLogin;
self.loginMessage = loginState.loginMessage || defaultLoginMessage;
self.loginMessage = loginState.loginMessage;
self.infoMessage = get(messageMap, parse($window.location.href, true).query.msg);
self.isDisabled = !isSecure && secureCookies;
self.isLoading = false;
Expand Down
6 changes: 6 additions & 0 deletions x-pack/plugins/security/public/views/login/login.less
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,9 @@ input.form-control {
font-size: 1.125em;
height: auto;
}

.loginErrorEsUnavailable,
.loginErrorXpackUnavailable {
width: 550px;
z-index: 10;
}
23 changes: 21 additions & 2 deletions x-pack/plugins/security/server/lib/__tests__/check_license.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ describe('check_license', function () {
beforeEach(function () {
mockXPackInfo = {
isAvailable: sinon.stub(),
isXpackUnavailable: sinon.stub(),
feature: sinon.stub(),
license: sinon.stub({
isOneOf() {},
Expand All @@ -24,8 +25,9 @@ describe('check_license', function () {
mockXPackInfo.isAvailable.returns(true);
});

it('should show login page but not allow login if license information is not available.', () => {
it('should display error when ES is unavailable', () => {
mockXPackInfo.isAvailable.returns(false);
mockXPackInfo.isXpackUnavailable.returns(false);

const licenseCheckResults = checkLicense(mockXPackInfo);
expect(licenseCheckResults).to.be.eql({
Expand All @@ -34,11 +36,28 @@ describe('check_license', function () {
showLinks: false,
allowRoleDocumentLevelSecurity: false,
allowRoleFieldLevelSecurity: false,
layout: 'error-es-unavailable',
allowRbac: false,
loginMessage: 'Login is currently disabled. Administrators should consult the Kibana logs for more details.'
});
});

it('should display error when X-Pack is unavailable', () => {
mockXPackInfo.isAvailable.returns(false);
mockXPackInfo.isXpackUnavailable.returns(true);

const licenseCheckResults = checkLicense(mockXPackInfo);
expect(licenseCheckResults).to.be.eql({
showLogin: true,
allowLogin: false,
showLinks: false,
allowRoleDocumentLevelSecurity: false,
allowRoleFieldLevelSecurity: false,
layout: 'error-xpack-unavailable',
allowRbac: false,
});
});


it('should not show login page or other security elements if license is basic.', () => {
mockXPackInfo.license.isOneOf.withArgs(['basic']).returns(true);
mockXPackInfo.feature.withArgs('security').returns({
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/security/server/lib/check_license.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export function checkLicense(xPackInfo) {
allowRoleDocumentLevelSecurity: false,
allowRoleFieldLevelSecurity: false,
allowRbac: false,
loginMessage: 'Login is currently disabled. Administrators should consult the Kibana logs for more details.'
layout: xPackInfo.isXpackUnavailable() ? 'error-xpack-unavailable' : 'error-es-unavailable'
};
}

Expand Down
16 changes: 16 additions & 0 deletions x-pack/plugins/xpack_main/server/lib/__tests__/xpack_info.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,22 @@ describe('XPackInfo', () => {
expect(xPackInfo.license.isActive()).to.be(true);
});


it('communicates X-Pack being unavailable', async () => {
const badRequestError = new Error('Bad request');
badRequestError.status = 400;

mockElasticsearchCluster.callWithInternalUser.returns(Promise.reject(badRequestError));
await xPackInfo.refreshNow();

expect(xPackInfo.isAvailable()).to.be(false);
expect(xPackInfo.isXpackUnavailable()).to.be(true);
expect(xPackInfo.license.isActive()).to.be(false);
expect(xPackInfo.unavailableReason()).to.be(
'X-Pack plugin is not installed on the [data] Elasticsearch cluster.'
);
});

it('correctly updates xpack info if Elasticsearch API fails.', async () => {
expect(xPackInfo.isAvailable()).to.be(true);
expect(xPackInfo.license.isActive()).to.be(true);
Expand Down
10 changes: 9 additions & 1 deletion x-pack/plugins/xpack_main/server/lib/xpack_info.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,14 @@ export class XPackInfo {
return !!this._cache.response && !!this._cache.response.license;
}

/**
* Checks whether ES was available
* @returns {boolean}
*/
isXpackUnavailable() {
return this._cache.error instanceof Error && this._cache.error.status === 400;
}

/**
* If present, describes the reason why XPack info is not available.
* @returns {Error|string}
Expand All @@ -104,7 +112,7 @@ export class XPackInfo {
return `[${this._clusterSource}] Elasticsearch cluster did not respond with license information.`;
}

if (this._cache.error instanceof Error && this._cache.error.status === 400) {
if (this.isXpackUnavailable()) {
return `X-Pack plugin is not installed on the [${this._clusterSource}] Elasticsearch cluster.`;
}

Expand Down

0 comments on commit 390d9bc

Please sign in to comment.