rockoauth 0.1.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.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/History.txt +5 -0
  3. data/README.rdoc +422 -0
  4. data/example/README.rdoc +11 -0
  5. data/example/application.rb +158 -0
  6. data/example/config.ru +3 -0
  7. data/example/environment.rb +11 -0
  8. data/example/models/connection.rb +9 -0
  9. data/example/models/note.rb +4 -0
  10. data/example/models/user.rb +5 -0
  11. data/example/public/style.css +78 -0
  12. data/example/schema.rb +22 -0
  13. data/example/views/authorize.erb +28 -0
  14. data/example/views/create_user.erb +3 -0
  15. data/example/views/error.erb +6 -0
  16. data/example/views/home.erb +24 -0
  17. data/example/views/layout.erb +24 -0
  18. data/example/views/login.erb +20 -0
  19. data/example/views/new_client.erb +25 -0
  20. data/example/views/new_user.erb +22 -0
  21. data/example/views/show_client.erb +15 -0
  22. data/lib/rockoauth/model/authorization.rb +132 -0
  23. data/lib/rockoauth/model/client.rb +54 -0
  24. data/lib/rockoauth/model/client_owner.rb +13 -0
  25. data/lib/rockoauth/model/hashing.rb +26 -0
  26. data/lib/rockoauth/model/helpers.rb +14 -0
  27. data/lib/rockoauth/model/resource_owner.rb +22 -0
  28. data/lib/rockoauth/model.rb +38 -0
  29. data/lib/rockoauth/provider/access_token.rb +70 -0
  30. data/lib/rockoauth/provider/authorization.rb +185 -0
  31. data/lib/rockoauth/provider/error.rb +19 -0
  32. data/lib/rockoauth/provider/exchange.rb +225 -0
  33. data/lib/rockoauth/provider.rb +133 -0
  34. data/lib/rockoauth/router.rb +75 -0
  35. data/lib/rockoauth/schema/20120828112156_rockoauth_schema_original_schema.rb +35 -0
  36. data/lib/rockoauth/schema/20121024180930_rockoauth_schema_add_authorization_index.rb +13 -0
  37. data/lib/rockoauth/schema/20121025180447_rockoauth_schema_add_unique_indexes.rb +31 -0
  38. data/lib/rockoauth/schema.rb +25 -0
  39. data/lib/rockoauth.rb +1 -0
  40. data/spec/factories.rb +20 -0
  41. data/spec/request_helpers.rb +62 -0
  42. data/spec/rockoauth/model/authorization_spec.rb +237 -0
  43. data/spec/rockoauth/model/client_spec.rb +44 -0
  44. data/spec/rockoauth/model/helpers_spec.rb +25 -0
  45. data/spec/rockoauth/model/resource_owner_spec.rb +87 -0
  46. data/spec/rockoauth/provider/access_token_spec.rb +138 -0
  47. data/spec/rockoauth/provider/authorization_spec.rb +356 -0
  48. data/spec/rockoauth/provider/exchange_spec.rb +361 -0
  49. data/spec/rockoauth/provider_spec.rb +560 -0
  50. data/spec/spec_helper.rb +80 -0
  51. data/spec/test_app/helper.rb +36 -0
  52. data/spec/test_app/provider/application.rb +67 -0
  53. data/spec/test_app/provider/views/authorize.erb +19 -0
  54. metadata +238 -0
@@ -0,0 +1,356 @@
1
+ require 'spec_helper'
2
+
3
+ describe RockOAuth::Provider::Authorization do
4
+ let(:resource_owner) { TestApp::User['Bob'] }
5
+
6
+ let(:authorization) { RockOAuth::Provider::Authorization.new(resource_owner, params) }
7
+
8
+ let(:params) { { 'response_type' => 'code',
9
+ 'client_id' => @client.client_id,
10
+ 'redirect_uri' => @client.redirect_uri }
11
+ }
12
+
13
+ before do
14
+ @client = Factory(:client)
15
+ allow(RockOAuth).to receive(:random_string).and_return('s1', 's2', 's3')
16
+ end
17
+
18
+ describe "with valid parameters" do
19
+ it "is valid" do
20
+ expect(authorization.error).to be_nil
21
+ end
22
+ end
23
+
24
+ describe "with the scope parameter" do
25
+ before { params['scope'] = 'foo bar qux' }
26
+
27
+ it "exposes the scope as a list of strings" do
28
+ expect(authorization.scopes).to eq(Set.new(%w[foo bar qux]))
29
+ end
30
+
31
+ it "exposes the scopes the client has not yet granted" do
32
+ expect(authorization.unauthorized_scopes).to eq(Set.new(%w[foo bar qux]))
33
+ end
34
+
35
+ describe "when the owner has already authorized the client" do
36
+ before do
37
+ create_authorization(:owner => resource_owner, :client => @client, :scope => 'foo bar')
38
+ end
39
+
40
+ it "exposes the scope as a list of strings" do
41
+ expect(authorization.scopes).to eq(Set.new(%w[foo bar qux]))
42
+ end
43
+
44
+ it "exposes the scopes the client has not yet granted" do
45
+ expect(authorization.unauthorized_scopes).to eq(%w[qux])
46
+ end
47
+ end
48
+ end
49
+
50
+ describe "missing response_type" do
51
+ before { params.delete('response_type') }
52
+
53
+ it "is invalid" do
54
+ expect(authorization.error).to eq("invalid_request")
55
+ expect(authorization.error_description).to eq("Missing required parameter response_type")
56
+ end
57
+ end
58
+
59
+ describe "with a bad response_type" do
60
+ before { params['response_type'] = "no_such_type" }
61
+
62
+ it "is invalid" do
63
+ expect(authorization.error).to eq("unsupported_response_type")
64
+ expect(authorization.error_description).to eq("Response type no_such_type is not supported")
65
+ end
66
+
67
+ it "causes a redirect" do
68
+ expect(authorization).to be_redirect
69
+ expect(authorization.redirect_uri).to eq("https://client.example.com/cb?error=unsupported_response_type&error_description=Response+type+no_such_type+is+not+supported")
70
+ end
71
+ end
72
+
73
+ describe "missing client_id" do
74
+ before { params.delete('client_id') }
75
+
76
+ it "is invalid" do
77
+ expect(authorization.error).to eq("invalid_request")
78
+ expect(authorization.error_description).to eq("Missing required parameter client_id")
79
+ end
80
+
81
+ it "does not cause a redirect" do
82
+ expect(authorization).not_to be_redirect
83
+ end
84
+ end
85
+
86
+ describe "with an unknown client_id" do
87
+ before { params['client_id'] = "unknown" }
88
+
89
+ it "is invalid" do
90
+ expect(authorization.error).to eq("invalid_client")
91
+ expect(authorization.error_description).to eq("Unknown client ID unknown")
92
+ end
93
+
94
+ it "does not cause a redirect" do
95
+ expect(authorization).not_to be_redirect
96
+ end
97
+ end
98
+
99
+ describe "missing redirect_uri" do
100
+ before { params.delete('redirect_uri') }
101
+
102
+ it "is invalid" do
103
+ expect(authorization.error).to eq("invalid_request")
104
+ expect(authorization.error_description).to eq("Missing required parameter redirect_uri")
105
+ end
106
+
107
+ it "causes a redirect to the client's registered redirect_uri" do
108
+ expect(authorization).to be_redirect
109
+ expect(authorization.redirect_uri).to eq("https://client.example.com/cb?error=invalid_request&error_description=Missing+required+parameter+redirect_uri")
110
+ end
111
+ end
112
+
113
+ describe "with a mismatched redirect_uri" do
114
+ before { params['redirect_uri'] = "http://songkick.com" }
115
+
116
+ it "is invalid" do
117
+ expect(authorization.error).to eq("redirect_uri_mismatch")
118
+ expect(authorization.error_description).to eq("Parameter redirect_uri does not match registered URI")
119
+ end
120
+
121
+ it "causes a redirect to the client's registered redirect_uri" do
122
+ expect(authorization).to be_redirect
123
+ expect(authorization.redirect_uri).to eq("https://client.example.com/cb?error=redirect_uri_mismatch&error_description=Parameter+redirect_uri+does+not+match+registered+URI")
124
+ end
125
+
126
+ describe "when the client has not registered a redirect_uri" do
127
+ before { @client.update_attribute(:redirect_uri, nil) }
128
+
129
+ it "is valid" do
130
+ expect(authorization.error).to be_nil
131
+ end
132
+ end
133
+ end
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
+ expect(authorization.redirect_uri).to eq("http://songkick.com?some_parameter&")
143
+ end
144
+ end
145
+
146
+ # http://en.wikipedia.org/wiki/HTTP_response_splitting
147
+ # scope and state values are passed back in the redirect
148
+
149
+ describe "with an illegal scope" do
150
+ before { params['scope'] = "http\r\nsplitter" }
151
+
152
+ it "is invalid" do
153
+ expect(authorization.error).to eq("invalid_request")
154
+ expect(authorization.error_description).to eq("Illegal value for scope parameter")
155
+ end
156
+ end
157
+
158
+ describe "with an illegal state" do
159
+ before { params['state'] = "http\r\nsplitter" }
160
+
161
+ it "is invalid" do
162
+ expect(authorization.error).to eq("invalid_request")
163
+ expect(authorization.error_description).to eq("Illegal value for state parameter")
164
+ end
165
+ end
166
+
167
+ describe "#grant_access!" do
168
+ describe "when there is an existing authorization with no code" do
169
+ before do
170
+ @model = create_authorization(
171
+ :owner => resource_owner,
172
+ :client => @client,
173
+ :code => nil)
174
+ end
175
+
176
+ it "generates and returns a code to the client" do
177
+ authorization.grant_access!
178
+ @model.reload
179
+ expect(@model.code).to eq("s1")
180
+ expect(authorization.code).to eq("s1")
181
+ end
182
+ end
183
+
184
+ describe "when there is an existing authorization with scopes" do
185
+ before do
186
+ @model = create_authorization(
187
+ :owner => resource_owner,
188
+ :client => @client,
189
+ :code => nil,
190
+ :scope => 'foo bar')
191
+
192
+ params['scope'] = 'qux'
193
+ end
194
+
195
+ it "merges the new scopes with the existing ones" do
196
+ authorization.grant_access!
197
+ @model.reload
198
+ expect(@model.scopes).to eq(Set.new(%w[foo bar qux]))
199
+ end
200
+ end
201
+
202
+ describe "when there is an existing expired authorization" do
203
+ before do
204
+ @model = create_authorization(
205
+ :owner => resource_owner,
206
+ :client => @client,
207
+ :expires_at => 2.months.ago,
208
+ :code => 'existing_code',
209
+ :scope => 'foo bar')
210
+ end
211
+
212
+ it "renews the authorization" do
213
+ authorization.grant_access!
214
+ @model.reload
215
+ expect(@model.expires_at).to be_nil
216
+ end
217
+
218
+ it "returns a code to the client" do
219
+ authorization.grant_access!
220
+ expect(authorization.code).to eq("existing_code")
221
+ expect(authorization.access_token).to be_nil
222
+ end
223
+
224
+ it "sets the expiry time if a duration is given" do
225
+ authorization.grant_access!(:duration => 1.hour)
226
+ @model.reload
227
+ expect(@model.expires_in).to eq(3600)
228
+ expect(authorization.expires_in).to eq(3600)
229
+ end
230
+
231
+ it "augments the scope" do
232
+ params['scope'] = 'qux'
233
+ authorization.grant_access!
234
+ @model.reload
235
+ expect(@model.scopes).to eq(Set.new(%w[foo bar qux]))
236
+ end
237
+ end
238
+
239
+ describe "for code requests" do
240
+ before do
241
+ params['response_type'] = 'code'
242
+ params['scope'] = 'foo bar'
243
+ end
244
+
245
+ it "makes the authorization redirect" do
246
+ authorization.grant_access!
247
+ expect(authorization.client).not_to be_nil
248
+ expect(authorization).to be_redirect
249
+ end
250
+
251
+ it "creates a code for the authorization" do
252
+ authorization.grant_access!
253
+ expect(authorization.code).to eq("s1")
254
+ expect(authorization.access_token).to be_nil
255
+ expect(authorization.expires_in).to be_nil
256
+ end
257
+
258
+ it "creates an Authorization in the database" do
259
+ authorization.grant_access!
260
+
261
+ authorization = RockOAuth::Model::Authorization.first
262
+ expect(authorization.owner).to eq(resource_owner)
263
+ expect(authorization.client).to eq(@client)
264
+ expect(authorization.code).to eq("s1")
265
+ expect(authorization.scopes).to eq(Set.new(%w[foo bar]))
266
+ end
267
+ end
268
+
269
+ describe "for token requests" do
270
+ before { params['response_type'] = 'token' }
271
+
272
+ it "creates a token for the authorization" do
273
+ authorization.grant_access!
274
+ expect(authorization.code).to be_nil
275
+ expect(authorization.access_token).to eq("s1")
276
+ expect(authorization.refresh_token).to eq("s2")
277
+ expect(authorization.expires_in).to be_nil
278
+ end
279
+
280
+ it "creates an Authorization in the database" do
281
+ authorization.grant_access!
282
+
283
+ authorization = RockOAuth::Model::Authorization.first
284
+ expect(authorization.owner).to eq(resource_owner)
285
+ expect(authorization.client).to eq(@client)
286
+ expect(authorization.code).to be_nil
287
+ expect(authorization.access_token_hash).to eq(RockOAuth.hashify("s1"))
288
+ expect(authorization.refresh_token_hash).to eq(RockOAuth.hashify("s2"))
289
+ end
290
+ end
291
+
292
+ describe "for code_and_token requests" do
293
+ before { params['response_type'] = 'code_and_token' }
294
+
295
+ it "creates a code and token for the authorization" do
296
+ authorization.grant_access!
297
+ expect(authorization.code).to eq("s1")
298
+ expect(authorization.access_token).to eq("s2")
299
+ expect(authorization.refresh_token).to eq("s3")
300
+ expect(authorization.expires_in).to be_nil
301
+ end
302
+
303
+ it "creates an Authorization in the database" do
304
+ authorization.grant_access!
305
+
306
+ authorization = RockOAuth::Model::Authorization.first
307
+ expect(authorization.owner).to eq(resource_owner)
308
+ expect(authorization.client).to eq(@client)
309
+ expect(authorization.code).to eq("s1")
310
+ expect(authorization.access_token_hash).to eq(RockOAuth.hashify("s2"))
311
+ expect(authorization.refresh_token_hash).to eq(RockOAuth.hashify("s3"))
312
+ end
313
+ end
314
+ end
315
+
316
+ describe "#deny_access!" do
317
+ it "puts the authorization in an error state" do
318
+ authorization.deny_access!
319
+ expect(authorization.error).to eq("access_denied")
320
+ expect(authorization.error_description).to eq("The user denied you access")
321
+ end
322
+
323
+ it "does not create an Authorization" do
324
+ expect(RockOAuth::Model::Authorization).not_to receive(:create)
325
+ expect(RockOAuth::Model::Authorization).not_to receive(:new)
326
+ authorization.deny_access!
327
+ end
328
+ end
329
+
330
+ describe "#params" do
331
+ before do
332
+ params['scope'] = params['state'] = 'valid'
333
+ params['controller'] = 'invalid'
334
+ end
335
+
336
+ it "only exposes OAuth-related parameters" do
337
+ expect(authorization.params).to eq({
338
+ 'response_type' => 'code',
339
+ 'client_id' => @client.client_id,
340
+ 'redirect_uri' => @client.redirect_uri,
341
+ 'state' => 'valid',
342
+ 'scope' => 'valid'
343
+ })
344
+ end
345
+
346
+ it "does not expose parameters with no value" do
347
+ params.delete('scope')
348
+ expect(authorization.params).to eq({
349
+ 'response_type' => 'code',
350
+ 'client_id' => @client.client_id,
351
+ 'redirect_uri' => @client.redirect_uri,
352
+ 'state' => 'valid'
353
+ })
354
+ end
355
+ end
356
+ end