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
@@ -4,26 +4,26 @@ describe Songkick::OAuth2::Provider::AccessToken do
4
4
  before do
5
5
  @alice = TestApp::User['Alice']
6
6
  @bob = TestApp::User['Bob']
7
-
7
+
8
8
  create_authorization(
9
9
  :owner => @alice,
10
10
  :client => Factory(:client),
11
11
  :scope => 'profile',
12
12
  :access_token => 'sesame')
13
-
13
+
14
14
  @authorization = create_authorization(
15
15
  :owner => @bob,
16
16
  :client => Factory(:client),
17
17
  :scope => 'profile',
18
18
  :access_token => 'magic-key')
19
-
19
+
20
20
  Songkick::OAuth2::Provider.realm = 'Demo App'
21
21
  end
22
-
22
+
23
23
  let :token do
24
24
  Songkick::OAuth2::Provider::AccessToken.new(@bob, ['profile'], 'magic-key')
25
25
  end
26
-
26
+
27
27
  shared_examples_for "valid token" do
28
28
  it "is valid" do
29
29
  token.should be_valid
@@ -38,7 +38,7 @@ describe Songkick::OAuth2::Provider::AccessToken do
38
38
  token.owner.should == @bob
39
39
  end
40
40
  end
41
-
41
+
42
42
  shared_examples_for "invalid token" do
43
43
  it "is not valid" do
44
44
  token.should_not be_valid
@@ -47,77 +47,89 @@ describe Songkick::OAuth2::Provider::AccessToken do
47
47
  token.owner.should be_nil
48
48
  end
49
49
  end
50
-
50
+
51
51
  describe "with the right user, scope and token" do
52
52
  it_should_behave_like "valid token"
53
53
  end
54
-
54
+
55
+ describe "with an implicit user" do
56
+ let :token do
57
+ Songkick::OAuth2::Provider::AccessToken.new(:implicit, ['profile'], 'magic-key')
58
+ end
59
+ it_should_behave_like "valid token"
60
+ end
61
+
55
62
  describe "with no user" do
56
63
  let :token do
57
64
  Songkick::OAuth2::Provider::AccessToken.new(nil, ['profile'], 'magic-key')
58
65
  end
59
- it_should_behave_like "valid token"
66
+ it_should_behave_like "invalid token"
67
+
68
+ it "returns an error response" do
69
+ token.response_headers['WWW-Authenticate'].should == "OAuth realm='Demo App', error='invalid_token'"
70
+ token.response_status.should == 401
71
+ end
60
72
  end
61
-
73
+
62
74
  describe "with less scope than was granted" do
63
75
  let :token do
64
76
  Songkick::OAuth2::Provider::AccessToken.new(@bob, [], 'magic-key')
65
77
  end
66
78
  it_should_behave_like "valid token"
67
79
  end
68
-
80
+
69
81
  describe "when the authorization has expired" do
70
82
  before { @authorization.update_attribute(:expires_at, 1.hour.ago) }
71
83
  it_should_behave_like "invalid token"
72
-
84
+
73
85
  it "returns an error response" do
74
86
  token.response_headers['WWW-Authenticate'].should == "OAuth realm='Demo App', error='expired_token'"
75
87
  token.response_status.should == 401
76
88
  end
77
89
  end
78
-
90
+
79
91
  describe "with a non-existent token" do
80
92
  let :token do
81
93
  Songkick::OAuth2::Provider::AccessToken.new(@bob, ['profile'], 'is-the-password-books')
82
94
  end
83
95
  it_should_behave_like "invalid token"
84
-
96
+
85
97
  it "returns an error response" do
86
98
  token.response_headers['WWW-Authenticate'].should == "OAuth realm='Demo App', error='invalid_token'"
87
99
  token.response_status.should == 401
88
100
  end
89
101
  end
90
-
102
+
91
103
  describe "with a token for the wrong user" do
92
104
  let :token do
93
105
  Songkick::OAuth2::Provider::AccessToken.new(@bob, ['profile'], 'sesame')
94
106
  end
95
107
  it_should_behave_like "invalid token"
96
-
108
+
97
109
  it "returns an error response" do
98
110
  token.response_headers['WWW-Authenticate'].should == "OAuth realm='Demo App', error='insufficient_scope'"
99
111
  token.response_status.should == 403
100
112
  end
101
113
  end
102
-
114
+
103
115
  describe "with a token for an ungranted scope" do
104
116
  let :token do
105
117
  Songkick::OAuth2::Provider::AccessToken.new(@bob, ['offline_access'], 'magic-key')
106
118
  end
107
119
  it_should_behave_like "invalid token"
108
-
120
+
109
121
  it "returns an error response" do
110
122
  token.response_headers['WWW-Authenticate'].should == "OAuth realm='Demo App', error='insufficient_scope'"
111
123
  token.response_status.should == 403
112
124
  end
113
125
  end
114
-
126
+
115
127
  describe "with no token string" do
116
128
  let :token do
117
129
  Songkick::OAuth2::Provider::AccessToken.new(@bob, ['profile'], nil)
118
130
  end
119
131
  it_should_behave_like "invalid token"
120
-
132
+
121
133
  it "returns an error response" do
122
134
  token.response_headers['WWW-Authenticate'].should == "OAuth realm='Demo App'"
123
135
  token.response_status.should == 401
@@ -2,157 +2,168 @@ require 'spec_helper'
2
2
 
3
3
  describe Songkick::OAuth2::Provider::Authorization do
4
4
  let(:resource_owner) { TestApp::User['Bob'] }
5
-
5
+
6
6
  let(:authorization) { Songkick::OAuth2::Provider::Authorization.new(resource_owner, params) }
7
-
7
+
8
8
  let(:params) { { 'response_type' => 'code',
9
9
  'client_id' => @client.client_id,
10
10
  'redirect_uri' => @client.redirect_uri }
11
11
  }
12
-
12
+
13
13
  before do
14
14
  @client = Factory(:client)
15
15
  Songkick::OAuth2.stub(:random_string).and_return('s1', 's2', 's3')
16
16
  end
17
-
17
+
18
18
  describe "with valid parameters" do
19
19
  it "is valid" do
20
20
  authorization.error.should be_nil
21
21
  end
22
22
  end
23
-
23
+
24
24
  describe "with the scope parameter" do
25
25
  before { params['scope'] = 'foo bar qux' }
26
-
26
+
27
27
  it "exposes the scope as a list of strings" do
28
28
  authorization.scopes.should == Set.new(%w[foo bar qux])
29
29
  end
30
-
30
+
31
31
  it "exposes the scopes the client has not yet granted" do
32
32
  authorization.unauthorized_scopes.should == Set.new(%w[foo bar qux])
33
33
  end
34
-
34
+
35
35
  describe "when the owner has already authorized the client" do
36
36
  before do
37
37
  create_authorization(:owner => resource_owner, :client => @client, :scope => 'foo bar')
38
38
  end
39
-
39
+
40
40
  it "exposes the scope as a list of strings" do
41
41
  authorization.scopes.should == Set.new(%w[foo bar qux])
42
42
  end
43
-
43
+
44
44
  it "exposes the scopes the client has not yet granted" do
45
45
  authorization.unauthorized_scopes.should == %w[qux]
46
46
  end
47
47
  end
48
48
  end
49
-
49
+
50
50
  describe "missing response_type" do
51
51
  before { params.delete('response_type') }
52
-
52
+
53
53
  it "is invalid" do
54
54
  authorization.error.should == "invalid_request"
55
55
  authorization.error_description.should == "Missing required parameter response_type"
56
56
  end
57
57
  end
58
-
58
+
59
59
  describe "with a bad response_type" do
60
60
  before { params['response_type'] = "no_such_type" }
61
-
61
+
62
62
  it "is invalid" do
63
63
  authorization.error.should == "unsupported_response_type"
64
64
  authorization.error_description.should == "Response type no_such_type is not supported"
65
65
  end
66
-
66
+
67
67
  it "causes a redirect" do
68
68
  authorization.should be_redirect
69
69
  authorization.redirect_uri.should == "https://client.example.com/cb?error=unsupported_response_type&error_description=Response+type+no_such_type+is+not+supported"
70
70
  end
71
71
  end
72
-
72
+
73
73
  describe "missing client_id" do
74
74
  before { params.delete('client_id') }
75
-
75
+
76
76
  it "is invalid" do
77
77
  authorization.error.should == "invalid_request"
78
78
  authorization.error_description.should == "Missing required parameter client_id"
79
79
  end
80
-
80
+
81
81
  it "does not cause a redirect" do
82
82
  authorization.should_not be_redirect
83
83
  end
84
84
  end
85
-
85
+
86
86
  describe "with an unknown client_id" do
87
87
  before { params['client_id'] = "unknown" }
88
-
88
+
89
89
  it "is invalid" do
90
90
  authorization.error.should == "invalid_client"
91
91
  authorization.error_description.should == "Unknown client ID unknown"
92
92
  end
93
-
93
+
94
94
  it "does not cause a redirect" do
95
95
  authorization.should_not be_redirect
96
96
  end
97
97
  end
98
-
98
+
99
99
  describe "missing redirect_uri" do
100
100
  before { params.delete('redirect_uri') }
101
-
101
+
102
102
  it "is invalid" do
103
103
  authorization.error.should == "invalid_request"
104
104
  authorization.error_description.should == "Missing required parameter redirect_uri"
105
105
  end
106
-
106
+
107
107
  it "causes a redirect to the client's registered redirect_uri" do
108
108
  authorization.should be_redirect
109
109
  authorization.redirect_uri.should == "https://client.example.com/cb?error=invalid_request&error_description=Missing+required+parameter+redirect_uri"
110
110
  end
111
111
  end
112
-
112
+
113
113
  describe "with a mismatched redirect_uri" do
114
114
  before { params['redirect_uri'] = "http://songkick.com" }
115
-
115
+
116
116
  it "is invalid" do
117
117
  authorization.error.should == "redirect_uri_mismatch"
118
118
  authorization.error_description.should == "Parameter redirect_uri does not match registered URI"
119
119
  end
120
-
120
+
121
121
  it "causes a redirect to the client's registered redirect_uri" do
122
122
  authorization.should be_redirect
123
123
  authorization.redirect_uri.should == "https://client.example.com/cb?error=redirect_uri_mismatch&error_description=Parameter+redirect_uri+does+not+match+registered+URI"
124
124
  end
125
-
125
+
126
126
  describe "when the client has not registered a redirect_uri" do
127
127
  before { @client.update_attribute(:redirect_uri, nil) }
128
-
128
+
129
129
  it "is valid" do
130
130
  authorization.error.should be_nil
131
131
  end
132
132
  end
133
133
  end
134
-
134
+
135
+ describe "with a redirect_uri with parameters" do
136
+ before do
137
+ authorization.client.redirect_uri = "http://songkick.com?some_parameter"
138
+ params['redirect_uri'] = "http://songkick.com?some_parameter"
139
+ end
140
+
141
+ it "adds the extra parameters with & instead of ?" do
142
+ authorization.redirect_uri.should == "http://songkick.com?some_parameter&"
143
+ end
144
+ end
145
+
135
146
  # http://en.wikipedia.org/wiki/HTTP_response_splitting
136
147
  # scope and state values are passed back in the redirect
137
-
148
+
138
149
  describe "with an illegal scope" do
139
150
  before { params['scope'] = "http\r\nsplitter" }
140
-
151
+
141
152
  it "is invalid" do
142
153
  authorization.error.should == "invalid_request"
143
154
  authorization.error_description.should == "Illegal value for scope parameter"
144
155
  end
145
156
  end
146
-
157
+
147
158
  describe "with an illegal state" do
148
159
  before { params['state'] = "http\r\nsplitter" }
149
-
160
+
150
161
  it "is invalid" do
151
162
  authorization.error.should == "invalid_request"
152
163
  authorization.error_description.should == "Illegal value for state parameter"
153
164
  end
154
165
  end
155
-
166
+
156
167
  describe "#grant_access!" do
157
168
  describe "when there is an existing authorization with no code" do
158
169
  before do
@@ -161,7 +172,7 @@ describe Songkick::OAuth2::Provider::Authorization do
161
172
  :client => @client,
162
173
  :code => nil)
163
174
  end
164
-
175
+
165
176
  it "generates and returns a code to the client" do
166
177
  authorization.grant_access!
167
178
  @model.reload
@@ -169,7 +180,7 @@ describe Songkick::OAuth2::Provider::Authorization do
169
180
  authorization.code.should == "s1"
170
181
  end
171
182
  end
172
-
183
+
173
184
  describe "when there is an existing authorization with scopes" do
174
185
  before do
175
186
  @model = create_authorization(
@@ -177,17 +188,17 @@ describe Songkick::OAuth2::Provider::Authorization do
177
188
  :client => @client,
178
189
  :code => nil,
179
190
  :scope => 'foo bar')
180
-
191
+
181
192
  params['scope'] = 'qux'
182
193
  end
183
-
194
+
184
195
  it "merges the new scopes with the existing ones" do
185
196
  authorization.grant_access!
186
197
  @model.reload
187
198
  @model.scopes.should == Set.new(%w[foo bar qux])
188
199
  end
189
200
  end
190
-
201
+
191
202
  describe "when there is an existing expired authorization" do
192
203
  before do
193
204
  @model = create_authorization(
@@ -197,26 +208,26 @@ describe Songkick::OAuth2::Provider::Authorization do
197
208
  :code => 'existing_code',
198
209
  :scope => 'foo bar')
199
210
  end
200
-
211
+
201
212
  it "renews the authorization" do
202
213
  authorization.grant_access!
203
214
  @model.reload
204
215
  @model.expires_at.should be_nil
205
216
  end
206
-
217
+
207
218
  it "returns a code to the client" do
208
219
  authorization.grant_access!
209
220
  authorization.code.should == "existing_code"
210
221
  authorization.access_token.should be_nil
211
222
  end
212
-
223
+
213
224
  it "sets the expiry time if a duration is given" do
214
225
  authorization.grant_access!(:duration => 1.hour)
215
226
  @model.reload
216
227
  @model.expires_in.should == 3600
217
228
  authorization.expires_in.should == 3600
218
229
  end
219
-
230
+
220
231
  it "augments the scope" do
221
232
  params['scope'] = 'qux'
222
233
  authorization.grant_access!
@@ -224,29 +235,29 @@ describe Songkick::OAuth2::Provider::Authorization do
224
235
  @model.scopes.should == Set.new(%w[foo bar qux])
225
236
  end
226
237
  end
227
-
238
+
228
239
  describe "for code requests" do
229
240
  before do
230
241
  params['response_type'] = 'code'
231
242
  params['scope'] = 'foo bar'
232
243
  end
233
-
244
+
234
245
  it "makes the authorization redirect" do
235
246
  authorization.grant_access!
236
247
  authorization.client.should_not be_nil
237
248
  authorization.should be_redirect
238
249
  end
239
-
250
+
240
251
  it "creates a code for the authorization" do
241
252
  authorization.grant_access!
242
253
  authorization.code.should == "s1"
243
254
  authorization.access_token.should be_nil
244
255
  authorization.expires_in.should be_nil
245
256
  end
246
-
257
+
247
258
  it "creates an Authorization in the database" do
248
259
  authorization.grant_access!
249
-
260
+
250
261
  authorization = Songkick::OAuth2::Model::Authorization.first
251
262
  authorization.owner.should == resource_owner
252
263
  authorization.client.should == @client
@@ -254,10 +265,10 @@ describe Songkick::OAuth2::Provider::Authorization do
254
265
  authorization.scopes.should == Set.new(%w[foo bar])
255
266
  end
256
267
  end
257
-
268
+
258
269
  describe "for token requests" do
259
270
  before { params['response_type'] = 'token' }
260
-
271
+
261
272
  it "creates a token for the authorization" do
262
273
  authorization.grant_access!
263
274
  authorization.code.should be_nil
@@ -265,10 +276,10 @@ describe Songkick::OAuth2::Provider::Authorization do
265
276
  authorization.refresh_token.should == "s2"
266
277
  authorization.expires_in.should be_nil
267
278
  end
268
-
279
+
269
280
  it "creates an Authorization in the database" do
270
281
  authorization.grant_access!
271
-
282
+
272
283
  authorization = Songkick::OAuth2::Model::Authorization.first
273
284
  authorization.owner.should == resource_owner
274
285
  authorization.client.should == @client
@@ -277,10 +288,10 @@ describe Songkick::OAuth2::Provider::Authorization do
277
288
  authorization.refresh_token_hash.should == Songkick::OAuth2.hashify("s2")
278
289
  end
279
290
  end
280
-
291
+
281
292
  describe "for code_and_token requests" do
282
293
  before { params['response_type'] = 'code_and_token' }
283
-
294
+
284
295
  it "creates a code and token for the authorization" do
285
296
  authorization.grant_access!
286
297
  authorization.code.should == "s1"
@@ -288,10 +299,10 @@ describe Songkick::OAuth2::Provider::Authorization do
288
299
  authorization.refresh_token.should == "s3"
289
300
  authorization.expires_in.should be_nil
290
301
  end
291
-
302
+
292
303
  it "creates an Authorization in the database" do
293
304
  authorization.grant_access!
294
-
305
+
295
306
  authorization = Songkick::OAuth2::Model::Authorization.first
296
307
  authorization.owner.should == resource_owner
297
308
  authorization.client.should == @client
@@ -301,27 +312,27 @@ describe Songkick::OAuth2::Provider::Authorization do
301
312
  end
302
313
  end
303
314
  end
304
-
315
+
305
316
  describe "#deny_access!" do
306
317
  it "puts the authorization in an error state" do
307
318
  authorization.deny_access!
308
319
  authorization.error.should == "access_denied"
309
320
  authorization.error_description.should == "The user denied you access"
310
321
  end
311
-
322
+
312
323
  it "does not create an Authorization" do
313
324
  Songkick::OAuth2::Model::Authorization.should_not_receive(:create)
314
325
  Songkick::OAuth2::Model::Authorization.should_not_receive(:new)
315
326
  authorization.deny_access!
316
327
  end
317
328
  end
318
-
329
+
319
330
  describe "#params" do
320
331
  before do
321
332
  params['scope'] = params['state'] = 'valid'
322
333
  params['controller'] = 'invalid'
323
334
  end
324
-
335
+
325
336
  it "only exposes OAuth-related parameters" do
326
337
  authorization.params.should == {
327
338
  'response_type' => 'code',
@@ -331,7 +342,7 @@ describe Songkick::OAuth2::Provider::Authorization do
331
342
  'scope' => 'valid'
332
343
  }
333
344
  end
334
-
345
+
335
346
  it "does not expose parameters with no value" do
336
347
  params.delete('scope')
337
348
  authorization.params.should == {