songkick-oauth2-provider 0.10.2 → 0.10.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/History.txt +7 -0
  3. data/README.rdoc +18 -11
  4. data/example/README.rdoc +1 -1
  5. data/example/application.rb +9 -9
  6. data/example/schema.rb +1 -1
  7. data/example/views/authorize.erb +2 -2
  8. data/example/views/layout.erb +4 -4
  9. data/example/views/login.erb +2 -2
  10. data/example/views/new_client.erb +1 -1
  11. data/example/views/new_user.erb +1 -1
  12. data/lib/songkick/oauth2/model.rb +8 -6
  13. data/lib/songkick/oauth2/model/authorization.rb +31 -31
  14. data/lib/songkick/oauth2/model/client.rb +15 -15
  15. data/lib/songkick/oauth2/model/client_owner.rb +2 -2
  16. data/lib/songkick/oauth2/model/hashing.rb +3 -3
  17. data/lib/songkick/oauth2/model/helpers.rb +16 -0
  18. data/lib/songkick/oauth2/model/resource_owner.rb +4 -4
  19. data/lib/songkick/oauth2/provider.rb +16 -16
  20. data/lib/songkick/oauth2/provider/access_token.rb +20 -15
  21. data/lib/songkick/oauth2/provider/authorization.rb +43 -42
  22. data/lib/songkick/oauth2/provider/error.rb +4 -4
  23. data/lib/songkick/oauth2/provider/exchange.rb +46 -46
  24. data/lib/songkick/oauth2/router.rb +13 -13
  25. data/lib/songkick/oauth2/schema.rb +11 -3
  26. data/lib/songkick/oauth2/schema/20120828112156_songkick_oauth2_schema_original_schema.rb +2 -2
  27. data/lib/songkick/oauth2/schema/20121024180930_songkick_oauth2_schema_add_authorization_index.rb +3 -3
  28. data/lib/songkick/oauth2/schema/20121025180447_songkick_oauth2_schema_add_unique_indexes.rb +7 -7
  29. data/spec/request_helpers.rb +25 -21
  30. data/spec/songkick/oauth2/model/authorization_spec.rb +56 -56
  31. data/spec/songkick/oauth2/model/client_spec.rb +9 -9
  32. data/spec/songkick/oauth2/model/helpers_spec.rb +26 -0
  33. data/spec/songkick/oauth2/model/resource_owner_spec.rb +13 -13
  34. data/spec/songkick/oauth2/provider/access_token_spec.rb +32 -20
  35. data/spec/songkick/oauth2/provider/authorization_spec.rb +73 -62
  36. data/spec/songkick/oauth2/provider/exchange_spec.rb +72 -72
  37. data/spec/songkick/oauth2/provider_spec.rb +101 -101
  38. data/spec/spec_helper.rb +5 -3
  39. data/spec/test_app/helper.rb +11 -7
  40. data/spec/test_app/provider/application.rb +12 -12
  41. data/spec/test_app/provider/views/authorize.erb +2 -2
  42. metadata +71 -93
@@ -8,91 +8,91 @@ describe Songkick::OAuth2::Provider::Exchange do
8
8
  @authorization = create_authorization(:client => @client, :owner => @bob, :code => 'a_fake_code', :scope => 'foo bar')
9
9
  Songkick::OAuth2.stub(:random_string).and_return('random_string')
10
10
  end
11
-
11
+
12
12
  let(:exchange) { Songkick::OAuth2::Provider::Exchange.new(@bob, params) }
13
-
13
+
14
14
  shared_examples_for "validates required parameters" do
15
15
  describe "missing grant_type" do
16
16
  before { params.delete('client_id') }
17
-
17
+
18
18
  it "is invalid" do
19
19
  exchange.error.should == "invalid_request"
20
20
  exchange.error_description.should == "Missing required parameter client_id"
21
21
  end
22
22
  end
23
-
23
+
24
24
  describe "with an unknown grant type" do
25
25
  before { params['grant_type'] = 'unknown' }
26
-
26
+
27
27
  it "is invalid" do
28
28
  exchange.error.should == "unsupported_grant_type"
29
29
  exchange.error_description.should == "The grant type unknown is not recognized"
30
30
  end
31
31
  end
32
-
32
+
33
33
  describe "missing client_id" do
34
34
  before { params.delete('client_id') }
35
-
35
+
36
36
  it "is invalid" do
37
37
  exchange.error.should == "invalid_request"
38
38
  exchange.error_description.should == "Missing required parameter client_id"
39
39
  end
40
40
  end
41
-
41
+
42
42
  describe "with an unknown client_id" do
43
43
  before { params['client_id'] = "unknown" }
44
-
44
+
45
45
  it "is invalid" do
46
46
  exchange.error.should == "invalid_client"
47
47
  exchange.error_description.should == "Unknown client ID unknown"
48
48
  end
49
49
  end
50
-
50
+
51
51
  describe "missing client_secret" do
52
52
  before { params.delete('client_secret') }
53
-
53
+
54
54
  it "is invalid" do
55
55
  exchange.error.should == "invalid_request"
56
56
  exchange.error_description.should == "Missing required parameter client_secret"
57
57
  end
58
58
  end
59
-
59
+
60
60
  describe "with a mismatched client_secret" do
61
61
  before { params['client_secret'] = "nosoupforyou" }
62
-
62
+
63
63
  it "is invalid" do
64
64
  exchange.error.should == "invalid_client"
65
65
  exchange.error_description.should == "Parameter client_secret does not match"
66
66
  end
67
67
  end
68
-
68
+
69
69
  describe "with lesser scope than the authorization code represents" do
70
70
  before { params['scope'] = 'bar' }
71
-
71
+
72
72
  it "is valid" do
73
73
  exchange.error.should be_nil
74
74
  end
75
75
  end
76
-
76
+
77
77
  describe "with scopes not covered by the authorization code" do
78
78
  before { params['scope'] = 'qux' }
79
-
79
+
80
80
  it "is invalid" do
81
81
  exchange.error.should == 'invalid_scope'
82
82
  exchange.error_description.should == 'The request scope was never granted by the user'
83
83
  end
84
84
  end
85
85
  end
86
-
86
+
87
87
  shared_examples_for "valid token request" do
88
88
  before do
89
89
  Songkick::OAuth2.stub(:random_string).and_return('random_access_token')
90
90
  end
91
-
91
+
92
92
  it "is valid" do
93
93
  exchange.error.should be_nil
94
94
  end
95
-
95
+
96
96
  it "updates the Authorization with tokens" do
97
97
  exchange.update_authorization
98
98
  authorization.reload
@@ -101,7 +101,7 @@ describe Songkick::OAuth2::Provider::Exchange do
101
101
  authorization.refresh_token.should be_nil
102
102
  end
103
103
  end
104
-
104
+
105
105
  describe "using authorization_code grant type" do
106
106
  let(:params) { { 'client_id' => @client.client_id,
107
107
  'client_secret' => @client.client_secret,
@@ -109,73 +109,73 @@ describe Songkick::OAuth2::Provider::Exchange do
109
109
  'code' => @authorization.code,
110
110
  'redirect_uri' => @client.redirect_uri }
111
111
  }
112
-
112
+
113
113
  let(:authorization) { @authorization }
114
-
114
+
115
115
  it_should_behave_like "validates required parameters"
116
116
  it_should_behave_like "valid token request"
117
-
117
+
118
118
  describe "missing redirect_uri" do
119
119
  before { params.delete('redirect_uri') }
120
-
120
+
121
121
  it "is invalid" do
122
122
  exchange.error.should == "invalid_request"
123
123
  exchange.error_description.should == "Missing required parameter redirect_uri"
124
124
  end
125
125
  end
126
-
126
+
127
127
  describe "with a mismatched redirect_uri" do
128
128
  before { params['redirect_uri'] = "http://songkick.com" }
129
-
129
+
130
130
  it "is invalid" do
131
131
  exchange.error.should == "redirect_uri_mismatch"
132
132
  exchange.error_description.should == "Parameter redirect_uri does not match registered URI"
133
133
  end
134
-
134
+
135
135
  describe "when the client has not registered a redirect_uri" do
136
136
  before { @client.update_attribute(:redirect_uri, nil) }
137
-
137
+
138
138
  it "is valid" do
139
139
  exchange.error.should be_nil
140
140
  end
141
141
  end
142
142
  end
143
-
143
+
144
144
  describe "missing code" do
145
145
  before { params.delete('code') }
146
-
146
+
147
147
  it "is invalid" do
148
148
  exchange.error.should == "invalid_request"
149
149
  exchange.error_description.should == "Missing required parameter code"
150
150
  end
151
151
  end
152
-
152
+
153
153
  describe "with an unknown code" do
154
154
  before { params['code'] = "unknown" }
155
-
155
+
156
156
  it "is invalid" do
157
157
  exchange.error.should == "invalid_grant"
158
158
  exchange.error_description.should == "The access grant you supplied is invalid"
159
159
  end
160
160
  end
161
-
161
+
162
162
  describe "with an expired code" do
163
163
  before { @authorization.update_attribute(:expires_at, 1.day.ago) }
164
-
164
+
165
165
  it "is invalid" do
166
166
  exchange.error.should == "invalid_grant"
167
167
  exchange.error_description.should == "The access grant you supplied is invalid"
168
168
  end
169
169
  end
170
170
  end
171
-
171
+
172
172
  describe "using password grant type" do
173
173
  let(:params) { { 'client_id' => @client.client_id,
174
174
  'client_secret' => @client.client_secret,
175
175
  'grant_type' => 'password',
176
176
  'password' => 'soldier' }
177
177
  }
178
-
178
+
179
179
  before do
180
180
  Songkick::OAuth2::Provider.handle_passwords do |client, username, password, scopes|
181
181
  user = TestApp::User[username]
@@ -186,54 +186,54 @@ describe Songkick::OAuth2::Provider::Exchange do
186
186
  end
187
187
  end
188
188
  end
189
-
189
+
190
190
  describe "for a user with existing authorization" do
191
191
  let(:authorization) { @authorization }
192
192
  before { params['username'] = 'Bob' }
193
-
193
+
194
194
  it_should_behave_like "validates required parameters"
195
195
  it_should_behave_like "valid token request"
196
-
196
+
197
197
  describe "missing username" do
198
198
  before { params.delete('username') }
199
-
199
+
200
200
  it "is invalid" do
201
201
  exchange.error.should == 'invalid_request'
202
202
  exchange.error_description.should == 'Missing required parameter username'
203
203
  end
204
204
  end
205
-
205
+
206
206
  describe "missing password" do
207
207
  before { params.delete('password') }
208
-
208
+
209
209
  it "is invalid" do
210
210
  exchange.error.should == 'invalid_request'
211
211
  exchange.error_description.should == 'Missing required parameter password'
212
212
  end
213
213
  end
214
-
214
+
215
215
  describe "with a bad password" do
216
216
  before { params['password'] = 'bad' }
217
-
217
+
218
218
  it "is invalid" do
219
219
  exchange.error.should == 'invalid_grant'
220
220
  exchange.error_description.should == 'The access grant you supplied is invalid'
221
221
  end
222
222
  end
223
223
  end
224
-
224
+
225
225
  describe "for a user with no existing authorization" do
226
226
  let(:authorization) { Songkick::OAuth2::Model::Authorization.find_by_oauth2_resource_owner_id(@alice.id) }
227
227
  before { params['username'] = 'Alice' }
228
-
228
+
229
229
  it_should_behave_like "validates required parameters"
230
230
  it_should_behave_like "valid token request"
231
-
231
+
232
232
  describe "with ungranted but permissible scopes" do
233
233
  before { params['scope'] = 'lol' }
234
234
  it_should_behave_like "validates required parameters"
235
235
  it_should_behave_like "valid token request"
236
-
236
+
237
237
  it "sets the scope from the request" do
238
238
  exchange.update_authorization
239
239
  authorization.reload
@@ -242,7 +242,7 @@ describe Songkick::OAuth2::Provider::Exchange do
242
242
  end
243
243
  end
244
244
  end
245
-
245
+
246
246
  describe "using assertion grant type" do
247
247
  let(:params) { { 'client_id' => @client.client_id,
248
248
  'client_secret' => @client.client_secret,
@@ -250,75 +250,75 @@ describe Songkick::OAuth2::Provider::Exchange do
250
250
  'assertion_type' => 'https://graph.facebook.com/me',
251
251
  'assertion' => 'Bob' }
252
252
  }
253
-
253
+
254
254
  let(:authorization) { @authorization }
255
-
255
+
256
256
  before do
257
257
  Songkick::OAuth2::Provider.filter_assertions { |client| @client == client }
258
-
258
+
259
259
  Songkick::OAuth2::Provider.handle_assertions('https://graph.facebook.com/me') do |client, assertion|
260
260
  user = TestApp::User[assertion]
261
261
  user.grant_access!(client, :scopes => ['foo', 'bar'])
262
262
  end
263
263
  end
264
-
264
+
265
265
  after do
266
266
  Songkick::OAuth2::Provider.clear_assertion_handlers!
267
267
  end
268
-
268
+
269
269
  it_should_behave_like "validates required parameters"
270
270
  it_should_behave_like "valid token request"
271
-
271
+
272
272
  describe "missing assertion_type" do
273
273
  before { params.delete('assertion_type') }
274
-
274
+
275
275
  it "is invalid" do
276
276
  exchange.error.should == 'invalid_request'
277
277
  exchange.error_description.should == 'Missing required parameter assertion_type'
278
278
  end
279
279
  end
280
-
280
+
281
281
  describe "with a non-URI assertion_type" do
282
282
  before { params['assertion_type'] = 'invalid' }
283
-
283
+
284
284
  it "is invalid" do
285
285
  exchange.error.should == 'invalid_request'
286
286
  exchange.error_description.should == 'Parameter assertion_type must be an absolute URI'
287
287
  end
288
288
  end
289
-
289
+
290
290
  describe "missing assertion" do
291
291
  before { params.delete('assertion') }
292
-
292
+
293
293
  it "is invalid" do
294
294
  exchange.error.should == 'invalid_request'
295
295
  exchange.error_description.should == 'Missing required parameter assertion'
296
296
  end
297
297
  end
298
-
298
+
299
299
  describe "with an unrecognized assertion_type" do
300
300
  before { params['assertion_type'] = 'https://oauth.what.com/ohai' }
301
-
301
+
302
302
  it "is invalid" do
303
303
  exchange.error.should == 'unauthorized_client'
304
304
  exchange.error_description.should == 'Client cannot use the given assertion type'
305
305
  end
306
306
  end
307
-
307
+
308
308
  describe "with a client unauthorized to use the assertion scheme" do
309
309
  before do
310
310
  client = Factory(:client)
311
311
  params['client_id'] = client.client_id
312
312
  params['client_secret'] = client.client_secret
313
313
  end
314
-
314
+
315
315
  it "is invalid" do
316
316
  exchange.error.should == 'unauthorized_client'
317
317
  exchange.error_description.should == 'Client cannot use the given assertion type'
318
318
  end
319
319
  end
320
320
  end
321
-
321
+
322
322
  describe "using refresh_token grant type" do
323
323
  before do
324
324
  @refresher = create_authorization(:client => @client,
@@ -327,27 +327,27 @@ describe Songkick::OAuth2::Provider::Exchange do
327
327
  :code => nil,
328
328
  :refresh_token => 'roflscale')
329
329
  end
330
-
330
+
331
331
  let(:params) { { 'client_id' => @client.client_id,
332
332
  'client_secret' => @client.client_secret,
333
333
  'grant_type' => 'refresh_token',
334
334
  'refresh_token' => 'roflscale' }
335
335
  }
336
-
336
+
337
337
  let(:authorization) { @refresher }
338
-
338
+
339
339
  it_should_behave_like "validates required parameters"
340
340
  it_should_behave_like "valid token request"
341
-
341
+
342
342
  describe "with unknown refresh_token" do
343
343
  before { params['refresh_token'] = 'woops' }
344
-
344
+
345
345
  it "is invalid" do
346
346
  exchange.error.should == "invalid_grant"
347
347
  exchange.error_description.should == "The access grant you supplied is invalid"
348
348
  end
349
349
  end
350
-
350
+
351
351
  end
352
352
  end
353
353
 
@@ -1,21 +1,21 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Songkick::OAuth2::Provider do
4
- before(:all) { TestApp::Provider.start(8000) }
4
+ include RequestHelpers
5
+
6
+ before(:all) { TestApp::Provider.start(RequestHelpers::SERVER_PORT) }
5
7
  after(:all) { TestApp::Provider.stop }
6
-
8
+
7
9
  let(:params) { { 'response_type' => 'code',
8
10
  'client_id' => @client.client_id,
9
11
  'redirect_uri' => @client.redirect_uri }
10
12
  }
11
-
13
+
12
14
  before do
13
15
  @client = Factory(:client, :name => 'Test client')
14
16
  @owner = TestApp::User['Bob']
15
17
  end
16
-
17
- include RequestHelpers
18
-
18
+
19
19
  describe "access grant request" do
20
20
  shared_examples_for "asks for user permission" do
21
21
  it "creates an authorization" do
@@ -23,7 +23,7 @@ describe Songkick::OAuth2::Provider do
23
23
  Songkick::OAuth2::Provider::Authorization.should_receive(:new).with(@owner, params, nil).and_return(auth)
24
24
  get(params)
25
25
  end
26
-
26
+
27
27
  it "displays an authorization page" do
28
28
  response = get(params)
29
29
  response.code.to_i.should == 200
@@ -31,30 +31,30 @@ describe Songkick::OAuth2::Provider do
31
31
  response['Content-Type'].should =~ /text\/html/
32
32
  end
33
33
  end
34
-
34
+
35
35
  describe "with valid parameters" do
36
36
  it_should_behave_like "asks for user permission"
37
37
  end
38
-
38
+
39
39
  describe "for token requests" do
40
40
  before { params['response_type'] = 'token' }
41
41
  it_should_behave_like "asks for user permission"
42
42
  end
43
-
43
+
44
44
  describe "for code_and_token requests" do
45
45
  before { params['response_type'] = 'code_and_token' }
46
46
  it_should_behave_like "asks for user permission"
47
47
  end
48
-
48
+
49
49
  describe "enforcing SSL" do
50
50
  before { Songkick::OAuth2::Provider.enforce_ssl = true }
51
-
51
+
52
52
  it "does not allow non-SSL requests" do
53
53
  response = get(params)
54
54
  validate_response(response, 400, 'WAT')
55
55
  end
56
56
  end
57
-
57
+
58
58
  describe "when there is already a pending authorization from the user" do
59
59
  before do
60
60
  @authorization = create_authorization(
@@ -63,31 +63,31 @@ describe Songkick::OAuth2::Provider do
63
63
  :code => 'pending_code',
64
64
  :scope => 'offline_access')
65
65
  end
66
-
66
+
67
67
  it "immediately redirects with the code" do
68
68
  response = get(params)
69
69
  response.code.to_i.should == 302
70
70
  response['location'].should == 'https://client.example.com/cb?code=pending_code'
71
71
  end
72
-
72
+
73
73
  describe "when the client is requesting scopes it already has access to" do
74
74
  before { params['scope'] = 'offline_access' }
75
-
75
+
76
76
  it "immediately redirects with the code" do
77
77
  response = get(params)
78
78
  response.code.to_i.should == 302
79
79
  response['location'].should == 'https://client.example.com/cb?code=pending_code&scope=offline_access'
80
80
  end
81
81
  end
82
-
82
+
83
83
  describe "when the client is requesting scopes it doesn't have yet" do
84
84
  before { params['scope'] = 'wall_publish' }
85
85
  it_should_behave_like "asks for user permission"
86
86
  end
87
-
87
+
88
88
  describe "and the authorization does not have a code" do
89
89
  before { @authorization.update_attribute(:code, nil) }
90
-
90
+
91
91
  it "generates a new code and redirects" do
92
92
  Songkick::OAuth2::Model::Authorization.should_not_receive(:create)
93
93
  Songkick::OAuth2::Model::Authorization.should_not_receive(:new)
@@ -97,13 +97,13 @@ describe Songkick::OAuth2::Provider do
97
97
  response['location'].should == 'https://client.example.com/cb?code=new_code'
98
98
  end
99
99
  end
100
-
100
+
101
101
  describe "and the authorization is expired" do
102
102
  before { @authorization.update_attribute(:expires_at, 2.hours.ago) }
103
103
  it_should_behave_like "asks for user permission"
104
104
  end
105
105
  end
106
-
106
+
107
107
  describe "when there is already a completed authorization from the user" do
108
108
  before do
109
109
  @authorization = create_authorization(
@@ -112,39 +112,39 @@ describe Songkick::OAuth2::Provider do
112
112
  :code => nil,
113
113
  :access_token => Songkick::OAuth2.hashify('complete_token'))
114
114
  end
115
-
115
+
116
116
  it "immediately redirects with a new code" do
117
117
  Songkick::OAuth2.should_receive(:random_string).and_return('new_code')
118
118
  response = get(params)
119
119
  response.code.to_i.should == 302
120
120
  response['location'].should == 'https://client.example.com/cb?code=new_code'
121
121
  end
122
-
122
+
123
123
  describe "for token requests" do
124
124
  before { params['response_type'] = 'token' }
125
-
125
+
126
126
  it "immediately redirects with a new token" do
127
127
  Songkick::OAuth2.should_receive(:random_string).and_return('new_access_token')
128
128
  response = get(params)
129
129
  response.code.to_i.should == 302
130
130
  response['location'].should == 'https://client.example.com/cb#access_token=new_access_token'
131
131
  end
132
-
132
+
133
133
  describe "with an invalid client_id" do
134
134
  before { params['client_id'] = 'unknown_id' }
135
-
135
+
136
136
  it "does not generate any new tokens" do
137
137
  Songkick::OAuth2.should_not_receive(:random_string)
138
138
  get(params)
139
139
  end
140
140
  end
141
141
  end
142
-
142
+
143
143
  it "does not create a new Authorization" do
144
144
  get(params)
145
145
  Songkick::OAuth2::Model::Authorization.count.should == 1
146
146
  end
147
-
147
+
148
148
  it "keeps the code and access token on the Authorization" do
149
149
  get(params)
150
150
  authorization = Songkick::OAuth2::Model::Authorization.first
@@ -152,48 +152,48 @@ describe Songkick::OAuth2::Provider do
152
152
  authorization.access_token_hash.should_not be_nil
153
153
  end
154
154
  end
155
-
155
+
156
156
  describe "with no parameters" do
157
157
  let(:params) { {} }
158
-
158
+
159
159
  it "renders an error page" do
160
160
  response = get(params)
161
161
  validate_response(response, 400, 'WAT')
162
162
  end
163
163
  end
164
-
164
+
165
165
  describe "with a redirect_uri and no client_id" do
166
166
  let(:params) { {'redirect_uri' => 'http://evilsite.com/callback'} }
167
-
167
+
168
168
  it "renders an error page" do
169
169
  response = get(params)
170
170
  validate_response(response, 400, 'WAT')
171
171
  end
172
172
  end
173
-
173
+
174
174
  describe "with a client_id and a bad redirect_uri" do
175
175
  let(:params) { {'redirect_uri' => 'http://evilsite.com/callback',
176
176
  'client_id' => @client.client_id} }
177
-
177
+
178
178
  it "redirects to the client's registered redirect_uri" do
179
179
  response = get(params)
180
180
  response.code.to_i.should == 302
181
181
  response['location'].should == 'https://client.example.com/cb?error=invalid_request&error_description=Missing+required+parameter+response_type'
182
182
  end
183
183
  end
184
-
184
+
185
185
  describe "with an invalid request" do
186
186
  before { params.delete('response_type') }
187
-
187
+
188
188
  it "redirects to the client's redirect_uri on error" do
189
189
  response = get(params)
190
190
  response.code.to_i.should == 302
191
191
  response['location'].should == 'https://client.example.com/cb?error=invalid_request&error_description=Missing+required+parameter+response_type'
192
192
  end
193
-
193
+
194
194
  describe "with a state parameter" do
195
195
  before { params['state'] = "Facebook\nmesses this\nup" }
196
-
196
+
197
197
  it "redirects to the client, including the state param" do
198
198
  response = get(params)
199
199
  response.code.to_i.should == 302
@@ -202,55 +202,55 @@ describe Songkick::OAuth2::Provider do
202
202
  end
203
203
  end
204
204
  end
205
-
205
+
206
206
  describe "authorization confirmation from the user" do
207
207
  let(:mock_auth) do
208
- mock = mock Songkick::OAuth2::Provider::Authorization,
209
- :redirect_uri => 'http://example.com/',
210
- :response_status => 302
211
-
208
+ mock = double Songkick::OAuth2::Provider::Authorization,
209
+ :redirect_uri => 'http://example.com/',
210
+ :response_status => 302
211
+
212
212
  Songkick::OAuth2::Provider::Authorization.stub(:new).and_return(mock)
213
213
  mock
214
214
  end
215
-
215
+
216
216
  describe "without the user's permission" do
217
217
  before { params['allow'] = '' }
218
-
218
+
219
219
  it "does not grant access" do
220
220
  mock_auth.should_receive(:deny_access!)
221
221
  allow_or_deny(params)
222
222
  end
223
-
223
+
224
224
  it "redirects to the client with an error" do
225
225
  response = allow_or_deny(params)
226
226
  response.code.to_i.should == 302
227
227
  response['location'].should == 'https://client.example.com/cb?error=access_denied&error_description=The+user+denied+you+access'
228
228
  end
229
229
  end
230
-
230
+
231
231
  describe "with valid parameters and user permission" do
232
232
  before { Songkick::OAuth2.stub(:random_string).and_return('foo') }
233
233
  before { params['allow'] = '1' }
234
-
234
+
235
235
  describe "for code requests" do
236
236
  it "grants access" do
237
237
  mock_auth.should_receive(:grant_access!)
238
238
  allow_or_deny(params)
239
239
  end
240
-
240
+
241
241
  it "redirects to the client with an authorization code" do
242
242
  response = allow_or_deny(params)
243
243
  response.code.to_i.should == 302
244
244
  response['location'].should == 'https://client.example.com/cb?code=foo'
245
245
  end
246
-
246
+
247
247
  it "passes the state parameter through" do
248
248
  params['state'] = 'illinois'
249
249
  response = allow_or_deny(params)
250
250
  response.code.to_i.should == 302
251
251
  response['location'].should == 'https://client.example.com/cb?code=foo&state=illinois'
252
252
  end
253
-
253
+
254
254
  it "passes the scope parameter through" do
255
255
  params['scope'] = 'foo bar'
256
256
  response = allow_or_deny(params)
@@ -258,28 +258,28 @@ describe Songkick::OAuth2::Provider do
258
258
  response['location'].should == 'https://client.example.com/cb?code=foo&scope=foo+bar'
259
259
  end
260
260
  end
261
-
261
+
262
262
  describe "for token requests" do
263
263
  before { params['response_type'] = 'token' }
264
-
264
+
265
265
  it "grants access" do
266
266
  mock_auth.should_receive(:grant_access!)
267
267
  allow_or_deny(params)
268
268
  end
269
-
269
+
270
270
  it "redirects to the client with an access token" do
271
271
  response = allow_or_deny(params)
272
272
  response.code.to_i.should == 302
273
273
  response['location'].should == 'https://client.example.com/cb#access_token=foo&expires_in=10800'
274
274
  end
275
-
275
+
276
276
  it "passes the state parameter through" do
277
277
  params['state'] = 'illinois'
278
278
  response = allow_or_deny(params)
279
279
  response.code.to_i.should == 302
280
280
  response['location'].should == 'https://client.example.com/cb#access_token=foo&expires_in=10800&state=illinois'
281
281
  end
282
-
282
+
283
283
  it "passes the scope parameter through" do
284
284
  params['scope'] = 'foo bar'
285
285
  response = allow_or_deny(params)
@@ -287,28 +287,28 @@ describe Songkick::OAuth2::Provider do
287
287
  response['location'].should == 'https://client.example.com/cb#access_token=foo&expires_in=10800&scope=foo+bar'
288
288
  end
289
289
  end
290
-
290
+
291
291
  describe "for code_and_token requests" do
292
292
  before { params['response_type'] = 'code_and_token' }
293
-
293
+
294
294
  it "grants access" do
295
295
  mock_auth.should_receive(:grant_access!)
296
296
  allow_or_deny(params)
297
297
  end
298
-
298
+
299
299
  it "redirects to the client with an access token" do
300
300
  response = allow_or_deny(params)
301
301
  response.code.to_i.should == 302
302
302
  response['location'].should == 'https://client.example.com/cb?code=foo#access_token=foo&expires_in=10800'
303
303
  end
304
-
304
+
305
305
  it "passes the state parameter through" do
306
306
  params['state'] = 'illinois'
307
307
  response = allow_or_deny(params)
308
308
  response.code.to_i.should == 302
309
309
  response['location'].should == 'https://client.example.com/cb?code=foo&state=illinois#access_token=foo&expires_in=10800'
310
310
  end
311
-
311
+
312
312
  it "passes the scope parameter through" do
313
313
  params['scope'] = 'foo bar'
314
314
  response = allow_or_deny(params)
@@ -318,7 +318,7 @@ describe Songkick::OAuth2::Provider do
318
318
  end
319
319
  end
320
320
  end
321
-
321
+
322
322
  describe "access token request" do
323
323
  before do
324
324
  @client = Factory(:client)
@@ -328,20 +328,20 @@ describe Songkick::OAuth2::Provider do
328
328
  :code => 'a_fake_code',
329
329
  :expires_at => 3.hours.from_now)
330
330
  end
331
-
331
+
332
332
  let(:auth_params) { { 'client_id' => @client.client_id,
333
333
  'client_secret' => @client.client_secret }
334
334
  }
335
-
335
+
336
336
  describe "using authorization_code request" do
337
337
  let(:query_params) { { 'client_id' => @client.client_id,
338
338
  'grant_type' => 'authorization_code',
339
339
  'code' => @authorization.code,
340
340
  'redirect_uri' => @client.redirect_uri }
341
341
  }
342
-
342
+
343
343
  let(:params) { auth_params.merge(query_params) }
344
-
344
+
345
345
  describe "with valid parameters" do
346
346
  it "does not respond to GET" do
347
347
  Songkick::OAuth2::Provider::Authorization.should_not_receive(:new)
@@ -352,7 +352,7 @@ describe Songkick::OAuth2::Provider do
352
352
  'error_description' => 'Bad request: must be a POST request'
353
353
  )
354
354
  end
355
-
355
+
356
356
  it "does not allow client credentials to be passed in the query string" do
357
357
  Songkick::OAuth2::Provider::Authorization.should_not_receive(:new)
358
358
  query_string = {'client_id' => params.delete('client_id'), 'client_secret' => params.delete('client_secret')}
@@ -362,10 +362,10 @@ describe Songkick::OAuth2::Provider do
362
362
  'error_description' => 'Bad request: must not send client credentials in the URI'
363
363
  )
364
364
  end
365
-
365
+
366
366
  describe "enforcing SSL" do
367
367
  before { Songkick::OAuth2::Provider.enforce_ssl = true }
368
-
368
+
369
369
  it "does not allow non-SSL requests" do
370
370
  response = get(params)
371
371
  validate_json_response(response, 400,
@@ -374,30 +374,30 @@ describe Songkick::OAuth2::Provider do
374
374
  )
375
375
  end
376
376
  end
377
-
377
+
378
378
  it "creates a Token when using Basic Auth" do
379
379
  token = mock_request(Songkick::OAuth2::Provider::Exchange, :response_body => 'Hello')
380
380
  Songkick::OAuth2::Provider::Exchange.should_receive(:new).with(@owner, params, nil).and_return(token)
381
381
  post_basic_auth(auth_params, query_params)
382
382
  end
383
-
383
+
384
384
  it "creates a Token when passing params in the POST body" do
385
385
  token = mock_request(Songkick::OAuth2::Provider::Exchange, :response_body => 'Hello')
386
386
  Songkick::OAuth2::Provider::Exchange.should_receive(:new).with(@owner, params, nil).and_return(token)
387
387
  post(params)
388
388
  end
389
-
389
+
390
390
  it "returns a successful response" do
391
391
  Songkick::OAuth2.stub(:random_string).and_return('random_access_token')
392
392
  response = post_basic_auth(auth_params, query_params)
393
393
  validate_json_response(response, 200, 'access_token' => 'random_access_token', 'expires_in' => 10800)
394
394
  end
395
-
395
+
396
396
  describe "with a scope parameter" do
397
397
  before do
398
398
  @authorization.update_attribute(:scope, 'foo bar')
399
399
  end
400
-
400
+
401
401
  it "passes the scope back in the success response" do
402
402
  Songkick::OAuth2.stub(:random_string).and_return('random_access_token')
403
403
  response = post_basic_auth(auth_params, query_params)
@@ -409,10 +409,10 @@ describe Songkick::OAuth2::Provider do
409
409
  end
410
410
  end
411
411
  end
412
-
412
+
413
413
  describe "with invalid parameters" do
414
414
  before { query_params.delete('code') }
415
-
415
+
416
416
  it "returns an error response" do
417
417
  response = post_basic_auth(auth_params, query_params)
418
418
  validate_json_response(response, 400,
@@ -421,10 +421,10 @@ describe Songkick::OAuth2::Provider do
421
421
  )
422
422
  end
423
423
  end
424
-
424
+
425
425
  describe "with mismatched client_id in POST params and Basic Auth params" do
426
426
  before { query_params['client_id'] = 'foo' }
427
-
427
+
428
428
  it "returns an error response" do
429
429
  response = post_basic_auth(auth_params, query_params)
430
430
  validate_json_response(response, 400,
@@ -433,13 +433,13 @@ describe Songkick::OAuth2::Provider do
433
433
  )
434
434
  end
435
435
  end
436
-
436
+
437
437
  describe "when there is an Authorization with code and token" do
438
438
  before do
439
439
  @authorization.update_attributes(:code => 'pending_code', :access_token => 'working_token')
440
440
  Songkick::OAuth2.stub(:random_string).and_return('random_access_token')
441
441
  end
442
-
442
+
443
443
  it "returns a new access token" do
444
444
  response = post(params)
445
445
  validate_json_response(response, 200,
@@ -447,7 +447,7 @@ describe Songkick::OAuth2::Provider do
447
447
  'expires_in' => 10800
448
448
  )
449
449
  end
450
-
450
+
451
451
  it "exchanges the code for the new token on the existing Authorization" do
452
452
  post(params)
453
453
  @authorization.reload
@@ -457,7 +457,7 @@ describe Songkick::OAuth2::Provider do
457
457
  end
458
458
  end
459
459
  end
460
-
460
+
461
461
  describe "protected resource request" do
462
462
  before do
463
463
  @authorization = create_authorization(
@@ -466,58 +466,58 @@ describe Songkick::OAuth2::Provider do
466
466
  :access_token => 'magic-key',
467
467
  :scope => 'profile')
468
468
  end
469
-
469
+
470
470
  shared_examples_for "protected resource" do
471
471
  it "creates an AccessToken response" do
472
- mock_token = mock(Songkick::OAuth2::Provider::AccessToken)
472
+ mock_token = double(Songkick::OAuth2::Provider::AccessToken)
473
473
  mock_token.should_receive(:response_headers).and_return({})
474
474
  mock_token.should_receive(:response_status).and_return(200)
475
475
  mock_token.should_receive(:valid?).and_return(true)
476
476
  Songkick::OAuth2::Provider::AccessToken.should_receive(:new).with(TestApp::User['Bob'], ['profile'], 'magic-key', nil).and_return(mock_token)
477
477
  request('/user_profile', 'oauth_token' => 'magic-key')
478
478
  end
479
-
479
+
480
480
  it "allows access when the key is passed" do
481
481
  response = request('/user_profile', 'oauth_token' => 'magic-key')
482
482
  JSON.parse(response.body)['data'].should == 'Top secret'
483
483
  response.code.to_i.should == 200
484
484
  end
485
-
485
+
486
486
  it "blocks access when the wrong key is passed" do
487
487
  response = request('/user_profile', 'oauth_token' => 'is-the-password-books')
488
488
  JSON.parse(response.body)['data'].should == 'No soup for you'
489
489
  response.code.to_i.should == 401
490
490
  response['WWW-Authenticate'].should == "OAuth realm='Demo App', error='invalid_token'"
491
491
  end
492
-
492
+
493
493
  it "blocks access when the no key is passed" do
494
494
  response = request('/user_profile')
495
495
  JSON.parse(response.body)['data'].should == 'No soup for you'
496
496
  response.code.to_i.should == 401
497
497
  response['WWW-Authenticate'].should == "OAuth realm='Demo App'"
498
498
  end
499
-
499
+
500
500
  describe "enforcing SSL" do
501
501
  before { Songkick::OAuth2::Provider.enforce_ssl = true }
502
-
502
+
503
503
  let(:authorization) do
504
504
  Songkick::OAuth2::Model::Authorization.find_by_access_token_hash(Songkick::OAuth2.hashify('magic-key'))
505
505
  end
506
-
506
+
507
507
  it "blocks access when not using HTTPS" do
508
508
  response = request('/user_profile', 'oauth_token' => 'magic-key')
509
509
  JSON.parse(response.body)['data'].should == 'No soup for you'
510
510
  response.code.to_i.should == 401
511
511
  response['WWW-Authenticate'].should == "OAuth realm='Demo App', error='invalid_request'"
512
512
  end
513
-
513
+
514
514
  it "destroys the access token since it's been leaked" do
515
515
  authorization.access_token_hash.should == Songkick::OAuth2.hashify('magic-key')
516
516
  request('/user_profile', 'oauth_token' => 'magic-key')
517
517
  authorization.reload
518
518
  authorization.access_token_hash.should be_nil
519
519
  end
520
-
520
+
521
521
  it "keeps the access token if the wrong key is passed" do
522
522
  authorization.access_token_hash.should == Songkick::OAuth2.hashify('magic-key')
523
523
  request('/user_profile', 'oauth_token' => 'is-the-password-books')
@@ -526,34 +526,34 @@ describe Songkick::OAuth2::Provider do
526
526
  end
527
527
  end
528
528
  end
529
-
529
+
530
530
  describe "for header-based requests" do
531
531
  def request(path, params = {})
532
532
  access_token = params.delete('oauth_token')
533
- http = Net::HTTP.new('localhost', 8000)
533
+ http = Net::HTTP.new('localhost', RequestHelpers::SERVER_PORT)
534
534
  qs = params.map { |k,v| "#{ CGI.escape k.to_s }=#{ CGI.escape v.to_s }" }.join('&')
535
535
  header = {'Authorization' => "OAuth #{access_token}"}
536
536
  http.request_get(path + '?' + qs, header)
537
537
  end
538
-
538
+
539
539
  it_should_behave_like "protected resource"
540
540
  end
541
-
541
+
542
542
  describe "for GET requests" do
543
543
  def request(path, params = {})
544
544
  qs = params.map { |k,v| "#{ CGI.escape k.to_s }=#{ CGI.escape v.to_s }" }.join('&')
545
- uri = URI.parse('http://localhost:8000' + path + '?' + qs)
545
+ uri = URI.parse("http://localhost:#{RequestHelpers::SERVER_PORT}" + path + '?' + qs)
546
546
  Net::HTTP.get_response(uri)
547
547
  end
548
-
548
+
549
549
  it_should_behave_like "protected resource"
550
550
  end
551
-
551
+
552
552
  describe "for POST requests" do
553
553
  def request(path, params = {})
554
- Net::HTTP.post_form(URI.parse('http://localhost:8000' + path), params)
554
+ Net::HTTP.post_form(URI.parse("http://localhost:#{RequestHelpers::SERVER_PORT}" + path), params)
555
555
  end
556
-
556
+
557
557
  it_should_behave_like "protected resource"
558
558
  end
559
559
  end