From 2eb7b4f1d61b66b346c7b4c71c84cb8c42b28b66 Mon Sep 17 00:00:00 2001 From: Peter Macdonald Date: Sat, 13 Apr 2024 17:39:39 +0200 Subject: [PATCH 01/12] updating getting-started permissions docs to new backend Signed-off-by: Peter Macdonald --- docs/permissions/getting-started.md | 146 ++++++++++++---------------- 1 file changed, 60 insertions(+), 86 deletions(-) diff --git a/docs/permissions/getting-started.md b/docs/permissions/getting-started.md index e241f0e38e..de092dce01 100644 --- a/docs/permissions/getting-started.md +++ b/docs/permissions/getting-started.md @@ -20,20 +20,12 @@ The permissions framework depends on a few other Backstage systems, which must b The permissions framework itself is new to Backstage and still evolving quickly. To ensure your version of Backstage has all the latest permission-related functionality, it’s important to upgrade to the latest version. The [Backstage upgrade helper](https://backstage.github.io/upgrade-helper/) is a great tool to help ensure that you’ve made all the necessary changes during the upgrade! -### Enable service-to-service authentication - -Service-to-service authentication allows Backstage backend code to verify that a given request originates from elsewhere in the Backstage backend. This is useful for tasks like collation of catalog entities in the search index. This type of request shouldn’t be permissioned, so it’s important to configure this feature before trying to use the permissions framework. - -To set up service-to-service authentication, follow the [service-to-service authentication docs](../auth/service-to-service-auth.md). - ### Supply an identity resolver to populate group membership on sign in **Note**: If you are working off of an existing Backstage instance, you likely already have some form of an identity resolver set up. Like many other parts of Backstage, the permissions framework relies on information about group membership. This simplifies authoring policies through the use of groups, rather than requiring each user to be listed in the configuration. Group membership is also often useful for conditional permissions, for example allowing permissions to act on an entity to be granted when a user is a member of a group that owns that entity. -[The IdentityResolver docs](../auth/identity-resolver.md) describe the process for resolving group membership on sign in. - ## Optionally add cookie-based authentication Asset requests initiated by the browser will not include a token in the `Authorization` header. If these requests check authorization through the permission framework, as done in plugins like TechDocs, then you'll need to set up cookie-based authentication. Refer to the ["Authenticate API requests"](https://github.com/backstage/backstage/blob/master/contrib/docs/tutorials/authenticate-api-requests.md) tutorial for a demonstration on how to implement this behavior. @@ -44,68 +36,32 @@ Asset requests initiated by the browser will not include a token in the `Authori The permissions framework uses a new `permission-backend` plugin to accept authorization requests from other plugins across your Backstage instance. The Backstage backend does not include this permission backend by default, so you will need to add it: -1. Add `@backstage/plugin-permission-backend` as a dependency of your Backstage backend: +1. Add `@backstage/plugin-permission-backend` and `@backstage/plugin-permission-backend-module-allow-all-policy` to your backend dependencies, this will add the permission backend and a policy that allows all permissions: ```bash # From your Backstage root directory yarn --cwd packages/backend add @backstage/plugin-permission-backend ``` -2. Add the following to a new file, `packages/backend/src/plugins/permission.ts`. This adds the permission-backend router, and configures it with a policy which allows everything. - - ```typescript title="packages/backend/src/plugins/permission.ts" - import { createRouter } from '@backstage/plugin-permission-backend'; - import { - AuthorizeResult, - PolicyDecision, - } from '@backstage/plugin-permission-common'; - import { PermissionPolicy } from '@backstage/plugin-permission-node'; - import { Router } from 'express'; - import { PluginEnvironment } from '../types'; - - class TestPermissionPolicy implements PermissionPolicy { - async handle(): Promise { - return { result: AuthorizeResult.ALLOW }; - } - } - - export default async function createPlugin( - env: PluginEnvironment, - ): Promise { - return await createRouter({ - config: env.config, - logger: env.logger, - discovery: env.discovery, - policy: new TestPermissionPolicy(), - identity: env.identity, - }); - } + ```bash + # From your Backstage root directory + yarn --cwd packages/backend add @backstage/plugin-permission-backend-module-allow-all-policy ``` -3. Wire up the permission policy in `packages/backend/src/index.ts`. [The index in the example backend](https://github.com/backstage/backstage/blob/master/packages/backend/src/index.ts) shows how to do this. You’ll need to import the module from the previous step, create a plugin environment, and add the router to the express app: +2. Add the following to `packages/backend/src/index.ts`. This adds the permission-backend router, and configures it with a policy which allows everything. - ```ts title="packages/backend/src/index.ts" - import proxy from './plugins/proxy'; - import techdocs from './plugins/techdocs'; - import search from './plugins/search'; + ```typescript title="packages/backend/src/index.ts" + import { createBackend } from '@backstage/backend-defaults'; + const backend = createBackend(); + // ... /* highlight-add-next-line */ - import permission from './plugins/permission'; - - async function main() { - const techdocsEnv = useHotMemoize(module, () => createEnv('techdocs')); - const searchEnv = useHotMemoize(module, () => createEnv('search')); - const appEnv = useHotMemoize(module, () => createEnv('app')); - /* highlight-add-next-line */ - const permissionEnv = useHotMemoize(module, () => createEnv('permission')); - // .. - - apiRouter.use('/techdocs', await techdocs(techdocsEnv)); - apiRouter.use('/proxy', await proxy(proxyEnv)); - apiRouter.use('/search', await search(searchEnv)); - /* highlight-add-next-line */ - apiRouter.use('/permission', await permission(permissionEnv)); - // .. - } + backend.add(import('@backstage/plugin-permission-backend/alpha')); + /* highlight-add-next-line */ + backend.add( + import('@backstage/plugin-permission-backend-module-allow-all-policy'), + ); + // ... + backend.start(); ``` ### 2. Enable and test the permissions system @@ -119,43 +75,61 @@ Now that the permission backend is running, it’s time to enable the permission enabled: true ``` -2. Update the PermissionPolicy in `packages/backend/src/plugins/permission.ts` to disable a permission that’s easy for us to test. This policy rejects any attempt to delete a catalog entity: +2. Its now all wired up and working, great! But perhaps we don't want to simply allow everything, lets try and create our own policy, to do this you can create a new folder in `packages/backend/src` called `permissions` and create a new file called `policy.ts`, in that file we can add the following to create a policy that denies deleting entities from the catalog: - ```ts title="packages/backend/src/plugins/permission.ts" - import { createRouter } from '@backstage/plugin-permission-backend'; + ```ts title="packages/backend/src/permissions/policy.ts" import { AuthorizeResult, PolicyDecision, } from '@backstage/plugin-permission-common'; - /* highlight-remove-next-line */ - import { PermissionPolicy } from '@backstage/plugin-permission-node'; - /* highlight-add-start */ import { PermissionPolicy, PolicyQuery, } from '@backstage/plugin-permission-node'; - /* highlight-add-end */ - import { Router } from 'express'; - import { PluginEnvironment } from '../types'; - - class TestPermissionPolicy implements PermissionPolicy { - /* highlight-remove-next-line */ - async handle(): Promise { - /* highlight-add-start */ - async handle(request: PolicyQuery): Promise { - if (request.permission.name === 'catalog.entity.delete') { - return { - result: AuthorizeResult.DENY, - }; - } - /* highlight-add-end */ - - return { result: AuthorizeResult.ALLOW }; - } - } ``` -3. Now that you’ve made this change, you should find that the unregister entity menu option on the catalog entity page is disabled. +export class DenyCatalogDeletePolicy implements PermissionPolicy { +async handle(request: PolicyQuery): Promise { +if (request.permission.name === 'catalog.entity.delete') { +return { +result: AuthorizeResult.DENY, +}; +} + + return { result: AuthorizeResult.ALLOW }; + } + +} + +```` +3. We then need to use the permissions backend policy extension point to register our policy, to do this we can add the following to `packages/backend/src/index.ts`: + +```ts title="packages/backend/src/index.ts" +import { createBackend } from '@backstage/backend-defaults'; +import { DenyCatalogDeletePolicy } from './permissions/policy'; +const backend = createBackend(); +// ... +backend.add(import('@backstage/plugin-permission-backend/alpha')); +/* highlight-remove-next-line */ +backend.add(import('@backstage/plugin-permission-backend-module-allow-all-policy')); + /* highlight-add-next-line */ + backend.add(createBackendModule({ + pluginId: 'permission', + moduleId: 'deny-catalog-delete-policy', + register(reg){ + reg.registerInit({ + deps: { policy: policyExtensionPoint }, + async init({ policy }) { + policy.setPolicy(new DenyCatalogDeletePolicy()); + } + }) + } +})); +// ... +backend.start(); +```` + +4. Now that you’ve made this change, you should find that the unregister entity menu option on the catalog entity page is disabled. ![Entity detail page showing disabled unregister entity context menu entry](../assets/permissions/disabled-unregister-entity.png) From 7246fba8ec04caf0bd693773cce2a16bb12416fb Mon Sep 17 00:00:00 2001 From: Peter Macdonald Date: Mon, 15 Apr 2024 15:18:17 +0200 Subject: [PATCH 02/12] removes old youtube video Signed-off-by: Peter Macdonald --- docs/permissions/getting-started.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/docs/permissions/getting-started.md b/docs/permissions/getting-started.md index de092dce01..bd4294caad 100644 --- a/docs/permissions/getting-started.md +++ b/docs/permissions/getting-started.md @@ -4,12 +4,6 @@ title: Getting Started description: How to get started with the permission framework as an integrator --- -If you prefer to watch a video instead, you can start with this video introduction: - - - -> Note: This video was recorded in the January 2022 Contributors Session using `@backstage/create-app@0.4.14`. Some aspects of the demo may have changed in later releases. - Backstage integrators control permissions by writing a policy. In general terms, a policy is simply an async function which receives a request to authorize a specific action for a user and (optional) resource, and returns a decision on whether to authorize that permission. Integrators can implement their own policies from scratch, or adopt reusable policies written by others. ## Prerequisites From cf3ec13839638d865fbfc186648fe207fc006c03 Mon Sep 17 00:00:00 2001 From: Peter Macdonald Date: Mon, 15 Apr 2024 15:23:13 +0200 Subject: [PATCH 03/12] weird formatting fix Signed-off-by: Peter Macdonald --- docs/permissions/getting-started.md | 120 ++++++++++++++-------------- 1 file changed, 61 insertions(+), 59 deletions(-) diff --git a/docs/permissions/getting-started.md b/docs/permissions/getting-started.md index bd4294caad..ef2194a91c 100644 --- a/docs/permissions/getting-started.md +++ b/docs/permissions/getting-started.md @@ -32,31 +32,31 @@ The permissions framework uses a new `permission-backend` plugin to accept autho 1. Add `@backstage/plugin-permission-backend` and `@backstage/plugin-permission-backend-module-allow-all-policy` to your backend dependencies, this will add the permission backend and a policy that allows all permissions: - ```bash - # From your Backstage root directory - yarn --cwd packages/backend add @backstage/plugin-permission-backend - ``` +```bash +# From your Backstage root directory +yarn --cwd packages/backend add @backstage/plugin-permission-backend +``` - ```bash - # From your Backstage root directory - yarn --cwd packages/backend add @backstage/plugin-permission-backend-module-allow-all-policy - ``` +```bash +# From your Backstage root directory +yarn --cwd packages/backend add @backstage/plugin-permission-backend-module-allow-all-policy +``` 2. Add the following to `packages/backend/src/index.ts`. This adds the permission-backend router, and configures it with a policy which allows everything. - ```typescript title="packages/backend/src/index.ts" - import { createBackend } from '@backstage/backend-defaults'; - const backend = createBackend(); - // ... - /* highlight-add-next-line */ - backend.add(import('@backstage/plugin-permission-backend/alpha')); - /* highlight-add-next-line */ - backend.add( - import('@backstage/plugin-permission-backend-module-allow-all-policy'), - ); - // ... - backend.start(); - ``` +```typescript title="packages/backend/src/index.ts" +import { createBackend } from '@backstage/backend-defaults'; +const backend = createBackend(); +// ... +/* highlight-add-next-line */ +backend.add(import('@backstage/plugin-permission-backend/alpha')); +/* highlight-add-next-line */ +backend.add( + import('@backstage/plugin-permission-backend-module-allow-all-policy'), +); +// ... +backend.start(); +``` ### 2. Enable and test the permissions system @@ -64,38 +64,36 @@ Now that the permission backend is running, it’s time to enable the permission 1. Set the property `permission.enabled` to `true` in `app-config.yaml`. - ```yaml title="app-config.yaml" - permission: - enabled: true - ``` +```yaml title="app-config.yaml" +permission: + enabled: true +``` 2. Its now all wired up and working, great! But perhaps we don't want to simply allow everything, lets try and create our own policy, to do this you can create a new folder in `packages/backend/src` called `permissions` and create a new file called `policy.ts`, in that file we can add the following to create a policy that denies deleting entities from the catalog: - ```ts title="packages/backend/src/permissions/policy.ts" - import { - AuthorizeResult, - PolicyDecision, - } from '@backstage/plugin-permission-common'; - import { - PermissionPolicy, - PolicyQuery, - } from '@backstage/plugin-permission-node'; - ``` +```ts title="packages/backend/src/permissions/policy.ts" +import { + AuthorizeResult, + PolicyDecision, +} from '@backstage/plugin-permission-common'; +import { + PermissionPolicy, + PolicyQuery, +} from '@backstage/plugin-permission-node'; export class DenyCatalogDeletePolicy implements PermissionPolicy { -async handle(request: PolicyQuery): Promise { -if (request.permission.name === 'catalog.entity.delete') { -return { -result: AuthorizeResult.DENY, -}; -} - - return { result: AuthorizeResult.ALLOW }; + async handle(request: PolicyQuery): Promise { + if (request.permission.name === 'catalog.entity.delete') { + return { + result: AuthorizeResult.DENY, + }; } + return { result: AuthorizeResult.ALLOW }; + } } +``` -```` 3. We then need to use the permissions backend policy extension point to register our policy, to do this we can add the following to `packages/backend/src/index.ts`: ```ts title="packages/backend/src/index.ts" @@ -105,23 +103,27 @@ const backend = createBackend(); // ... backend.add(import('@backstage/plugin-permission-backend/alpha')); /* highlight-remove-next-line */ -backend.add(import('@backstage/plugin-permission-backend-module-allow-all-policy')); - /* highlight-add-next-line */ - backend.add(createBackendModule({ - pluginId: 'permission', - moduleId: 'deny-catalog-delete-policy', - register(reg){ - reg.registerInit({ - deps: { policy: policyExtensionPoint }, - async init({ policy }) { - policy.setPolicy(new DenyCatalogDeletePolicy()); - } - }) - } -})); +backend.add( + import('@backstage/plugin-permission-backend-module-allow-all-policy'), +); +/* highlight-add-next-line */ +backend.add( + createBackendModule({ + pluginId: 'permission', + moduleId: 'deny-catalog-delete-policy', + register(reg) { + reg.registerInit({ + deps: { policy: policyExtensionPoint }, + async init({ policy }) { + policy.setPolicy(new DenyCatalogDeletePolicy()); + }, + }); + }, + }), +); // ... backend.start(); -```` +``` 4. Now that you’ve made this change, you should find that the unregister entity menu option on the catalog entity page is disabled. From 18f565273a1eb0d30e1b6fc0735ad5df985e2016 Mon Sep 17 00:00:00 2001 From: Peter Macdonald Date: Tue, 16 Apr 2024 20:15:35 +0200 Subject: [PATCH 04/12] fixing highlight line Signed-off-by: Peter Macdonald --- docs/permissions/getting-started.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/permissions/getting-started.md b/docs/permissions/getting-started.md index ef2194a91c..5f8db5556e 100644 --- a/docs/permissions/getting-started.md +++ b/docs/permissions/getting-started.md @@ -106,7 +106,7 @@ backend.add(import('@backstage/plugin-permission-backend/alpha')); backend.add( import('@backstage/plugin-permission-backend-module-allow-all-policy'), ); -/* highlight-add-next-line */ +/* highlight-add-start */ backend.add( createBackendModule({ pluginId: 'permission', @@ -121,6 +121,7 @@ backend.add( }, }), ); +/* highlight-add-end */ // ... backend.start(); ``` From 52cbfae035eeb7e99a7c9aa7d3fbdbc1fb56a68e Mon Sep 17 00:00:00 2001 From: Peter Macdonald Date: Thu, 25 Apr 2024 15:01:05 +0200 Subject: [PATCH 05/12] update based on feedback Signed-off-by: Peter Macdonald --- docs/permissions/getting-started.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/permissions/getting-started.md b/docs/permissions/getting-started.md index 5f8db5556e..9e71a6f924 100644 --- a/docs/permissions/getting-started.md +++ b/docs/permissions/getting-started.md @@ -20,6 +20,8 @@ The permissions framework itself is new to Backstage and still evolving quickly. Like many other parts of Backstage, the permissions framework relies on information about group membership. This simplifies authoring policies through the use of groups, rather than requiring each user to be listed in the configuration. Group membership is also often useful for conditional permissions, for example allowing permissions to act on an entity to be granted when a user is a member of a group that owns that entity. +[The IdentityResolver docs](../auth/identity-resolver.md) describe the process for resolving group membership on sign in. + ## Optionally add cookie-based authentication Asset requests initiated by the browser will not include a token in the `Authorization` header. If these requests check authorization through the permission framework, as done in plugins like TechDocs, then you'll need to set up cookie-based authentication. Refer to the ["Authenticate API requests"](https://github.com/backstage/backstage/blob/master/contrib/docs/tutorials/authenticate-api-requests.md) tutorial for a demonstration on how to implement this behavior. @@ -34,12 +36,7 @@ The permissions framework uses a new `permission-backend` plugin to accept autho ```bash # From your Backstage root directory -yarn --cwd packages/backend add @backstage/plugin-permission-backend -``` - -```bash -# From your Backstage root directory -yarn --cwd packages/backend add @backstage/plugin-permission-backend-module-allow-all-policy +yarn --cwd packages/backend add @backstage/plugin-permission-backend @backstage/plugin-permission-backend-module-allow-all-policy ``` 2. Add the following to `packages/backend/src/index.ts`. This adds the permission-backend router, and configures it with a policy which allows everything. From 30a6ca16178c328e66a777f00ed37820bc4a0af7 Mon Sep 17 00:00:00 2001 From: Peter Macdonald Date: Thu, 25 Apr 2024 15:03:10 +0200 Subject: [PATCH 06/12] cuts out writing policies and save it for next section Signed-off-by: Peter Macdonald --- docs/permissions/getting-started.md | 63 +---------------------------- 1 file changed, 1 insertion(+), 62 deletions(-) diff --git a/docs/permissions/getting-started.md b/docs/permissions/getting-started.md index 9e71a6f924..7e828bac56 100644 --- a/docs/permissions/getting-started.md +++ b/docs/permissions/getting-started.md @@ -66,65 +66,4 @@ permission: enabled: true ``` -2. Its now all wired up and working, great! But perhaps we don't want to simply allow everything, lets try and create our own policy, to do this you can create a new folder in `packages/backend/src` called `permissions` and create a new file called `policy.ts`, in that file we can add the following to create a policy that denies deleting entities from the catalog: - -```ts title="packages/backend/src/permissions/policy.ts" -import { - AuthorizeResult, - PolicyDecision, -} from '@backstage/plugin-permission-common'; -import { - PermissionPolicy, - PolicyQuery, -} from '@backstage/plugin-permission-node'; - -export class DenyCatalogDeletePolicy implements PermissionPolicy { - async handle(request: PolicyQuery): Promise { - if (request.permission.name === 'catalog.entity.delete') { - return { - result: AuthorizeResult.DENY, - }; - } - - return { result: AuthorizeResult.ALLOW }; - } -} -``` - -3. We then need to use the permissions backend policy extension point to register our policy, to do this we can add the following to `packages/backend/src/index.ts`: - -```ts title="packages/backend/src/index.ts" -import { createBackend } from '@backstage/backend-defaults'; -import { DenyCatalogDeletePolicy } from './permissions/policy'; -const backend = createBackend(); -// ... -backend.add(import('@backstage/plugin-permission-backend/alpha')); -/* highlight-remove-next-line */ -backend.add( - import('@backstage/plugin-permission-backend-module-allow-all-policy'), -); -/* highlight-add-start */ -backend.add( - createBackendModule({ - pluginId: 'permission', - moduleId: 'deny-catalog-delete-policy', - register(reg) { - reg.registerInit({ - deps: { policy: policyExtensionPoint }, - async init({ policy }) { - policy.setPolicy(new DenyCatalogDeletePolicy()); - }, - }); - }, - }), -); -/* highlight-add-end */ -// ... -backend.start(); -``` - -4. Now that you’ve made this change, you should find that the unregister entity menu option on the catalog entity page is disabled. - -![Entity detail page showing disabled unregister entity context menu entry](../assets/permissions/disabled-unregister-entity.png) - -Now that the framework is fully configured, you can craft a permission policy that works best for your organization by utilizing a provided authorization method or by [writing your own policy](./writing-a-policy.md)! +Congratulations! Now that the framework is configured, you can craft a permission policy that works best for your organization by utilizing a provided authorization method or by [writing your own policy](./writing-a-policy.md)! From 8329a2c639ec81ee5724c205c3122024ea151b24 Mon Sep 17 00:00:00 2001 From: Peter Macdonald Date: Thu, 25 Apr 2024 15:05:12 +0200 Subject: [PATCH 07/12] removes cookie section, not needed Signed-off-by: Peter Macdonald --- docs/permissions/getting-started.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/permissions/getting-started.md b/docs/permissions/getting-started.md index 7e828bac56..a33df6cfc2 100644 --- a/docs/permissions/getting-started.md +++ b/docs/permissions/getting-started.md @@ -22,10 +22,6 @@ Like many other parts of Backstage, the permissions framework relies on informat [The IdentityResolver docs](../auth/identity-resolver.md) describe the process for resolving group membership on sign in. -## Optionally add cookie-based authentication - -Asset requests initiated by the browser will not include a token in the `Authorization` header. If these requests check authorization through the permission framework, as done in plugins like TechDocs, then you'll need to set up cookie-based authentication. Refer to the ["Authenticate API requests"](https://github.com/backstage/backstage/blob/master/contrib/docs/tutorials/authenticate-api-requests.md) tutorial for a demonstration on how to implement this behavior. - ## Integrating the permission framework with your Backstage instance ### 1. Set up the permission backend From dbc37cc75e1c63a09559ef74602a30853b9f41f8 Mon Sep 17 00:00:00 2001 From: Peter Macdonald Date: Thu, 25 Apr 2024 15:06:45 +0200 Subject: [PATCH 08/12] removes the evolving quickly part Signed-off-by: Peter Macdonald --- docs/permissions/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/permissions/getting-started.md b/docs/permissions/getting-started.md index a33df6cfc2..33e2f95041 100644 --- a/docs/permissions/getting-started.md +++ b/docs/permissions/getting-started.md @@ -12,7 +12,7 @@ The permissions framework depends on a few other Backstage systems, which must b ### Upgrade to the latest version of Backstage -The permissions framework itself is new to Backstage and still evolving quickly. To ensure your version of Backstage has all the latest permission-related functionality, it’s important to upgrade to the latest version. The [Backstage upgrade helper](https://backstage.github.io/upgrade-helper/) is a great tool to help ensure that you’ve made all the necessary changes during the upgrade! +To ensure your version of Backstage has all the latest permission-related functionality, it’s important to upgrade to the latest version. The [Backstage upgrade helper](https://backstage.github.io/upgrade-helper/) is a great tool to help ensure that you’ve made all the necessary changes during the upgrade! ### Supply an identity resolver to populate group membership on sign in From 244b57722ac91d28562f202dc1dabcbeb21308fc Mon Sep 17 00:00:00 2001 From: Peter Macdonald Date: Thu, 2 May 2024 16:35:04 +0200 Subject: [PATCH 09/12] added info block as suggested to inform permissions are setup when using create-app, reworded section Signed-off-by: Peter Macdonald --- docs/permissions/getting-started.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/permissions/getting-started.md b/docs/permissions/getting-started.md index 33e2f95041..33eea296d9 100644 --- a/docs/permissions/getting-started.md +++ b/docs/permissions/getting-started.md @@ -24,9 +24,13 @@ Like many other parts of Backstage, the permissions framework relies on informat ## Integrating the permission framework with your Backstage instance +:::info +If you created your backstage app using the [@backstage/create-app](https://backstage.io/docs/getting-started/#1-create-your-backstage-app), the permission framework will already be setup including the allow-all policy! +::: + ### 1. Set up the permission backend -The permissions framework uses a new `permission-backend` plugin to accept authorization requests from other plugins across your Backstage instance. The Backstage backend does not include this permission backend by default, so you will need to add it: +The permissions framework uses the `permission-backend` plugin to accept authorization requests from other plugins across your Backstage deployment. The default `@backstage/create-app` template includes the permission backend, but if you need to make the change manually, these are the steps: 1. Add `@backstage/plugin-permission-backend` and `@backstage/plugin-permission-backend-module-allow-all-policy` to your backend dependencies, this will add the permission backend and a policy that allows all permissions: From 0445f5309a3eaaf675275b981b68fcaeb439b959 Mon Sep 17 00:00:00 2001 From: Peter Macdonald Date: Thu, 2 May 2024 19:41:59 +0200 Subject: [PATCH 10/12] removes info block Signed-off-by: Peter Macdonald --- docs/permissions/getting-started.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/permissions/getting-started.md b/docs/permissions/getting-started.md index 33eea296d9..825fefe598 100644 --- a/docs/permissions/getting-started.md +++ b/docs/permissions/getting-started.md @@ -24,10 +24,6 @@ Like many other parts of Backstage, the permissions framework relies on informat ## Integrating the permission framework with your Backstage instance -:::info -If you created your backstage app using the [@backstage/create-app](https://backstage.io/docs/getting-started/#1-create-your-backstage-app), the permission framework will already be setup including the allow-all policy! -::: - ### 1. Set up the permission backend The permissions framework uses the `permission-backend` plugin to accept authorization requests from other plugins across your Backstage deployment. The default `@backstage/create-app` template includes the permission backend, but if you need to make the change manually, these are the steps: From 989b57e9455e5c60844d03377a82e5a6763e6c3c Mon Sep 17 00:00:00 2001 From: Peter Macdonald Date: Wed, 8 May 2024 13:11:32 +0200 Subject: [PATCH 11/12] cleaning up docs Signed-off-by: Peter Macdonald --- docs/permissions/getting-started.md | 33 ++--------------------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/docs/permissions/getting-started.md b/docs/permissions/getting-started.md index 825fefe598..03290bd45e 100644 --- a/docs/permissions/getting-started.md +++ b/docs/permissions/getting-started.md @@ -22,38 +22,9 @@ Like many other parts of Backstage, the permissions framework relies on informat [The IdentityResolver docs](../auth/identity-resolver.md) describe the process for resolving group membership on sign in. -## Integrating the permission framework with your Backstage instance +## Enable and test the permissions system -### 1. Set up the permission backend - -The permissions framework uses the `permission-backend` plugin to accept authorization requests from other plugins across your Backstage deployment. The default `@backstage/create-app` template includes the permission backend, but if you need to make the change manually, these are the steps: - -1. Add `@backstage/plugin-permission-backend` and `@backstage/plugin-permission-backend-module-allow-all-policy` to your backend dependencies, this will add the permission backend and a policy that allows all permissions: - -```bash -# From your Backstage root directory -yarn --cwd packages/backend add @backstage/plugin-permission-backend @backstage/plugin-permission-backend-module-allow-all-policy -``` - -2. Add the following to `packages/backend/src/index.ts`. This adds the permission-backend router, and configures it with a policy which allows everything. - -```typescript title="packages/backend/src/index.ts" -import { createBackend } from '@backstage/backend-defaults'; -const backend = createBackend(); -// ... -/* highlight-add-next-line */ -backend.add(import('@backstage/plugin-permission-backend/alpha')); -/* highlight-add-next-line */ -backend.add( - import('@backstage/plugin-permission-backend-module-allow-all-policy'), -); -// ... -backend.start(); -``` - -### 2. Enable and test the permissions system - -Now that the permission backend is running, it’s time to enable the permissions framework and make sure it’s working properly. +All you need to do now is enable the permissions system in your Backstage instance! 1. Set the property `permission.enabled` to `true` in `app-config.yaml`. From edafab4f3e033e1396e9c38870989f5ca80b5f3f Mon Sep 17 00:00:00 2001 From: Peter Macdonald Date: Tue, 21 May 2024 19:21:14 +0200 Subject: [PATCH 12/12] keeps old docs and creates new docs with --new Signed-off-by: Peter Macdonald --- docs/permissions/getting-started--new.md | 36 ++++++ docs/permissions/getting-started.md | 146 +++++++++++++++++++++-- 2 files changed, 174 insertions(+), 8 deletions(-) create mode 100644 docs/permissions/getting-started--new.md diff --git a/docs/permissions/getting-started--new.md b/docs/permissions/getting-started--new.md new file mode 100644 index 0000000000..b6198a222f --- /dev/null +++ b/docs/permissions/getting-started--new.md @@ -0,0 +1,36 @@ +--- +id: getting-started--new +title: Getting Started +description: How to get started with the permission framework as an integrator +--- + +Backstage integrators control permissions by writing a policy. In general terms, a policy is simply an async function which receives a request to authorize a specific action for a user and (optional) resource, and returns a decision on whether to authorize that permission. Integrators can implement their own policies from scratch, or adopt reusable policies written by others. + +## Prerequisites + +The permissions framework depends on a few other Backstage systems, which must be set up before we can dive into writing a policy. + +### Upgrade to the latest version of Backstage + +To ensure your version of Backstage has all the latest permission-related functionality, it’s important to upgrade to the latest version. The [Backstage upgrade helper](https://backstage.github.io/upgrade-helper/) is a great tool to help ensure that you’ve made all the necessary changes during the upgrade! + +### Supply an identity resolver to populate group membership on sign in + +**Note**: If you are working off of an existing Backstage instance, you likely already have some form of an identity resolver set up. + +Like many other parts of Backstage, the permissions framework relies on information about group membership. This simplifies authoring policies through the use of groups, rather than requiring each user to be listed in the configuration. Group membership is also often useful for conditional permissions, for example allowing permissions to act on an entity to be granted when a user is a member of a group that owns that entity. + +[The IdentityResolver docs](../auth/identity-resolver.md) describe the process for resolving group membership on sign in. + +## Enable and test the permissions system + +All you need to do now is enable the permissions system in your Backstage instance! + +1. Set the property `permission.enabled` to `true` in `app-config.yaml`. + +```yaml title="app-config.yaml" +permission: + enabled: true +``` + +Congratulations! Now that the framework is configured, you can craft a permission policy that works best for your organization by utilizing a provided authorization method or by [writing your own policy](./writing-a-policy.md)! diff --git a/docs/permissions/getting-started.md b/docs/permissions/getting-started.md index 03290bd45e..4dabf3fcbd 100644 --- a/docs/permissions/getting-started.md +++ b/docs/permissions/getting-started.md @@ -4,6 +4,16 @@ title: Getting Started description: How to get started with the permission framework as an integrator --- +If you prefer to watch a video instead, you can start with this video introduction: + + + +:::note Note + +This video was recorded in the January 2022 Contributors Session using `@backstage/create-app@0.4.14`. Some aspects of the demo may have changed in later releases. + +::: + Backstage integrators control permissions by writing a policy. In general terms, a policy is simply an async function which receives a request to authorize a specific action for a user and (optional) resource, and returns a decision on whether to authorize that permission. Integrators can implement their own policies from scratch, or adopt reusable policies written by others. ## Prerequisites @@ -12,7 +22,13 @@ The permissions framework depends on a few other Backstage systems, which must b ### Upgrade to the latest version of Backstage -To ensure your version of Backstage has all the latest permission-related functionality, it’s important to upgrade to the latest version. The [Backstage upgrade helper](https://backstage.github.io/upgrade-helper/) is a great tool to help ensure that you’ve made all the necessary changes during the upgrade! +The permissions framework itself is new to Backstage and still evolving quickly. To ensure your version of Backstage has all the latest permission-related functionality, it’s important to upgrade to the latest version. The [Backstage upgrade helper](https://backstage.github.io/upgrade-helper/) is a great tool to help ensure that you’ve made all the necessary changes during the upgrade! + +### Enable service-to-service authentication + +Service-to-service authentication allows Backstage backend code to verify that a given request originates from elsewhere in the Backstage backend. This is useful for tasks like collation of catalog entities in the search index. This type of request shouldn’t be permissioned, so it’s important to configure this feature before trying to use the permissions framework. + +To set up service-to-service authentication, follow the [service-to-service authentication docs](../auth/service-to-service-auth.md). ### Supply an identity resolver to populate group membership on sign in @@ -22,15 +38,129 @@ Like many other parts of Backstage, the permissions framework relies on informat [The IdentityResolver docs](../auth/identity-resolver.md) describe the process for resolving group membership on sign in. -## Enable and test the permissions system +## Optionally add cookie-based authentication -All you need to do now is enable the permissions system in your Backstage instance! +Asset requests initiated by the browser will not include a token in the `Authorization` header. If these requests check authorization through the permission framework, as done in plugins like TechDocs, then you'll need to set up cookie-based authentication. Refer to the ["Authenticate API requests"](https://github.com/backstage/backstage/blob/master/contrib/docs/tutorials/authenticate-api-requests.md) tutorial for a demonstration on how to implement this behavior. + +## Integrating the permission framework with your Backstage instance + +### 1. Set up the permission backend + +The permissions framework uses a new `permission-backend` plugin to accept authorization requests from other plugins across your Backstage instance. The Backstage backend does not include this permission backend by default, so you will need to add it: + +1. Add `@backstage/plugin-permission-backend` as a dependency of your Backstage backend: + + ```bash + # From your Backstage root directory + yarn --cwd packages/backend add @backstage/plugin-permission-backend + ``` + +2. Add the following to a new file, `packages/backend/src/plugins/permission.ts`. This adds the permission-backend router, and configures it with a policy which allows everything. + + ```typescript title="packages/backend/src/plugins/permission.ts" + import { createRouter } from '@backstage/plugin-permission-backend'; + import { + AuthorizeResult, + PolicyDecision, + } from '@backstage/plugin-permission-common'; + import { PermissionPolicy } from '@backstage/plugin-permission-node'; + import { Router } from 'express'; + import { PluginEnvironment } from '../types'; + + class TestPermissionPolicy implements PermissionPolicy { + async handle(): Promise { + return { result: AuthorizeResult.ALLOW }; + } + } + + export default async function createPlugin( + env: PluginEnvironment, + ): Promise { + return await createRouter({ + config: env.config, + logger: env.logger, + discovery: env.discovery, + policy: new TestPermissionPolicy(), + identity: env.identity, + }); + } + ``` + +3. Wire up the permission policy in `packages/backend/src/index.ts`. [The index in the example backend](https://github.com/backstage/backstage/blob/master/packages/backend/src/index.ts) shows how to do this. You’ll need to import the module from the previous step, create a plugin environment, and add the router to the express app: + + ```ts title="packages/backend/src/index.ts" + import proxy from './plugins/proxy'; + import techdocs from './plugins/techdocs'; + import search from './plugins/search'; + /* highlight-add-next-line */ + import permission from './plugins/permission'; + + async function main() { + const techdocsEnv = useHotMemoize(module, () => createEnv('techdocs')); + const searchEnv = useHotMemoize(module, () => createEnv('search')); + const appEnv = useHotMemoize(module, () => createEnv('app')); + /* highlight-add-next-line */ + const permissionEnv = useHotMemoize(module, () => createEnv('permission')); + // .. + + apiRouter.use('/techdocs', await techdocs(techdocsEnv)); + apiRouter.use('/proxy', await proxy(proxyEnv)); + apiRouter.use('/search', await search(searchEnv)); + /* highlight-add-next-line */ + apiRouter.use('/permission', await permission(permissionEnv)); + // .. + } + ``` + +### 2. Enable and test the permissions system + +Now that the permission backend is running, it’s time to enable the permissions framework and make sure it’s working properly. 1. Set the property `permission.enabled` to `true` in `app-config.yaml`. -```yaml title="app-config.yaml" -permission: - enabled: true -``` + ```yaml title="app-config.yaml" + permission: + enabled: true + ``` -Congratulations! Now that the framework is configured, you can craft a permission policy that works best for your organization by utilizing a provided authorization method or by [writing your own policy](./writing-a-policy.md)! +2. Update the PermissionPolicy in `packages/backend/src/plugins/permission.ts` to disable a permission that’s easy for us to test. This policy rejects any attempt to delete a catalog entity: + + ```ts title="packages/backend/src/plugins/permission.ts" + import { createRouter } from '@backstage/plugin-permission-backend'; + import { + AuthorizeResult, + PolicyDecision, + } from '@backstage/plugin-permission-common'; + /* highlight-remove-next-line */ + import { PermissionPolicy } from '@backstage/plugin-permission-node'; + /* highlight-add-start */ + import { + PermissionPolicy, + PolicyQuery, + } from '@backstage/plugin-permission-node'; + /* highlight-add-end */ + import { Router } from 'express'; + import { PluginEnvironment } from '../types'; + + class TestPermissionPolicy implements PermissionPolicy { + /* highlight-remove-next-line */ + async handle(): Promise { + /* highlight-add-start */ + async handle(request: PolicyQuery): Promise { + if (request.permission.name === 'catalog.entity.delete') { + return { + result: AuthorizeResult.DENY, + }; + } + /* highlight-add-end */ + + return { result: AuthorizeResult.ALLOW }; + } + } + ``` + +3. Now that you’ve made this change, you should find that the unregister entity menu option on the catalog entity page is disabled. + +![Entity detail page showing disabled unregister entity context menu entry](../assets/permissions/disabled-unregister-entity.png) + +Now that the framework is fully configured, you can craft a permission policy that works best for your organization by utilizing a provided authorization method or by [writing your own policy](./writing-a-policy.md)!