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,374 +1,366 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "spec_helper"
4
+ require "bcrypt"
4
5
 
5
- describe Doorkeeper::Application do
6
- let(:require_owner) { Doorkeeper.configuration.instance_variable_set("@confirm_application_owner", true) }
7
- let(:unset_require_owner) { Doorkeeper.configuration.instance_variable_set("@confirm_application_owner", false) }
8
- let(:new_application) { FactoryBot.build(:application) }
6
+ module Doorkeeper
7
+ describe Application do
8
+ let(:clazz) { Doorkeeper::Application }
9
+ let(:require_owner) { Doorkeeper.configuration.instance_variable_set("@confirm_application_owner", true) }
10
+ let(:unset_require_owner) { Doorkeeper.configuration.instance_variable_set("@confirm_application_owner", false) }
11
+ let(:new_application) { FactoryBot.build(:application) }
9
12
 
10
- let(:uid) { SecureRandom.hex(8) }
11
- let(:secret) { SecureRandom.hex(8) }
13
+ let(:uid) { SecureRandom.hex(8) }
14
+ let(:secret) { SecureRandom.hex(8) }
12
15
 
13
- it "is invalid without a name" do
14
- new_application.name = nil
15
- expect(new_application).not_to be_valid
16
- end
17
-
18
- it "is invalid without determining confidentiality" do
19
- new_application.confidential = nil
20
- expect(new_application).not_to be_valid
21
- end
16
+ context "application_owner is enabled" do
17
+ before do
18
+ Doorkeeper.configure do
19
+ orm DOORKEEPER_ORM
20
+ enable_application_owner
21
+ end
22
+ end
22
23
 
23
- it "generates uid on create" do
24
- expect(new_application.uid).to be_nil
25
- new_application.save
26
- expect(new_application.uid).not_to be_nil
27
- end
24
+ context "application owner is not required" do
25
+ before(:each) do
26
+ unset_require_owner
27
+ end
28
28
 
29
- it "generates uid on create if an empty string" do
30
- new_application.uid = ""
31
- new_application.save
32
- expect(new_application.uid).not_to be_blank
33
- end
29
+ it "is valid given valid attributes" do
30
+ expect(new_application).to be_valid
31
+ end
32
+ end
34
33
 
35
- it "generates uid on create unless one is set" do
36
- new_application.uid = uid
37
- new_application.save
38
- expect(new_application.uid).to eq(uid)
39
- end
34
+ context "application owner is required" do
35
+ before(:each) do
36
+ require_owner
37
+ @owner = FactoryBot.build_stubbed(:doorkeeper_testing_user)
38
+ end
40
39
 
41
- it "is invalid without uid" do
42
- new_application.save
43
- new_application.uid = nil
44
- expect(new_application).not_to be_valid
45
- end
40
+ it "is invalid without an owner" do
41
+ expect(new_application).not_to be_valid
42
+ end
46
43
 
47
- it "checks uniqueness of uid" do
48
- app1 = FactoryBot.create(:application)
49
- app2 = FactoryBot.create(:application)
50
- app2.uid = app1.uid
51
- expect(app2).not_to be_valid
52
- end
44
+ it "is valid with an owner" do
45
+ new_application.owner = @owner
46
+ expect(new_application).to be_valid
47
+ end
48
+ end
49
+ end
53
50
 
54
- it "expects database to throw an error when uids are the same" do
55
- app1 = FactoryBot.create(:application)
56
- app2 = FactoryBot.create(:application)
57
- app2.uid = app1.uid
58
- expect { app2.save!(validate: false) }.to raise_error(uniqueness_error)
59
- end
51
+ it "is invalid without a name" do
52
+ new_application.name = nil
53
+ expect(new_application).not_to be_valid
54
+ end
60
55
 
61
- it "generate secret on create" do
62
- expect(new_application.secret).to be_nil
63
- new_application.save
64
- expect(new_application.secret).not_to be_nil
65
- end
56
+ it "is invalid without determining confidentiality" do
57
+ new_application.confidential = nil
58
+ expect(new_application).not_to be_valid
59
+ end
66
60
 
67
- it "generate secret on create if is blank string" do
68
- new_application.secret = ""
69
- new_application.save
70
- expect(new_application.secret).not_to be_blank
71
- end
61
+ it "generates uid on create" do
62
+ expect(new_application.uid).to be_nil
63
+ new_application.save
64
+ expect(new_application.uid).not_to be_nil
65
+ end
72
66
 
73
- it "generate secret on create unless one is set" do
74
- new_application.secret = secret
75
- new_application.save
76
- expect(new_application.secret).to eq(secret)
77
- end
67
+ it "generates uid on create if an empty string" do
68
+ new_application.uid = ""
69
+ new_application.save
70
+ expect(new_application.uid).not_to be_blank
71
+ end
78
72
 
79
- it "is invalid without secret" do
80
- new_application.save
81
- new_application.secret = nil
82
- expect(new_application).not_to be_valid
83
- end
73
+ it "generates uid on create unless one is set" do
74
+ new_application.uid = uid
75
+ new_application.save
76
+ expect(new_application.uid).to eq(uid)
77
+ end
84
78
 
85
- context "application_owner is enabled" do
86
- before do
87
- Doorkeeper.configure do
88
- orm DOORKEEPER_ORM
89
- enable_application_owner
90
- end
79
+ it "is invalid without uid" do
80
+ new_application.save
81
+ new_application.uid = nil
82
+ expect(new_application).not_to be_valid
91
83
  end
92
84
 
93
- context "application owner is not required" do
94
- before(:each) do
95
- unset_require_owner
96
- end
85
+ context "redirect URI" do
86
+ context "when grant flows allow blank redirect URI" do
87
+ before do
88
+ Doorkeeper.configure do
89
+ grant_flows %w[password client_credentials]
90
+ end
91
+ end
97
92
 
98
- it "is valid given valid attributes" do
99
- expect(new_application).to be_valid
93
+ it "is valid without redirect_uri" do
94
+ new_application.save
95
+ new_application.redirect_uri = nil
96
+ expect(new_application).to be_valid
97
+ end
100
98
  end
101
- end
102
99
 
103
- context "application owner is required" do
104
- before do
105
- require_owner
106
- @owner = FactoryBot.build_stubbed(:doorkeeper_testing_user)
107
- end
100
+ context "when grant flows require redirect URI" do
101
+ before do
102
+ Doorkeeper.configure do
103
+ grant_flows %w[password client_credentials authorization_code]
104
+ end
105
+ end
108
106
 
109
- it "is invalid without an owner" do
110
- expect(new_application).not_to be_valid
107
+ it "is invalid without redirect_uri" do
108
+ new_application.save
109
+ new_application.redirect_uri = nil
110
+ expect(new_application).not_to be_valid
111
+ end
111
112
  end
112
113
 
113
- it "is valid with an owner" do
114
- new_application.owner = @owner
115
- expect(new_application).to be_valid
114
+ context "when blank URI option disabled" do
115
+ before do
116
+ Doorkeeper.configure do
117
+ grant_flows %w[password client_credentials]
118
+ allow_blank_redirect_uri false
119
+ end
120
+ end
121
+
122
+ it "is invalid without redirect_uri" do
123
+ new_application.save
124
+ new_application.redirect_uri = nil
125
+ expect(new_application).not_to be_valid
126
+ end
116
127
  end
117
128
  end
118
- end
119
129
 
120
- context "redirect URI" do
121
- it "is invalid without redirect_uri" do
122
- new_application.save
123
- new_application.redirect_uri = nil
124
- expect(new_application).not_to be_valid
130
+ it "checks uniqueness of uid" do
131
+ app1 = FactoryBot.create(:application)
132
+ app2 = FactoryBot.create(:application)
133
+ app2.uid = app1.uid
134
+ expect(app2).not_to be_valid
125
135
  end
126
- end
127
136
 
128
- describe "destroy related models on cascade" do
129
- before(:each) do
130
- new_application.save
137
+ it "expects database to throw an error when uids are the same" do
138
+ app1 = FactoryBot.create(:application)
139
+ app2 = FactoryBot.create(:application)
140
+ app2.uid = app1.uid
141
+ expect { app2.save!(validate: false) }.to raise_error(uniqueness_error)
131
142
  end
132
143
 
133
- let(:resource_owner) { FactoryBot.create(:doorkeeper_testing_user) }
144
+ it "generate secret on create" do
145
+ expect(new_application.secret).to be_nil
146
+ new_application.save
147
+ expect(new_application.secret).not_to be_nil
148
+ end
134
149
 
135
- it "should destroy its access grants" do
136
- FactoryBot.create(
137
- :access_grant,
138
- application: new_application,
139
- resource_owner_id: resource_owner.id,
140
- )
150
+ it "generate secret on create if is blank string" do
151
+ new_application.secret = ""
152
+ new_application.save
153
+ expect(new_application.secret).not_to be_blank
154
+ end
141
155
 
142
- expect { new_application.destroy }.to change { Doorkeeper::AccessGrant.count }.by(-1)
156
+ it "generate secret on create unless one is set" do
157
+ new_application.secret = secret
158
+ new_application.save
159
+ expect(new_application.secret).to eq(secret)
143
160
  end
144
161
 
145
- it "should destroy its access tokens" do
146
- FactoryBot.create(:access_token, application: new_application)
147
- FactoryBot.create(:access_token, application: new_application, revoked_at: Time.now.utc)
148
- expect do
149
- new_application.destroy
150
- end.to change { Doorkeeper::AccessToken.count }.by(-2)
162
+ it "is invalid without secret" do
163
+ new_application.save
164
+ new_application.secret = nil
165
+ expect(new_application).not_to be_valid
151
166
  end
152
- end
153
167
 
154
- describe "#ordered_by" do
155
- let(:applications) { FactoryBot.create_list(:application, 5) }
168
+ context "with hashing enabled" do
169
+ include_context "with application hashing enabled"
170
+ let(:app) { FactoryBot.create :application }
171
+ let(:default_strategy) { Doorkeeper::SecretStoring::Sha256Hash }
156
172
 
157
- context "when a direction is not specified" do
158
- it "calls order with a default order of asc" do
159
- names = applications.map(&:name).sort
160
- expect(described_class.ordered_by(:name).map(&:name)).to eq(names)
173
+ it "uses SHA256 to avoid additional dependencies" do
174
+ # Ensure token was generated
175
+ app.validate
176
+ expect(app.secret).to eq(default_strategy.transform_secret(app.plaintext_secret))
161
177
  end
162
- end
163
178
 
164
- context "when a direction is specified" do
165
- it "calls order with specified direction" do
166
- names = applications.map(&:name).sort.reverse
167
- expect(described_class.ordered_by(:name, :desc).map(&:name)).to eq(names)
168
- end
169
- end
170
- end
179
+ context "when bcrypt strategy is configured" do
180
+ # In this text context, we have bcrypt loaded so `bcrypt_present?`
181
+ # will always be true
182
+ before do
183
+ Doorkeeper.configure do
184
+ hash_application_secrets using: "Doorkeeper::SecretStoring::BCrypt"
185
+ end
186
+ end
171
187
 
172
- describe "#redirect_uri=" do
173
- context "when array of valid redirect_uris" do
174
- it "should join by newline" do
175
- new_application.redirect_uri = ["http://localhost/callback1", "http://localhost/callback2"]
176
- expect(new_application.redirect_uri).to eq("http://localhost/callback1\nhttp://localhost/callback2")
177
- end
178
- end
179
- context "when string of valid redirect_uris" do
180
- it "should store as-is" do
181
- new_application.redirect_uri = "http://localhost/callback1\nhttp://localhost/callback2"
182
- expect(new_application.redirect_uri).to eq("http://localhost/callback1\nhttp://localhost/callback2")
188
+ it "holds a volatile plaintext and BCrypt secret" do
189
+ expect(app.secret_strategy).to eq Doorkeeper::SecretStoring::BCrypt
190
+ expect(app.plaintext_secret).to be_a(String)
191
+ expect(app.secret).not_to eq(app.plaintext_secret)
192
+ expect { ::BCrypt::Password.create(app.secret) }.not_to raise_error
193
+ end
183
194
  end
184
- end
185
- end
186
195
 
187
- describe "#authorized_for" do
188
- let(:resource_owner) { FactoryBot.create(:doorkeeper_testing_user) }
189
- let(:other_resource_owner) { FactoryBot.create(:doorkeeper_testing_user) }
196
+ it "does not fallback to plain lookup by default" do
197
+ lookup = clazz.by_uid_and_secret(app.uid, app.secret)
198
+ expect(lookup).to eq(nil)
190
199
 
191
- it "is empty if the application is not authorized for anyone" do
192
- expect(described_class.authorized_for(resource_owner)).to be_empty
193
- end
200
+ lookup = clazz.by_uid_and_secret(app.uid, app.plaintext_secret)
201
+ expect(lookup).to eq(app)
202
+ end
194
203
 
195
- it "returns only application for a specific resource owner" do
196
- FactoryBot.create(
197
- :access_token,
198
- resource_owner_id: other_resource_owner.id,
199
- )
200
- token = FactoryBot.create(
201
- :access_token,
202
- resource_owner_id: resource_owner.id,
203
- )
204
- expect(described_class.authorized_for(resource_owner)).to eq([token.application])
205
- end
204
+ context "with fallback enabled" do
205
+ include_context "with token hashing and fallback lookup enabled"
206
206
 
207
- it "excludes revoked tokens" do
208
- FactoryBot.create(
209
- :access_token,
210
- resource_owner_id: resource_owner.id,
211
- revoked_at: 2.days.ago,
212
- )
213
- expect(described_class.authorized_for(resource_owner)).to be_empty
214
- end
207
+ it "provides plain and hashed lookup" do
208
+ lookup = clazz.by_uid_and_secret(app.uid, app.secret)
209
+ expect(lookup).to eq(app)
215
210
 
216
- it "returns all applications that have been authorized" do
217
- token1 = FactoryBot.create(
218
- :access_token,
219
- resource_owner_id: resource_owner.id,
220
- )
221
- token2 = FactoryBot.create(
222
- :access_token,
223
- resource_owner_id: resource_owner.id,
224
- )
225
- expect(described_class.authorized_for(resource_owner))
226
- .to eq([token1.application, token2.application])
227
- end
211
+ lookup = clazz.by_uid_and_secret(app.uid, app.plaintext_secret)
212
+ expect(lookup).to eq(app)
213
+ end
214
+ end
228
215
 
229
- it "returns only one application even if it has been authorized twice" do
230
- application = FactoryBot.create(:application)
231
- FactoryBot.create(
232
- :access_token,
233
- resource_owner_id: resource_owner.id,
234
- application: application,
235
- )
236
- FactoryBot.create(
237
- :access_token,
238
- resource_owner_id: resource_owner.id,
239
- application: application,
240
- )
241
- expect(described_class.authorized_for(resource_owner)).to eq([application])
216
+ it "does not provide access to secret after loading" do
217
+ lookup = clazz.by_uid_and_secret(app.uid, app.plaintext_secret)
218
+ expect(lookup.plaintext_secret).to be_nil
219
+ end
242
220
  end
243
- end
244
221
 
245
- describe "#revoke_tokens_and_grants_for" do
246
- it "revokes all access tokens and access grants" do
247
- application_id = 42
248
- resource_owner = double
249
- expect(Doorkeeper::AccessToken)
250
- .to receive(:revoke_all_for).with(application_id, resource_owner)
251
- expect(Doorkeeper::AccessGrant)
252
- .to receive(:revoke_all_for).with(application_id, resource_owner)
222
+ describe "destroy related models on cascade" do
223
+ before(:each) do
224
+ new_application.save
225
+ end
226
+
227
+ it "should destroy its access grants" do
228
+ FactoryBot.create(:access_grant, application: new_application)
229
+ expect { new_application.destroy }.to change { Doorkeeper::AccessGrant.count }.by(-1)
230
+ end
253
231
 
254
- described_class.revoke_tokens_and_grants_for(application_id, resource_owner)
232
+ it "should destroy its access tokens" do
233
+ FactoryBot.create(:access_token, application: new_application)
234
+ FactoryBot.create(:access_token, application: new_application, revoked_at: Time.now.utc)
235
+ expect do
236
+ new_application.destroy
237
+ end.to change { Doorkeeper::AccessToken.count }.by(-2)
238
+ end
255
239
  end
256
- end
257
240
 
258
- describe "#by_uid_and_secret" do
259
- context "when application is private/confidential" do
260
- it "finds the application via uid/secret" do
261
- app = FactoryBot.create :application
262
- authenticated = described_class.by_uid_and_secret(app.uid, app.secret)
263
- expect(authenticated).to eq(app)
241
+ describe :ordered_by do
242
+ let(:applications) { FactoryBot.create_list(:application, 5) }
243
+
244
+ context "when a direction is not specified" do
245
+ it "calls order with a default order of asc" do
246
+ names = applications.map(&:name).sort
247
+ expect(Application.ordered_by(:name).map(&:name)).to eq(names)
248
+ end
264
249
  end
265
- context "when secret is wrong" do
266
- it "should not find the application" do
267
- app = FactoryBot.create :application
268
- authenticated = described_class.by_uid_and_secret(app.uid, "bad")
269
- expect(authenticated).to eq(nil)
250
+
251
+ context "when a direction is specified" do
252
+ it "calls order with specified direction" do
253
+ names = applications.map(&:name).sort.reverse
254
+ expect(Application.ordered_by(:name, :desc).map(&:name)).to eq(names)
270
255
  end
271
256
  end
272
257
  end
273
258
 
274
- context "when application is public/non-confidential" do
275
- context "when secret is blank" do
276
- it "should find the application" do
277
- app = FactoryBot.create :application, confidential: false
278
- authenticated = described_class.by_uid_and_secret(app.uid, nil)
279
- expect(authenticated).to eq(app)
259
+ describe "#redirect_uri=" do
260
+ context "when array of valid redirect_uris" do
261
+ it "should join by newline" do
262
+ new_application.redirect_uri = ["http://localhost/callback1", "http://localhost/callback2"]
263
+ expect(new_application.redirect_uri).to eq("http://localhost/callback1\nhttp://localhost/callback2")
280
264
  end
281
265
  end
282
- context "when secret is wrong" do
283
- it "should not find the application" do
284
- app = FactoryBot.create :application, confidential: false
285
- authenticated = described_class.by_uid_and_secret(app.uid, "bad")
286
- expect(authenticated).to eq(nil)
266
+ context "when string of valid redirect_uris" do
267
+ it "should store as-is" do
268
+ new_application.redirect_uri = "http://localhost/callback1\nhttp://localhost/callback2"
269
+ expect(new_application.redirect_uri).to eq("http://localhost/callback1\nhttp://localhost/callback2")
287
270
  end
288
271
  end
289
272
  end
290
- end
291
273
 
292
- describe "#confidential?" do
293
- subject { FactoryBot.create(:application, confidential: confidential).confidential? }
274
+ describe :authorized_for do
275
+ let(:resource_owner) { double(:resource_owner, id: 10) }
294
276
 
295
- context "when application is private/confidential" do
296
- let(:confidential) { true }
297
- it { expect(subject).to eq(true) }
298
- end
299
-
300
- context "when application is public/non-confidential" do
301
- let(:confidential) { false }
302
- it { expect(subject).to eq(false) }
303
- end
304
- end
305
-
306
- describe "#as_json" do
307
- let(:app) { FactoryBot.create :application, secret: "123123123" }
277
+ it "is empty if the application is not authorized for anyone" do
278
+ expect(Application.authorized_for(resource_owner)).to be_empty
279
+ end
308
280
 
309
- # AR specific feature
310
- if DOORKEEPER_ORM == :active_record
311
- it "correctly works with #to_json" do
312
- ActiveRecord::Base.include_root_in_json = true
313
- expect(app.to_json(include_root_in_json: true)).to match(/application.+?:\{/)
314
- ActiveRecord::Base.include_root_in_json = false
281
+ it "returns only application for a specific resource owner" do
282
+ FactoryBot.create(:access_token, resource_owner_id: resource_owner.id + 1)
283
+ token = FactoryBot.create(:access_token, resource_owner_id: resource_owner.id)
284
+ expect(Application.authorized_for(resource_owner)).to eq([token.application])
315
285
  end
316
- end
317
286
 
318
- context "when called without authorized resource owner" do
319
- it "includes minimal set of attributes" do
320
- expect(app.as_json).to match(
321
- "id" => app.id,
322
- "name" => app.name,
323
- "created_at" => anything,
324
- )
287
+ it "excludes revoked tokens" do
288
+ FactoryBot.create(:access_token, resource_owner_id: resource_owner.id, revoked_at: 2.days.ago)
289
+ expect(Application.authorized_for(resource_owner)).to be_empty
325
290
  end
326
291
 
327
- it "includes application UID if it's public" do
328
- app = FactoryBot.create :application, secret: "123123123", confidential: false
292
+ it "returns all applications that have been authorized" do
293
+ token1 = FactoryBot.create(:access_token, resource_owner_id: resource_owner.id)
294
+ token2 = FactoryBot.create(:access_token, resource_owner_id: resource_owner.id)
295
+ expect(Application.authorized_for(resource_owner)).to eq([token1.application, token2.application])
296
+ end
329
297
 
330
- expect(app.as_json).to match(
331
- "id" => app.id,
332
- "name" => app.name,
333
- "created_at" => anything,
334
- "uid" => app.uid,
335
- )
298
+ it "returns only one application even if it has been authorized twice" do
299
+ application = FactoryBot.create(:application)
300
+ FactoryBot.create(:access_token, resource_owner_id: resource_owner.id, application: application)
301
+ FactoryBot.create(:access_token, resource_owner_id: resource_owner.id, application: application)
302
+ expect(Application.authorized_for(resource_owner)).to eq([application])
336
303
  end
304
+ end
337
305
 
338
- it "respects custom options" do
339
- expect(app.as_json(except: :id)).not_to include("id")
340
- expect(app.as_json(only: %i[name created_at secret]))
341
- .to match(
342
- "name" => app.name,
343
- "created_at" => anything,
344
- )
306
+ describe :revoke_tokens_and_grants_for do
307
+ it "revokes all access tokens and access grants" do
308
+ application_id = 42
309
+ resource_owner = double
310
+ expect(Doorkeeper::AccessToken)
311
+ .to receive(:revoke_all_for).with(application_id, resource_owner)
312
+ expect(Doorkeeper::AccessGrant)
313
+ .to receive(:revoke_all_for).with(application_id, resource_owner)
314
+
315
+ Application.revoke_tokens_and_grants_for(application_id, resource_owner)
345
316
  end
346
317
  end
347
318
 
348
- context "when called with authorized resource owner" do
349
- let(:owner) { FactoryBot.create(:doorkeeper_testing_user) }
350
- let(:other_owner) { FactoryBot.create(:doorkeeper_testing_user) }
351
- let(:app) { FactoryBot.create(:application, secret: "123123123", owner: owner) }
319
+ describe :by_uid_and_secret do
320
+ context "when application is private/confidential" do
321
+ it "finds the application via uid/secret" do
322
+ app = FactoryBot.create :application
323
+ authenticated = Application.by_uid_and_secret(app.uid, app.secret)
324
+ expect(authenticated).to eq(app)
325
+ end
326
+ context "when secret is wrong" do
327
+ it "should not find the application" do
328
+ app = FactoryBot.create :application
329
+ authenticated = Application.by_uid_and_secret(app.uid, "bad")
330
+ expect(authenticated).to eq(nil)
331
+ end
332
+ end
333
+ end
352
334
 
353
- before do
354
- Doorkeeper.configure do
355
- orm DOORKEEPER_ORM
356
- enable_application_owner confirmation: false
335
+ context "when application is public/non-confidential" do
336
+ context "when secret is blank" do
337
+ it "should find the application" do
338
+ app = FactoryBot.create :application, confidential: false
339
+ authenticated = Application.by_uid_and_secret(app.uid, nil)
340
+ expect(authenticated).to eq(app)
341
+ end
342
+ end
343
+ context "when secret is wrong" do
344
+ it "should not find the application" do
345
+ app = FactoryBot.create :application, confidential: false
346
+ authenticated = Application.by_uid_and_secret(app.uid, "bad")
347
+ expect(authenticated).to eq(nil)
348
+ end
357
349
  end
358
350
  end
351
+ end
352
+
353
+ describe :confidential? do
354
+ subject { FactoryBot.create(:application, confidential: confidential).confidential? }
359
355
 
360
- it "includes all the attributes" do
361
- expect(app.as_json(current_resource_owner: owner))
362
- .to include(
363
- "secret" => "123123123",
364
- "redirect_uri" => app.redirect_uri,
365
- "uid" => app.uid,
366
- )
356
+ context "when application is private/confidential" do
357
+ let(:confidential) { true }
358
+ it { expect(subject).to eq(true) }
367
359
  end
368
360
 
369
- it "doesn't include unsafe attributes if current owner isn't the same as owner" do
370
- expect(app.as_json(current_resource_owner: other_owner))
371
- .not_to include("redirect_uri")
361
+ context "when application is public/non-confidential" do
362
+ let(:confidential) { false }
363
+ it { expect(subject).to eq(false) }
372
364
  end
373
365
  end
374
366
  end