diff --git a/OPTIONS.md b/OPTIONS.md index 8189ccfba..6b70acac9 100644 --- a/OPTIONS.md +++ b/OPTIONS.md @@ -21,3 +21,4 @@ allowUrlPathVarMatching|boolean|-|false|Whether to allow matching path variables enableOptionalParameters|boolean|-|true|Optional parameters aren't selected in the collection. Once enabled they will be selected in the collection and request as well.|CONVERSION|v2, v1 keepImplicitHeaders|boolean|-|false|Whether to keep implicit headers from the OpenAPI specification, which are removed by default.|CONVERSION|v2, v1 includeDeprecated|boolean|-|true|Select whether to include deprecated operations, parameters, and properties in generated collection or not|CONVERSION, VALIDATION|v2, v1 +alwaysInheritAuthentication|boolean|-|false|Whether authentication details should be included on every request, or always inherited from the collection.|CONVERSION|v2, v1 diff --git a/lib/options.js b/lib/options.js index d43436726..a75da5bc7 100644 --- a/lib/options.js +++ b/lib/options.js @@ -374,6 +374,18 @@ module.exports = { usage: ['CONVERSION', 'VALIDATION'], supportedIn: [VERSION20, VERSION30, VERSION31], supportedModuleVersion: [MODULE_VERSION.V2, MODULE_VERSION.V1] + }, + { + name: 'Always inherit authentication', + id: 'alwaysInheritAuthentication', + type: 'boolean', + default: false, + description: 'Whether authentication details should be included on every request, or always inherited from ' + + 'the collection.', + external: true, + usage: ['CONVERSION'], + supportedIn: [VERSION20, VERSION30, VERSION31], + supportedModuleVersion: [MODULE_VERSION.V2, MODULE_VERSION.V1] } ]; diff --git a/lib/schemaUtils.js b/lib/schemaUtils.js index 0b45efcc5..a7e4ca972 100644 --- a/lib/schemaUtils.js +++ b/lib/schemaUtils.js @@ -2647,7 +2647,9 @@ module.exports = { } // handling authentication here (for http type only) - authHelper = this.getAuthHelper(openapi, operation.security); + if (!options.alwaysInheritAuthentication) { + authHelper = this.getAuthHelper(openapi, operation.security); + } // creating the request object item = new sdk.Item({ diff --git a/libV2/schemaUtils.js b/libV2/schemaUtils.js index ed66390fb..b96146f49 100644 --- a/libV2/schemaUtils.js +++ b/libV2/schemaUtils.js @@ -1866,7 +1866,8 @@ module.exports = { requestBody = resolveRequestBodyForPostmanRequest(context, operationItem[method]), request, securitySchema = _.get(operationItem, [method, 'security']), - authHelper = generateAuthForCollectionFromOpenAPI(context.openapi, securitySchema); + authHelper = generateAuthForCollectionFromOpenAPI(context.openapi, securitySchema), + { alwaysInheritAuthentication } = context.computedOptions; headers.push(..._.get(requestBody, 'headers', [])); pathVariables.push(...baseUrlData.pathVariables); @@ -1885,7 +1886,7 @@ module.exports = { }, headers, body: _.get(requestBody, 'body'), - auth: authHelper + auth: alwaysInheritAuthentication ? undefined : authHelper }; const { responses, acceptHeader } = resolveResponseForPostmanRequest(context, operationItem[method], request); diff --git a/test/data/valid_openapi/security-test-inheritance.yaml b/test/data/valid_openapi/security-test-inheritance.yaml new file mode 100644 index 000000000..191fd945a --- /dev/null +++ b/test/data/valid_openapi/security-test-inheritance.yaml @@ -0,0 +1,48 @@ +openapi: 3.0.0 +info: + title: "Reproduce Authorization issue" + version: 0.0.1 +security: + - MyAuth: [] + - BearerAuth: [] +paths: + /health: + get: + summary: "health" + description: "Health check - always returns OK" + operationId: "get_healthz" + security: + - BearerAuth: [] + responses: + '200': + description: "OK" + content: + text/plain: + schema: + type: "string" + default: "OK" + /status: + get: + summary: "status" + description: "Returns the service version" + operationId: "get_status" + security: + - MyAuth: [] + responses: + '200': + description: "Service info multi-line string" + content: + text/plain: + schema: + type: "string" +components: + securitySchemes: + BearerAuth: + type: http + scheme: bearer + bearerFormat: token + MyAuth: + type: apiKey + description: "This is my auth" + name: Mera-Auth + in: header \ No newline at end of file diff --git a/test/system/structure.test.js b/test/system/structure.test.js index fbb604125..cc49ba486 100644 --- a/test/system/structure.test.js +++ b/test/system/structure.test.js @@ -29,7 +29,8 @@ const optionIds = [ 'includeReferenceMap', 'includeDeprecated', 'parametersResolution', - 'disabledParametersValidation' + 'disabledParametersValidation', + 'alwaysInheritAuthentication' ], expectedOptions = { collapseFolders: { @@ -222,6 +223,15 @@ const optionIds = [ description: 'Whether disabled parameters of collection should be validated', external: false, usage: ['VALIDATION'] + }, + alwaysInheritAuthentication: { + name: 'Always inherit authentication', + type: 'boolean', + default: false, + description: 'Whether authentication details should be included on every request, or always inherited from ' + + 'the collection.', + external: true, + usage: ['CONVERSION'] } }; diff --git a/test/unit/base.test.js b/test/unit/base.test.js index ffd8b8312..f7a2a69b3 100644 --- a/test/unit/base.test.js +++ b/test/unit/base.test.js @@ -46,6 +46,7 @@ describe('CONVERT FUNCTION TESTS ', function() { tooManyRefs = path.join(__dirname, VALID_OPENAPI_PATH, '/too-many-refs.json'), tagsFolderSpec = path.join(__dirname, VALID_OPENAPI_PATH + '/petstore-detailed.yaml'), securityTestCases = path.join(__dirname, VALID_OPENAPI_PATH + '/security-test-cases.yaml'), + securityTestInheritance = path.join(__dirname, VALID_OPENAPI_PATH + '/security-test-inheritance.yaml'), emptySecurityTestCase = path.join(__dirname, VALID_OPENAPI_PATH + '/empty-security-test-case.yaml'), rootUrlServerWithVariables = path.join(__dirname, VALID_OPENAPI_PATH + '/root_url_server_with_variables.json'), parameterExamples = path.join(__dirname, VALID_OPENAPI_PATH + '/parameteres_with_examples.yaml'), @@ -97,6 +98,34 @@ describe('CONVERT FUNCTION TESTS ', function() { path.join(__dirname, VALID_OPENAPI_PATH, '/recursiveRefComponents.yaml'); + it('Should explicitly set auth when specified on a request ' + + securityTestInheritance, function(done) { + var openapi = fs.readFileSync(securityTestInheritance, 'utf8'); + Converter.convert({ type: 'string', data: openapi }, {}, (err, conversionResult) => { + + expect(err).to.be.null; + expect(conversionResult.output[0].data.auth.type).to.equal('apikey'); + expect(conversionResult.output[0].data.item[0].request.auth.type).to.equal('bearer'); + expect(conversionResult.output[0].data.item[1].request.auth.type).to.equal('apikey'); + done(); + }); + }); + + it('Should not explicitly set auth when specified on a request when passed alwaysInheritAuthentication ' + + securityTestInheritance, function(done) { + var openapi = fs.readFileSync(securityTestInheritance, 'utf8'); + Converter.convert( + { type: 'string', data: openapi }, + { alwaysInheritAuthentication: true }, (err, conversionResult) => { + + expect(err).to.be.null; + expect(conversionResult.output[0].data.auth.type).to.equal('apikey'); + expect(conversionResult.output[0].data.item[0].request.auth).to.be.undefined; + expect(conversionResult.output[0].data.item[1].request.auth).to.be.undefined; + done(); + }); + }); + it('Should add collection level auth with type as `bearer`' + securityTestCases, function(done) { var openapi = fs.readFileSync(securityTestCases, 'utf8'), diff --git a/test/unit/convertV2.test.js b/test/unit/convertV2.test.js index 0a3c09cb7..386b8525e 100644 --- a/test/unit/convertV2.test.js +++ b/test/unit/convertV2.test.js @@ -39,6 +39,7 @@ const expect = require('chai').expect, issue193 = path.join(__dirname, VALID_OPENAPI_PATH, '/issue#193.yml'), tooManyRefs = path.join(__dirname, VALID_OPENAPI_PATH, '/too_many_ref_example.json'), securityTestCases = path.join(__dirname, VALID_OPENAPI_PATH + '/security-test-cases.yaml'), + securityTestInheritance = path.join(__dirname, VALID_OPENAPI_PATH + '/security-test-inheritance.yaml'), emptySecurityTestCase = path.join(__dirname, VALID_OPENAPI_PATH + '/empty-security-test-case.yaml'), rootUrlServerWithVariables = path.join(__dirname, VALID_OPENAPI_PATH + '/root_url_server_with_variables.json'), parameterExamples = path.join(__dirname, VALID_OPENAPI_PATH + '/parameteres_with_examples.yaml'), @@ -94,6 +95,37 @@ const expect = require('chai').expect, describe('The convert v2 Function', function() { + it('Should explicitly set auth when specified on a request ' + + securityTestInheritance, function(done) { + var openapi = fs.readFileSync(securityTestInheritance, 'utf8'); + Converter.convertV2({ type: 'string', data: openapi }, {}, (err, conversionResult) => { + + expect(err).to.be.null; + expect(conversionResult.output[0].data.auth.type).to.equal('apikey'); + expect(conversionResult.output[0].data.item[0].item[0].request.auth.type).to.equal('apikey'); + expect(conversionResult.output[0].data.item[1].item[0].request.auth.type).to.equal('bearer'); + done(); + }); + }); + + it('Should not explicitly set auth when specified on a request when passed alwaysInheritAuthentication ' + + securityTestInheritance, function(done) { + const isEmptyArrayOrNull = (value) => { + return Array.isArray(value) && value.length === 0 || value === null; + }; + var openapi = fs.readFileSync(securityTestInheritance, 'utf8'); + Converter.convertV2( + { type: 'string', data: openapi }, + { alwaysInheritAuthentication: true }, (err, conversionResult) => { + + expect(err).to.be.null; + expect(conversionResult.output[0].data.auth.type).to.equal('apikey'); + expect(conversionResult.output[0].data.item[0].item[0].request.auth).to.satisfy(isEmptyArrayOrNull); + expect(conversionResult.output[0].data.item[1].item[0].request.auth).to.satisfy(isEmptyArrayOrNull); + done(); + }); + }); + it('Should add collection level auth with type as `bearer`' + securityTestCases, function(done) { var openapi = fs.readFileSync(securityTestCases, 'utf8'),