diff --git a/lib/oauth/request_proxy/base.rb b/lib/oauth/request_proxy/base.rb index a8411cb3..3cc628b9 100644 --- a/lib/oauth/request_proxy/base.rb +++ b/lib/oauth/request_proxy/base.rb @@ -23,15 +23,15 @@ def initialize(request, options = {}) ## OAuth parameters def oauth_callback - parameters["oauth_callback"] + [parameters["oauth_callback"]].flatten.first end def oauth_consumer_key - parameters["oauth_consumer_key"] + [parameters["oauth_consumer_key"]].flatten.first end def oauth_nonce - parameters["oauth_nonce"] + [parameters["oauth_nonce"]].flatten.first end def oauth_signature @@ -40,31 +40,26 @@ def oauth_signature end def oauth_signature_method - case parameters["oauth_signature_method"] - when Array - parameters["oauth_signature_method"].first - else - parameters["oauth_signature_method"] - end + [parameters["oauth_signature_method"]].flatten.first end def oauth_timestamp - parameters["oauth_timestamp"] + [parameters["oauth_timestamp"]].flatten.first end def oauth_token - parameters["oauth_token"] + [parameters["oauth_token"]].flatten.first end # OAuth 1.0a only: value returned to the Consumer after user authorization # and required when exchanging a Request Token for an Access Token. # Not present in OAuth 1.0 flows. def oauth_verifier - parameters["oauth_verifier"] + [parameters["oauth_verifier"]].flatten.first end def oauth_version - parameters["oauth_version"] + [parameters["oauth_version"]].flatten.first end # TODO: deprecate these diff --git a/lib/oauth/request_proxy/rack_request.rb b/lib/oauth/request_proxy/rack_request.rb index f006fc4a..f35c1f42 100644 --- a/lib/oauth/request_proxy/rack_request.rb +++ b/lib/oauth/request_proxy/rack_request.rb @@ -26,10 +26,6 @@ def parameters end end - def signature - parameters["oauth_signature"] - end - protected def query_params diff --git a/spec/oauth/request_proxy/base_accessors_spec.rb b/spec/oauth/request_proxy/base_accessors_spec.rb new file mode 100644 index 00000000..f01f12b8 --- /dev/null +++ b/spec/oauth/request_proxy/base_accessors_spec.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +require "oauth/request_proxy/base" + +RSpec.describe OAuth::RequestProxy::Base do + # Subclasses that use wrap_values (e.g. ActionControllerRequest, + # ActionDispatchRequest) store all parameters as arrays. These specs + # verify that every oauth_* accessor safely unwraps array values so + # callers always receive a scalar string (or nil), regardless of whether + # the parameter arrived as a scalar or a wrapped array. + let(:proxy_class) do + Class.new(OAuth::RequestProxy::Base) do + attr_writer :params + + def parameters + @params || {} + end + end + end + + def proxy_with(params) + proxy_class.new(Object.new).tap { |p| p.params = params } + end + + shared_examples "scalar accessor" do |method, key| + it "returns a scalar when the parameter is stored as a single-element array" do + p = proxy_with(key => ["value"]) + expect(p.public_send(method)).to eq("value") + end + + it "returns a scalar when the parameter is stored as a plain string" do + p = proxy_with(key => "value") + expect(p.public_send(method)).to eq("value") + end + + it "returns nil when the parameter is absent" do + p = proxy_with({}) + expect(p.public_send(method)).to be_nil + end + end + + include_examples "scalar accessor", :oauth_consumer_key, "oauth_consumer_key" + include_examples "scalar accessor", :oauth_nonce, "oauth_nonce" + include_examples "scalar accessor", :oauth_timestamp, "oauth_timestamp" + include_examples "scalar accessor", :oauth_token, "oauth_token" + include_examples "scalar accessor", :oauth_signature_method,"oauth_signature_method" + include_examples "scalar accessor", :oauth_callback, "oauth_callback" + include_examples "scalar accessor", :oauth_verifier, "oauth_verifier" + include_examples "scalar accessor", :oauth_version, "oauth_version" + + describe "#oauth_signature" do + it "returns a scalar when the parameter is stored as a single-element array" do + p = proxy_with("oauth_signature" => ["sig"]) + expect(p.oauth_signature).to eq("sig") + end + + it "returns a scalar when the parameter is stored as a plain string" do + p = proxy_with("oauth_signature" => "sig") + expect(p.oauth_signature).to eq("sig") + end + + it "returns an empty string when the parameter is absent" do + p = proxy_with({}) + expect(p.oauth_signature).to eq("") + end + end +end diff --git a/spec/oauth/request_proxy/rack_spec.rb b/spec/oauth/request_proxy/rack_spec.rb index 26ef2cf4..81009318 100644 --- a/spec/oauth/request_proxy/rack_spec.rb +++ b/spec/oauth/request_proxy/rack_spec.rb @@ -23,6 +23,23 @@ def app expect(proxy.parameters).to include("a" => "1", "b" => "2") end + describe "#signature" do + it "returns a scalar when oauth_signature is a plain string" do + proxy = OAuth::RequestProxy::RackRequest.new(Object.new, clobber_request: true, parameters: {"oauth_signature" => "sig"}) + expect(proxy.signature).to eq("sig") + end + + it "returns a scalar when oauth_signature is a single-element array" do + proxy = OAuth::RequestProxy::RackRequest.new(Object.new, clobber_request: true, parameters: {"oauth_signature" => ["sig"]}) + expect(proxy.signature).to eq("sig") + end + + it "returns an empty string when oauth_signature is absent" do + proxy = OAuth::RequestProxy::RackRequest.new(Object.new, clobber_request: true, parameters: {}) + expect(proxy.signature).to eq("") + end + end + it "proxies Rack::Request POST form params" do post "/test", {"x" => "9", "y" => "10"} rack_req = last_request