Unauthorized route migration for routes owned by kibana-core (#198333)

### 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: Jean-Louis Leysens <jeanlouis.leysens@elastic.co>
This commit is contained in:
Kibana Machine 2024-11-12 23:21:58 +11:00 committed by GitHub
parent 9bb3661060
commit 763b5deafd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 281 additions and 25 deletions

View file

@ -14,6 +14,12 @@ export const registerHitsStatusRoute = (router: IRouter) => {
router.post(
{
path: '/api/home/hits_status',
security: {
authz: {
enabled: false,
reason: 'This route is opted out from authorization',
},
},
validate: {
body: schema.object({
index: schema.string(),

View file

@ -15,6 +15,12 @@ export const registerBulkDeleteRoute = (router: IRouter) => {
router.post(
{
path: '/internal/kibana/management/saved_objects/_bulk_delete',
security: {
authz: {
enabled: false,
reason: 'This route is opted out from authorization',
},
},
validate: {
body: schema.arrayOf(
schema.object({

View file

@ -20,6 +20,12 @@ export const registerBulkGetRoute = (
router.post(
{
path: '/api/kibana/management/saved_objects/_bulk_get',
security: {
authz: {
enabled: false,
reason: 'This route is opted out from authorization',
},
},
validate: {
body: schema.arrayOf(
schema.object({

View file

@ -34,6 +34,12 @@ export const registerFindRoute = (
router.get(
{
path: '/api/kibana/management/saved_objects/_find',
security: {
authz: {
enabled: false,
reason: 'This route is opted out from authorization',
},
},
validate: {
query: schema.object({
perPage: schema.number({ min: 0, defaultValue: 20 }),

View file

@ -23,6 +23,12 @@ export const registerGetAllowedTypesRoute = (router: IRouter) => {
router.get(
{
path: '/api/kibana/management/saved_objects/_allowed_types',
security: {
authz: {
enabled: false,
reason: 'This route is opted out from authorization',
},
},
validate: false,
},
async (context, req, res) => {

View file

@ -21,6 +21,12 @@ export const registerRelationshipsRoute = (
router.get(
{
path: '/api/kibana/management/saved_objects/relationships/{type}/{id}',
security: {
authz: {
enabled: false,
reason: 'This route is opted out from authorization',
},
},
validate: {
params: schema.object({
type: schema.string(),

View file

@ -17,6 +17,12 @@ export const registerScrollForCountRoute = (router: IRouter) => {
router.post(
{
path: '/api/kibana/management/saved_objects/scroll/counts',
security: {
authz: {
enabled: false,
reason: 'This route is opted out from authorization',
},
},
validate: {
body: schema.object({
typesToInclude: schema.arrayOf(schema.string()),

View file

@ -102,12 +102,46 @@ export function registerTelemetryConfigRoutes({
options: { authRequired: 'optional' },
})
// Just because it used to be /v2/, we are creating identical v1 and v2.
.addVersion({ version: '1', validate: v2Validations }, v2Handler)
.addVersion({ version: '2', validate: v2Validations }, v2Handler);
.addVersion(
{
version: '1',
security: {
authz: {
enabled: false,
reason: 'This route is opted out from authorization',
},
},
validate: v2Validations,
},
v2Handler
)
.addVersion(
{
version: '2',
security: {
authz: {
enabled: false,
reason: 'This route is opted out from authorization',
},
},
validate: v2Validations,
},
v2Handler
);
// Register the deprecated public and path-based for BWC
// as we know this one is used by other Elastic products to fetch the opt-in status.
router.versioned
.get({ access: 'public', path: FetchTelemetryConfigRoutePathBasedV2 })
.addVersion({ version: '2023-10-31', validate: v2Validations }, v2Handler);
router.versioned.get({ access: 'public', path: FetchTelemetryConfigRoutePathBasedV2 }).addVersion(
{
version: '2023-10-31',
security: {
authz: {
enabled: false,
reason: 'This route is opted out from authorization',
},
},
validate: v2Validations,
},
v2Handler
);
}

View file

@ -40,8 +40,32 @@ export function registerTelemetryLastReported(
router.versioned
.get({ access: 'internal', path: LastReportedRoute })
// Just because it used to be /v2/, we are creating identical v1 and v2.
.addVersion({ version: '1', validate: v2GetValidations }, v2GetHandler)
.addVersion({ version: '2', validate: v2GetValidations }, v2GetHandler);
.addVersion(
{
version: '1',
security: {
authz: {
enabled: false,
reason: 'This route is opted out from authorization',
},
},
validate: v2GetValidations,
},
v2GetHandler
)
.addVersion(
{
version: '2',
security: {
authz: {
enabled: false,
reason: 'This route is opted out from authorization',
},
},
validate: v2GetValidations,
},
v2GetHandler
);
// PUT to update
const v2PutHandler: RequestHandler = async (context, req, res) => {
@ -55,6 +79,30 @@ export function registerTelemetryLastReported(
router.versioned
.put({ access: 'internal', path: LastReportedRoute })
// Just because it used to be /v2/, we are creating identical v1 and v2.
.addVersion({ version: '1', validate: false }, v2PutHandler)
.addVersion({ version: '2', validate: false }, v2PutHandler);
.addVersion(
{
version: '1',
security: {
authz: {
enabled: false,
reason: 'This route is opted out from authorization',
},
},
validate: false,
},
v2PutHandler
)
.addVersion(
{
version: '2',
security: {
authz: {
enabled: false,
reason: 'This route is opted out from authorization',
},
},
validate: false,
},
v2PutHandler
);
}

View file

@ -128,6 +128,30 @@ export function registerTelemetryOptInRoutes({
router.versioned
.post({ access: 'internal', path: OptInRoute })
// Just because it used to be /v2/, we are creating identical v1 and v2.
.addVersion({ version: '1', validate: v2Validations }, v2Handler)
.addVersion({ version: '2', validate: v2Validations }, v2Handler);
.addVersion(
{
version: '1',
security: {
authz: {
enabled: false,
reason: 'This route is opted out from authorization',
},
},
validate: v2Validations,
},
v2Handler
)
.addVersion(
{
version: '2',
security: {
authz: {
enabled: false,
reason: 'This route is opted out from authorization',
},
},
validate: v2Validations,
},
v2Handler
);
}

View file

@ -72,6 +72,12 @@ export function registerTelemetryOptInStatsRoutes(
.addVersion(
{
version: '2023-10-31',
security: {
authz: {
enabled: false,
reason: 'This route is opted out from authorization',
},
},
validate: {
request: {
body: schema.object({

View file

@ -97,6 +97,30 @@ export function registerTelemetryUsageStatsRoutes(
enableQueryVersion: true, // Allow specifying the version through querystring so that we can use it in Dev Console
})
// Just because it used to be /v2/, we are creating identical v1 and v2.
.addVersion({ version: '1', validate: v2Validations }, v2Handler)
.addVersion({ version: '2', validate: v2Validations }, v2Handler);
.addVersion(
{
version: '1',
security: {
authz: {
enabled: false,
reason: 'This route is opted out from authorization',
},
},
validate: v2Validations,
},
v2Handler
)
.addVersion(
{
version: '2',
security: {
authz: {
enabled: false,
reason: 'This route is opted out from authorization',
},
},
validate: v2Validations,
},
v2Handler
);
}

View file

@ -56,6 +56,30 @@ export function registerTelemetryUserHasSeenNotice(router: IRouter, currentKiban
router.versioned
.put({ access: 'internal', path: UserHasSeenNoticeRoute })
// Just because it used to be /v2/, we are creating identical v1 and v2.
.addVersion({ version: '1', validate: false }, v2Handler)
.addVersion({ version: '2', validate: false }, v2Handler);
.addVersion(
{
version: '1',
security: {
authz: {
enabled: false,
reason: 'This route is opted out from authorization',
},
},
validate: false,
},
v2Handler
)
.addVersion(
{
version: '2',
security: {
authz: {
enabled: false,
reason: 'This route is opted out from authorization',
},
},
validate: false,
},
v2Handler
);
}

View file

@ -54,6 +54,12 @@ export function registerStatsRoute({
router.get(
{
path: '/api/stats',
security: {
authz: {
enabled: false,
reason: 'This route is opted out from authorization',
},
},
options: {
authRequired: !config.allowAnonymous,
// The `api` tag ensures that unauthenticated calls receive a 401 rather than a 302 redirect to login page.

View file

@ -21,6 +21,12 @@ export function registerUiCountersRoute(
router.post(
{
path: '/api/ui_counters/_report',
security: {
authz: {
enabled: false,
reason: 'This route is opted out from authorization',
},
},
validate: {
body: schema.object({
report: reportSchema,

View file

@ -24,12 +24,24 @@ export function defineRoutes({
path: ELASTICSEARCH_CONFIG_ROUTE,
access: 'internal',
})
.addVersion({ version: '1', validate: {} }, async (context, request, response) => {
const body: ElasticsearchConfigType = {
elasticsearch_url: elasticsearchUrl,
};
return response.ok({
body,
});
});
.addVersion(
{
version: '1',
security: {
authz: {
enabled: false,
reason: 'This route is opted out from authorization',
},
},
validate: {},
},
async (context, request, response) => {
const body: ElasticsearchConfigType = {
elasticsearch_url: elasticsearchUrl,
};
return response.ok({
body,
});
}
);
}

View file

@ -14,7 +14,16 @@ export function registerFeatureUsageRoute(
getStartServices: StartServicesAccessor<{}, LicensingPluginStart>
) {
router.get(
{ path: '/api/licensing/feature_usage', validate: false },
{
path: '/api/licensing/feature_usage',
security: {
authz: {
enabled: false,
reason: 'This route is opted out from authorization',
},
},
validate: false,
},
async (context, request, response) => {
const [, , { featureUsage }] = await getStartServices();
return response.ok({

View file

@ -9,7 +9,16 @@ import { LicensingRouter } from '../types';
export function registerInfoRoute(router: LicensingRouter) {
router.get(
{ path: '/api/licensing/info', validate: false },
{
path: '/api/licensing/info',
security: {
authz: {
enabled: false,
reason: 'This route is opted out from authorization',
},
},
validate: false,
},
async (context, request, response) => {
return response.ok({
body: (await context.licensing).license,

View file

@ -12,6 +12,12 @@ export function registerNotifyFeatureUsageRoute(router: LicensingRouter) {
router.post(
{
path: '/internal/licensing/feature_usage/notify',
security: {
authz: {
enabled: false,
reason: 'This route is opted out from authorization',
},
},
validate: {
body: schema.object({
featureName: schema.string(),

View file

@ -17,6 +17,12 @@ export function registerRegisterFeatureRoute(
router.post(
{
path: '/internal/licensing/feature_usage/register',
security: {
authz: {
enabled: false,
reason: 'This route is opted out from authorization',
},
},
validate: {
body: schema.arrayOf(
schema.object({