Leverage security definitions for headers in example requests

This commit is contained in:
domaindrivendev
2017-02-15 15:38:03 -05:00
parent e40c5fc26e
commit de7ec5f15d
11 changed files with 209 additions and 34 deletions

View File

@@ -14,7 +14,7 @@ module Rswag
api_metadata[:operation][:verb],
factory.build_fullpath(self),
factory.build_body(self),
factory.build_headers(self)
rackify_headers(factory.build_headers(self)) # Rails test infrastructure requires Rack headers
)
else
send(
@@ -36,6 +36,22 @@ module Rswag
private
def rackify_headers(headers)
name_value_pairs = headers.map do |name, value|
[
case name
when 'Accept' then 'HTTP_ACCEPT'
when 'Content-Type' then 'CONTENT_TYPE'
when 'Authorization' then 'HTTP_AUTHORIZATION'
else name
end,
value
]
end
Hash[ name_value_pairs ]
end
def rswag_config
::Rswag::Specs.config
end

View File

@@ -32,13 +32,20 @@ module Rswag
end
def build_headers(example)
headers = Hash[ parameters_in(:header).map { |p| [ p[:name], example.send(p[:name]).to_s ] } ]
headers.tap do |h|
produces = @api_metadata[:operation][:produces] || @global_metadata[:produces]
consumes = @api_metadata[:operation][:consumes] || @global_metadata[:consumes]
h['ACCEPT'] = produces.join(';') unless produces.nil?
h['CONTENT_TYPE'] = consumes.join(';') unless consumes.nil?
name_value_pairs = parameters_in(:header).map do |param|
[
param[:name],
example.send(param[:name]).to_s
]
end
# Add MIME type headers based on produces/consumes metadata
produces = @api_metadata[:operation][:produces] || @global_metadata[:produces]
consumes = @api_metadata[:operation][:consumes] || @global_metadata[:consumes]
name_value_pairs << [ 'Accept', produces.join(';') ] unless produces.nil?
name_value_pairs << [ 'Content-Type', consumes.join(';') ] unless consumes.nil?
Hash[ name_value_pairs ]
end
private
@@ -52,42 +59,48 @@ module Rswag
applicable_params
.map { |p| p['$ref'] ? resolve_parameter(p['$ref']) : p } # resolve any references
.concat(resolve_api_key_parameters)
.concat(security_parameters)
.select { |p| p[:in] == location }
end
def resolve_parameter(ref)
defined_params = @global_metadata[:parameters]
defined_params = @global_metadata[:parameters]
key = ref.sub('#/parameters/', '')
raise "Referenced parameter '#{ref}' must be defined" unless defined_params && defined_params[key]
defined_params[key]
end
def resolve_api_key_parameters
@api_key_params ||= begin
# First figure out the security requirement applicable to the operation
global_requirements = (@global_metadata[:security] || [] ).map { |r| r.keys.first }
operation_requirements = (@api_metadata[:operation][:security] || [] ).map { |r| r.keys.first }
requirements = global_requirements | operation_requirements
# Then obtain the scheme definitions for those requirements
definitions = (@global_metadata[:securityDefinitions] || {}).slice(*requirements)
definitions.values.select { |d| d[:type] == :apiKey }
def security_parameters
applicable_security_schemes.map do |scheme|
if scheme[:type] == :apiKey
{ name: scheme[:name], type: :string, in: scheme[:in] }
else
{ name: 'Authorization', type: :string, in: :header } # use auth header for basic & oauth2
end
end
end
def applicable_security_schemes
# First figure out the security requirement applicable to the operation
requirements = @api_metadata[:operation][:security] || @global_metadata[:security]
scheme_names = requirements ? requirements.map { |r| r.keys.first } : []
# Then obtain the scheme definitions for those requirements
(@global_metadata[:securityDefinitions] || {}).slice(*scheme_names).values
end
def build_query_string_part(param, value)
return "#{param[:name]}=#{value.to_s}" unless param[:type].to_sym == :array
name = param[:name]
case param[:collectionFormat]
when :ssv
when :ssv
"#{name}=#{value.join(' ')}"
when :tsv
when :tsv
"#{name}=#{value.join('\t')}"
when :pipes
when :pipes
"#{name}=#{value.join('|')}"
when :multi
when :multi
value.map { |v| "#{name}=#{v}" }.join('&')
else
"#{name}=#{value.join(',')}" # csv is default

View File

@@ -193,6 +193,33 @@ module Rswag
end
end
context "global definition for 'basic auth'" do
before do
global_metadata[:securityDefinitions] = { basic_auth: { type: :basic} }
allow(example).to receive(:'Authorization').and_return('Basic foobar')
end
context 'global requirement' do
before { global_metadata[:security] = [ { basic_auth: [] } ] }
it "includes a corresponding Authorization header" do
expect(headers).to match(
'Authorization' => 'Basic foobar'
)
end
end
context 'operation-specific requirement' do
before { api_metadata[:operation][:security] = [ { basic_auth: [] } ] }
it "includes a corresponding Authorization header" do
expect(headers).to match(
'Authorization' => 'Basic foobar'
)
end
end
end
context 'consumes & produces' do
before do
api_metadata[:operation][:consumes] = [ 'application/json', 'application/xml' ]
@@ -201,8 +228,8 @@ module Rswag
it "includes corresponding 'Accept' & 'Content-Type' headers" do
expect(headers).to match(
'ACCEPT' => 'application/json;application/xml',
'CONTENT_TYPE' => 'application/json;application/xml'
'Accept' => 'application/json;application/xml',
'Content-Type' => 'application/json;application/xml'
)
end
end
@@ -217,8 +244,8 @@ module Rswag
it "includes corresponding 'Accept' & 'Content-Type' headers" do
expect(headers).to match(
'ACCEPT' => 'application/json;application/xml',
'CONTENT_TYPE' => 'application/json;application/xml'
'Accept' => 'application/json;application/xml',
'Content-Type' => 'application/json;application/xml'
)
end
end