Fix anonymous access to status page (#24706)

* fix status.allowAnonymous

* address PR feedback
This commit is contained in:
Larry Gregory 2018-11-14 16:28:26 -05:00 committed by GitHub
parent c26dc55759
commit d8f8a0c26d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 128 additions and 6 deletions

View file

@ -18,6 +18,7 @@
*/
import 'ui/autoload/styles';
import 'ui/i18n';
import { uiModules } from 'ui/modules';
import chrome from 'ui/chrome';
import { destroyStatusPage, renderStatusPage } from './components/render';

View file

@ -20,14 +20,21 @@
import { wrapAuthConfig } from '../../wrap_auth_config';
export function registerStatusPage(kbnServer, server, config) {
const wrapAuth = wrapAuthConfig(config.get('status.allowAnonymous'));
const allowAnonymous = config.get('status.allowAnonymous');
const wrapAuth = wrapAuthConfig(allowAnonymous);
server.decorate('toolkit', 'renderStatusPage', async function () {
const app = server.getHiddenUiAppById('status_page');
const h = this;
const response = app
? await h.renderApp(app)
: h.response(kbnServer.status.toString());
let response;
// An unauthenticated (anonymous) user may not have access to the customized configuration.
// For this scenario, render with the default config.
if (app) {
response = allowAnonymous ? await h.renderAppWithDefaultConfig(app) : await h.renderApp(app);
} else {
h.response(kbnServer.status.toString());
}
if (response) {
return response.code(kbnServer.status.isGreen() ? 200 : 503);

View file

@ -9,6 +9,8 @@ import { SpacesManager } from 'plugins/spaces/lib/spaces_manager';
// @ts-ignore
import template from 'plugins/spaces/views/nav_control/nav_control.html';
import { NavControlPopover } from 'plugins/spaces/views/nav_control/nav_control_popover';
// @ts-ignore
import { PathProvider } from 'plugins/xpack_main/services/path';
import { UserProfileProvider } from 'plugins/xpack_main/services/user_profile';
import React from 'react';
import ReactDOM from 'react-dom';
@ -45,6 +47,7 @@ module.controller(
'spacesNavController',
($scope: any, $http: any, chrome: any, Private: any, activeSpace: any) => {
const userProfile = Private(UserProfileProvider);
const pathProvider = Private(PathProvider);
const domNode = document.getElementById(`spacesNavReactRoot`);
const spaceSelectorURL = chrome.getInjected('spaceSelectorURL');
@ -54,7 +57,7 @@ module.controller(
let mounted = false;
$scope.$parent.$watch('isVisible', function isVisibleWatcher(isVisible: boolean) {
if (isVisible && !mounted) {
if (isVisible && !mounted && !pathProvider.isUnauthenticated()) {
render(
<NavControlPopover
spacesManager={spacesManager}
@ -99,6 +102,11 @@ chromeHeaderNavControlsRegistry.register(
side: NavControlSide.Left,
render(el: HTMLElement) {
const userProfile = Private(UserProfileProvider);
const pathProvider = Private(PathProvider);
if (pathProvider.isUnauthenticated()) {
return;
}
const spaceSelectorURL = chrome.getInjected('spaceSelectorURL');

View file

@ -10,7 +10,7 @@ export function PathProvider($window) {
const path = chrome.removeBasePath($window.location.pathname);
return {
isUnauthenticated() {
return path === '/login' || path === '/logout' || path === '/logged_out';
return path === '/login' || path === '/logout' || path === '/logged_out' || path === '/status';
}
};
}

View file

@ -0,0 +1,13 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { TestInvoker } from './lib/types';
// tslint:disable:no-default-export
export default function statusPage({ loadTestFile }: TestInvoker) {
describe('Status page', function statusPageTestSuite() {
loadTestFile(require.resolve('./status_page'));
});
}

View file

@ -0,0 +1,27 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
export type DescribeFn = (text: string, fn: () => void) => void;
export interface TestDefinitionAuthentication {
username?: string;
password?: string;
}
export type LoadTestFileFn = (path: string) => string;
export type GetServiceFn = (service: string) => any;
export type ReadConfigFileFn = (path: string) => any;
export type GetPageObjectsFn = (pageObjects: string[]) => any;
export interface TestInvoker {
getService: GetServiceFn;
getPageObjects: GetPageObjectsFn;
loadTestFile: LoadTestFileFn;
readConfigFile: ReadConfigFileFn;
}

View file

@ -0,0 +1,22 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { TestInvoker } from './lib/types';
// tslint:disable:no-default-export
export default function statusPageFunctonalTests({ getService, getPageObjects }: TestInvoker) {
const esArchiver = getService('esArchiver');
const PageObjects = getPageObjects(['security', 'statusPage', 'home']);
describe('Status Page', () => {
before(async () => await esArchiver.load('empty_kibana'));
after(async () => await esArchiver.unload('empty_kibana'));
it('allows user to navigate without authentication', async () => {
await PageObjects.statusPage.navigateToPage();
await PageObjects.statusPage.expectStatusPage();
});
});
}

View file

@ -19,6 +19,7 @@ import {
SpaceSelectorPageProvider,
AccountSettingProvider,
InfraHomePageProvider,
StatusPagePageProvider,
} from './page_objects';
import {
@ -71,6 +72,7 @@ export default async function ({ readConfigFile }) {
resolve(__dirname, './apps/logstash'),
resolve(__dirname, './apps/grok_debugger'),
resolve(__dirname, './apps/infra'),
resolve(__dirname, './apps/status_page'),
],
// define the name and providers for services that should be
@ -121,6 +123,7 @@ export default async function ({ readConfigFile }) {
reporting: ReportingPageProvider,
spaceSelector: SpaceSelectorPageProvider,
infraHome: InfraHomePageProvider,
statusPage: StatusPagePageProvider,
},
servers: kibanaFunctionalConfig.get('servers'),
@ -138,6 +141,7 @@ export default async function ({ readConfigFile }) {
...kibanaCommonConfig.get('kbnTestServer'),
serverArgs: [
...kibanaCommonConfig.get('kbnTestServer.serverArgs'),
'--status.allowAnonymous=true',
'--server.uuid=5b2de169-2785-441b-ae8c-186a1936b17d',
'--xpack.xpack_main.telemetry.enabled=false',
'--xpack.security.encryptionKey="wuGNaIhoMpk5sO4UBxgr3NyW1sFcLgIf"', // server restarts should not invalidate active sessions

View file

@ -14,3 +14,4 @@ export { ReportingPageProvider } from './reporting_page';
export { SpaceSelectorPageProvider } from './space_selector_page';
export { AccountSettingProvider } from './accountsetting_page';
export { InfraHomePageProvider } from './infra_home_page';
export { StatusPagePageProvider } from './status_page';

View file

@ -0,0 +1,39 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import expect from 'expect.js';
export function StatusPagePageProvider({ getService, getPageObjects }) {
const retry = getService('retry');
const log = getService('log');
const remote = getService('remote');
const PageObjects = getPageObjects(['common', 'home', 'security']);
class StatusPage {
async initTests() {
log.debug('StatusPage:initTests');
}
async navigateToPage() {
return await retry.try(async () => {
const url = PageObjects.common.getHostPort() + '/status';
log.info(`StatusPage:navigateToPage(): ${url}`);
await remote.get(url);
});
}
async expectStatusPage() {
return await retry.try(async () => {
log.debug(`expectStatusPage()`);
await remote.setFindTimeout(20000).findByCssSelector('[data-test-subj="kibanaChrome"] nav:not(.ng-hide) ');
const url = await remote.getCurrentUrl();
expect(url).to.contain(`/status`);
});
}
}
return new StatusPage();
}