[security] Improve communication for ES/X-Pack being unavailable (#21124) (#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.
This commit is contained in:
Tyler Smalley 2018-07-24 20:37:35 -07:00 committed by GitHub
parent 928e6f0898
commit 390d9bcd63
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 70 additions and 11 deletions

View file

@ -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,
}
};
});

View file

@ -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>
@ -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>

View file

@ -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;

View file

@ -117,3 +117,9 @@ input.form-control {
font-size: 1.125em;
height: auto;
}
.loginErrorEsUnavailable,
.loginErrorXpackUnavailable {
width: 550px;
z-index: 10;
}

View file

@ -15,6 +15,7 @@ describe('check_license', function () {
beforeEach(function () {
mockXPackInfo = {
isAvailable: sinon.stub(),
isXpackUnavailable: sinon.stub(),
feature: sinon.stub(),
license: sinon.stub({
isOneOf() {},
@ -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({
@ -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({

View file

@ -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'
};
}

View file

@ -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);

View file

@ -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}
@ -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.`;
}