Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[security] Improve communication for ES/X-Pack being unavailable #21124

Merged
merged 4 commits into from
Jul 24, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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