-
Notifications
You must be signed in to change notification settings - Fork 147
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
283 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
module ApiAuth | ||
module RequestDrivers # :nodoc: | ||
class TyphoeusRequest # :nodoc: | ||
include ApiAuth::Helpers | ||
|
||
def initialize(request) | ||
@request = request | ||
fetch_headers | ||
end | ||
|
||
def set_auth_header(header) | ||
@request.options[:headers]['Authorization'] = header | ||
fetch_headers | ||
@request | ||
end | ||
|
||
def calculated_md5 | ||
body = @request.options[:body] || '' | ||
md5_base64digest(body.to_s) | ||
end | ||
|
||
def populate_content_md5 | ||
return unless %w[POST PUT].include?(http_method.to_s.upcase) | ||
|
||
@request.options[:headers]['Content-MD5'] = calculated_md5 | ||
fetch_headers | ||
end | ||
|
||
def md5_mismatch? | ||
if %w[POST PUT].include?(http_method.to_s.upcase) | ||
calculated_md5 != content_md5 | ||
else | ||
false | ||
end | ||
end | ||
|
||
def fetch_headers | ||
@headers = capitalize_keys(@request.options[:headers]) | ||
end | ||
|
||
def http_method | ||
@request.options[:method].to_s.upcase | ||
end | ||
|
||
def content_type | ||
find_header(%w[CONTENT-TYPE CONTENT_TYPE HTTP_CONTENT_TYPE]) | ||
end | ||
|
||
def content_md5 | ||
find_header(%w[CONTENT-MD5 CONTENT_MD5 HTTP-CONTENT-MD5 HTTP_CONTENT_MD5]) | ||
end | ||
|
||
def original_uri | ||
find_header(%w[X-ORIGINAL-URI X_ORIGINAL_URI HTTP_X_ORIGINAL_URI]) | ||
end | ||
|
||
def request_uri | ||
URI.parse(@request.url).request_uri | ||
end | ||
|
||
def set_date | ||
@request.options[:headers]['Date'] = Time.now.utc.httpdate | ||
fetch_headers | ||
end | ||
|
||
def timestamp | ||
find_header(%w[DATE HTTP_DATE]) | ||
end | ||
|
||
def authorization_header | ||
find_header %w[Authorization AUTHORIZATION HTTP_AUTHORIZATION] | ||
end | ||
|
||
private | ||
|
||
def find_header(keys) | ||
keys.map { |key| @headers[key] }.compact.first | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
require 'spec_helper' | ||
|
||
describe ApiAuth::RequestDrivers::TyphoeusRequest do | ||
let(:timestamp) { Time.now.utc.httpdate } | ||
|
||
let(:request_url) { 'https://example.com/resource.xml?foo=bar&bar=foo' } | ||
let(:upload_file) { File.open('spec/fixtures/upload.png', 'r') } | ||
|
||
let(:request_headers) do | ||
{ | ||
'Authorization' => 'APIAuth 1044:12345', | ||
'content-md5' => '1B2M2Y8AsgTpgAmY7PhCfg==', | ||
'content-type' => 'text/plain', | ||
'date' => timestamp | ||
} | ||
end | ||
|
||
let(:upload_request) do | ||
Typhoeus::Request.new(request_url, method: :put, headers: request_headers, body: { file: upload_file }) | ||
end | ||
|
||
let(:request) do | ||
Typhoeus::Request.new(request_url, method: :put, headers: request_headers, body: "hello\nworld") | ||
end | ||
|
||
subject(:driven_request) { ApiAuth::RequestDrivers::TyphoeusRequest.new(request) } | ||
|
||
describe 'getting headers correctly' do | ||
describe '#content_type' do | ||
it 'gets the content_type' do | ||
expect(driven_request.content_type).to eq('text/plain') | ||
end | ||
end | ||
|
||
it 'gets the content_md5' do | ||
expect(driven_request.content_md5).to eq('1B2M2Y8AsgTpgAmY7PhCfg==') | ||
end | ||
|
||
it 'gets the request_uri' do | ||
expect(driven_request.request_uri).to eq('/resource.xml?foo=bar&bar=foo') | ||
end | ||
|
||
it 'gets the timestamp' do | ||
expect(driven_request.timestamp).to eq(timestamp) | ||
end | ||
|
||
it 'gets the authorization_header' do | ||
expect(driven_request.authorization_header).to eq('APIAuth 1044:12345') | ||
end | ||
|
||
describe '#calculated_md5' do | ||
it 'calculates md5 from the body' do | ||
expect(driven_request.calculated_md5).to eq('kZXQvrKoieG+Be1rsZVINw==') | ||
end | ||
|
||
it 'treats no body as empty string' do | ||
request.options[:body] = nil | ||
expect(driven_request.calculated_md5).to eq('1B2M2Y8AsgTpgAmY7PhCfg==') | ||
end | ||
|
||
context 'file upload' do | ||
let(:request) { upload_request } | ||
|
||
it 'calculates correctly for multipart content' do | ||
expect(driven_request.calculated_md5).to eq('3wNtEzQ9ZdXZnkyg/glH1g==') | ||
end | ||
end | ||
end | ||
|
||
describe 'http_method' do | ||
context 'when put request' do | ||
let(:request) { Typhoeus::Request.new(request_url, method: :put, headers: request_headers) } | ||
|
||
it 'returns upcased put' do | ||
expect(driven_request.http_method).to eq('PUT') | ||
end | ||
end | ||
|
||
context 'when get request' do | ||
let(:request) { Typhoeus::Request.new(request_url, method: :get, headers: request_headers) } | ||
|
||
it 'returns upcased get' do | ||
expect(driven_request.http_method).to eq('GET') | ||
end | ||
end | ||
end | ||
end | ||
|
||
describe 'setting headers correctly' do | ||
let(:request_headers) do | ||
{ | ||
'content-type' => 'text/plain' | ||
} | ||
end | ||
|
||
let(:request) do | ||
Typhoeus::Request.new(request_url, method: :put, headers: request_headers) | ||
end | ||
|
||
describe '#populate_content_md5' do | ||
context 'when request type has no body' do | ||
let(:request) do | ||
Typhoeus::Request.new(request_url, method: :get, headers: request_headers) | ||
end | ||
|
||
it "doesn't populate content-md5" do | ||
driven_request.populate_content_md5 | ||
expect(request.options[:headers]['Content-MD5']).to be_nil | ||
end | ||
end | ||
|
||
context 'when request type has a body' do | ||
let(:request) do | ||
Typhoeus::Request.new(request_url, method: :put, headers: request_headers, body: "hello\nworld") | ||
end | ||
|
||
it 'populates content-md5' do | ||
driven_request.populate_content_md5 | ||
expect(request.options[:headers]['Content-MD5']).to eq('kZXQvrKoieG+Be1rsZVINw==') | ||
end | ||
|
||
it 'refreshes the cached headers' do | ||
driven_request.populate_content_md5 | ||
expect(driven_request.content_md5).to eq('kZXQvrKoieG+Be1rsZVINw==') | ||
end | ||
end | ||
end | ||
|
||
describe '#set_date' do | ||
before do | ||
allow(Time).to receive_message_chain(:now, :utc, :httpdate).and_return(timestamp) | ||
end | ||
|
||
it 'sets the date header of the request' do | ||
driven_request.set_date | ||
expect(request.options[:headers]['Date']).to eq(timestamp) | ||
end | ||
|
||
it 'refreshes the cached headers' do | ||
driven_request.set_date | ||
expect(driven_request.timestamp).to eq(timestamp) | ||
end | ||
end | ||
|
||
describe '#set_auth_header' do | ||
it 'sets the auth header' do | ||
driven_request.set_auth_header('APIAuth 1044:54321') | ||
expect(request.options[:headers]['Authorization']).to eq('APIAuth 1044:54321') | ||
end | ||
end | ||
end | ||
|
||
describe 'md5_mismatch?' do | ||
context 'when request type has no body' do | ||
let(:request) do | ||
Typhoeus::Request.new(request_url, method: :get, headers: request_headers) | ||
end | ||
|
||
it 'is false' do | ||
expect(driven_request.md5_mismatch?).to be false | ||
end | ||
end | ||
|
||
context 'when request type has a body' do | ||
let(:request) do | ||
Typhoeus::Request.new(request_url, method: :put, headers: request_headers, body: "hello\world") | ||
end | ||
|
||
context 'when calculated matches sent' do | ||
before do | ||
request.options[:headers]['Content-MD5'] = '/F4DjTilcDIIVEHn/nAQsA==' | ||
end | ||
|
||
it 'is false' do | ||
expect(driven_request.md5_mismatch?).to be false | ||
end | ||
end | ||
|
||
context "when calculated doesn't match sent" do | ||
before do | ||
request.options[:headers]['Content-MD5'] = '3' | ||
end | ||
|
||
it 'is true' do | ||
expect(driven_request.md5_mismatch?).to be true | ||
end | ||
end | ||
end | ||
end | ||
|
||
describe 'fetch_headers' do | ||
it 'returns request headers' do | ||
expect(driven_request.fetch_headers).to include('CONTENT-TYPE' => 'text/plain') | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters