From d6071082b28ca9459b15814db80553a1d1a4812d Mon Sep 17 00:00:00 2001 From: Tyler Smalley Date: Tue, 24 Jul 2018 16:48:51 -0700 Subject: [PATCH] [security] Improve communication for ES/X-Pack being unavailable (#21124) 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. --- x-pack/plugins/security/index.js | 5 +++-- .../security/public/views/login/login.html | 13 ++++++++++- .../security/public/views/login/login.js | 6 ++--- .../security/public/views/login/login.less | 6 +++++ .../server/lib/__tests__/check_license.js | 22 +++++++++++++++++-- .../security/server/lib/check_license.js | 2 +- .../server/lib/__tests__/xpack_info.js | 16 ++++++++++++++ .../xpack_main/server/lib/xpack_info.js | 10 ++++++++- 8 files changed, 69 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/security/index.js b/x-pack/plugins/security/index.js index 5e4602d448084..d0ea7284f2711 100644 --- a/x-pack/plugins/security/index.js +++ b/x-pack/plugins/security/index.js @@ -101,13 +101,14 @@ export const security = (kibana) => new kibana.Plugin({ server.injectUiAppVars('login', () => { const pluginId = 'security'; const xpackInfo = server.plugins.xpack_main.info; - const { showLogin, loginMessage, allowLogin } = xpackInfo.feature(pluginId).getLicenseCheckResults() || {}; + const { showLogin, loginMessage, allowLogin, layout = 'form' } = xpackInfo.feature(pluginId).getLicenseCheckResults() || {}; return { loginState: { showLogin, allowLogin, - loginMessage + loginMessage, + layout, } }; }); diff --git a/x-pack/plugins/security/public/views/login/login.html b/x-pack/plugins/security/public/views/login/login.html index 1f407299af53a..775fbbc9f7c77 100644 --- a/x-pack/plugins/security/public/views/login/login.html +++ b/x-pack/plugins/security/public/views/login/login.html @@ -3,7 +3,7 @@ -
+
+
+

Cannot connect to the Elasticsearch cluster currently configured for Kibana.

+

Refer to the Kibana logs for more details and refresh to try again.

+
+ +
+

Cannot connect to an Elasticsearch cluster running the OSS distribution from an instance of Kibana that has a Basic license or above.

+

Upgrade Elasticsearch to the default distribution, or use the OSS version of Kibana.

+

Refresh to try again.

+
+
diff --git a/x-pack/plugins/security/public/views/login/login.js b/x-pack/plugins/security/public/views/login/login.js index d83f0ac076e98..a7671bf788242 100644 --- a/x-pack/plugins/security/public/views/login/login.js +++ b/x-pack/plugins/security/public/views/login/login.js @@ -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; diff --git a/x-pack/plugins/security/public/views/login/login.less b/x-pack/plugins/security/public/views/login/login.less index 97845d3830b68..2336833640116 100644 --- a/x-pack/plugins/security/public/views/login/login.less +++ b/x-pack/plugins/security/public/views/login/login.less @@ -117,3 +117,9 @@ input.form-control { font-size: 1.125em; height: auto; } + +.loginErrorEsUnavailable, +.loginErrorXpackUnavailable { + width: 550px; + z-index: 10; +} \ No newline at end of file diff --git a/x-pack/plugins/security/server/lib/__tests__/check_license.js b/x-pack/plugins/security/server/lib/__tests__/check_license.js index 63f2502226774..ead0c64668e2a 100644 --- a/x-pack/plugins/security/server/lib/__tests__/check_license.js +++ b/x-pack/plugins/security/server/lib/__tests__/check_license.js @@ -15,6 +15,7 @@ describe('check_license', function () { beforeEach(function () { mockXPackInfo = { isAvailable: sinon.stub(), + isXpackUnavailable: sinon.stub(), feature: sinon.stub(), license: sinon.stub({ isOneOf() {}, @@ -25,8 +26,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({ @@ -35,10 +37,26 @@ describe('check_license', function () { showLinks: false, allowRoleDocumentLevelSecurity: false, allowRoleFieldLevelSecurity: false, - loginMessage: 'Login is currently disabled. Administrators should consult the Kibana logs for more details.' + layout: 'error-es-unavailable', }); }); + 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', + }); + }); + + it('should not show login page or other security elements if license is basic.', () => { mockXPackInfo.license.isOneOf.withArgs(['basic']).returns(true); mockXPackInfo.license.isActive.returns(true); diff --git a/x-pack/plugins/security/server/lib/check_license.js b/x-pack/plugins/security/server/lib/check_license.js index 624f80e64be79..b4add890b38c0 100644 --- a/x-pack/plugins/security/server/lib/check_license.js +++ b/x-pack/plugins/security/server/lib/check_license.js @@ -33,7 +33,7 @@ export function checkLicense(xPackInfo) { showLinks: false, allowRoleDocumentLevelSecurity: false, allowRoleFieldLevelSecurity: false, - loginMessage: 'Login is currently disabled. Administrators should consult the Kibana logs for more details.' + layout: xPackInfo.isXpackUnavailable() ? 'error-xpack-unavailable' : 'error-es-unavailable' }; } diff --git a/x-pack/plugins/xpack_main/server/lib/__tests__/xpack_info.js b/x-pack/plugins/xpack_main/server/lib/__tests__/xpack_info.js index 4d896268d4d8a..e377b0bb33f54 100644 --- a/x-pack/plugins/xpack_main/server/lib/__tests__/xpack_info.js +++ b/x-pack/plugins/xpack_main/server/lib/__tests__/xpack_info.js @@ -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); diff --git a/x-pack/plugins/xpack_main/server/lib/xpack_info.js b/x-pack/plugins/xpack_main/server/lib/xpack_info.js index 3a8d00904885e..ca4a68ba0ad41 100644 --- a/x-pack/plugins/xpack_main/server/lib/xpack_info.js +++ b/x-pack/plugins/xpack_main/server/lib/xpack_info.js @@ -94,6 +94,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} @@ -103,7 +111,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.`; }