[Cloud/Security] Fix the server-side import of the contract CloudStart (#149203)

Co-authored-by: Aleh Zasypkin <aleh.zasypkin@elastic.co>
Resolves https://github.com/elastic/kibana/issues/149204
This commit is contained in:
Alejandro Fernández Haro 2023-01-19 18:49:34 +01:00 committed by GitHub
parent ebb61a5089
commit f0b5db6f70
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 187 additions and 18 deletions

View file

@ -262,6 +262,7 @@ enabled:
- x-pack/test/security_api_integration/oidc.config.ts
- x-pack/test/security_api_integration/pki.config.ts
- x-pack/test/security_api_integration/saml.config.ts
- x-pack/test/security_api_integration/saml_cloud.config.ts
- x-pack/test/security_api_integration/session_idle.config.ts
- x-pack/test/security_api_integration/session_invalidate.config.ts
- x-pack/test/security_api_integration/session_lifespan.config.ts

View file

@ -8,7 +8,7 @@
import { PluginInitializerContext } from '@kbn/core/server';
import { CloudPlugin } from './plugin';
export type { CloudSetup } from './plugin';
export type { CloudSetup, CloudStart } from './plugin';
export { config } from './config';
export const plugin = (initializerContext: PluginInitializerContext) => {
return new CloudPlugin(initializerContext);

View file

@ -16,22 +16,23 @@ const baseConfig = {
};
describe('Cloud Plugin', () => {
const setupPlugin = () => {
const initContext = coreMock.createPluginInitializerContext({
...baseConfig,
id: 'cloudId',
cname: 'cloud.elastic.co',
});
const plugin = new CloudPlugin(initContext);
const coreSetup = coreMock.createSetup();
const setup = plugin.setup(coreSetup, {});
const start = plugin.start();
return { setup, start };
};
describe('#setup', () => {
describe('interface', () => {
const setupPlugin = () => {
const initContext = coreMock.createPluginInitializerContext({
...baseConfig,
id: 'cloudId',
cname: 'cloud.elastic.co',
});
const plugin = new CloudPlugin(initContext);
const coreSetup = coreMock.createSetup();
const setup = plugin.setup(coreSetup, {});
return { setup };
};
it('exposes isCloudEnabled', () => {
const { setup } = setupPlugin();
expect(setup.isCloudEnabled).toBe(true);
@ -58,4 +59,13 @@ describe('Cloud Plugin', () => {
});
});
});
describe('#start', () => {
describe('interface', () => {
it('exposes isCloudEnabled', () => {
const { start } = setupPlugin();
expect(start.isCloudEnabled).toBe(true);
});
});
});
});

View file

@ -55,7 +55,17 @@ export interface CloudSetup {
};
}
export class CloudPlugin implements Plugin<CloudSetup> {
/**
* Start contract
*/
export interface CloudStart {
/**
* `true` when running on Elastic Cloud.
*/
isCloudEnabled: boolean;
}
export class CloudPlugin implements Plugin<CloudSetup, CloudStart> {
private readonly config: CloudConfigType;
constructor(private readonly context: PluginInitializerContext) {
@ -85,5 +95,9 @@ export class CloudPlugin implements Plugin<CloudSetup> {
};
}
public start() {}
public start() {
return {
isCloudEnabled: getIsCloudEnabled(this.config.id),
};
}
}

View file

@ -8,7 +8,7 @@
import type { Subscription } from 'rxjs';
import { map } from 'rxjs/operators';
import type { CloudStart } from '@kbn/cloud-plugin/public';
import type { CloudStart } from '@kbn/cloud-plugin/server';
import type { TypeOf } from '@kbn/config-schema';
import type {
CoreSetup,

View file

@ -0,0 +1,55 @@
/*
* 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 { resolve } from 'path';
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'));
const kibanaPort = xPackAPITestsConfig.get('servers.kibana.port');
const idpPath = resolve(__dirname, './fixtures/saml/idp_metadata.xml');
return {
testFiles: [require.resolve('./tests/saml_cloud')],
servers: xPackAPITestsConfig.get('servers'),
security: { disableTestUser: true },
services,
junit: {
reportName: 'X-Pack Security API Integration Tests (Cloud SAML)',
},
esTestCluster: {
...xPackAPITestsConfig.get('esTestCluster'),
serverArgs: [
...xPackAPITestsConfig.get('esTestCluster.serverArgs'),
'xpack.security.authc.token.enabled=true',
'xpack.security.authc.token.timeout=15s',
'xpack.security.authc.realms.saml.cloud-saml-kibana.order=0',
`xpack.security.authc.realms.saml.cloud-saml-kibana.idp.metadata.path=${idpPath}`,
'xpack.security.authc.realms.saml.cloud-saml-kibana.idp.entity_id=http://www.elastic.co/saml1',
`xpack.security.authc.realms.saml.cloud-saml-kibana.sp.entity_id=http://localhost:${kibanaPort}`,
`xpack.security.authc.realms.saml.cloud-saml-kibana.sp.logout=http://localhost:${kibanaPort}/logout`,
`xpack.security.authc.realms.saml.cloud-saml-kibana.sp.acs=http://localhost:${kibanaPort}/api/security/saml/callback`,
'xpack.security.authc.realms.saml.cloud-saml-kibana.attributes.principal=urn:oid:0.0.7',
],
},
kbnTestServer: {
...xPackAPITestsConfig.get('kbnTestServer'),
serverArgs: [
...xPackAPITestsConfig.get('kbnTestServer.serverArgs'),
'--xpack.cloud.id=ftr_fake_cloud_id',
`--xpack.security.authc.providers=${JSON.stringify({
basic: { 'cloud-basic': { order: 0 } },
saml: { 'cloud-saml-kibana': { order: 1, realm: 'cloud-saml-kibana' } },
})}`,
],
},
};
}

View file

@ -0,0 +1,14 @@
/*
* 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 - Cloud SAML', function () {
loadTestFile(require.resolve('./saml_login'));
});
}

View file

@ -0,0 +1,75 @@
/*
* 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 { parse as parseCookie, Cookie } from 'tough-cookie';
import { getSAMLResponse } from '../../fixtures/saml/saml_tools';
import { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ getService }: FtrProviderContext) {
const randomness = getService('randomness');
const supertest = getService('supertestWithoutAuth');
const config = getService('config');
const kibanaServerConfig = config.get('servers.kibana');
function createSAMLResponse(options = {}) {
return getSAMLResponse({
destination: `http://localhost:${kibanaServerConfig.port}/api/security/saml/callback`,
sessionIndex: String(randomness.naturalNumber()),
...options,
});
}
async function checkSessionCookie(sessionCookie: Cookie, username = 'a@b.c') {
const apiResponse = await supertest
.get('/internal/security/me')
.set('kbn-xsrf', 'xxx')
.set('Cookie', sessionCookie.cookieString())
.expect(200);
expect(apiResponse.body.username).to.be(username);
expect(apiResponse.body.elastic_cloud_user).to.be(true);
expect(apiResponse.body.authentication_realm).to.eql({
type: 'saml',
name: 'cloud-saml-kibana',
});
expect(apiResponse.body.authentication_provider).to.eql({
type: 'saml',
name: 'cloud-saml-kibana',
});
expect(apiResponse.body.authentication_type).to.be('token');
}
describe('Cloud SAML authentication', () => {
let sessionCookie: Cookie;
beforeEach(async () => {
// Cloud SAML relies on IdP initiated login.
const samlAuthenticationResponse = await supertest
.post('/api/security/saml/callback')
.send({ SAMLResponse: await createSAMLResponse({ username: 'a@b.c' }) })
.expect(302);
expect(samlAuthenticationResponse.headers.location).to.be('/');
sessionCookie = parseCookie(samlAuthenticationResponse.headers['set-cookie'][0])!;
});
it('should properly set `elastic_cloud_user` user property', async () => {
// Same user, same provider - session ID hasn't changed and cookie should still be valid.
await supertest
.get('/internal/security/me')
.set('kbn-xsrf', 'xxx')
.set('Cookie', sessionCookie.cookieString())
.expect(200);
// Check that all properties of the cloud saml user are properly set.
await checkSessionCookie(sessionCookie);
});
});
}