rockoauth 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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