api-auth 1.3.2 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rspec +2 -2
- data/.travis.yml +4 -0
- data/Appraisals +6 -0
- data/CHANGELOG.md +36 -0
- data/Gemfile.lock +77 -44
- data/README.md +15 -8
- data/VERSION +1 -1
- data/api_auth.gemspec +4 -4
- data/gemfiles/rails_23.gemfile +1 -1
- data/gemfiles/rails_23.gemfile.lock +19 -11
- data/gemfiles/rails_30.gemfile +1 -1
- data/gemfiles/rails_30.gemfile.lock +19 -11
- data/gemfiles/rails_31.gemfile +1 -1
- data/gemfiles/rails_31.gemfile.lock +19 -11
- data/gemfiles/rails_32.gemfile +1 -1
- data/gemfiles/rails_32.gemfile.lock +19 -11
- data/gemfiles/rails_4.gemfile +1 -1
- data/gemfiles/rails_4.gemfile.lock +19 -11
- data/gemfiles/rails_41.gemfile +1 -1
- data/gemfiles/rails_41.gemfile.lock +19 -11
- data/gemfiles/rails_42.gemfile +9 -0
- data/gemfiles/rails_42.gemfile.lock +115 -0
- data/lib/api_auth/base.rb +37 -23
- data/lib/api_auth/headers.rb +23 -3
- data/lib/api_auth/request_drivers/action_controller.rb +4 -0
- data/lib/api_auth/request_drivers/curb.rb +4 -0
- data/lib/api_auth/request_drivers/faraday.rb +4 -0
- data/lib/api_auth/request_drivers/httpi.rb +5 -1
- data/lib/api_auth/request_drivers/net_http.rb +4 -0
- data/lib/api_auth/request_drivers/rack.rb +5 -1
- data/lib/api_auth/request_drivers/rest_client.rb +4 -0
- data/spec/api_auth_spec.rb +112 -628
- data/spec/headers_spec.rb +132 -289
- data/spec/helpers_spec.rb +2 -2
- data/spec/railtie_spec.rb +13 -8
- data/spec/request_drivers/action_controller_spec.rb +218 -0
- data/spec/request_drivers/action_dispatch_spec.rb +219 -0
- data/spec/request_drivers/curb_spec.rb +89 -0
- data/spec/request_drivers/faraday_spec.rb +243 -0
- data/spec/request_drivers/httpi_spec.rb +147 -0
- data/spec/request_drivers/net_http_spec.rb +185 -0
- data/spec/request_drivers/rack_spec.rb +288 -0
- data/spec/request_drivers/rest_client_spec.rb +311 -0
- metadata +44 -19
- data/spec/application_helper.rb +0 -2
- data/spec/test_helper.rb +0 -2
data/lib/api_auth/headers.rb
CHANGED
@@ -52,10 +52,28 @@ module ApiAuth
|
|
52
52
|
end
|
53
53
|
|
54
54
|
# Returns the canonical string computed from the request's headers
|
55
|
-
def
|
55
|
+
def canonical_string_without_http_method
|
56
56
|
[ @request.content_type,
|
57
57
|
@request.content_md5,
|
58
|
-
parse_uri(@request.request_uri),
|
58
|
+
parse_uri(@request.request_uri),
|
59
|
+
@request.timestamp
|
60
|
+
].join(",")
|
61
|
+
end
|
62
|
+
|
63
|
+
# temp backwards compatibility
|
64
|
+
alias_method :canonical_string, :canonical_string_without_http_method
|
65
|
+
|
66
|
+
def canonical_string_with_http_method(override_method = nil)
|
67
|
+
request_method = override_method || @request.http_method
|
68
|
+
|
69
|
+
if request_method.nil?
|
70
|
+
raise ArgumentError, "unable to determine the http method from the request, please supply an override"
|
71
|
+
end
|
72
|
+
|
73
|
+
[ request_method.upcase,
|
74
|
+
@request.content_type,
|
75
|
+
@request.content_md5,
|
76
|
+
parse_uri(@request.request_uri),
|
59
77
|
@request.timestamp
|
60
78
|
].join(",")
|
61
79
|
end
|
@@ -92,8 +110,10 @@ module ApiAuth
|
|
92
110
|
|
93
111
|
private
|
94
112
|
|
113
|
+
URI_WITHOUT_HOST_REGEXP = %r{https?://[^,?/]*}
|
114
|
+
|
95
115
|
def parse_uri(uri)
|
96
|
-
uri_without_host = uri.gsub(
|
116
|
+
uri_without_host = uri.gsub(URI_WITHOUT_HOST_REGEXP, '')
|
97
117
|
return '/' if uri_without_host.empty?
|
98
118
|
uri_without_host
|
99
119
|
end
|
@@ -30,6 +30,10 @@ module ApiAuth
|
|
30
30
|
capitalize_keys @request.headers
|
31
31
|
end
|
32
32
|
|
33
|
+
def http_method
|
34
|
+
nil # not possible to get the method at this layer
|
35
|
+
end
|
36
|
+
|
33
37
|
def content_type
|
34
38
|
value = find_header(%w(CONTENT-TYPE CONTENT_TYPE HTTP_CONTENT_TYPE))
|
35
39
|
value.nil? ? "" : value
|
@@ -40,6 +40,10 @@ module ApiAuth
|
|
40
40
|
capitalize_keys @request.headers
|
41
41
|
end
|
42
42
|
|
43
|
+
def http_method
|
44
|
+
nil # not possible to get the method at this layer
|
45
|
+
end
|
46
|
+
|
43
47
|
def content_type
|
44
48
|
value = find_header(%w(CONTENT-TYPE CONTENT_TYPE HTTP_CONTENT_TYPE))
|
45
49
|
value.nil? ? "" : value
|
@@ -77,4 +81,4 @@ module ApiAuth
|
|
77
81
|
|
78
82
|
end
|
79
83
|
|
80
|
-
end
|
84
|
+
end
|
@@ -46,6 +46,10 @@ module ApiAuth
|
|
46
46
|
capitalize_keys @request.env
|
47
47
|
end
|
48
48
|
|
49
|
+
def http_method
|
50
|
+
@request.request_method.upcase
|
51
|
+
end
|
52
|
+
|
49
53
|
def content_type
|
50
54
|
value = find_header(%w(CONTENT-TYPE CONTENT_TYPE HTTP_CONTENT_TYPE))
|
51
55
|
value.nil? ? "" : value
|
@@ -57,7 +61,7 @@ module ApiAuth
|
|
57
61
|
end
|
58
62
|
|
59
63
|
def request_uri
|
60
|
-
@request.
|
64
|
+
@request.fullpath
|
61
65
|
end
|
62
66
|
|
63
67
|
def set_date
|
data/spec/api_auth_spec.rb
CHANGED
@@ -10,678 +10,162 @@ describe "ApiAuth" do
|
|
10
10
|
end
|
11
11
|
|
12
12
|
it "should generate secret keys that are 88 characters" do
|
13
|
-
ApiAuth.generate_secret_key.size.
|
13
|
+
expect(ApiAuth.generate_secret_key.size).to be(88)
|
14
14
|
end
|
15
15
|
|
16
16
|
it "should generate keys that have a Hamming Distance of at least 65" do
|
17
17
|
key1 = ApiAuth.generate_secret_key
|
18
18
|
key2 = ApiAuth.generate_secret_key
|
19
|
-
Amatch::Hamming.new(key1).match(key2).
|
19
|
+
expect(Amatch::Hamming.new(key1).match(key2)).to be > 65
|
20
20
|
end
|
21
21
|
|
22
22
|
end
|
23
23
|
|
24
|
-
|
24
|
+
def hmac(secret_key, request, canonical_string = nil)
|
25
|
+
canonical_string ||= ApiAuth::Headers.new(request).canonical_string
|
26
|
+
digest = OpenSSL::Digest.new('sha1')
|
27
|
+
ApiAuth.b64_encode(OpenSSL::HMAC.digest(digest, secret_key, canonical_string))
|
28
|
+
end
|
25
29
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
ApiAuth.b64_encode(OpenSSL::HMAC.digest(digest, secret_key, canonical_string))
|
30
|
-
end
|
30
|
+
describe ".sign!" do
|
31
|
+
let(:request){ RestClient::Request.new(:url => "http://google.com", :method => :get) }
|
32
|
+
let(:headers){ ApiAuth::Headers.new(request) }
|
31
33
|
|
32
|
-
before
|
33
|
-
|
34
|
-
@secret_key = ApiAuth.generate_secret_key
|
35
|
-
end
|
34
|
+
it "generates date header before signing" do
|
35
|
+
expect(ApiAuth::Headers).to receive(:new).and_return(headers)
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
before(:each) do
|
40
|
-
@request = Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
|
41
|
-
'content-type' => 'text/plain',
|
42
|
-
'content-md5' => '1B2M2Y8AsgTpgAmY7PhCfg==',
|
43
|
-
'date' => Time.now.utc.httpdate)
|
44
|
-
@signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
45
|
-
end
|
37
|
+
expect(headers).to receive(:set_date).ordered
|
38
|
+
expect(headers).to receive(:sign_header).ordered
|
46
39
|
|
47
|
-
|
48
|
-
|
49
|
-
end
|
40
|
+
ApiAuth.sign!(request, "abc", "123")
|
41
|
+
end
|
50
42
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
'content-type' => 'text/plain',
|
56
|
-
'date' => "Mon, 23 Jan 1984 03:29:56 GMT")
|
57
|
-
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
58
|
-
signed_request['Content-MD5'].should == "1B2M2Y8AsgTpgAmY7PhCfg=="
|
59
|
-
end
|
60
|
-
|
61
|
-
it "should calculate for real content" do
|
62
|
-
request = Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
|
63
|
-
'content-type' => 'text/plain',
|
64
|
-
'date' => "Mon, 23 Jan 1984 03:29:56 GMT")
|
65
|
-
request.body = "hello\nworld"
|
66
|
-
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
67
|
-
signed_request['Content-MD5'].should == "kZXQvrKoieG+Be1rsZVINw=="
|
68
|
-
end
|
69
|
-
|
70
|
-
it "should calculate for real multipart content" do
|
71
|
-
request = Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
|
72
|
-
'content-type' => 'text/plain',
|
73
|
-
'date' => "Mon, 23 Jan 1984 03:29:56 GMT")
|
74
|
-
request.body_stream = File.new('spec/fixtures/upload.png')
|
75
|
-
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
76
|
-
signed_request['Content-MD5'].should == "k4U8MTA3RHDcewBzymVNEQ=="
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
it "should leave the content-md5 alone if provided" do
|
81
|
-
@signed_request['Content-MD5'].should == '1B2M2Y8AsgTpgAmY7PhCfg=='
|
82
|
-
end
|
83
|
-
end
|
43
|
+
it "generates content-md5 header before signing" do
|
44
|
+
expect(ApiAuth::Headers).to receive(:new).and_return(headers)
|
45
|
+
expect(headers).to receive(:calculate_md5).ordered
|
46
|
+
expect(headers).to receive(:sign_header).ordered
|
84
47
|
|
85
|
-
|
86
|
-
|
87
|
-
end
|
48
|
+
ApiAuth.sign!(request, "abc", "123")
|
49
|
+
end
|
88
50
|
|
89
|
-
|
90
|
-
|
91
|
-
|
51
|
+
it "returns the same request object back" do
|
52
|
+
expect(ApiAuth.sign!(request, "abc", "123")).to be request
|
53
|
+
end
|
92
54
|
|
93
|
-
|
94
|
-
|
95
|
-
|
55
|
+
it "calculates the hmac_signature as expected" do
|
56
|
+
ApiAuth.sign!(request, "1044", "123")
|
57
|
+
signature = hmac("123", request)
|
58
|
+
expect(request.headers['Authorization']).to eq("APIAuth 1044:#{signature}")
|
59
|
+
end
|
96
60
|
|
97
|
-
|
98
|
-
|
61
|
+
context "when passed the with_http_method option" do
|
62
|
+
let(:request){
|
63
|
+
Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
|
99
64
|
'content-type' => 'text/plain',
|
100
|
-
'
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
ApiAuth.authentic?(signed_request, @secret_key).should be_false
|
105
|
-
end
|
106
|
-
|
107
|
-
it "should NOT authenticate an expired request" do
|
108
|
-
@request['Date'] = 16.minutes.ago.utc.httpdate
|
109
|
-
signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
110
|
-
ApiAuth.authentic?(signed_request, @secret_key).should be_false
|
111
|
-
end
|
65
|
+
'content-md5' => '1B2M2Y8AsgTpgAmY7PhCfg==',
|
66
|
+
'date' => Time.now.utc.httpdate
|
67
|
+
)
|
68
|
+
}
|
112
69
|
|
113
|
-
|
114
|
-
@request['Date'] = "٢٠١٤-٠٩-٠٨ ١٦:٣١:١٤ +٠٣٠٠"
|
115
|
-
signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
116
|
-
ApiAuth.authentic?(signed_request, @secret_key).should be_false
|
117
|
-
end
|
70
|
+
let(:canonical_string){ ApiAuth::Headers.new(request).canonical_string_with_http_method }
|
118
71
|
|
119
|
-
it "
|
120
|
-
ApiAuth.
|
72
|
+
it "calculates the hmac_signature with http method" do
|
73
|
+
ApiAuth.sign!(request, "1044", "123", { :with_http_method => true })
|
74
|
+
signature = hmac("123", request, canonical_string)
|
75
|
+
expect(request['Authorization']).to eq("APIAuth 1044:#{signature}")
|
121
76
|
end
|
122
|
-
|
123
77
|
end
|
78
|
+
end
|
124
79
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
describe "md5 header" do
|
142
|
-
context "not already provided" do
|
143
|
-
it "should calculate for empty string" do
|
144
|
-
headers = { 'Content-Type' => "text/plain",
|
145
|
-
'Date' => "Mon, 23 Jan 1984 03:29:56 GMT" }
|
146
|
-
request = RestClient::Request.new(:url => "/resource.xml?foo=bar&bar=foo",
|
147
|
-
:headers => headers,
|
148
|
-
:method => :put)
|
149
|
-
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
150
|
-
signed_request.headers['Content-MD5'].should == "1B2M2Y8AsgTpgAmY7PhCfg=="
|
151
|
-
end
|
152
|
-
|
153
|
-
it "should calculate for real content" do
|
154
|
-
headers = { 'Content-Type' => "text/plain",
|
155
|
-
'Date' => "Mon, 23 Jan 1984 03:29:56 GMT" }
|
156
|
-
request = RestClient::Request.new(:url => "/resource.xml?foo=bar&bar=foo",
|
157
|
-
:headers => headers,
|
158
|
-
:method => :put,
|
159
|
-
:payload => "hellow\nworld")
|
160
|
-
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
161
|
-
signed_request.headers['Content-MD5'].should == "G0grublI06013h58g9j8Vw=="
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
it "should leave the content-md5 alone if provided" do
|
166
|
-
@signed_request.headers['Content-MD5'].should == "1B2M2Y8AsgTpgAmY7PhCfg=="
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
it "should sign the request" do
|
171
|
-
@signed_request.headers['Authorization'].should == "APIAuth 1044:#{hmac(@secret_key, @request)}"
|
172
|
-
end
|
173
|
-
|
174
|
-
it "should sign the request using the generated md5 header" do
|
175
|
-
date = Time.now.utc.httpdate
|
176
|
-
headers1 = { 'Content-MD5' => "1B2M2Y8AsgTpgAmY7PhCfg==",
|
177
|
-
'Content-Type' => "text/plain",
|
178
|
-
'Date' => date }
|
179
|
-
request1 = RestClient::Request.new(:url => "/resource.xml?foo=bar&bar=foo",
|
180
|
-
:headers => headers1,
|
181
|
-
:method => :put)
|
182
|
-
headers2 = { 'Content-Type' => "text/plain",
|
183
|
-
'Date' => date }
|
184
|
-
request2 = RestClient::Request.new(:url => "/resource.xml?foo=bar&bar=foo",
|
185
|
-
:headers => headers2,
|
186
|
-
:method => :put)
|
187
|
-
|
188
|
-
ApiAuth.sign!(request1, @access_id, @secret_key)
|
189
|
-
ApiAuth.sign!(request2, @access_id, @secret_key)
|
190
|
-
|
191
|
-
request2.headers['Authorization'].should == request1.headers['Authorization']
|
192
|
-
end
|
193
|
-
|
194
|
-
it "should sign the request using the generated Date header" do
|
195
|
-
headers1 = { 'Content-MD5' => "1B2M2Y8AsgTpgAmY7PhCfg==",
|
196
|
-
'Content-Type' => "text/plain"}
|
197
|
-
request1 = RestClient::Request.new(:url => "/resource.xml?foo=bar&bar=foo",
|
198
|
-
:headers => headers1,
|
199
|
-
:method => :put)
|
200
|
-
ApiAuth.sign!(request1, @access_id, @secret_key)
|
201
|
-
headers2 = { 'Content-MD5' => "1B2M2Y8AsgTpgAmY7PhCfg==",
|
202
|
-
'Content-Type' => "text/plain",
|
203
|
-
'Date' => request1.headers['DATE'] }
|
204
|
-
request2 = RestClient::Request.new(:url => "/resource.xml?foo=bar&bar=foo",
|
205
|
-
:headers => headers2,
|
206
|
-
:method => :put)
|
207
|
-
|
208
|
-
ApiAuth.sign!(request2, @access_id, @secret_key)
|
209
|
-
|
210
|
-
request2.headers['Authorization'].should == request1.headers['Authorization']
|
211
|
-
end
|
212
|
-
|
213
|
-
it "should authenticate a valid request" do
|
214
|
-
ApiAuth.authentic?(@signed_request, @secret_key).should be_true
|
215
|
-
end
|
216
|
-
|
217
|
-
it "should NOT authenticate a non-valid request" do
|
218
|
-
ApiAuth.authentic?(@signed_request, @secret_key+'j').should be_false
|
219
|
-
end
|
220
|
-
|
221
|
-
it "should NOT authenticate a mismatched content-md5 when body has changed" do
|
222
|
-
headers = { 'Content-Type' => "text/plain",
|
223
|
-
'Date' => "Mon, 23 Jan 1984 03:29:56 GMT" }
|
224
|
-
request = RestClient::Request.new(:url => "/resource.xml?foo=bar&bar=foo",
|
225
|
-
:headers => headers,
|
226
|
-
:method => :put,
|
227
|
-
:payload => "hello\nworld")
|
228
|
-
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
229
|
-
signed_request.instance_variable_set("@payload", RestClient::Payload.generate('goodbye'))
|
230
|
-
ApiAuth.authentic?(signed_request, @secret_key).should be_false
|
231
|
-
end
|
232
|
-
|
233
|
-
it "should NOT authenticate an expired request" do
|
234
|
-
@request.headers['Date'] = 16.minutes.ago.utc.httpdate
|
235
|
-
signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
236
|
-
ApiAuth.authentic?(signed_request, @secret_key).should be_false
|
237
|
-
end
|
238
|
-
|
239
|
-
it "should NOT authenticate a request with an invalid date" do
|
240
|
-
@request.headers['Date'] = "٢٠١٤-٠٩-٠٨ ١٦:٣١:١٤ +٠٣٠٠"
|
241
|
-
signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
242
|
-
ApiAuth.authentic?(signed_request, @secret_key).should be_false
|
243
|
-
end
|
244
|
-
|
245
|
-
it "should retrieve the access_id" do
|
246
|
-
ApiAuth.access_id(@signed_request).should == "1044"
|
247
|
-
end
|
248
|
-
|
80
|
+
describe ".authentic?" do
|
81
|
+
let(:request){
|
82
|
+
new_request = Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
|
83
|
+
'content-type' => 'text/plain',
|
84
|
+
'content-md5' => '1B2M2Y8AsgTpgAmY7PhCfg==',
|
85
|
+
'date' => Time.now.utc.httpdate
|
86
|
+
)
|
87
|
+
|
88
|
+
signature = hmac("123", new_request)
|
89
|
+
new_request["Authorization"] = "APIAuth 1044:#{signature}"
|
90
|
+
new_request
|
91
|
+
}
|
92
|
+
|
93
|
+
it "validates that the signature in the request header matches the way we sign it" do
|
94
|
+
expect(ApiAuth.authentic?(request, "123")).to eq true
|
249
95
|
end
|
250
96
|
|
251
|
-
|
252
|
-
|
253
|
-
before(:each) do
|
254
|
-
headers = { 'Content-MD5' => "e59ff97941044f85df5297e1c302d260",
|
255
|
-
'Content-Type' => "text/plain",
|
256
|
-
'Date' => Time.now.utc.httpdate }
|
257
|
-
@request = Curl::Easy.new("/resource.xml?foo=bar&bar=foo") do |curl|
|
258
|
-
curl.headers = headers
|
259
|
-
end
|
260
|
-
@signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
261
|
-
end
|
262
|
-
|
263
|
-
it "should return a Curl::Easy object after signing it" do
|
264
|
-
ApiAuth.sign!(@request, @access_id, @secret_key).class.to_s.should match("Curl::Easy")
|
265
|
-
end
|
266
|
-
|
267
|
-
describe "md5 header" do
|
268
|
-
it "should not calculate and add the content-md5 header if not provided" do
|
269
|
-
headers = { 'Content-Type' => "text/plain",
|
270
|
-
'Date' => "Mon, 23 Jan 1984 03:29:56 GMT" }
|
271
|
-
request = Curl::Easy.new("/resource.xml?foo=bar&bar=foo") do |curl|
|
272
|
-
curl.headers = headers
|
273
|
-
end
|
274
|
-
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
275
|
-
signed_request.headers['Content-MD5'].should == nil
|
276
|
-
end
|
277
|
-
|
278
|
-
it "should leave the content-md5 alone if provided" do
|
279
|
-
@signed_request.headers['Content-MD5'].should == "e59ff97941044f85df5297e1c302d260"
|
280
|
-
end
|
281
|
-
end
|
282
|
-
|
283
|
-
it "should sign the request" do
|
284
|
-
@signed_request.headers['Authorization'].should == "APIAuth 1044:#{hmac(@secret_key, @request)}"
|
285
|
-
end
|
286
|
-
|
287
|
-
it "should authenticate a valid request" do
|
288
|
-
ApiAuth.authentic?(@signed_request, @secret_key).should be_true
|
289
|
-
end
|
290
|
-
|
291
|
-
it "should NOT authenticate a non-valid request" do
|
292
|
-
ApiAuth.authentic?(@signed_request, @secret_key+'j').should be_false
|
293
|
-
end
|
294
|
-
|
295
|
-
it "should NOT authenticate an expired request" do
|
296
|
-
@request.headers['Date'] = 16.minutes.ago.utc.httpdate
|
297
|
-
signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
298
|
-
ApiAuth.authentic?(signed_request, @secret_key).should be_false
|
299
|
-
end
|
300
|
-
|
301
|
-
it "should NOT authenticate a request with an invalid date" do
|
302
|
-
@request.headers['Date'] = "٢٠١٤-٠٩-٠٨ ١٦:٣١:١٤ +٠٣٠٠"
|
303
|
-
signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
304
|
-
ApiAuth.authentic?(signed_request, @secret_key).should be_false
|
305
|
-
end
|
306
|
-
|
307
|
-
it "should retrieve the access_id" do
|
308
|
-
ApiAuth.access_id(@signed_request).should == "1044"
|
309
|
-
end
|
310
|
-
|
97
|
+
it "fails to validate a non matching signature" do
|
98
|
+
expect(ApiAuth.authentic?(request, "456")).to eq false
|
311
99
|
end
|
312
100
|
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
before(:each) do
|
318
|
-
@request = request_klass.new(
|
319
|
-
'PATH_INFO' => '/resource.xml',
|
320
|
-
'QUERY_STRING' => 'foo=bar&bar=foo',
|
321
|
-
'REQUEST_METHOD' => 'PUT',
|
322
|
-
'CONTENT_MD5' => '1B2M2Y8AsgTpgAmY7PhCfg==',
|
323
|
-
'CONTENT_TYPE' => 'text/plain',
|
324
|
-
'HTTP_DATE' => Time.now.utc.httpdate,
|
325
|
-
'rack.input' => StringIO.new)
|
326
|
-
@signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
327
|
-
end
|
328
|
-
|
329
|
-
it "should return a ActionDispatch::Request object after signing it" do
|
330
|
-
ApiAuth.sign!(@request, @access_id, @secret_key).class.to_s.should match(request_klass.to_s)
|
331
|
-
end
|
332
|
-
|
333
|
-
describe "md5 header" do
|
334
|
-
context "not already provided" do
|
335
|
-
it "should calculate for empty string" do
|
336
|
-
request = request_klass.new(
|
337
|
-
'PATH_INFO' => '/resource.xml',
|
338
|
-
'QUERY_STRING' => 'foo=bar&bar=foo',
|
339
|
-
'REQUEST_METHOD' => 'PUT',
|
340
|
-
'CONTENT_TYPE' => 'text/plain',
|
341
|
-
'HTTP_DATE' => 'Mon, 23 Jan 1984 03:29:56 GMT',
|
342
|
-
'rack.input' => StringIO.new)
|
343
|
-
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
344
|
-
signed_request.env['Content-MD5'].should == "1B2M2Y8AsgTpgAmY7PhCfg=="
|
345
|
-
end
|
346
|
-
|
347
|
-
it "should calculate for real content" do
|
348
|
-
request = request_klass.new(
|
349
|
-
'PATH_INFO' => '/resource.xml',
|
350
|
-
'QUERY_STRING' => 'foo=bar&bar=foo',
|
351
|
-
'REQUEST_METHOD' => 'PUT',
|
352
|
-
'CONTENT_TYPE' => 'text/plain',
|
353
|
-
'HTTP_DATE' => 'Mon, 23 Jan 1984 03:29:56 GMT',
|
354
|
-
'rack.input' => StringIO.new("hello\nworld"),
|
355
|
-
'CONTENT_LENGTH' => '11')
|
356
|
-
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
357
|
-
signed_request.env['Content-MD5'].should == "kZXQvrKoieG+Be1rsZVINw=="
|
358
|
-
end
|
359
|
-
|
360
|
-
end
|
361
|
-
|
362
|
-
it "should leave the content-md5 alone if provided" do
|
363
|
-
@signed_request.env['CONTENT_MD5'].should == '1B2M2Y8AsgTpgAmY7PhCfg=='
|
364
|
-
end
|
365
|
-
end
|
366
|
-
|
367
|
-
it "should sign the request" do
|
368
|
-
@signed_request.env['Authorization'].should == "APIAuth 1044:#{hmac(@secret_key, @request)}"
|
369
|
-
end
|
370
|
-
|
371
|
-
it "should authenticate a valid request" do
|
372
|
-
ApiAuth.authentic?(@signed_request, @secret_key).should be_true
|
373
|
-
end
|
374
|
-
|
375
|
-
it "should NOT authenticate a non-valid request" do
|
376
|
-
ApiAuth.authentic?(@signed_request, @secret_key+'j').should be_false
|
377
|
-
end
|
378
|
-
|
379
|
-
it "should NOT authenticate a mismatched content-md5 when body has changed" do
|
380
|
-
request = request_klass.new(
|
381
|
-
'PATH_INFO' => '/resource.xml',
|
382
|
-
'QUERY_STRING' => 'foo=bar&bar=foo',
|
383
|
-
'REQUEST_METHOD' => 'PUT',
|
384
|
-
'CONTENT_TYPE' => 'text/plain',
|
385
|
-
'HTTP_DATE' => 'Mon, 23 Jan 1984 03:29:56 GMT',
|
386
|
-
'rack.input' => StringIO.new("hello\nworld"))
|
387
|
-
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
388
|
-
signed_request.instance_variable_get("@env")["rack.input"] = StringIO.new("goodbye")
|
389
|
-
ApiAuth.authentic?(signed_request, @secret_key).should be_false
|
390
|
-
end
|
391
|
-
|
392
|
-
it "should NOT authenticate an expired request" do
|
393
|
-
@request.env['HTTP_DATE'] = 16.minutes.ago.utc.httpdate
|
394
|
-
signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
395
|
-
ApiAuth.authentic?(signed_request, @secret_key).should be_false
|
396
|
-
end
|
397
|
-
|
398
|
-
it "should NOT authenticate a request with an invalid date" do
|
399
|
-
@request.env['Date'] = "٢٠١٤-٠٩-٠٨ ١٦:٣١:١٤ +٠٣٠٠"
|
400
|
-
signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
401
|
-
ApiAuth.authentic?(signed_request, @secret_key).should be_false
|
402
|
-
end
|
403
|
-
|
404
|
-
it "should retrieve the access_id" do
|
405
|
-
ApiAuth.access_id(@signed_request).should == "1044"
|
406
|
-
end
|
407
|
-
|
101
|
+
it "fails to validate non matching md5" do
|
102
|
+
request['content-md5'] = '12345'
|
103
|
+
expect(ApiAuth.authentic?(request, "123")).to eq false
|
408
104
|
end
|
409
105
|
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
headers = { 'Content-MD5' => "1B2M2Y8AsgTpgAmY7PhCfg==",
|
414
|
-
'Content-Type' => "text/plain",
|
415
|
-
'Date' => Time.now.utc.httpdate }
|
416
|
-
@request = Rack::Request.new(Rack::MockRequest.env_for("/resource.xml?foo=bar&bar=foo", :method => :put).merge!(headers))
|
417
|
-
@signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
418
|
-
end
|
419
|
-
|
420
|
-
it "should return a Rack::Request object after signing it" do
|
421
|
-
ApiAuth.sign!(@request, @access_id, @secret_key).class.to_s.should match("Rack::Request")
|
422
|
-
end
|
423
|
-
|
424
|
-
describe "md5 header" do
|
425
|
-
context "not already provided" do
|
426
|
-
it "should calculate for empty string" do
|
427
|
-
headers = { 'Content-Type' => "text/plain",
|
428
|
-
'Date' => "Mon, 23 Jan 1984 03:29:56 GMT" }
|
429
|
-
request = Rack::Request.new(Rack::MockRequest.env_for("/resource.xml?foo=bar&bar=foo", :method => :put).merge!(headers))
|
430
|
-
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
431
|
-
signed_request.env['Content-MD5'].should == "1B2M2Y8AsgTpgAmY7PhCfg=="
|
432
|
-
end
|
433
|
-
|
434
|
-
it "should calculate for real content" do
|
435
|
-
headers = { 'Content-Type' => "text/plain",
|
436
|
-
'Date' => "Mon, 23 Jan 1984 03:29:56 GMT" }
|
437
|
-
request = Rack::Request.new(Rack::MockRequest.env_for("/resource.xml?foo=bar&bar=foo", :method => :put, :input => "hellow\nworld").merge!(headers))
|
438
|
-
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
439
|
-
signed_request.env['Content-MD5'].should == "G0grublI06013h58g9j8Vw=="
|
440
|
-
end
|
441
|
-
end
|
442
|
-
|
443
|
-
it "should leave the content-md5 alone if provided" do
|
444
|
-
@signed_request.env['Content-MD5'].should == "1B2M2Y8AsgTpgAmY7PhCfg=="
|
445
|
-
end
|
446
|
-
end
|
447
|
-
|
448
|
-
it "should sign the request" do
|
449
|
-
@signed_request.env['Authorization'].should == "APIAuth 1044:#{hmac(@secret_key, @request)}"
|
450
|
-
end
|
451
|
-
|
452
|
-
it "should authenticate a valid request" do
|
453
|
-
ApiAuth.authentic?(@signed_request, @secret_key).should be_true
|
454
|
-
end
|
455
|
-
|
456
|
-
it "should NOT authenticate a non-valid request" do
|
457
|
-
ApiAuth.authentic?(@signed_request, @secret_key+'j').should be_false
|
458
|
-
end
|
459
|
-
|
460
|
-
it "should NOT authenticate a mismatched content-md5 when body has changed" do
|
461
|
-
headers = { 'Content-Type' => "text/plain",
|
462
|
-
'Date' => "Mon, 23 Jan 1984 03:29:56 GMT" }
|
463
|
-
request = Rack::Request.new(Rack::MockRequest.env_for("/resource.xml?foo=bar&bar=foo", :method => :put, :input => "hellow\nworld").merge!(headers))
|
464
|
-
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
465
|
-
changed_request = Rack::Request.new(Rack::MockRequest.env_for("/resource.xml?foo=bar&bar=foo", :method => :put, :input => "goodbye").merge!(headers))
|
466
|
-
signed_request.env['rack.input'] = changed_request.env['rack.input']
|
467
|
-
signed_request.env['CONTENT_LENGTH'] = changed_request.env['CONTENT_LENGTH']
|
468
|
-
ApiAuth.authentic?(signed_request, @secret_key).should be_false
|
469
|
-
end
|
470
|
-
|
471
|
-
it "should NOT authenticate an expired request" do
|
472
|
-
@request.env['Date'] = 16.minutes.ago.utc.httpdate
|
473
|
-
signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
474
|
-
ApiAuth.authentic?(signed_request, @secret_key).should be_false
|
475
|
-
end
|
476
|
-
|
477
|
-
it "should NOT authenticate a request with an invalid date" do
|
478
|
-
@request.env['Date'] = "٢٠١٤-٠٩-٠٨ ١٦:٣١:١٤ +٠٣٠٠"
|
479
|
-
signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
480
|
-
ApiAuth.authentic?(signed_request, @secret_key).should be_false
|
481
|
-
end
|
482
|
-
|
483
|
-
it "should retrieve the access_id" do
|
484
|
-
ApiAuth.access_id(@signed_request).should == "1044"
|
485
|
-
end
|
486
|
-
|
106
|
+
it "fails to validate expired requests" do
|
107
|
+
request['date'] = 16.minutes.ago.utc.httpdate
|
108
|
+
expect(ApiAuth.authentic?(request, "123")).to eq false
|
487
109
|
end
|
488
110
|
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
@request.headers.merge!({
|
493
|
-
'content-type' => 'text/plain',
|
494
|
-
'content-md5' => '1B2M2Y8AsgTpgAmY7PhCfg==',
|
495
|
-
'date' => Time.now.utc.httpdate
|
496
|
-
})
|
497
|
-
@headers = ApiAuth::Headers.new(@request)
|
498
|
-
@signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
499
|
-
end
|
500
|
-
|
501
|
-
it "should return a HTTPI object after signing it" do
|
502
|
-
ApiAuth.sign!(@request, @access_id, @secret_key).class.to_s.should match("HTTPI::Request")
|
503
|
-
end
|
504
|
-
|
505
|
-
describe "md5 header" do
|
506
|
-
context "not already provided" do
|
507
|
-
it "should calculate for empty string" do
|
508
|
-
request = Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
|
509
|
-
'content-type' => 'text/plain',
|
510
|
-
'date' => "Mon, 23 Jan 1984 03:29:56 GMT")
|
511
|
-
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
512
|
-
signed_request['Content-MD5'].should == "1B2M2Y8AsgTpgAmY7PhCfg=="
|
513
|
-
end
|
514
|
-
|
515
|
-
it "should calculate for real content" do
|
516
|
-
request = Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
|
517
|
-
'content-type' => 'text/plain',
|
518
|
-
'date' => "Mon, 23 Jan 1984 03:29:56 GMT")
|
519
|
-
request.body = "hello\nworld"
|
520
|
-
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
521
|
-
signed_request['Content-MD5'].should == "kZXQvrKoieG+Be1rsZVINw=="
|
522
|
-
end
|
523
|
-
end
|
524
|
-
|
525
|
-
it "should leave the content-md5 alone if provided" do
|
526
|
-
@signed_request.headers['Content-MD5'].should == '1B2M2Y8AsgTpgAmY7PhCfg=='
|
527
|
-
end
|
528
|
-
end
|
529
|
-
|
530
|
-
it "should sign the request" do
|
531
|
-
@signed_request.headers['Authorization'].should == "APIAuth 1044:#{hmac(@secret_key, @request)}"
|
532
|
-
end
|
533
|
-
|
534
|
-
it "should authenticate a valid request" do
|
535
|
-
ApiAuth.authentic?(@signed_request, @secret_key).should be_true
|
536
|
-
end
|
537
|
-
|
538
|
-
it "should NOT authenticate a non-valid request" do
|
539
|
-
ApiAuth.authentic?(@signed_request, @secret_key+'j').should be_false
|
540
|
-
end
|
541
|
-
|
542
|
-
it "should NOT authenticate a mismatched content-md5 when body has changed" do
|
543
|
-
request = Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
|
544
|
-
'content-type' => 'text/plain',
|
545
|
-
'date' => "Mon, 23 Jan 1984 03:29:56 GMT")
|
546
|
-
request.body = "hello\nworld"
|
547
|
-
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
548
|
-
signed_request.body = "goodbye"
|
549
|
-
ApiAuth.authentic?(signed_request, @secret_key).should be_false
|
550
|
-
end
|
551
|
-
|
552
|
-
it "should NOT authenticate an expired request" do
|
553
|
-
@request.headers['Date'] = 16.minutes.ago.utc.httpdate
|
554
|
-
signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
555
|
-
ApiAuth.authentic?(signed_request, @secret_key).should be_false
|
556
|
-
end
|
557
|
-
|
558
|
-
it "should NOT authenticate a request with an invalid date" do
|
559
|
-
@request.headers['Date'] = "٢٠١٤-٠٩-٠٨ ١٦:٣١:١٤ +٠٣٠٠"
|
560
|
-
signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
561
|
-
ApiAuth.authentic?(signed_request, @secret_key).should be_false
|
562
|
-
end
|
563
|
-
|
564
|
-
it "should retrieve the access_id" do
|
565
|
-
ApiAuth.access_id(@signed_request).should == "1044"
|
566
|
-
end
|
111
|
+
it "fails to validate if the date is invalid" do
|
112
|
+
request['date'] = "٢٠١٤-٠٩-٠٨ ١٦:٣١:١٤ +٠٣٠٠"
|
113
|
+
expect(ApiAuth.authentic?(request, "123")).to eq false
|
567
114
|
end
|
568
115
|
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
@faraday_conn.put '/resource.xml?foo=bar&bar=foo' do |request|
|
582
|
-
@request = request
|
583
|
-
@request.headers.merge!({'Content-MD5' => "1B2M2Y8AsgTpgAmY7PhCfg==",
|
584
|
-
'content-type' => 'text/plain',
|
585
|
-
'DATE' => Time.now.utc.httpdate})
|
586
|
-
end
|
587
|
-
|
588
|
-
@headers = ApiAuth::Headers.new(@request)
|
589
|
-
@signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
590
|
-
end
|
591
|
-
|
592
|
-
it "should return a Faraday::Request object after signing it" do
|
593
|
-
ApiAuth.sign!(@request, @access_id, @secret_key).class.to_s.should match("Faraday::Request")
|
594
|
-
end
|
595
|
-
|
596
|
-
describe "md5 header" do
|
597
|
-
context "not already provided" do
|
598
|
-
it "should calculate for empty string" do
|
599
|
-
@faraday_conn.put '/resource.xml?foo=bar&bar=foo' do |request|
|
600
|
-
request.headers.merge!({'content-type' => 'text/plain',
|
601
|
-
'DATE' => 'Mon, 23 Jan 1984 03:29:56 GMT'})
|
602
|
-
|
603
|
-
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
604
|
-
signed_request['Content-MD5'].should == "1B2M2Y8AsgTpgAmY7PhCfg=="
|
605
|
-
end
|
606
|
-
end
|
607
|
-
|
608
|
-
it "should calculate for real content" do
|
609
|
-
@faraday_conn.put '/resource.xml?foo=bar&bar=foo' do |request|
|
610
|
-
request.headers.merge!({'content-type' => 'text/plain',
|
611
|
-
'DATE' => 'Mon, 23 Jan 1984 03:29:56 GMT'})
|
612
|
-
request.body = "hello\nworld"
|
613
|
-
|
614
|
-
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
615
|
-
signed_request['Content-MD5'].should == "kZXQvrKoieG+Be1rsZVINw=="
|
616
|
-
end
|
617
|
-
end
|
618
|
-
end
|
619
|
-
|
620
|
-
it "should leave the content-md5 alone if provided" do
|
621
|
-
@signed_request.headers['Content-MD5'].should == '1B2M2Y8AsgTpgAmY7PhCfg=='
|
622
|
-
end
|
623
|
-
end
|
624
|
-
|
625
|
-
it "should sign the request" do
|
626
|
-
@signed_request.headers['Authorization'].should == "APIAuth 1044:#{hmac(@secret_key, @request)}"
|
627
|
-
end
|
628
|
-
|
629
|
-
it "should authenticate a valid request with parameters" do
|
630
|
-
ApiAuth.authentic?(@signed_request, @secret_key).should be_true
|
631
|
-
end
|
116
|
+
context "canonical string contains the http_method" do
|
117
|
+
let(:request){
|
118
|
+
new_request = Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
|
119
|
+
'content-type' => 'text/plain',
|
120
|
+
'content-md5' => '1B2M2Y8AsgTpgAmY7PhCfg==',
|
121
|
+
'date' => Time.now.utc.httpdate
|
122
|
+
)
|
123
|
+
canonical_string = ApiAuth::Headers.new(new_request).canonical_string_with_http_method
|
124
|
+
signature = hmac("123", new_request, canonical_string)
|
125
|
+
new_request["Authorization"] = "APIAuth 1044:#{signature}"
|
126
|
+
new_request
|
127
|
+
}
|
632
128
|
|
633
|
-
it "
|
634
|
-
ApiAuth.authentic?(
|
129
|
+
it "validates for canonical_strings containing the http_method" do
|
130
|
+
expect(ApiAuth.authentic?(request, "123")).to eq true
|
635
131
|
end
|
636
132
|
|
637
|
-
it "
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
644
|
-
signed_request.body = 'goodbye'
|
645
|
-
ApiAuth.authentic?(signed_request, @secret_key).should be_false
|
646
|
-
end
|
133
|
+
it "fails to validate if the request method differs" do
|
134
|
+
canonical_string = ApiAuth::Headers.new(request).canonical_string_with_http_method('POST')
|
135
|
+
signature = hmac("123", request, canonical_string)
|
136
|
+
request["Authorization"] = "APIAuth 1044:#{signature}"
|
137
|
+
expect(ApiAuth.authentic?(request, "123")).to eq false
|
647
138
|
end
|
139
|
+
end
|
140
|
+
end
|
648
141
|
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
142
|
+
describe ".access_id" do
|
143
|
+
context "normal APIAuth Auth header" do
|
144
|
+
let(:request){
|
145
|
+
RestClient::Request.new(
|
146
|
+
:url => "http://google.com",
|
147
|
+
:method => :get,
|
148
|
+
:headers => {:authorization => "APIAuth 1044:aGVsbG8gd29ybGQ="}
|
149
|
+
)
|
150
|
+
}
|
654
151
|
|
655
|
-
it "
|
656
|
-
|
657
|
-
signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
658
|
-
ApiAuth.authentic?(signed_request, @secret_key).should be_false
|
152
|
+
it "parses it from the Auth Header" do
|
153
|
+
expect(ApiAuth.access_id(request)).to eq("1044")
|
659
154
|
end
|
155
|
+
end
|
660
156
|
|
661
|
-
|
662
|
-
|
663
|
-
|
157
|
+
context "Corporate prefixed APIAuth header" do
|
158
|
+
let(:request){
|
159
|
+
RestClient::Request.new(
|
160
|
+
:url => "http://google.com",
|
161
|
+
:method => :get,
|
162
|
+
:headers => {:authorization => "Corporate APIAuth 1044:aGVsbG8gd29ybGQ="}
|
163
|
+
)
|
164
|
+
}
|
664
165
|
|
665
|
-
|
666
|
-
|
667
|
-
it "should return urls with a query string" do
|
668
|
-
req = ::ApiAuth::RequestDrivers::FaradayRequest.new(@request)
|
669
|
-
req.request_uri.should == '/resource.xml?bar=foo&foo=bar'
|
670
|
-
end
|
671
|
-
end
|
672
|
-
|
673
|
-
context 'without parameters' do
|
674
|
-
it "should return urls with no query string" do
|
675
|
-
@faraday_conn.put '/resource.xml' do |request|
|
676
|
-
request.headers.merge!({'content-type' => 'text/plain',
|
677
|
-
'DATE' => Time.now.utc.httpdate})
|
678
|
-
req = ::ApiAuth::RequestDrivers::FaradayRequest.new(request)
|
679
|
-
req.request_uri.should == '/resource.xml'
|
680
|
-
end
|
681
|
-
end
|
682
|
-
end
|
166
|
+
it "parses it from the Auth Header" do
|
167
|
+
expect(ApiAuth.access_id(request)).to eq("1044")
|
683
168
|
end
|
684
169
|
end
|
685
170
|
end
|
686
|
-
|
687
171
|
end
|