From 521dedd6ea97a9ac331ce9116e67e02b827011fe Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 8 Jan 2025 12:27:21 +0100 Subject: [PATCH] docs/permissions: update to use new integrations service Signed-off-by: Patrik Oldsberg --- docs/permissions/custom-rules.md | 18 ++-- .../02-adding-a-basic-permission-check.md | 22 ++--- .../03-adding-a-resource-permission-check.md | 82 +++++++++++-------- ...04-authorizing-access-to-paginated-data.md | 48 +++++++---- 4 files changed, 100 insertions(+), 70 deletions(-) diff --git a/docs/permissions/custom-rules.md b/docs/permissions/custom-rules.md index 00f4d81939..fa56637bd6 100644 --- a/docs/permissions/custom-rules.md +++ b/docs/permissions/custom-rules.md @@ -139,7 +139,13 @@ class CustomPermissionPolicy implements PermissionPolicy { Now that we have a custom rule defined and added to our policy, we need provide it to the catalog plugin. This step is important because the catalog plugin will use the rule's `toQuery` and `apply` methods while evaluating conditional authorize results. There's no guarantee that the catalog and permission backends are running on the same server, so we must explicitly link the rule to ensure that it's available at runtime. -The api for providing custom rules may differ between plugins, but there should typically be an [extension point](../backend-system/architecture/05-extension-points.md) that you can use in your created module to add your rule. For the catalog, this extension point is exposed via `catalogPermissionExtensionPoint`. Here's the steps you'll need to take to add the `isInSystemRule` we created above to the catalog: +:::warning Warning + +The `PermissionIntegrationsService` is a fairly new addition and not yet supported by all plugins as they might still be using the old `createPermissionIntegrationRouter` that cannot be extended. If you encounter errors when installing custom rules for a plugin, the plugin may need to be switched to using the `PermissionIntegrationsService` first. + +::: + +To install custom rules in a plugin, we need to use the [`PermissionIntegrationsService`](../backend-system/core-services/permissionIntegrations.md). Here's the steps you'll need to take to add the `isInSystemRule` we created above to the catalog: 1. We will be using the `@backstage/plugin-catalog-node` package as it contains the extension point we need. Run this to add it: @@ -147,10 +153,10 @@ The api for providing custom rules may differ between plugins, but there should yarn --cwd packages/backend add @backstage/plugin-catalog-node ``` -2. Next create a `catalogPermissionRules.ts` file in the `packages/backend/src/extensions` folder. +2. Next create a `catalogPermissionRules.ts` file in the `packages/backend/src/modules` folder. 3. Then add this as the contents of the new `catalogPermissionRules.ts` file: - ```typescript title="packages/backend/src/extensions/catalogPermissionRules.ts" + ```typescript title="packages/backend/src/modules/catalogPermissionRules.ts" import { createBackendModule } from '@backstage/backend-plugin-api'; import { catalogPermissionExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; import { isInSystemRule } from './permissionPolicyExtension'; @@ -160,9 +166,9 @@ The api for providing custom rules may differ between plugins, but there should moduleId: 'permission-rules', register(reg) { reg.registerInit({ - deps: { catalog: catalogPermissionExtensionPoint }, - async init({ catalog }) { - catalog.addPermissionRules(isInSystemRule); + deps: { permissionIntegrations: coreServices.permissionIntegrations }, + async init({ permissionIntegrations }) { + permissionIntegrations.addPermissionRules([isInSystemRule]); }, }); }, diff --git a/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md b/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md index 2d39d5cef3..f6b4c11433 100644 --- a/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md +++ b/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md @@ -67,8 +67,6 @@ import { LoggerService, HttpAuthService } from '@backstage/backend-plugin-api'; import { InputError, NotAllowedError } from '@backstage/errors'; import { LoggerService, HttpAuthService, PermissionsService } from '@backstage/backend-plugin-api'; import { AuthorizeResult } from '@backstage/plugin-permission-common'; -import { createPermissionIntegrationRouter } from '@backstage/plugin-permission-node'; -import { todoListCreatePermission } from '@internal/plugin-todo-list-common'; /* highlight-add-end */ export interface RouterOptions { @@ -86,12 +84,6 @@ export async function createRouter( /* highlight-add-next-line */ const { logger, httpAuth, permissions } = options; - /* highlight-add-start */ - const permissionIntegrationRouter = createPermissionIntegrationRouter({ - permissions: [todoListCreatePermission], - }); - /* highlight-add-end */ - const router = Router(); router.use(express.json()); @@ -100,9 +92,6 @@ export async function createRouter( response.json({ status: 'ok' }); }); - /* highlight-add-next-line */ - router.use(permissionIntegrationRouter); - router.get('/todos', async (_req, res) => { res.json(getAll()); }); @@ -137,11 +126,13 @@ export async function createRouter( // ... ``` -Pass the `permissions` object to the plugin in `plugins/todo-list-backend/src/plugin.ts`: +Pass the `permissions` service and register the new permission to the plugin in `plugins/todo-list-backend/src/plugin.ts`: ```ts title="plugins/todo-list-backend/src/plugin.ts" import { coreServices, createBackendPlugin } from '@backstage/backend-plugin-api'; import { createRouter } from './service/router'; +/* highlight-add-next-line */ +import { todoListCreatePermission } from '@internal/plugin-todo-list-common'; export const exampleTodoListPlugin = createBackendPlugin({ pluginId: 'todolist', @@ -153,11 +144,16 @@ export const exampleTodoListPlugin = createBackendPlugin({ httpRouter: coreServices.httpRouter, /* highlight-add-next-line */ permissions: coreServices.permissions, + /* highlight-add-next-line */ + permissionIntegrations: coreServices.permissionIntegrations, }, /* highlight-remove-next-line */ async init({ logger, httpAuth, httpRouter }) { /* highlight-add-next-line */ - async init({ logger, httpAuth, httpRouter, permissions }) { + async init({ httpAuth, logger, httpRouter, permissions, permissionIntegrations }) { + /* highlight-add-next-line */ + permissionIntegrations.addPermissions([todoListCreatePermission]); + httpRouter.use( await createRouter({ logger, diff --git a/docs/permissions/plugin-authors/03-adding-a-resource-permission-check.md b/docs/permissions/plugin-authors/03-adding-a-resource-permission-check.md index 3f5a9777b3..f33ae583bd 100644 --- a/docs/permissions/plugin-authors/03-adding-a-resource-permission-check.md +++ b/docs/permissions/plugin-authors/03-adding-a-resource-permission-check.md @@ -47,9 +47,9 @@ Notice that unlike `todoListCreatePermission`, the `todoListUpdatePermission` pe ## Setting up authorization for the update permission -To start, let's edit `plugins/todo-list-backend/src/service/router.ts` in the same manner as we did in the previous section: +To start, let's edit `plugins/todo-list-backend/src/plugin.ts` to add the new permission to our plugin: -```ts title="plugins/todo-list-backend/src/service/router.ts" +```ts title="plugins/todo-list-backend/src/plugin.ts" /* highlight-remove-next-line */ import { todoListCreatePermission } from '@internal/plugin-todo-list-common'; /* highlight-add-start */ @@ -61,14 +61,29 @@ import { // ... -const permissionIntegrationRouter = createPermissionIntegrationRouter({ - /* highlight-remove-next-line */ - permissions: [todoListCreatePermission], - /* highlight-add-next-line */ - permissions: [todoListCreatePermission, todoListUpdatePermission], -}); +/* highlight-remove-next-line */ +permissionIntegrations.addPermissions([todoListCreatePermission]); +/* highlight-add-start */ +permissionIntegrations.addPermissions([ + todoListCreatePermission, + todoListUpdatePermission, +]); +/* highlight-add-end */ // ... +``` + +Then let's edit `plugins/todo-list-backend/src/service/router.ts` in the same manner as we did in the previous section: + +```ts title="plugins/todo-list-backend/src/service/router.ts" +/* highlight-remove-next-line */ +import { todoListCreatePermission } from '@internal/plugin-todo-list-common'; +/* highlight-add-start */ +import { + todoListCreatePermission, + todoListUpdatePermission, +} from '@internal/plugin-todo-list-common'; +/* highlight-add-end */ router.put('/todos', async (req, res) => { /* highlight-add-start */ @@ -155,49 +170,50 @@ Specifically, the `apply` function is used to understand whether the passed reso Let's skip the `toQuery` function for now, we'll come back to that in the next section. -Now, let's create the new endpoint by editing `plugins/todo-list-backend/src/service/router.ts`. This uses the `createPermissionIntegrationRouter` helper to add the APIs needed by the permission framework to your plugin. You'll need to supply: +Now, let's add the new resource type to the permissions system via the +`PermissionIntegrationsService`. You'll need to supply: - `getResources`: a function that accepts an array of `resourceRefs` in the same format you expect to be passed to `authorize`, and returns an array of the corresponding resources. - `resourceType`: the same value used in the permission rule above. - `permissions`: the list of permissions that your plugin accepts. - `rules`: an array of all the permission rules you want to support in conditional decisions. -```ts title="plugins/todo-list-backend/src/service/router.ts" +```ts title="plugins/todo-list-backend/src/plugin.ts" // ... +import { + coreServices, + createBackendPlugin, +} from '@backstage/backend-plugin-api'; +import { createRouter } from './service/router'; import { /* highlight-add-next-line */ TODO_LIST_RESOURCE_TYPE, todoListCreatePermission, todoListUpdatePermission, } from '@internal/plugin-todo-list-common'; -/* highlight-remove-next-line */ -import { add, getAll, update } from './todos'; /* highlight-add-start */ -import { add, getAll, getTodo, update } from './todos'; +import { getTodo } from './todos'; import { rules } from './rules'; /* highlight-add-end */ -export async function createRouter( - options: RouterOptions, -): Promise { - const { logger, identity, permissions } = options; +// ... - const permissionIntegrationRouter = createPermissionIntegrationRouter({ - permissions: [todoListCreatePermission, todoListUpdatePermission], - /* highlight-add-start */ - getResources: async resourceRefs => { - return resourceRefs.map(getTodo); - }, - resourceType: TODO_LIST_RESOURCE_TYPE, - rules: Object.values(rules), - /* highlight-add-end */ - }); - - const router = Router(); - router.use(express.json()); - - // ... -} +/* highlight-remove-start */ +permissionIntegrations.addPermissions([ + todoListCreatePermission, + todoListUpdatePermission, +]); +/* highlight-remove-end */ +/* highlight-add-start */ +permissionIntegrations.addResourceType({ + resourceType: TODO_LIST_RESOURCE_TYPE, + permissions: [todoListCreatePermission, todoListUpdatePermission], + rules: Object.values(rules), + getResources: async resourceRefs => { + return Promise.all(resourceRefs.map(getTodo)); + }, +}); +/* highlight-add-end */ ``` ## Provide utilities for policy authors diff --git a/docs/permissions/plugin-authors/04-authorizing-access-to-paginated-data.md b/docs/permissions/plugin-authors/04-authorizing-access-to-paginated-data.md index 883884b6e5..d85d82510d 100644 --- a/docs/permissions/plugin-authors/04-authorizing-access-to-paginated-data.md +++ b/docs/permissions/plugin-authors/04-authorizing-access-to-paginated-data.md @@ -84,14 +84,41 @@ export const todoListPermissions = [ ## Using conditional policy decisions +As usual, we'll start by updating the permission integration to include the new permission: + +```ts title="plugins/todo-list-backend/src/plugin.ts" +import { + TODO_LIST_RESOURCE_TYPE, + todoListCreatePermission, + todoListUpdatePermission, + /* highlight-add-next-line */ + todoListReadPermission, +} from '@internal/plugin-todo-list-common'; + +// ... + +permissionIntegrations.addResourceType({ + resourceType: TODO_LIST_RESOURCE_TYPE, + /* highlight-remove-next-line */ + permissions: [todoListCreatePermission, todoListUpdatePermission], + /* highlight-add-next-line */ + permissions: [ + todoListCreatePermission, + todoListUpdatePermission, + todoListReadPermission, + ], + rules: Object.values(rules), + getResources: async resourceRefs => { + return Promise.all(resourceRefs.map(getTodo)); + }, +}); +``` + So far we've only used the `PermissionsService.authorize` method, which will evaluate conditional decisions before returning a result. In this step, we want to evaluate conditional decisions within our plugin, so we'll use `PermissionsService.authorizeConditional` instead. ```ts title="plugins/todo-list-backend/src/service/router.ts" -/* highlight-remove-next-line */ -import { createPermissionIntegrationRouter } from '@backstage/plugin-permission-node'; /* highlight-add-start */ import { - createPermissionIntegrationRouter, createConditionTransformer, ConditionTransformer, } from '@backstage/plugin-permission-node'; @@ -101,7 +128,6 @@ import { add, getAll, getTodo, update } from './todos'; /* highlight-add-next-line */ import { add, getAll, getTodo, TodoFilter, update } from './todos'; import { - TODO_LIST_RESOURCE_TYPE, todoListCreatePermission, todoListUpdatePermission, /* highlight-add-next-line */ @@ -110,20 +136,6 @@ import { // ... -const permissionIntegrationRouter = createPermissionIntegrationRouter({ - /* highlight-remove-next-line */ - permissions: [todoListCreatePermission, todoListUpdatePermission], - /* highlight-add-next-line */ - permissions: [todoListCreatePermission, todoListUpdatePermission, todoListReadPermission], - getResources: async resourceRefs => { - return resourceRefs.map(getTodo); - }, - resourceType: TODO_LIST_RESOURCE_TYPE, - rules: Object.values(rules), -}); - -// ... - /* highlight-add-next-line */ const transformConditions: ConditionTransformer = createConditionTransformer(Object.values(rules));