[SECURITY] Stop kibana crashes when no authentication providers are enabled (#118784)

* check if a provider is there

* the last puzzle

* fix lint

* review oleg

* Update x-pack/plugins/security/public/nav_control/nav_control_component.tsx

Co-authored-by: Aleh Zasypkin <aleh.zasypkin@gmail.com>

* Update x-pack/test/security_api_integration/http_no_auth_providers.config.ts

Co-authored-by: Aleh Zasypkin <aleh.zasypkin@gmail.com>

* Update x-pack/plugins/security/server/authentication/authenticator.test.ts

Co-authored-by: Aleh Zasypkin <aleh.zasypkin@gmail.com>

* Update x-pack/test/security_api_integration/tests/http_no_auth_providers/authentication.ts

Co-authored-by: Aleh Zasypkin <aleh.zasypkin@gmail.com>

* Update x-pack/test/security_api_integration/http_no_auth_providers.config.ts

Co-authored-by: Aleh Zasypkin <aleh.zasypkin@gmail.com>

* review II

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Aleh Zasypkin <aleh.zasypkin@gmail.com>
This commit is contained in:
Xavier Mouligneau 2021-11-19 11:06:34 -05:00 committed by GitHub
parent 1ac885e428
commit 55fa55ddc9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 131 additions and 6 deletions

View file

@ -128,6 +128,19 @@ describe('AuthenticationService', () => {
expect.any(Function)
);
});
it('properly registers auth handler with no providers', () => {
mockSetupAuthenticationParams.config.authc = {
...mockSetupAuthenticationParams.config.authc,
sortedProviders: [],
};
service.setup(mockSetupAuthenticationParams);
expect(mockSetupAuthenticationParams.http.registerAuth).toHaveBeenCalledTimes(1);
expect(mockSetupAuthenticationParams.http.registerAuth).toHaveBeenCalledWith(
expect.any(Function)
);
});
});
describe('#start()', () => {

View file

@ -98,7 +98,8 @@ export class AuthenticationService {
// 2. Login selector is disabled, but the provider with the lowest `order` uses login form
const isLoginPageAvailable =
config.authc.selector.enabled ||
shouldProviderUseLoginForm(config.authc.sortedProviders[0].type);
(config.authc.sortedProviders.length > 0 &&
shouldProviderUseLoginForm(config.authc.sortedProviders[0].type));
http.registerAuth(async (request, response, t) => {
if (!license.isLicenseAvailable()) {

View file

@ -1872,6 +1872,24 @@ describe('Authenticator', () => {
expect(mockOptions.session.invalidate).not.toHaveBeenCalled();
});
it('if session does not exist and providers is empty, redirects to default logout path.', async () => {
const request = httpServerMock.createKibanaRequest();
mockOptions = getMockOptions({
providers: { basic: { basic1: { order: 0, enabled: false } } },
});
authenticator = new Authenticator(mockOptions);
await expect(authenticator.logout(request)).resolves.toEqual(
DeauthenticationResult.redirectTo(
'/mock-server-basepath/security/logged_out?msg=LOGGED_OUT'
)
);
expect(mockBasicAuthenticationProvider.logout).not.toHaveBeenCalled();
expect(mockOptions.session.invalidate).not.toHaveBeenCalled();
});
it('redirects to login form if session does not exist and provider name is invalid', async () => {
const request = httpServerMock.createKibanaRequest({ query: { provider: 'foo' } });
mockOptions.session.get.mockResolvedValue(null);

View file

@ -806,10 +806,7 @@ export class Authenticator {
* @param providerType Type of the provider that handles logout. If not specified, then the first
* provider in the chain (default) is assumed.
*/
private getLoggedOutURL(
request: KibanaRequest,
providerType: string = this.options.config.authc.sortedProviders[0].type
) {
private getLoggedOutURL(request: KibanaRequest, providerType?: string) {
// The app that handles logout needs to know the reason of the logout and the URL we may need to
// redirect user to once they log in again (e.g. when session expires).
const searchParams = new URLSearchParams();
@ -825,7 +822,12 @@ export class Authenticator {
// Query string may contain the path where logout has been called or
// logout reason that login page may need to know.
return this.options.config.authc.selector.enabled || shouldProviderUseLoginForm(providerType)
return this.options.config.authc.selector.enabled ||
(providerType
? shouldProviderUseLoginForm(providerType)
: this.options.config.authc.sortedProviders.length > 0
? shouldProviderUseLoginForm(this.options.config.authc.sortedProviders[0].type)
: false)
? `${this.options.basePath.serverBasePath}/login?${searchParams.toString()}`
: `${this.options.basePath.serverBasePath}/security/logged_out?${searchParams.toString()}`;
}

View file

@ -53,6 +53,7 @@ const onlyNotInCoverageTests = [
require.resolve('../test/security_api_integration/login_selector.config.ts'),
require.resolve('../test/security_api_integration/audit.config.ts'),
require.resolve('../test/security_api_integration/http_bearer.config.ts'),
require.resolve('../test/security_api_integration/http_no_auth_providers.config.ts'),
require.resolve('../test/security_api_integration/kerberos.config.ts'),
require.resolve('../test/security_api_integration/kerberos_anonymous_access.config.ts'),
require.resolve('../test/security_api_integration/pki.config.ts'),

View file

@ -0,0 +1,36 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { FtrConfigProviderContext } from '@kbn/test';
import { services } from './services';
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
const xPackAPITestsConfig = await readConfigFile(require.resolve('../api_integration/config.ts'));
return {
testFiles: [require.resolve('./tests/http_no_auth_providers')],
servers: xPackAPITestsConfig.get('servers'),
security: { disableTestUser: true },
services,
junit: {
reportName: 'X-Pack Security API Integration Tests (HTTP without providers)',
},
esTestCluster: xPackAPITestsConfig.get('esTestCluster'),
kbnTestServer: {
...xPackAPITestsConfig.get('kbnTestServer'),
serverArgs: [
...xPackAPITestsConfig.get('kbnTestServer.serverArgs'),
`--xpack.security.authc.http.schemes=${JSON.stringify(['apikey', 'basic'])}`,
`--xpack.security.authc.providers=${JSON.stringify({
basic: { basic1: { order: 0, enabled: false } },
})}`,
],
},
};
}

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
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import { adminTestUser } from '@kbn/test';
import { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertestWithoutAuth');
describe('authorization', () => {
it('fail request without authorization request header', async () => {
await supertest.get('/internal/security/me').set('kbn-xsrf', 'true').expect(401);
});
it('accept request with authorization request header', async () => {
const credentials = Buffer.from(
`${adminTestUser.username}:${adminTestUser.password}`
).toString('base64');
const { body: user, headers } = await supertest
.get('/internal/security/me')
.set('kbn-xsrf', 'xxx')
.set('Authorization', `Basic ${credentials}`)
.expect(200);
expect(user.username).to.eql(adminTestUser.username);
expect(user.authentication_provider).to.eql({ type: 'http', name: '__http__' });
expect(user.authentication_type).to.eql('realm');
// Make sure we don't automatically create a session
expect(headers['set-cookie']).to.be(undefined);
});
});
}

View file

@ -0,0 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('security APIs - HTTP no authentication providers are enabled', function () {
this.tags('ciGroup6');
loadTestFile(require.resolve('./authentication'));
});
}