mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
Make Kerberos authentication provider work with Elastic anonymous access. (#40994)
This commit is contained in:
parent
7731e5ba94
commit
6a29ef4105
5 changed files with 99 additions and 16 deletions
|
@ -36,13 +36,28 @@ exports.NativeRealm = class NativeRealm {
|
|||
`setting ${chalk.bold(username)} password to ${chalk.bold(password)}`
|
||||
);
|
||||
|
||||
await this._client.security.changePassword({
|
||||
username,
|
||||
refresh: 'wait_for',
|
||||
body: {
|
||||
password,
|
||||
},
|
||||
});
|
||||
try {
|
||||
await this._client.security.changePassword({
|
||||
username,
|
||||
refresh: 'wait_for',
|
||||
body: {
|
||||
password,
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
const isAnonymousUserPasswordChangeError =
|
||||
err.statusCode === 400 &&
|
||||
err.body &&
|
||||
err.body.error &&
|
||||
err.body.error.reason.startsWith(`user [${username}] is anonymous`);
|
||||
if (!isAnonymousUserPasswordChangeError) {
|
||||
throw err;
|
||||
} else {
|
||||
this._log.info(
|
||||
`cannot set password for anonymous user ${chalk.bold(username)}, skipping`
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -69,7 +69,14 @@ describe('KerberosAuthenticationProvider', () => {
|
|||
|
||||
it('does not handle requests that can be authenticated without `Negotiate` header.', async () => {
|
||||
const request = requestFixture();
|
||||
callWithRequest.withArgs(request, 'shield.authenticate').resolves({});
|
||||
callWithRequest
|
||||
.withArgs(
|
||||
sinon.match({
|
||||
headers: { authorization: `Negotiate ${Buffer.from('__fake__').toString('base64')}` },
|
||||
}),
|
||||
'shield.authenticate'
|
||||
)
|
||||
.resolves({});
|
||||
|
||||
const authenticationResult = await provider.authenticate(request, null);
|
||||
|
||||
|
@ -78,7 +85,14 @@ describe('KerberosAuthenticationProvider', () => {
|
|||
|
||||
it('does not handle requests if backend does not support Kerberos.', async () => {
|
||||
const request = requestFixture();
|
||||
callWithRequest.withArgs(request, 'shield.authenticate').rejects(Boom.unauthorized());
|
||||
callWithRequest
|
||||
.withArgs(
|
||||
sinon.match({
|
||||
headers: { authorization: `Negotiate ${Buffer.from('__fake__').toString('base64')}` },
|
||||
}),
|
||||
'shield.authenticate'
|
||||
)
|
||||
.rejects(Boom.unauthorized());
|
||||
let authenticationResult = await provider.authenticate(request, null);
|
||||
expect(authenticationResult.notHandled()).toBe(true);
|
||||
|
||||
|
@ -93,7 +107,7 @@ describe('KerberosAuthenticationProvider', () => {
|
|||
const request = requestFixture();
|
||||
const tokenPair = { accessToken: 'token', refreshToken: 'refresh-token' };
|
||||
|
||||
callWithRequest.withArgs(request, 'shield.authenticate').rejects(Boom.unauthorized());
|
||||
callWithRequest.withArgs(sinon.match.any, 'shield.authenticate').rejects(Boom.unauthorized());
|
||||
tokens.refresh.withArgs(tokenPair.refreshToken).resolves(null);
|
||||
|
||||
let authenticationResult = await provider.authenticate(request, tokenPair);
|
||||
|
@ -102,7 +116,7 @@ describe('KerberosAuthenticationProvider', () => {
|
|||
expect(authenticationResult.challenges).toBeUndefined();
|
||||
|
||||
callWithRequest
|
||||
.withArgs(request, 'shield.authenticate')
|
||||
.withArgs(sinon.match.any, 'shield.authenticate')
|
||||
.rejects(Boom.unauthorized(null, 'Basic'));
|
||||
|
||||
authenticationResult = await provider.authenticate(request, tokenPair);
|
||||
|
@ -114,7 +128,12 @@ describe('KerberosAuthenticationProvider', () => {
|
|||
it('fails with `Negotiate` challenge if backend supports Kerberos.', async () => {
|
||||
const request = requestFixture();
|
||||
callWithRequest
|
||||
.withArgs(request, 'shield.authenticate')
|
||||
.withArgs(
|
||||
sinon.match({
|
||||
headers: { authorization: `Negotiate ${Buffer.from('__fake__').toString('base64')}` },
|
||||
}),
|
||||
'shield.authenticate'
|
||||
)
|
||||
.rejects(Boom.unauthorized(null, 'Negotiate'));
|
||||
|
||||
const authenticationResult = await provider.authenticate(request, null);
|
||||
|
@ -126,7 +145,9 @@ describe('KerberosAuthenticationProvider', () => {
|
|||
|
||||
it('fails if request authentication is failed with non-401 error.', async () => {
|
||||
const request = requestFixture();
|
||||
callWithRequest.withArgs(request, 'shield.authenticate').rejects(Boom.serverUnavailable());
|
||||
callWithRequest
|
||||
.withArgs(sinon.match.any, 'shield.authenticate')
|
||||
.rejects(Boom.serverUnavailable());
|
||||
|
||||
const authenticationResult = await provider.authenticate(request, null);
|
||||
|
||||
|
@ -135,7 +156,7 @@ describe('KerberosAuthenticationProvider', () => {
|
|||
expect(authenticationResult.challenges).toBeUndefined();
|
||||
});
|
||||
|
||||
it('gets an token pair in exchange to SPNEGO one and stores it in the state.', async () => {
|
||||
it('gets a token pair in exchange to SPNEGO one and stores it in the state.', async () => {
|
||||
const user = { username: 'user' };
|
||||
const request = requestFixture({ headers: { authorization: 'negotiate spnego' } });
|
||||
|
||||
|
@ -307,7 +328,12 @@ describe('KerberosAuthenticationProvider', () => {
|
|||
statusCode: 500,
|
||||
body: { error: { reason: 'token document is missing and must be present' } },
|
||||
})
|
||||
.withArgs(sinon.match({ headers: {} }), 'shield.authenticate')
|
||||
.withArgs(
|
||||
sinon.match({
|
||||
headers: { authorization: `Negotiate ${Buffer.from('__fake__').toString('base64')}` },
|
||||
}),
|
||||
'shield.authenticate'
|
||||
)
|
||||
.rejects(Boom.unauthorized(null, 'Negotiate'));
|
||||
|
||||
tokens.refresh.withArgs(tokenPair.refreshToken).resolves(null);
|
||||
|
|
|
@ -276,7 +276,19 @@ export class KerberosAuthenticationProvider extends BaseAuthenticationProvider {
|
|||
// Try to authenticate current request with Elasticsearch to see whether it supports SPNEGO.
|
||||
let authenticationError: Error;
|
||||
try {
|
||||
await this.options.client.callWithRequest(request, 'shield.authenticate');
|
||||
await this.options.client.callWithRequest(
|
||||
{
|
||||
headers: {
|
||||
...request.headers,
|
||||
// We should send a fake SPNEGO token to Elasticsearch to make sure Kerberos realm is included
|
||||
// into authentication chain and adds a `WWW-Authenticate: Negotiate` header to the error
|
||||
// response. Otherwise it may not be even consulted if request can be authenticated by other
|
||||
// means (e.g. when anonymous access is enabled in Elasticsearch).
|
||||
authorization: `Negotiate ${Buffer.from('__fake__').toString('base64')}`,
|
||||
},
|
||||
},
|
||||
'shield.authenticate'
|
||||
);
|
||||
this.debug('Request was not supposed to be authenticated, ignoring result.');
|
||||
return AuthenticationResult.notHandled();
|
||||
} catch (err) {
|
||||
|
|
|
@ -14,6 +14,7 @@ require('@kbn/test').runTestsCli([
|
|||
require.resolve('../test/api_integration/config.js'),
|
||||
require.resolve('../test/plugin_api_integration/config.js'),
|
||||
require.resolve('../test/kerberos_api_integration/config'),
|
||||
require.resolve('../test/kerberos_api_integration/anonymous_access.config'),
|
||||
require.resolve('../test/saml_api_integration/config.js'),
|
||||
require.resolve('../test/token_api_integration/config.js'),
|
||||
// require.resolve('../test/oidc_api_integration/config.js'),
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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 { KibanaFunctionalTestDefaultProviders } from '../types/providers';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default async function({ readConfigFile }: KibanaFunctionalTestDefaultProviders) {
|
||||
const kerberosAPITestsConfig = await readConfigFile(require.resolve('./config.ts'));
|
||||
|
||||
return {
|
||||
...kerberosAPITestsConfig.getAll(),
|
||||
|
||||
junit: {
|
||||
reportName: 'X-Pack Kerberos API with Anonymous Access Integration Tests',
|
||||
},
|
||||
|
||||
esTestCluster: {
|
||||
...kerberosAPITestsConfig.get('esTestCluster'),
|
||||
serverArgs: [
|
||||
...kerberosAPITestsConfig.get('esTestCluster.serverArgs'),
|
||||
'xpack.security.authc.anonymous.username=anonymous_user',
|
||||
'xpack.security.authc.anonymous.roles=superuser',
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue