From 6f353445dc4259d9b66215d89978f1b7af56e6a4 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Thu, 13 Apr 2023 20:57:45 +0200 Subject: [PATCH 1/8] docs: scaffolder permissions Signed-off-by: Vincenzo Scamporlino --- ...uthorizing-parameters-steps-and-actions.md | 113 ++++++++++++++++++ microsite/sidebars.json | 1 + 2 files changed, 114 insertions(+) create mode 100644 docs/features/software-templates/authorizing-parameters-steps-and-actions.md diff --git a/docs/features/software-templates/authorizing-parameters-steps-and-actions.md b/docs/features/software-templates/authorizing-parameters-steps-and-actions.md new file mode 100644 index 0000000000..874f30b0d5 --- /dev/null +++ b/docs/features/software-templates/authorizing-parameters-steps-and-actions.md @@ -0,0 +1,113 @@ +--- +id: authorizing-parameters-steps-and-actions +title: 'Authorizing parameters, steps and actions' +description: How to authorize part of a template +--- + +The scaffolder plugin integrates with the Backstage [permission framework](../../permissions/overview), which allows you to control access to certain parameters and steps in your templates based on the user executing the template. + +### Authorizing parameters and steps + +To mark specific parameters or steps as requiring permission, add the backstage:permissions property to the parameter or step with one or more tags. For example: + +```yaml +apiVersion: scaffolder.backstage.io/v1beta3 +kind: Template +metadata: + name: my_custom_template + parameters: + - title: Provide some simple information + properties: + title: + title: Title + type: string + - title: Extra information + properties: + description: + title: Description + type: string + backstage:permissions: + tags: + - secret + steps: + - id: step1 + name: First log + action: debug:log + input: + message: hello + - id: step2 + name: Log message + action: debug:log + input: + message: hello + backstage:permissions: + tags: + - secret + +parameters: + - title: Simple information + properties: + input: + title: Title + type: string + backstage:permissions: + tags: + - secret + - title: Simple information + properties: + description: + title: Description + type: string + backstage:permissions: + tags: + - secret + steps: + - id: step1 + name: Log message + action: debug:log + input: + message: hello + backstage:permissions: + tags: + - secret +``` + +In this example, the `description` parameter and the `step2` step are marked with the `secret` tag. + +To conditionally authorize parameters and steps based on the user executing the template, [edit your permission policy](../../permissions/writing-a-policy), by targeting `templateParameterReadPermission` and `templateStepReadPermission` permissions, which are provided by the scaffolder plugin. For example: + +```ts +import { + templateParameterReadPermission, + templateStepReadPermission, +} from '@backstage/plugin-scaffolder-common/alpha'; +import { + createScaffolderTemplateConditionalDecision, + scaffolderTemplateConditions, +} from '@backstage/plugin-scaffolder-backend/alpha'; + +class ExamplePermissionPolicy implements PermissionPolicy { + async handle( + request: PolicyQuery, + user?: BackstageIdentityResponse, + ): Promise { + if ( + isPermission(request.permission, templateParameterReadPermission) || + isPermission(request.permission, templateStepReadPermission) + ) { + if (user?.identity.userEntityRef === 'user:default/spiderman') + return createScaffolderTemplateConditionalDecision(request.permission, { + not: scaffolderTemplateConditions.hasTag({ tag: 'secret' }), + }); + } + + return { + result: AuthorizeResult.ALLOW, + }; + } +} +``` + +In this example, the user `spiderman` is not authorized to read parameters or steps marked with the `secret` tag. + +By combining this feature with restricting the ingestion of templates in the Catalog as recommended in our threat model, you can create a solid system to restrict certain actions. diff --git a/microsite/sidebars.json b/microsite/sidebars.json index ec6c68fa94..55e7ca4df9 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -109,6 +109,7 @@ "features/software-templates/writing-custom-actions", "features/software-templates/writing-custom-field-extensions", "features/software-templates/writing-custom-step-layouts", + "features/software-templates/authorizing-parameters-steps-and-actions", "features/software-templates/testing-scaffolder-alpha", "features/software-templates/migrating-from-v1beta2-to-v1beta3" ] From 7153f97938d216a5bee2377eb4373d17eae67a21 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Fri, 14 Apr 2023 11:40:40 +0200 Subject: [PATCH 2/8] docs: permissioning scaffolder actions Signed-off-by: Vincenzo Scamporlino --- ...uthorizing-parameters-steps-and-actions.md | 113 +++++++++++++----- 1 file changed, 84 insertions(+), 29 deletions(-) diff --git a/docs/features/software-templates/authorizing-parameters-steps-and-actions.md b/docs/features/software-templates/authorizing-parameters-steps-and-actions.md index 874f30b0d5..2151ee0675 100644 --- a/docs/features/software-templates/authorizing-parameters-steps-and-actions.md +++ b/docs/features/software-templates/authorizing-parameters-steps-and-actions.md @@ -43,33 +43,6 @@ metadata: backstage:permissions: tags: - secret - -parameters: - - title: Simple information - properties: - input: - title: Title - type: string - backstage:permissions: - tags: - - secret - - title: Simple information - properties: - description: - title: Description - type: string - backstage:permissions: - tags: - - secret - steps: - - id: step1 - name: Log message - action: debug:log - input: - message: hello - backstage:permissions: - tags: - - secret ``` In this example, the `description` parameter and the `step2` step are marked with the `secret` tag. @@ -82,7 +55,7 @@ import { templateStepReadPermission, } from '@backstage/plugin-scaffolder-common/alpha'; import { - createScaffolderTemplateConditionalDecision, + createScaffolderActionConditionalDecision, scaffolderTemplateConditions, } from '@backstage/plugin-scaffolder-backend/alpha'; @@ -97,7 +70,12 @@ class ExamplePermissionPolicy implements PermissionPolicy { ) { if (user?.identity.userEntityRef === 'user:default/spiderman') return createScaffolderTemplateConditionalDecision(request.permission, { - not: scaffolderTemplateConditions.hasTag({ tag: 'secret' }), + not: { + allOf: [ + scaffolderTemplateConditions.hasTag({ tag: 'secret' }), + scaffolderTemplateConditions.hasTag({ tag: 'secret' }), + ], + }, }); } @@ -111,3 +89,80 @@ class ExamplePermissionPolicy implements PermissionPolicy { In this example, the user `spiderman` is not authorized to read parameters or steps marked with the `secret` tag. By combining this feature with restricting the ingestion of templates in the Catalog as recommended in our threat model, you can create a solid system to restrict certain actions. + +### Authorizing actions + +Similar to parameters and steps, the scaffolder plugin exposes permissions to restrict access to certain actions. This can be useful if you want to enforce security on your templates. + +To restrict access to a particular action, you can modify your permission policy as follows: + +```ts +import { actionExecutePermission } from '@backstage/plugin-scaffolder-common/alpha'; +import { + createScaffolderActionConditionalDecision, + scaffolderActionConditions, +} from '@backstage/plugin-scaffolder-backend/alpha'; + +class ExamplePermissionPolicy implements PermissionPolicy { + async handle( + request: PolicyQuery, + user?: BackstageIdentityResponse, + ): Promise { + if (isPermission(request.permission, actionExecutePermission)) { + if (user?.identity.userEntityRef === 'user:default/spiderman') { + return createScaffolderActionConditionalDecision(request.permission, { + not: scaffolderActionConditions.hasActionId({ + actionId: 'debug:log', + }), + }); + } + } + + return { + result: AuthorizeResult.ALLOW, + }; + } +} +``` + +With this permission policy, spiderman won't be able to execute the debug:log action. + +You can also restrict the input provided to the action by combining multiple rules. +In the example below, spiderman won't be able to execute debug:log when passing `{ "message": "not-this!" }` as action input: + +```ts +import { actionExecutePermission } from '@backstage/plugin-scaffolder-common/alpha'; +import { + createScaffolderActionConditionalDecision, + scaffolderActionConditions, +} from '@backstage/plugin-scaffolder-backend/alpha'; + +class ExamplePermissionPolicy implements PermissionPolicy { + async handle( + request: PolicyQuery, + user?: BackstageIdentityResponse, + ): Promise { + if (isPermission(request.permission, actionExecutePermission)) { + if (user?.identity.userEntityRef === 'user:default/spiderman') { + return createScaffolderActionConditionalDecision(request.permission, { + not: { + allOf: [ + scaffolderActionConditions.hasActionId({ actionId: 'debug:log' }), + scaffolderActionConditions.hasProperty({ + key: 'message', + value: 'not-this!', + }), + ], + }, + }); + } + } + + return { + result: AuthorizeResult.ALLOW, + }; + } +} +``` + +Although the rules exported by the scaffolder are simple, combining them can help you achieve more complex cases. From d49ddb8e7a1ef2ea679862ea1a951a7511d679b2 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Fri, 14 Apr 2023 11:59:52 +0200 Subject: [PATCH 3/8] docs: fix links Signed-off-by: Vincenzo Scamporlino --- .../authorizing-parameters-steps-and-actions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/features/software-templates/authorizing-parameters-steps-and-actions.md b/docs/features/software-templates/authorizing-parameters-steps-and-actions.md index 2151ee0675..73259570a3 100644 --- a/docs/features/software-templates/authorizing-parameters-steps-and-actions.md +++ b/docs/features/software-templates/authorizing-parameters-steps-and-actions.md @@ -4,7 +4,7 @@ title: 'Authorizing parameters, steps and actions' description: How to authorize part of a template --- -The scaffolder plugin integrates with the Backstage [permission framework](../../permissions/overview), which allows you to control access to certain parameters and steps in your templates based on the user executing the template. +The scaffolder plugin integrates with the Backstage [permission framework](../../permissions/overview.md), which allows you to control access to certain parameters and steps in your templates based on the user executing the template. ### Authorizing parameters and steps @@ -47,7 +47,7 @@ metadata: In this example, the `description` parameter and the `step2` step are marked with the `secret` tag. -To conditionally authorize parameters and steps based on the user executing the template, [edit your permission policy](../../permissions/writing-a-policy), by targeting `templateParameterReadPermission` and `templateStepReadPermission` permissions, which are provided by the scaffolder plugin. For example: +To conditionally authorize parameters and steps based on the user executing the template, [edit your permission policy](../../permissions/writing-a-policy.md), by targeting `templateParameterReadPermission` and `templateStepReadPermission` permissions, which are provided by the scaffolder plugin. For example: ```ts import { From 1e043555f30033bccd3db3da447905b25b56b608 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Fri, 14 Apr 2023 12:04:42 +0200 Subject: [PATCH 4/8] yes, I meant spiderman Signed-off-by: Vincenzo Scamporlino --- .github/vale/Vocab/Backstage/accept.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/vale/Vocab/Backstage/accept.txt b/.github/vale/Vocab/Backstage/accept.txt index 83fba8aa56..a8e9d639c9 100644 --- a/.github/vale/Vocab/Backstage/accept.txt +++ b/.github/vale/Vocab/Backstage/accept.txt @@ -328,6 +328,7 @@ Snyk Sonarqube sourcemaps sparklines +spiderman Splunk Spotifiers spotify From 6ce580660a758bcf4ba076191dfad1b6e7d858eb Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Fri, 21 Apr 2023 20:07:29 +0200 Subject: [PATCH 5/8] Apply suggestions from code review Co-authored-by: Johan Haals Signed-off-by: Vincenzo Scamporlino --- .../authorizing-parameters-steps-and-actions.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/features/software-templates/authorizing-parameters-steps-and-actions.md b/docs/features/software-templates/authorizing-parameters-steps-and-actions.md index 73259570a3..8c1bb29067 100644 --- a/docs/features/software-templates/authorizing-parameters-steps-and-actions.md +++ b/docs/features/software-templates/authorizing-parameters-steps-and-actions.md @@ -8,7 +8,7 @@ The scaffolder plugin integrates with the Backstage [permission framework](../.. ### Authorizing parameters and steps -To mark specific parameters or steps as requiring permission, add the backstage:permissions property to the parameter or step with one or more tags. For example: +To mark specific parameters or steps as requiring permission, add the `backstage:permissions` property to the parameter or step with one or more tags. For example: ```yaml apiVersion: scaffolder.backstage.io/v1beta3 @@ -92,7 +92,7 @@ By combining this feature with restricting the ingestion of templates in the Cat ### Authorizing actions -Similar to parameters and steps, the scaffolder plugin exposes permissions to restrict access to certain actions. This can be useful if you want to enforce security on your templates. +Similar to parameters and steps, the scaffolder plugin exposes permissions to restrict access to certain actions. This can be useful if you want to secure your templates. To restrict access to a particular action, you can modify your permission policy as follows: @@ -125,7 +125,7 @@ class ExamplePermissionPolicy implements PermissionPolicy { } ``` -With this permission policy, spiderman won't be able to execute the debug:log action. +With this permission policy, the user `spiderman` won't be able to execute the debug:log action. You can also restrict the input provided to the action by combining multiple rules. In the example below, spiderman won't be able to execute debug:log when passing `{ "message": "not-this!" }` as action input: From 60328b110da88ccb8d65d4d6290425a1bf644484 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Fri, 21 Apr 2023 20:11:39 +0200 Subject: [PATCH 6/8] No I didn't mean spiderman Signed-off-by: Vincenzo Scamporlino --- .github/vale/Vocab/Backstage/accept.txt | 1 - .../authorizing-parameters-steps-and-actions.md | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/vale/Vocab/Backstage/accept.txt b/.github/vale/Vocab/Backstage/accept.txt index a8e9d639c9..83fba8aa56 100644 --- a/.github/vale/Vocab/Backstage/accept.txt +++ b/.github/vale/Vocab/Backstage/accept.txt @@ -328,7 +328,6 @@ Snyk Sonarqube sourcemaps sparklines -spiderman Splunk Spotifiers spotify diff --git a/docs/features/software-templates/authorizing-parameters-steps-and-actions.md b/docs/features/software-templates/authorizing-parameters-steps-and-actions.md index 8c1bb29067..de8b5b328c 100644 --- a/docs/features/software-templates/authorizing-parameters-steps-and-actions.md +++ b/docs/features/software-templates/authorizing-parameters-steps-and-actions.md @@ -128,7 +128,7 @@ class ExamplePermissionPolicy implements PermissionPolicy { With this permission policy, the user `spiderman` won't be able to execute the debug:log action. You can also restrict the input provided to the action by combining multiple rules. -In the example below, spiderman won't be able to execute debug:log when passing `{ "message": "not-this!" }` as action input: +In the example below, `spiderman` won't be able to execute debug:log when passing `{ "message": "not-this!" }` as action input: ```ts import { actionExecutePermission } from '@backstage/plugin-scaffolder-common/alpha'; From 9dc715901e1be032cd1b7123534764ead5e9cefa Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Fri, 21 Apr 2023 20:14:48 +0200 Subject: [PATCH 7/8] docs: fix conditional decision Signed-off-by: Vincenzo Scamporlino --- .../authorizing-parameters-steps-and-actions.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/docs/features/software-templates/authorizing-parameters-steps-and-actions.md b/docs/features/software-templates/authorizing-parameters-steps-and-actions.md index de8b5b328c..7934323253 100644 --- a/docs/features/software-templates/authorizing-parameters-steps-and-actions.md +++ b/docs/features/software-templates/authorizing-parameters-steps-and-actions.md @@ -70,12 +70,7 @@ class ExamplePermissionPolicy implements PermissionPolicy { ) { if (user?.identity.userEntityRef === 'user:default/spiderman') return createScaffolderTemplateConditionalDecision(request.permission, { - not: { - allOf: [ - scaffolderTemplateConditions.hasTag({ tag: 'secret' }), - scaffolderTemplateConditions.hasTag({ tag: 'secret' }), - ], - }, + not: scaffolderTemplateConditions.hasTag({ tag: 'secret' }), }); } From 283f3c38552750e21535a6e2ae231cedb38e8e6b Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Fri, 21 Apr 2023 20:26:26 +0200 Subject: [PATCH 8/8] docs: beautify snippets Signed-off-by: Vincenzo Scamporlino --- ...authorizing-parameters-steps-and-actions.md | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/docs/features/software-templates/authorizing-parameters-steps-and-actions.md b/docs/features/software-templates/authorizing-parameters-steps-and-actions.md index 7934323253..1351676c37 100644 --- a/docs/features/software-templates/authorizing-parameters-steps-and-actions.md +++ b/docs/features/software-templates/authorizing-parameters-steps-and-actions.md @@ -49,7 +49,8 @@ In this example, the `description` parameter and the `step2` step are marked wit To conditionally authorize parameters and steps based on the user executing the template, [edit your permission policy](../../permissions/writing-a-policy.md), by targeting `templateParameterReadPermission` and `templateStepReadPermission` permissions, which are provided by the scaffolder plugin. For example: -```ts +```ts title="packages/backend/src/plugins/permission.ts" +/* highlight-add-start */ import { templateParameterReadPermission, templateStepReadPermission, @@ -58,12 +59,14 @@ import { createScaffolderActionConditionalDecision, scaffolderTemplateConditions, } from '@backstage/plugin-scaffolder-backend/alpha'; +/* highlight-add-end */ class ExamplePermissionPolicy implements PermissionPolicy { async handle( request: PolicyQuery, user?: BackstageIdentityResponse, ): Promise { + /* highlight-add-start */ if ( isPermission(request.permission, templateParameterReadPermission) || isPermission(request.permission, templateStepReadPermission) @@ -73,6 +76,7 @@ class ExamplePermissionPolicy implements PermissionPolicy { not: scaffolderTemplateConditions.hasTag({ tag: 'secret' }), }); } + /* highlight-add-end */ return { result: AuthorizeResult.ALLOW, @@ -91,18 +95,21 @@ Similar to parameters and steps, the scaffolder plugin exposes permissions to re To restrict access to a particular action, you can modify your permission policy as follows: -```ts +```ts title="packages/backend/src/plugins/permission.ts" +/* highlight-add-start */ import { actionExecutePermission } from '@backstage/plugin-scaffolder-common/alpha'; import { createScaffolderActionConditionalDecision, scaffolderActionConditions, } from '@backstage/plugin-scaffolder-backend/alpha'; +/* highlight-add-end */ class ExamplePermissionPolicy implements PermissionPolicy { async handle( request: PolicyQuery, user?: BackstageIdentityResponse, ): Promise { + /* highlight-add-start */ if (isPermission(request.permission, actionExecutePermission)) { if (user?.identity.userEntityRef === 'user:default/spiderman') { return createScaffolderActionConditionalDecision(request.permission, { @@ -112,6 +119,7 @@ class ExamplePermissionPolicy implements PermissionPolicy { }); } } + /* highlight-add-end */ return { result: AuthorizeResult.ALLOW, @@ -125,18 +133,21 @@ With this permission policy, the user `spiderman` won't be able to execute the d You can also restrict the input provided to the action by combining multiple rules. In the example below, `spiderman` won't be able to execute debug:log when passing `{ "message": "not-this!" }` as action input: -```ts +```ts title="packages/backend/src/plugins/permission.ts" +/* highlight-add-start */ import { actionExecutePermission } from '@backstage/plugin-scaffolder-common/alpha'; import { createScaffolderActionConditionalDecision, scaffolderActionConditions, } from '@backstage/plugin-scaffolder-backend/alpha'; +/* highlight-add-end */ class ExamplePermissionPolicy implements PermissionPolicy { async handle( request: PolicyQuery, user?: BackstageIdentityResponse, ): Promise { + /* highlight-add-start */ if (isPermission(request.permission, actionExecutePermission)) { if (user?.identity.userEntityRef === 'user:default/spiderman') { return createScaffolderActionConditionalDecision(request.permission, { @@ -152,6 +163,7 @@ class ExamplePermissionPolicy implements PermissionPolicy { }); } } + /* highlight-add-end */ return { result: AuthorizeResult.ALLOW,