Unauthorized route migration for routes owned by kibana-data-discovery (#198332)

### Authz API migration for unauthorized routes

This PR migrates unauthorized routes owned by your team to a new
security configuration.
Please refer to the documentation for more information: [Authorization
API](https://docs.elastic.dev/kibana-dev-docs/key-concepts/security-api-authorization)

### **Before migration:**
```ts
router.get({
  path: '/api/path',
  ...
}, handler);
```

### **After migration:**
```ts
router.get({
  path: '/api/path',
  security: {
    authz: {
      enabled: false,
      reason: 'This route is opted out from authorization because ...',
    },
  },
  ...
}, handler);
```

### What to do next?
1. Review the changes in this PR.
2. Elaborate on the reasoning to opt-out of authorization.
3. Routes without a compelling reason to opt-out of authorization should
plan to introduce them as soon as possible.
2. You might need to update your tests to reflect the new security
configuration:
  - If you have snapshot tests that include the route definition.

## Any questions?
If you have any questions or need help with API authorization, please
reach out to the `@elastic/kibana-security` team.

---------

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
Co-authored-by: Lukas Olson <lukas@elastic.co>
Co-authored-by: Davis McPhee <davis.mcphee@elastic.co>
Co-authored-by: Matthias Wilhelm <matthias.wilhelm@elastic.co>
This commit is contained in:
Kibana Machine 2024-12-03 02:46:33 +11:00 committed by GitHub
parent 5ed54ec93c
commit fc8ca4f3b8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 191 additions and 15 deletions

View file

@ -51,6 +51,12 @@ export const registerFieldPreviewRoute = ({ router }: RouteDependencies): void =
router.versioned.post({ path, access: 'internal' }).addVersion(
{
version: '1',
security: {
authz: {
enabled: false,
reason: 'Authorization provided by Elasticsearch',
},
},
validate: {
request: {
body: bodySchema,

View file

@ -14,6 +14,12 @@ export function registerPreviewScriptedFieldRoute(router: IRouter): void {
router.post(
{
path: '/internal/index-pattern-management/preview_scripted_field',
security: {
authz: {
enabled: false,
reason: 'Authorization provided by Elasticsearch',
},
},
validate: {
body: schema.object({
index: schema.string(),

View file

@ -15,6 +15,12 @@ export function registerResolveIndexRoute(router: IRouter): void {
router.get(
{
path: '/internal/index-pattern-management/resolve_index/{query}',
security: {
authz: {
enabled: false,
reason: 'Authorization provided by Elasticsearch',
},
},
validate: {
params: schema.object({
query: schema.string(),

View file

@ -60,6 +60,12 @@ export const registerExistingIndicesPath = (router: IRouter): void => {
.addVersion(
{
version,
security: {
authz: {
enabled: false,
reason: 'This route is opted out from authorization',
},
},
validate: {
request: {
query: schema.object({

View file

@ -146,10 +146,17 @@ export const registerFields = (
>,
isRollupsEnabled: () => boolean
) => {
router.versioned
.get({ path, access: 'internal', enableQueryVersion: true })
.addVersion(
{ version: '1', validate: { request: { query: querySchema }, response: validate.response } },
handler(isRollupsEnabled)
);
router.versioned.get({ path, access: 'internal', enableQueryVersion: true }).addVersion(
{
version: '1',
security: {
authz: {
enabled: false,
reason: 'Authorization provided by Elasticsearch',
},
},
validate: { request: { query: querySchema }, response: validate.response },
},
handler(isRollupsEnabled)
);
};

View file

@ -9,7 +9,7 @@
import { estypes } from '@elastic/elasticsearch';
import { schema } from '@kbn/config-schema';
import { IRouter, RequestHandler, StartServicesAccessor } from '@kbn/core/server';
import type { IRouter, RequestHandler, RouteAuthz, StartServicesAccessor } from '@kbn/core/server';
import { VersionedRouteValidation } from '@kbn/core-http-server';
import { INITIAL_REST_VERSION_INTERNAL as version } from '../../constants';
import { IndexPatternsFetcher } from '../../fetcher';
@ -217,14 +217,31 @@ export const registerFieldForWildcard = (
isRollupsEnabled: () => boolean
) => {
const configuredHandler = handler(isRollupsEnabled);
const authz: RouteAuthz = { enabled: false, reason: 'Authorization provided by Elasticsearch' };
// handler
router.versioned.put({ path, access }).addVersion({ version, validate }, configuredHandler);
router.versioned.post({ path, access }).addVersion({ version, validate }, configuredHandler);
router.versioned
.get({ path, access })
.addVersion(
{ version, validate: { request: { query: querySchema }, response: validate.response } },
configuredHandler
);
router.versioned.put({ path, access }).addVersion(
{
version,
security: { authz },
validate,
},
configuredHandler
);
router.versioned.post({ path, access }).addVersion(
{
version,
security: { authz },
validate,
},
configuredHandler
);
router.versioned.get({ path, access }).addVersion(
{
version,
security: { authz },
validate: { request: { query: querySchema }, response: validate.response },
},
configuredHandler
);
};

View file

@ -45,6 +45,12 @@ export const registerHasDataViewsRoute = (router: IRouter): void => {
.addVersion(
{
version: '1',
security: {
authz: {
enabled: false,
reason: 'Authorization provided by saved objects client',
},
},
validate: {
response: {
200: {

View file

@ -133,6 +133,12 @@ export const registerHasEsDataRoute = (
.addVersion(
{
version: '1',
security: {
authz: {
enabled: false,
reason: 'Authorization provided by Elasticsearch',
},
},
validate: {
response: {
200: {

View file

@ -61,6 +61,11 @@ const registerCreateDataViewRouteFactory =
router.versioned.post({ path, access: 'public', description }).addVersion(
{
version: INITIAL_REST_VERSION,
security: {
authz: {
requiredPrivileges: ['indexPatterns:manage'],
},
},
validate: {
request: {
body: schema.object({

View file

@ -73,6 +73,12 @@ const manageDefaultIndexPatternRoutesFactory =
router.versioned.get({ path, access: 'public', description: getDescription }).addVersion(
{
version: INITIAL_REST_VERSION,
security: {
authz: {
enabled: false,
reason: 'Authorization provided by saved objects client',
},
},
validate: {
request: {},
response: {
@ -110,6 +116,11 @@ const manageDefaultIndexPatternRoutesFactory =
router.versioned.post({ path, access: 'public', description: postDescription }).addVersion(
{
version: INITIAL_REST_VERSION,
security: {
authz: {
requiredPrivileges: ['indexPatterns:manage'],
},
},
validate: {
request: {
body: schema.object({

View file

@ -53,6 +53,11 @@ const deleteIndexPatternRouteFactory =
router.versioned.delete({ path, access: 'public', description }).addVersion(
{
version: INITIAL_REST_VERSION,
security: {
authz: {
requiredPrivileges: ['indexPatterns:manage'],
},
},
validate: {
request: {
params: schema.object(

View file

@ -131,6 +131,11 @@ const updateFieldsActionRouteFactory = (path: string, serviceKey: string, descri
router.versioned.post({ path, access: 'public', description }).addVersion(
{
version: INITIAL_REST_VERSION,
security: {
authz: {
requiredPrivileges: ['indexPatterns:manage'],
},
},
validate: {
request: {
params: schema.object(

View file

@ -57,6 +57,12 @@ const getDataViewRouteFactory =
router.versioned.get({ path, access: 'public', description }).addVersion(
{
version: INITIAL_REST_VERSION,
security: {
authz: {
enabled: false,
reason: 'Authorization provided by saved objects client',
},
},
validate: {
request: {
params: schema.object(

View file

@ -66,6 +66,12 @@ const getDataViewsRouteFactory =
router.versioned.get({ path, access: 'public', description }).addVersion(
{
version: INITIAL_REST_VERSION,
security: {
authz: {
enabled: false,
reason: 'Authorization provided by saved objects client',
},
},
validate: {
request: {},
response: { 200: { body: responseValidation } },

View file

@ -46,6 +46,12 @@ const hasUserDataViewRouteFactory =
router.versioned.get({ path, access: 'internal' }).addVersion(
{
version: '1',
security: {
authz: {
enabled: false,
reason: 'Authorization provided by saved objects client',
},
},
validate: {
request: {},
response: {

View file

@ -82,6 +82,11 @@ const runtimeCreateFieldRouteFactory =
router.versioned.post({ path, access: 'public', description }).addVersion(
{
version: INITIAL_REST_VERSION,
security: {
authz: {
requiredPrivileges: ['indexPatterns:manage'],
},
},
validate: {
request: {
params: schema.object({

View file

@ -65,6 +65,11 @@ const deleteRuntimeFieldRouteFactory =
router.versioned.delete({ path, access: 'public', description }).addVersion(
{
version: INITIAL_REST_VERSION,
security: {
authz: {
requiredPrivileges: ['indexPatterns:manage'],
},
},
validate: {
request: {
params: schema.object({

View file

@ -73,6 +73,12 @@ const getRuntimeFieldRouteFactory =
router.versioned.get({ path, access: 'public', description }).addVersion(
{
version: INITIAL_REST_VERSION,
security: {
authz: {
enabled: false,
reason: 'Authorization provided by saved objects client',
},
},
validate: {
request: {
params: schema.object({

View file

@ -81,6 +81,11 @@ const putRuntimeFieldRouteFactory =
router.versioned.put({ path, access: 'public', description }).addVersion(
{
version: INITIAL_REST_VERSION,
security: {
authz: {
requiredPrivileges: ['indexPatterns:manage'],
},
},
validate: {
request: {
params: schema.object({

View file

@ -81,6 +81,11 @@ const updateRuntimeFieldRouteFactory =
router.versioned.post({ path, access: 'public', description }).addVersion(
{
version: INITIAL_REST_VERSION,
security: {
authz: {
requiredPrivileges: ['indexPatterns:manage'],
},
},
validate: {
request: {
params: schema.object({

View file

@ -31,6 +31,11 @@ export const registerCreateScriptedFieldRoute = (
.addVersion(
{
version: INITIAL_REST_VERSION,
security: {
authz: {
requiredPrivileges: ['indexPatterns:manage'],
},
},
validate: {
request: {
params: schema.object(

View file

@ -32,6 +32,11 @@ export const registerDeleteScriptedFieldRoute = (
.addVersion(
{
version: INITIAL_REST_VERSION,
security: {
authz: {
requiredPrivileges: ['indexPatterns:manage'],
},
},
validate: {
request: {
params: schema.object(

View file

@ -31,6 +31,12 @@ export const registerGetScriptedFieldRoute = (
.addVersion(
{
version: INITIAL_REST_VERSION,
security: {
authz: {
enabled: false,
reason: 'Authorization provided by saved objects client',
},
},
validate: {
request: {
params: schema.object(

View file

@ -31,6 +31,11 @@ export const registerPutScriptedFieldRoute = (
.addVersion(
{
version: INITIAL_REST_VERSION,
security: {
authz: {
requiredPrivileges: ['indexPatterns:manage'],
},
},
validate: {
request: {
params: schema.object(

View file

@ -36,6 +36,11 @@ export const registerUpdateScriptedFieldRoute = (
.addVersion(
{
version: INITIAL_REST_VERSION,
security: {
authz: {
requiredPrivileges: ['indexPatterns:manage'],
},
},
validate: {
request: {
params: schema.object(

View file

@ -70,6 +70,14 @@ export const swapReferencesRoute =
router.versioned.post({ path, access: 'public', description }).addVersion(
{
version: INITIAL_REST_VERSION,
security: {
authz: {
enabled: false,
// We don't use the indexPatterns:manage privilege for this route because it can be used for saved object
// types other than index-pattern
reason: 'Authorization provided by saved objects client',
},
},
validate: {
request: {
body: schema.object({

View file

@ -147,6 +147,11 @@ const updateDataViewRouteFactory =
router.versioned.post({ path, access: 'public', description }).addVersion(
{
version: INITIAL_REST_VERSION,
security: {
authz: {
requiredPrivileges: ['indexPatterns:manage'],
},
},
validate: {
request: {
params: schema.object(

View file

@ -881,6 +881,9 @@ exports[`buildOSSFeatures with a basic license returns the indexPatterns feature
Array [
Object {
"privilege": Object {
"api": Array [
"indexPatterns:manage",
],
"app": Array [
"kibana",
],
@ -1520,6 +1523,9 @@ exports[`buildOSSFeatures with a enterprise license returns the indexPatterns fe
Array [
Object {
"privilege": Object {
"api": Array [
"indexPatterns:manage",
],
"app": Array [
"kibana",
],

View file

@ -401,6 +401,7 @@ export const buildOSSFeatures = ({
read: [],
},
ui: ['save'],
api: ['indexPatterns:manage'],
},
read: {
app: ['kibana'],