doorkeeper 4.2.6 → 5.5.4

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of doorkeeper might be problematic. Click here for more details.

Files changed (274) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1049 -0
  3. data/README.md +110 -353
  4. data/app/assets/stylesheets/doorkeeper/admin/application.css +2 -2
  5. data/app/controllers/doorkeeper/application_controller.rb +6 -7
  6. data/app/controllers/doorkeeper/application_metal_controller.rb +7 -11
  7. data/app/controllers/doorkeeper/applications_controller.rb +65 -16
  8. data/app/controllers/doorkeeper/authorizations_controller.rb +97 -17
  9. data/app/controllers/doorkeeper/authorized_applications_controller.rb +22 -3
  10. data/app/controllers/doorkeeper/token_info_controller.rb +16 -4
  11. data/app/controllers/doorkeeper/tokens_controller.rb +115 -38
  12. data/app/helpers/doorkeeper/dashboard_helper.rb +10 -6
  13. data/app/views/doorkeeper/applications/_delete_form.html.erb +3 -1
  14. data/app/views/doorkeeper/applications/_form.html.erb +33 -21
  15. data/app/views/doorkeeper/applications/edit.html.erb +1 -1
  16. data/app/views/doorkeeper/applications/index.html.erb +18 -6
  17. data/app/views/doorkeeper/applications/new.html.erb +1 -1
  18. data/app/views/doorkeeper/applications/show.html.erb +40 -16
  19. data/app/views/doorkeeper/authorizations/error.html.erb +1 -1
  20. data/app/views/doorkeeper/authorizations/form_post.html.erb +15 -0
  21. data/app/views/doorkeeper/authorizations/new.html.erb +6 -0
  22. data/app/views/doorkeeper/authorized_applications/index.html.erb +0 -1
  23. data/app/views/layouts/doorkeeper/admin.html.erb +16 -14
  24. data/config/locales/en.yml +34 -7
  25. data/lib/doorkeeper/config/abstract_builder.rb +28 -0
  26. data/lib/doorkeeper/config/option.rb +82 -0
  27. data/lib/doorkeeper/config/validations.rb +53 -0
  28. data/lib/doorkeeper/config.rb +514 -167
  29. data/lib/doorkeeper/engine.rb +11 -5
  30. data/lib/doorkeeper/errors.rb +25 -16
  31. data/lib/doorkeeper/grant_flow/fallback_flow.rb +15 -0
  32. data/lib/doorkeeper/grant_flow/flow.rb +44 -0
  33. data/lib/doorkeeper/grant_flow/registry.rb +50 -0
  34. data/lib/doorkeeper/grant_flow.rb +45 -0
  35. data/lib/doorkeeper/grape/authorization_decorator.rb +6 -4
  36. data/lib/doorkeeper/grape/helpers.rb +23 -12
  37. data/lib/doorkeeper/helpers/controller.rb +51 -14
  38. data/lib/doorkeeper/models/access_grant_mixin.rb +94 -27
  39. data/lib/doorkeeper/models/access_token_mixin.rb +284 -96
  40. data/lib/doorkeeper/models/application_mixin.rb +58 -27
  41. data/lib/doorkeeper/models/concerns/accessible.rb +2 -0
  42. data/lib/doorkeeper/models/concerns/expirable.rb +12 -6
  43. data/lib/doorkeeper/models/concerns/orderable.rb +15 -0
  44. data/lib/doorkeeper/models/concerns/ownership.rb +4 -7
  45. data/lib/doorkeeper/models/concerns/resource_ownerable.rb +47 -0
  46. data/lib/doorkeeper/models/concerns/reusable.rb +19 -0
  47. data/lib/doorkeeper/models/concerns/revocable.rb +3 -27
  48. data/lib/doorkeeper/models/concerns/scopes.rb +12 -2
  49. data/lib/doorkeeper/models/concerns/secret_storable.rb +106 -0
  50. data/lib/doorkeeper/oauth/authorization/code.rb +48 -12
  51. data/lib/doorkeeper/oauth/authorization/context.rb +17 -0
  52. data/lib/doorkeeper/oauth/authorization/token.rb +66 -28
  53. data/lib/doorkeeper/oauth/authorization/uri_builder.rb +7 -5
  54. data/lib/doorkeeper/oauth/authorization_code_request.rb +63 -10
  55. data/lib/doorkeeper/oauth/base_request.rb +35 -19
  56. data/lib/doorkeeper/oauth/base_response.rb +2 -0
  57. data/lib/doorkeeper/oauth/client/credentials.rb +9 -7
  58. data/lib/doorkeeper/oauth/client.rb +10 -11
  59. data/lib/doorkeeper/oauth/client_credentials/creator.rb +47 -4
  60. data/lib/doorkeeper/oauth/client_credentials/issuer.rb +16 -9
  61. data/lib/doorkeeper/oauth/client_credentials/validator.rb +56 -0
  62. data/lib/doorkeeper/oauth/client_credentials_request.rb +10 -11
  63. data/lib/doorkeeper/oauth/code_request.rb +8 -12
  64. data/lib/doorkeeper/oauth/code_response.rb +27 -15
  65. data/lib/doorkeeper/oauth/error.rb +5 -3
  66. data/lib/doorkeeper/oauth/error_response.rb +35 -15
  67. data/lib/doorkeeper/oauth/forbidden_token_response.rb +11 -3
  68. data/lib/doorkeeper/oauth/helpers/scope_checker.rb +23 -18
  69. data/lib/doorkeeper/oauth/helpers/unique_token.rb +20 -3
  70. data/lib/doorkeeper/oauth/helpers/uri_checker.rb +53 -3
  71. data/lib/doorkeeper/oauth/hooks/context.rb +21 -0
  72. data/lib/doorkeeper/oauth/invalid_request_response.rb +43 -0
  73. data/lib/doorkeeper/oauth/invalid_token_response.rb +29 -5
  74. data/lib/doorkeeper/oauth/nonstandard.rb +39 -0
  75. data/lib/doorkeeper/oauth/password_access_token_request.rb +44 -10
  76. data/lib/doorkeeper/oauth/pre_authorization.rb +135 -26
  77. data/lib/doorkeeper/oauth/refresh_token_request.rb +60 -31
  78. data/lib/doorkeeper/oauth/scopes.rb +26 -12
  79. data/lib/doorkeeper/oauth/token.rb +13 -9
  80. data/lib/doorkeeper/oauth/token_introspection.rb +202 -0
  81. data/lib/doorkeeper/oauth/token_request.rb +8 -20
  82. data/lib/doorkeeper/oauth/token_response.rb +14 -10
  83. data/lib/doorkeeper/oauth.rb +13 -0
  84. data/lib/doorkeeper/orm/active_record/access_grant.rb +6 -4
  85. data/lib/doorkeeper/orm/active_record/access_token.rb +5 -42
  86. data/lib/doorkeeper/orm/active_record/application.rb +6 -20
  87. data/lib/doorkeeper/orm/active_record/mixins/access_grant.rb +69 -0
  88. data/lib/doorkeeper/orm/active_record/mixins/access_token.rb +60 -0
  89. data/lib/doorkeeper/orm/active_record/mixins/application.rb +199 -0
  90. data/lib/doorkeeper/orm/active_record/redirect_uri_validator.rb +66 -0
  91. data/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +33 -0
  92. data/lib/doorkeeper/orm/active_record.rb +37 -8
  93. data/lib/doorkeeper/rails/helpers.rb +14 -13
  94. data/lib/doorkeeper/rails/routes/abstract_router.rb +35 -0
  95. data/lib/doorkeeper/rails/routes/mapper.rb +4 -2
  96. data/lib/doorkeeper/rails/routes/mapping.rb +9 -7
  97. data/lib/doorkeeper/rails/routes/registry.rb +45 -0
  98. data/lib/doorkeeper/rails/routes.rb +41 -28
  99. data/lib/doorkeeper/rake/db.rake +40 -0
  100. data/lib/doorkeeper/rake/setup.rake +11 -0
  101. data/lib/doorkeeper/rake.rb +14 -0
  102. data/lib/doorkeeper/request/authorization_code.rb +6 -4
  103. data/lib/doorkeeper/request/client_credentials.rb +3 -3
  104. data/lib/doorkeeper/request/code.rb +1 -1
  105. data/lib/doorkeeper/request/password.rb +5 -14
  106. data/lib/doorkeeper/request/refresh_token.rb +6 -5
  107. data/lib/doorkeeper/request/strategy.rb +4 -2
  108. data/lib/doorkeeper/request/token.rb +1 -1
  109. data/lib/doorkeeper/request.rb +62 -29
  110. data/lib/doorkeeper/secret_storing/base.rb +64 -0
  111. data/lib/doorkeeper/secret_storing/bcrypt.rb +60 -0
  112. data/lib/doorkeeper/secret_storing/plain.rb +33 -0
  113. data/lib/doorkeeper/secret_storing/sha256_hash.rb +26 -0
  114. data/lib/doorkeeper/server.rb +9 -11
  115. data/lib/doorkeeper/stale_records_cleaner.rb +24 -0
  116. data/lib/doorkeeper/validations.rb +5 -2
  117. data/lib/doorkeeper/version.rb +12 -1
  118. data/lib/doorkeeper.rb +111 -62
  119. data/lib/generators/doorkeeper/application_owner_generator.rb +28 -13
  120. data/lib/generators/doorkeeper/confidential_applications_generator.rb +33 -0
  121. data/lib/generators/doorkeeper/enable_polymorphic_resource_owner_generator.rb +39 -0
  122. data/lib/generators/doorkeeper/install_generator.rb +19 -9
  123. data/lib/generators/doorkeeper/migration_generator.rb +27 -10
  124. data/lib/generators/doorkeeper/pkce_generator.rb +33 -0
  125. data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +31 -19
  126. data/lib/generators/doorkeeper/templates/add_confidential_to_applications.rb.erb +13 -0
  127. data/lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb.erb +9 -0
  128. data/{spec/dummy/db/migrate/20160320211015_add_previous_refresh_token_to_access_tokens.rb → lib/generators/doorkeeper/templates/add_previous_refresh_token_to_access_tokens.rb.erb} +3 -1
  129. data/lib/generators/doorkeeper/templates/enable_pkce_migration.rb.erb +8 -0
  130. data/lib/generators/doorkeeper/templates/enable_polymorphic_resource_owner_migration.rb.erb +17 -0
  131. data/lib/generators/doorkeeper/templates/initializer.rb +412 -33
  132. data/lib/generators/doorkeeper/templates/migration.rb.erb +88 -0
  133. data/lib/generators/doorkeeper/views_generator.rb +8 -4
  134. data/vendor/assets/stylesheets/doorkeeper/bootstrap.min.css +4 -5
  135. metadata +114 -276
  136. data/.coveralls.yml +0 -1
  137. data/.gitignore +0 -19
  138. data/.hound.yml +0 -13
  139. data/.rspec +0 -1
  140. data/.travis.yml +0 -26
  141. data/Appraisals +0 -14
  142. data/CONTRIBUTING.md +0 -47
  143. data/Gemfile +0 -10
  144. data/NEWS.md +0 -606
  145. data/RELEASING.md +0 -10
  146. data/Rakefile +0 -20
  147. data/app/validators/redirect_uri_validator.rb +0 -34
  148. data/doorkeeper.gemspec +0 -29
  149. data/gemfiles/rails_4_2.gemfile +0 -11
  150. data/gemfiles/rails_5_0.gemfile +0 -12
  151. data/gemfiles/rails_5_1.gemfile +0 -13
  152. data/lib/doorkeeper/oauth/client_credentials/validation.rb +0 -45
  153. data/lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb +0 -7
  154. data/lib/generators/doorkeeper/templates/add_previous_refresh_token_to_access_tokens.rb +0 -11
  155. data/lib/generators/doorkeeper/templates/migration.rb +0 -68
  156. data/spec/controllers/application_metal_controller.rb +0 -10
  157. data/spec/controllers/applications_controller_spec.rb +0 -58
  158. data/spec/controllers/authorizations_controller_spec.rb +0 -218
  159. data/spec/controllers/protected_resources_controller_spec.rb +0 -300
  160. data/spec/controllers/token_info_controller_spec.rb +0 -52
  161. data/spec/controllers/tokens_controller_spec.rb +0 -88
  162. data/spec/dummy/Rakefile +0 -7
  163. data/spec/dummy/app/controllers/application_controller.rb +0 -3
  164. data/spec/dummy/app/controllers/custom_authorizations_controller.rb +0 -7
  165. data/spec/dummy/app/controllers/full_protected_resources_controller.rb +0 -12
  166. data/spec/dummy/app/controllers/home_controller.rb +0 -17
  167. data/spec/dummy/app/controllers/metal_controller.rb +0 -11
  168. data/spec/dummy/app/controllers/semi_protected_resources_controller.rb +0 -11
  169. data/spec/dummy/app/helpers/application_helper.rb +0 -5
  170. data/spec/dummy/app/models/user.rb +0 -5
  171. data/spec/dummy/app/views/home/index.html.erb +0 -0
  172. data/spec/dummy/app/views/layouts/application.html.erb +0 -14
  173. data/spec/dummy/config/application.rb +0 -23
  174. data/spec/dummy/config/boot.rb +0 -9
  175. data/spec/dummy/config/database.yml +0 -15
  176. data/spec/dummy/config/environment.rb +0 -5
  177. data/spec/dummy/config/environments/development.rb +0 -29
  178. data/spec/dummy/config/environments/production.rb +0 -62
  179. data/spec/dummy/config/environments/test.rb +0 -44
  180. data/spec/dummy/config/initializers/active_record_belongs_to_required_by_default.rb +0 -6
  181. data/spec/dummy/config/initializers/backtrace_silencers.rb +0 -7
  182. data/spec/dummy/config/initializers/doorkeeper.rb +0 -96
  183. data/spec/dummy/config/initializers/secret_token.rb +0 -9
  184. data/spec/dummy/config/initializers/session_store.rb +0 -8
  185. data/spec/dummy/config/initializers/wrap_parameters.rb +0 -14
  186. data/spec/dummy/config/locales/doorkeeper.en.yml +0 -5
  187. data/spec/dummy/config/routes.rb +0 -52
  188. data/spec/dummy/config.ru +0 -4
  189. data/spec/dummy/db/migrate/20111122132257_create_users.rb +0 -9
  190. data/spec/dummy/db/migrate/20120312140401_add_password_to_users.rb +0 -5
  191. data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +0 -60
  192. data/spec/dummy/db/migrate/20151223200000_add_owner_to_application.rb +0 -7
  193. data/spec/dummy/db/schema.rb +0 -67
  194. data/spec/dummy/public/404.html +0 -26
  195. data/spec/dummy/public/422.html +0 -26
  196. data/spec/dummy/public/500.html +0 -26
  197. data/spec/dummy/public/favicon.ico +0 -0
  198. data/spec/dummy/script/rails +0 -6
  199. data/spec/factories.rb +0 -28
  200. data/spec/generators/application_owner_generator_spec.rb +0 -22
  201. data/spec/generators/install_generator_spec.rb +0 -31
  202. data/spec/generators/migration_generator_spec.rb +0 -20
  203. data/spec/generators/templates/routes.rb +0 -3
  204. data/spec/generators/views_generator_spec.rb +0 -27
  205. data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +0 -24
  206. data/spec/lib/config_spec.rb +0 -334
  207. data/spec/lib/doorkeeper_spec.rb +0 -150
  208. data/spec/lib/models/expirable_spec.rb +0 -50
  209. data/spec/lib/models/revocable_spec.rb +0 -59
  210. data/spec/lib/models/scopes_spec.rb +0 -43
  211. data/spec/lib/oauth/authorization/uri_builder_spec.rb +0 -41
  212. data/spec/lib/oauth/authorization_code_request_spec.rb +0 -80
  213. data/spec/lib/oauth/base_request_spec.rb +0 -160
  214. data/spec/lib/oauth/base_response_spec.rb +0 -45
  215. data/spec/lib/oauth/client/credentials_spec.rb +0 -88
  216. data/spec/lib/oauth/client_credentials/creator_spec.rb +0 -44
  217. data/spec/lib/oauth/client_credentials/issuer_spec.rb +0 -86
  218. data/spec/lib/oauth/client_credentials/validation_spec.rb +0 -54
  219. data/spec/lib/oauth/client_credentials_integration_spec.rb +0 -27
  220. data/spec/lib/oauth/client_credentials_request_spec.rb +0 -104
  221. data/spec/lib/oauth/client_spec.rb +0 -39
  222. data/spec/lib/oauth/code_request_spec.rb +0 -45
  223. data/spec/lib/oauth/code_response_spec.rb +0 -34
  224. data/spec/lib/oauth/error_response_spec.rb +0 -61
  225. data/spec/lib/oauth/error_spec.rb +0 -23
  226. data/spec/lib/oauth/forbidden_token_response_spec.rb +0 -23
  227. data/spec/lib/oauth/helpers/scope_checker_spec.rb +0 -64
  228. data/spec/lib/oauth/helpers/unique_token_spec.rb +0 -20
  229. data/spec/lib/oauth/helpers/uri_checker_spec.rb +0 -104
  230. data/spec/lib/oauth/invalid_token_response_spec.rb +0 -56
  231. data/spec/lib/oauth/password_access_token_request_spec.rb +0 -90
  232. data/spec/lib/oauth/pre_authorization_spec.rb +0 -155
  233. data/spec/lib/oauth/refresh_token_request_spec.rb +0 -154
  234. data/spec/lib/oauth/scopes_spec.rb +0 -122
  235. data/spec/lib/oauth/token_request_spec.rb +0 -98
  236. data/spec/lib/oauth/token_response_spec.rb +0 -85
  237. data/spec/lib/oauth/token_spec.rb +0 -116
  238. data/spec/lib/request/strategy_spec.rb +0 -53
  239. data/spec/lib/server_spec.rb +0 -49
  240. data/spec/models/doorkeeper/access_grant_spec.rb +0 -36
  241. data/spec/models/doorkeeper/access_token_spec.rb +0 -394
  242. data/spec/models/doorkeeper/application_spec.rb +0 -179
  243. data/spec/requests/applications/applications_request_spec.rb +0 -94
  244. data/spec/requests/applications/authorized_applications_spec.rb +0 -30
  245. data/spec/requests/endpoints/authorization_spec.rb +0 -71
  246. data/spec/requests/endpoints/token_spec.rb +0 -64
  247. data/spec/requests/flows/authorization_code_errors_spec.rb +0 -76
  248. data/spec/requests/flows/authorization_code_spec.rb +0 -148
  249. data/spec/requests/flows/client_credentials_spec.rb +0 -58
  250. data/spec/requests/flows/implicit_grant_errors_spec.rb +0 -32
  251. data/spec/requests/flows/implicit_grant_spec.rb +0 -61
  252. data/spec/requests/flows/password_spec.rb +0 -115
  253. data/spec/requests/flows/refresh_token_spec.rb +0 -174
  254. data/spec/requests/flows/revoke_token_spec.rb +0 -157
  255. data/spec/requests/flows/skip_authorization_spec.rb +0 -59
  256. data/spec/requests/protected_resources/metal_spec.rb +0 -14
  257. data/spec/requests/protected_resources/private_api_spec.rb +0 -81
  258. data/spec/routing/custom_controller_routes_spec.rb +0 -71
  259. data/spec/routing/default_routes_spec.rb +0 -35
  260. data/spec/routing/scoped_routes_spec.rb +0 -31
  261. data/spec/spec_helper.rb +0 -4
  262. data/spec/spec_helper_integration.rb +0 -63
  263. data/spec/support/dependencies/factory_girl.rb +0 -2
  264. data/spec/support/helpers/access_token_request_helper.rb +0 -11
  265. data/spec/support/helpers/authorization_request_helper.rb +0 -41
  266. data/spec/support/helpers/config_helper.rb +0 -9
  267. data/spec/support/helpers/model_helper.rb +0 -67
  268. data/spec/support/helpers/request_spec_helper.rb +0 -84
  269. data/spec/support/helpers/url_helper.rb +0 -55
  270. data/spec/support/http_method_shim.rb +0 -38
  271. data/spec/support/orm/active_record.rb +0 -3
  272. data/spec/support/shared/controllers_shared_context.rb +0 -69
  273. data/spec/support/shared/models_shared_examples.rb +0 -52
  274. data/spec/validators/redirect_uri_validator_spec.rb +0 -78
@@ -1,50 +1,31 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module AccessTokenMixin
3
5
  extend ActiveSupport::Concern
4
6
 
5
7
  include OAuth::Helpers
6
8
  include Models::Expirable
9
+ include Models::Reusable
7
10
  include Models::Revocable
8
11
  include Models::Accessible
12
+ include Models::Orderable
13
+ include Models::SecretStorable
9
14
  include Models::Scopes
10
- include ActiveModel::MassAssignmentSecurity if defined?(::ProtectedAttributes)
11
-
12
- included do
13
- belongs_to_options = {
14
- class_name: 'Doorkeeper::Application',
15
- inverse_of: :access_tokens
16
- }
17
- if defined?(ActiveRecord::Base) && ActiveRecord::VERSION::MAJOR >= 5
18
- belongs_to_options[:optional] = true
19
- end
20
-
21
- belongs_to :application, belongs_to_options
22
-
23
- validates :token, presence: true, uniqueness: true
24
- validates :refresh_token, uniqueness: true, if: :use_refresh_token?
25
-
26
- # @attr_writer [Boolean, nil] use_refresh_token
27
- # indicates the possibility of using refresh token
28
- attr_writer :use_refresh_token
29
-
30
- before_validation :generate_token, on: :create
31
- before_validation :generate_refresh_token,
32
- on: :create,
33
- if: :use_refresh_token?
34
- end
15
+ include Models::ResourceOwnerable
35
16
 
36
17
  module ClassMethods
37
18
  # Returns an instance of the Doorkeeper::AccessToken with
38
- # specific token value.
19
+ # specific plain text token value.
39
20
  #
40
21
  # @param token [#to_s]
41
- # token value (any object that responds to `#to_s`)
22
+ # Plain text token value (any object that responds to `#to_s`)
42
23
  #
43
24
  # @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
44
25
  # if there is no record with such token
45
26
  #
46
27
  def by_token(token)
47
- find_by(token: token.to_s)
28
+ find_by_plaintext_token(:token, token)
48
29
  end
49
30
 
50
31
  # Returns an instance of the Doorkeeper::AccessToken
@@ -57,7 +38,22 @@ module Doorkeeper
57
38
  # if there is no record with such refresh token
58
39
  #
59
40
  def by_refresh_token(refresh_token)
60
- find_by(refresh_token: refresh_token.to_s)
41
+ find_by_plaintext_token(:refresh_token, refresh_token)
42
+ end
43
+
44
+ # Returns an instance of the Doorkeeper::AccessToken
45
+ # found by previous refresh token. Keep in mind that value
46
+ # of the previous_refresh_token isn't encrypted using
47
+ # secrets strategy.
48
+ #
49
+ # @param previous_refresh_token [#to_s]
50
+ # previous refresh token value (any object that responds to `#to_s`)
51
+ #
52
+ # @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
53
+ # if there is no record with such refresh token
54
+ #
55
+ def by_previous_refresh_token(previous_refresh_token)
56
+ find_by(refresh_token: previous_refresh_token)
61
57
  end
62
58
 
63
59
  # Revokes AccessToken records that have not been revoked and associated
@@ -65,22 +61,24 @@ module Doorkeeper
65
61
  #
66
62
  # @param application_id [Integer]
67
63
  # ID of the Application
68
- # @param resource_owner [ActiveRecord::Base]
69
- # instance of the Resource Owner model
70
- #
71
- def revoke_all_for(application_id, resource_owner)
72
- where(application_id: application_id,
73
- resource_owner_id: resource_owner.id,
74
- revoked_at: nil).
75
- each(&:revoke)
64
+ # @param resource_owner [ActiveRecord::Base, Integer]
65
+ # instance of the Resource Owner model or it's ID
66
+ #
67
+ def revoke_all_for(application_id, resource_owner, clock = Time)
68
+ by_resource_owner(resource_owner)
69
+ .where(
70
+ application_id: application_id,
71
+ revoked_at: nil,
72
+ )
73
+ .update_all(revoked_at: clock.now.utc)
76
74
  end
77
75
 
78
- # Looking for not expired Access Token with a matching set of scopes
76
+ # Looking for not revoked Access Token with a matching set of scopes
79
77
  # that belongs to specific Application and Resource Owner.
80
78
  #
81
79
  # @param application [Doorkeeper::Application]
82
80
  # Application instance
83
- # @param resource_owner_or_id [ActiveRecord::Base, Integer]
81
+ # @param resource_owner [ActiveRecord::Base, Integer]
84
82
  # Resource Owner model instance or it's ID
85
83
  # @param scopes [String, Doorkeeper::OAuth::Scopes]
86
84
  # set of scopes
@@ -88,37 +86,77 @@ module Doorkeeper
88
86
  # @return [Doorkeeper::AccessToken, nil] Access Token instance or
89
87
  # nil if matching record was not found
90
88
  #
91
- def matching_token_for(application, resource_owner_or_id, scopes)
92
- resource_owner_id = if resource_owner_or_id.respond_to?(:to_key)
93
- resource_owner_or_id.id
94
- else
95
- resource_owner_or_id
96
- end
97
- token = last_authorized_token_for(application.try(:id), resource_owner_id)
98
- if token && scopes_match?(token.scopes, scopes, application.try(:scopes))
99
- token
89
+ def matching_token_for(application, resource_owner, scopes)
90
+ tokens = authorized_tokens_for(application&.id, resource_owner)
91
+ find_matching_token(tokens, application, scopes)
92
+ end
93
+
94
+ # Interface to enumerate access token records in batches in order not
95
+ # to bloat the memory. Could be overloaded in any ORM extension.
96
+ #
97
+ def find_access_token_in_batches(relation, **args, &block)
98
+ relation.find_in_batches(**args, &block)
99
+ end
100
+
101
+ # Enumerates AccessToken records in batches to find a matching token.
102
+ # Batching is required in order not to pollute the memory if Application
103
+ # has huge amount of associated records.
104
+ #
105
+ # ActiveRecord 5.x - 6.x ignores custom ordering so we can't perform a
106
+ # database sort by created_at, so we need to load all the matching records,
107
+ # sort them and find latest one. Probably it would be better to rewrite this
108
+ # query using Time math if possible, but we n eed to consider ORM and
109
+ # different databases support.
110
+ #
111
+ # @param relation [ActiveRecord::Relation]
112
+ # Access tokens relation
113
+ # @param application [Doorkeeper::Application]
114
+ # Application instance
115
+ # @param scopes [String, Doorkeeper::OAuth::Scopes]
116
+ # set of scopes
117
+ #
118
+ # @return [Doorkeeper::AccessToken, nil] Access Token instance or
119
+ # nil if matching record was not found
120
+ #
121
+ def find_matching_token(relation, application, scopes)
122
+ return nil unless relation
123
+
124
+ matching_tokens = []
125
+ batch_size = Doorkeeper.configuration.token_lookup_batch_size
126
+
127
+ find_access_token_in_batches(relation, batch_size: batch_size) do |batch|
128
+ tokens = batch.select do |token|
129
+ scopes_match?(token.scopes, scopes, application&.scopes)
130
+ end
131
+
132
+ matching_tokens.concat(tokens)
100
133
  end
134
+
135
+ matching_tokens.max_by(&:created_at)
101
136
  end
102
137
 
103
- # Checks whether the token scopes match the scopes from the parameters or
104
- # Application scopes (if present).
138
+ # Checks whether the token scopes match the scopes from the parameters
105
139
  #
106
140
  # @param token_scopes [#to_s]
107
141
  # set of scopes (any object that responds to `#to_s`)
108
- # @param param_scopes [String]
142
+ # @param param_scopes [Doorkeeper::OAuth::Scopes]
109
143
  # scopes from params
110
- # @param app_scopes [String]
144
+ # @param app_scopes [Doorkeeper::OAuth::Scopes]
111
145
  # Application scopes
112
146
  #
113
- # @return [Boolean] true if all scopes and blank or matches
147
+ # @return [Boolean] true if the param scopes match the token scopes,
148
+ # and all the param scopes are defined in the application (or in the
149
+ # server configuration if the application doesn't define any scopes),
114
150
  # and false in other cases
115
151
  #
116
152
  def scopes_match?(token_scopes, param_scopes, app_scopes)
117
- (!token_scopes.present? && !param_scopes.present?) ||
118
- Doorkeeper::OAuth::Helpers::ScopeChecker.match?(
119
- token_scopes.to_s,
120
- param_scopes,
121
- app_scopes
153
+ return true if token_scopes.empty? && param_scopes.empty?
154
+
155
+ (token_scopes.sort == param_scopes.sort) &&
156
+ Doorkeeper::OAuth::Helpers::ScopeChecker.valid?(
157
+ scope_str: param_scopes.to_s,
158
+ server_scopes: Doorkeeper.config.scopes,
159
+ app_scopes: app_scopes,
122
160
  )
123
161
  end
124
162
 
@@ -128,59 +166,124 @@ module Doorkeeper
128
166
  #
129
167
  # @param application [Doorkeeper::Application]
130
168
  # Application instance
131
- # @param resource_owner_id [ActiveRecord::Base, Integer]
169
+ # @param resource_owner [ActiveRecord::Base, Integer]
132
170
  # Resource Owner model instance or it's ID
133
171
  # @param scopes [#to_s]
134
172
  # set of scopes (any object that responds to `#to_s`)
135
- # @param expires_in [Integer]
173
+ # @param token_attributes [Hash]
174
+ # Additional attributes to use when creating a token
175
+ # @option token_attributes [Integer] :expires_in
136
176
  # token lifetime in seconds
137
- # @param use_refresh_token [Boolean]
177
+ # @option token_attributes [Boolean] :use_refresh_token
138
178
  # whether to use the refresh token
139
179
  #
140
180
  # @return [Doorkeeper::AccessToken] existing record or a new one
141
181
  #
142
- def find_or_create_for(application, resource_owner_id, scopes, expires_in, use_refresh_token)
143
- if Doorkeeper.configuration.reuse_access_token
144
- access_token = matching_token_for(application, resource_owner_id, scopes)
145
- if access_token && !access_token.expired?
146
- return access_token
147
- end
182
+ def find_or_create_for(application:, resource_owner:, scopes:, **token_attributes)
183
+ if Doorkeeper.config.reuse_access_token
184
+ access_token = matching_token_for(application, resource_owner, scopes)
185
+
186
+ return access_token if access_token&.reusable?
148
187
  end
149
188
 
150
- create!(
151
- application_id: application.try(:id),
152
- resource_owner_id: resource_owner_id,
153
- scopes: scopes.to_s,
154
- expires_in: expires_in,
155
- use_refresh_token: use_refresh_token
189
+ create_for(
190
+ application: application,
191
+ resource_owner: resource_owner,
192
+ scopes: scopes,
193
+ **token_attributes,
156
194
  )
157
195
  end
158
196
 
159
- # Looking for not revoked Access Token record that belongs to specific
197
+ # Creates a not expired AccessToken record with a matching set of
198
+ # scopes that belongs to specific Application and Resource Owner.
199
+ #
200
+ # @param application [Doorkeeper::Application]
201
+ # Application instance
202
+ # @param resource_owner [ActiveRecord::Base, Integer]
203
+ # Resource Owner model instance or it's ID
204
+ # @param scopes [#to_s]
205
+ # set of scopes (any object that responds to `#to_s`)
206
+ # @param token_attributes [Hash]
207
+ # Additional attributes to use when creating a token
208
+ # @option token_attributes [Integer] :expires_in
209
+ # token lifetime in seconds
210
+ # @option token_attributes [Boolean] :use_refresh_token
211
+ # whether to use the refresh token
212
+ #
213
+ # @return [Doorkeeper::AccessToken] new access token
214
+ #
215
+ def create_for(application:, resource_owner:, scopes:, **token_attributes)
216
+ token_attributes[:application_id] = application&.id
217
+ token_attributes[:scopes] = scopes.to_s
218
+
219
+ if Doorkeeper.config.polymorphic_resource_owner?
220
+ token_attributes[:resource_owner] = resource_owner
221
+ else
222
+ token_attributes[:resource_owner_id] = resource_owner_id_for(resource_owner)
223
+ end
224
+
225
+ create!(token_attributes)
226
+ end
227
+
228
+ # Looking for not revoked Access Token records that belongs to specific
160
229
  # Application and Resource Owner.
161
230
  #
162
231
  # @param application_id [Integer]
163
232
  # ID of the Application model instance
164
- # @param resource_owner_id [Integer]
233
+ # @param resource_owner [ActiveRecord::Base, Integer]
234
+ # Resource Owner model instance or it's ID
235
+ #
236
+ # @return [ActiveRecord::Relation]
237
+ # collection of matching AccessToken objects
238
+ #
239
+ def authorized_tokens_for(application_id, resource_owner)
240
+ by_resource_owner(resource_owner).where(
241
+ application_id: application_id,
242
+ revoked_at: nil,
243
+ )
244
+ end
245
+
246
+ # Convenience method for backwards-compatibility, return the last
247
+ # matching token for the given Application and Resource Owner.
248
+ #
249
+ # @param application_id [Integer]
250
+ # ID of the Application model instance
251
+ # @param resource_owner [ActiveRecord::Base, Integer]
165
252
  # ID of the Resource Owner model instance
166
253
  #
167
254
  # @return [Doorkeeper::AccessToken, nil] matching AccessToken object or
168
255
  # nil if nothing was found
169
256
  #
170
- def last_authorized_token_for(application_id, resource_owner_id)
171
- send(order_method, created_at_desc).
172
- find_by(application_id: application_id,
173
- resource_owner_id: resource_owner_id,
174
- revoked_at: nil)
257
+ def last_authorized_token_for(application_id, resource_owner)
258
+ authorized_tokens_for(application_id, resource_owner)
259
+ .ordered_by(:created_at, :desc)
260
+ .first
261
+ end
262
+
263
+ ##
264
+ # Determines the secret storing transformer
265
+ # Unless configured otherwise, uses the plain secret strategy
266
+ #
267
+ # @return [Doorkeeper::SecretStoring::Base]
268
+ #
269
+ def secret_strategy
270
+ ::Doorkeeper.config.token_secret_strategy
271
+ end
272
+
273
+ ##
274
+ # Determine the fallback storing strategy
275
+ # Unless configured, there will be no fallback
276
+ def fallback_secret_strategy
277
+ ::Doorkeeper.config.token_secret_fallback_strategy
175
278
  end
176
279
  end
177
280
 
178
281
  # Access Token type: Bearer.
179
- # @see https://tools.ietf.org/html/rfc6750
282
+ # @see https://datatracker.ietf.org/doc/html/rfc6750
180
283
  # The OAuth 2.0 Authorization Framework: Bearer Token Usage
181
284
  #
182
285
  def token_type
183
- 'bearer'
286
+ "Bearer"
184
287
  end
185
288
 
186
289
  def use_refresh_token?
@@ -193,12 +296,16 @@ module Doorkeeper
193
296
  # @return [Hash] hash with token data
194
297
  def as_json(_options = {})
195
298
  {
196
- resource_owner_id: resource_owner_id,
197
- scopes: scopes,
198
- expires_in_seconds: expires_in_seconds,
199
- application: { uid: application.try(:uid) },
200
- created_at: created_at.to_i
201
- }
299
+ resource_owner_id: resource_owner_id,
300
+ scope: scopes,
301
+ expires_in: expires_in_seconds,
302
+ application: { uid: application.try(:uid) },
303
+ created_at: created_at.to_i,
304
+ }.tap do |json|
305
+ if Doorkeeper.configuration.polymorphic_resource_owner?
306
+ json[:resource_owner_type] = resource_owner_type
307
+ end
308
+ end
202
309
  end
203
310
 
204
311
  # Indicates whether the token instance have the same credential
@@ -210,7 +317,22 @@ module Doorkeeper
210
317
  #
211
318
  def same_credential?(access_token)
212
319
  application_id == access_token.application_id &&
320
+ same_resource_owner?(access_token)
321
+ end
322
+
323
+ # Indicates whether the token instance have the same credential
324
+ # as the other Access Token.
325
+ #
326
+ # @param access_token [Doorkeeper::AccessToken] other token
327
+ #
328
+ # @return [Boolean] true if credentials are same of false in other cases
329
+ #
330
+ def same_resource_owner?(access_token)
331
+ if Doorkeeper.configuration.polymorphic_resource_owner?
332
+ resource_owner == access_token.resource_owner
333
+ else
213
334
  resource_owner_id == access_token.resource_owner_id
335
+ end
214
336
  end
215
337
 
216
338
  # Indicates if token is acceptable for specific scopes.
@@ -224,18 +346,63 @@ module Doorkeeper
224
346
  accessible? && includes_scope?(*scopes)
225
347
  end
226
348
 
349
+ # We keep a volatile copy of the raw refresh token for initial communication
350
+ # The stored refresh_token may be mapped and not available in cleartext.
351
+ def plaintext_refresh_token
352
+ if secret_strategy.allows_restoring_secrets?
353
+ secret_strategy.restore_secret(self, :refresh_token)
354
+ else
355
+ @raw_refresh_token
356
+ end
357
+ end
358
+
359
+ # We keep a volatile copy of the raw token for initial communication
360
+ # The stored refresh_token may be mapped and not available in cleartext.
361
+ #
362
+ # Some strategies allow restoring stored secrets (e.g. symmetric encryption)
363
+ # while hashing strategies do not, so you cannot rely on this value
364
+ # returning a present value for persisted tokens.
365
+ def plaintext_token
366
+ if secret_strategy.allows_restoring_secrets?
367
+ secret_strategy.restore_secret(self, :token)
368
+ else
369
+ @raw_token
370
+ end
371
+ end
372
+
373
+ # Revokes token with `:refresh_token` equal to `:previous_refresh_token`
374
+ # and clears `:previous_refresh_token` attribute.
375
+ #
376
+ def revoke_previous_refresh_token!
377
+ return if !self.class.refresh_token_revoked_on_use? || previous_refresh_token.blank?
378
+
379
+ old_refresh_token&.revoke
380
+ update_attribute(:previous_refresh_token, "")
381
+ end
382
+
227
383
  private
228
384
 
385
+ # Searches for Access Token record with `:refresh_token` equal to
386
+ # `:previous_refresh_token` value.
387
+ #
388
+ # @return [Doorkeeper::AccessToken, nil]
389
+ # Access Token record or nil if nothing found
390
+ #
391
+ def old_refresh_token
392
+ @old_refresh_token ||= self.class.by_previous_refresh_token(previous_refresh_token)
393
+ end
394
+
229
395
  # Generates refresh token with UniqueToken generator.
230
396
  #
231
397
  # @return [String] refresh token value
232
398
  #
233
399
  def generate_refresh_token
234
- write_attribute :refresh_token, UniqueToken.generate
400
+ @raw_refresh_token = UniqueToken.generate
401
+ secret_strategy.store_secret(self, :refresh_token, @raw_refresh_token)
235
402
  end
236
403
 
237
404
  # Generates and sets the token value with the
238
- # configured Generator class (see Doorkeeper.configuration).
405
+ # configured Generator class (see Doorkeeper.config).
239
406
  #
240
407
  # @return [String] generated token value
241
408
  #
@@ -247,18 +414,39 @@ module Doorkeeper
247
414
  def generate_token
248
415
  self.created_at ||= Time.now.utc
249
416
 
250
- generator = Doorkeeper.configuration.access_token_generator.constantize
251
- self.token = generator.generate(
417
+ @raw_token = token_generator.generate(attributes_for_token_generator)
418
+ secret_strategy.store_secret(self, :token, @raw_token)
419
+ @raw_token
420
+ end
421
+
422
+ # Set of attributes that would be passed to token generator to
423
+ # generate unique token based on them.
424
+ #
425
+ # @return [Hash] set of attributes
426
+ #
427
+ def attributes_for_token_generator
428
+ {
252
429
  resource_owner_id: resource_owner_id,
253
430
  scopes: scopes,
254
431
  application: application,
255
432
  expires_in: expires_in,
256
- created_at: created_at
257
- )
258
- rescue NoMethodError
433
+ created_at: created_at,
434
+ }.tap do |attributes|
435
+ if Doorkeeper.config.polymorphic_resource_owner?
436
+ attributes[:resource_owner] = resource_owner
437
+ end
438
+ end
439
+ end
440
+
441
+ def token_generator
442
+ generator_name = Doorkeeper.config.access_token_generator
443
+ generator = generator_name.constantize
444
+
445
+ return generator if generator.respond_to?(:generate)
446
+
259
447
  raise Errors::UnableToGenerateToken, "#{generator} does not respond to `.generate`."
260
448
  rescue NameError
261
- raise Errors::TokenGeneratorNotFound, "#{generator} not found"
449
+ raise Errors::TokenGeneratorNotFound, "#{generator_name} not found"
262
450
  end
263
451
  end
264
452
  end
@@ -1,34 +1,35 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module ApplicationMixin
3
5
  extend ActiveSupport::Concern
4
6
 
5
7
  include OAuth::Helpers
8
+ include Models::Orderable
9
+ include Models::SecretStorable
6
10
  include Models::Scopes
7
- include ActiveModel::MassAssignmentSecurity if defined?(::ProtectedAttributes)
8
-
9
- included do
10
- has_many :access_grants, dependent: :delete_all, class_name: 'Doorkeeper::AccessGrant'
11
- has_many :access_tokens, dependent: :delete_all, class_name: 'Doorkeeper::AccessToken'
12
-
13
- validates :name, :secret, :uid, presence: true
14
- validates :uid, uniqueness: true
15
- validates :redirect_uri, redirect_uri: true
16
-
17
- before_validation :generate_uid, :generate_secret, on: :create
18
- end
19
11
 
12
+ # :nodoc
20
13
  module ClassMethods
21
14
  # Returns an instance of the Doorkeeper::Application with
22
15
  # specific UID and secret.
23
16
  #
17
+ # Public/Non-confidential applications will only find by uid if secret is
18
+ # blank.
19
+ #
24
20
  # @param uid [#to_s] UID (any object that responds to `#to_s`)
25
21
  # @param secret [#to_s] secret (any object that responds to `#to_s`)
26
22
  #
27
- # @return [Doorkeeper::Application, nil] Application instance or nil
28
- # if there is no record with such credentials
23
+ # @return [Doorkeeper::Application, nil]
24
+ # Application instance or nil if there is no record with such credentials
29
25
  #
30
26
  def by_uid_and_secret(uid, secret)
31
- find_by(uid: uid.to_s, secret: secret.to_s)
27
+ app = by_uid(uid)
28
+ return unless app
29
+ return app if secret.blank? && !app.confidential?
30
+ return unless app.secret_matches?(secret)
31
+
32
+ app
32
33
  end
33
34
 
34
35
  # Returns an instance of the Doorkeeper::Application with specific UID.
@@ -41,24 +42,54 @@ module Doorkeeper
41
42
  def by_uid(uid)
42
43
  find_by(uid: uid.to_s)
43
44
  end
44
- end
45
45
 
46
- private
46
+ ##
47
+ # Determines the secret storing transformer
48
+ # Unless configured otherwise, uses the plain secret strategy
49
+ def secret_strategy
50
+ ::Doorkeeper.config.application_secret_strategy
51
+ end
47
52
 
48
- def has_scopes?
49
- Doorkeeper.configuration.orm != :active_record ||
50
- Doorkeeper::Application.column_names.include?("scopes")
53
+ ##
54
+ # Determine the fallback storing strategy
55
+ # Unless configured, there will be no fallback
56
+ def fallback_secret_strategy
57
+ ::Doorkeeper.config.application_secret_fallback_strategy
58
+ end
51
59
  end
52
60
 
53
- def generate_uid
54
- if uid.blank?
55
- self.uid = UniqueToken.generate
56
- end
61
+ # Set an application's valid redirect URIs.
62
+ #
63
+ # @param uris [String, Array<String>] Newline-separated string or array the URI(s)
64
+ #
65
+ # @return [String] The redirect URI(s) separated by newlines.
66
+ #
67
+ def redirect_uri=(uris)
68
+ super(uris.is_a?(Array) ? uris.join("\n") : uris)
57
69
  end
58
70
 
59
- def generate_secret
60
- if secret.blank?
61
- self.secret = UniqueToken.generate
71
+ # Check whether the given plain text secret matches our stored secret
72
+ #
73
+ # @param input [#to_s] Plain secret provided by user
74
+ # (any object that responds to `#to_s`)
75
+ #
76
+ # @return [Boolean] Whether the given secret matches the stored secret
77
+ # of this application.
78
+ #
79
+ def secret_matches?(input)
80
+ # return false if either is nil, since secure_compare depends on strings
81
+ # but Application secrets MAY be nil depending on confidentiality.
82
+ return false if input.nil? || secret.nil?
83
+
84
+ # When matching the secret by comparer function, all is well.
85
+ return true if secret_strategy.secret_matches?(input, secret)
86
+
87
+ # When fallback lookup is enabled, ensure applications
88
+ # with plain secrets can still be found
89
+ if fallback_secret_strategy
90
+ fallback_secret_strategy.secret_matches?(input, secret)
91
+ else
92
+ false
62
93
  end
63
94
  end
64
95
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module Models
3
5
  module Accessible
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module Models
3
5
  module Expirable
@@ -6,7 +8,7 @@ module Doorkeeper
6
8
  #
7
9
  # @return [Boolean] true if object expired and false in other case
8
10
  def expired?
9
- expires_in && Time.now.utc > expired_time
11
+ !!(expires_in && Time.now.utc > expires_at)
10
12
  end
11
13
 
12
14
  # Calculates expiration time in seconds.
@@ -15,15 +17,19 @@ module Doorkeeper
15
17
  # or nil if object never expires.
16
18
  def expires_in_seconds
17
19
  return nil if expires_in.nil?
18
- expires = (created_at + expires_in.seconds) - Time.now.utc
20
+
21
+ expires = expires_at - Time.now.utc
19
22
  expires_sec = expires.seconds.round(0)
20
23
  expires_sec > 0 ? expires_sec : 0
21
24
  end
22
25
 
23
- private
24
-
25
- def expired_time
26
- created_at + expires_in.seconds
26
+ # Expiration time (date time of creation + TTL).
27
+ #
28
+ # @return [Time, nil] expiration time in UTC
29
+ # or nil if the object never expires.
30
+ #
31
+ def expires_at
32
+ expires_in && created_at + expires_in.seconds
27
33
  end
28
34
  end
29
35
  end