From dbd89112a581053599927864ae3fd1d04db18b5c Mon Sep 17 00:00:00 2001 From: Mike Lewis Date: Mon, 22 Nov 2021 17:36:21 +0000 Subject: [PATCH] permission-backend: add permission-node integration tests Signed-off-by: Mike Lewis --- .../PermissionIntegrationClient.test.ts | 175 ++++++++++++++---- 1 file changed, 144 insertions(+), 31 deletions(-) diff --git a/plugins/permission-backend/src/service/PermissionIntegrationClient.test.ts b/plugins/permission-backend/src/service/PermissionIntegrationClient.test.ts index 325ee7c21c..8e3e85b5af 100644 --- a/plugins/permission-backend/src/service/PermissionIntegrationClient.test.ts +++ b/plugins/permission-backend/src/service/PermissionIntegrationClient.test.ts @@ -14,50 +14,54 @@ * limitations under the License. */ +import { AddressInfo } from 'net'; +import { Server } from 'http'; +import express, { Router } from 'express'; import { RestContext, rest } from 'msw'; -import { setupServer } from 'msw/node'; +import { setupServer, SetupServerApi } from 'msw/node'; import { PluginEndpointDiscovery } from '@backstage/backend-common'; import { AuthorizeResult } from '@backstage/plugin-permission-common'; +import { createPermissionIntegrationRouter } from '@backstage/plugin-permission-node'; import { PermissionIntegrationClient } from './PermissionIntegrationClient'; -const server = setupServer(); - -const mockBaseUrl = 'http://backstage:9191/i-am-a-mock-base'; -const discovery: PluginEndpointDiscovery = { - async getBaseUrl() { - return mockBaseUrl; - }, - async getExternalBaseUrl() { - throw new Error('Not implemented.'); - }, -}; - -const client: PermissionIntegrationClient = new PermissionIntegrationClient({ - discovery, -}); - -const mockConditions = { - not: { - allOf: [ - { rule: 'RULE_1', params: [] }, - { rule: 'RULE_2', params: ['abc'] }, - ], - }, -}; - describe('PermissionIntegrationClient', () => { - beforeAll(() => server.listen({ onUnhandledRequest: 'error' })); - afterAll(() => server.close()); - afterEach(() => server.resetHandlers()); - describe('applyConditions', () => { + let server: SetupServerApi; + + const mockConditions = { + not: { + allOf: [ + { rule: 'RULE_1', params: [] }, + { rule: 'RULE_2', params: ['abc'] }, + ], + }, + }; + const mockApplyConditionsHandler = jest.fn( (_req, res, { json }: RestContext) => { return res(json({ result: AuthorizeResult.ALLOW })); }, ); - beforeEach(() => { + const mockBaseUrl = 'http://backstage:9191/i-am-a-mock-base'; + const discovery: PluginEndpointDiscovery = { + async getBaseUrl() { + return mockBaseUrl; + }, + async getExternalBaseUrl() { + throw new Error('Not implemented.'); + }, + }; + + const client: PermissionIntegrationClient = new PermissionIntegrationClient( + { + discovery, + }, + ); + + beforeAll(() => { + server = setupServer(); + server.listen({ onUnhandledRequest: 'error' }); server.use( rest.post( `${mockBaseUrl}/permissions/apply-conditions`, @@ -66,6 +70,8 @@ describe('PermissionIntegrationClient', () => { ); }); + afterAll(() => server.close()); + afterEach(() => { jest.clearAllMocks(); }); @@ -176,4 +182,111 @@ describe('PermissionIntegrationClient', () => { ).rejects.toThrowError(/invalid input/i); }); }); + + describe('integration with @backstage/plugin-permission-node', () => { + let server: Server; + let client: PermissionIntegrationClient; + + beforeAll(async () => { + const router = Router(); + + router.use( + createPermissionIntegrationRouter({ + resourceType: 'test-resource', + getResource: async resourceRef => ({ id: resourceRef }), + rules: [ + { + name: 'RULE_1', + description: 'Test rule 1', + apply: (_resource: any, input: 'yes' | 'no') => input === 'yes', + toQuery: () => { + throw new Error('Not implemented'); + }, + }, + { + name: 'RULE_2', + description: 'Test rule 2', + apply: (_resource: any, input: 'yes' | 'no') => input === 'yes', + toQuery: () => { + throw new Error('Not implemented'); + }, + }, + ], + }), + ); + + const app = express(); + + app.use('/test-plugin', router); + + await new Promise(resolve => { + server = app.listen(resolve); + }); + + const discovery: PluginEndpointDiscovery = { + async getBaseUrl(pluginId: string) { + const listenPort = (server.address()! as AddressInfo).port; + + return `http://0.0.0.0:${listenPort}/${pluginId}`; + }, + async getExternalBaseUrl() { + throw new Error('Not implemented.'); + }, + }; + + client = new PermissionIntegrationClient({ + discovery, + }); + }); + + afterAll( + async () => + new Promise((resolve, reject) => + server.close(err => (err ? reject(err) : resolve())), + ), + ); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('works for simple conditions', async () => { + await expect( + client.applyConditions({ + pluginId: 'test-plugin', + resourceRef: 'testResource1', + resourceType: 'test-resource', + conditions: { rule: 'RULE_1', params: ['no'] }, + }), + ).resolves.toEqual({ result: AuthorizeResult.DENY }); + }); + + it('works for complex criteria', async () => { + await expect( + client.applyConditions({ + pluginId: 'test-plugin', + resourceRef: 'testResource1', + resourceType: 'test-resource', + conditions: { + allOf: [ + { + allOf: [ + { rule: 'RULE_1', params: ['yes'] }, + { not: { rule: 'RULE_2', params: ['no'] } }, + ], + }, + { + not: { + allOf: [ + { rule: 'RULE_1', params: ['no'] }, + { rule: 'RULE_2', params: ['yes'] }, + ], + }, + }, + ], + }, + }), + ).resolves.toEqual({ result: AuthorizeResult.ALLOW }); + }); + }); });