doorkeeper 4.2.0 → 5.5.2

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 (271) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1038 -0
  3. data/README.md +110 -348
  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 -20
  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 +112 -35
  12. data/app/helpers/doorkeeper/dashboard_helper.rb +10 -6
  13. data/app/views/doorkeeper/applications/_delete_form.html.erb +4 -3
  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 +7 -1
  22. data/app/views/doorkeeper/authorized_applications/_delete_form.html.erb +1 -2
  23. data/app/views/doorkeeper/authorized_applications/index.html.erb +0 -1
  24. data/app/views/layouts/doorkeeper/admin.html.erb +16 -14
  25. data/config/locales/en.yml +33 -9
  26. data/lib/doorkeeper/config/abstract_builder.rb +28 -0
  27. data/lib/doorkeeper/config/option.rb +82 -0
  28. data/lib/doorkeeper/config/validations.rb +53 -0
  29. data/lib/doorkeeper/config.rb +545 -143
  30. data/lib/doorkeeper/engine.rb +11 -5
  31. data/lib/doorkeeper/errors.rb +37 -10
  32. data/lib/doorkeeper/grant_flow/fallback_flow.rb +15 -0
  33. data/lib/doorkeeper/grant_flow/flow.rb +44 -0
  34. data/lib/doorkeeper/grant_flow/registry.rb +50 -0
  35. data/lib/doorkeeper/grant_flow.rb +45 -0
  36. data/lib/doorkeeper/grape/authorization_decorator.rb +6 -4
  37. data/lib/doorkeeper/grape/helpers.rb +24 -12
  38. data/lib/doorkeeper/helpers/controller.rb +49 -27
  39. data/lib/doorkeeper/models/access_grant_mixin.rb +100 -21
  40. data/lib/doorkeeper/models/access_token_mixin.rb +379 -75
  41. data/lib/doorkeeper/models/application_mixin.rb +72 -25
  42. data/lib/doorkeeper/models/concerns/accessible.rb +6 -0
  43. data/lib/doorkeeper/models/concerns/expirable.rb +20 -6
  44. data/lib/doorkeeper/models/concerns/orderable.rb +15 -0
  45. data/lib/doorkeeper/models/concerns/ownership.rb +4 -7
  46. data/lib/doorkeeper/models/concerns/resource_ownerable.rb +47 -0
  47. data/lib/doorkeeper/models/concerns/reusable.rb +19 -0
  48. data/lib/doorkeeper/models/concerns/revocable.rb +12 -18
  49. data/lib/doorkeeper/models/concerns/scopes.rb +12 -2
  50. data/lib/doorkeeper/models/concerns/secret_storable.rb +106 -0
  51. data/lib/doorkeeper/oauth/authorization/code.rb +48 -12
  52. data/lib/doorkeeper/oauth/authorization/context.rb +17 -0
  53. data/lib/doorkeeper/oauth/authorization/token.rb +66 -28
  54. data/lib/doorkeeper/oauth/authorization/uri_builder.rb +22 -18
  55. data/lib/doorkeeper/oauth/authorization_code_request.rb +64 -14
  56. data/lib/doorkeeper/oauth/base_request.rb +66 -0
  57. data/lib/doorkeeper/oauth/base_response.rb +31 -0
  58. data/lib/doorkeeper/oauth/client/credentials.rb +23 -10
  59. data/lib/doorkeeper/oauth/client.rb +10 -12
  60. data/lib/doorkeeper/oauth/client_credentials/creator.rb +47 -4
  61. data/lib/doorkeeper/oauth/client_credentials/issuer.rb +16 -9
  62. data/lib/doorkeeper/oauth/client_credentials/validator.rb +56 -0
  63. data/lib/doorkeeper/oauth/client_credentials_request.rb +11 -15
  64. data/lib/doorkeeper/oauth/code_request.rb +8 -12
  65. data/lib/doorkeeper/oauth/code_response.rb +28 -15
  66. data/lib/doorkeeper/oauth/error.rb +5 -3
  67. data/lib/doorkeeper/oauth/error_response.rb +41 -20
  68. data/lib/doorkeeper/oauth/forbidden_token_response.rb +10 -3
  69. data/lib/doorkeeper/oauth/helpers/scope_checker.rb +23 -18
  70. data/lib/doorkeeper/oauth/helpers/unique_token.rb +20 -3
  71. data/lib/doorkeeper/oauth/helpers/uri_checker.rb +53 -3
  72. data/lib/doorkeeper/oauth/hooks/context.rb +21 -0
  73. data/lib/doorkeeper/oauth/invalid_request_response.rb +43 -0
  74. data/lib/doorkeeper/oauth/invalid_token_response.rb +31 -5
  75. data/lib/doorkeeper/oauth/nonstandard.rb +39 -0
  76. data/lib/doorkeeper/oauth/password_access_token_request.rb +45 -13
  77. data/lib/doorkeeper/oauth/pre_authorization.rb +135 -26
  78. data/lib/doorkeeper/oauth/refresh_token_request.rb +61 -36
  79. data/lib/doorkeeper/oauth/scopes.rb +26 -12
  80. data/lib/doorkeeper/oauth/token.rb +25 -23
  81. data/lib/doorkeeper/oauth/token_introspection.rb +202 -0
  82. data/lib/doorkeeper/oauth/token_request.rb +8 -21
  83. data/lib/doorkeeper/oauth/token_response.rb +14 -10
  84. data/lib/doorkeeper/oauth.rb +13 -0
  85. data/lib/doorkeeper/orm/active_record/access_grant.rb +6 -4
  86. data/lib/doorkeeper/orm/active_record/access_token.rb +5 -25
  87. data/lib/doorkeeper/orm/active_record/application.rb +6 -15
  88. data/lib/doorkeeper/orm/active_record/mixins/access_grant.rb +68 -0
  89. data/lib/doorkeeper/orm/active_record/mixins/access_token.rb +59 -0
  90. data/lib/doorkeeper/orm/active_record/mixins/application.rb +198 -0
  91. data/lib/doorkeeper/orm/active_record/redirect_uri_validator.rb +66 -0
  92. data/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +33 -0
  93. data/lib/doorkeeper/orm/active_record.rb +37 -8
  94. data/lib/doorkeeper/rails/helpers.rb +14 -15
  95. data/lib/doorkeeper/rails/routes/abstract_router.rb +35 -0
  96. data/lib/doorkeeper/rails/routes/mapper.rb +3 -1
  97. data/lib/doorkeeper/rails/routes/mapping.rb +10 -8
  98. data/lib/doorkeeper/rails/routes/registry.rb +45 -0
  99. data/lib/doorkeeper/rails/routes.rb +42 -30
  100. data/lib/doorkeeper/rake/db.rake +40 -0
  101. data/lib/doorkeeper/rake/setup.rake +11 -0
  102. data/lib/doorkeeper/rake.rb +14 -0
  103. data/lib/doorkeeper/request/authorization_code.rb +12 -4
  104. data/lib/doorkeeper/request/client_credentials.rb +3 -3
  105. data/lib/doorkeeper/request/code.rb +1 -1
  106. data/lib/doorkeeper/request/password.rb +5 -14
  107. data/lib/doorkeeper/request/refresh_token.rb +6 -5
  108. data/lib/doorkeeper/request/strategy.rb +4 -2
  109. data/lib/doorkeeper/request/token.rb +1 -1
  110. data/lib/doorkeeper/request.rb +62 -29
  111. data/lib/doorkeeper/secret_storing/base.rb +64 -0
  112. data/lib/doorkeeper/secret_storing/bcrypt.rb +60 -0
  113. data/lib/doorkeeper/secret_storing/plain.rb +33 -0
  114. data/lib/doorkeeper/secret_storing/sha256_hash.rb +26 -0
  115. data/lib/doorkeeper/server.rb +9 -19
  116. data/lib/doorkeeper/stale_records_cleaner.rb +24 -0
  117. data/lib/doorkeeper/validations.rb +5 -2
  118. data/lib/doorkeeper/version.rb +12 -1
  119. data/lib/doorkeeper.rb +111 -56
  120. data/lib/generators/doorkeeper/application_owner_generator.rb +28 -13
  121. data/lib/generators/doorkeeper/confidential_applications_generator.rb +33 -0
  122. data/lib/generators/doorkeeper/enable_polymorphic_resource_owner_generator.rb +39 -0
  123. data/lib/generators/doorkeeper/install_generator.rb +19 -9
  124. data/lib/generators/doorkeeper/migration_generator.rb +27 -10
  125. data/lib/generators/doorkeeper/pkce_generator.rb +33 -0
  126. data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +31 -19
  127. data/lib/generators/doorkeeper/templates/add_confidential_to_applications.rb.erb +13 -0
  128. data/lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb.erb +9 -0
  129. data/lib/generators/doorkeeper/templates/{add_previous_refresh_token_to_access_tokens.rb → add_previous_refresh_token_to_access_tokens.rb.erb} +3 -1
  130. data/lib/generators/doorkeeper/templates/enable_pkce_migration.rb.erb +8 -0
  131. data/lib/generators/doorkeeper/templates/enable_polymorphic_resource_owner_migration.rb.erb +17 -0
  132. data/lib/generators/doorkeeper/templates/initializer.rb +410 -31
  133. data/lib/generators/doorkeeper/templates/migration.rb.erb +88 -0
  134. data/lib/generators/doorkeeper/views_generator.rb +8 -4
  135. data/vendor/assets/stylesheets/doorkeeper/bootstrap.min.css +4 -5
  136. metadata +132 -286
  137. data/.gitignore +0 -14
  138. data/.hound.yml +0 -13
  139. data/.rspec +0 -1
  140. data/.travis.yml +0 -20
  141. data/CONTRIBUTING.md +0 -47
  142. data/Gemfile +0 -14
  143. data/NEWS.md +0 -593
  144. data/RELEASING.md +0 -17
  145. data/Rakefile +0 -20
  146. data/app/validators/redirect_uri_validator.rb +0 -34
  147. data/doorkeeper.gemspec +0 -28
  148. data/lib/doorkeeper/oauth/client/methods.rb +0 -18
  149. data/lib/doorkeeper/oauth/client_credentials/validation.rb +0 -45
  150. data/lib/doorkeeper/oauth/request_concern.rb +0 -48
  151. data/lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb +0 -7
  152. data/lib/generators/doorkeeper/templates/migration.rb +0 -68
  153. data/spec/controllers/application_metal_controller.rb +0 -10
  154. data/spec/controllers/applications_controller_spec.rb +0 -58
  155. data/spec/controllers/authorizations_controller_spec.rb +0 -189
  156. data/spec/controllers/protected_resources_controller_spec.rb +0 -300
  157. data/spec/controllers/token_info_controller_spec.rb +0 -52
  158. data/spec/controllers/tokens_controller_spec.rb +0 -88
  159. data/spec/dummy/Rakefile +0 -7
  160. data/spec/dummy/app/controllers/application_controller.rb +0 -3
  161. data/spec/dummy/app/controllers/custom_authorizations_controller.rb +0 -7
  162. data/spec/dummy/app/controllers/full_protected_resources_controller.rb +0 -12
  163. data/spec/dummy/app/controllers/home_controller.rb +0 -17
  164. data/spec/dummy/app/controllers/metal_controller.rb +0 -11
  165. data/spec/dummy/app/controllers/semi_protected_resources_controller.rb +0 -11
  166. data/spec/dummy/app/helpers/application_helper.rb +0 -5
  167. data/spec/dummy/app/models/user.rb +0 -5
  168. data/spec/dummy/app/views/home/index.html.erb +0 -0
  169. data/spec/dummy/app/views/layouts/application.html.erb +0 -14
  170. data/spec/dummy/config/application.rb +0 -23
  171. data/spec/dummy/config/boot.rb +0 -9
  172. data/spec/dummy/config/database.yml +0 -15
  173. data/spec/dummy/config/environment.rb +0 -5
  174. data/spec/dummy/config/environments/development.rb +0 -29
  175. data/spec/dummy/config/environments/production.rb +0 -62
  176. data/spec/dummy/config/environments/test.rb +0 -44
  177. data/spec/dummy/config/initializers/active_record_belongs_to_required_by_default.rb +0 -6
  178. data/spec/dummy/config/initializers/backtrace_silencers.rb +0 -7
  179. data/spec/dummy/config/initializers/doorkeeper.rb +0 -96
  180. data/spec/dummy/config/initializers/secret_token.rb +0 -9
  181. data/spec/dummy/config/initializers/session_store.rb +0 -8
  182. data/spec/dummy/config/initializers/wrap_parameters.rb +0 -14
  183. data/spec/dummy/config/locales/doorkeeper.en.yml +0 -5
  184. data/spec/dummy/config/routes.rb +0 -52
  185. data/spec/dummy/config.ru +0 -4
  186. data/spec/dummy/db/migrate/20111122132257_create_users.rb +0 -9
  187. data/spec/dummy/db/migrate/20120312140401_add_password_to_users.rb +0 -5
  188. data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +0 -60
  189. data/spec/dummy/db/migrate/20151223200000_add_owner_to_application.rb +0 -7
  190. data/spec/dummy/db/migrate/20160320211015_add_previous_refresh_token_to_access_tokens.rb +0 -11
  191. data/spec/dummy/db/schema.rb +0 -67
  192. data/spec/dummy/public/404.html +0 -26
  193. data/spec/dummy/public/422.html +0 -26
  194. data/spec/dummy/public/500.html +0 -26
  195. data/spec/dummy/public/favicon.ico +0 -0
  196. data/spec/dummy/script/rails +0 -6
  197. data/spec/factories.rb +0 -28
  198. data/spec/generators/application_owner_generator_spec.rb +0 -22
  199. data/spec/generators/install_generator_spec.rb +0 -31
  200. data/spec/generators/migration_generator_spec.rb +0 -20
  201. data/spec/generators/templates/routes.rb +0 -3
  202. data/spec/generators/views_generator_spec.rb +0 -27
  203. data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +0 -24
  204. data/spec/lib/config_spec.rb +0 -334
  205. data/spec/lib/doorkeeper_spec.rb +0 -28
  206. data/spec/lib/models/expirable_spec.rb +0 -51
  207. data/spec/lib/models/revocable_spec.rb +0 -59
  208. data/spec/lib/models/scopes_spec.rb +0 -43
  209. data/spec/lib/oauth/authorization/uri_builder_spec.rb +0 -42
  210. data/spec/lib/oauth/authorization_code_request_spec.rb +0 -80
  211. data/spec/lib/oauth/client/credentials_spec.rb +0 -47
  212. data/spec/lib/oauth/client/methods_spec.rb +0 -54
  213. data/spec/lib/oauth/client_credentials/creator_spec.rb +0 -44
  214. data/spec/lib/oauth/client_credentials/issuer_spec.rb +0 -86
  215. data/spec/lib/oauth/client_credentials/validation_spec.rb +0 -54
  216. data/spec/lib/oauth/client_credentials_integration_spec.rb +0 -27
  217. data/spec/lib/oauth/client_credentials_request_spec.rb +0 -104
  218. data/spec/lib/oauth/client_spec.rb +0 -39
  219. data/spec/lib/oauth/code_request_spec.rb +0 -45
  220. data/spec/lib/oauth/code_response_spec.rb +0 -34
  221. data/spec/lib/oauth/error_response_spec.rb +0 -61
  222. data/spec/lib/oauth/error_spec.rb +0 -23
  223. data/spec/lib/oauth/forbidden_token_response_spec.rb +0 -23
  224. data/spec/lib/oauth/helpers/scope_checker_spec.rb +0 -64
  225. data/spec/lib/oauth/helpers/unique_token_spec.rb +0 -20
  226. data/spec/lib/oauth/helpers/uri_checker_spec.rb +0 -104
  227. data/spec/lib/oauth/invalid_token_response_spec.rb +0 -28
  228. data/spec/lib/oauth/password_access_token_request_spec.rb +0 -90
  229. data/spec/lib/oauth/pre_authorization_spec.rb +0 -155
  230. data/spec/lib/oauth/refresh_token_request_spec.rb +0 -154
  231. data/spec/lib/oauth/scopes_spec.rb +0 -122
  232. data/spec/lib/oauth/token_request_spec.rb +0 -98
  233. data/spec/lib/oauth/token_response_spec.rb +0 -85
  234. data/spec/lib/oauth/token_spec.rb +0 -116
  235. data/spec/lib/request/strategy_spec.rb +0 -53
  236. data/spec/lib/server_spec.rb +0 -52
  237. data/spec/models/doorkeeper/access_grant_spec.rb +0 -36
  238. data/spec/models/doorkeeper/access_token_spec.rb +0 -394
  239. data/spec/models/doorkeeper/application_spec.rb +0 -179
  240. data/spec/requests/applications/applications_request_spec.rb +0 -94
  241. data/spec/requests/applications/authorized_applications_spec.rb +0 -30
  242. data/spec/requests/endpoints/authorization_spec.rb +0 -72
  243. data/spec/requests/endpoints/token_spec.rb +0 -64
  244. data/spec/requests/flows/authorization_code_errors_spec.rb +0 -66
  245. data/spec/requests/flows/authorization_code_spec.rb +0 -156
  246. data/spec/requests/flows/client_credentials_spec.rb +0 -58
  247. data/spec/requests/flows/implicit_grant_errors_spec.rb +0 -32
  248. data/spec/requests/flows/implicit_grant_spec.rb +0 -61
  249. data/spec/requests/flows/password_spec.rb +0 -115
  250. data/spec/requests/flows/refresh_token_spec.rb +0 -174
  251. data/spec/requests/flows/revoke_token_spec.rb +0 -157
  252. data/spec/requests/flows/skip_authorization_spec.rb +0 -59
  253. data/spec/requests/protected_resources/metal_spec.rb +0 -14
  254. data/spec/requests/protected_resources/private_api_spec.rb +0 -81
  255. data/spec/routing/custom_controller_routes_spec.rb +0 -71
  256. data/spec/routing/default_routes_spec.rb +0 -35
  257. data/spec/routing/scoped_routes_spec.rb +0 -31
  258. data/spec/spec_helper.rb +0 -2
  259. data/spec/spec_helper_integration.rb +0 -59
  260. data/spec/support/dependencies/factory_girl.rb +0 -2
  261. data/spec/support/helpers/access_token_request_helper.rb +0 -11
  262. data/spec/support/helpers/authorization_request_helper.rb +0 -41
  263. data/spec/support/helpers/config_helper.rb +0 -9
  264. data/spec/support/helpers/model_helper.rb +0 -67
  265. data/spec/support/helpers/request_spec_helper.rb +0 -76
  266. data/spec/support/helpers/url_helper.rb +0 -55
  267. data/spec/support/http_method_shim.rb +0 -24
  268. data/spec/support/orm/active_record.rb +0 -3
  269. data/spec/support/shared/controllers_shared_context.rb +0 -69
  270. data/spec/support/shared/models_shared_examples.rb +0 -52
  271. data/spec/validators/redirect_uri_validator_spec.rb +0 -78
@@ -1,100 +1,289 @@
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)
15
+ include Models::ResourceOwnerable
11
16
 
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
17
+ module ClassMethods
18
+ # Returns an instance of the Doorkeeper::AccessToken with
19
+ # specific plain text token value.
20
+ #
21
+ # @param token [#to_s]
22
+ # Plain text token value (any object that responds to `#to_s`)
23
+ #
24
+ # @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
25
+ # if there is no record with such token
26
+ #
27
+ def by_token(token)
28
+ find_by_plaintext_token(:token, token)
19
29
  end
20
30
 
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 :use_refresh_token
31
+ # Returns an instance of the Doorkeeper::AccessToken
32
+ # with specific token value.
33
+ #
34
+ # @param refresh_token [#to_s]
35
+ # refresh token value (any object that responds to `#to_s`)
36
+ #
37
+ # @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
38
+ # if there is no record with such refresh token
39
+ #
40
+ def by_refresh_token(refresh_token)
41
+ find_by_plaintext_token(:refresh_token, refresh_token)
42
+ end
27
43
 
28
- before_validation :generate_token, on: :create
29
- before_validation :generate_refresh_token,
30
- on: :create,
31
- if: :use_refresh_token?
32
- end
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)
57
+ end
33
58
 
34
- module ClassMethods
35
- def by_token(token)
36
- find_by(token: token.to_s)
59
+ # Revokes AccessToken records that have not been revoked and associated
60
+ # with the specific Application and Resource Owner.
61
+ #
62
+ # @param application_id [Integer]
63
+ # ID of the Application
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)
37
74
  end
38
75
 
39
- def by_refresh_token(refresh_token)
40
- find_by(refresh_token: refresh_token.to_s)
76
+ # Looking for not revoked Access Token with a matching set of scopes
77
+ # that belongs to specific Application and Resource Owner.
78
+ #
79
+ # @param application [Doorkeeper::Application]
80
+ # Application instance
81
+ # @param resource_owner [ActiveRecord::Base, Integer]
82
+ # Resource Owner model instance or it's ID
83
+ # @param scopes [String, Doorkeeper::OAuth::Scopes]
84
+ # set of scopes
85
+ #
86
+ # @return [Doorkeeper::AccessToken, nil] Access Token instance or
87
+ # nil if matching record was not found
88
+ #
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)
41
92
  end
42
93
 
43
- def revoke_all_for(application_id, resource_owner)
44
- where(application_id: application_id,
45
- resource_owner_id: resource_owner.id,
46
- revoked_at: nil).
47
- each(&:revoke)
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)
48
99
  end
49
100
 
50
- def matching_token_for(application, resource_owner_or_id, scopes)
51
- resource_owner_id = if resource_owner_or_id.respond_to?(:to_key)
52
- resource_owner_or_id.id
53
- else
54
- resource_owner_or_id
55
- end
56
- token = last_authorized_token_for(application.try(:id), resource_owner_id)
57
- if token && scopes_match?(token.scopes, scopes, application.try(:scopes))
58
- token
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)
59
133
  end
134
+
135
+ matching_tokens.max_by(&:created_at)
60
136
  end
61
137
 
138
+ # Checks whether the token scopes match the scopes from the parameters
139
+ #
140
+ # @param token_scopes [#to_s]
141
+ # set of scopes (any object that responds to `#to_s`)
142
+ # @param param_scopes [Doorkeeper::OAuth::Scopes]
143
+ # scopes from params
144
+ # @param app_scopes [Doorkeeper::OAuth::Scopes]
145
+ # Application scopes
146
+ #
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),
150
+ # and false in other cases
151
+ #
62
152
  def scopes_match?(token_scopes, param_scopes, app_scopes)
63
- (!token_scopes.present? && !param_scopes.present?) ||
64
- Doorkeeper::OAuth::Helpers::ScopeChecker.match?(
65
- token_scopes.to_s,
66
- param_scopes,
67
- 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,
68
160
  )
69
161
  end
70
162
 
71
- def find_or_create_for(application, resource_owner_id, scopes, expires_in, use_refresh_token)
72
- if Doorkeeper.configuration.reuse_access_token
73
- access_token = matching_token_for(application, resource_owner_id, scopes)
74
- if access_token && !access_token.expired?
75
- return access_token
76
- end
163
+ # Looking for not expired AccessToken record with a matching set of
164
+ # scopes that belongs to specific Application and Resource Owner.
165
+ # If it doesn't exists - then creates it.
166
+ #
167
+ # @param application [Doorkeeper::Application]
168
+ # Application instance
169
+ # @param resource_owner [ActiveRecord::Base, Integer]
170
+ # Resource Owner model instance or it's ID
171
+ # @param scopes [#to_s]
172
+ # set of scopes (any object that responds to `#to_s`)
173
+ # @param token_attributes [Hash]
174
+ # Additional attributes to use when creating a token
175
+ # @option token_attributes [Integer] :expires_in
176
+ # token lifetime in seconds
177
+ # @option token_attributes [Boolean] :use_refresh_token
178
+ # whether to use the refresh token
179
+ #
180
+ # @return [Doorkeeper::AccessToken] existing record or a new one
181
+ #
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?
77
187
  end
78
188
 
79
- create!(
80
- application_id: application.try(:id),
81
- resource_owner_id: resource_owner_id,
82
- scopes: scopes.to_s,
83
- expires_in: expires_in,
84
- use_refresh_token: use_refresh_token
189
+ create_for(
190
+ application: application,
191
+ resource_owner: resource_owner,
192
+ scopes: scopes,
193
+ **token_attributes,
85
194
  )
86
195
  end
87
196
 
88
- def last_authorized_token_for(application_id, resource_owner_id)
89
- send(order_method, created_at_desc).
90
- find_by(application_id: application_id,
91
- resource_owner_id: resource_owner_id,
92
- revoked_at: nil)
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
229
+ # Application and Resource Owner.
230
+ #
231
+ # @param application_id [Integer]
232
+ # ID of the Application model instance
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]
252
+ # ID of the Resource Owner model instance
253
+ #
254
+ # @return [Doorkeeper::AccessToken, nil] matching AccessToken object or
255
+ # nil if nothing was found
256
+ #
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
93
278
  end
94
279
  end
95
280
 
281
+ # Access Token type: Bearer.
282
+ # @see https://tools.ietf.org/html/rfc6750
283
+ # The OAuth 2.0 Authorization Framework: Bearer Token Usage
284
+ #
96
285
  def token_type
97
- 'bearer'
286
+ "Bearer"
98
287
  end
99
288
 
100
289
  def use_refresh_token?
@@ -102,47 +291,162 @@ module Doorkeeper
102
291
  !!@use_refresh_token
103
292
  end
104
293
 
294
+ # JSON representation of the Access Token instance.
295
+ #
296
+ # @return [Hash] hash with token data
105
297
  def as_json(_options = {})
106
298
  {
107
- resource_owner_id: resource_owner_id,
108
- scopes: scopes,
109
- expires_in_seconds: expires_in_seconds,
110
- application: { uid: application.try(:uid) },
111
- created_at: created_at.to_i
112
- }
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
113
309
  end
114
310
 
115
- # It indicates whether the tokens have the same credential
311
+ # Indicates whether the token instance have the same credential
312
+ # as the other Access Token.
313
+ #
314
+ # @param access_token [Doorkeeper::AccessToken] other token
315
+ #
316
+ # @return [Boolean] true if credentials are same of false in other cases
317
+ #
116
318
  def same_credential?(access_token)
117
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
118
334
  resource_owner_id == access_token.resource_owner_id
335
+ end
119
336
  end
120
337
 
338
+ # Indicates if token is acceptable for specific scopes.
339
+ #
340
+ # @param scopes [Array<String>] scopes
341
+ #
342
+ # @return [Boolean] true if record is accessible and includes scopes or
343
+ # false in other cases
344
+ #
121
345
  def acceptable?(scopes)
122
346
  accessible? && includes_scope?(*scopes)
123
347
  end
124
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
+
125
383
  private
126
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
+
395
+ # Generates refresh token with UniqueToken generator.
396
+ #
397
+ # @return [String] refresh token value
398
+ #
127
399
  def generate_refresh_token
128
- write_attribute :refresh_token, UniqueToken.generate
400
+ @raw_refresh_token = UniqueToken.generate
401
+ secret_strategy.store_secret(self, :refresh_token, @raw_refresh_token)
129
402
  end
130
403
 
404
+ # Generates and sets the token value with the
405
+ # configured Generator class (see Doorkeeper.config).
406
+ #
407
+ # @return [String] generated token value
408
+ #
409
+ # @raise [Doorkeeper::Errors::UnableToGenerateToken]
410
+ # custom class doesn't implement .generate method
411
+ # @raise [Doorkeeper::Errors::TokenGeneratorNotFound]
412
+ # custom class doesn't exist
413
+ #
131
414
  def generate_token
132
415
  self.created_at ||= Time.now.utc
133
416
 
134
- generator = Doorkeeper.configuration.access_token_generator.constantize
135
- 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
+ {
136
429
  resource_owner_id: resource_owner_id,
137
430
  scopes: scopes,
138
431
  application: application,
139
432
  expires_in: expires_in,
140
- created_at: created_at
141
- )
142
- 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
+
143
447
  raise Errors::UnableToGenerateToken, "#{generator} does not respond to `.generate`."
144
448
  rescue NameError
145
- raise Errors::TokenGeneratorNotFound, "#{generator} not found"
449
+ raise Errors::TokenGeneratorNotFound, "#{generator_name} not found"
146
450
  end
147
451
  end
148
452
  end
@@ -1,48 +1,95 @@
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: :destroy, class_name: 'Doorkeeper::AccessGrant'
11
- has_many :access_tokens, dependent: :destroy, 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
14
+ # Returns an instance of the Doorkeeper::Application with
15
+ # specific UID and secret.
16
+ #
17
+ # Public/Non-confidential applications will only find by uid if secret is
18
+ # blank.
19
+ #
20
+ # @param uid [#to_s] UID (any object that responds to `#to_s`)
21
+ # @param secret [#to_s] secret (any object that responds to `#to_s`)
22
+ #
23
+ # @return [Doorkeeper::Application, nil]
24
+ # Application instance or nil if there is no record with such credentials
25
+ #
21
26
  def by_uid_and_secret(uid, secret)
22
- 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
23
33
  end
24
34
 
35
+ # Returns an instance of the Doorkeeper::Application with specific UID.
36
+ #
37
+ # @param uid [#to_s] UID (any object that responds to `#to_s`)
38
+ #
39
+ # @return [Doorkeeper::Application, nil] Application instance or nil
40
+ # if there is no record with such UID
41
+ #
25
42
  def by_uid(uid)
26
43
  find_by(uid: uid.to_s)
27
44
  end
28
- end
29
45
 
30
- 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
31
52
 
32
- def has_scopes?
33
- Doorkeeper.configuration.orm != :active_record ||
34
- 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
35
59
  end
36
60
 
37
- def generate_uid
38
- if uid.blank?
39
- self.uid = UniqueToken.generate
40
- 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)
41
69
  end
42
70
 
43
- def generate_secret
44
- if secret.blank?
45
- 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
46
93
  end
47
94
  end
48
95
  end
@@ -1,6 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module Models
3
5
  module Accessible
6
+ # Indicates whether the object is accessible (not expired and not revoked).
7
+ #
8
+ # @return [Boolean] true if object accessible or false in other case
9
+ #
4
10
  def accessible?
5
11
  !expired? && !revoked?
6
12
  end