mirror of
https://github.com/elastic/kibana.git
synced 2025-06-28 11:05:39 -04:00
## Summary This PR makes `security` a required field for route registration. To incorporate the new required filed, changes has been made: 1. **Test file updates**. A lot of the updates made in this PR were made in tests. 2. **Versioned route security configuration**. For the versioned route `security` config has been lifted up to the top-level definition: Before ```ts router.versioned .get({ path: '/api/path', options: { ... }, ... }, handler) .addVersion({ version: 1, validate: false, security: { authz: { requiredPrivileges: ['privilege'], }, }, }); ``` After ```ts router.versioned .get({ path: '/api/path', options: { ... }, security: { authz: { requiredPrivileges: ['privilege'], }, }, ... }, handler) .addVersion({ version: 1, validate: false, }); ``` 3. **Type adjustments for route wrappers**. Type changes has been made in: - `x-pack/solutions/observability/plugins/infra/server/lib/adapters/framework/adapter_types.ts` - `x-pack/solutions/observability/plugins/metrics_data_access/server/lib/adapters/framework/adapter_types.ts` - `x-pack/solutions/observability/plugins/synthetics/server/routes/types.ts` - `x-pack/solutions/observability/plugins/uptime/server/legacy_uptime/routes/types.ts` Security was made an optional field for the wrappers defined in those files, since the default security is provided in the wrapper itself and then passed down to the core router. ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [x] The PR description includes the appropriate Release Notes section, and the correct `release_note:*` label is applied per the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) __Closes: https://github.com/elastic/kibana/issues/215331__ --------- Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
157 lines
5 KiB
TypeScript
157 lines
5 KiB
TypeScript
/*
|
|
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
|
* Public License v 1"; you may not use this file except in compliance with, at
|
|
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
|
* License v3.0 only", or the "Server Side Public License, v 1".
|
|
*/
|
|
|
|
import { schema } from '@kbn/config-schema';
|
|
import type { CorePreboot, PrebootPlugin, PluginInitializerContext } from '@kbn/core/server';
|
|
import fs from 'fs/promises';
|
|
import { errors } from '@elastic/elasticsearch';
|
|
import Boom from '@hapi/boom';
|
|
import type { ConfigType } from './config';
|
|
|
|
export function getDetailedErrorMessage(error: any): string {
|
|
if (error instanceof errors.ResponseError) {
|
|
return JSON.stringify(error.body);
|
|
}
|
|
|
|
if (Boom.isBoom(error)) {
|
|
return JSON.stringify(error.output.payload);
|
|
}
|
|
|
|
return error.message;
|
|
}
|
|
|
|
export class PrebootExamplePlugin implements PrebootPlugin {
|
|
readonly #initializerContext: PluginInitializerContext<ConfigType>;
|
|
constructor(initializerContext: PluginInitializerContext<ConfigType>) {
|
|
this.#initializerContext = initializerContext;
|
|
}
|
|
|
|
public setup(core: CorePreboot) {
|
|
const { skipSetup } = this.#initializerContext.config.get<ConfigType>();
|
|
let completeSetup: (result: { shouldReloadConfig: boolean }) => void;
|
|
|
|
core.http.registerRoutes('', (prebootRouter) => {
|
|
prebootRouter.get(
|
|
{
|
|
path: '/api/preboot/state',
|
|
security: {
|
|
authz: {
|
|
enabled: false,
|
|
reason: 'This route is opted out of authorization as it is a core preboot route',
|
|
},
|
|
},
|
|
validate: false,
|
|
options: { authRequired: false },
|
|
},
|
|
(_, request, response) => {
|
|
const isSetupModeActive = !skipSetup && core.preboot.isSetupOnHold();
|
|
return response.ok({ body: { isSetupModeActive } });
|
|
}
|
|
);
|
|
if (skipSetup) {
|
|
return;
|
|
}
|
|
|
|
prebootRouter.post(
|
|
{
|
|
path: '/api/preboot/complete_setup',
|
|
security: {
|
|
authz: {
|
|
enabled: false,
|
|
reason: 'This route is opted out of authorization as it is a core preboot route',
|
|
},
|
|
},
|
|
validate: {
|
|
body: schema.object({ shouldReloadConfig: schema.boolean() }),
|
|
},
|
|
options: { authRequired: false },
|
|
},
|
|
(_, request, response) => {
|
|
completeSetup({ shouldReloadConfig: request.body.shouldReloadConfig });
|
|
return response.noContent();
|
|
}
|
|
);
|
|
|
|
prebootRouter.post(
|
|
{
|
|
path: '/api/preboot/write_config',
|
|
security: {
|
|
authz: {
|
|
enabled: false,
|
|
reason: 'This route is opted out of authorization as it is a core preboot route',
|
|
},
|
|
},
|
|
validate: {
|
|
body: schema.object({ key: schema.string(), value: schema.string() }),
|
|
},
|
|
options: { authRequired: false },
|
|
},
|
|
async (_, request, response) => {
|
|
const configPath = this.#initializerContext.env.configs.find((path) =>
|
|
path.includes('dev')
|
|
);
|
|
|
|
if (!configPath) {
|
|
return response.customError({ statusCode: 500, body: 'Cannot find dev config.' });
|
|
}
|
|
|
|
await fs.appendFile(configPath, `${request.body.key}: ${request.body.value}\n`);
|
|
return response.noContent();
|
|
}
|
|
);
|
|
|
|
prebootRouter.post(
|
|
{
|
|
path: '/api/preboot/connect_to_es',
|
|
security: {
|
|
authz: { enabled: false, reason: 'This route delegates authorization to es client' },
|
|
},
|
|
validate: {
|
|
body: schema.object({
|
|
host: schema.string(),
|
|
username: schema.string(),
|
|
password: schema.string(),
|
|
}),
|
|
},
|
|
options: { authRequired: false },
|
|
},
|
|
async (_, request, response) => {
|
|
const esClient = core.elasticsearch.createClient('data', {
|
|
hosts: [request.body.host],
|
|
});
|
|
|
|
const scopedClient = esClient.asScoped({
|
|
headers: {
|
|
authorization: `Basic ${Buffer.from(
|
|
`${request.body.username}:${request.body.password}`
|
|
).toString('base64')}`,
|
|
},
|
|
});
|
|
|
|
try {
|
|
return response.ok({
|
|
body: await scopedClient.asCurrentUser.security.authenticate(),
|
|
});
|
|
} catch (err) {
|
|
return response.customError({ statusCode: 500, body: getDetailedErrorMessage(err) });
|
|
}
|
|
}
|
|
);
|
|
|
|
core.preboot.holdSetupUntilResolved(
|
|
'Elasticsearch connection is not set up',
|
|
new Promise<{ shouldReloadConfig: boolean }>((resolve) => {
|
|
completeSetup = resolve;
|
|
})
|
|
);
|
|
});
|
|
}
|
|
|
|
public stop() {}
|
|
}
|