[8.x] [Authz] Migrated routes with access tags to security config (#209756) (#210596)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Authz] Migrated routes with access tags to security config
(#209756)](https://github.com/elastic/kibana/pull/209756)

<!--- Backport version: 9.4.3 -->

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

<!--BACKPORT [{"author":{"name":"Elena
Shostak","email":"165678770+elena-shostak@users.noreply.github.com"},"sourceCommit":{"committedDate":"2025-02-11T14:36:38Z","message":"[Authz]
Migrated routes with access tags to security config (#209756)\n\n##
Summary\r\n\r\nThis PR migrates the last routes with
`access:<privilege>` tags used in\r\nroute definitions to new security
configuration.\r\nPlease refer to the documentation for more
information:
[Authorization\r\nAPI](https://docs.elastic.dev/kibana-dev-docs/key-concepts/security-api-authorization)\r\n\r\n###
**Before Migration:**\r\nAccess control tags were defined in the
`options` object of the route:\r\n\r\n```ts\r\nrouter.get({\r\n path:
'/api/path',\r\n options: {\r\n tags: ['access:<privilege_1>',
'access:<privilege_2>'],\r\n },\r\n ...\r\n},
handler);\r\n```\r\n\r\n### **After Migration:**\r\nTags have been
replaced with the more robust\r\n`security.authz.requiredPrivileges`
field under `security`:\r\n\r\n```ts\r\nrouter.get({\r\n path:
'/api/path',\r\n security: {\r\n authz: {\r\n requiredPrivileges:
['<privilege_1>', '<privilege_2>'],\r\n },\r\n },\r\n ...\r\n},
handler);\r\n```\r\n\r\n### Checklist\r\n\r\n-
[x]\r\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\r\nwas
added for features that require explanation or tutorials\r\n- [x] [Unit
or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common
scenarios\r\n\r\n---------\r\n\r\nCo-authored-by: Elastic Machine
<elasticmachine@users.noreply.github.com>","sha":"ad0e1d9d9d5ffba3c0bd7839affe0e885c3f2f03","branchLabelMapping":{"^v9.1.0$":"main","^v8.19.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["Team:Security","release_note:skip","Feature:Security/Authorization","backport:prev-minor","backport:version","Authz:
API migration","v9.1.0","v8.19.0"],"title":"[Authz] Migrated routes with
access tags to security
config","number":209756,"url":"https://github.com/elastic/kibana/pull/209756","mergeCommit":{"message":"[Authz]
Migrated routes with access tags to security config (#209756)\n\n##
Summary\r\n\r\nThis PR migrates the last routes with
`access:<privilege>` tags used in\r\nroute definitions to new security
configuration.\r\nPlease refer to the documentation for more
information:
[Authorization\r\nAPI](https://docs.elastic.dev/kibana-dev-docs/key-concepts/security-api-authorization)\r\n\r\n###
**Before Migration:**\r\nAccess control tags were defined in the
`options` object of the route:\r\n\r\n```ts\r\nrouter.get({\r\n path:
'/api/path',\r\n options: {\r\n tags: ['access:<privilege_1>',
'access:<privilege_2>'],\r\n },\r\n ...\r\n},
handler);\r\n```\r\n\r\n### **After Migration:**\r\nTags have been
replaced with the more robust\r\n`security.authz.requiredPrivileges`
field under `security`:\r\n\r\n```ts\r\nrouter.get({\r\n path:
'/api/path',\r\n security: {\r\n authz: {\r\n requiredPrivileges:
['<privilege_1>', '<privilege_2>'],\r\n },\r\n },\r\n ...\r\n},
handler);\r\n```\r\n\r\n### Checklist\r\n\r\n-
[x]\r\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\r\nwas
added for features that require explanation or tutorials\r\n- [x] [Unit
or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common
scenarios\r\n\r\n---------\r\n\r\nCo-authored-by: Elastic Machine
<elasticmachine@users.noreply.github.com>","sha":"ad0e1d9d9d5ffba3c0bd7839affe0e885c3f2f03"}},"sourceBranch":"main","suggestedTargetBranches":["8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.1.0","branchLabelMappingKey":"^v9.1.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/209756","number":209756,"mergeCommit":{"message":"[Authz]
Migrated routes with access tags to security config (#209756)\n\n##
Summary\r\n\r\nThis PR migrates the last routes with
`access:<privilege>` tags used in\r\nroute definitions to new security
configuration.\r\nPlease refer to the documentation for more
information:
[Authorization\r\nAPI](https://docs.elastic.dev/kibana-dev-docs/key-concepts/security-api-authorization)\r\n\r\n###
**Before Migration:**\r\nAccess control tags were defined in the
`options` object of the route:\r\n\r\n```ts\r\nrouter.get({\r\n path:
'/api/path',\r\n options: {\r\n tags: ['access:<privilege_1>',
'access:<privilege_2>'],\r\n },\r\n ...\r\n},
handler);\r\n```\r\n\r\n### **After Migration:**\r\nTags have been
replaced with the more robust\r\n`security.authz.requiredPrivileges`
field under `security`:\r\n\r\n```ts\r\nrouter.get({\r\n path:
'/api/path',\r\n security: {\r\n authz: {\r\n requiredPrivileges:
['<privilege_1>', '<privilege_2>'],\r\n },\r\n },\r\n ...\r\n},
handler);\r\n```\r\n\r\n### Checklist\r\n\r\n-
[x]\r\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\r\nwas
added for features that require explanation or tutorials\r\n- [x] [Unit
or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common
scenarios\r\n\r\n---------\r\n\r\nCo-authored-by: Elastic Machine
<elasticmachine@users.noreply.github.com>","sha":"ad0e1d9d9d5ffba3c0bd7839affe0e885c3f2f03"}},{"branch":"8.x","label":"v8.19.0","branchLabelMappingKey":"^v8.19.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Elena Shostak <165678770+elena-shostak@users.noreply.github.com>
This commit is contained in:
Kibana Machine 2025-02-12 03:37:06 +11:00 committed by GitHub
parent 1e03fc0166
commit 5d11caa37b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 67 additions and 40 deletions

View file

@ -69,8 +69,10 @@ export class FeatureControlsPluginExample
{
path: '/internal/my_plugin/sensitive_action',
validate: false,
options: {
tags: ['access:my_closed_example_api'],
security: {
authz: {
requiredPrivileges: ['my_closed_example_api'],
},
},
},
async (context, request, response) => {

View file

@ -40,7 +40,11 @@ export class UserProfilesPlugin implements Plugin<void, void, SetupDeps, StartDe
/**
* Important: You must restrict access to this endpoint using access `tags`.
*/
options: { tags: ['access:suggestUserProfiles'] },
security: {
authz: {
requiredPrivileges: ['suggestUserProfiles'],
},
},
},
async (context, request, response) => {
const [, pluginDeps] = await core.getStartServices();

View file

@ -268,9 +268,11 @@ export const createEntityRoute = (router: Router): void => {
.post({
access: 'public',
path: '/api/my/data/{id}',
options: {
tags: ['access:securitySolution'],
},
security: {
authz: {
requiredPrivileges: ['securitySolution']
}
}
})
.addVersion(
{

View file

@ -93,8 +93,10 @@ describe('CoreApp', () => {
expect(routerMock.versioned.put).toHaveBeenCalledWith({
path: '/internal/core/_settings',
access: 'internal',
options: {
tags: ['access:updateDynamicConfig'],
security: {
authz: {
requiredPrivileges: ['updateDynamicConfig'],
},
},
});
});

View file

@ -278,8 +278,10 @@ export class CoreAppsService {
.put({
path: '/internal/core/_settings',
access: 'internal',
options: {
tags: ['access:updateDynamicConfig'],
security: {
authz: {
requiredPrivileges: ['updateDynamicConfig'],
},
},
})
.addVersion(

View file

@ -87,13 +87,9 @@ export const registerAutocompleteEntitiesRoute = (deps: RouteDependencies) => {
deps.router.get(
{
path: '/api/console/autocomplete_entities',
options: {
tags: ['access:console'],
},
security: {
authz: {
enabled: false,
reason: 'Relies on es client for authorization',
requiredPrivileges: ['console'],
},
},
validate: autoCompleteEntitiesValidationConfig,

View file

@ -46,7 +46,7 @@ export function registerTelemetryUsageStatsRoutes(
const security = getSecurity();
// We need to check useRbacForRequest to figure out if ES has security enabled before making the privileges check
if (security && unencrypted && security.authz.mode.useRbacForRequest(req)) {
// Normally we would use `options: { tags: ['access:decryptedTelemetry'] }` in the route definition to check authorization for an
// Normally we would use `security: { authz: { requiredPrivileges: ['decryptedTelemetry'] } } }` in the route definition to check authorization for an
// API action, however, we want to check this conditionally based on the `unencrypted` parameter. In this case we need to use the
// security API directly to check privileges for this action. Note that the 'decryptedTelemetry' API privilege string is only
// granted to users that have "Global All" or "Global Read" privileges in Kibana.

View file

@ -122,8 +122,10 @@ export function routes(coreSetup: CoreSetup<StartDeps, unknown>, logger: Logger)
.post({
path: '/internal/data_visualizer/inference/{inferenceId}',
access: 'internal',
options: {
tags: ['access:fileUpload:analyzeFile'],
security: {
authz: {
requiredPrivileges: ['fileUpload:analyzeFile'],
},
},
})
.addVersion(

View file

@ -54,25 +54,27 @@ export interface FeatureKibanaPrivileges {
*
* @example
* ```ts
* // Configure your routes with a tag starting with the 'access:' prefix
* // Configure your routes with requiredPrivileges
* server.route({
* path: '/api/my-route',
* method: 'GET',
* handler: () => { ...},
* options: {
* tags: ['access:my_feature-admin']
* }
* security: {
* authz: {
* requiredPrivileges: ['my_feature_admin']
* },
* },
* });
*
* Then, specify the tags here (without the 'access:' prefix) which should be secured:
* Then, specify requiredPrivileges which should be secured:
*
* {
* api: ['my_feature-admin']
* api: ['my_feature_admin']
* }
* ```
*
* NOTE: It is important to name your tags in a way that will not collide with other platform/plugins/shared/features.
* A generic tag name like "access:read" could be used elsewhere, and access to that API endpoint would also
* NOTE: It is important to name your privileges in a way that will not collide with other platform/plugins/shared/features.
* A generic tag name like "read" could be used elsewhere, and access to that API endpoint would also
* extend to any routes you have also tagged with that name.
*/
api?: readonly string[];

View file

@ -119,7 +119,11 @@ export function backgroundTaskUtilizationRoute(
},
},
// Uncomment when we determine that we can restrict API usage to Global admins based on telemetry
// options: { tags: ['access:taskManager'] },
// security: {
// authz: {
// requiredPrivileges: ['taskManager'],
// },
// },
validate: false,
options: {
access: 'public', // access must be public to allow "system" users, like metrics collectors, to access these routes

View file

@ -148,7 +148,11 @@ export function healthRoute(params: HealthRouteParams): {
},
},
// Uncomment when we determine that we can restrict API usage to Global admins based on telemetry
// options: { tags: ['access:taskManager'] },
// security: {
// authz: {
// requiredPrivileges: ['taskManager'],
// },
// },
validate: false,
options: {
access: 'public',

View file

@ -62,7 +62,11 @@ export function metricsRoute(params: MetricsRouteParams) {
tags: ['security:acceptJWT'],
},
// Uncomment when we determine that we can restrict API usage to Global admins based on telemetry
// options: { tags: ['access:taskManager'] },
// security: {
// authz: {
// requiredPrivileges: ['taskManager'],
// },
// },
validate: {
query: QuerySchema,
},

View file

@ -9,8 +9,10 @@ import { extractEntityIndexPatternsFromDefinitions } from './extract_entity_inde
export const getEntityDefinitionSourceIndexPatternsByType = createInventoryServerRoute({
endpoint: 'GET /internal/inventory/entity/definitions/sources',
options: {
tags: ['access:inventory'],
security: {
authz: {
requiredPrivileges: ['inventory'],
},
},
async handler({ context, request, plugins }) {
const [_coreContext, entityManagerStart] = await Promise.all([

View file

@ -44,15 +44,12 @@ export class KibanaFramework {
config: InfraRouteConfig<Params, Query, Body, Method>,
handler: RequestHandler<Params, Query, Body, RequestHandlerContext>
) {
const defaultOptions = {
tags: ['access:infra'],
};
const routeConfig = {
path: config.path,
validate: config.validate,
// Currently we have no use of custom options beyond tags, this can be extended
// beyond defaultOptions if it's needed.
options: defaultOptions,
security: {
authz: { requiredPrivileges: ['infra'] },
},
};
switch (config.method) {
case 'get':

View file

@ -122,11 +122,13 @@ export const bulkDeleteRulesRoute = (
access: 'public',
path: DETECTION_ENGINE_RULES_BULK_DELETE,
options: {
tags: ['access:securitySolution'],
timeout: {
idleSocket: RULE_MANAGEMENT_BULK_ACTION_SOCKET_TIMEOUT_MS,
},
},
security: {
authz: { requiredPrivileges: ['securitySolution'] },
},
};
router.versioned.delete(routeConfig).addVersion(
{

View file

@ -25,8 +25,10 @@ export const entityStoreInternalPrivilegesRoute = (
.get({
access: 'internal',
path: ENTITY_STORE_INTERNAL_PRIVILEGES_URL,
options: {
tags: ['access:securitySolution', `access:${APP_ID}-entity-analytics`],
security: {
authz: {
requiredPrivileges: ['securitySolution', `${APP_ID}-entity-analytics`],
},
},
})
.addVersion(