Authorized route migration for routes owned by @elastic/response-ops (#198188)

### Authz API migration for authorized routes

This PR migrates `access:<privilege>` tags used in route definitions to
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:**
Access control tags were defined in the `options` object of the route:

```ts
router.get({
  path: '/api/path',
  options: {
    tags: ['access:<privilege_1>', 'access:<privilege_2>'],
  },
  ...
}, handler);
```

### **After migration:**
Tags have been replaced with the more robust
`security.authz.requiredPrivileges` field under `security`:

```ts
router.get({
  path: '/api/path',
  security: {
    authz: {
      requiredPrivileges: ['<privilege_1>', '<privilege_2>'],
    },
  },
  ...
}, handler);
```

### What to do next?
1. Review the changes in this PR.
2. You might need to update your tests to reflect the new security
configuration:
  - If you have tests that rely on checking `access` tags.
  - If you have snapshot tests that include the route definition.
- If you have FTR tests that rely on checking unauthorized error
message. The error message changed to also include missing privileges.

## 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: Christos Nasikas <christos.nasikas@elastic.co>
This commit is contained in:
Kibana Machine 2024-11-20 20:03:48 +11:00 committed by GitHub
parent d94f8008a6
commit e5bbd2d31e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
48 changed files with 252 additions and 79 deletions

View file

@ -38,11 +38,15 @@ describe('getFlappingSettingsRoute', () => {
Object {
"options": Object {
"access": "internal",
"tags": Array [
"access:read-flapping-settings",
],
},
"path": "/internal/alerting/rules/settings/_flapping",
"security": Object {
"authz": Object {
"requiredPrivileges": Array [
"read-flapping-settings",
],
},
},
"validate": false,
}
`);

View file

@ -37,9 +37,13 @@ export const getFlappingSettingsRoute = (
{
path: `${INTERNAL_BASE_ALERTING_API_PATH}/rules/settings/_flapping`,
validate: false,
security: {
authz: {
requiredPrivileges: [`${API_PRIVILEGES.READ_FLAPPING_SETTINGS}`],
},
},
options: {
access: 'internal',
tags: [`access:${API_PRIVILEGES.READ_FLAPPING_SETTINGS}`],
},
},
router.handleLegacyErrors(

View file

@ -58,9 +58,16 @@ describe('archiveMaintenanceWindowRoute', () => {
expect(config.options).toMatchInlineSnapshot(`
Object {
"access": "internal",
"tags": Array [
"access:write-maintenance-window",
],
}
`);
expect(config.security).toMatchInlineSnapshot(`
Object {
"authz": Object {
"requiredPrivileges": Array [
"write-maintenance-window",
],
},
}
`);

View file

@ -34,9 +34,13 @@ export const archiveMaintenanceWindowRoute = (
params: archiveParamsSchemaV1,
body: archiveBodySchemaV1,
},
security: {
authz: {
requiredPrivileges: [`${MAINTENANCE_WINDOW_API_PRIVILEGES.WRITE_MAINTENANCE_WINDOW}`],
},
},
options: {
access: 'internal',
tags: [`access:${MAINTENANCE_WINDOW_API_PRIVILEGES.WRITE_MAINTENANCE_WINDOW}`],
},
},
router.handleLegacyErrors(

View file

@ -71,9 +71,16 @@ describe('bulkGetMaintenanceWindowRoute', () => {
expect(config.options).toMatchInlineSnapshot(`
Object {
"access": "internal",
"tags": Array [
"access:read-maintenance-window",
],
}
`);
expect(config.security).toMatchInlineSnapshot(`
Object {
"authz": Object {
"requiredPrivileges": Array [
"read-maintenance-window",
],
},
}
`);

View file

@ -31,9 +31,13 @@ export const bulkGetMaintenanceWindowRoute = (
validate: {
body: bulkGetBodySchemaV1,
},
security: {
authz: {
requiredPrivileges: [`${MAINTENANCE_WINDOW_API_PRIVILEGES.READ_MAINTENANCE_WINDOW}`],
},
},
options: {
access: 'internal',
tags: [`access:${MAINTENANCE_WINDOW_API_PRIVILEGES.READ_MAINTENANCE_WINDOW}`],
},
},
router.handleLegacyErrors(

View file

@ -62,9 +62,16 @@ describe('createMaintenanceWindowRoute', () => {
expect(config.options).toMatchInlineSnapshot(`
Object {
"access": "internal",
"tags": Array [
"access:write-maintenance-window",
],
}
`);
expect(config.security).toMatchInlineSnapshot(`
Object {
"authz": Object {
"requiredPrivileges": Array [
"write-maintenance-window",
],
},
}
`);

View file

@ -32,9 +32,13 @@ export const createMaintenanceWindowRoute = (
validate: {
body: createBodySchemaV1,
},
security: {
authz: {
requiredPrivileges: [`${MAINTENANCE_WINDOW_API_PRIVILEGES.WRITE_MAINTENANCE_WINDOW}`],
},
},
options: {
access: 'internal',
tags: [`access:${MAINTENANCE_WINDOW_API_PRIVILEGES.WRITE_MAINTENANCE_WINDOW}`],
},
},
router.handleLegacyErrors(

View file

@ -50,9 +50,16 @@ describe('deleteMaintenanceWindowRoute', () => {
expect(config.options).toMatchInlineSnapshot(`
Object {
"access": "internal",
"tags": Array [
"access:write-maintenance-window",
],
}
`);
expect(config.security).toMatchInlineSnapshot(`
Object {
"authz": Object {
"requiredPrivileges": Array [
"write-maintenance-window",
],
},
}
`);

View file

@ -29,9 +29,13 @@ export const deleteMaintenanceWindowRoute = (
validate: {
params: deleteParamsSchemaV1,
},
security: {
authz: {
requiredPrivileges: [`${MAINTENANCE_WINDOW_API_PRIVILEGES.WRITE_MAINTENANCE_WINDOW}`],
},
},
options: {
access: 'internal',
tags: [`access:${MAINTENANCE_WINDOW_API_PRIVILEGES.WRITE_MAINTENANCE_WINDOW}`],
},
},
router.handleLegacyErrors(

View file

@ -62,9 +62,16 @@ describe('findMaintenanceWindowsRoute', () => {
expect(config.options).toMatchInlineSnapshot(`
Object {
"access": "internal",
"tags": Array [
"access:read-maintenance-window",
],
}
`);
expect(config.security).toMatchInlineSnapshot(`
Object {
"authz": Object {
"requiredPrivileges": Array [
"read-maintenance-window",
],
},
}
`);
@ -103,9 +110,16 @@ describe('findMaintenanceWindowsRoute', () => {
expect(config.options).toMatchInlineSnapshot(`
Object {
"access": "internal",
"tags": Array [
"access:read-maintenance-window",
],
}
`);
expect(config.security).toMatchInlineSnapshot(`
Object {
"authz": Object {
"requiredPrivileges": Array [
"read-maintenance-window",
],
},
}
`);

View file

@ -49,9 +49,13 @@ export const findMaintenanceWindowsRoute = (
},
},
},
security: {
authz: {
requiredPrivileges: [`${MAINTENANCE_WINDOW_API_PRIVILEGES.READ_MAINTENANCE_WINDOW}`],
},
},
options: {
access: 'internal',
tags: [`access:${MAINTENANCE_WINDOW_API_PRIVILEGES.READ_MAINTENANCE_WINDOW}`],
},
},
router.handleLegacyErrors(

View file

@ -51,9 +51,16 @@ describe('finishMaintenanceWindowRoute', () => {
expect(config.options).toMatchInlineSnapshot(`
Object {
"access": "internal",
"tags": Array [
"access:write-maintenance-window",
],
}
`);
expect(config.security).toMatchInlineSnapshot(`
Object {
"authz": Object {
"requiredPrivileges": Array [
"write-maintenance-window",
],
},
}
`);

View file

@ -31,9 +31,13 @@ export const finishMaintenanceWindowRoute = (
validate: {
params: finishParamsSchemaV1,
},
security: {
authz: {
requiredPrivileges: [`${MAINTENANCE_WINDOW_API_PRIVILEGES.WRITE_MAINTENANCE_WINDOW}`],
},
},
options: {
access: 'internal',
tags: [`access:${MAINTENANCE_WINDOW_API_PRIVILEGES.WRITE_MAINTENANCE_WINDOW}`],
},
},
router.handleLegacyErrors(

View file

@ -51,9 +51,16 @@ describe('getMaintenanceWindowRoute', () => {
expect(config.options).toMatchInlineSnapshot(`
Object {
"access": "internal",
"tags": Array [
"access:read-maintenance-window",
],
}
`);
expect(config.security).toMatchInlineSnapshot(`
Object {
"authz": Object {
"requiredPrivileges": Array [
"read-maintenance-window",
],
},
}
`);

View file

@ -31,9 +31,13 @@ export const getMaintenanceWindowRoute = (
validate: {
params: getParamsSchemaV1,
},
security: {
authz: {
requiredPrivileges: [`${MAINTENANCE_WINDOW_API_PRIVILEGES.READ_MAINTENANCE_WINDOW}`],
},
},
options: {
access: 'internal',
tags: [`access:${MAINTENANCE_WINDOW_API_PRIVILEGES.READ_MAINTENANCE_WINDOW}`],
},
},
router.handleLegacyErrors(

View file

@ -59,9 +59,16 @@ describe('getActiveMaintenanceWindowsRoute', () => {
expect(config.options).toMatchInlineSnapshot(`
Object {
"access": "internal",
"tags": Array [
"access:read-maintenance-window",
],
}
`);
expect(config.security).toMatchInlineSnapshot(`
Object {
"authz": Object {
"requiredPrivileges": Array [
"read-maintenance-window",
],
},
}
`);

View file

@ -26,9 +26,13 @@ export const getActiveMaintenanceWindowsRoute = (
{
path: INTERNAL_ALERTING_API_GET_ACTIVE_MAINTENANCE_WINDOWS_PATH,
validate: {},
security: {
authz: {
requiredPrivileges: [`${MAINTENANCE_WINDOW_API_PRIVILEGES.READ_MAINTENANCE_WINDOW}`],
},
},
options: {
access: 'internal',
tags: [`access:${MAINTENANCE_WINDOW_API_PRIVILEGES.READ_MAINTENANCE_WINDOW}`],
},
},
router.handleLegacyErrors(

View file

@ -68,9 +68,16 @@ describe('updateMaintenanceWindowRoute', () => {
expect(config.options).toMatchInlineSnapshot(`
Object {
"access": "internal",
"tags": Array [
"access:write-maintenance-window",
],
}
`);
expect(config.security).toMatchInlineSnapshot(`
Object {
"authz": Object {
"requiredPrivileges": Array [
"write-maintenance-window",
],
},
}
`);

View file

@ -35,9 +35,13 @@ export const updateMaintenanceWindowRoute = (
body: updateBodySchemaV1,
params: updateParamsSchemaV1,
},
security: {
authz: {
requiredPrivileges: [`${MAINTENANCE_WINDOW_API_PRIVILEGES.WRITE_MAINTENANCE_WINDOW}`],
},
},
options: {
access: 'internal',
tags: [`access:${MAINTENANCE_WINDOW_API_PRIVILEGES.WRITE_MAINTENANCE_WINDOW}`],
},
},
router.handleLegacyErrors(

View file

@ -38,11 +38,15 @@ describe('getQueryDelaySettingsRoute', () => {
Object {
"options": Object {
"access": "internal",
"tags": Array [
"access:read-query-delay-settings",
],
},
"path": "/internal/alerting/rules/settings/_query_delay",
"security": Object {
"authz": Object {
"requiredPrivileges": Array [
"read-query-delay-settings",
],
},
},
"validate": Object {},
}
`);

View file

@ -21,9 +21,13 @@ export const getQueryDelaySettingsRoute = (
{
path: `${INTERNAL_BASE_ALERTING_API_PATH}/rules/settings/_query_delay`,
validate: {},
security: {
authz: {
requiredPrivileges: [`${API_PRIVILEGES.READ_QUERY_DELAY_SETTINGS}`],
},
},
options: {
access: 'internal',
tags: [`access:${API_PRIVILEGES.READ_QUERY_DELAY_SETTINGS}`],
},
},
router.handleLegacyErrors(

View file

@ -46,9 +46,6 @@ describe('updateQueryDelaySettingsRoute', () => {
expect(config.options).toMatchInlineSnapshot(`
Object {
"access": "internal",
"tags": Array [
"access:write-query-delay-settings",
],
}
`);

View file

@ -27,9 +27,13 @@ export const updateQueryDelaySettingsRoute = (
validate: {
body: updateQueryDelaySettingsBodySchemaV1,
},
security: {
authz: {
requiredPrivileges: [`${API_PRIVILEGES.WRITE_QUERY_DELAY_SETTINGS}`],
},
},
options: {
access: 'internal',
tags: [`access:${API_PRIVILEGES.WRITE_QUERY_DELAY_SETTINGS}`],
},
},
router.handleLegacyErrors(

View file

@ -48,9 +48,6 @@ describe('updateFlappingSettingsRoute', () => {
expect(config.options).toMatchInlineSnapshot(`
Object {
"access": "internal",
"tags": Array [
"access:write-flapping-settings",
],
}
`);

View file

@ -60,9 +60,13 @@ export const updateFlappingSettingsRoute = (
validate: {
body: bodySchema,
},
security: {
authz: {
requiredPrivileges: [`${API_PRIVILEGES.WRITE_FLAPPING_SETTINGS}`],
},
},
options: {
access: 'internal',
tags: [`access:${API_PRIVILEGES.WRITE_FLAPPING_SETTINGS}`],
},
},
router.handleLegacyErrors(

View file

@ -45,9 +45,13 @@ export const bulkUpdateAlertsRoute = (router: IRouter<RacRequestHandlerContext>)
])
),
},
security: {
authz: {
requiredPrivileges: ['rac'],
},
},
options: {
access: 'internal',
tags: ['access:rac'],
},
},
async (context, req, response) => {

View file

@ -37,9 +37,13 @@ export const findAlertsByQueryRoute = (router: IRouter<RacRequestHandlerContext>
)
),
},
security: {
authz: {
requiredPrivileges: ['rac'],
},
},
options: {
access: 'internal',
tags: ['access:rac'],
},
},
async (context, request, response) => {

View file

@ -26,9 +26,13 @@ export const getAADFieldsByRuleType = (router: IRouter<RacRequestHandlerContext>
)
),
},
security: {
authz: {
requiredPrivileges: ['rac'],
},
},
options: {
access: 'internal',
tags: ['access:rac'],
},
},
async (context, request, response) => {

View file

@ -34,9 +34,13 @@ export const getAlertByIdRoute = (router: IRouter<RacRequestHandlerContext>) =>
])
),
},
security: {
authz: {
requiredPrivileges: ['rac'],
},
},
options: {
access: 'internal',
tags: ['access:rac'],
},
},
async (context, request, response) => {

View file

@ -27,9 +27,13 @@ export const getAlertsIndexRoute = (router: IRouter<RacRequestHandlerContext>) =
)
),
},
security: {
authz: {
requiredPrivileges: ['rac'],
},
},
options: {
access: 'internal',
tags: ['access:rac'],
},
},
async (context, request, response) => {

View file

@ -39,9 +39,13 @@ export const getAlertSummaryRoute = (router: IRouter<RacRequestHandlerContext>)
])
),
},
security: {
authz: {
requiredPrivileges: ['rac'],
},
},
options: {
access: 'internal',
tags: ['access:rac'],
},
},
async (context, request, response) => {

View file

@ -35,9 +35,13 @@ export const getAlertsGroupAggregations = (router: IRouter<RacRequestHandlerCont
)
),
},
security: {
authz: {
requiredPrivileges: ['rac'],
},
},
options: {
access: 'internal',
tags: ['access:rac'],
},
},
async (context, request, response) => {

View file

@ -26,9 +26,13 @@ export const getBrowserFieldsByFeatureId = (router: IRouter<RacRequestHandlerCon
)
),
},
security: {
authz: {
requiredPrivileges: ['rac'],
},
},
options: {
access: 'internal',
tags: ['access:rac'],
},
},
async (context, request, response) => {

View file

@ -26,9 +26,13 @@ export const getFeatureIdsByRegistrationContexts = (router: IRouter<RacRequestHa
)
),
},
security: {
authz: {
requiredPrivileges: ['rac'],
},
},
options: {
access: 'internal',
tags: ['access:rac'],
},
},
async (context, request, response) => {

View file

@ -35,9 +35,13 @@ export const updateAlertByIdRoute = (router: IRouter<RacRequestHandlerContext>)
])
),
},
security: {
authz: {
requiredPrivileges: ['rac'],
},
},
options: {
access: 'internal',
tags: ['access:rac'],
},
},
async (context, req, response) => {

View file

@ -42,7 +42,8 @@ export default function getFlappingSettingsTests({ getService }: FtrProviderCont
expect(response.statusCode).to.eql(403);
expect(response.body).to.eql({
error: 'Forbidden',
message: 'Forbidden',
message:
'API [GET /internal/alerting/rules/settings/_flapping] is unauthorized for user, this action is granted by the Kibana privileges [read-flapping-settings]',
statusCode: 403,
});
break;

View file

@ -44,7 +44,8 @@ export default function getQueryDelaySettingsTests({ getService }: FtrProviderCo
expect(response.statusCode).to.eql(403);
expect(response.body).to.eql({
error: 'Forbidden',
message: 'Forbidden',
message:
'API [GET /internal/alerting/rules/settings/_query_delay] is unauthorized for user, this action is granted by the Kibana privileges [read-query-delay-settings]',
statusCode: 403,
});
break;

View file

@ -43,7 +43,8 @@ export default function updateFlappingSettingsTest({ getService }: FtrProviderCo
expect(response.statusCode).to.eql(403);
expect(response.body).to.eql({
error: 'Forbidden',
message: 'Forbidden',
message:
'API [POST /internal/alerting/rules/settings/_flapping] is unauthorized for user, this action is granted by the Kibana privileges [write-flapping-settings]',
statusCode: 403,
});
break;

View file

@ -43,7 +43,8 @@ export default function updateQueryDelaySettingsTest({ getService }: FtrProvider
expect(response.statusCode).to.eql(403);
expect(response.body).to.eql({
error: 'Forbidden',
message: 'Forbidden',
message:
'API [POST /internal/alerting/rules/settings/_query_delay] is unauthorized for user, this action is granted by the Kibana privileges [write-query-delay-settings]',
statusCode: 403,
});
break;

View file

@ -92,7 +92,8 @@ export default function activeMaintenanceWindowTests({ getService }: FtrProvider
expect(response.statusCode).to.eql(403);
expect(response.body).to.eql({
error: 'Forbidden',
message: 'Forbidden',
message:
'API [GET /internal/alerting/rules/maintenance_window/_active] is unauthorized for user, this action is granted by the Kibana privileges [read-maintenance-window]',
statusCode: 403,
});
break;

View file

@ -65,7 +65,7 @@ export default function updateMaintenanceWindowTests({ getService }: FtrProvider
expect(response.statusCode).to.eql(403);
expect(response.body).to.eql({
error: 'Forbidden',
message: 'Forbidden',
message: `API [POST /internal/alerting/rules/maintenance_window/${createdMaintenanceWindow.id}/_archive] is unauthorized for user, this action is granted by the Kibana privileges [write-maintenance-window]`,
statusCode: 403,
});
break;

View file

@ -88,7 +88,8 @@ export default function createMaintenanceWindowTests({ getService }: FtrProvider
expect(response.statusCode).to.eql(403);
expect(response.body).to.eql({
error: 'Forbidden',
message: 'Forbidden',
message:
'API [POST /internal/alerting/rules/maintenance_window] is unauthorized for user, this action is granted by the Kibana privileges [write-maintenance-window]',
statusCode: 403,
});
break;

View file

@ -55,7 +55,7 @@ export default function deleteMaintenanceWindowTests({ getService }: FtrProvider
expect(response.statusCode).to.eql(403);
expect(response.body).to.eql({
error: 'Forbidden',
message: 'Forbidden',
message: `API [DELETE /internal/alerting/rules/maintenance_window/${maintenanceWindowBody.id}] is unauthorized for user, this action is granted by the Kibana privileges [write-maintenance-window]`,
statusCode: 403,
});
objectRemover.add(

View file

@ -71,7 +71,8 @@ export default function findMaintenanceWindowTests({ getService }: FtrProviderCo
expect(response.statusCode).to.eql(403);
expect(response.body).to.eql({
error: 'Forbidden',
message: 'Forbidden',
message:
'API [GET /internal/alerting/rules/maintenance_window/_find] is unauthorized for user, this action is granted by the Kibana privileges [read-maintenance-window]',
statusCode: 403,
});
break;
@ -135,7 +136,8 @@ export default function findMaintenanceWindowTests({ getService }: FtrProviderCo
expect(response.statusCode).to.eql(403);
expect(response.body).to.eql({
error: 'Forbidden',
message: 'Forbidden',
message:
'API [GET /internal/alerting/rules/maintenance_window/_find?page=1&per_page=1] is unauthorized for user, this action is granted by the Kibana privileges [read-maintenance-window]',
statusCode: 403,
});
break;
@ -184,7 +186,8 @@ export default function findMaintenanceWindowTests({ getService }: FtrProviderCo
expect(response.statusCode).to.eql(403);
expect(response.body).to.eql({
error: 'Forbidden',
message: 'Forbidden',
message:
'API [GET /internal/alerting/rules/maintenance_window/_find?page=101&per_page=100] is unauthorized for user, this action is granted by the Kibana privileges [read-maintenance-window]',
statusCode: 403,
});
break;

View file

@ -66,7 +66,7 @@ export default function findMaintenanceWindowTests({ getService }: FtrProviderCo
expect(response.statusCode).to.eql(403);
expect(response.body).to.eql({
error: 'Forbidden',
message: 'Forbidden',
message: `API [POST /internal/alerting/rules/maintenance_window/${createdMaintenanceWindow.id}/_finish] is unauthorized for user, this action is granted by the Kibana privileges [write-maintenance-window]`,
statusCode: 403,
});
break;

View file

@ -61,7 +61,7 @@ export default function getMaintenanceWindowTests({ getService }: FtrProviderCon
expect(response.statusCode).to.eql(403);
expect(response.body).to.eql({
error: 'Forbidden',
message: 'Forbidden',
message: `API [GET /internal/alerting/rules/maintenance_window/${createdMaintenanceWindow.id}] is unauthorized for user, this action is granted by the Kibana privileges [read-maintenance-window]`,
statusCode: 403,
});
break;

View file

@ -99,7 +99,7 @@ export default function updateMaintenanceWindowTests({ getService }: FtrProvider
expect(response.statusCode).to.eql(403);
expect(response.body).to.eql({
error: 'Forbidden',
message: 'Forbidden',
message: `API [POST /internal/alerting/rules/maintenance_window/${createdMaintenanceWindow.id}] is unauthorized for user, this action is granted by the Kibana privileges [write-maintenance-window]`,
statusCode: 403,
});
break;