[8.7] Only log deprecation warnings for calls to Saved Objects routes from non-kibana request (#152971) (#153053)

# Backport

This will backport the following commits from `main` to `8.7`:
- [Only log deprecation warnings for calls to Saved Objects routes from
non-kibana request
(#152971)](https://github.com/elastic/kibana/pull/152971)

<!--- Backport version: 8.9.7 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Christiane (Tina)
Heiligers","email":"christiane.heiligers@elastic.co"},"sourceCommit":{"committedDate":"2023-03-09T17:23:21Z","message":"Only
log deprecation warnings for calls to Saved Objects routes from
non-kibana request (#152971)\n\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"e37e83428abe9bb8971419672969bcaa9db9918e","branchLabelMapping":{"^v8.8.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["Team:Core","Feature:Saved
Objects","release_note:skip","backport:prev-minor","v8.8.0"],"number":152971,"url":"https://github.com/elastic/kibana/pull/152971","mergeCommit":{"message":"Only
log deprecation warnings for calls to Saved Objects routes from
non-kibana request (#152971)\n\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"e37e83428abe9bb8971419672969bcaa9db9918e"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v8.8.0","labelRegex":"^v8.8.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/152971","number":152971,"mergeCommit":{"message":"Only
log deprecation warnings for calls to Saved Objects routes from
non-kibana request (#152971)\n\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"e37e83428abe9bb8971419672969bcaa9db9918e"}}]}]
BACKPORT-->
This commit is contained in:
Christiane (Tina) Heiligers 2023-03-09 13:07:11 -07:00 committed by GitHub
parent ac4f4e5e6a
commit 6e8c4f5c3d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 201 additions and 34 deletions

View file

@ -10,7 +10,11 @@ import { schema } from '@kbn/config-schema';
import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-server-internal';
import type { Logger } from '@kbn/logging';
import type { InternalSavedObjectRouter } from '../internal_types';
import { catchAndReturnBoomErrors, throwIfAnyTypeNotVisibleByAPI } from './utils';
import {
catchAndReturnBoomErrors,
logWarnOnExternalRequest,
throwIfAnyTypeNotVisibleByAPI,
} from './utils';
interface RouteDependencies {
coreUsageData: InternalCoreUsageDataSetup;
@ -51,9 +55,12 @@ export const registerBulkCreateRoute = (
},
},
catchAndReturnBoomErrors(async (context, req, res) => {
logger.warn(
"The bulk create saved object API '/api/saved_objects/_bulk_create' is deprecated."
);
logWarnOnExternalRequest({
method: 'post',
path: '/api/saved_objects/_bulk_create',
req,
logger,
});
const { overwrite } = req.query;
const usageStatsClient = coreUsageData.getClient();

View file

@ -10,7 +10,11 @@ import { schema } from '@kbn/config-schema';
import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-server-internal';
import type { Logger } from '@kbn/logging';
import type { InternalSavedObjectRouter } from '../internal_types';
import { catchAndReturnBoomErrors, throwIfAnyTypeNotVisibleByAPI } from './utils';
import {
catchAndReturnBoomErrors,
logWarnOnExternalRequest,
throwIfAnyTypeNotVisibleByAPI,
} from './utils';
interface RouteDependencies {
coreUsageData: InternalCoreUsageDataSetup;
@ -37,9 +41,12 @@ export const registerBulkDeleteRoute = (
},
},
catchAndReturnBoomErrors(async (context, req, res) => {
logger.warn(
"The bulk update saved object API '/api/saved_objects/_bulk_update' is deprecated."
);
logWarnOnExternalRequest({
method: 'post',
path: '/api/saved_objects/_bulk_delete',
req,
logger,
});
const { force } = req.query;
const usageStatsClient = coreUsageData.getClient();
usageStatsClient.incrementSavedObjectsBulkDelete({ request: req }).catch(() => {});

View file

@ -10,7 +10,11 @@ import { schema } from '@kbn/config-schema';
import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-server-internal';
import type { Logger } from '@kbn/logging';
import type { InternalSavedObjectRouter } from '../internal_types';
import { catchAndReturnBoomErrors, throwIfAnyTypeNotVisibleByAPI } from './utils';
import {
catchAndReturnBoomErrors,
logWarnOnExternalRequest,
throwIfAnyTypeNotVisibleByAPI,
} from './utils';
interface RouteDependencies {
coreUsageData: InternalCoreUsageDataSetup;
@ -36,7 +40,12 @@ export const registerBulkGetRoute = (
},
},
catchAndReturnBoomErrors(async (context, req, res) => {
logger.warn("The bulk get saved object API '/api/saved_objects/_bulk_get' is deprecated.");
logWarnOnExternalRequest({
method: 'post',
path: '/api/saved_objects/_bulk_get',
req,
logger,
});
const usageStatsClient = coreUsageData.getClient();
usageStatsClient.incrementSavedObjectsBulkGet({ request: req }).catch(() => {});

View file

@ -10,7 +10,11 @@ import { schema } from '@kbn/config-schema';
import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-server-internal';
import type { Logger } from '@kbn/logging';
import type { InternalSavedObjectRouter } from '../internal_types';
import { catchAndReturnBoomErrors, throwIfAnyTypeNotVisibleByAPI } from './utils';
import {
catchAndReturnBoomErrors,
logWarnOnExternalRequest,
throwIfAnyTypeNotVisibleByAPI,
} from './utils';
interface RouteDependencies {
coreUsageData: InternalCoreUsageDataSetup;
@ -34,9 +38,12 @@ export const registerBulkResolveRoute = (
},
},
catchAndReturnBoomErrors(async (context, req, res) => {
logger.warn(
"The bulk resolve saved object API '/api/saved_objects/_bulk_resolve' is deprecated."
);
logWarnOnExternalRequest({
method: 'post',
path: '/api/saved_objects/_bulk_resolve',
req,
logger,
});
const usageStatsClient = coreUsageData.getClient();
usageStatsClient.incrementSavedObjectsBulkResolve({ request: req }).catch(() => {});

View file

@ -10,7 +10,11 @@ import { schema } from '@kbn/config-schema';
import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-server-internal';
import type { Logger } from '@kbn/logging';
import type { InternalSavedObjectRouter } from '../internal_types';
import { catchAndReturnBoomErrors, throwIfAnyTypeNotVisibleByAPI } from './utils';
import {
catchAndReturnBoomErrors,
logWarnOnExternalRequest,
throwIfAnyTypeNotVisibleByAPI,
} from './utils';
interface RouteDependencies {
coreUsageData: InternalCoreUsageDataSetup;
@ -46,9 +50,12 @@ export const registerBulkUpdateRoute = (
},
},
catchAndReturnBoomErrors(async (context, req, res) => {
logger.warn(
"The bulk update saved object API '/api/saved_objects/_bulk_update' is deprecated."
);
logWarnOnExternalRequest({
method: 'put',
path: '/api/saved_objects/_bulk_update',
req,
logger,
});
const usageStatsClient = coreUsageData.getClient();
usageStatsClient.incrementSavedObjectsBulkUpdate({ request: req }).catch(() => {});

View file

@ -10,7 +10,11 @@ import { schema } from '@kbn/config-schema';
import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-server-internal';
import type { Logger } from '@kbn/logging';
import type { InternalSavedObjectRouter } from '../internal_types';
import { catchAndReturnBoomErrors, throwIfTypeNotVisibleByAPI } from './utils';
import {
catchAndReturnBoomErrors,
logWarnOnExternalRequest,
throwIfTypeNotVisibleByAPI,
} from './utils';
interface RouteDependencies {
coreUsageData: InternalCoreUsageDataSetup;
@ -50,7 +54,12 @@ export const registerCreateRoute = (
},
},
catchAndReturnBoomErrors(async (context, req, res) => {
logger.warn("The create saved object API '/api/saved_objects/{type}/{id}' is deprecated.");
logWarnOnExternalRequest({
method: 'post',
path: '/api/saved_objects/{type}/{id?}',
req,
logger,
});
const { type, id } = req.params;
const { overwrite } = req.query;
const { attributes, migrationVersion, coreMigrationVersion, references, initialNamespaces } =

View file

@ -10,7 +10,11 @@ import { schema } from '@kbn/config-schema';
import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-server-internal';
import type { Logger } from '@kbn/logging';
import type { InternalSavedObjectRouter } from '../internal_types';
import { catchAndReturnBoomErrors, throwIfTypeNotVisibleByAPI } from './utils';
import {
catchAndReturnBoomErrors,
logWarnOnExternalRequest,
throwIfTypeNotVisibleByAPI,
} from './utils';
interface RouteDependencies {
coreUsageData: InternalCoreUsageDataSetup;
@ -35,7 +39,12 @@ export const registerDeleteRoute = (
},
},
catchAndReturnBoomErrors(async (context, req, res) => {
logger.warn("The delete saved object API '/api/saved_objects/{type}/{id}' is deprecated.");
logWarnOnExternalRequest({
method: 'delete',
path: '/api/saved_objects/{type}/{id}',
req,
logger,
});
const { type, id } = req.params;
const { force } = req.query;
const { getClient, typeRegistry } = (await context.core).savedObjects;

View file

@ -11,7 +11,7 @@ import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-serve
import type { Logger } from '@kbn/logging';
import type { InternalSavedObjectRouter } from '../internal_types';
import { catchAndReturnBoomErrors, throwOnHttpHiddenTypes } from './utils';
import { logWarnOnExternalRequest } from './utils';
interface RouteDependencies {
coreUsageData: InternalCoreUsageDataSetup;
logger: Logger;
@ -61,7 +61,12 @@ export const registerFindRoute = (
},
},
catchAndReturnBoomErrors(async (context, req, res) => {
logger.warn("The find saved object API '/api/saved_objects/_find' is deprecated.");
logWarnOnExternalRequest({
method: 'get',
path: '/api/saved_objects/_find',
req,
logger,
});
const query = req.query;
const namespaces =

View file

@ -10,7 +10,11 @@ import { schema } from '@kbn/config-schema';
import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-server-internal';
import type { Logger } from '@kbn/logging';
import type { InternalSavedObjectRouter } from '../internal_types';
import { catchAndReturnBoomErrors, throwIfTypeNotVisibleByAPI } from './utils';
import {
catchAndReturnBoomErrors,
logWarnOnExternalRequest,
throwIfTypeNotVisibleByAPI,
} from './utils';
interface RouteDependencies {
coreUsageData: InternalCoreUsageDataSetup;
@ -32,7 +36,12 @@ export const registerGetRoute = (
},
},
catchAndReturnBoomErrors(async (context, req, res) => {
logger.warn("The get saved object API '/api/saved_objects/{type}/{id}' is deprecated.");
logWarnOnExternalRequest({
method: 'get',
path: '/api/saved_objects/{type}/{id}',
req,
logger,
});
const { type, id } = req.params;
const usageStatsClient = coreUsageData.getClient();

View file

@ -10,7 +10,7 @@ import { schema } from '@kbn/config-schema';
import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-server-internal';
import type { Logger } from '@kbn/logging';
import type { InternalSavedObjectRouter } from '../internal_types';
import { throwIfTypeNotVisibleByAPI } from './utils';
import { throwIfTypeNotVisibleByAPI, logWarnOnExternalRequest } from './utils';
interface RouteDependencies {
coreUsageData: InternalCoreUsageDataSetup;
@ -32,9 +32,12 @@ export const registerResolveRoute = (
},
},
router.handleLegacyErrors(async (context, req, res) => {
logger.warn(
"The resolve saved object API '/api/saved_objects/resolve/{type}/{id}' is deprecated."
);
logWarnOnExternalRequest({
method: 'get',
path: '/api/saved_objects/resolve/{type}/{id}',
req,
logger,
});
const { type, id } = req.params;
const { savedObjects } = await context.core;

View file

@ -11,7 +11,11 @@ import type { SavedObjectsUpdateOptions } from '@kbn/core-saved-objects-api-serv
import type { Logger } from '@kbn/logging';
import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-server-internal';
import type { InternalSavedObjectRouter } from '../internal_types';
import { catchAndReturnBoomErrors, throwIfTypeNotVisibleByAPI } from './utils';
import {
catchAndReturnBoomErrors,
logWarnOnExternalRequest,
throwIfTypeNotVisibleByAPI,
} from './utils';
interface RouteDependencies {
coreUsageData: InternalCoreUsageDataSetup;
@ -47,7 +51,12 @@ export const registerUpdateRoute = (
},
},
catchAndReturnBoomErrors(async (context, req, res) => {
logger.warn("The update saved object API '/api/saved_objects/{type}/{id}' is deprecated.");
logWarnOnExternalRequest({
method: 'get',
path: '/api/saved_objects/{type}/{id}',
req,
logger,
});
const { type, id } = req.params;
const { attributes, version, references, upsert } = req.body;
const options: SavedObjectsUpdateOptions = { version, references, upsert };

View file

@ -14,6 +14,7 @@ import {
throwOnGloballyHiddenTypes,
throwIfTypeNotVisibleByAPI,
throwIfAnyTypeNotVisibleByAPI,
logWarnOnExternalRequest,
} from './utils';
import { Readable } from 'stream';
import { createPromiseFromStreams, createConcatStream } from '@kbn/utils';
@ -27,6 +28,8 @@ import type {
} from '@kbn/core-http-server';
import { kibanaResponseFactory } from '@kbn/core-http-router-server-internal';
import { typeRegistryInstanceMock } from '../saved_objects_service.test.mocks';
import { httpServerMock } from '@kbn/core-http-server-mocks';
import { loggerMock, type MockedLogger } from '@kbn/logging-mocks';
async function readStreamToCompletion(stream: Readable) {
return createPromiseFromStreams([stream, createConcatStream([])]);
@ -341,3 +344,60 @@ describe('throwIfAnyTypeNotVisibleByAPI', () => {
expect(() => throwIfAnyTypeNotVisibleByAPI(['config'], registry)).not.toThrowError();
});
});
describe('logWarnOnExternalRequest', () => {
let logger: MockedLogger;
const firstPartyRequestHeaders = { 'kbn-version': 'a', referer: 'b' };
const kibRequest = httpServerMock.createKibanaRequest({ headers: firstPartyRequestHeaders });
const extRequest = httpServerMock.createKibanaRequest();
beforeEach(() => {
logger = loggerMock.create();
});
afterEach(() => {
jest.clearAllMocks();
});
it('logs on external requests to non-bulk apis', () => {
logWarnOnExternalRequest({
method: 'get',
path: '/resolve/{type}/{id}',
req: extRequest,
logger,
});
expect(logger.warn).toHaveBeenCalledTimes(1);
expect(logger.warn).toHaveBeenCalledWith(
'The get saved object API /resolve/{type}/{id} is deprecated.'
);
});
it('logs on external requests to bulk apis', () => {
logWarnOnExternalRequest({
method: 'post',
path: '/_bulk_resolve',
req: extRequest,
logger,
});
expect(logger.warn).toHaveBeenCalledTimes(1);
expect(logger.warn).toHaveBeenCalledWith(
'The post saved object API /_bulk_resolve is deprecated.'
);
});
it('does not log a warning on internal requests', () => {
logWarnOnExternalRequest({
method: 'get',
path: '/resolve/{type}/{id}',
req: kibRequest,
logger,
});
expect(logger.warn).toHaveBeenCalledTimes(0);
logWarnOnExternalRequest({
method: 'post',
path: '/_bulk_resolve',
req: kibRequest,
logger,
});
expect(logger.warn).toHaveBeenCalledTimes(0);
});
});

View file

@ -16,13 +16,14 @@ import {
createConcatStream,
} from '@kbn/utils';
import Boom from '@hapi/boom';
import type { RequestHandlerWrapper } from '@kbn/core-http-server';
import type {
import type { KibanaRequest, RequestHandlerWrapper } from '@kbn/core-http-server';
import {
SavedObject,
ISavedObjectTypeRegistry,
SavedObjectsExportResultDetails,
} from '@kbn/core-saved-objects-server';
import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server';
import { Logger } from '@kbn/logging';
export async function createSavedObjectsStreamFromNdJson(ndJsonStream: Readable) {
const savedObjects = await createPromiseFromStreams([
@ -152,3 +153,27 @@ export interface BulkGetItem {
fields?: string[];
namespaces?: string[];
}
export function isKibanaRequest({ headers }: KibanaRequest) {
// The presence of these two request headers gives us a good indication that this is a first-party request from the Kibana client.
// We can't be 100% certain, but this is a reasonable attempt.
return headers && headers['kbn-version'] && headers.referer;
}
export interface LogWarnOnExternalRequest {
method: string;
path: string;
req: KibanaRequest;
logger: Logger;
}
/**
* Only log a warning when the request is internal
* Allows us to silence the logs for development
* @internal
*/
export function logWarnOnExternalRequest(params: LogWarnOnExternalRequest) {
const { method, path, req, logger } = params;
if (!isKibanaRequest(req)) {
logger.warn(`The ${method} saved object API ${path} is deprecated.`);
}
}

View file

@ -45,6 +45,7 @@
"@kbn/core-elasticsearch-server-mocks",
"@kbn/utils",
"@kbn/core-http-router-server-internal",
"@kbn/logging-mocks",
"@kbn/core-saved-objects-utils-server",
],
"exclude": [