doorkeeper 5.1.0 → 5.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. checksums.yaml +4 -4
  2. data/Appraisals +3 -3
  3. data/CHANGELOG.md +881 -0
  4. data/CONTRIBUTING.md +11 -9
  5. data/Dangerfile +2 -2
  6. data/Dockerfile +29 -0
  7. data/Gemfile +4 -3
  8. data/NEWS.md +1 -814
  9. data/README.md +11 -3
  10. data/RELEASING.md +6 -5
  11. data/app/controllers/doorkeeper/application_controller.rb +2 -2
  12. data/app/controllers/doorkeeper/application_metal_controller.rb +3 -2
  13. data/app/controllers/doorkeeper/applications_controller.rb +1 -0
  14. data/app/controllers/doorkeeper/authorizations_controller.rb +16 -9
  15. data/app/controllers/doorkeeper/authorized_applications_controller.rb +2 -2
  16. data/app/controllers/doorkeeper/tokens_controller.rb +32 -9
  17. data/app/views/doorkeeper/applications/_form.html.erb +0 -6
  18. data/app/views/doorkeeper/applications/show.html.erb +1 -1
  19. data/config/locales/en.yml +8 -2
  20. data/doorkeeper.gemspec +9 -1
  21. data/gemfiles/rails_5_0.gemfile +3 -2
  22. data/gemfiles/rails_5_1.gemfile +3 -2
  23. data/gemfiles/rails_5_2.gemfile +3 -2
  24. data/gemfiles/rails_6_0.gemfile +4 -3
  25. data/gemfiles/rails_master.gemfile +3 -2
  26. data/lib/doorkeeper/config/option.rb +13 -7
  27. data/lib/doorkeeper/config.rb +157 -42
  28. data/lib/doorkeeper/errors.rb +13 -18
  29. data/lib/doorkeeper/grape/helpers.rb +6 -2
  30. data/lib/doorkeeper/helpers/controller.rb +28 -7
  31. data/lib/doorkeeper/models/access_grant_mixin.rb +7 -6
  32. data/lib/doorkeeper/models/access_token_mixin.rb +99 -20
  33. data/lib/doorkeeper/models/application_mixin.rb +3 -3
  34. data/lib/doorkeeper/models/concerns/ownership.rb +1 -1
  35. data/lib/doorkeeper/models/concerns/reusable.rb +1 -1
  36. data/lib/doorkeeper/models/concerns/revocable.rb +0 -27
  37. data/lib/doorkeeper/oauth/authorization/code.rb +12 -14
  38. data/lib/doorkeeper/oauth/authorization/token.rb +10 -7
  39. data/lib/doorkeeper/oauth/authorization_code_request.rb +31 -15
  40. data/lib/doorkeeper/oauth/base_request.rb +10 -4
  41. data/lib/doorkeeper/oauth/client.rb +7 -8
  42. data/lib/doorkeeper/oauth/client_credentials/creator.rb +23 -2
  43. data/lib/doorkeeper/oauth/client_credentials/issuer.rb +7 -7
  44. data/lib/doorkeeper/oauth/client_credentials/{validation.rb → validator.rb} +10 -2
  45. data/lib/doorkeeper/oauth/client_credentials_request.rb +1 -1
  46. data/lib/doorkeeper/oauth/code_request.rb +5 -11
  47. data/lib/doorkeeper/oauth/code_response.rb +4 -4
  48. data/lib/doorkeeper/oauth/error.rb +1 -1
  49. data/lib/doorkeeper/oauth/error_response.rb +6 -6
  50. data/lib/doorkeeper/oauth/helpers/scope_checker.rb +7 -5
  51. data/lib/doorkeeper/oauth/helpers/unique_token.rb +8 -5
  52. data/lib/doorkeeper/oauth/helpers/uri_checker.rb +19 -5
  53. data/lib/doorkeeper/oauth/invalid_request_response.rb +43 -0
  54. data/lib/doorkeeper/oauth/invalid_token_response.rb +5 -2
  55. data/lib/doorkeeper/oauth/nonstandard.rb +39 -0
  56. data/lib/doorkeeper/oauth/password_access_token_request.rb +9 -4
  57. data/lib/doorkeeper/oauth/pre_authorization.rb +76 -38
  58. data/lib/doorkeeper/oauth/refresh_token_request.rb +17 -14
  59. data/lib/doorkeeper/oauth/token.rb +2 -2
  60. data/lib/doorkeeper/oauth/token_introspection.rb +26 -16
  61. data/lib/doorkeeper/oauth/token_request.rb +4 -18
  62. data/lib/doorkeeper/orm/active_record/access_grant.rb +4 -43
  63. data/lib/doorkeeper/orm/active_record/access_token.rb +4 -35
  64. data/lib/doorkeeper/orm/active_record/application.rb +5 -83
  65. data/lib/doorkeeper/orm/active_record/mixins/access_grant.rb +53 -0
  66. data/lib/doorkeeper/orm/active_record/mixins/access_token.rb +47 -0
  67. data/lib/doorkeeper/orm/active_record/mixins/application.rb +128 -0
  68. data/lib/doorkeeper/orm/active_record/redirect_uri_validator.rb +61 -0
  69. data/lib/doorkeeper/orm/active_record.rb +21 -6
  70. data/lib/doorkeeper/rails/helpers.rb +4 -4
  71. data/lib/doorkeeper/rails/routes.rb +5 -7
  72. data/lib/doorkeeper/rake/db.rake +3 -3
  73. data/lib/doorkeeper/request/authorization_code.rb +5 -3
  74. data/lib/doorkeeper/request/client_credentials.rb +2 -2
  75. data/lib/doorkeeper/request/password.rb +2 -2
  76. data/lib/doorkeeper/request/refresh_token.rb +3 -3
  77. data/lib/doorkeeper/request.rb +7 -12
  78. data/lib/doorkeeper/server.rb +3 -7
  79. data/lib/doorkeeper/stale_records_cleaner.rb +6 -2
  80. data/lib/doorkeeper/version.rb +1 -1
  81. data/lib/doorkeeper.rb +6 -3
  82. data/lib/generators/doorkeeper/application_owner_generator.rb +1 -1
  83. data/lib/generators/doorkeeper/confidential_applications_generator.rb +1 -1
  84. data/lib/generators/doorkeeper/migration_generator.rb +1 -1
  85. data/lib/generators/doorkeeper/pkce_generator.rb +1 -1
  86. data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +7 -7
  87. data/lib/generators/doorkeeper/templates/initializer.rb +149 -33
  88. data/lib/generators/doorkeeper/templates/migration.rb.erb +4 -1
  89. data/spec/controllers/application_metal_controller_spec.rb +1 -1
  90. data/spec/controllers/applications_controller_spec.rb +95 -1
  91. data/spec/controllers/authorizations_controller_spec.rb +156 -75
  92. data/spec/controllers/protected_resources_controller_spec.rb +28 -20
  93. data/spec/controllers/token_info_controller_spec.rb +1 -1
  94. data/spec/controllers/tokens_controller_spec.rb +206 -38
  95. data/spec/dummy/config/application.rb +3 -1
  96. data/spec/dummy/config/initializers/doorkeeper.rb +54 -9
  97. data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +3 -3
  98. data/spec/dummy/db/migrate/20160320211015_add_previous_refresh_token_to_access_tokens.rb +1 -1
  99. data/spec/dummy/db/migrate/20180210183654_add_confidential_to_applications.rb +1 -1
  100. data/spec/generators/install_generator_spec.rb +1 -1
  101. data/spec/generators/previous_refresh_token_generator_spec.rb +2 -2
  102. data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +1 -1
  103. data/spec/lib/config_spec.rb +104 -7
  104. data/spec/lib/doorkeeper_spec.rb +1 -1
  105. data/spec/lib/models/revocable_spec.rb +3 -3
  106. data/spec/lib/oauth/authorization_code_request_spec.rb +129 -115
  107. data/spec/lib/oauth/base_request_spec.rb +154 -135
  108. data/spec/lib/oauth/base_response_spec.rb +27 -29
  109. data/spec/lib/oauth/client/credentials_spec.rb +1 -1
  110. data/spec/lib/oauth/client_credentials/creator_spec.rb +43 -3
  111. data/spec/lib/oauth/client_credentials/issuer_spec.rb +12 -12
  112. data/spec/lib/oauth/client_credentials/validation_spec.rb +4 -4
  113. data/spec/lib/oauth/client_credentials_integration_spec.rb +16 -18
  114. data/spec/lib/oauth/client_credentials_request_spec.rb +78 -80
  115. data/spec/lib/oauth/client_spec.rb +26 -26
  116. data/spec/lib/oauth/code_request_spec.rb +34 -35
  117. data/spec/lib/oauth/code_response_spec.rb +21 -25
  118. data/spec/lib/oauth/error_response_spec.rb +42 -44
  119. data/spec/lib/oauth/error_spec.rb +12 -14
  120. data/spec/lib/oauth/forbidden_token_response_spec.rb +11 -13
  121. data/spec/lib/oauth/helpers/scope_checker_spec.rb +30 -18
  122. data/spec/lib/oauth/helpers/uri_checker_spec.rb +17 -2
  123. data/spec/lib/oauth/invalid_request_response_spec.rb +73 -0
  124. data/spec/lib/oauth/invalid_token_response_spec.rb +32 -34
  125. data/spec/lib/oauth/password_access_token_request_spec.rb +145 -147
  126. data/spec/lib/oauth/pre_authorization_spec.rb +161 -153
  127. data/spec/lib/oauth/refresh_token_request_spec.rb +138 -138
  128. data/spec/lib/oauth/scopes_spec.rb +104 -106
  129. data/spec/lib/oauth/token_request_spec.rb +117 -110
  130. data/spec/lib/oauth/token_response_spec.rb +71 -73
  131. data/spec/lib/oauth/token_spec.rb +121 -123
  132. data/spec/lib/server_spec.rb +0 -12
  133. data/spec/models/doorkeeper/access_grant_spec.rb +21 -4
  134. data/spec/models/doorkeeper/access_token_spec.rb +42 -11
  135. data/spec/models/doorkeeper/application_spec.rb +114 -77
  136. data/spec/requests/applications/applications_request_spec.rb +1 -1
  137. data/spec/requests/endpoints/authorization_spec.rb +26 -8
  138. data/spec/requests/endpoints/token_spec.rb +1 -1
  139. data/spec/requests/flows/authorization_code_errors_spec.rb +1 -0
  140. data/spec/requests/flows/authorization_code_spec.rb +123 -45
  141. data/spec/requests/flows/client_credentials_spec.rb +38 -0
  142. data/spec/requests/flows/implicit_grant_errors_spec.rb +22 -10
  143. data/spec/requests/flows/implicit_grant_spec.rb +9 -8
  144. data/spec/requests/flows/password_spec.rb +67 -10
  145. data/spec/requests/flows/refresh_token_spec.rb +20 -20
  146. data/spec/requests/flows/revoke_token_spec.rb +37 -23
  147. data/spec/spec_helper.rb +1 -4
  148. data/spec/support/doorkeeper_rspec.rb +1 -1
  149. data/spec/support/helpers/request_spec_helper.rb +14 -2
  150. data/spec/support/shared/controllers_shared_context.rb +33 -23
  151. data/spec/validators/redirect_uri_validator_spec.rb +41 -16
  152. metadata +19 -16
  153. data/.coveralls.yml +0 -1
  154. data/.github/ISSUE_TEMPLATE.md +0 -25
  155. data/.github/PULL_REQUEST_TEMPLATE.md +0 -17
  156. data/.gitignore +0 -20
  157. data/.gitlab-ci.yml +0 -16
  158. data/.hound.yml +0 -3
  159. data/.rspec +0 -1
  160. data/.rubocop.yml +0 -50
  161. data/.travis.yml +0 -35
  162. data/app/validators/redirect_uri_validator.rb +0 -50
  163. data/spec/support/http_method_shim.rb +0 -29
@@ -7,7 +7,7 @@ module Doorkeeper
7
7
  # @see https://tools.ietf.org/html/rfc7662
8
8
  class TokenIntrospection
9
9
  attr_reader :server, :token
10
- attr_reader :error
10
+ attr_reader :error, :invalid_request_reason
11
11
 
12
12
  def initialize(server, token)
13
13
  @server = server
@@ -25,6 +25,8 @@ module Doorkeeper
25
25
 
26
26
  if @error == :invalid_token
27
27
  OAuth::InvalidTokenResponse.from_access_token(authorized_token)
28
+ elsif @error == :invalid_request
29
+ OAuth::InvalidRequestResponse.from_request(self)
28
30
  else
29
31
  OAuth::ErrorResponse.new(name: @error)
30
32
  end
@@ -67,9 +69,10 @@ module Doorkeeper
67
69
  # HTTP 401 code as described in Section 3 of OAuth 2.0 Bearer Token
68
70
  # Usage [RFC6750].
69
71
  #
70
- @error = :invalid_token if authorized_token_matches_introspected? || !authorized_token.accessible?
72
+ @error = :invalid_token unless valid_authorized_token?
71
73
  else
72
74
  @error = :invalid_request
75
+ @invalid_request_reason = :request_not_authorized
73
76
  end
74
77
  end
75
78
 
@@ -80,8 +83,7 @@ module Doorkeeper
80
83
 
81
84
  # Bearer Token Authentication
82
85
  def authorized_token
83
- @authorized_token ||=
84
- OAuth::Token.authenticate(server.context.request, :from_bearer_authorization)
86
+ @authorized_token ||= Doorkeeper.authenticate(server.context.request)
85
87
  end
86
88
 
87
89
  # 2.2. Introspection Response
@@ -92,7 +94,7 @@ module Doorkeeper
92
94
  client_id: @token.try(:application).try(:uid),
93
95
  token_type: @token.token_type,
94
96
  exp: @token.expires_at.to_i,
95
- iat: @token.created_at.to_i
97
+ iat: @token.created_at.to_i,
96
98
  )
97
99
  end
98
100
 
@@ -150,7 +152,7 @@ module Doorkeeper
150
152
  #
151
153
  def active?
152
154
  if authorized_client
153
- valid_token? && authorized_for_client?
155
+ valid_token? && token_introspection_allowed?(auth_client: authorized_client.application)
154
156
  else
155
157
  valid_token?
156
158
  end
@@ -161,19 +163,27 @@ module Doorkeeper
161
163
  @token&.accessible?
162
164
  end
163
165
 
166
+ def valid_authorized_token?
167
+ !authorized_token_matches_introspected? &&
168
+ authorized_token.accessible? &&
169
+ token_introspection_allowed?(auth_token: authorized_token)
170
+ end
171
+
164
172
  # RFC7662 Section 2.1
165
173
  def authorized_token_matches_introspected?
166
174
  authorized_token.token == @token&.token
167
175
  end
168
176
 
169
- # If token doesn't belong to some client, then it is public.
170
- # Otherwise in it required for token to be connected to the same client.
171
- def authorized_for_client?
172
- if @token.application
173
- @token.application == authorized_client.application
174
- else
175
- true
176
- end
177
+ # Config constraints for introspection in Doorkeeper.config.allow_token_introspection
178
+ def token_introspection_allowed?(auth_client: nil, auth_token: nil)
179
+ allow_introspection = Doorkeeper.config.allow_token_introspection
180
+ return allow_introspection unless allow_introspection.respond_to?(:call)
181
+
182
+ allow_introspection.call(
183
+ @token,
184
+ auth_client,
185
+ auth_token,
186
+ )
177
187
  end
178
188
 
179
189
  # Allows to customize introspection response.
@@ -183,9 +193,9 @@ module Doorkeeper
183
193
  # @see https://tools.ietf.org/html/rfc7662#section-2.2
184
194
  #
185
195
  def customize_response(response)
186
- customized_response = Doorkeeper.configuration.custom_introspection_response.call(
196
+ customized_response = Doorkeeper.config.custom_introspection_response.call(
187
197
  token,
188
- server.context
198
+ server.context,
189
199
  )
190
200
  return response if customized_response.blank?
191
201
 
@@ -11,28 +11,14 @@ module Doorkeeper
11
11
  end
12
12
 
13
13
  def authorize
14
- if pre_auth.authorizable?
15
- auth = Authorization::Token.new(pre_auth, resource_owner)
16
- auth.issue_token
17
- @response = CodeResponse.new pre_auth,
18
- auth,
19
- response_on_fragment: true
20
- else
21
- @response = error_response
22
- end
14
+ auth = Authorization::Token.new(pre_auth, resource_owner)
15
+ auth.issue_token
16
+ CodeResponse.new(pre_auth, auth, response_on_fragment: true)
23
17
  end
24
18
 
25
19
  def deny
26
20
  pre_auth.error = :access_denied
27
- error_response
28
- end
29
-
30
- private
31
-
32
- def error_response
33
- ErrorResponse.from_request pre_auth,
34
- redirect_uri: pre_auth.redirect_uri,
35
- response_on_fragment: true
21
+ pre_auth.error_response
36
22
  end
37
23
  end
38
24
  end
@@ -1,48 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Doorkeeper
4
- class AccessGrant < ActiveRecord::Base
5
- self.table_name = "#{table_name_prefix}oauth_access_grants#{table_name_suffix}"
6
-
7
- include AccessGrantMixin
8
-
9
- belongs_to :application, class_name: "Doorkeeper::Application",
10
- optional: true, inverse_of: :access_grants
11
-
12
- validates :resource_owner_id,
13
- :application_id,
14
- :token,
15
- :expires_in,
16
- :redirect_uri,
17
- presence: true
18
-
19
- validates :token, uniqueness: true
3
+ require "doorkeeper/orm/active_record/mixins/access_grant"
20
4
 
21
- before_validation :generate_token, on: :create
22
-
23
- # We keep a volatile copy of the raw token for initial communication
24
- # The stored refresh_token may be mapped and not available in cleartext.
25
- #
26
- # Some strategies allow restoring stored secrets (e.g. symmetric encryption)
27
- # while hashing strategies do not, so you cannot rely on this value
28
- # returning a present value for persisted tokens.
29
- def plaintext_token
30
- if secret_strategy.allows_restoring_secrets?
31
- secret_strategy.restore_secret(self, :token)
32
- else
33
- @raw_token
34
- end
35
- end
36
-
37
- private
38
-
39
- # Generates token value with UniqueToken class.
40
- #
41
- # @return [String] token value
42
- #
43
- def generate_token
44
- @raw_token = UniqueToken.generate
45
- secret_strategy.store_secret(self, :token, @raw_token)
46
- end
5
+ module Doorkeeper
6
+ class AccessGrant < ::ActiveRecord::Base
7
+ include Doorkeeper::Orm::ActiveRecord::Mixins::AccessGrant
47
8
  end
48
9
  end
@@ -1,40 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Doorkeeper
4
- class AccessToken < ActiveRecord::Base
5
- self.table_name = "#{table_name_prefix}oauth_access_tokens#{table_name_suffix}"
6
-
7
- include AccessTokenMixin
8
-
9
- belongs_to :application, class_name: "Doorkeeper::Application",
10
- inverse_of: :access_tokens, optional: true
11
-
12
- validates :token, presence: true, uniqueness: true
13
- validates :refresh_token, uniqueness: true, if: :use_refresh_token?
3
+ require "doorkeeper/orm/active_record/mixins/access_token"
14
4
 
15
- # @attr_writer [Boolean, nil] use_refresh_token
16
- # indicates the possibility of using refresh token
17
- attr_writer :use_refresh_token
18
-
19
- before_validation :generate_token, on: :create
20
- before_validation :generate_refresh_token,
21
- on: :create, if: :use_refresh_token?
22
-
23
- # Searches for not revoked Access Tokens associated with the
24
- # specific Resource Owner.
25
- #
26
- # @param resource_owner [ActiveRecord::Base]
27
- # Resource Owner model instance
28
- #
29
- # @return [ActiveRecord::Relation]
30
- # active Access Tokens for Resource Owner
31
- #
32
- def self.active_for(resource_owner)
33
- where(resource_owner_id: resource_owner.id, revoked_at: nil)
34
- end
35
-
36
- def self.refresh_token_revoked_on_use?
37
- column_names.include?("previous_refresh_token")
38
- end
5
+ module Doorkeeper
6
+ class AccessToken < ::ActiveRecord::Base
7
+ include Doorkeeper::Orm::ActiveRecord::Mixins::AccessToken
39
8
  end
40
9
  end
@@ -1,88 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Doorkeeper
4
- class Application < ActiveRecord::Base
5
- self.table_name = "#{table_name_prefix}oauth_applications#{table_name_suffix}"
6
-
7
- include ApplicationMixin
8
-
9
- has_many :access_grants, dependent: :delete_all, class_name: "Doorkeeper::AccessGrant"
10
- has_many :access_tokens, dependent: :delete_all, class_name: "Doorkeeper::AccessToken"
11
-
12
- validates :name, :secret, :uid, presence: true
13
- validates :uid, uniqueness: true
14
- validates :redirect_uri, redirect_uri: true
15
- validates :confidential, inclusion: { in: [true, false] }
16
-
17
- validate :scopes_match_configured, if: :enforce_scopes?
18
-
19
- before_validation :generate_uid, :generate_secret, on: :create
20
-
21
- has_many :authorized_tokens, -> { where(revoked_at: nil) }, class_name: "AccessToken"
22
- has_many :authorized_applications, through: :authorized_tokens, source: :application
23
-
24
- # Returns Applications associated with active (not revoked) Access Tokens
25
- # that are owned by the specific Resource Owner.
26
- #
27
- # @param resource_owner [ActiveRecord::Base]
28
- # Resource Owner model instance
29
- #
30
- # @return [ActiveRecord::Relation]
31
- # Applications authorized for the Resource Owner
32
- #
33
- def self.authorized_for(resource_owner)
34
- resource_access_tokens = AccessToken.active_for(resource_owner)
35
- where(id: resource_access_tokens.select(:application_id).distinct)
36
- end
3
+ require "doorkeeper/orm/active_record/redirect_uri_validator"
4
+ require "doorkeeper/orm/active_record/mixins/application"
37
5
 
38
- # Revokes AccessToken and AccessGrant records that have not been revoked and
39
- # associated with the specific Application and Resource Owner.
40
- #
41
- # @param resource_owner [ActiveRecord::Base]
42
- # instance of the Resource Owner model
43
- #
44
- def self.revoke_tokens_and_grants_for(id, resource_owner)
45
- AccessToken.revoke_all_for(id, resource_owner)
46
- AccessGrant.revoke_all_for(id, resource_owner)
47
- end
48
-
49
- # We keep a volatile copy of the raw secret for initial communication
50
- # The stored refresh_token may be mapped and not available in cleartext.
51
- #
52
- # Some strategies allow restoring stored secrets (e.g. symmetric encryption)
53
- # while hashing strategies do not, so you cannot rely on this value
54
- # returning a present value for persisted tokens.
55
- def plaintext_secret
56
- if secret_strategy.allows_restoring_secrets?
57
- secret_strategy.restore_secret(self, :secret)
58
- else
59
- @raw_secret
60
- end
61
- end
62
-
63
- private
64
-
65
- def generate_uid
66
- self.uid = UniqueToken.generate if uid.blank?
67
- end
68
-
69
- def generate_secret
70
- return unless secret.blank?
71
-
72
- @raw_secret = UniqueToken.generate
73
- secret_strategy.store_secret(self, :secret, @raw_secret)
74
- end
75
-
76
- def scopes_match_configured
77
- if scopes.present? &&
78
- !ScopeChecker.valid?(scope_str: scopes.to_s,
79
- server_scopes: Doorkeeper.configuration.scopes)
80
- errors.add(:scopes, :not_match_configured)
81
- end
82
- end
83
-
84
- def enforce_scopes?
85
- Doorkeeper.configuration.enforce_configured_scopes?
86
- end
6
+ module Doorkeeper
7
+ class Application < ::ActiveRecord::Base
8
+ include ::Doorkeeper::Orm::ActiveRecord::Mixins::Application
87
9
  end
88
10
  end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper::Orm::ActiveRecord::Mixins
4
+ module AccessGrant
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ self.table_name = "#{table_name_prefix}oauth_access_grants#{table_name_suffix}"
9
+
10
+ include ::Doorkeeper::AccessGrantMixin
11
+
12
+ belongs_to :application, class_name: Doorkeeper.config.application_class,
13
+ optional: true,
14
+ inverse_of: :access_grants
15
+
16
+ validates :resource_owner_id,
17
+ :application_id,
18
+ :token,
19
+ :expires_in,
20
+ :redirect_uri,
21
+ presence: true
22
+
23
+ validates :token, uniqueness: { case_sensitive: true }
24
+
25
+ before_validation :generate_token, on: :create
26
+
27
+ # We keep a volatile copy of the raw token for initial communication
28
+ # The stored refresh_token may be mapped and not available in cleartext.
29
+ #
30
+ # Some strategies allow restoring stored secrets (e.g. symmetric encryption)
31
+ # while hashing strategies do not, so you cannot rely on this value
32
+ # returning a present value for persisted tokens.
33
+ def plaintext_token
34
+ if secret_strategy.allows_restoring_secrets?
35
+ secret_strategy.restore_secret(self, :token)
36
+ else
37
+ @raw_token
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ # Generates token value with UniqueToken class.
44
+ #
45
+ # @return [String] token value
46
+ #
47
+ def generate_token
48
+ @raw_token = Doorkeeper::OAuth::Helpers::UniqueToken.generate
49
+ secret_strategy.store_secret(self, :token, @raw_token)
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper::Orm::ActiveRecord::Mixins
4
+ module AccessToken
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ self.table_name = "#{table_name_prefix}oauth_access_tokens#{table_name_suffix}"
9
+
10
+ include ::Doorkeeper::AccessTokenMixin
11
+
12
+ belongs_to :application, class_name: Doorkeeper.config.application_class,
13
+ inverse_of: :access_tokens,
14
+ optional: true
15
+
16
+ validates :token, presence: true, uniqueness: { case_sensitive: true }
17
+ validates :refresh_token, uniqueness: { case_sensitive: true }, if: :use_refresh_token?
18
+
19
+ # @attr_writer [Boolean, nil] use_refresh_token
20
+ # indicates the possibility of using refresh token
21
+ attr_writer :use_refresh_token
22
+
23
+ before_validation :generate_token, on: :create
24
+ before_validation :generate_refresh_token,
25
+ on: :create, if: :use_refresh_token?
26
+ end
27
+
28
+ class_methods do
29
+ # Searches for not revoked Access Tokens associated with the
30
+ # specific Resource Owner.
31
+ #
32
+ # @param resource_owner [ActiveRecord::Base]
33
+ # Resource Owner model instance
34
+ #
35
+ # @return [ActiveRecord::Relation]
36
+ # active Access Tokens for Resource Owner
37
+ #
38
+ def active_for(resource_owner)
39
+ where(resource_owner_id: resource_owner.id, revoked_at: nil)
40
+ end
41
+
42
+ def refresh_token_revoked_on_use?
43
+ column_names.include?("previous_refresh_token")
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper::Orm::ActiveRecord::Mixins
4
+ module Application
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ self.table_name = "#{table_name_prefix}oauth_applications#{table_name_suffix}"
9
+
10
+ include ::Doorkeeper::ApplicationMixin
11
+
12
+ has_many :access_grants,
13
+ foreign_key: :application_id,
14
+ dependent: :delete_all,
15
+ class_name: Doorkeeper.config.access_grant_class
16
+
17
+ has_many :access_tokens,
18
+ foreign_key: :application_id,
19
+ dependent: :delete_all,
20
+ class_name: Doorkeeper.config.access_token_class
21
+
22
+ validates :name, :secret, :uid, presence: true
23
+ validates :uid, uniqueness: { case_sensitive: true }
24
+ validates :redirect_uri, "doorkeeper/redirect_uri": true
25
+ validates :confidential, inclusion: { in: [true, false] }
26
+
27
+ validate :scopes_match_configured, if: :enforce_scopes?
28
+
29
+ before_validation :generate_uid, :generate_secret, on: :create
30
+
31
+ has_many :authorized_tokens,
32
+ -> { where(revoked_at: nil) },
33
+ foreign_key: :application_id,
34
+ class_name: Doorkeeper.config.access_token_class
35
+
36
+ has_many :authorized_applications,
37
+ through: :authorized_tokens,
38
+ source: :application
39
+
40
+ # Generates a new secret for this application, intended to be used
41
+ # for rotating the secret or in case of compromise.
42
+ #
43
+ # @return [String] new transformed secret value
44
+ #
45
+ def renew_secret
46
+ @raw_secret = Doorkeeper::OAuth::Helpers::UniqueToken.generate
47
+ secret_strategy.store_secret(self, :secret, @raw_secret)
48
+ end
49
+
50
+ # We keep a volatile copy of the raw secret for initial communication
51
+ # The stored refresh_token may be mapped and not available in cleartext.
52
+ #
53
+ # Some strategies allow restoring stored secrets (e.g. symmetric encryption)
54
+ # while hashing strategies do not, so you cannot rely on this value
55
+ # returning a present value for persisted tokens.
56
+ def plaintext_secret
57
+ if secret_strategy.allows_restoring_secrets?
58
+ secret_strategy.restore_secret(self, :secret)
59
+ else
60
+ @raw_secret
61
+ end
62
+ end
63
+
64
+ # This is the right way how we want to override ActiveRecord #to_json
65
+ #
66
+ # @return [String] entity attributes as JSON
67
+ #
68
+ def as_json(options = {})
69
+ hash = super
70
+
71
+ hash["secret"] = plaintext_secret if hash.key?("secret")
72
+ hash
73
+ end
74
+
75
+ private
76
+
77
+ def generate_uid
78
+ self.uid = Doorkeeper::OAuth::Helpers::UniqueToken.generate if uid.blank?
79
+ end
80
+
81
+ def generate_secret
82
+ return unless secret.blank?
83
+
84
+ renew_secret
85
+ end
86
+
87
+ def scopes_match_configured
88
+ if scopes.present? && !Doorkeeper::OAuth::Helpers::ScopeChecker.valid?(
89
+ scope_str: scopes.to_s,
90
+ server_scopes: Doorkeeper.config.scopes,
91
+ )
92
+ errors.add(:scopes, :not_match_configured)
93
+ end
94
+ end
95
+
96
+ def enforce_scopes?
97
+ Doorkeeper.config.enforce_configured_scopes?
98
+ end
99
+ end
100
+
101
+ class_methods do
102
+ # Returns Applications associated with active (not revoked) Access Tokens
103
+ # that are owned by the specific Resource Owner.
104
+ #
105
+ # @param resource_owner [ActiveRecord::Base]
106
+ # Resource Owner model instance
107
+ #
108
+ # @return [ActiveRecord::Relation]
109
+ # Applications authorized for the Resource Owner
110
+ #
111
+ def authorized_for(resource_owner)
112
+ resource_access_tokens = Doorkeeper.config.access_token_model.active_for(resource_owner)
113
+ where(id: resource_access_tokens.select(:application_id).distinct)
114
+ end
115
+
116
+ # Revokes AccessToken and AccessGrant records that have not been revoked and
117
+ # associated with the specific Application and Resource Owner.
118
+ #
119
+ # @param resource_owner [ActiveRecord::Base]
120
+ # instance of the Resource Owner model
121
+ #
122
+ def revoke_tokens_and_grants_for(id, resource_owner)
123
+ Doorkeeper.config.access_token_model.revoke_all_for(id, resource_owner)
124
+ Doorkeeper.config.access_grant_model.revoke_all_for(id, resource_owner)
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
4
+
5
+ module Doorkeeper
6
+ # ActiveModel validator for redirect URI validation in according
7
+ # to OAuth standards and Doorkeeper configuration.
8
+ class RedirectUriValidator < ActiveModel::EachValidator
9
+ def validate_each(record, attribute, value)
10
+ if value.blank?
11
+ return if Doorkeeper.config.allow_blank_redirect_uri?(record)
12
+
13
+ record.errors.add(attribute, :blank)
14
+ else
15
+ value.split.each do |val|
16
+ next if oob_redirect_uri?(val)
17
+
18
+ uri = ::URI.parse(val)
19
+ record.errors.add(attribute, :forbidden_uri) if forbidden_uri?(uri)
20
+ record.errors.add(attribute, :fragment_present) unless uri.fragment.nil?
21
+ record.errors.add(attribute, :unspecified_scheme) if unspecified_scheme?(uri)
22
+ record.errors.add(attribute, :relative_uri) if relative_uri?(uri)
23
+ record.errors.add(attribute, :secured_uri) if invalid_ssl_uri?(uri)
24
+ end
25
+ end
26
+ rescue URI::InvalidURIError
27
+ record.errors.add(attribute, :invalid_uri)
28
+ end
29
+
30
+ private
31
+
32
+ def oob_redirect_uri?(uri)
33
+ Doorkeeper::OAuth::NonStandard::IETF_WG_OAUTH2_OOB_METHODS.include?(uri)
34
+ end
35
+
36
+ def forbidden_uri?(uri)
37
+ Doorkeeper.config.forbid_redirect_uri.call(uri)
38
+ end
39
+
40
+ def unspecified_scheme?(uri)
41
+ return true if uri.opaque.present?
42
+
43
+ %w[localhost].include?(uri.try(:scheme))
44
+ end
45
+
46
+ def relative_uri?(uri)
47
+ uri.scheme.nil? && uri.host.nil?
48
+ end
49
+
50
+ def invalid_ssl_uri?(uri)
51
+ forces_ssl = Doorkeeper.config.force_ssl_in_redirect_uri
52
+ non_https = uri.try(:scheme) == "http"
53
+
54
+ if forces_ssl.respond_to?(:call)
55
+ forces_ssl.call(uri) && non_https
56
+ else
57
+ forces_ssl && non_https
58
+ end
59
+ end
60
+ end
61
+ end
@@ -2,20 +2,27 @@
2
2
 
3
3
  require "active_support/lazy_load_hooks"
4
4
 
5
- require "doorkeeper/orm/active_record/stale_records_cleaner"
6
-
7
5
  module Doorkeeper
8
6
  module Orm
7
+ # ActiveRecord ORM for Doorkeeper entity models.
8
+ # Consists of three main OAuth entities:
9
+ # * Access Token
10
+ # * Access Grant
11
+ # * Application (client)
12
+ #
13
+ # Do a lazy loading of all the required and configured stuff.
14
+ #
9
15
  module ActiveRecord
10
16
  def self.initialize_models!
11
17
  lazy_load do
18
+ require "doorkeeper/orm/active_record/stale_records_cleaner"
12
19
  require "doorkeeper/orm/active_record/access_grant"
13
20
  require "doorkeeper/orm/active_record/access_token"
14
21
  require "doorkeeper/orm/active_record/application"
15
22
 
16
- if Doorkeeper.configuration.active_record_options[:establish_connection]
17
- [Doorkeeper::AccessGrant, Doorkeeper::AccessToken, Doorkeeper::Application].each do |model|
18
- options = Doorkeeper.configuration.active_record_options[:establish_connection]
23
+ if Doorkeeper.config.active_record_options[:establish_connection]
24
+ Doorkeeper::Orm::ActiveRecord.models.each do |model|
25
+ options = Doorkeeper.config.active_record_options[:establish_connection]
19
26
  model.establish_connection(options)
20
27
  end
21
28
  end
@@ -26,13 +33,21 @@ module Doorkeeper
26
33
  lazy_load do
27
34
  require "doorkeeper/models/concerns/ownership"
28
35
 
29
- Doorkeeper::Application.send :include, Doorkeeper::Models::Ownership
36
+ Doorkeeper.config.application_model.send :include, Doorkeeper::Models::Ownership
30
37
  end
31
38
  end
32
39
 
33
40
  def self.lazy_load(&block)
34
41
  ActiveSupport.on_load(:active_record, {}, &block)
35
42
  end
43
+
44
+ def self.models
45
+ [
46
+ Doorkeeper::AccessGrant,
47
+ Doorkeeper::AccessToken,
48
+ Doorkeeper::Application,
49
+ ]
50
+ end
36
51
  end
37
52
  end
38
53
  end