From 620b944314d97c46ef37ff8d5ae2864641252c8b Mon Sep 17 00:00:00 2001 From: stanleyn Date: Fri, 15 Dec 2023 13:36:41 +0000 Subject: [PATCH 1/5] Update custom permission rule naming and files Signed-off-by: stanleyn --- docs/permissions/custom-rules.md | 61 +++++++++++++++----------------- 1 file changed, 28 insertions(+), 33 deletions(-) diff --git a/docs/permissions/custom-rules.md b/docs/permissions/custom-rules.md index 76b0f16cfd..d0d1ef93d5 100644 --- a/docs/permissions/custom-rules.md +++ b/docs/permissions/custom-rules.md @@ -8,9 +8,10 @@ For some use cases, you may want to define custom [rules](./concepts.md#resource ## Define a custom rule -Plugins should export a rule factory that provides type-safety that ensures compatibility with the plugin's backend. The catalog plugin exports `createCatalogPermissionRule` from `@backstage/plugin-catalog-backend/alpha` for this purpose. Note: the `/alpha` path segment is temporary until this API is marked as stable. For this example, we'll define the rule in `packages/backend/src/customPermissionRules/isInSystem.ts`, but you can put it anywhere that's accessible by your `backend` package. +Plugins should export a rule factory that provides type-safety that ensures compatibility with the plugin's backend. The catalog plugin exports `createCatalogPermissionRule` from `@backstage/plugin-catalog-backend/alpha` for this purpose. Note: the `/alpha` path segment is temporary until this API is marked as stable. For this example, we'll define the rule and create a condition in `permissions.ts`. -```typescript title="packages/backend/src/customPermissionRules/isInSystem.ts" +```typescript title="packages/backend/src/plugins/permission.ts" +... import type { Entity } from '@backstage/catalog-model'; import { createCatalogPermissionRule } from '@backstage/plugin-catalog-backend/alpha'; import { createConditionFactory } from '@backstage/plugin-permission-node'; @@ -40,43 +41,15 @@ export const isInSystemRule = createCatalogPermissionRule({ }), }); -export const isInSystemRuleFactory = createConditionFactory(isInSystemRule); +const isInSystem = createConditionFactory(isInSystemRule); ``` For a more detailed explanation on defining rules, refer to the [documentation for plugin authors](./plugin-authors/03-adding-a-resource-permission-check.md#adding-support-for-conditional-decisions). -## Provide the rule during plugin setup - -Now that we have a custom rule defined, 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 some integration point during the creation of the backend router. For the catalog, this integration point is exposed via `CatalogBuilder.addPermissionRules`. - -```typescript title="packages/backend/src/plugins/catalog.ts" -import { CatalogBuilder } from '@backstage/plugin-catalog-backend'; -import { isInSystemRule } from '../customPermissionRules/isInSystem'; - -... - -export default async function createPlugin( - env: PluginEnvironment, -): Promise { - const builder = await CatalogBuilder.create(env); - builder.addPermissionRules(isInSystemRule); - ... - return router; -} -``` - -The new rule is now ready for use in a permission policy! - -## Use the rule in a policy - -Let's bring this all together by extending the example policy from the previous section. +Still in the `permissions.ts` file, let's use the condition we just created in our `TestPermissionPolicy`. ```ts title="packages/backend/src/plugins/permission.ts" ... -/* highlight-add-next-line */ -import { isInSystemRuleFactory } from '../customPermissionRules/isInSystem'; class TestPermissionPolicy implements PermissionPolicy { async handle( @@ -97,7 +70,7 @@ class TestPermissionPolicy implements PermissionPolicy { catalogConditions.isEntityOwner({ claims: user?.identity.ownershipEntityRefs ?? [] }), - isInSystemRuleFactory({ systemRef: 'interviewing' }), + isInSystem({ systemRef: 'interviewing' }), ] } /* highlight-add-end */ @@ -107,8 +80,30 @@ class TestPermissionPolicy implements PermissionPolicy { return { result: AuthorizeResult.ALLOW }; } } +``` + +## Provide the rule during plugin setup + +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 some integration point during the creation of the backend router. For the catalog, this integration point is exposed via `CatalogBuilder.addPermissionRules`. + +```typescript title="packages/backend/src/plugins/catalog.ts" +import { CatalogBuilder } from '@backstage/plugin-catalog-backend'; +/* highlight-add-next-line */ +import { isInSystemRule } from '../customPermissionRules/isInSystem'; ... + +export default async function createPlugin( + env: PluginEnvironment, +): Promise { + const builder = await CatalogBuilder.create(env); + /* highlight-add-next-line */ + builder.addPermissionRules(isInSystemRule); + ... + return router; +} ``` The updated policy will allow catalog entity resource permissions if any of the following are true: From 0f97e22a2390d068b281578d2c3b785a28b255ad Mon Sep 17 00:00:00 2001 From: stanleyn Date: Thu, 21 Dec 2023 17:03:53 +0000 Subject: [PATCH 2/5] Update file examples, routing Signed-off-by: stanleyn --- docs/permissions/custom-rules.md | 50 +++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/docs/permissions/custom-rules.md b/docs/permissions/custom-rules.md index d0d1ef93d5..2ffad693bd 100644 --- a/docs/permissions/custom-rules.md +++ b/docs/permissions/custom-rules.md @@ -8,10 +8,11 @@ For some use cases, you may want to define custom [rules](./concepts.md#resource ## Define a custom rule -Plugins should export a rule factory that provides type-safety that ensures compatibility with the plugin's backend. The catalog plugin exports `createCatalogPermissionRule` from `@backstage/plugin-catalog-backend/alpha` for this purpose. Note: the `/alpha` path segment is temporary until this API is marked as stable. For this example, we'll define the rule and create a condition in `permissions.ts`. +Plugins should export a rule factory that provides type-safety that ensures compatibility with the plugin's backend. The catalog plugin exports `createCatalogPermissionRule` from `@backstage/plugin-catalog-backend/alpha` for this purpose. Note: the `/alpha` path segment is temporary until this API is marked as stable. For this example, we'll define the rule and create a condition in `packages/backend/src/plugins/permission.ts`. ```typescript title="packages/backend/src/plugins/permission.ts" ... + import type { Entity } from '@backstage/catalog-model'; import { createCatalogPermissionRule } from '@backstage/plugin-catalog-backend/alpha'; import { createConditionFactory } from '@backstage/plugin-permission-node'; @@ -42,15 +43,48 @@ export const isInSystemRule = createCatalogPermissionRule({ }); const isInSystem = createConditionFactory(isInSystemRule); + +... ``` For a more detailed explanation on defining rules, refer to the [documentation for plugin authors](./plugin-authors/03-adding-a-resource-permission-check.md#adding-support-for-conditional-decisions). -Still in the `permissions.ts` file, let's use the condition we just created in our `TestPermissionPolicy`. +Still in the `packages/backend/src/plugins/permission.ts` file, let's use the condition we just created in our `TestPermissionPolicy`. ```ts title="packages/backend/src/plugins/permission.ts" ... +import type { Entity } from '@backstage/catalog-model'; +import { createCatalogPermissionRule } from '@backstage/plugin-catalog-backend/alpha'; +import { createConditionFactory } from '@backstage/plugin-permission-node'; +import { z } from 'zod'; + +export const isInSystemRule = createCatalogPermissionRule({ + name: 'IS_IN_SYSTEM', + description: 'Checks if an entity is part of the system provided', + resourceType: 'catalog-entity', + paramsSchema: z.object({ + systemRef: z + .string() + .describe('SystemRef to check the resource is part of'), + }), + apply: (resource: Entity, { systemRef }) => { + if (!resource.relations) { + return false; + } + + return resource.relations + .filter(relation => relation.type === 'partOf') + .some(relation => relation.targetRef === systemRef); + }, + toQuery: ({ systemRef }) => ({ + key: 'relations.partOf', + values: [systemRef], + }), +}); + +const isInSystem = createConditionFactory(isInSystemRule); + class TestPermissionPolicy implements PermissionPolicy { async handle( request: PolicyQuery, @@ -68,11 +102,11 @@ class TestPermissionPolicy implements PermissionPolicy { { anyOf: [ catalogConditions.isEntityOwner({ - claims: user?.identity.ownershipEntityRefs ?? [] + claims: user?.identity.ownershipEntityRefs ?? [], }), isInSystem({ systemRef: 'interviewing' }), - ] - } + ], + }, /* highlight-add-end */ ); } @@ -80,6 +114,8 @@ class TestPermissionPolicy implements PermissionPolicy { return { result: AuthorizeResult.ALLOW }; } } + +... ``` ## Provide the rule during plugin setup @@ -91,7 +127,7 @@ The api for providing custom rules may differ between plugins, but there should ```typescript title="packages/backend/src/plugins/catalog.ts" import { CatalogBuilder } from '@backstage/plugin-catalog-backend'; /* highlight-add-next-line */ -import { isInSystemRule } from '../customPermissionRules/isInSystem'; +import { isInSystemRule } from './permissions'; ... @@ -109,4 +145,4 @@ export default async function createPlugin( The updated policy will allow catalog entity resource permissions if any of the following are true: - User owns the target entity -- Target entity is part of the `'interviewing'` system +- Target entity is part of the `interviewing` system From 83ddafc5a9ddb04fe87cb146abd1c231e1234c6d Mon Sep 17 00:00:00 2001 From: stanleyn Date: Fri, 22 Dec 2023 15:08:56 +0000 Subject: [PATCH 3/5] Add missing imports, add zod install, fix import Signed-off-by: stanleyn --- docs/permissions/custom-rules.md | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/docs/permissions/custom-rules.md b/docs/permissions/custom-rules.md index 2ffad693bd..1fc81b3100 100644 --- a/docs/permissions/custom-rules.md +++ b/docs/permissions/custom-rules.md @@ -10,6 +10,12 @@ For some use cases, you may want to define custom [rules](./concepts.md#resource Plugins should export a rule factory that provides type-safety that ensures compatibility with the plugin's backend. The catalog plugin exports `createCatalogPermissionRule` from `@backstage/plugin-catalog-backend/alpha` for this purpose. Note: the `/alpha` path segment is temporary until this API is marked as stable. For this example, we'll define the rule and create a condition in `packages/backend/src/plugins/permission.ts`. +We use Zod in our example to create our params schema. To install, run: + +```bash +yarn workspace backend add zod +``` + ```typescript title="packages/backend/src/plugins/permission.ts" ... @@ -53,11 +59,19 @@ Still in the `packages/backend/src/plugins/permission.ts` file, let's use the co ```ts title="packages/backend/src/plugins/permission.ts" ... - -import type { Entity } from '@backstage/catalog-model'; +/* highlight-remove-next-line */ import { createCatalogPermissionRule } from '@backstage/plugin-catalog-backend/alpha'; +/* highlight-add-next-line */ +import { catalogConditions, createCatalogConditionalDecision, createCatalogPermissionRule } from '@backstage/plugin-catalog-backend/alpha'; +/* highlight-remove-next-line */ import { createConditionFactory } from '@backstage/plugin-permission-node'; -import { z } from 'zod'; +/* highlight-add-next-line */ +import { PermissionPolicy, PolicyQuery, createConditionFactory } from '@backstage/plugin-permission-node'; +/* highlight-add-start */ +import { BackstageIdentityResponse } from '@backstage/plugin-auth-node'; +import { AuthorizeResult, PolicyDecision, isResourcePermission } from '@backstage/plugin-permission-common'; +/* highlight-add-end */ + export const isInSystemRule = createCatalogPermissionRule({ name: 'IS_IN_SYSTEM', @@ -127,7 +141,7 @@ The api for providing custom rules may differ between plugins, but there should ```typescript title="packages/backend/src/plugins/catalog.ts" import { CatalogBuilder } from '@backstage/plugin-catalog-backend'; /* highlight-add-next-line */ -import { isInSystemRule } from './permissions'; +import { isInSystemRule } from './permission'; ... @@ -145,4 +159,4 @@ export default async function createPlugin( The updated policy will allow catalog entity resource permissions if any of the following are true: - User owns the target entity -- Target entity is part of the `interviewing` system +- Target entity is part of the 'interviewing' system From f8119bdf53072b43f77493b5ea14826a57ab9742 Mon Sep 17 00:00:00 2001 From: stanleyn Date: Fri, 22 Dec 2023 15:16:53 +0000 Subject: [PATCH 4/5] fix spelling Signed-off-by: stanleyn --- docs/permissions/custom-rules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/permissions/custom-rules.md b/docs/permissions/custom-rules.md index 1fc81b3100..457d1fc7c4 100644 --- a/docs/permissions/custom-rules.md +++ b/docs/permissions/custom-rules.md @@ -10,7 +10,7 @@ For some use cases, you may want to define custom [rules](./concepts.md#resource Plugins should export a rule factory that provides type-safety that ensures compatibility with the plugin's backend. The catalog plugin exports `createCatalogPermissionRule` from `@backstage/plugin-catalog-backend/alpha` for this purpose. Note: the `/alpha` path segment is temporary until this API is marked as stable. For this example, we'll define the rule and create a condition in `packages/backend/src/plugins/permission.ts`. -We use Zod in our example to create our params schema. To install, run: +We use Zod in our example below. To install, run: ```bash yarn workspace backend add zod From 93c39784b95144e901c08d34d354c022c8297a57 Mon Sep 17 00:00:00 2001 From: stanleyn Date: Fri, 22 Dec 2023 15:18:30 +0000 Subject: [PATCH 5/5] add ellipsis Signed-off-by: stanleyn --- docs/permissions/custom-rules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/permissions/custom-rules.md b/docs/permissions/custom-rules.md index 457d1fc7c4..c0f4e3e156 100644 --- a/docs/permissions/custom-rules.md +++ b/docs/permissions/custom-rules.md @@ -71,7 +71,7 @@ import { PermissionPolicy, PolicyQuery, createConditionFactory } from '@backstag import { BackstageIdentityResponse } from '@backstage/plugin-auth-node'; import { AuthorizeResult, PolicyDecision, isResourcePermission } from '@backstage/plugin-permission-common'; /* highlight-add-end */ - +... export const isInSystemRule = createCatalogPermissionRule({ name: 'IS_IN_SYSTEM',