diff --git a/docs/assets/permission/disabled-unregister-entity.png b/docs/assets/permission/disabled-unregister-entity.png new file mode 100644 index 0000000000..f7975a0ced Binary files /dev/null and b/docs/assets/permission/disabled-unregister-entity.png differ diff --git a/docs/permission/getting-started.md b/docs/permission/getting-started.md index e0138171b6..0faeb679f7 100644 --- a/docs/permission/getting-started.md +++ b/docs/permission/getting-started.md @@ -3,3 +3,137 @@ id: getting-started 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: + + + +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 + +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 backend-to-backend authentication + +Backend-to-backend 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 backend-to-backend authentication, follow the [backend-to-backend authentication docs](../tutorials/backend-to-backend-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. + +## 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. +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 +import { IdentityClient } from '@backstage/plugin-auth-node'; +import { createRouter } from '@backstage/plugin-permission-backend'; +import { AuthorizeResult } from '@backstage/plugin-permission-common'; +import { + PermissionPolicy, + PolicyDecision, +} 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 { + const { config, logger, discovery } = env; + return await createRouter({ + config, + logger, + discovery, + policy: new TestPermissionPolicy(), + identity: IdentityClient.create({ + discovery, + issuer: await discovery.getExternalBaseUrl('auth'), + }), + }); +} +``` + +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. 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 +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: + +```diff + import { IdentityClient } from '@backstage/plugin-auth-node'; + import { createRouter } from '@backstage/plugin-permission-backend'; + import { AuthorizeResult } from '@backstage/plugin-permission-common'; + import { + PermissionPolicy, ++ PolicyAuthorizeQuery, + PolicyDecision, + } from '@backstage/plugin-permission-node'; + import { Router } from 'express'; + import { PluginEnvironment } from '../types'; + + class TestPermissionPolicy implements PermissionPolicy { +- async handle(): Promise { ++ async handle(request: PolicyAuthorizeQuery): Promise { ++ if (request.permission.name === 'catalog.entity.delete') { ++ return { ++ result: AuthorizeResult.DENY, ++ }; ++ } ++ + return { result: AuthorizeResult.ALLOW }; + } + } + + export default async function createPlugin( + env: PluginEnvironment, + ): Promise { + const { config, logger, discovery } = env; + return await createRouter({ + config, + logger, + discovery, + policy: new TestPermissionPolicy(), + identity: IdentityClient.create({ + discovery, + issuer: await discovery.getExternalBaseUrl('auth'), + }), + }); + } +``` + +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/permission/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)!