diff --git a/src/plugins/validation/2and3/semantic-validators/security-definitions-ibm.js b/src/plugins/validation/2and3/semantic-validators/security-definitions-ibm.js index 401fc03c6..b382fd3c0 100644 --- a/src/plugins/validation/2and3/semantic-validators/security-definitions-ibm.js +++ b/src/plugins/validation/2and3/semantic-validators/security-definitions-ibm.js @@ -74,24 +74,25 @@ module.exports.validate = function({ resolvedSpec, isOAS3 }, config) { function flagUsedDefinitions(security) { security.forEach(scheme => { - // each object in this array should only have one key - the name of the scheme - const name = Object.keys(scheme)[0]; - - // make sure this scheme was in the security definitions, then label as used - if (definedSchemes[name]) { - definedSchemes[name].used = true; - - const type = definedSchemes[name].type; - const scopesArray = scheme[name]; - - if (type.toLowerCase() === 'oauth2') { - scopesArray.forEach(scope => { - if (definedScopes[scope]) { - definedScopes[scope].used = true; - } - }); + const schemeNames = Object.keys(scheme); + + schemeNames.forEach(schemeName => { + // make sure this scheme was in the security definitions, then label as used + if (definedSchemes[schemeName]) { + definedSchemes[schemeName].used = true; + + const type = definedSchemes[schemeName].type; + const scopesArray = scheme[schemeName]; + + if (type.toLowerCase() === 'oauth2') { + scopesArray.forEach(scope => { + if (definedScopes[scope]) { + definedScopes[scope].used = true; + } + }); + } } - } + }); }); } diff --git a/src/plugins/validation/2and3/semantic-validators/security-ibm.js b/src/plugins/validation/2and3/semantic-validators/security-ibm.js index 0eab4af1d..1c53c448a 100644 --- a/src/plugins/validation/2and3/semantic-validators/security-ibm.js +++ b/src/plugins/validation/2and3/semantic-validators/security-ibm.js @@ -59,73 +59,62 @@ module.exports.validate = function({ jsSpec, isOAS3 }, config) { function validateSecurityObject({ security, path }) { security.forEach(schemeObject => { - // each object in this array should only have one key - the name of the scheme const schemeNames = Object.keys(schemeObject); - const schemeName = schemeNames[0]; - - // if there is more than one key, they will be ignored. the structural validator should - // catch these but in case the spec changes in later versions of swagger, - // a non-configurable warning should be printed to alert the user - if (schemeNames.length > 1) { - result.warning.push({ - path, - message: - 'The validator expects only 1 key-value pair for each object in a security array.' - }); - } - - const schemeIsDefined = - securityDefinitions && securityDefinitions[schemeName]; - // ensure the security scheme is defined - if (!schemeIsDefined) { - result.error.push({ - path: `${path}.${schemeName}`, - message: 'security requirements must match a security definition' - }); - } else { - const schemeType = securityDefinitions[schemeName].type; - const isNonEmptyArray = schemeObject[schemeName].length > 0; - const schemesWithNonEmptyArrays = isOAS3 - ? ['oauth2', 'openIdConnect'] - : ['oauth2']; - - const isSchemeWithNonEmptyArray = schemesWithNonEmptyArrays.includes( - schemeType - ); - - if (isNonEmptyArray && !isSchemeWithNonEmptyArray) { - const checkStatus = config.invalid_non_empty_security_array; - if (checkStatus !== 'off') { - result[checkStatus].push({ - path: `${path}.${schemeName}`, - message: `For security scheme types other than ${schemesWithNonEmptyArrays.join( - ' or ' - )}, the value must be an empty array.` - }); + schemeNames.forEach(schemeName => { + const schemeIsDefined = + securityDefinitions && securityDefinitions[schemeName]; + + // ensure the security scheme is defined + if (!schemeIsDefined) { + result.error.push({ + path: `${path}.${schemeName}`, + message: 'security requirements must match a security definition' + }); + } else { + const schemeType = securityDefinitions[schemeName].type; + const isNonEmptyArray = schemeObject[schemeName].length > 0; + const schemesWithNonEmptyArrays = isOAS3 + ? ['oauth2', 'openIdConnect'] + : ['oauth2']; + + const isSchemeWithNonEmptyArray = schemesWithNonEmptyArrays.includes( + schemeType + ); + + if (isNonEmptyArray && !isSchemeWithNonEmptyArray) { + const checkStatus = config.invalid_non_empty_security_array; + if (checkStatus !== 'off') { + result[checkStatus].push({ + path: `${path}.${schemeName}`, + message: `For security scheme types other than ${schemesWithNonEmptyArrays.join( + ' or ' + )}, the value must be an empty array.` + }); + } } - } - if (isSchemeWithNonEmptyArray) { - // check for resolution of specific scopes - const scopes = schemeObject[schemeName]; - if (Array.isArray(scopes)) { - // Check for unknown scopes - const securityDefinition = securityDefinitions[schemeName]; - scopes.forEach((scope, i) => { - const scopeIsDefined = isOAS3 - ? checkOAS3Scopes(scope, securityDefinition) - : checkSwagger2Scopes(scope, securityDefinition); - if (!scopeIsDefined) { - result.error.push({ - message: `Definition could not be resolved for security scope: ${scope}`, - path: `${path}.${schemeName}.${i}` - }); - } - }); + if (isSchemeWithNonEmptyArray) { + // check for resolution of specific scopes + const scopes = schemeObject[schemeName]; + if (Array.isArray(scopes)) { + // Check for unknown scopes + const securityDefinition = securityDefinitions[schemeName]; + scopes.forEach((scope, i) => { + const scopeIsDefined = isOAS3 + ? checkOAS3Scopes(scope, securityDefinition) + : checkSwagger2Scopes(scope, securityDefinition); + if (!scopeIsDefined) { + result.error.push({ + message: `Definition could not be resolved for security scope: ${scope}`, + path: `${path}.${schemeName}.${i}` + }); + } + }); + } } } - } + }); }); } diff --git a/test/plugins/validation/2and3/security-definitions-ibm.js b/test/plugins/validation/2and3/security-definitions-ibm.js index 90bab6f48..62d2967fe 100644 --- a/test/plugins/validation/2and3/security-definitions-ibm.js +++ b/test/plugins/validation/2and3/security-definitions-ibm.js @@ -26,7 +26,6 @@ describe('validation plugin - semantic - security-definitions-ibm', function() { } }, api_key: { - // eslint-disable-line camelcase type: 'apiKey', name: 'api_key', in: 'header' @@ -290,5 +289,47 @@ describe('validation plugin - semantic - security-definitions-ibm', function() { expect(res.errors.length).toEqual(0); expect(res.warnings.length).toEqual(0); }); + + it('should not complain if all definitions are used with multiple auth schemes', function() { + const spec = { + components: { + securitySchemes: { + api_key: { + type: 'apiKey', + name: 'api_key', + in: 'header' + }, + api_secret: { + type: 'apiKey', + name: 'api_secret', + in: 'header' + } + } + }, + paths: { + '/': { + get: { + operationId: 'list', + summary: 'list everything', + security: [ + { + api_key: [], + api_secret: [] + } + ], + responses: { + default: { + description: 'default response' + } + } + } + } + } + }; + + const res = validate({ resolvedSpec: spec, isOAS3: true }, config); + expect(res.errors.length).toEqual(0); + expect(res.warnings.length).toEqual(0); + }); }); }); diff --git a/test/plugins/validation/2and3/security-ibm.js b/test/plugins/validation/2and3/security-ibm.js index d9a7553ea..7665f2318 100644 --- a/test/plugins/validation/2and3/security-ibm.js +++ b/test/plugins/validation/2and3/security-ibm.js @@ -499,5 +499,34 @@ describe('validation plugin - semantic - security-ibm', function() { expect(res.errors.length).toEqual(0); expect(res.warnings.length).toEqual(0); }); + + it('should not complain when multiple security requirements are referenced', () => { + const spec = { + components: { + securitySchemes: { + api_key: { + type: 'apiKey', + name: 'api_key', + in: 'header' + }, + api_secret: { + type: 'apiKey', + name: 'api_secret', + in: 'header' + } + } + }, + security: [ + { + api_key: [], + api_secret: [] + } + ] + }; + + const res = validate({ jsSpec: spec, isOAS3: true }, config); + expect(res.errors.length).toEqual(0); + expect(res.warnings.length).toEqual(0); + }); }); });