From d23c1ab3d591a0c37b98cbe397b09f950e4d0c41 Mon Sep 17 00:00:00 2001 From: Sahil Date: Mon, 29 May 2023 14:41:19 +0530 Subject: [PATCH 1/5] Added alwaysInheritAuthention option to v2 --- OPTIONS.md | 1 + lib/options.js | 12 +++++ lib/schemaUtils.js | 9 +++- libV2/schemaUtils.js | 5 +- .../security-test-inheritance.yaml | 48 +++++++++++++++++++ test/system/structure.test.js | 12 ++++- test/unit/base.test.js | 29 +++++++++++ test/unit/convertV2.test.js | 29 +++++++++++ 8 files changed, 140 insertions(+), 5 deletions(-) create mode 100644 test/data/valid_openapi/security-test-inheritance.yaml diff --git a/OPTIONS.md b/OPTIONS.md index 8189ccfba..ed4579157 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 \ No newline at end of file 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..e0dd26f78 100644 --- a/lib/schemaUtils.js +++ b/lib/schemaUtils.js @@ -2647,7 +2647,12 @@ module.exports = { } // handling authentication here (for http type only) - authHelper = this.getAuthHelper(openapi, operation.security); + if (options.alwaysInheritAuthentication) { + authHelper = this.getAuthHelper(openapi, openapi.security); + } + else { + authHelper = this.getAuthHelper(openapi, operation.security); + } // creating the request object item = new sdk.Item({ @@ -2670,7 +2675,7 @@ module.exports = { thisAuthObject[authMap[authMeta.currentHelper]] = authMeta.helperAttributes; item.request.auth = new sdk.RequestAuth(thisAuthObject); } - else { + else if (!options.alwaysInheritAuthentication) { item.request.auth = authHelper; } 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..62fac0290 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,34 @@ 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) { + 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.be.empty; + expect(conversionResult.output[0].data.item[1].item[0].request.auth).to.be.empty; + done(); + }); + }); + it('Should add collection level auth with type as `bearer`' + securityTestCases, function(done) { var openapi = fs.readFileSync(securityTestCases, 'utf8'), From ebd46d806d28b0297073ace54bb45aaff3c6433f Mon Sep 17 00:00:00 2001 From: Sahil Date: Mon, 29 May 2023 14:52:38 +0530 Subject: [PATCH 2/5] Added newline to fix failing test --- OPTIONS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OPTIONS.md b/OPTIONS.md index ed4579157..6b70acac9 100644 --- a/OPTIONS.md +++ b/OPTIONS.md @@ -21,4 +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 \ No newline at end of file +alwaysInheritAuthentication|boolean|-|false|Whether authentication details should be included on every request, or always inherited from the collection.|CONVERSION|v2, v1 From a942449c69f43d1c2ab9650f78c2643e0c76951c Mon Sep 17 00:00:00 2001 From: Sahil Date: Fri, 2 Jun 2023 14:38:36 +0530 Subject: [PATCH 3/5] set value of authHelper only if alwaysInheritAuthentication is falsy --- lib/schemaUtils.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/schemaUtils.js b/lib/schemaUtils.js index e0dd26f78..45899cb13 100644 --- a/lib/schemaUtils.js +++ b/lib/schemaUtils.js @@ -2647,10 +2647,7 @@ module.exports = { } // handling authentication here (for http type only) - if (options.alwaysInheritAuthentication) { - authHelper = this.getAuthHelper(openapi, openapi.security); - } - else { + if (!options.alwaysInheritAuthentication) { authHelper = this.getAuthHelper(openapi, operation.security); } From 57287574106a720d5650a18411a90402e28d4c19 Mon Sep 17 00:00:00 2001 From: Sahil Date: Tue, 6 Jun 2023 13:48:11 +0530 Subject: [PATCH 4/5] fixed failing tests --- test/unit/convertV2.test.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/unit/convertV2.test.js b/test/unit/convertV2.test.js index 62fac0290..386b8525e 100644 --- a/test/unit/convertV2.test.js +++ b/test/unit/convertV2.test.js @@ -110,6 +110,9 @@ describe('The convert v2 Function', function() { 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 }, @@ -117,8 +120,8 @@ describe('The convert v2 Function', function() { 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.be.empty; - expect(conversionResult.output[0].data.item[1].item[0].request.auth).to.be.empty; + 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(); }); }); From 2879de27b35c07844e926968bf92b4a1bad9b116 Mon Sep 17 00:00:00 2001 From: Sahil Date: Tue, 6 Jun 2023 18:28:07 +0530 Subject: [PATCH 5/5] Addressed comments --- lib/schemaUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/schemaUtils.js b/lib/schemaUtils.js index 45899cb13..a7e4ca972 100644 --- a/lib/schemaUtils.js +++ b/lib/schemaUtils.js @@ -2672,7 +2672,7 @@ module.exports = { thisAuthObject[authMap[authMeta.currentHelper]] = authMeta.helperAttributes; item.request.auth = new sdk.RequestAuth(thisAuthObject); } - else if (!options.alwaysInheritAuthentication) { + else { item.request.auth = authHelper; }