From 2bacc8be347311f2ffe231b42e349a8ccf8410cb Mon Sep 17 00:00:00 2001 From: Mike Lewis Date: Wed, 24 Nov 2021 16:46:19 +0000 Subject: [PATCH] permission-backend: expand router test suite Signed-off-by: Mike Lewis --- .../src/service/router.test.ts | 222 ++++++++++++++++-- 1 file changed, 205 insertions(+), 17 deletions(-) diff --git a/plugins/permission-backend/src/service/router.test.ts b/plugins/permission-backend/src/service/router.test.ts index 29b5245006..78e7121da6 100644 --- a/plugins/permission-backend/src/service/router.test.ts +++ b/plugins/permission-backend/src/service/router.test.ts @@ -18,15 +18,22 @@ import express from 'express'; import request from 'supertest'; import { getVoidLogger } from '@backstage/backend-common'; import { IdentityClient } from '@backstage/plugin-auth-backend'; -import { - AuthorizeResult, - Permission, -} from '@backstage/plugin-permission-common'; -import { PermissionPolicy } from '@backstage/plugin-permission-node'; +import { AuthorizeResult } from '@backstage/plugin-permission-common'; +import { ApplyConditionsResponse } from '@backstage/plugin-permission-node'; +import { PermissionIntegrationClient } from './PermissionIntegrationClient'; import { createRouter } from './router'; -const policy: PermissionPolicy = { +const mockApplyConditions: jest.MockedFunction< + InstanceType['applyConditions'] +> = jest.fn(); +jest.mock('./PermissionIntegrationClient', () => ({ + PermissionIntegrationClient: jest.fn(() => ({ + applyConditions: mockApplyConditions, + })), +})); + +const policy = { handle: jest.fn().mockImplementation((_req, identity) => { if (identity) { return { result: AuthorizeResult.ALLOW }; @@ -35,11 +42,6 @@ const policy: PermissionPolicy = { }), }; -const permission: Permission = { - name: 'test.permission', - attributes: { action: 'read' }, -}; - describe('createRouter', () => { let app: express.Express; @@ -81,12 +83,47 @@ describe('createRouter', () => { it('calls the permission policy', async () => { const response = await request(app) .post('/authorize') - .send([{ id: 123, permission }]); + .send([ + { + id: '123', + permission: { + name: 'test.permission1', + attributes: {}, + }, + }, + { + id: '234', + permission: { + name: 'test.permission2', + attributes: {}, + }, + }, + ]); expect(response.status).toEqual(200); - expect(policy.handle).toHaveBeenCalledWith({ permission }, undefined); + + expect(policy.handle).toHaveBeenCalledWith( + { + permission: { + name: 'test.permission1', + attributes: {}, + }, + }, + undefined, + ); + expect(policy.handle).toHaveBeenCalledWith( + { + permission: { + name: 'test.permission2', + attributes: {}, + }, + }, + undefined, + ); + expect(response.body).toEqual([ - { id: 123, result: AuthorizeResult.DENY }, + { id: '123', result: AuthorizeResult.DENY }, + { id: '234', result: AuthorizeResult.DENY }, ]); }); @@ -95,16 +132,167 @@ describe('createRouter', () => { const response = await request(app) .post('/authorize') .auth(token, { type: 'bearer' }) - .send([{ id: 123, permission }]); + .send([ + { + id: '123', + permission: { + name: 'test.permission', + attributes: {}, + }, + }, + ]); expect(response.status).toEqual(200); expect(policy.handle).toHaveBeenCalledWith( - { permission }, + { + permission: { + name: 'test.permission', + attributes: {}, + }, + }, { id: 'test-user', token: 'test-token' }, ); expect(response.body).toEqual([ - { id: 123, result: AuthorizeResult.ALLOW }, + { id: '123', result: AuthorizeResult.ALLOW }, ]); }); + + describe('conditional policy result', () => { + beforeEach(() => { + policy.handle.mockReturnValueOnce({ + result: AuthorizeResult.CONDITIONAL, + conditions: { + pluginId: 'test-plugin', + resourceType: 'test-resource-1', + conditions: { + anyOf: [{ rule: 'test-rule', params: ['abc'] }], + }, + }, + }); + }); + + it('returns conditions if no resourceRef is supplied', async () => { + const response = await request(app) + .post('/authorize') + .send([ + { + id: '123', + permission: { + name: 'test.permission', + resourceType: 'test-resource-1', + attributes: {}, + }, + }, + ]); + + expect(response.status).toEqual(200); + expect(response.body).toEqual([ + { + id: '123', + result: AuthorizeResult.CONDITIONAL, + conditions: { anyOf: [{ rule: 'test-rule', params: ['abc'] }] }, + }, + ]); + }); + + it.each([ + AuthorizeResult.ALLOW, + AuthorizeResult.DENY, + ])( + 'applies conditions and returns %s if resourceRef is supplied', + async result => { + mockApplyConditions.mockResolvedValueOnce({ + result, + }); + + const response = await request(app) + .post('/authorize') + .auth('test-token', { type: 'bearer' }) + .send([ + { + id: '123', + resourceRef: 'test/resource', + permission: { + name: 'test.permission', + resourceType: 'test-resource-1', + attributes: {}, + }, + }, + ]); + + expect(mockApplyConditions).toHaveBeenCalledWith( + { + pluginId: 'test-plugin', + resourceType: 'test-resource-1', + resourceRef: 'test/resource', + conditions: { anyOf: [{ rule: 'test-rule', params: ['abc'] }] }, + }, + 'Bearer test-token', + ); + + expect(response.status).toEqual(200); + expect(response.body).toEqual([ + { + id: '123', + result, + }, + ]); + }, + ); + }); + + it.each([ + undefined, + '', + {}, + [{ permission: { name: 'test.permission', attributes: {} } }], + [{ id: '123' }], + [{ id: '123', permission: { name: 'test.permission' } }], + [{ id: '123', permission: { attributes: { invalid: 'attribute' } } }], + ])('returns a 500 error for invalid request %#', async requestBody => { + const response = await request(app).post('/authorize').send(requestBody); + + expect(response.status).toEqual(500); + expect(response.body).toEqual( + expect.objectContaining({ + error: expect.objectContaining({ + message: expect.stringMatching(/invalid/i), + }), + }), + ); + }); + + it('returns a 500 error if the policy returns a different resourceType', async () => { + policy.handle.mockReturnValueOnce({ + result: AuthorizeResult.CONDITIONAL, + conditions: { + pluginId: 'test-plugin', + resourceType: 'test-resource-2', + conditions: {}, + }, + }); + + const response = await request(app) + .post('/authorize') + .send([ + { + id: '123', + permission: { + name: 'test.permission', + resourceType: 'test-resource-1', + attributes: {}, + }, + }, + ]); + + expect(response.status).toEqual(500); + expect(response.body).toEqual( + expect.objectContaining({ + error: expect.objectContaining({ + message: expect.stringMatching(/invalid resource conditions/i), + }), + }), + ); + }); }); });