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