From e2571d17616fe5dcdbba2f7fded86109341e25ba Mon Sep 17 00:00:00 2001 From: Harry Hogg Date: Tue, 7 Feb 2023 13:29:33 +0000 Subject: [PATCH] POC Signed-off-by: Harry Hogg --- .../tasks/NunjucksWorkflowRunner.ts | 1 + .../scaffolder-backend/src/service/router.ts | 56 +++++++++-------- .../scaffolder-backend/src/service/rules.ts | 60 ++++++++++++++++++- plugins/scaffolder-common/src/permissions.ts | 20 +++++++ 4 files changed, 109 insertions(+), 28 deletions(-) diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts index 9358484642..b31def1739 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts @@ -66,6 +66,7 @@ type TemplateContext = { entity?: UserEntity; ref?: string; }; + token?: string; }; const isValidTaskSpec = (taskSpec: TaskSpec): taskSpec is TaskSpecV1beta3 => { diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index 1f2d390f9f..0927e5f062 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -378,31 +378,7 @@ export async function createRouter( const baseUrl = getEntityBaseUrl(template); - const taskSpec: TaskSpec = { - apiVersion: template.apiVersion, - steps: template.spec.steps.map((step, index) => ({ - ...step, - id: step.id ?? `step-${index + 1}`, - name: step.name ?? step.action, - })), - output: template.spec.output ?? {}, - parameters: values, - user: { - entity: userEntity as UserEntity, - ref: userEntityRef, - }, - templateInfo: { - entityRef: stringifyEntityRef({ - kind, - namespace, - name: template.metadata?.name, - }), - baseUrl, - entity: { - metadata: template.metadata, - }, - }, - }; + const taskSpec = await authorizeTaskSpec(template); const result = await taskBroker.dispatch({ spec: taskSpec, @@ -651,5 +627,35 @@ export async function createRouter( return template; } + async function authorizeTaskSpec(template: TemplateEntityV1beta3): TaskSpec { + const taskSpec: TaskSpec = { + apiVersion: template.apiVersion, + steps: template.spec.steps.map((step, index) => ({ + ...step, + id: step.id ?? `step-${index + 1}`, + name: step.name ?? step.action, + })), + output: template.spec.output ?? {}, + parameters: values, + user: { + entity: userEntity as UserEntity, + ref: userEntityRef, + }, + templateInfo: { + entityRef: stringifyEntityRef({ + kind, + namespace, + name: template.metadata?.name, + }), + baseUrl, + entity: { + metadata: template.metadata, + }, + }, + }; + + return taskSpec; + } + return app; } diff --git a/plugins/scaffolder-backend/src/service/rules.ts b/plugins/scaffolder-backend/src/service/rules.ts index d94f7dd4f4..129d61e591 100644 --- a/plugins/scaffolder-backend/src/service/rules.ts +++ b/plugins/scaffolder-backend/src/service/rules.ts @@ -15,21 +15,75 @@ */ import { makeCreatePermissionRule } from '@backstage/plugin-permission-node'; +import { + RESOURCE_TYPE_SCAFFOLDER_TEMPLATE, + RESOURCE_TYPE_SCAFFOLDER_ACTION, +} from '@backstage/plugin-scaffolder-common/alpha'; + import { TemplateEntityStepV1beta3, TemplateParametersV1beta3, } from '@backstage/plugin-scaffolder-common'; -import { RESOURCE_TYPE_SCAFFOLDER_TEMPLATE } from '@backstage/plugin-scaffolder-common/alpha'; import { z } from 'zod'; +import { JsonObject } from '@backstage/types'; -export const createScaffolderPermissionRule = makeCreatePermissionRule< +export const createTemplatePermissionRule = makeCreatePermissionRule< TemplateEntityStepV1beta3 | TemplateParametersV1beta3, {}, typeof RESOURCE_TYPE_SCAFFOLDER_TEMPLATE >(); -export const hasTag = createScaffolderPermissionRule({ +export const createActionPermissionRule = makeCreatePermissionRule< + { + actionId: string; + input: JsonObject; + template: TemplateEntityStepV1beta3 | TemplateParametersV1beta3; + }, + {}, + typeof RESOURCE_TYPE_SCAFFOLDER_ACTION +>(); + +export const hasActionId = createActionPermissionRule({ + name: 'HAS_ACTION_ID', + resourceType: RESOURCE_TYPE_SCAFFOLDER_ACTION, + description: `Match actions with the given actionId`, + paramsSchema: z.object({ + actionId: z.string().describe('Name of the actionId to match on'), + }), + apply: (resource, { actionId }) => { + return resource.actionId === actionId; + }, + toQuery: () => ({}), +}); + +export const matchesInput = createActionPermissionRule({ + name: 'MATCHED_INPUT', + resourceType: RESOURCE_TYPE_SCAFFOLDER_ACTION, + description: `Matches actionId and the input given`, + paramsSchema: z.object({ + actionId: z.string().describe('Name of the actionId to match on'), + + // Pass in a json schema to validate the input against + input: z.jsonSchema({}).describe('Input to match on'), + }), + apply: (resource, { actionId, input }) => { + if (resource.actionId !== actionId) { + return false; + } + + for (const [key, value] of Object.entries(input)) { + if (resource.input[key] !== value) { + return false; + } + } + + return true; + }, + toQuery: () => ({}), +}); + +export const hasTag = createTemplatePermissionRule({ name: 'HAS_TAG', resourceType: RESOURCE_TYPE_SCAFFOLDER_TEMPLATE, description: `Match parameters or steps with the given tag`, diff --git a/plugins/scaffolder-common/src/permissions.ts b/plugins/scaffolder-common/src/permissions.ts index 858430eaf3..f06d7984f2 100644 --- a/plugins/scaffolder-common/src/permissions.ts +++ b/plugins/scaffolder-common/src/permissions.ts @@ -23,6 +23,25 @@ import { createPermission } from '@backstage/plugin-permission-common'; */ export const RESOURCE_TYPE_SCAFFOLDER_TEMPLATE = 'scaffolder-template'; +/** + * Permission resource type which corresponds to a scaffolder action. + * + * @alpha + */ +export const RESOURCE_TYPE_SCAFFOLDER_ACTION = 'scaffolder-action'; + +/** + * This permission is used to authorize actions that involve executing + * an action from a template. + * + * @alpha + */ +export const actionExecutePermission = createPermission({ + name: 'scaffolder.action.execute', + attributes: {}, + resourceType: RESOURCE_TYPE_SCAFFOLDER_ACTION, +}); + /** * This permission is used to authorize actions that involve reading * one or more parameters from a template. @@ -64,6 +83,7 @@ export const templateStepReadPermission = createPermission({ * @alpha */ export const scaffolderPermissions = [ + actionExecutePermission, templateParameterReadPermission, templateStepReadPermission, ];