doorkeeper 5.0.3 → 5.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 (215) hide show
  1. checksums.yaml +4 -4
  2. data/.hound.yml +2 -1
  3. data/.rubocop.yml +37 -4
  4. data/.travis.yml +11 -21
  5. data/Appraisals +29 -7
  6. data/Dangerfile +5 -2
  7. data/Gemfile +19 -5
  8. data/NEWS.md +81 -21
  9. data/README.md +75 -485
  10. data/Rakefile +10 -8
  11. data/app/controllers/doorkeeper/application_controller.rb +1 -2
  12. data/app/controllers/doorkeeper/application_metal_controller.rb +2 -13
  13. data/app/controllers/doorkeeper/applications_controller.rb +20 -8
  14. data/app/controllers/doorkeeper/authorized_applications_controller.rb +1 -1
  15. data/app/controllers/doorkeeper/token_info_controller.rb +2 -2
  16. data/app/controllers/doorkeeper/tokens_controller.rb +11 -11
  17. data/app/helpers/doorkeeper/dashboard_helper.rb +1 -1
  18. data/app/validators/redirect_uri_validator.rb +5 -2
  19. data/app/views/doorkeeper/applications/_form.html.erb +6 -0
  20. data/app/views/doorkeeper/applications/show.html.erb +1 -1
  21. data/app/views/layouts/doorkeeper/admin.html.erb +5 -3
  22. data/bin/console +16 -0
  23. data/config/locales/en.yml +1 -0
  24. data/doorkeeper.gemspec +24 -21
  25. data/gemfiles/rails_5_0.gemfile +9 -4
  26. data/gemfiles/rails_5_1.gemfile +9 -4
  27. data/gemfiles/rails_5_2.gemfile +10 -5
  28. data/gemfiles/rails_6_0.gemfile +17 -0
  29. data/gemfiles/rails_master.gemfile +9 -9
  30. data/lib/doorkeeper/config/option.rb +64 -0
  31. data/lib/doorkeeper/config.rb +191 -75
  32. data/lib/doorkeeper/engine.rb +1 -1
  33. data/lib/doorkeeper/grape/authorization_decorator.rb +4 -4
  34. data/lib/doorkeeper/grape/helpers.rb +3 -3
  35. data/lib/doorkeeper/helpers/controller.rb +3 -2
  36. data/lib/doorkeeper/models/access_grant_mixin.rb +20 -3
  37. data/lib/doorkeeper/models/access_token_mixin.rb +63 -19
  38. data/lib/doorkeeper/models/application_mixin.rb +43 -1
  39. data/lib/doorkeeper/models/concerns/expirable.rb +4 -2
  40. data/lib/doorkeeper/models/concerns/ownership.rb +1 -6
  41. data/lib/doorkeeper/models/concerns/reusable.rb +19 -0
  42. data/lib/doorkeeper/models/concerns/revocable.rb +2 -1
  43. data/lib/doorkeeper/models/concerns/scopes.rb +5 -1
  44. data/lib/doorkeeper/models/concerns/secret_storable.rb +108 -0
  45. data/lib/doorkeeper/oauth/authorization/code.rb +2 -2
  46. data/lib/doorkeeper/oauth/authorization/token.rb +12 -7
  47. data/lib/doorkeeper/oauth/authorization/uri_builder.rb +1 -1
  48. data/lib/doorkeeper/oauth/authorization_code_request.rb +6 -4
  49. data/lib/doorkeeper/oauth/client.rb +1 -1
  50. data/lib/doorkeeper/oauth/client_credentials/validation.rb +5 -4
  51. data/lib/doorkeeper/oauth/client_credentials_request.rb +1 -1
  52. data/lib/doorkeeper/oauth/code_response.rb +2 -2
  53. data/lib/doorkeeper/oauth/error_response.rb +10 -6
  54. data/lib/doorkeeper/oauth/forbidden_token_response.rb +1 -1
  55. data/lib/doorkeeper/oauth/helpers/scope_checker.rb +23 -8
  56. data/lib/doorkeeper/oauth/helpers/unique_token.rb +13 -1
  57. data/lib/doorkeeper/oauth/helpers/uri_checker.rb +37 -1
  58. data/lib/doorkeeper/oauth/invalid_token_response.rb +5 -1
  59. data/lib/doorkeeper/oauth/password_access_token_request.rb +7 -2
  60. data/lib/doorkeeper/oauth/pre_authorization.rb +11 -5
  61. data/lib/doorkeeper/oauth/refresh_token_request.rb +5 -2
  62. data/lib/doorkeeper/oauth/scopes.rb +5 -3
  63. data/lib/doorkeeper/oauth/token.rb +2 -2
  64. data/lib/doorkeeper/oauth/token_introspection.rb +74 -8
  65. data/lib/doorkeeper/oauth/token_response.rb +9 -9
  66. data/lib/doorkeeper/oauth.rb +5 -5
  67. data/lib/doorkeeper/orm/active_record/access_grant.rb +27 -13
  68. data/lib/doorkeeper/orm/active_record/access_token.rb +6 -13
  69. data/lib/doorkeeper/orm/active_record/application.rb +21 -57
  70. data/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +10 -3
  71. data/lib/doorkeeper/orm/active_record.rb +6 -6
  72. data/lib/doorkeeper/rails/helpers.rb +1 -1
  73. data/lib/doorkeeper/rails/routes/mapping.rb +7 -7
  74. data/lib/doorkeeper/rails/routes.rb +11 -11
  75. data/lib/doorkeeper/rake/db.rake +13 -13
  76. data/lib/doorkeeper/rake.rb +1 -1
  77. data/lib/doorkeeper/request.rb +1 -1
  78. data/lib/doorkeeper/secret_storing/base.rb +64 -0
  79. data/lib/doorkeeper/secret_storing/bcrypt.rb +60 -0
  80. data/lib/doorkeeper/secret_storing/plain.rb +33 -0
  81. data/lib/doorkeeper/secret_storing/sha256_hash.rb +26 -0
  82. data/lib/doorkeeper/stale_records_cleaner.rb +1 -1
  83. data/lib/doorkeeper/version.rb +3 -3
  84. data/lib/doorkeeper.rb +69 -60
  85. data/lib/generators/doorkeeper/application_owner_generator.rb +10 -9
  86. data/lib/generators/doorkeeper/confidential_applications_generator.rb +10 -9
  87. data/lib/generators/doorkeeper/install_generator.rb +11 -9
  88. data/lib/generators/doorkeeper/migration_generator.rb +9 -9
  89. data/lib/generators/doorkeeper/pkce_generator.rb +10 -9
  90. data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +10 -9
  91. data/lib/generators/doorkeeper/templates/initializer.rb +120 -13
  92. data/lib/generators/doorkeeper/templates/migration.rb.erb +15 -7
  93. data/lib/generators/doorkeeper/views_generator.rb +6 -4
  94. data/spec/controllers/application_metal_controller_spec.rb +24 -10
  95. data/spec/controllers/applications_controller_spec.rb +54 -52
  96. data/spec/controllers/authorizations_controller_spec.rb +139 -145
  97. data/spec/controllers/protected_resources_controller_spec.rb +78 -76
  98. data/spec/controllers/token_info_controller_spec.rb +14 -12
  99. data/spec/controllers/tokens_controller_spec.rb +167 -108
  100. data/spec/dummy/Rakefile +3 -1
  101. data/spec/dummy/app/controllers/application_controller.rb +3 -1
  102. data/spec/dummy/app/controllers/custom_authorizations_controller.rb +2 -0
  103. data/spec/dummy/app/controllers/full_protected_resources_controller.rb +4 -2
  104. data/spec/dummy/app/controllers/home_controller.rb +5 -3
  105. data/spec/dummy/app/controllers/metal_controller.rb +2 -0
  106. data/spec/dummy/app/controllers/semi_protected_resources_controller.rb +4 -2
  107. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  108. data/spec/dummy/app/models/user.rb +2 -0
  109. data/spec/dummy/config/application.rb +25 -1
  110. data/spec/dummy/config/environments/development.rb +2 -0
  111. data/spec/dummy/config/environments/production.rb +2 -0
  112. data/spec/dummy/config/environments/test.rb +3 -1
  113. data/spec/dummy/config/initializers/backtrace_silencers.rb +2 -0
  114. data/spec/dummy/config/initializers/doorkeeper.rb +5 -2
  115. data/spec/dummy/config/initializers/secret_token.rb +3 -1
  116. data/spec/dummy/config/initializers/session_store.rb +3 -1
  117. data/spec/dummy/config/initializers/wrap_parameters.rb +2 -0
  118. data/spec/dummy/config.ru +3 -1
  119. data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +17 -10
  120. data/spec/dummy/db/migrate/20170822064514_enable_pkce.rb +2 -0
  121. data/spec/dummy/db/schema.rb +1 -1
  122. data/spec/dummy/script/rails +5 -3
  123. data/spec/factories.rb +8 -6
  124. data/spec/generators/application_owner_generator_spec.rb +13 -26
  125. data/spec/generators/confidential_applications_generator_spec.rb +12 -28
  126. data/spec/generators/install_generator_spec.rb +17 -15
  127. data/spec/generators/migration_generator_spec.rb +13 -26
  128. data/spec/generators/pkce_generator_spec.rb +11 -26
  129. data/spec/generators/previous_refresh_token_generator_spec.rb +16 -29
  130. data/spec/generators/templates/routes.rb +2 -0
  131. data/spec/generators/views_generator_spec.rb +14 -12
  132. data/spec/grape/grape_integration_spec.rb +34 -32
  133. data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +9 -7
  134. data/spec/lib/config_spec.rb +268 -99
  135. data/spec/lib/doorkeeper_spec.rb +3 -1
  136. data/spec/lib/models/expirable_spec.rb +22 -8
  137. data/spec/lib/models/reusable_spec.rb +40 -0
  138. data/spec/lib/models/revocable_spec.rb +8 -6
  139. data/spec/lib/models/scopes_spec.rb +27 -13
  140. data/spec/lib/models/secret_storable_spec.rb +135 -0
  141. data/spec/lib/oauth/authorization/uri_builder_spec.rb +17 -15
  142. data/spec/lib/oauth/authorization_code_request_spec.rb +34 -11
  143. data/spec/lib/oauth/base_request_spec.rb +20 -8
  144. data/spec/lib/oauth/base_response_spec.rb +3 -1
  145. data/spec/lib/oauth/client/credentials_spec.rb +24 -22
  146. data/spec/lib/oauth/client_credentials/creator_spec.rb +56 -10
  147. data/spec/lib/oauth/client_credentials/issuer_spec.rb +27 -18
  148. data/spec/lib/oauth/client_credentials/validation_spec.rb +20 -15
  149. data/spec/lib/oauth/client_credentials_integration_spec.rb +7 -5
  150. data/spec/lib/oauth/client_credentials_request_spec.rb +27 -21
  151. data/spec/lib/oauth/client_spec.rb +15 -13
  152. data/spec/lib/oauth/code_request_spec.rb +8 -6
  153. data/spec/lib/oauth/code_response_spec.rb +9 -7
  154. data/spec/lib/oauth/error_response_spec.rb +19 -11
  155. data/spec/lib/oauth/error_spec.rb +4 -2
  156. data/spec/lib/oauth/forbidden_token_response_spec.rb +7 -5
  157. data/spec/lib/oauth/helpers/scope_checker_spec.rb +68 -31
  158. data/spec/lib/oauth/helpers/unique_token_spec.rb +8 -6
  159. data/spec/lib/oauth/helpers/uri_checker_spec.rb +115 -95
  160. data/spec/lib/oauth/invalid_token_response_spec.rb +3 -1
  161. data/spec/lib/oauth/password_access_token_request_spec.rb +79 -30
  162. data/spec/lib/oauth/pre_authorization_spec.rb +80 -54
  163. data/spec/lib/oauth/refresh_token_request_spec.rb +36 -33
  164. data/spec/lib/oauth/scopes_spec.rb +63 -61
  165. data/spec/lib/oauth/token_request_spec.rb +79 -24
  166. data/spec/lib/oauth/token_response_spec.rb +40 -38
  167. data/spec/lib/oauth/token_spec.rb +60 -44
  168. data/spec/lib/request/strategy_spec.rb +3 -1
  169. data/spec/lib/secret_storing/base_spec.rb +60 -0
  170. data/spec/lib/secret_storing/bcrypt_spec.rb +49 -0
  171. data/spec/lib/secret_storing/plain_spec.rb +44 -0
  172. data/spec/lib/secret_storing/sha256_hash_spec.rb +48 -0
  173. data/spec/lib/server_spec.rb +16 -14
  174. data/spec/lib/stale_records_cleaner_spec.rb +17 -17
  175. data/spec/models/doorkeeper/access_grant_spec.rb +80 -15
  176. data/spec/models/doorkeeper/access_token_spec.rb +198 -73
  177. data/spec/models/doorkeeper/application_spec.rb +280 -288
  178. data/spec/requests/applications/applications_request_spec.rb +98 -66
  179. data/spec/requests/applications/authorized_applications_spec.rb +20 -18
  180. data/spec/requests/endpoints/authorization_spec.rb +25 -23
  181. data/spec/requests/endpoints/token_spec.rb +38 -36
  182. data/spec/requests/flows/authorization_code_errors_spec.rb +26 -24
  183. data/spec/requests/flows/authorization_code_spec.rb +193 -151
  184. data/spec/requests/flows/client_credentials_spec.rb +54 -52
  185. data/spec/requests/flows/implicit_grant_errors_spec.rb +10 -8
  186. data/spec/requests/flows/implicit_grant_spec.rb +27 -25
  187. data/spec/requests/flows/password_spec.rb +59 -55
  188. data/spec/requests/flows/refresh_token_spec.rb +45 -43
  189. data/spec/requests/flows/revoke_token_spec.rb +40 -54
  190. data/spec/requests/flows/skip_authorization_spec.rb +23 -21
  191. data/spec/requests/protected_resources/metal_spec.rb +7 -5
  192. data/spec/requests/protected_resources/private_api_spec.rb +35 -33
  193. data/spec/routing/custom_controller_routes_spec.rb +71 -65
  194. data/spec/routing/default_routes_spec.rb +22 -20
  195. data/spec/routing/scoped_routes_spec.rb +20 -18
  196. data/spec/spec_helper.rb +15 -13
  197. data/spec/spec_helper_integration.rb +3 -1
  198. data/spec/support/dependencies/factory_bot.rb +3 -1
  199. data/spec/support/doorkeeper_rspec.rb +3 -1
  200. data/spec/support/helpers/access_token_request_helper.rb +3 -1
  201. data/spec/support/helpers/authorization_request_helper.rb +4 -2
  202. data/spec/support/helpers/config_helper.rb +2 -0
  203. data/spec/support/helpers/model_helper.rb +3 -1
  204. data/spec/support/helpers/request_spec_helper.rb +5 -3
  205. data/spec/support/helpers/url_helper.rb +9 -7
  206. data/spec/support/http_method_shim.rb +4 -9
  207. data/spec/support/orm/active_record.rb +3 -1
  208. data/spec/support/shared/controllers_shared_context.rb +18 -16
  209. data/spec/support/shared/hashing_shared_context.rb +36 -0
  210. data/spec/support/shared/models_shared_examples.rb +12 -10
  211. data/spec/validators/redirect_uri_validator_spec.rb +74 -45
  212. data/spec/version/version_spec.rb +7 -5
  213. metadata +60 -26
  214. data/gemfiles/rails_4_2.gemfile +0 -13
  215. data/spec/dummy/config/initializers/new_framework_defaults.rb +0 -8
@@ -1,14 +1,17 @@
1
- require 'spec_helper'
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
2
4
 
3
5
  module Doorkeeper
4
6
  describe AccessToken do
7
+ let(:clazz) { Doorkeeper::AccessToken }
5
8
  subject { FactoryBot.build(:access_token) }
6
9
 
7
10
  it { expect(subject).to be_valid }
8
11
 
9
- it_behaves_like 'an accessible token'
10
- it_behaves_like 'a revocable token'
11
- it_behaves_like 'a unique token' do
12
+ it_behaves_like "an accessible token"
13
+ it_behaves_like "a revocable token"
14
+ it_behaves_like "a unique token" do
12
15
  let(:factory_name) { :access_token }
13
16
  end
14
17
 
@@ -17,14 +20,75 @@ module Doorkeeper
17
20
  end
18
21
 
19
22
  describe :generate_token do
20
- it 'generates a token using the default method' do
23
+ it "generates a token using the default method" do
21
24
  FactoryBot.create :access_token
22
25
 
23
26
  token = FactoryBot.create :access_token
24
27
  expect(token.token).to be_a(String)
25
28
  end
26
29
 
27
- it 'generates a token using a custom object' do
30
+ context "with hashing enabled" do
31
+ let(:token) { FactoryBot.create :access_token }
32
+ include_context "with token hashing enabled"
33
+
34
+ it "holds a volatile plaintext token when created" do
35
+ expect(token.plaintext_token).to be_a(String)
36
+ expect(token.token)
37
+ .to eq(hashed_or_plain_token_func.call(token.plaintext_token))
38
+
39
+ # Finder method only finds the hashed token
40
+ loaded = clazz.find_by(token: token.token)
41
+ expect(loaded).to eq(token)
42
+ expect(loaded.plaintext_token).to be_nil
43
+ expect(loaded.token).to eq(token.token)
44
+ end
45
+
46
+ it "does not find_by plain text tokens" do
47
+ expect(clazz.find_by(token: token.plaintext_token)).to be_nil
48
+ end
49
+
50
+ describe "with having a plain text token" do
51
+ let(:plain_text_token) { "plain text token" }
52
+ let(:access_token) { FactoryBot.create :access_token }
53
+
54
+ before do
55
+ # Assume we have a plain text token from before activating the option
56
+ access_token.update_column(:token, plain_text_token)
57
+ end
58
+
59
+ context "without fallback lookup" do
60
+ it "does not provide lookups with either through by_token" do
61
+ expect(clazz.by_token(plain_text_token)).to eq(nil)
62
+ expect(clazz.by_token(access_token.token)).to eq(nil)
63
+
64
+ # And it does not touch the token
65
+ access_token.reload
66
+ expect(access_token.token).to eq(plain_text_token)
67
+ end
68
+ end
69
+
70
+ context "with fallback lookup" do
71
+ include_context "with token hashing and fallback lookup enabled"
72
+
73
+ it "upgrades a plain token when falling back to it" do
74
+ # Side-effect: This will automatically upgrade the token
75
+ expect(clazz).to receive(:upgrade_fallback_value).and_call_original
76
+ expect(clazz.by_token(plain_text_token)).to eq(access_token)
77
+
78
+ # Will find subsequently by hashing the token
79
+ expect(clazz.by_token(plain_text_token)).to eq(access_token)
80
+
81
+ # And it modifies the token value
82
+ access_token.reload
83
+ expect(access_token.token).not_to eq(plain_text_token)
84
+ expect(clazz.find_by(token: plain_text_token)).to eq(nil)
85
+ expect(clazz.find_by(token: access_token.token)).not_to be_nil
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ it "generates a token using a custom object" do
28
92
  eigenclass = class << CustomGeneratorArgs; self; end
29
93
  eigenclass.class_eval do
30
94
  remove_method :generate
@@ -44,7 +108,7 @@ module Doorkeeper
44
108
  expect(token.token).to match(/custom_generator_token_\d+/)
45
109
  end
46
110
 
47
- it 'allows the custom generator to access the application details' do
111
+ it "allows the custom generator to access the application details" do
48
112
  eigenclass = class << CustomGeneratorArgs; self; end
49
113
  eigenclass.class_eval do
50
114
  remove_method :generate
@@ -64,7 +128,7 @@ module Doorkeeper
64
128
  expect(token.token).to match(/custom_generator_token_Application \d+/)
65
129
  end
66
130
 
67
- it 'allows the custom generator to access the scopes' do
131
+ it "allows the custom generator to access the scopes" do
68
132
  eigenclass = class << CustomGeneratorArgs; self; end
69
133
  eigenclass.class_eval do
70
134
  remove_method :generate
@@ -80,12 +144,12 @@ module Doorkeeper
80
144
  access_token_generator "Doorkeeper::CustomGeneratorArgs"
81
145
  end
82
146
 
83
- token = FactoryBot.create :access_token, scopes: 'public write'
147
+ token = FactoryBot.create :access_token, scopes: "public write"
84
148
 
85
- expect(token.token).to eq 'custom_generator_token_2_public write'
149
+ expect(token.token).to eq "custom_generator_token_2_public write"
86
150
  end
87
151
 
88
- it 'allows the custom generator to access the expiry length' do
152
+ it "allows the custom generator to access the expiry length" do
89
153
  eigenclass = class << CustomGeneratorArgs; self; end
90
154
  eigenclass.class_eval do
91
155
  remove_method :generate
@@ -102,10 +166,10 @@ module Doorkeeper
102
166
  end
103
167
 
104
168
  token = FactoryBot.create :access_token
105
- expect(token.token).to eq 'custom_generator_token_7200'
169
+ expect(token.token).to eq "custom_generator_token_7200"
106
170
  end
107
171
 
108
- it 'allows the custom generator to access the created time' do
172
+ it "allows the custom generator to access the created time" do
109
173
  module CustomGeneratorArgs
110
174
  def self.generate(opts = {})
111
175
  "custom_generator_token_#{opts[:created_at].to_i}"
@@ -122,7 +186,7 @@ module Doorkeeper
122
186
  expect(token.token).to eq "custom_generator_token_#{created_at.to_i}"
123
187
  end
124
188
 
125
- it 'raises an error if the custom object does not support generate' do
189
+ it "raises an error if the custom object does not support generate" do
126
190
  module NoGenerate
127
191
  end
128
192
 
@@ -136,7 +200,7 @@ module Doorkeeper
136
200
  )
137
201
  end
138
202
 
139
- it 'raises original error if something went wrong in custom generator' do
203
+ it "raises original error if something went wrong in custom generator" do
140
204
  eigenclass = class << CustomGeneratorArgs; self; end
141
205
  eigenclass.class_eval do
142
206
  remove_method :generate
@@ -144,7 +208,7 @@ module Doorkeeper
144
208
 
145
209
  module CustomGeneratorArgs
146
210
  def self.generate(_opts = {})
147
- raise LoadError, 'custom behaviour'
211
+ raise LoadError, "custom behaviour"
148
212
  end
149
213
  end
150
214
 
@@ -158,7 +222,7 @@ module Doorkeeper
158
222
  )
159
223
  end
160
224
 
161
- it 'raises an error if the custom object does not exist' do
225
+ it "raises an error if the custom object does not exist" do
162
226
  Doorkeeper.configure do
163
227
  orm DOORKEEPER_ORM
164
228
  access_token_generator "Doorkeeper::NotReal"
@@ -171,24 +235,24 @@ module Doorkeeper
171
235
  end
172
236
 
173
237
  describe :refresh_token do
174
- it 'has empty refresh token if it was not required' do
238
+ it "has empty refresh token if it was not required" do
175
239
  token = FactoryBot.create :access_token
176
240
  expect(token.refresh_token).to be_nil
177
241
  end
178
242
 
179
- it 'generates a refresh token if it was requested' do
243
+ it "generates a refresh token if it was requested" do
180
244
  token = FactoryBot.create :access_token, use_refresh_token: true
181
245
  expect(token.refresh_token).not_to be_nil
182
246
  end
183
247
 
184
- it 'is not valid if token exists' do
248
+ it "is not valid if token exists" do
185
249
  token1 = FactoryBot.create :access_token, use_refresh_token: true
186
250
  token2 = FactoryBot.create :access_token, use_refresh_token: true
187
251
  token2.refresh_token = token1.refresh_token
188
252
  expect(token2).not_to be_valid
189
253
  end
190
254
 
191
- it 'expects database to raise an error if refresh tokens are the same' do
255
+ it "expects database to raise an error if refresh tokens are the same" do
192
256
  token1 = FactoryBot.create :access_token, use_refresh_token: true
193
257
  token2 = FactoryBot.create :access_token, use_refresh_token: true
194
258
  expect do
@@ -196,24 +260,85 @@ module Doorkeeper
196
260
  token2.save(validate: false)
197
261
  end.to raise_error(uniqueness_error)
198
262
  end
263
+
264
+ context "with hashing enabled" do
265
+ include_context "with token hashing enabled"
266
+ let(:token) { FactoryBot.create :access_token, use_refresh_token: true }
267
+
268
+ it "holds a volatile refresh token when created" do
269
+ expect(token.plaintext_refresh_token).to be_a(String)
270
+ expect(token.refresh_token)
271
+ .to eq(hashed_or_plain_token_func.call(token.plaintext_refresh_token))
272
+
273
+ # Finder method only finds the hashed token
274
+ loaded = clazz.find_by(refresh_token: token.refresh_token)
275
+ expect(loaded).to eq(token)
276
+ expect(loaded.plaintext_refresh_token).to be_nil
277
+ expect(loaded.refresh_token).to eq(token.refresh_token)
278
+ end
279
+
280
+ it "does not find_by plain text refresh tokens" do
281
+ expect(clazz.find_by(refresh_token: token.plaintext_refresh_token)).to be_nil
282
+ end
283
+
284
+ describe "with having a plain text token" do
285
+ let(:plain_refresh_token) { "plain refresh token" }
286
+ let(:access_token) { FactoryBot.create :access_token }
287
+
288
+ before do
289
+ # Assume we have a plain text token from before activating the option
290
+ access_token.update_column(:refresh_token, plain_refresh_token)
291
+ end
292
+
293
+ context "without fallback lookup" do
294
+ it "does not provide lookups with either through by_token" do
295
+ expect(clazz.by_refresh_token(plain_refresh_token)).to eq(nil)
296
+ expect(clazz.by_refresh_token(access_token.refresh_token)).to eq(nil)
297
+
298
+ # And it does not touch the token
299
+ access_token.reload
300
+ expect(access_token.refresh_token).to eq(plain_refresh_token)
301
+ end
302
+ end
303
+
304
+ context "with fallback lookup" do
305
+ include_context "with token hashing and fallback lookup enabled"
306
+
307
+ it "upgrades a plain token when falling back to it" do
308
+ # Side-effect: This will automatically upgrade the token
309
+ expect(clazz).to receive(:upgrade_fallback_value).and_call_original
310
+ expect(clazz.by_refresh_token(plain_refresh_token)).to eq(access_token)
311
+
312
+ # Will find subsequently by hashing the token
313
+ expect(clazz.by_refresh_token(plain_refresh_token)).to eq(access_token)
314
+
315
+ # And it modifies the token value
316
+ access_token.reload
317
+ expect(access_token.refresh_token).not_to eq(plain_refresh_token)
318
+ expect(clazz.find_by(refresh_token: plain_refresh_token)).to eq(nil)
319
+ expect(clazz.find_by(refresh_token: access_token.refresh_token)).not_to be_nil
320
+ end
321
+ end
322
+ end
323
+ end
199
324
  end
200
325
 
201
- describe 'validations' do
202
- it 'is valid without resource_owner_id' do
326
+ describe "validations" do
327
+ it "is valid without resource_owner_id" do
203
328
  # For client credentials flow
204
329
  subject.resource_owner_id = nil
205
330
  expect(subject).to be_valid
206
331
  end
207
332
 
208
- it 'is valid without application_id' do
333
+ it "is valid without application_id" do
209
334
  # For resource owner credentials flow
210
335
  subject.application_id = nil
211
336
  expect(subject).to be_valid
212
337
  end
213
338
  end
214
339
 
215
- describe '#same_credential?' do
216
- context 'with default parameters' do
340
+ describe "#same_credential?" do
341
+ context "with default parameters" do
217
342
  let(:resource_owner_id) { 100 }
218
343
  let(:application) { FactoryBot.create :application }
219
344
  let(:default_attributes) do
@@ -221,14 +346,14 @@ module Doorkeeper
221
346
  end
222
347
  let(:access_token1) { FactoryBot.create :access_token, default_attributes }
223
348
 
224
- context 'the second token has the same owner and same app' do
349
+ context "the second token has the same owner and same app" do
225
350
  let(:access_token2) { FactoryBot.create :access_token, default_attributes }
226
- it 'success' do
351
+ it "success" do
227
352
  expect(access_token1.same_credential?(access_token2)).to be_truthy
228
353
  end
229
354
  end
230
355
 
231
- context 'the second token has same owner and different app' do
356
+ context "the second token has same owner and different app" do
232
357
  let(:other_application) { FactoryBot.create :application }
233
358
  let(:access_token2) do
234
359
  FactoryBot.create :access_token,
@@ -236,72 +361,72 @@ module Doorkeeper
236
361
  resource_owner_id: resource_owner_id
237
362
  end
238
363
 
239
- it 'fail' do
364
+ it "fail" do
240
365
  expect(access_token1.same_credential?(access_token2)).to be_falsey
241
366
  end
242
367
  end
243
368
 
244
- context 'the second token has different owner and different app' do
369
+ context "the second token has different owner and different app" do
245
370
  let(:other_application) { FactoryBot.create :application }
246
371
  let(:access_token2) do
247
372
  FactoryBot.create :access_token, application: other_application, resource_owner_id: 42
248
373
  end
249
374
 
250
- it 'fail' do
375
+ it "fail" do
251
376
  expect(access_token1.same_credential?(access_token2)).to be_falsey
252
377
  end
253
378
  end
254
379
 
255
- context 'the second token has different owner and same app' do
380
+ context "the second token has different owner and same app" do
256
381
  let(:access_token2) do
257
382
  FactoryBot.create :access_token, application: application, resource_owner_id: 42
258
383
  end
259
384
 
260
- it 'fail' do
385
+ it "fail" do
261
386
  expect(access_token1.same_credential?(access_token2)).to be_falsey
262
387
  end
263
388
  end
264
389
  end
265
390
  end
266
391
 
267
- describe '#acceptable?' do
268
- context 'a token that is not accessible' do
392
+ describe "#acceptable?" do
393
+ context "a token that is not accessible" do
269
394
  let(:token) { FactoryBot.create(:access_token, created_at: 6.hours.ago) }
270
395
 
271
- it 'should return false' do
396
+ it "should return false" do
272
397
  expect(token.acceptable?(nil)).to be false
273
398
  end
274
399
  end
275
400
 
276
- context 'a token that has the incorrect scopes' do
401
+ context "a token that has the incorrect scopes" do
277
402
  let(:token) { FactoryBot.create(:access_token) }
278
403
 
279
- it 'should return false' do
280
- expect(token.acceptable?(['public'])).to be false
404
+ it "should return false" do
405
+ expect(token.acceptable?(["public"])).to be false
281
406
  end
282
407
  end
283
408
 
284
- context 'a token is acceptable with the correct scopes' do
409
+ context "a token is acceptable with the correct scopes" do
285
410
  let(:token) do
286
411
  token = FactoryBot.create(:access_token)
287
- token[:scopes] = 'public'
412
+ token[:scopes] = "public"
288
413
  token
289
414
  end
290
415
 
291
- it 'should return true' do
292
- expect(token.acceptable?(['public'])).to be true
416
+ it "should return true" do
417
+ expect(token.acceptable?(["public"])).to be true
293
418
  end
294
419
  end
295
420
  end
296
421
 
297
- describe '.revoke_all_for' do
422
+ describe ".revoke_all_for" do
298
423
  let(:resource_owner) { double(id: 100) }
299
424
  let(:application) { FactoryBot.create :application }
300
425
  let(:default_attributes) do
301
426
  { application: application, resource_owner_id: resource_owner.id }
302
427
  end
303
428
 
304
- it 'revokes all tokens for given application and resource owner' do
429
+ it "revokes all tokens for given application and resource owner" do
305
430
  FactoryBot.create :access_token, default_attributes
306
431
  AccessToken.revoke_all_for application.id, resource_owner
307
432
  AccessToken.all.each do |token|
@@ -309,7 +434,7 @@ module Doorkeeper
309
434
  end
310
435
  end
311
436
 
312
- it 'matches application' do
437
+ it "matches application" do
313
438
  access_token_for_different_app = FactoryBot.create(
314
439
  :access_token,
315
440
  default_attributes.merge(application: FactoryBot.create(:application))
@@ -320,7 +445,7 @@ module Doorkeeper
320
445
  expect(access_token_for_different_app.reload).not_to be_revoked
321
446
  end
322
447
 
323
- it 'matches resource owner' do
448
+ it "matches resource owner" do
324
449
  access_token_for_different_owner = FactoryBot.create(
325
450
  :access_token,
326
451
  default_attributes.merge(resource_owner_id: 90)
@@ -332,15 +457,15 @@ module Doorkeeper
332
457
  end
333
458
  end
334
459
 
335
- describe '.matching_token_for' do
460
+ describe ".matching_token_for" do
336
461
  let(:resource_owner_id) { 100 }
337
462
  let(:application) { FactoryBot.create :application }
338
- let(:scopes) { Doorkeeper::OAuth::Scopes.from_string('public write') }
463
+ let(:scopes) { Doorkeeper::OAuth::Scopes.from_string("public write") }
339
464
  let(:default_attributes) do
340
465
  {
341
466
  application: application,
342
467
  resource_owner_id: resource_owner_id,
343
- scopes: scopes.to_s
468
+ scopes: scopes.to_s,
344
469
  }
345
470
  end
346
471
 
@@ -348,26 +473,26 @@ module Doorkeeper
348
473
  default_scopes_exist(*scopes.all)
349
474
  end
350
475
 
351
- it 'returns only one token' do
476
+ it "returns only one token" do
352
477
  token = FactoryBot.create :access_token, default_attributes
353
478
  last_token = AccessToken.matching_token_for(application, resource_owner_id, scopes)
354
479
  expect(last_token).to eq(token)
355
480
  end
356
481
 
357
- it 'accepts resource owner as object' do
482
+ it "accepts resource owner as object" do
358
483
  resource_owner = double(to_key: true, id: 100)
359
484
  token = FactoryBot.create :access_token, default_attributes
360
485
  last_token = AccessToken.matching_token_for(application, resource_owner, scopes)
361
486
  expect(last_token).to eq(token)
362
487
  end
363
488
 
364
- it 'accepts nil as resource owner' do
489
+ it "accepts nil as resource owner" do
365
490
  token = FactoryBot.create :access_token, default_attributes.merge(resource_owner_id: nil)
366
491
  last_token = AccessToken.matching_token_for(application, nil, scopes)
367
492
  expect(last_token).to eq(token)
368
493
  end
369
494
 
370
- it 'excludes revoked tokens' do
495
+ it "excludes revoked tokens" do
371
496
  FactoryBot.create :access_token, default_attributes.merge(revoked_at: 1.day.ago)
372
497
  last_token = AccessToken.matching_token_for(application, resource_owner_id, scopes)
373
498
  expect(last_token).to be_nil
@@ -386,32 +511,32 @@ module Doorkeeper
386
511
  end
387
512
 
388
513
  it "excludes tokens with fewer scopes" do
389
- FactoryBot.create :access_token, default_attributes.merge(scopes: 'public')
514
+ FactoryBot.create :access_token, default_attributes.merge(scopes: "public")
390
515
  last_token = AccessToken.matching_token_for(application, resource_owner_id, scopes)
391
516
  expect(last_token).to be_nil
392
517
  end
393
518
 
394
- it 'excludes tokens with different scopes' do
395
- FactoryBot.create :access_token, default_attributes.merge(scopes: 'public email')
519
+ it "excludes tokens with different scopes" do
520
+ FactoryBot.create :access_token, default_attributes.merge(scopes: "public email")
396
521
  last_token = AccessToken.matching_token_for(application, resource_owner_id, scopes)
397
522
  expect(last_token).to be_nil
398
523
  end
399
524
 
400
- it 'excludes tokens with additional scopes' do
401
- FactoryBot.create :access_token, default_attributes.merge(scopes: 'public write email')
525
+ it "excludes tokens with additional scopes" do
526
+ FactoryBot.create :access_token, default_attributes.merge(scopes: "public write email")
402
527
  last_token = AccessToken.matching_token_for(application, resource_owner_id, scopes)
403
528
  expect(last_token).to be_nil
404
529
  end
405
530
 
406
- it 'excludes tokens with scopes that are not present in server scopes' do
531
+ it "excludes tokens with scopes that are not present in server scopes" do
407
532
  FactoryBot.create :access_token, default_attributes.merge(
408
- application: application, scopes: 'public read'
533
+ application: application, scopes: "public read"
409
534
  )
410
535
  last_token = AccessToken.matching_token_for(application, resource_owner_id, scopes)
411
536
  expect(last_token).to be_nil
412
537
  end
413
538
 
414
- it 'excludes tokens with scopes that are not present in application scopes' do
539
+ it "excludes tokens with scopes that are not present in application scopes" do
415
540
  application = FactoryBot.create :application, scopes: "private read"
416
541
  FactoryBot.create :access_token, default_attributes.merge(
417
542
  application: application
@@ -420,11 +545,11 @@ module Doorkeeper
420
545
  expect(last_token).to be_nil
421
546
  end
422
547
 
423
- it 'does not match token if empty scope requested and token/app scopes present' do
548
+ it "does not match token if empty scope requested and token/app scopes present" do
424
549
  application = FactoryBot.create :application, scopes: "sample:scope"
425
550
  app_params = {
426
551
  application_id: application.id, scopes: "sample:scope",
427
- resource_owner_id: 100
552
+ resource_owner_id: 100,
428
553
  }
429
554
  FactoryBot.create :access_token, app_params
430
555
  empty_scopes = Doorkeeper::OAuth::Scopes.from_string("")
@@ -432,17 +557,17 @@ module Doorkeeper
432
557
  expect(last_token).to be_nil
433
558
  end
434
559
 
435
- it 'matches token if empty scope requested and no token scopes present' do
560
+ it "matches token if empty scope requested and no token scopes present" do
436
561
  empty_scopes = Doorkeeper::OAuth::Scopes.from_string("")
437
562
  token = FactoryBot.create :access_token, default_attributes.merge(scopes: empty_scopes)
438
563
  last_token = AccessToken.matching_token_for(application, 100, empty_scopes)
439
564
  expect(last_token).to eq(token)
440
565
  end
441
566
 
442
- it 'returns the last matching token' do
567
+ it "returns the last matching token" do
443
568
  FactoryBot.create :access_token, default_attributes.merge(created_at: 1.day.ago)
444
569
  matching_token = FactoryBot.create :access_token, default_attributes
445
- FactoryBot.create :access_token, default_attributes.merge(scopes: 'public')
570
+ FactoryBot.create :access_token, default_attributes.merge(scopes: "public")
446
571
 
447
572
  last_token = AccessToken.matching_token_for(application, resource_owner_id, scopes)
448
573
  expect(last_token).to eq(matching_token)
@@ -453,11 +578,11 @@ module Doorkeeper
453
578
  it "returns as_json hash" do
454
579
  token = FactoryBot.create :access_token
455
580
  token_hash = {
456
- resource_owner_id: token.resource_owner_id,
457
- scope: token.scopes,
458
- expires_in: token.expires_in_seconds,
459
- application: { uid: token.application.uid },
460
- created_at: token.created_at.to_i
581
+ resource_owner_id: token.resource_owner_id,
582
+ scope: token.scopes,
583
+ expires_in: token.expires_in_seconds,
584
+ application: { uid: token.application.uid },
585
+ created_at: token.created_at.to_i,
461
586
  }
462
587
  expect(token.as_json).to eq token_hash
463
588
  end