# encoding: utf-8
proc { |p| $:.unshift(p) unless $:.any? { |lp| File.expand_path(lp) == p } }.call(File.expand_path('.', File.dirname(__FILE__)))
require 'helper'
# config methods for testing OAuthenticator. simple
module OAuthenticatorTestConfigMethods
class << self
# a set of nonces
define_method(:nonces) { @nonces ||= Set.new }
# a Hash keyed by consumer keys with values of consumer secrets
define_method(:consumer_secrets) { @consumer_secrets ||= {} }
# a Hash keyed by access tokens with values of access token secrets
define_method(:access_token_secrets) { @access_token_secrets ||= {} }
# a Hash keyed by access tokens with values of consumer keys
define_method(:access_token_consumers) { @access_token_consumers ||= {} }
end
def nonce_used?
OAuthenticatorTestConfigMethods.nonces.include?(oauth_header_params[:nonce])
end
def use_nonce!
OAuthenticatorTestConfigMethods.nonces << oauth_header_params[:nonce]
end
def timestamp_valid_period
10
end
def allowed_signature_methods
%w(HMAC-SHA1 RSA-SHA1 PLAINTEXT)
end
def consumer_secret
OAuthenticatorTestConfigMethods.consumer_secrets[oauth_header_params[:consumer_key]]
end
def access_token_secret
OAuthenticatorTestConfigMethods.access_token_secrets[oauth_header_params[:token]]
end
def access_token_belongs_to_consumer?
OAuthenticatorTestConfigMethods.access_token_consumers[oauth_header_params[:token]] == oauth_header_params[:consumer_key]
end
end
describe OAuthenticator::Middleware do
# act like a database cleaner
after do
[:nonces, :consumer_secrets, :access_token_secrets, :access_token_consumers].each do |db|
OAuthenticatorTestConfigMethods.send(db).clear
end
Timecop.return
end
let(:simpleapp) { proc { |env| [200, {}, ['☺']] } }
let(:oapp) { OAuthenticator::Middleware.new(simpleapp, :config_methods => OAuthenticatorTestConfigMethods) }
let(:consumer) do
{:key => "test_client_app_key", :secret => "test_client_app_secret"}.tap do |consumer|
OAuthenticatorTestConfigMethods.consumer_secrets[consumer[:key]] = consumer[:secret]
end
end
let(:consumer_key) { consumer[:key] }
let(:consumer_secret) { consumer[:secret] }
let(:access_token_hash) do
{:token => 'test_access_token', :secret => 'test_access_token_secret', :consumer_key => consumer_key}.tap do |hash|
OAuthenticatorTestConfigMethods.access_token_secrets[hash[:token]] = hash[:secret]
OAuthenticatorTestConfigMethods.access_token_consumers[hash[:token]] = hash[:consumer_key]
end
end
let(:access_token) { access_token_hash[:token] }
let(:access_token_secret) { access_token_hash[:secret] }
def assert_response(expected_status, expected_body, actual_status, actual_headers, actual_body)
actual_body_s = actual_body.to_enum.to_a.join
assert_equal expected_status.to_i, actual_status.to_i, "Expected status to be #{expected_status.inspect}" +
"; got #{actual_status.inspect}. body was: #{actual_body_s}"
assert expected_body === actual_body_s, "Expected match for #{expected_body}; got #{actual_body_s}"
end
it 'makes a valid two-legged signed request (generated)' do
request = Rack::Request.new(Rack::MockRequest.env_for('/', :method => 'GET'))
request.env['HTTP_AUTHORIZATION'] = SimpleOAuth::Header.new(
request.request_method,
request.url,
nil,
{:consumer_key => consumer_key, :consumer_secret => consumer_secret}
).to_s
assert_response(200, '☺', *oapp.call(request.env))
end
it 'makes a valid two-legged signed request with a blank access token (generated)' do
request = Rack::Request.new(Rack::MockRequest.env_for('/', :method => 'GET'))
request.env['HTTP_AUTHORIZATION'] = SimpleOAuth::Header.new(
request.request_method,
request.url,
nil,
{ :consumer_key => consumer_key,
:consumer_secret => consumer_secret,
:token => '',
:token_secret => '',
}
).to_s
assert_response(200, '☺', *oapp.call(request.env))
end
it 'makes a valid two-legged signed request with a form encoded body (generated)' do
request = Rack::Request.new(Rack::MockRequest.env_for('/',
:method => 'GET',
:input => 'a=b&a=c',
'CONTENT_TYPE' => 'application/x-www-form-urlencoded; charset=UTF8',
))
request.env['HTTP_AUTHORIZATION'] = SimpleOAuth::Header.new(
request.request_method,
request.url,
[['a', 'b'], ['a', 'c']],
{:consumer_key => consumer_key, :consumer_secret => consumer_secret}
).to_s
assert_response(200, '☺', *oapp.call(request.env))
end
it 'makes a valid three-legged signed request (generated)' do
request = Rack::Request.new(Rack::MockRequest.env_for('/', :method => 'GET'))
request.env['HTTP_AUTHORIZATION'] = SimpleOAuth::Header.new(
request.request_method,
request.url,
nil,
{ :consumer_key => consumer_key,
:consumer_secret => consumer_secret,
:token => access_token,
:token_secret => access_token_secret,
}
).to_s
assert_response(200, '☺', *oapp.call(request.env))
end
2.times do |i|
# run these twice to make sure that the databas cleaner clears out the nonce since we use the same
# nonce across tests
it "makes a valid signed two-legged request (static #{i})" do
Timecop.travel Time.at 1391021695
consumer # cause this to be created
request = Rack::Request.new(Rack::MockRequest.env_for('/', :method => 'GET'))
request.env['HTTP_AUTHORIZATION'] = %q(OAuth oauth_consumer_key="test_client_app_key", ) +
%q(oauth_nonce="c1c2bd8676d44e48691c8dceffa66a96", ) +
%q(oauth_signature="Xy1s5IUn8x0U2KPyHBw4B2cHZMo%3D", ) +
%q(oauth_signature_method="HMAC-SHA1", ) +
%q(oauth_timestamp="1391021695", ) +
%q(oauth_version="1.0")
assert_response(200, '☺', *oapp.call(request.env))
end
it "makes a valid signed three-legged request (static #{i})" do
Timecop.travel Time.at 1391021695
consumer # cause this to be created
access_token_hash # cause this to be created
request = Rack::Request.new(Rack::MockRequest.env_for('/', :method => 'GET'))
request.env['HTTP_AUTHORIZATION'] = %q(OAuth ) +
%q(oauth_consumer_key="test_client_app_key", ) +
%q(oauth_nonce="6320851a8f4e18b2ac223497b0477f2e", ) +
%q(oauth_signature="B0sJjhfiXajEveqgjaRL3L60sCM%3D", ) +
%q(oauth_signature_method="HMAC-SHA1", ) +
%q(oauth_timestamp="1391021695", ) +
%q(oauth_token="test_access_token", ) +
%q(oauth_version="1.0")
assert_response(200, '☺', *oapp.call(request.env))
end
end
it 'complains about a missing Authorization header' do
assert_response(401, /Authorization header is missing/, *oapp.call({}))
end
it 'complains about a blank Authorization header' do
assert_response(401, /Authorization header is blank/, *oapp.call({'HTTP_AUTHORIZATION' => ' '}))
end
it 'complains about a non-OAuth Authentication header' do
assert_response(401, /Authorization scheme is not OAuth/, *oapp.call({'HTTP_AUTHORIZATION' => 'Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='}))
end
describe 'invalid Authorization header' do
if SimpleOAuth.const_defined?(:ParseError)
it 'does not prefix with oauth_' do
assert_response(401, /Could not parse Authorization header/, *oapp.call({'HTTP_AUTHORIZATION' => %q(OAuth client_app_key="test_client_app_key")}))
end
it 'has something unparseable' do
assert_response(401, /Could not parse Authorization header/, *oapp.call({'HTTP_AUTHORIZATION' => %q(OAuth test_client_app_key)}))
end
else
it 'has something unparseable' do
assert_response(401, /Authorization header is not a properly-formed OAuth 1.0 header./, *oapp.call({'HTTP_AUTHORIZATION' => %q(OAuth test_client_app_key)}))
end
end
end
it 'omits timestamp' do
Timecop.travel Time.at 1391021695
consumer # cause this to be created
request = Rack::Request.new(Rack::MockRequest.env_for('/', :method => 'GET'))
request.env['HTTP_AUTHORIZATION'] = %q(OAuth oauth_consumer_key="test_client_app_key", ) +
%q(oauth_nonce="c1c2bd8676d44e48691c8dceffa66a96", ) +
%q(oauth_signature="Xy1s5IUn8x0U2KPyHBw4B2cHZMo%3D", ) +
%q(oauth_signature_method="HMAC-SHA1", ) +
#%q(oauth_timestamp="1391021695", ) +
%q(oauth_version="1.0")
assert_response(401, /Authorization oauth_timestamp.*is missing/m, *oapp.call(request.env))
end
it 'has a non-integer timestamp' do
Timecop.travel Time.at 1391021695
consumer # cause this to be created
request = Rack::Request.new(Rack::MockRequest.env_for('/', :method => 'GET'))
request.env['HTTP_AUTHORIZATION'] = %q(OAuth oauth_consumer_key="test_client_app_key", ) +
%q(oauth_nonce="c1c2bd8676d44e48691c8dceffa66a96", ) +
%q(oauth_signature="Xy1s5IUn8x0U2KPyHBw4B2cHZMo%3D", ) +
%q(oauth_signature_method="HMAC-SHA1", ) +
%q(oauth_timestamp="now", ) +
%q(oauth_version="1.0")
assert_response(401, /Authorization oauth_timestamp.*is not an integer - got: now/m, *oapp.call(request.env))
end
it 'has a too-old timestamp' do
Timecop.travel Time.at 1391021695
consumer # cause this to be created
request = Rack::Request.new(Rack::MockRequest.env_for('/', :method => 'GET'))
request.env['HTTP_AUTHORIZATION'] = %q(OAuth oauth_consumer_key="test_client_app_key", ) +
%q(oauth_nonce="c1c2bd8676d44e48691c8dceffa66a96", ) +
%q(oauth_signature="Xy1s5IUn8x0U2KPyHBw4B2cHZMo%3D", ) +
%q(oauth_signature_method="HMAC-SHA1", ) +
%q(oauth_timestamp="1391010893", ) +
%q(oauth_version="1.0")
assert_response(401, /Authorization oauth_timestamp.*is too old: 1391010893/m, *oapp.call(request.env))
end
it 'has a timestamp too far in the future' do
Timecop.travel Time.at 1391021695
consumer # cause this to be created
request = Rack::Request.new(Rack::MockRequest.env_for('/', :method => 'GET'))
request.env['HTTP_AUTHORIZATION'] = %q(OAuth oauth_consumer_key="test_client_app_key", ) +
%q(oauth_nonce="c1c2bd8676d44e48691c8dceffa66a96", ) +
%q(oauth_signature="Xy1s5IUn8x0U2KPyHBw4B2cHZMo%3D", ) +
%q(oauth_signature_method="HMAC-SHA1", ) +
%q(oauth_timestamp="1391032497", ) +
%q(oauth_version="1.0")
assert_response(401, /Authorization oauth_timestamp.*is too far in the future: 1391032497/m, *oapp.call(request.env))
end
it 'omits version' do
Timecop.travel Time.at 1391021695
consumer # cause this to be created
request = Rack::Request.new(Rack::MockRequest.env_for('/', :method => 'GET'))
request.env['HTTP_AUTHORIZATION'] = %q(OAuth oauth_consumer_key="test_client_app_key", ) +
%q(oauth_nonce="c1c2bd8676d44e48691c8dceffa66a96", ) +
%q(oauth_signature="lCVypLHYc6oKz+vOa6DKEivoyys%3D", ) +
%q(oauth_signature_method="HMAC-SHA1", ) +
%q(oauth_timestamp="1391021695")
#%q(oauth_version="1.0")
assert_response(200, '☺', *oapp.call(request.env))
end
it 'has a wrong version' do
Timecop.travel Time.at 1391021695
consumer # cause this to be created
request = Rack::Request.new(Rack::MockRequest.env_for('/', :method => 'GET'))
request.env['HTTP_AUTHORIZATION'] = %q(OAuth oauth_consumer_key="test_client_app_key", ) +
%q(oauth_nonce="c1c2bd8676d44e48691c8dceffa66a96", ) +
%q(oauth_signature="Xy1s5IUn8x0U2KPyHBw4B2cHZMo%3D", ) +
%q(oauth_signature_method="HMAC-SHA1", ) +
%q(oauth_timestamp="1391021695", ) +
%q(oauth_version="3.14")
assert_response(401, /Authorization oauth_version.*must be 1\.0; got: 3\.14/m, *oapp.call(request.env))
end
it 'omits consumer key' do
Timecop.travel Time.at 1391021695
consumer # cause this to be created
request = Rack::Request.new(Rack::MockRequest.env_for('/', :method => 'GET'))
request.env['HTTP_AUTHORIZATION'] = %q(OAuth ) + #%q(oauth_consumer_key="test_client_app_key", ) +
%q(oauth_nonce="c1c2bd8676d44e48691c8dceffa66a96", ) +
%q(oauth_signature="Xy1s5IUn8x0U2KPyHBw4B2cHZMo%3D", ) +
%q(oauth_signature_method="HMAC-SHA1", ) +
%q(oauth_timestamp="1391021695", ) +
%q(oauth_version="1.0")
assert_response(401, /Authorization oauth_consumer_key.*is missing/m, *oapp.call(request.env))
end
it 'has an invalid consumer key' do
Timecop.travel Time.at 1391021695
consumer # cause this to be created
request = Rack::Request.new(Rack::MockRequest.env_for('/', :method => 'GET'))
request.env['HTTP_AUTHORIZATION'] = %q(OAuth oauth_consumer_key="nonexistent_app_key", ) +
%q(oauth_nonce="c1c2bd8676d44e48691c8dceffa66a96", ) +
%q(oauth_signature="Xy1s5IUn8x0U2KPyHBw4B2cHZMo%3D", ) +
%q(oauth_signature_method="HMAC-SHA1", ) +
%q(oauth_timestamp="1391021695", ) +
%q(oauth_version="1.0")
assert_response(401, /Authorization oauth_consumer_key.*is invalid/m, *oapp.call(request.env))
end
it 'has an invalid access token' do
Timecop.travel Time.at 1391021695
consumer # cause this to be created
access_token_hash # cause this to be created
request = Rack::Request.new(Rack::MockRequest.env_for('/', :method => 'GET'))
request.env['HTTP_AUTHORIZATION'] = %q(OAuth ) +
%q(oauth_consumer_key="test_client_app_key", ) +
%q(oauth_nonce="6320851a8f4e18b2ac223497b0477f2e", ) +
%q(oauth_signature="B0sJjhfiXajEveqgjaRL3L60sCM%3D", ) +
%q(oauth_signature_method="HMAC-SHA1", ) +
%q(oauth_timestamp="1391021695", ) +
%q(oauth_token="nonexistent_access_token", ) +
%q(oauth_version="1.0")
assert_response(401, /Authorization oauth_token.*is invalid/m, *oapp.call(request.env))
end
it 'has an access token belonging to a different consumer key' do
Timecop.travel Time.at 1391021695
consumer # cause this to be created
access_token_hash # cause this to be created
OAuthenticatorTestConfigMethods.consumer_secrets["different_client_app_key"] = "different_client_app_secret"
request = Rack::Request.new(Rack::MockRequest.env_for('/', :method => 'GET'))
request.env['HTTP_AUTHORIZATION'] = %q(OAuth ) +
%q(oauth_consumer_key="different_client_app_key", ) +
%q(oauth_nonce="6320851a8f4e18b2ac223497b0477f2e", ) +
%q(oauth_signature="PVscPDg%2B%2FjAXRiahIggkeBpN5zI%3D", ) +
%q(oauth_signature_method="HMAC-SHA1", ) +
%q(oauth_timestamp="1391021695", ) +
%q(oauth_token="test_access_token", ) +
%q(oauth_version="1.0")
assert_response(401, /Authorization oauth_token.*does not belong to the specified consumer/m, *oapp.call(request.env))
end
it 'omits nonce' do
Timecop.travel Time.at 1391021695
consumer # cause this to be created
request = Rack::Request.new(Rack::MockRequest.env_for('/', :method => 'GET'))
request.env['HTTP_AUTHORIZATION'] = %q(OAuth oauth_consumer_key="test_client_app_key", ) +
#%q(oauth_nonce="c1c2bd8676d44e48691c8dceffa66a96", ) +
%q(oauth_signature="Xy1s5IUn8x0U2KPyHBw4B2cHZMo%3D", ) +
%q(oauth_signature_method="HMAC-SHA1", ) +
%q(oauth_timestamp="1391021695", ) +
%q(oauth_version="1.0")
assert_response(401, /Authorization oauth_nonce.*is missing/m, *oapp.call(request.env))
end
it 'has an already-used nonce' do
Timecop.travel Time.at 1391021695
consumer # cause this to be created
request = Rack::Request.new(Rack::MockRequest.env_for('/', :method => 'GET'))
request.env['HTTP_AUTHORIZATION'] = %q(OAuth oauth_consumer_key="test_client_app_key", ) +
%q(oauth_nonce="c1c2bd8676d44e48691c8dceffa66a96", ) +
%q(oauth_signature="Xy1s5IUn8x0U2KPyHBw4B2cHZMo%3D", ) +
%q(oauth_signature_method="HMAC-SHA1", ) +
%q(oauth_timestamp="1391021695", ) +
%q(oauth_version="1.0")
assert_response(200, '☺', *oapp.call(request.env))
assert_response(401, /Authorization oauth_nonce.*has already been used/m, *oapp.call(request.env))
end
it 'omits signature' do
Timecop.travel Time.at 1391021695
consumer # cause this to be created
request = Rack::Request.new(Rack::MockRequest.env_for('/', :method => 'GET'))
request.env['HTTP_AUTHORIZATION'] = %q(OAuth oauth_consumer_key="test_client_app_key", ) +
%q(oauth_nonce="c1c2bd8676d44e48691c8dceffa66a96", ) +
#%q(oauth_signature="Xy1s5IUn8x0U2KPyHBw4B2cHZMo%3D", ) +
%q(oauth_signature_method="HMAC-SHA1", ) +
%q(oauth_timestamp="1391021695", ) +
%q(oauth_version="1.0")
assert_response(401, /Authorization oauth_signature.*is missing/m, *oapp.call(request.env))
end
it 'omits signature method' do
Timecop.travel Time.at 1391021695
consumer # cause this to be created
request = Rack::Request.new(Rack::MockRequest.env_for('/', :method => 'GET'))
request.env['HTTP_AUTHORIZATION'] = %q(OAuth oauth_consumer_key="test_client_app_key", ) +
%q(oauth_nonce="c1c2bd8676d44e48691c8dceffa66a96", ) +
%q(oauth_signature="Xy1s5IUn8x0U2KPyHBw4B2cHZMo%3D", ) +
#%q(oauth_signature_method="HMAC-SHA1", ) +
%q(oauth_timestamp="1391021695", ) +
%q(oauth_version="1.0")
assert_response(401, /Authorization oauth_signature_method.*is missing/m, *oapp.call(request.env))
end
it 'specifies an invalid signature method' do
Timecop.travel Time.at 1391021695
consumer # cause this to be created
request = Rack::Request.new(Rack::MockRequest.env_for('/', :method => 'GET'))
request.env['HTTP_AUTHORIZATION'] = %q(OAuth oauth_consumer_key="test_client_app_key", ) +
%q(oauth_nonce="c1c2bd8676d44e48691c8dceffa66a96", ) +
%q(oauth_signature="Xy1s5IUn8x0U2KPyHBw4B2cHZMo%3D", ) +
%q(oauth_signature_method="ROT13", ) +
%q(oauth_timestamp="1391021695", ) +
%q(oauth_version="1.0")
assert_response(401, /Authorization oauth_signature_method.*must be one of HMAC-SHA1, RSA-SHA1, PLAINTEXT; got: ROT13/m, *oapp.call(request.env))
end
it 'has an invalid signature' do
Timecop.travel Time.at 1391021695
consumer # cause this to be created
request = Rack::Request.new(Rack::MockRequest.env_for('/', :method => 'GET'))
request.env['HTTP_AUTHORIZATION'] = %q(OAuth oauth_consumer_key="test_client_app_key", ) +
%q(oauth_nonce="c1c2bd8676d44e48691c8dceffa66a96", ) +
%q(oauth_signature="totallylegit", ) +
%q(oauth_signature_method="HMAC-SHA1", ) +
%q(oauth_timestamp="1391021695", ) +
%q(oauth_version="1.0")
assert_response(401, /Authorization oauth_signature.*is invalid/m, *oapp.call(request.env))
end
describe :bypass do
it 'bypasses with invalid request' do
oapp = OAuthenticator::Middleware.new(simpleapp, :bypass => proc { true }, :config_methods => OAuthenticatorTestConfigMethods)
env = Rack::MockRequest.env_for('/', :method => 'GET').merge({'HTTP_AUTHORIZATION' => 'oauth ?'})
assert_response(200, '☺', *oapp.call(env))
end
it 'does not bypass with invalid request' do
oapp = OAuthenticator::Middleware.new(simpleapp, :bypass => proc { false }, :config_methods => OAuthenticatorTestConfigMethods)
assert_equal(401, oapp.call({}).first)
end
it 'bypasses with valid request' do
was_authenticated = nil
bapp = proc { |env| was_authenticated = env['oauth.authenticated']; [200, {}, ['☺']] }
boapp = OAuthenticator::Middleware.new(bapp, :bypass => proc { true }, :config_methods => OAuthenticatorTestConfigMethods)
request = Rack::Request.new(Rack::MockRequest.env_for('/', :method => 'GET'))
request.env['HTTP_AUTHORIZATION'] = SimpleOAuth::Header.new(
request.request_method,
request.url,
nil,
{:consumer_key => consumer_key, :consumer_secret => consumer_secret}
).to_s
assert_response(200, '☺', *boapp.call(request.env))
assert(was_authenticated == false)
end
it 'does not bypass with valid request' do
was_authenticated = nil
bapp = proc { |env| was_authenticated = env['oauth.authenticated']; [200, {}, ['☺']] }
boapp = OAuthenticator::Middleware.new(bapp, :bypass => proc { false }, :config_methods => OAuthenticatorTestConfigMethods)
request = Rack::Request.new(Rack::MockRequest.env_for('/', :method => 'GET'))
request.env['HTTP_AUTHORIZATION'] = SimpleOAuth::Header.new(
request.request_method,
request.url,
nil,
{:consumer_key => consumer_key, :consumer_secret => consumer_secret}
).to_s
assert_response(200, '☺', *boapp.call(request.env))
assert(was_authenticated == true)
end
end
end