UNPKG

openapi-middleware

Version:

OpenAPI middleware for common api frameworks (pre-release version!)

225 lines (214 loc) 8.14 kB
import { strict as assert } from 'node:assert'; import sinon from 'sinon'; import SecurityValidator from '../../lib/SecurityValidator.js'; import SecurityError from '../../lib/errors/SecurityError.js'; describe('unit: SecurityValidator', () => { let securityDefinition; let endpointSecurityDefinition; it('should throw on corrupted definitions', () => { securityDefinition = { emptyObj: {}, basicAuth: { type: 'failure' }, apiKeyInQuery: { type: 'apiKey', in: 'failure', name: 123 }, apiKeyInHeader: { type: 'apiKey', in: 'failure', name: 123 }, apiKeyInvalid: { type: 'apiKey' }, http: { type: 'http', scheme: 'failure', bearerFormat: 123 }, httpInvalid: { type: 'http' }, oauth2: { type: 'oauth2' }, openIdConnect: { type: 'openIdConnect' }, }; endpointSecurityDefinition = [ { emptyObj: [] }, { basicAuth: [] }, { apiKeyInQuery: [] }, { apiKeyInHeader: [] }, { apiKeyInvalid: [] }, { http: [] }, { httpInvalid: [] }, { oauth2: [] }, { openIdConnect: [] }, ]; assert.throws(() => new SecurityValidator(securityDefinition, endpointSecurityDefinition, { emptyObj: 'string' }), new SecurityError('security definition was invalid', [ { code: 'optional', message: 'apiKeyInvalid is of type apiKey but missing in / name definition', property: '@.securitySchemes', reason: 'unknown', }, { code: 'optional', message: 'httpInvalid is of type http but missing scheme definition', property: '@.securitySchemes', reason: 'unknown', }, { code: null, message: 'is missing and not optional', property: '@.securitySchemes.emptyObj.type', reason: 'optional', }, { code: null, message: 'must be equal to ["http" or "apiKey"], but is equal to "failure"', property: '@.securitySchemes.basicAuth.type', reason: 'eq', }, { code: null, message: 'must be equal to ["header" or "query"], but is equal to "failure"', property: '@.securitySchemes.apiKeyInQuery.in', reason: 'eq', }, { code: null, message: 'must be string, but is number', property: '@.securitySchemes.apiKeyInQuery.name', reason: 'type', }, { code: null, message: 'must be equal to ["header" or "query"], but is equal to "failure"', property: '@.securitySchemes.apiKeyInHeader.in', reason: 'eq', }, { code: null, message: 'must be string, but is number', property: '@.securitySchemes.apiKeyInHeader.name', reason: 'type', }, { code: null, message: 'must be equal to ["bearer" or "basic"], but is equal to "failure"', property: '@.securitySchemes.http.scheme', reason: 'eq', }, { code: null, message: 'must be string, but is number', property: '@.securitySchemes.http.bearerFormat', reason: 'type', }, { code: null, message: 'must be equal to ["http" or "apiKey"], but is equal to "oauth2"', property: '@.securitySchemes.oauth2.type', reason: 'eq', }, { code: null, message: 'must be equal to ["http" or "apiKey"], but is equal to "openIdConnect"', property: '@.securitySchemes.openIdConnect.type', reason: 'eq', }, { code: 'optional', message: 'all securitySchemes must have correlating callbacks', property: '@.securityCallbacks', reason: 'unknown', }, { code: null, message: 'must be function, but is string', property: '@.securityCallbacks.emptyObj', reason: 'type', }, ])); }); describe('should succeed security definition validation', () => { let securityCallbacks; before(() => { securityDefinition = { basicAuth: { type: 'http', scheme: 'basic' }, apiKeyInQuery: { type: 'apiKey', in: 'query', name: 'authorizationQuery' }, apiKeyInHeader: { type: 'apiKey', in: 'header', name: 'authorizationHeader' }, http: { type: 'http', scheme: 'bearer', bearerFormat: 'somedoc' }, }; securityCallbacks = { basicAuth: sinon.fake.rejects(new Error('basic auth failed')), apiKeyInQuery: sinon.fake.resolves(), apiKeyInHeader: sinon.fake.resolves(), http: sinon.fake.resolves(), }; endpointSecurityDefinition = [ { basicAuth: [] }, { apiKeyInQuery: [] }, { apiKeyInHeader: [] }, { http: [] }, ]; }); it('should be able to validate definitions', () => { assert.ok(new SecurityValidator(securityDefinition, endpointSecurityDefinition, securityCallbacks)); }); it('should fail to call callback', async () => { const headers = { }; const query = {}; const validator = new SecurityValidator(securityDefinition, endpointSecurityDefinition, securityCallbacks); await assert.rejects(() => validator.test(headers, query), new SecurityError('missing security parameters from request', [ { code: null, reason: 'optional', message: 'is missing and not optional', property: '@.header.authorization', }, { code: null, reason: 'optional', message: 'is missing and not optional', property: '@.header.authorizationHeader', }, { code: null, reason: 'optional', message: 'is missing and not optional', property: '@.query.authorizationQuery', }, ])); }); it('should fail to test all callbacks', async () => { const headers = { authorization: 'test basic', authorizationHeader: 'test header' }; const query = { authorizationQuery: 'test query' }; const errors = [ new Error('basicAuth auth failed'), new Error('apiKeyInQuery auth failed'), new Error('apiKeyInHeader auth failed'), new Error('http auth failed'), ]; securityCallbacks = { basicAuth: sinon.fake.rejects(errors[0]), apiKeyInQuery: sinon.fake.rejects(errors[1]), apiKeyInHeader: sinon.fake.rejects(errors[2]), http: sinon.fake.rejects(errors[3]), }; const validator = new SecurityValidator(securityDefinition, endpointSecurityDefinition, securityCallbacks); await assert.rejects( () => validator.test(headers, query), new SecurityError('unauthorized', errors), ); sinon.assert.calledOnce(securityCallbacks.basicAuth); sinon.assert.calledOnce(securityCallbacks.apiKeyInQuery); sinon.assert.calledOnce(securityCallbacks.apiKeyInHeader); sinon.assert.calledOnce(securityCallbacks.http); sinon.assert.calledWith(securityCallbacks.basicAuth, 'test basic'); sinon.assert.calledWith(securityCallbacks.apiKeyInQuery, 'test query'); sinon.assert.calledWith(securityCallbacks.apiKeyInHeader, 'test header'); sinon.assert.calledWith(securityCallbacks.http, 'test basic'); }); it('should succeed in test of second callback', async () => { const headers = { authorization: 'test basic', authorizationHeader: 'test header' }; const query = { authorizationQuery: 'test query' }; securityCallbacks = { basicAuth: sinon.fake.rejects(new Error('basicAuth auth failed')), apiKeyInQuery: sinon.fake.resolves(), apiKeyInHeader: sinon.fake.resolves(), http: sinon.fake.resolves(), }; const validator = new SecurityValidator(securityDefinition, endpointSecurityDefinition, securityCallbacks); await validator.test(headers, query); sinon.assert.calledOnce(securityCallbacks.basicAuth); sinon.assert.calledOnce(securityCallbacks.apiKeyInQuery); sinon.assert.notCalled(securityCallbacks.apiKeyInHeader); sinon.assert.notCalled(securityCallbacks.http); }); }); });