doorkeeper 5.1.2 → 5.6.6

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 (272) hide show
  1. checksums.yaml +4 -4
  2. data/{NEWS.md → CHANGELOG.md} +314 -27
  3. data/README.md +39 -22
  4. data/app/controllers/doorkeeper/application_controller.rb +3 -2
  5. data/app/controllers/doorkeeper/application_metal_controller.rb +3 -2
  6. data/app/controllers/doorkeeper/applications_controller.rb +5 -4
  7. data/app/controllers/doorkeeper/authorizations_controller.rb +76 -25
  8. data/app/controllers/doorkeeper/authorized_applications_controller.rb +5 -5
  9. data/app/controllers/doorkeeper/token_info_controller.rb +12 -2
  10. data/app/controllers/doorkeeper/tokens_controller.rb +99 -28
  11. data/app/helpers/doorkeeper/dashboard_helper.rb +1 -1
  12. data/app/views/doorkeeper/applications/_form.html.erb +1 -7
  13. data/app/views/doorkeeper/applications/show.html.erb +35 -14
  14. data/app/views/doorkeeper/authorizations/error.html.erb +3 -1
  15. data/app/views/doorkeeper/authorizations/form_post.html.erb +15 -0
  16. data/app/views/doorkeeper/authorizations/new.html.erb +16 -14
  17. data/config/locales/en.yml +16 -3
  18. data/lib/doorkeeper/config/abstract_builder.rb +28 -0
  19. data/lib/doorkeeper/config/option.rb +20 -2
  20. data/lib/doorkeeper/config/validations.rb +53 -0
  21. data/lib/doorkeeper/config.rb +300 -136
  22. data/lib/doorkeeper/engine.rb +10 -3
  23. data/lib/doorkeeper/errors.rb +13 -18
  24. data/lib/doorkeeper/grant_flow/fallback_flow.rb +15 -0
  25. data/lib/doorkeeper/grant_flow/flow.rb +44 -0
  26. data/lib/doorkeeper/grant_flow/registry.rb +50 -0
  27. data/lib/doorkeeper/grant_flow.rb +45 -0
  28. data/lib/doorkeeper/grape/helpers.rb +7 -3
  29. data/lib/doorkeeper/helpers/controller.rb +36 -11
  30. data/lib/doorkeeper/models/access_grant_mixin.rb +23 -19
  31. data/lib/doorkeeper/models/access_token_mixin.rb +195 -52
  32. data/lib/doorkeeper/models/application_mixin.rb +8 -7
  33. data/lib/doorkeeper/models/concerns/expirable.rb +1 -1
  34. data/lib/doorkeeper/models/concerns/expiration_time_sql_math.rb +88 -0
  35. data/lib/doorkeeper/models/concerns/ownership.rb +1 -1
  36. data/lib/doorkeeper/models/concerns/polymorphic_resource_owner.rb +30 -0
  37. data/lib/doorkeeper/models/concerns/resource_ownerable.rb +47 -0
  38. data/lib/doorkeeper/models/concerns/reusable.rb +1 -1
  39. data/lib/doorkeeper/models/concerns/revocable.rb +1 -28
  40. data/lib/doorkeeper/models/concerns/scopes.rb +5 -1
  41. data/lib/doorkeeper/models/concerns/secret_storable.rb +1 -3
  42. data/lib/doorkeeper/oauth/authorization/code.rb +31 -14
  43. data/lib/doorkeeper/oauth/authorization/context.rb +5 -5
  44. data/lib/doorkeeper/oauth/authorization/token.rb +30 -19
  45. data/lib/doorkeeper/oauth/authorization/uri_builder.rb +4 -4
  46. data/lib/doorkeeper/oauth/authorization_code_request.rb +51 -22
  47. data/lib/doorkeeper/oauth/base_request.rb +21 -22
  48. data/lib/doorkeeper/oauth/client/credentials.rb +2 -4
  49. data/lib/doorkeeper/oauth/client.rb +8 -9
  50. data/lib/doorkeeper/oauth/client_credentials/creator.rb +42 -5
  51. data/lib/doorkeeper/oauth/client_credentials/issuer.rb +10 -8
  52. data/lib/doorkeeper/oauth/client_credentials/{validation.rb → validator.rb} +14 -5
  53. data/lib/doorkeeper/oauth/client_credentials_request.rb +8 -7
  54. data/lib/doorkeeper/oauth/code_request.rb +6 -12
  55. data/lib/doorkeeper/oauth/code_response.rb +24 -14
  56. data/lib/doorkeeper/oauth/error.rb +1 -1
  57. data/lib/doorkeeper/oauth/error_response.rb +11 -13
  58. data/lib/doorkeeper/oauth/forbidden_token_response.rb +2 -1
  59. data/lib/doorkeeper/oauth/helpers/scope_checker.rb +8 -12
  60. data/lib/doorkeeper/oauth/helpers/unique_token.rb +10 -7
  61. data/lib/doorkeeper/oauth/helpers/uri_checker.rb +19 -23
  62. data/lib/doorkeeper/oauth/hooks/context.rb +21 -0
  63. data/lib/doorkeeper/oauth/invalid_request_response.rb +43 -0
  64. data/lib/doorkeeper/oauth/invalid_token_response.rb +7 -4
  65. data/lib/doorkeeper/oauth/nonstandard.rb +39 -0
  66. data/lib/doorkeeper/oauth/password_access_token_request.rb +34 -11
  67. data/lib/doorkeeper/oauth/pre_authorization.rb +114 -44
  68. data/lib/doorkeeper/oauth/refresh_token_request.rb +54 -34
  69. data/lib/doorkeeper/oauth/token.rb +6 -7
  70. data/lib/doorkeeper/oauth/token_introspection.rb +28 -22
  71. data/lib/doorkeeper/oauth/token_request.rb +6 -20
  72. data/lib/doorkeeper/oauth/token_response.rb +2 -3
  73. data/lib/doorkeeper/orm/active_record/access_grant.rb +4 -43
  74. data/lib/doorkeeper/orm/active_record/access_token.rb +4 -35
  75. data/lib/doorkeeper/orm/active_record/application.rb +5 -149
  76. data/lib/doorkeeper/orm/active_record/mixins/access_grant.rb +63 -0
  77. data/lib/doorkeeper/orm/active_record/mixins/access_token.rb +77 -0
  78. data/lib/doorkeeper/orm/active_record/mixins/application.rb +210 -0
  79. data/lib/doorkeeper/orm/active_record/redirect_uri_validator.rb +66 -0
  80. data/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +5 -2
  81. data/lib/doorkeeper/orm/active_record.rb +29 -22
  82. data/lib/doorkeeper/rails/helpers.rb +4 -4
  83. data/lib/doorkeeper/rails/routes/abstract_router.rb +35 -0
  84. data/lib/doorkeeper/rails/routes/mapper.rb +2 -2
  85. data/lib/doorkeeper/rails/routes/registry.rb +45 -0
  86. data/lib/doorkeeper/rails/routes.rb +28 -27
  87. data/lib/doorkeeper/rake/db.rake +6 -6
  88. data/lib/doorkeeper/request/authorization_code.rb +5 -3
  89. data/lib/doorkeeper/request/client_credentials.rb +2 -2
  90. data/lib/doorkeeper/request/password.rb +3 -2
  91. data/lib/doorkeeper/request/refresh_token.rb +5 -4
  92. data/lib/doorkeeper/request/strategy.rb +2 -2
  93. data/lib/doorkeeper/request.rb +49 -17
  94. data/lib/doorkeeper/server.rb +7 -11
  95. data/lib/doorkeeper/stale_records_cleaner.rb +6 -2
  96. data/lib/doorkeeper/version.rb +2 -6
  97. data/lib/doorkeeper.rb +183 -80
  98. data/lib/generators/doorkeeper/application_owner_generator.rb +1 -1
  99. data/lib/generators/doorkeeper/confidential_applications_generator.rb +2 -2
  100. data/lib/generators/doorkeeper/enable_polymorphic_resource_owner_generator.rb +39 -0
  101. data/lib/generators/doorkeeper/migration_generator.rb +1 -1
  102. data/lib/generators/doorkeeper/pkce_generator.rb +1 -1
  103. data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +7 -7
  104. data/lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb.erb +3 -1
  105. data/lib/generators/doorkeeper/templates/add_previous_refresh_token_to_access_tokens.rb.erb +2 -0
  106. data/lib/generators/doorkeeper/templates/enable_pkce_migration.rb.erb +2 -0
  107. data/lib/generators/doorkeeper/templates/enable_polymorphic_resource_owner_migration.rb.erb +17 -0
  108. data/lib/generators/doorkeeper/templates/initializer.rb +230 -50
  109. data/lib/generators/doorkeeper/templates/migration.rb.erb +31 -9
  110. metadata +61 -327
  111. data/.coveralls.yml +0 -1
  112. data/.github/ISSUE_TEMPLATE.md +0 -25
  113. data/.github/PULL_REQUEST_TEMPLATE.md +0 -17
  114. data/.gitignore +0 -20
  115. data/.gitlab-ci.yml +0 -16
  116. data/.hound.yml +0 -3
  117. data/.rspec +0 -1
  118. data/.rubocop.yml +0 -50
  119. data/.travis.yml +0 -35
  120. data/Appraisals +0 -40
  121. data/CODE_OF_CONDUCT.md +0 -46
  122. data/CONTRIBUTING.md +0 -47
  123. data/Dangerfile +0 -67
  124. data/Gemfile +0 -24
  125. data/RELEASING.md +0 -10
  126. data/Rakefile +0 -28
  127. data/SECURITY.md +0 -15
  128. data/UPGRADE.md +0 -2
  129. data/app/validators/redirect_uri_validator.rb +0 -50
  130. data/bin/console +0 -16
  131. data/doorkeeper.gemspec +0 -34
  132. data/gemfiles/rails_5_0.gemfile +0 -17
  133. data/gemfiles/rails_5_1.gemfile +0 -17
  134. data/gemfiles/rails_5_2.gemfile +0 -17
  135. data/gemfiles/rails_6_0.gemfile +0 -17
  136. data/gemfiles/rails_master.gemfile +0 -17
  137. data/spec/controllers/application_metal_controller_spec.rb +0 -64
  138. data/spec/controllers/applications_controller_spec.rb +0 -180
  139. data/spec/controllers/authorizations_controller_spec.rb +0 -527
  140. data/spec/controllers/protected_resources_controller_spec.rb +0 -353
  141. data/spec/controllers/token_info_controller_spec.rb +0 -50
  142. data/spec/controllers/tokens_controller_spec.rb +0 -330
  143. data/spec/dummy/Rakefile +0 -9
  144. data/spec/dummy/app/assets/config/manifest.js +0 -2
  145. data/spec/dummy/app/controllers/application_controller.rb +0 -5
  146. data/spec/dummy/app/controllers/custom_authorizations_controller.rb +0 -9
  147. data/spec/dummy/app/controllers/full_protected_resources_controller.rb +0 -14
  148. data/spec/dummy/app/controllers/home_controller.rb +0 -18
  149. data/spec/dummy/app/controllers/metal_controller.rb +0 -13
  150. data/spec/dummy/app/controllers/semi_protected_resources_controller.rb +0 -13
  151. data/spec/dummy/app/helpers/application_helper.rb +0 -7
  152. data/spec/dummy/app/models/user.rb +0 -7
  153. data/spec/dummy/app/views/home/index.html.erb +0 -0
  154. data/spec/dummy/app/views/layouts/application.html.erb +0 -14
  155. data/spec/dummy/config/application.rb +0 -47
  156. data/spec/dummy/config/boot.rb +0 -7
  157. data/spec/dummy/config/database.yml +0 -15
  158. data/spec/dummy/config/environment.rb +0 -5
  159. data/spec/dummy/config/environments/development.rb +0 -31
  160. data/spec/dummy/config/environments/production.rb +0 -64
  161. data/spec/dummy/config/environments/test.rb +0 -45
  162. data/spec/dummy/config/initializers/backtrace_silencers.rb +0 -9
  163. data/spec/dummy/config/initializers/doorkeeper.rb +0 -121
  164. data/spec/dummy/config/initializers/secret_token.rb +0 -10
  165. data/spec/dummy/config/initializers/session_store.rb +0 -10
  166. data/spec/dummy/config/initializers/wrap_parameters.rb +0 -16
  167. data/spec/dummy/config/locales/doorkeeper.en.yml +0 -5
  168. data/spec/dummy/config/routes.rb +0 -13
  169. data/spec/dummy/config.ru +0 -6
  170. data/spec/dummy/db/migrate/20111122132257_create_users.rb +0 -11
  171. data/spec/dummy/db/migrate/20120312140401_add_password_to_users.rb +0 -7
  172. data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +0 -69
  173. data/spec/dummy/db/migrate/20151223200000_add_owner_to_application.rb +0 -9
  174. data/spec/dummy/db/migrate/20160320211015_add_previous_refresh_token_to_access_tokens.rb +0 -13
  175. data/spec/dummy/db/migrate/20170822064514_enable_pkce.rb +0 -8
  176. data/spec/dummy/db/migrate/20180210183654_add_confidential_to_applications.rb +0 -13
  177. data/spec/dummy/db/schema.rb +0 -68
  178. data/spec/dummy/public/404.html +0 -26
  179. data/spec/dummy/public/422.html +0 -26
  180. data/spec/dummy/public/500.html +0 -26
  181. data/spec/dummy/public/favicon.ico +0 -0
  182. data/spec/dummy/script/rails +0 -9
  183. data/spec/factories.rb +0 -30
  184. data/spec/generators/application_owner_generator_spec.rb +0 -28
  185. data/spec/generators/confidential_applications_generator_spec.rb +0 -29
  186. data/spec/generators/install_generator_spec.rb +0 -36
  187. data/spec/generators/migration_generator_spec.rb +0 -28
  188. data/spec/generators/pkce_generator_spec.rb +0 -28
  189. data/spec/generators/previous_refresh_token_generator_spec.rb +0 -44
  190. data/spec/generators/templates/routes.rb +0 -4
  191. data/spec/generators/views_generator_spec.rb +0 -29
  192. data/spec/grape/grape_integration_spec.rb +0 -137
  193. data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +0 -26
  194. data/spec/lib/config_spec.rb +0 -697
  195. data/spec/lib/doorkeeper_spec.rb +0 -27
  196. data/spec/lib/models/expirable_spec.rb +0 -61
  197. data/spec/lib/models/reusable_spec.rb +0 -40
  198. data/spec/lib/models/revocable_spec.rb +0 -59
  199. data/spec/lib/models/scopes_spec.rb +0 -53
  200. data/spec/lib/models/secret_storable_spec.rb +0 -135
  201. data/spec/lib/oauth/authorization/uri_builder_spec.rb +0 -39
  202. data/spec/lib/oauth/authorization_code_request_spec.rb +0 -156
  203. data/spec/lib/oauth/base_request_spec.rb +0 -205
  204. data/spec/lib/oauth/base_response_spec.rb +0 -47
  205. data/spec/lib/oauth/client/credentials_spec.rb +0 -90
  206. data/spec/lib/oauth/client_credentials/creator_spec.rb +0 -94
  207. data/spec/lib/oauth/client_credentials/issuer_spec.rb +0 -112
  208. data/spec/lib/oauth/client_credentials/validation_spec.rb +0 -59
  209. data/spec/lib/oauth/client_credentials_integration_spec.rb +0 -29
  210. data/spec/lib/oauth/client_credentials_request_spec.rb +0 -109
  211. data/spec/lib/oauth/client_spec.rb +0 -38
  212. data/spec/lib/oauth/code_request_spec.rb +0 -47
  213. data/spec/lib/oauth/code_response_spec.rb +0 -36
  214. data/spec/lib/oauth/error_response_spec.rb +0 -66
  215. data/spec/lib/oauth/error_spec.rb +0 -23
  216. data/spec/lib/oauth/forbidden_token_response_spec.rb +0 -22
  217. data/spec/lib/oauth/helpers/scope_checker_spec.rb +0 -98
  218. data/spec/lib/oauth/helpers/unique_token_spec.rb +0 -21
  219. data/spec/lib/oauth/helpers/uri_checker_spec.rb +0 -247
  220. data/spec/lib/oauth/invalid_token_response_spec.rb +0 -55
  221. data/spec/lib/oauth/password_access_token_request_spec.rb +0 -192
  222. data/spec/lib/oauth/pre_authorization_spec.rb +0 -215
  223. data/spec/lib/oauth/refresh_token_request_spec.rb +0 -177
  224. data/spec/lib/oauth/scopes_spec.rb +0 -148
  225. data/spec/lib/oauth/token_request_spec.rb +0 -150
  226. data/spec/lib/oauth/token_response_spec.rb +0 -86
  227. data/spec/lib/oauth/token_spec.rb +0 -158
  228. data/spec/lib/request/strategy_spec.rb +0 -54
  229. data/spec/lib/secret_storing/base_spec.rb +0 -60
  230. data/spec/lib/secret_storing/bcrypt_spec.rb +0 -49
  231. data/spec/lib/secret_storing/plain_spec.rb +0 -44
  232. data/spec/lib/secret_storing/sha256_hash_spec.rb +0 -48
  233. data/spec/lib/server_spec.rb +0 -61
  234. data/spec/lib/stale_records_cleaner_spec.rb +0 -89
  235. data/spec/models/doorkeeper/access_grant_spec.rb +0 -144
  236. data/spec/models/doorkeeper/access_token_spec.rb +0 -591
  237. data/spec/models/doorkeeper/application_spec.rb +0 -472
  238. data/spec/requests/applications/applications_request_spec.rb +0 -259
  239. data/spec/requests/applications/authorized_applications_spec.rb +0 -32
  240. data/spec/requests/endpoints/authorization_spec.rb +0 -73
  241. data/spec/requests/endpoints/token_spec.rb +0 -75
  242. data/spec/requests/flows/authorization_code_errors_spec.rb +0 -78
  243. data/spec/requests/flows/authorization_code_spec.rb +0 -447
  244. data/spec/requests/flows/client_credentials_spec.rb +0 -128
  245. data/spec/requests/flows/implicit_grant_errors_spec.rb +0 -34
  246. data/spec/requests/flows/implicit_grant_spec.rb +0 -90
  247. data/spec/requests/flows/password_spec.rb +0 -259
  248. data/spec/requests/flows/refresh_token_spec.rb +0 -233
  249. data/spec/requests/flows/revoke_token_spec.rb +0 -143
  250. data/spec/requests/flows/skip_authorization_spec.rb +0 -66
  251. data/spec/requests/protected_resources/metal_spec.rb +0 -16
  252. data/spec/requests/protected_resources/private_api_spec.rb +0 -83
  253. data/spec/routing/custom_controller_routes_spec.rb +0 -133
  254. data/spec/routing/default_routes_spec.rb +0 -41
  255. data/spec/routing/scoped_routes_spec.rb +0 -47
  256. data/spec/spec_helper.rb +0 -57
  257. data/spec/spec_helper_integration.rb +0 -4
  258. data/spec/support/dependencies/factory_bot.rb +0 -4
  259. data/spec/support/doorkeeper_rspec.rb +0 -22
  260. data/spec/support/helpers/access_token_request_helper.rb +0 -13
  261. data/spec/support/helpers/authorization_request_helper.rb +0 -43
  262. data/spec/support/helpers/config_helper.rb +0 -11
  263. data/spec/support/helpers/model_helper.rb +0 -78
  264. data/spec/support/helpers/request_spec_helper.rb +0 -98
  265. data/spec/support/helpers/url_helper.rb +0 -62
  266. data/spec/support/http_method_shim.rb +0 -29
  267. data/spec/support/orm/active_record.rb +0 -5
  268. data/spec/support/shared/controllers_shared_context.rb +0 -123
  269. data/spec/support/shared/hashing_shared_context.rb +0 -36
  270. data/spec/support/shared/models_shared_examples.rb +0 -54
  271. data/spec/validators/redirect_uri_validator_spec.rb +0 -158
  272. data/spec/version/version_spec.rb +0 -17
@@ -12,6 +12,8 @@ module Doorkeeper
12
12
  include Models::Orderable
13
13
  include Models::SecretStorable
14
14
  include Models::Scopes
15
+ include Models::ResourceOwnerable
16
+ include Models::ExpirationTimeSqlMath
15
17
 
16
18
  module ClassMethods
17
19
  # Returns an instance of the Doorkeeper::AccessToken with
@@ -40,18 +42,35 @@ module Doorkeeper
40
42
  find_by_plaintext_token(:refresh_token, refresh_token)
41
43
  end
42
44
 
45
+ # Returns an instance of the Doorkeeper::AccessToken
46
+ # found by previous refresh token. Keep in mind that value
47
+ # of the previous_refresh_token isn't encrypted using
48
+ # secrets strategy.
49
+ #
50
+ # @param previous_refresh_token [#to_s]
51
+ # previous refresh token value (any object that responds to `#to_s`)
52
+ #
53
+ # @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
54
+ # if there is no record with such refresh token
55
+ #
56
+ def by_previous_refresh_token(previous_refresh_token)
57
+ find_by(refresh_token: previous_refresh_token)
58
+ end
59
+
43
60
  # Revokes AccessToken records that have not been revoked and associated
44
61
  # with the specific Application and Resource Owner.
45
62
  #
46
63
  # @param application_id [Integer]
47
64
  # ID of the Application
48
- # @param resource_owner [ActiveRecord::Base]
49
- # instance of the Resource Owner model
65
+ # @param resource_owner [ActiveRecord::Base, Integer]
66
+ # instance of the Resource Owner model or it's ID
50
67
  #
51
68
  def revoke_all_for(application_id, resource_owner, clock = Time)
52
- where(application_id: application_id,
53
- resource_owner_id: resource_owner.id,
54
- revoked_at: nil)
69
+ by_resource_owner(resource_owner)
70
+ .where(
71
+ application_id: application_id,
72
+ revoked_at: nil,
73
+ )
55
74
  .update_all(revoked_at: clock.now.utc)
56
75
  end
57
76
 
@@ -60,7 +79,7 @@ module Doorkeeper
60
79
  #
61
80
  # @param application [Doorkeeper::Application]
62
81
  # Application instance
63
- # @param resource_owner_or_id [ActiveRecord::Base, Integer]
82
+ # @param resource_owner [ActiveRecord::Base, Integer]
64
83
  # Resource Owner model instance or it's ID
65
84
  # @param scopes [String, Doorkeeper::OAuth::Scopes]
66
85
  # set of scopes
@@ -68,17 +87,52 @@ module Doorkeeper
68
87
  # @return [Doorkeeper::AccessToken, nil] Access Token instance or
69
88
  # nil if matching record was not found
70
89
  #
71
- def matching_token_for(application, resource_owner_or_id, scopes)
72
- resource_owner_id = if resource_owner_or_id.respond_to?(:to_key)
73
- resource_owner_or_id.id
74
- else
75
- resource_owner_or_id
76
- end
90
+ def matching_token_for(application, resource_owner, scopes, include_expired: true)
91
+ tokens = authorized_tokens_for(application&.id, resource_owner)
92
+ tokens = tokens.not_expired unless include_expired
93
+ find_matching_token(tokens, application, scopes)
94
+ end
77
95
 
78
- tokens = authorized_tokens_for(application.try(:id), resource_owner_id)
79
- tokens.detect do |token|
80
- scopes_match?(token.scopes, scopes, application.try(:scopes))
96
+ # Interface to enumerate access token records in batches in order not
97
+ # to bloat the memory. Could be overloaded in any ORM extension.
98
+ #
99
+ def find_access_token_in_batches(relation, **args, &block)
100
+ relation.find_in_batches(**args, &block)
101
+ end
102
+
103
+ # Enumerates AccessToken records in batches to find a matching token.
104
+ # Batching is required in order not to pollute the memory if Application
105
+ # has huge amount of associated records.
106
+ #
107
+ # ActiveRecord 5.x - 6.x ignores custom ordering so we can't perform a
108
+ # database sort by created_at, so we need to load all the matching records,
109
+ # sort them and find latest one.
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)
81
133
  end
134
+
135
+ matching_tokens.max_by(&:created_at)
82
136
  end
83
137
 
84
138
  # Checks whether the token scopes match the scopes from the parameters
@@ -101,8 +155,8 @@ module Doorkeeper
101
155
  (token_scopes.sort == param_scopes.sort) &&
102
156
  Doorkeeper::OAuth::Helpers::ScopeChecker.valid?(
103
157
  scope_str: param_scopes.to_s,
104
- server_scopes: Doorkeeper.configuration.scopes,
105
- app_scopes: app_scopes
158
+ server_scopes: Doorkeeper.config.scopes,
159
+ app_scopes: app_scopes,
106
160
  )
107
161
  end
108
162
 
@@ -112,48 +166,81 @@ module Doorkeeper
112
166
  #
113
167
  # @param application [Doorkeeper::Application]
114
168
  # Application instance
115
- # @param resource_owner_id [ActiveRecord::Base, Integer]
169
+ # @param resource_owner [ActiveRecord::Base, Integer]
116
170
  # Resource Owner model instance or it's ID
117
171
  # @param scopes [#to_s]
118
172
  # set of scopes (any object that responds to `#to_s`)
119
- # @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
120
176
  # token lifetime in seconds
121
- # @param use_refresh_token [Boolean]
177
+ # @option token_attributes [Boolean] :use_refresh_token
122
178
  # whether to use the refresh token
123
179
  #
124
180
  # @return [Doorkeeper::AccessToken] existing record or a new one
125
181
  #
126
- def find_or_create_for(application, resource_owner_id, scopes, expires_in, use_refresh_token)
127
- if Doorkeeper.configuration.reuse_access_token
128
- access_token = matching_token_for(application, resource_owner_id, scopes)
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, include_expired: false)
129
185
 
130
186
  return access_token if access_token&.reusable?
131
187
  end
132
188
 
133
- create!(
134
- application_id: application.try(:id),
135
- resource_owner_id: resource_owner_id,
136
- scopes: scopes.to_s,
137
- expires_in: expires_in,
138
- use_refresh_token: use_refresh_token
189
+ create_for(
190
+ application: application,
191
+ resource_owner: resource_owner,
192
+ scopes: scopes,
193
+ **token_attributes,
139
194
  )
140
195
  end
141
196
 
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] = application
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
+
142
228
  # Looking for not revoked Access Token records that belongs to specific
143
229
  # Application and Resource Owner.
144
230
  #
145
231
  # @param application_id [Integer]
146
232
  # ID of the Application model instance
147
- # @param resource_owner_id [Integer]
148
- # ID of the Resource Owner model instance
233
+ # @param resource_owner [ActiveRecord::Base, Integer]
234
+ # Resource Owner model instance or it's ID
149
235
  #
150
- # @return [Doorkeeper::AccessToken] array of matching AccessToken objects
236
+ # @return [ActiveRecord::Relation]
237
+ # collection of matching AccessToken objects
151
238
  #
152
- def authorized_tokens_for(application_id, resource_owner_id)
153
- ordered_by(:created_at, :desc)
154
- .where(application_id: application_id,
155
- resource_owner_id: resource_owner_id,
156
- revoked_at: nil)
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
+ )
157
244
  end
158
245
 
159
246
  # Convenience method for backwards-compatibility, return the last
@@ -161,33 +248,38 @@ module Doorkeeper
161
248
  #
162
249
  # @param application_id [Integer]
163
250
  # ID of the Application model instance
164
- # @param resource_owner_id [Integer]
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
- authorized_tokens_for(application_id, resource_owner_id).first
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
172
261
  end
173
262
 
174
263
  ##
175
264
  # Determines the secret storing transformer
176
265
  # Unless configured otherwise, uses the plain secret strategy
266
+ #
267
+ # @return [Doorkeeper::SecretStoring::Base]
268
+ #
177
269
  def secret_strategy
178
- ::Doorkeeper.configuration.token_secret_strategy
270
+ ::Doorkeeper.config.token_secret_strategy
179
271
  end
180
272
 
181
273
  ##
182
274
  # Determine the fallback storing strategy
183
275
  # Unless configured, there will be no fallback
184
276
  def fallback_secret_strategy
185
- ::Doorkeeper.configuration.token_secret_fallback_strategy
277
+ ::Doorkeeper.config.token_secret_fallback_strategy
186
278
  end
187
279
  end
188
280
 
189
281
  # Access Token type: Bearer.
190
- # @see https://tools.ietf.org/html/rfc6750
282
+ # @see https://datatracker.ietf.org/doc/html/rfc6750
191
283
  # The OAuth 2.0 Authorization Framework: Bearer Token Usage
192
284
  #
193
285
  def token_type
@@ -209,7 +301,11 @@ module Doorkeeper
209
301
  expires_in: expires_in_seconds,
210
302
  application: { uid: application.try(:uid) },
211
303
  created_at: created_at.to_i,
212
- }
304
+ }.tap do |json|
305
+ if Doorkeeper.configuration.polymorphic_resource_owner?
306
+ json[:resource_owner_type] = resource_owner_type
307
+ end
308
+ end
213
309
  end
214
310
 
215
311
  # Indicates whether the token instance have the same credential
@@ -221,7 +317,22 @@ module Doorkeeper
221
317
  #
222
318
  def same_credential?(access_token)
223
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
224
334
  resource_owner_id == access_token.resource_owner_id
335
+ end
225
336
  end
226
337
 
227
338
  # Indicates if token is acceptable for specific scopes.
@@ -259,8 +370,28 @@ module Doorkeeper
259
370
  end
260
371
  end
261
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
+
262
383
  private
263
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
+
264
395
  # Generates refresh token with UniqueToken generator.
265
396
  #
266
397
  # @return [String] refresh token value
@@ -271,7 +402,7 @@ module Doorkeeper
271
402
  end
272
403
 
273
404
  # Generates and sets the token value with the
274
- # configured Generator class (see Doorkeeper.configuration).
405
+ # configured Generator class (see Doorkeeper.config).
275
406
  #
276
407
  # @return [String] generated token value
277
408
  #
@@ -283,20 +414,32 @@ module Doorkeeper
283
414
  def generate_token
284
415
  self.created_at ||= Time.now.utc
285
416
 
286
- @raw_token = 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
+ {
287
429
  resource_owner_id: resource_owner_id,
288
430
  scopes: scopes,
289
431
  application: application,
290
432
  expires_in: expires_in,
291
- created_at: created_at
292
- )
293
-
294
- secret_strategy.store_secret(self, :token, @raw_token)
295
- @raw_token
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
296
439
  end
297
440
 
298
441
  def token_generator
299
- generator_name = Doorkeeper.configuration.access_token_generator
442
+ generator_name = Doorkeeper.config.access_token_generator
300
443
  generator = generator_name.constantize
301
444
 
302
445
  return generator if generator.respond_to?(:generate)
@@ -20,8 +20,8 @@ module Doorkeeper
20
20
  # @param uid [#to_s] UID (any object that responds to `#to_s`)
21
21
  # @param secret [#to_s] secret (any object that responds to `#to_s`)
22
22
  #
23
- # @return [Doorkeeper::Application, nil] Application instance or nil
24
- # 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
25
25
  #
26
26
  def by_uid_and_secret(uid, secret)
27
27
  app = by_uid(uid)
@@ -47,22 +47,23 @@ module Doorkeeper
47
47
  # Determines the secret storing transformer
48
48
  # Unless configured otherwise, uses the plain secret strategy
49
49
  def secret_strategy
50
- ::Doorkeeper.configuration.application_secret_strategy
50
+ ::Doorkeeper.config.application_secret_strategy
51
51
  end
52
52
 
53
53
  ##
54
54
  # Determine the fallback storing strategy
55
55
  # Unless configured, there will be no fallback
56
56
  def fallback_secret_strategy
57
- ::Doorkeeper.configuration.application_secret_fallback_strategy
57
+ ::Doorkeeper.config.application_secret_fallback_strategy
58
58
  end
59
59
  end
60
60
 
61
61
  # Set an application's valid redirect URIs.
62
62
  #
63
- # @param uris [String, Array] Newline-separated string or array the URI(s)
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.
64
66
  #
65
- # @return [String] The redirect URI(s) seperated by newlines.
66
67
  def redirect_uri=(uris)
67
68
  super(uris.is_a?(Array) ? uris.join("\n") : uris)
68
69
  end
@@ -72,7 +73,7 @@ module Doorkeeper
72
73
  # @param input [#to_s] Plain secret provided by user
73
74
  # (any object that responds to `#to_s`)
74
75
  #
75
- # @return [true] Whether the given secret matches the stored secret
76
+ # @return [Boolean] Whether the given secret matches the stored secret
76
77
  # of this application.
77
78
  #
78
79
  def secret_matches?(input)
@@ -8,7 +8,7 @@ module Doorkeeper
8
8
  #
9
9
  # @return [Boolean] true if object expired and false in other case
10
10
  def expired?
11
- expires_in && Time.now.utc > expires_at
11
+ !!(expires_in && Time.now.utc > expires_at)
12
12
  end
13
13
 
14
14
  # Calculates expiration time in seconds.
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper
4
+ module Models
5
+ module ExpirationTimeSqlMath
6
+ extend ::ActiveSupport::Concern
7
+
8
+ class ExpirationTimeSqlGenerator
9
+ attr_reader :model
10
+
11
+ delegate :table_name, to: :@model
12
+
13
+ def initialize(model)
14
+ @model = model
15
+ end
16
+
17
+ def generate_sql
18
+ raise "`generate_sql` should be overridden for a #{self.class.name}!"
19
+ end
20
+ end
21
+
22
+ class MySqlExpirationTimeSqlGenerator < ExpirationTimeSqlGenerator
23
+ def generate_sql
24
+ Arel.sql("DATE_ADD(#{table_name}.created_at, INTERVAL #{table_name}.expires_in SECOND)")
25
+ end
26
+ end
27
+
28
+ class SqlLiteExpirationTimeSqlGenerator < ExpirationTimeSqlGenerator
29
+ def generate_sql
30
+ Arel.sql("DATETIME(#{table_name}.created_at, '+' || #{table_name}.expires_in || ' SECONDS')")
31
+ end
32
+ end
33
+
34
+ class SqlServerExpirationTimeSqlGenerator < ExpirationTimeSqlGenerator
35
+ def generate_sql
36
+ Arel.sql("DATEADD(second, #{table_name}.expires_in, #{table_name}.created_at) AT TIME ZONE 'UTC'")
37
+ end
38
+ end
39
+
40
+ class OracleExpirationTimeSqlGenerator < ExpirationTimeSqlGenerator
41
+ def generate_sql
42
+ Arel.sql("#{table_name}.created_at + INTERVAL to_char(#{table_name}.expires_in) second")
43
+ end
44
+ end
45
+
46
+ class PostgresExpirationTimeSqlGenerator < ExpirationTimeSqlGenerator
47
+ def generate_sql
48
+ Arel.sql("#{table_name}.created_at + #{table_name}.expires_in * INTERVAL '1 SECOND'")
49
+ end
50
+ end
51
+
52
+ ADAPTERS_MAPPING = {
53
+ "sqlite" => SqlLiteExpirationTimeSqlGenerator,
54
+ "sqlite3" => SqlLiteExpirationTimeSqlGenerator,
55
+ "postgis" => PostgresExpirationTimeSqlGenerator,
56
+ "postgresql" => PostgresExpirationTimeSqlGenerator,
57
+ "mysql" => MySqlExpirationTimeSqlGenerator,
58
+ "mysql2" => MySqlExpirationTimeSqlGenerator,
59
+ "trilogy" => MySqlExpirationTimeSqlGenerator,
60
+ "sqlserver" => SqlServerExpirationTimeSqlGenerator,
61
+ "oracleenhanced" => OracleExpirationTimeSqlGenerator,
62
+ }.freeze
63
+
64
+ module ClassMethods
65
+ def supports_expiration_time_math?
66
+ ADAPTERS_MAPPING.key?(adapter_name.downcase) ||
67
+ respond_to?(:custom_expiration_time_sql)
68
+ end
69
+
70
+ def expiration_time_sql
71
+ if respond_to?(:custom_expiration_time_sql)
72
+ custom_expiration_time_sql
73
+ else
74
+ expiration_time_sql_expression
75
+ end
76
+ end
77
+
78
+ def expiration_time_sql_expression
79
+ ADAPTERS_MAPPING.fetch(adapter_name.downcase).new(self).generate_sql
80
+ end
81
+
82
+ def adapter_name
83
+ ActiveRecord::Base.connection.adapter_name
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -11,7 +11,7 @@ module Doorkeeper
11
11
  end
12
12
 
13
13
  def validate_owner?
14
- Doorkeeper.configuration.confirm_application_owner?
14
+ Doorkeeper.config.confirm_application_owner?
15
15
  end
16
16
  end
17
17
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper
4
+ module Models
5
+ module PolymorphicResourceOwner
6
+ module ForAccessGrant
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ if Doorkeeper.config.polymorphic_resource_owner?
11
+ belongs_to :resource_owner, polymorphic: true, optional: false
12
+ else
13
+ validates :resource_owner_id, presence: true
14
+ end
15
+ end
16
+ end
17
+
18
+ module ForAccessToken
19
+ extend ActiveSupport::Concern
20
+
21
+ included do
22
+ if Doorkeeper.config.polymorphic_resource_owner?
23
+ belongs_to :resource_owner, polymorphic: true, optional: true
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper
4
+ module Models
5
+ module ResourceOwnerable
6
+ extend ActiveSupport::Concern
7
+
8
+ module ClassMethods
9
+ # Searches for record by Resource Owner considering Doorkeeper
10
+ # configuration for resource owner association.
11
+ #
12
+ # @param resource_owner [ActiveRecord::Base, Integer]
13
+ # resource owner
14
+ #
15
+ # @return [Doorkeeper::AccessGrant, Doorkeeper::AccessToken]
16
+ # collection of records
17
+ #
18
+ def by_resource_owner(resource_owner)
19
+ if Doorkeeper.configuration.polymorphic_resource_owner?
20
+ where(resource_owner: resource_owner)
21
+ else
22
+ where(resource_owner_id: resource_owner_id_for(resource_owner))
23
+ end
24
+ end
25
+
26
+ protected
27
+
28
+ # Backward compatible way to retrieve resource owner itself (if
29
+ # polymorphic association enabled) or just it's ID.
30
+ #
31
+ # @param resource_owner [ActiveRecord::Base, Integer]
32
+ # resource owner
33
+ #
34
+ # @return [ActiveRecord::Base, Integer]
35
+ # instance of Resource Owner or it's ID
36
+ #
37
+ def resource_owner_id_for(resource_owner)
38
+ if resource_owner.respond_to?(:to_key)
39
+ resource_owner.id
40
+ else
41
+ resource_owner
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -11,7 +11,7 @@ module Doorkeeper
11
11
  return false if expired?
12
12
  return true unless expires_in
13
13
 
14
- threshold_limit = 100 - Doorkeeper.configuration.token_reuse_limit
14
+ threshold_limit = 100 - Doorkeeper.config.token_reuse_limit
15
15
  expires_in_seconds >= threshold_limit * expires_in / 100
16
16
  end
17
17
  end
@@ -9,7 +9,7 @@ module Doorkeeper
9
9
  # @param clock [Time] time object
10
10
  #
11
11
  def revoke(clock = Time)
12
- update_attribute :revoked_at, clock.now.utc
12
+ update_attribute(:revoked_at, clock.now.utc)
13
13
  end
14
14
 
15
15
  # Indicates whether the object has been revoked.
@@ -19,33 +19,6 @@ module Doorkeeper
19
19
  def revoked?
20
20
  !!(revoked_at && revoked_at <= Time.now.utc)
21
21
  end
22
-
23
- # Revokes token with `:refresh_token` equal to `:previous_refresh_token`
24
- # and clears `:previous_refresh_token` attribute.
25
- #
26
- def revoke_previous_refresh_token!
27
- return unless refresh_token_revoked_on_use?
28
-
29
- old_refresh_token&.revoke
30
- update_attribute :previous_refresh_token, ""
31
- end
32
-
33
- private
34
-
35
- # Searches for Access Token record with `:refresh_token` equal to
36
- # `:previous_refresh_token` value.
37
- #
38
- # @return [Doorkeeper::AccessToken, nil]
39
- # Access Token record or nil if nothing found
40
- #
41
- def old_refresh_token
42
- @old_refresh_token ||=
43
- AccessToken.by_refresh_token(previous_refresh_token)
44
- end
45
-
46
- def refresh_token_revoked_on_use?
47
- AccessToken.refresh_token_revoked_on_use?
48
- end
49
22
  end
50
23
  end
51
24
  end