diff --git a/plugins/permission-node/src/integration/createPermissionIntegrationRouter.test.ts b/plugins/permission-node/src/integration/createPermissionIntegrationRouter.test.ts index 33b12f4fc2..af764b01e2 100644 --- a/plugins/permission-node/src/integration/createPermissionIntegrationRouter.test.ts +++ b/plugins/permission-node/src/integration/createPermissionIntegrationRouter.test.ts @@ -62,12 +62,16 @@ const createApp = ( | typeof defaultMockedGetResources | null = defaultMockedGetResources, ) => { - const router = createPermissionIntegrationRouter({ - resourceType: 'test-resource', - permissions: [testPermission], - getResources: mockedGetResources || undefined, - rules: [testRule1, testRule2], - }); + const router = createPermissionIntegrationRouter( + mockedGetResources + ? { + resourceType: 'test-resource', + permissions: [testPermission], + getResources: mockedGetResources, + rules: [testRule1, testRule2], + } + : { permissions: [testPermission] }, + ); return express().use(router); }; @@ -555,16 +559,16 @@ describe('createPermissionIntegrationRouter', () => { expect(response.error && response.error.text).toMatch(/invalid/i); }); - it('returns 400 with no getResources implementation', async () => { + it('returns 501 with no getResources implementation', async () => { const response = await request(createApp(null)) .post('/.well-known/backstage/permissions/apply-conditions') .send({ items: [], }); - expect(response.status).toEqual(400); + expect(response.status).toEqual(501); expect(response.body.error.message).toEqual( - 'This plugin does not support the apply-conditions API.', + 'This plugin does not support the apply-conditions API', ); }); }); diff --git a/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts b/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts index 49a89f0394..d35bf06b24 100644 --- a/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts +++ b/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts @@ -36,6 +36,7 @@ import { isNotCriteria, isOrCriteria, } from './util'; +import { NotImplementedError } from '@backstage/errors'; const permissionCriteriaSchema: z.ZodSchema< PermissionCriteria @@ -204,10 +205,11 @@ const applyConditions = ( * * @public */ -export const createPermissionIntegrationRouter = < + +type CreatePermissionIntegrationRouterResourceOptions< TResourceType extends string, TResource, ->(options: { +> = { resourceType: TResourceType; permissions?: Array; // Do not infer value of TResourceType from supplied rules. @@ -215,12 +217,28 @@ export const createPermissionIntegrationRouter = < // consider any rules whose resource type does not match // to be an error. rules: PermissionRule>[]; - getResources?: GetResourcesFn; -}): express.Router => { - const { resourceType, permissions, rules, getResources } = options; + getResources: GetResourcesFn; +}; + +type CreatePermissionIntegrationRouterOptions< + TResourceType extends string, + TResource, +> = + | { + permissions: Array; + } + | CreatePermissionIntegrationRouterResourceOptions; + +export const createPermissionIntegrationRouter = < + TResourceType extends string, + TResource, +>( + options: CreatePermissionIntegrationRouterOptions, +): express.Router => { const router = Router(); router.use(express.json()); + const { permissions = [], rules = [] } = { rules: [], ...options }; router.get('/.well-known/backstage/permissions/metadata', (_, res) => { const serializedRules: MetadataResponseSerializedRule[] = rules.map( rule => ({ @@ -239,30 +257,31 @@ export const createPermissionIntegrationRouter = < return res.json(responseJson); }); - const getRule = createGetRule(rules); - - const assertValidResourceTypes = ( - requests: ApplyConditionsRequestEntry[], - ) => { - const invalidResourceTypes = requests - .filter(request => request.resourceType !== resourceType) - .map(request => request.resourceType); - - if (invalidResourceTypes.length) { - throw new InputError( - `Unexpected resource types: ${invalidResourceTypes.join(', ')}.`, - ); - } - }; - router.post( '/.well-known/backstage/permissions/apply-conditions', async (req, res: Response) => { - if (!getResources) { - throw new InputError( - 'This plugin does not support the apply-conditions API.', + if (!isCreatePermissionIntegrationRouterResourceOptions(options)) { + throw new NotImplementedError( + 'This plugin does not support the apply-conditions API', ); } + const { resourceType, getResources } = options; + + const getRule = createGetRule(rules); + + const assertValidResourceTypes = ( + requests: ApplyConditionsRequestEntry[], + ) => { + const invalidResourceTypes = requests + .filter(request => request.resourceType !== resourceType) + .map(request => request.resourceType); + + if (invalidResourceTypes.length) { + throw new InputError( + `Unexpected resource types: ${invalidResourceTypes.join(', ')}.`, + ); + } + }; const parseResult = applyConditionsRequestSchema.safeParse(req.body); @@ -303,3 +322,22 @@ export const createPermissionIntegrationRouter = < return router; }; + +function isCreatePermissionIntegrationRouterResourceOptions< + TResourceType extends string, + TResource, +>( + options: CreatePermissionIntegrationRouterOptions, +): options is CreatePermissionIntegrationRouterResourceOptions< + TResourceType, + TResource +> { + return ( + ( + options as CreatePermissionIntegrationRouterResourceOptions< + TResourceType, + TResource + > + ).resourceType !== undefined + ); +}