doorkeeper 4.4.3 → 5.0.3

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 (223) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/.gitlab-ci.yml +16 -0
  4. data/.travis.yml +7 -0
  5. data/Appraisals +2 -2
  6. data/Dangerfile +64 -0
  7. data/Gemfile +1 -1
  8. data/NEWS.md +98 -8
  9. data/README.md +110 -12
  10. data/Rakefile +6 -0
  11. data/UPGRADE.md +2 -0
  12. data/app/assets/stylesheets/doorkeeper/admin/application.css +2 -2
  13. data/app/controllers/doorkeeper/application_controller.rb +6 -3
  14. data/app/controllers/doorkeeper/application_metal_controller.rb +6 -0
  15. data/app/controllers/doorkeeper/applications_controller.rb +46 -24
  16. data/app/controllers/doorkeeper/authorizations_controller.rb +55 -12
  17. data/app/controllers/doorkeeper/authorized_applications_controller.rb +21 -2
  18. data/app/controllers/doorkeeper/token_info_controller.rb +2 -0
  19. data/app/controllers/doorkeeper/tokens_controller.rb +4 -6
  20. data/app/helpers/doorkeeper/dashboard_helper.rb +9 -7
  21. data/app/validators/redirect_uri_validator.rb +5 -2
  22. data/app/views/doorkeeper/applications/_delete_form.html.erb +3 -1
  23. data/app/views/doorkeeper/applications/_form.html.erb +25 -24
  24. data/app/views/doorkeeper/applications/edit.html.erb +1 -1
  25. data/app/views/doorkeeper/applications/index.html.erb +17 -7
  26. data/app/views/doorkeeper/applications/new.html.erb +1 -1
  27. data/app/views/doorkeeper/applications/show.html.erb +6 -6
  28. data/app/views/doorkeeper/authorizations/error.html.erb +1 -1
  29. data/app/views/doorkeeper/authorizations/new.html.erb +4 -0
  30. data/app/views/layouts/doorkeeper/admin.html.erb +15 -15
  31. data/config/locales/en.yml +10 -1
  32. data/doorkeeper.gemspec +25 -26
  33. data/gemfiles/rails_5_2.gemfile +1 -1
  34. data/gemfiles/rails_master.gemfile +4 -1
  35. data/lib/doorkeeper/config.rb +81 -40
  36. data/lib/doorkeeper/engine.rb +6 -0
  37. data/lib/doorkeeper/errors.rb +17 -3
  38. data/lib/doorkeeper/grape/authorization_decorator.rb +2 -0
  39. data/lib/doorkeeper/grape/helpers.rb +3 -1
  40. data/lib/doorkeeper/helpers/controller.rb +9 -2
  41. data/lib/doorkeeper/models/access_grant_mixin.rb +73 -0
  42. data/lib/doorkeeper/models/access_token_mixin.rb +44 -25
  43. data/lib/doorkeeper/models/application_mixin.rb +2 -0
  44. data/lib/doorkeeper/models/concerns/accessible.rb +2 -0
  45. data/lib/doorkeeper/models/concerns/expirable.rb +2 -0
  46. data/lib/doorkeeper/models/concerns/orderable.rb +2 -0
  47. data/lib/doorkeeper/models/concerns/ownership.rb +2 -0
  48. data/lib/doorkeeper/models/concerns/revocable.rb +2 -0
  49. data/lib/doorkeeper/models/concerns/scopes.rb +3 -1
  50. data/lib/doorkeeper/oauth/authorization/code.rb +33 -8
  51. data/lib/doorkeeper/oauth/authorization/context.rb +17 -0
  52. data/lib/doorkeeper/oauth/authorization/token.rb +38 -14
  53. data/lib/doorkeeper/oauth/authorization/uri_builder.rb +2 -0
  54. data/lib/doorkeeper/oauth/authorization_code_request.rb +29 -2
  55. data/lib/doorkeeper/oauth/base_request.rb +22 -9
  56. data/lib/doorkeeper/oauth/base_response.rb +2 -0
  57. data/lib/doorkeeper/oauth/client/credentials.rb +3 -1
  58. data/lib/doorkeeper/oauth/client.rb +1 -1
  59. data/lib/doorkeeper/oauth/client_credentials/creator.rb +4 -1
  60. data/lib/doorkeeper/oauth/client_credentials/issuer.rb +7 -2
  61. data/lib/doorkeeper/oauth/client_credentials/validation.rb +5 -5
  62. data/lib/doorkeeper/oauth/client_credentials_request.rb +1 -3
  63. data/lib/doorkeeper/oauth/code_request.rb +2 -0
  64. data/lib/doorkeeper/oauth/code_response.rb +2 -0
  65. data/lib/doorkeeper/oauth/error.rb +2 -0
  66. data/lib/doorkeeper/oauth/error_response.rb +21 -3
  67. data/lib/doorkeeper/oauth/forbidden_token_response.rb +9 -2
  68. data/lib/doorkeeper/oauth/helpers/scope_checker.rb +2 -8
  69. data/lib/doorkeeper/oauth/helpers/unique_token.rb +2 -0
  70. data/lib/doorkeeper/oauth/helpers/uri_checker.rb +5 -2
  71. data/lib/doorkeeper/oauth/invalid_token_response.rb +18 -0
  72. data/lib/doorkeeper/oauth/password_access_token_request.rb +9 -4
  73. data/lib/doorkeeper/oauth/pre_authorization.rb +43 -11
  74. data/lib/doorkeeper/oauth/refresh_token_request.rb +16 -3
  75. data/lib/doorkeeper/oauth/scopes.rb +3 -1
  76. data/lib/doorkeeper/oauth/token.rb +7 -2
  77. data/lib/doorkeeper/oauth/token_introspection.rb +4 -2
  78. data/lib/doorkeeper/oauth/token_request.rb +2 -0
  79. data/lib/doorkeeper/oauth/token_response.rb +6 -2
  80. data/lib/doorkeeper/oauth.rb +13 -0
  81. data/lib/doorkeeper/orm/active_record/application.rb +75 -12
  82. data/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +26 -0
  83. data/lib/doorkeeper/orm/active_record.rb +4 -0
  84. data/lib/doorkeeper/rails/helpers.rb +6 -4
  85. data/lib/doorkeeper/rails/routes/mapper.rb +2 -0
  86. data/lib/doorkeeper/rails/routes/mapping.rb +2 -0
  87. data/lib/doorkeeper/rails/routes.rb +23 -8
  88. data/lib/doorkeeper/rake/db.rake +40 -0
  89. data/lib/doorkeeper/rake/setup.rake +6 -0
  90. data/lib/doorkeeper/rake.rb +14 -0
  91. data/lib/doorkeeper/request/authorization_code.rb +1 -1
  92. data/lib/doorkeeper/request/client_credentials.rb +1 -1
  93. data/lib/doorkeeper/request/code.rb +1 -1
  94. data/lib/doorkeeper/request/password.rb +1 -1
  95. data/lib/doorkeeper/request/refresh_token.rb +1 -1
  96. data/lib/doorkeeper/request/strategy.rb +2 -0
  97. data/lib/doorkeeper/request/token.rb +1 -1
  98. data/lib/doorkeeper/request.rb +29 -34
  99. data/lib/doorkeeper/server.rb +2 -0
  100. data/lib/doorkeeper/stale_records_cleaner.rb +20 -0
  101. data/lib/doorkeeper/validations.rb +2 -0
  102. data/lib/doorkeeper/version.rb +6 -24
  103. data/lib/doorkeeper.rb +20 -17
  104. data/lib/generators/doorkeeper/application_owner_generator.rb +23 -18
  105. data/lib/generators/doorkeeper/confidential_applications_generator.rb +32 -0
  106. data/lib/generators/doorkeeper/install_generator.rb +17 -9
  107. data/lib/generators/doorkeeper/migration_generator.rb +23 -18
  108. data/lib/generators/doorkeeper/pkce_generator.rb +32 -0
  109. data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +29 -24
  110. data/lib/generators/doorkeeper/templates/add_confidential_to_applications.rb.erb +13 -0
  111. data/lib/generators/doorkeeper/templates/enable_pkce_migration.rb.erb +6 -0
  112. data/lib/generators/doorkeeper/templates/initializer.rb +96 -13
  113. data/lib/generators/doorkeeper/templates/migration.rb.erb +2 -3
  114. data/lib/generators/doorkeeper/views_generator.rb +3 -1
  115. data/spec/controllers/application_metal_controller_spec.rb +50 -0
  116. data/spec/controllers/applications_controller_spec.rb +123 -14
  117. data/spec/controllers/authorizations_controller_spec.rb +334 -51
  118. data/spec/controllers/protected_resources_controller_spec.rb +60 -18
  119. data/spec/controllers/token_info_controller_spec.rb +4 -12
  120. data/spec/controllers/tokens_controller_spec.rb +17 -20
  121. data/spec/dummy/Rakefile +1 -1
  122. data/spec/dummy/app/assets/config/manifest.js +2 -0
  123. data/spec/dummy/app/controllers/custom_authorizations_controller.rb +1 -1
  124. data/spec/dummy/app/controllers/home_controller.rb +1 -2
  125. data/spec/dummy/config/application.rb +1 -1
  126. data/spec/dummy/config/boot.rb +2 -4
  127. data/spec/dummy/config/environment.rb +1 -1
  128. data/spec/dummy/config/environments/test.rb +5 -6
  129. data/spec/dummy/config/initializers/doorkeeper.rb +12 -6
  130. data/spec/dummy/config/initializers/new_framework_defaults.rb +2 -0
  131. data/spec/dummy/config/initializers/secret_token.rb +1 -1
  132. data/spec/dummy/config/routes.rb +3 -42
  133. data/spec/dummy/config.ru +1 -1
  134. data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +4 -4
  135. data/spec/dummy/db/migrate/20151223200000_add_owner_to_application.rb +1 -1
  136. data/spec/dummy/db/migrate/20170822064514_enable_pkce.rb +6 -0
  137. data/spec/dummy/db/migrate/{20180210183654_add_confidential_to_application.rb → 20180210183654_add_confidential_to_applications.rb} +1 -1
  138. data/spec/dummy/db/schema.rb +36 -36
  139. data/spec/dummy/script/rails +4 -3
  140. data/spec/factories.rb +6 -6
  141. data/spec/generators/application_owner_generator_spec.rb +1 -1
  142. data/spec/generators/confidential_applications_generator_spec.rb +45 -0
  143. data/spec/generators/install_generator_spec.rb +5 -2
  144. data/spec/generators/migration_generator_spec.rb +1 -1
  145. data/spec/generators/pkce_generator_spec.rb +43 -0
  146. data/spec/generators/previous_refresh_token_generator_spec.rb +1 -1
  147. data/spec/generators/templates/routes.rb +0 -1
  148. data/spec/generators/views_generator_spec.rb +2 -2
  149. data/spec/grape/grape_integration_spec.rb +2 -2
  150. data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +1 -1
  151. data/spec/lib/config_spec.rb +105 -39
  152. data/spec/lib/doorkeeper_spec.rb +6 -131
  153. data/spec/lib/models/expirable_spec.rb +0 -3
  154. data/spec/lib/models/revocable_spec.rb +0 -2
  155. data/spec/lib/models/scopes_spec.rb +0 -4
  156. data/spec/lib/oauth/authorization/uri_builder_spec.rb +0 -4
  157. data/spec/lib/oauth/authorization_code_request_spec.rb +17 -7
  158. data/spec/lib/oauth/base_request_spec.rb +49 -11
  159. data/spec/lib/oauth/base_response_spec.rb +1 -1
  160. data/spec/lib/oauth/client/credentials_spec.rb +2 -4
  161. data/spec/lib/oauth/client_credentials/creator_spec.rb +5 -1
  162. data/spec/lib/oauth/client_credentials/issuer_spec.rb +24 -7
  163. data/spec/lib/oauth/client_credentials/validation_spec.rb +4 -4
  164. data/spec/lib/oauth/client_credentials_integration_spec.rb +2 -2
  165. data/spec/lib/oauth/client_credentials_request_spec.rb +3 -5
  166. data/spec/lib/oauth/client_spec.rb +0 -3
  167. data/spec/lib/oauth/code_request_spec.rb +5 -3
  168. data/spec/lib/oauth/code_response_spec.rb +1 -1
  169. data/spec/lib/oauth/error_response_spec.rb +0 -3
  170. data/spec/lib/oauth/error_spec.rb +0 -2
  171. data/spec/lib/oauth/forbidden_token_response_spec.rb +1 -4
  172. data/spec/lib/oauth/helpers/scope_checker_spec.rb +8 -11
  173. data/spec/lib/oauth/helpers/unique_token_spec.rb +0 -1
  174. data/spec/lib/oauth/helpers/uri_checker_spec.rb +22 -13
  175. data/spec/lib/oauth/invalid_token_response_spec.rb +1 -4
  176. data/spec/lib/oauth/password_access_token_request_spec.rb +53 -6
  177. data/spec/lib/oauth/pre_authorization_spec.rb +33 -4
  178. data/spec/lib/oauth/refresh_token_request_spec.rb +22 -14
  179. data/spec/lib/oauth/scopes_spec.rb +0 -3
  180. data/spec/lib/oauth/token_request_spec.rb +8 -9
  181. data/spec/lib/oauth/token_response_spec.rb +0 -1
  182. data/spec/lib/oauth/token_spec.rb +40 -14
  183. data/spec/lib/request/strategy_spec.rb +0 -1
  184. data/spec/lib/server_spec.rb +7 -7
  185. data/spec/lib/stale_records_cleaner_spec.rb +89 -0
  186. data/spec/models/doorkeeper/access_grant_spec.rb +44 -1
  187. data/spec/models/doorkeeper/access_token_spec.rb +80 -32
  188. data/spec/models/doorkeeper/application_spec.rb +293 -221
  189. data/spec/requests/applications/applications_request_spec.rb +134 -1
  190. data/spec/requests/applications/authorized_applications_spec.rb +1 -1
  191. data/spec/requests/endpoints/authorization_spec.rb +3 -3
  192. data/spec/requests/endpoints/token_spec.rb +7 -5
  193. data/spec/requests/flows/authorization_code_errors_spec.rb +2 -2
  194. data/spec/requests/flows/authorization_code_spec.rb +258 -2
  195. data/spec/requests/flows/client_credentials_spec.rb +46 -6
  196. data/spec/requests/flows/implicit_grant_errors_spec.rb +3 -3
  197. data/spec/requests/flows/implicit_grant_spec.rb +38 -11
  198. data/spec/requests/flows/password_spec.rb +61 -3
  199. data/spec/requests/flows/refresh_token_spec.rb +59 -2
  200. data/spec/requests/flows/revoke_token_spec.rb +20 -20
  201. data/spec/requests/flows/skip_authorization_spec.rb +16 -11
  202. data/spec/requests/protected_resources/metal_spec.rb +1 -1
  203. data/spec/requests/protected_resources/private_api_spec.rb +3 -3
  204. data/spec/routing/custom_controller_routes_spec.rb +59 -7
  205. data/spec/routing/default_routes_spec.rb +2 -2
  206. data/spec/routing/scoped_routes_spec.rb +16 -2
  207. data/spec/spec_helper.rb +54 -3
  208. data/spec/spec_helper_integration.rb +2 -74
  209. data/spec/support/dependencies/{factory_girl.rb → factory_bot.rb} +0 -0
  210. data/spec/support/doorkeeper_rspec.rb +20 -0
  211. data/spec/support/helpers/authorization_request_helper.rb +4 -4
  212. data/spec/support/helpers/model_helper.rb +8 -4
  213. data/spec/support/helpers/request_spec_helper.rb +10 -2
  214. data/spec/support/helpers/url_helper.rb +18 -14
  215. data/spec/support/http_method_shim.rb +12 -16
  216. data/spec/support/shared/controllers_shared_context.rb +56 -0
  217. data/spec/validators/redirect_uri_validator_spec.rb +9 -3
  218. data/spec/version/version_spec.rb +3 -3
  219. data/vendor/assets/stylesheets/doorkeeper/bootstrap.min.css +4 -5
  220. metadata +54 -35
  221. data/lib/generators/doorkeeper/add_client_confidentiality_generator.rb +0 -31
  222. data/lib/generators/doorkeeper/templates/add_confidential_to_application_migration.rb.erb +0 -11
  223. data/spec/controllers/application_metal_controller.rb +0 -10
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OAuth
3
5
  class PreAuthorization
@@ -7,17 +9,21 @@ module Doorkeeper
7
9
  validate :client, error: :invalid_client
8
10
  validate :scopes, error: :invalid_scope
9
11
  validate :redirect_uri, error: :invalid_redirect_uri
12
+ validate :code_challenge_method, error: :invalid_code_challenge_method
10
13
 
11
- attr_accessor :server, :client, :response_type, :redirect_uri, :state
14
+ attr_accessor :server, :client, :response_type, :redirect_uri, :state,
15
+ :code_challenge, :code_challenge_method
12
16
  attr_writer :scope
13
17
 
14
18
  def initialize(server, client, attrs = {})
15
- @server = server
16
- @client = client
17
- @response_type = attrs[:response_type]
18
- @redirect_uri = attrs[:redirect_uri]
19
- @scope = attrs[:scope]
20
- @state = attrs[:state]
19
+ @server = server
20
+ @client = client
21
+ @response_type = attrs[:response_type]
22
+ @redirect_uri = attrs[:redirect_uri]
23
+ @scope = attrs[:scope]
24
+ @state = attrs[:state]
25
+ @code_challenge = attrs[:code_challenge]
26
+ @code_challenge_method = attrs[:code_challenge_method]
21
27
  end
22
28
 
23
29
  def authorizable?
@@ -29,15 +35,36 @@ module Doorkeeper
29
35
  end
30
36
 
31
37
  def scope
32
- @scope.presence || server.default_scopes.to_s
38
+ @scope.presence || build_scopes
33
39
  end
34
40
 
35
41
  def error_response
36
42
  OAuth::ErrorResponse.from_request(self)
37
43
  end
38
44
 
45
+ def as_json(_options)
46
+ {
47
+ client_id: client.uid,
48
+ redirect_uri: redirect_uri,
49
+ state: state,
50
+ response_type: response_type,
51
+ scope: scope,
52
+ client_name: client.name,
53
+ status: I18n.t('doorkeeper.pre_authorization.status')
54
+ }
55
+ end
56
+
39
57
  private
40
58
 
59
+ def build_scopes
60
+ client_scopes = client.application.scopes
61
+ if client_scopes.blank?
62
+ server.default_scopes.to_s
63
+ else
64
+ (server.default_scopes & client_scopes).to_s
65
+ end
66
+ end
67
+
41
68
  def validate_response_type
42
69
  server.authorization_response_types.include? response_type
43
70
  end
@@ -47,7 +74,8 @@ module Doorkeeper
47
74
  end
48
75
 
49
76
  def validate_scopes
50
- return true unless scope.present?
77
+ return true if scope.blank?
78
+
51
79
  Helpers::ScopeChecker.valid?(
52
80
  scope,
53
81
  server.scopes,
@@ -55,14 +83,18 @@ module Doorkeeper
55
83
  )
56
84
  end
57
85
 
58
- # TODO: test uri should be matched against the client's one
59
86
  def validate_redirect_uri
60
87
  return false if redirect_uri.blank?
61
88
 
62
89
  Helpers::URIChecker.valid_for_authorization?(
63
- redirect_uri, client.redirect_uri
90
+ redirect_uri,
91
+ client.redirect_uri
64
92
  )
65
93
  end
94
+
95
+ def validate_code_challenge_method
96
+ !code_challenge.present? || (code_challenge_method.present? && code_challenge_method =~ /^plain$|^S256$/)
97
+ end
66
98
  end
67
99
  end
68
100
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OAuth
3
5
  class RefreshTokenRequest < BaseRequest
@@ -65,7 +67,12 @@ module Doorkeeper
65
67
  end
66
68
 
67
69
  def access_token_expires_in
68
- Authorization::Token.access_token_expires_in(server, client)
70
+ context = Authorization::Token.build_context(
71
+ client,
72
+ Doorkeeper::OAuth::REFRESH_TOKEN,
73
+ scopes
74
+ )
75
+ Authorization::Token.access_token_expires_in(server, context)
69
76
  end
70
77
 
71
78
  def validate_token_presence
@@ -77,11 +84,17 @@ module Doorkeeper
77
84
  end
78
85
 
79
86
  def validate_client
80
- !credentials || !!client
87
+ return true if credentials.blank?
88
+
89
+ client.present?
81
90
  end
82
91
 
92
+ # @see https://tools.ietf.org/html/draft-ietf-oauth-v2-22#section-1.5
93
+ #
83
94
  def validate_client_match
84
- !client || refresh_token.application_id == client.id
95
+ return true if refresh_token.application_id.blank?
96
+
97
+ client && refresh_token.application_id == client.id
85
98
  end
86
99
 
87
100
  def validate_scope
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OAuth
3
5
  class Scopes
@@ -41,7 +43,7 @@ module Doorkeeper
41
43
  end
42
44
 
43
45
  def has_scopes?(scopes)
44
- scopes.all? { |s| exists?(s) }
46
+ scopes.all? { |scope| exists?(scope) }
45
47
  end
46
48
 
47
49
  def +(other)
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OAuth
3
5
  class Token
4
6
  class << self
5
7
  def from_request(request, *methods)
6
- methods.inject(nil) do |credentials, method|
8
+ methods.inject(nil) do |_, method|
7
9
  method = self.method(method) if method.is_a?(Symbol)
8
10
  credentials = method.call(request)
9
11
  break credentials unless credentials.blank?
@@ -13,7 +15,10 @@ module Doorkeeper
13
15
  def authenticate(request, *methods)
14
16
  if (token = from_request(request, *methods))
15
17
  access_token = AccessToken.by_token(token)
16
- access_token.revoke_previous_refresh_token! if access_token
18
+ refresh_token_enabled = Doorkeeper.configuration.refresh_token_enabled?
19
+ if access_token.present? && refresh_token_enabled
20
+ access_token.revoke_previous_refresh_token!
21
+ end
17
22
  access_token
18
23
  end
19
24
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OAuth
3
5
  # RFC7662 OAuth 2.0 Token Introspection
@@ -47,12 +49,12 @@ module Doorkeeper
47
49
 
48
50
  # Client Authentication
49
51
  def authorized_client
50
- @_authorized_client ||= server.credentials && server.client
52
+ @authorized_client ||= server.credentials && server.client
51
53
  end
52
54
 
53
55
  # Bearer Token Authentication
54
56
  def authorized_token
55
- @_authorized_token ||=
57
+ @authorized_token ||=
56
58
  OAuth::Token.authenticate(server.context.request, :from_bearer_authorization)
57
59
  end
58
60
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OAuth
3
5
  class TokenRequest
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OAuth
3
5
  class TokenResponse
@@ -23,9 +25,11 @@ module Doorkeeper
23
25
  end
24
26
 
25
27
  def headers
26
- { 'Cache-Control' => 'no-store',
28
+ {
29
+ 'Cache-Control' => 'no-store',
27
30
  'Pragma' => 'no-cache',
28
- 'Content-Type' => 'application/json; charset=utf-8' }
31
+ 'Content-Type' => 'application/json; charset=utf-8'
32
+ }
29
33
  end
30
34
  end
31
35
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper
4
+ module OAuth
5
+ GRANT_TYPES = [
6
+ AUTHORIZATION_CODE = 'authorization_code'.freeze,
7
+ IMPLICIT = 'implicit'.freeze,
8
+ PASSWORD = 'password'.freeze,
9
+ CLIENT_CREDENTIALS = 'client_credentials'.freeze,
10
+ REFRESH_TOKEN = 'refresh_token'.freeze
11
+ ].freeze
12
+ end
13
+ end
@@ -13,6 +13,8 @@ module Doorkeeper
13
13
  validates :redirect_uri, redirect_uri: true
14
14
  validates :confidential, inclusion: { in: [true, false] }
15
15
 
16
+ validate :scopes_match_configured, if: :enforce_scopes?
17
+
16
18
  before_validation :generate_uid, :generate_secret, on: :create
17
19
 
18
20
  has_many :authorized_tokens, -> { where(revoked_at: nil) }, class_name: 'AccessToken'
@@ -32,20 +34,38 @@ module Doorkeeper
32
34
  where(id: resource_access_tokens.select(:application_id).distinct)
33
35
  end
34
36
 
35
- # Fallback to existing, default behaviour of assuming all apps to be
36
- # confidential if the migration hasn't been run
37
- def confidential
38
- return super if self.class.supports_confidentiality?
39
- ActiveSupport::Deprecation.warn 'You are susceptible to security bug ' \
40
- 'CVE-2018-1000211. Please follow instructions outlined in ' \
41
- 'Doorkeeper::CVE_2018_1000211_WARNING'
42
- true
37
+ # Revokes AccessToken and AccessGrant records that have not been revoked and
38
+ # associated with the specific Application and Resource Owner.
39
+ #
40
+ # @param resource_owner [ActiveRecord::Base]
41
+ # instance of the Resource Owner model
42
+ #
43
+ def self.revoke_tokens_and_grants_for(id, resource_owner)
44
+ AccessToken.revoke_all_for(id, resource_owner)
45
+ AccessGrant.revoke_all_for(id, resource_owner)
43
46
  end
44
47
 
45
- alias_method :confidential?, :confidential
46
-
47
- def self.supports_confidentiality?
48
- column_names.include?('confidential')
48
+ # Represents client as set of it's attributes in JSON format.
49
+ # This is the right way how we want to override ActiveRecord #to_json.
50
+ #
51
+ # Respects privacy settings and serializes minimum set of attributes
52
+ # for public/private clients and full set for authorized owners.
53
+ #
54
+ # @return [Hash] entity attributes for JSON
55
+ #
56
+ def as_json(options = {})
57
+ # if application belongs to some owner we need to check if it's the same as
58
+ # the one passed in the options or check if we render the client as an owner
59
+ if (respond_to?(:owner) && owner && owner == options[:current_resource_owner]) ||
60
+ options[:as_owner]
61
+ # Owners can see all the client attributes, fallback to ActiveModel serialization
62
+ super
63
+ else
64
+ # if application has no owner or it's owner doesn't match one from the options
65
+ # we render only minimum set of attributes that could be exposed to a public
66
+ only = extract_serializable_attributes(options)
67
+ super(options.merge(only: only))
68
+ end
49
69
  end
50
70
 
51
71
  private
@@ -57,5 +77,48 @@ module Doorkeeper
57
77
  def generate_secret
58
78
  self.secret = UniqueToken.generate if secret.blank?
59
79
  end
80
+
81
+ def scopes_match_configured
82
+ if scopes.present? &&
83
+ !ScopeChecker.valid?(scopes.to_s, Doorkeeper.configuration.scopes)
84
+ errors.add(:scopes, :not_match_configured)
85
+ end
86
+ end
87
+
88
+ def enforce_scopes?
89
+ Doorkeeper.configuration.enforce_configured_scopes?
90
+ end
91
+
92
+ # Helper method to extract collection of serializable attribute names
93
+ # considering serialization options (like `only`, `except` and so on).
94
+ #
95
+ # @param options [Hash] serialization options
96
+ #
97
+ # @return [Array<String>]
98
+ # collection of attributes to be serialized using #as_json
99
+ #
100
+ def extract_serializable_attributes(options = {})
101
+ opts = options.try(:dup) || {}
102
+ only = Array.wrap(opts[:only]).map(&:to_s)
103
+
104
+ only = if only.blank?
105
+ serializable_attributes
106
+ else
107
+ only & serializable_attributes
108
+ end
109
+
110
+ only -= Array.wrap(opts[:except]).map(&:to_s) if opts.key?(:except)
111
+ only.uniq
112
+ end
113
+
114
+ # Collection of attributes that could be serialized for public.
115
+ # Override this method if you need additional attributes to be serialized.
116
+ #
117
+ # @return [Array<String>] collection of serializable attributes
118
+ def serializable_attributes
119
+ attributes = %w[id name created_at]
120
+ attributes << "uid" unless confidential?
121
+ attributes
122
+ end
60
123
  end
61
124
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper
4
+ module Orm
5
+ module ActiveRecord
6
+ class StaleRecordsCleaner
7
+ def initialize(base_scope)
8
+ @base_scope = base_scope
9
+ end
10
+
11
+ def clean_revoked
12
+ table = @base_scope.arel_table
13
+ @base_scope.where.not(revoked_at: nil)
14
+ .where(table[:revoked_at].lt(Time.current))
15
+ .delete_all
16
+ end
17
+
18
+ def clean_expired(ttl)
19
+ table = @base_scope.arel_table
20
+ @base_scope.where(table[:created_at].lt(Time.current - ttl))
21
+ .delete_all
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,5 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/lazy_load_hooks'
2
4
 
5
+ require 'doorkeeper/orm/active_record/stale_records_cleaner'
6
+
3
7
  module Doorkeeper
4
8
  module Orm
5
9
  module ActiveRecord
@@ -1,12 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module Rails
3
5
  module Helpers
4
6
  def doorkeeper_authorize!(*scopes)
5
7
  @_doorkeeper_scopes = scopes.presence || Doorkeeper.configuration.default_scopes
6
8
 
7
- unless valid_doorkeeper_token?
8
- doorkeeper_render_error
9
- end
9
+ doorkeeper_render_error unless valid_doorkeeper_token?
10
10
  end
11
11
 
12
12
  def doorkeeper_unauthorized_render_options(**); end
@@ -21,6 +21,8 @@ module Doorkeeper
21
21
 
22
22
  def doorkeeper_render_error
23
23
  error = doorkeeper_error
24
+ error.raise_exception! if Doorkeeper.configuration.raise_on_errors?
25
+
24
26
  headers.merge!(error.headers.reject { |k| k == "Content-Type" })
25
27
  doorkeeper_render_error_with(error)
26
28
  end
@@ -68,7 +70,7 @@ module Doorkeeper
68
70
  end
69
71
 
70
72
  def doorkeeper_token
71
- @_doorkeeper_token ||= OAuth::Token.authenticate(
73
+ @doorkeeper_token ||= OAuth::Token.authenticate(
72
74
  request,
73
75
  *Doorkeeper.configuration.access_token_methods
74
76
  )
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module Rails
3
5
  class Routes # :nodoc:
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module Rails
3
5
  class Routes # :nodoc:
@@ -1,9 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'doorkeeper/rails/routes/mapping'
2
4
  require 'doorkeeper/rails/routes/mapper'
3
5
 
4
6
  module Doorkeeper
5
7
  module Rails
6
8
  class Routes # :nodoc:
9
+ mattr_reader :mapping do
10
+ {}
11
+ end
12
+
7
13
  module Helper
8
14
  def use_doorkeeper(options = {}, &block)
9
15
  Doorkeeper::Rails::Routes.new(self, &block).generate_routes!(options)
@@ -19,6 +25,10 @@ module Doorkeeper
19
25
  def initialize(routes, &block)
20
26
  @routes = routes
21
27
  @mapping = Mapper.new.map(&block)
28
+
29
+ if Doorkeeper.configuration.api_only
30
+ @mapping.skips.push(:applications, :authorized_applications)
31
+ end
22
32
  end
23
33
 
24
34
  def generate_routes!(options)
@@ -36,7 +46,11 @@ module Doorkeeper
36
46
  private
37
47
 
38
48
  def map_route(name, method)
39
- send(method, @mapping[name]) unless @mapping.skipped?(name)
49
+ return if @mapping.skipped?(name)
50
+
51
+ send(method, @mapping[name])
52
+
53
+ mapping[name] = @mapping[name]
40
54
  end
41
55
 
42
56
  def authorization_routes(mapping)
@@ -47,7 +61,7 @@ module Doorkeeper
47
61
  as: mapping[:as],
48
62
  controller: mapping[:controllers]
49
63
  ) do
50
- routes.get native_authorization_code_route, action: :show, on: :member
64
+ routes.get '/native', action: :show, on: :member
51
65
  routes.get '/', action: :new, on: :member
52
66
  end
53
67
  end
@@ -79,15 +93,16 @@ module Doorkeeper
79
93
  end
80
94
 
81
95
  def application_routes(mapping)
82
- routes.resources :doorkeeper_applications, controller: mapping[:controllers], as: :applications, path: 'applications'
96
+ routes.resources :doorkeeper_applications,
97
+ controller: mapping[:controllers],
98
+ as: :applications,
99
+ path: 'applications'
83
100
  end
84
101
 
85
102
  def authorized_applications_routes(mapping)
86
- routes.resources :authorized_applications, only: %i[index destroy], controller: mapping[:controllers]
87
- end
88
-
89
- def native_authorization_code_route
90
- Doorkeeper.configuration.native_authorization_code_route
103
+ routes.resources :authorized_applications,
104
+ only: %i[index destroy],
105
+ controller: mapping[:controllers]
91
106
  end
92
107
  end
93
108
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :doorkeeper do
4
+ namespace :db do
5
+ desc 'Removes stale data from doorkeeper related database tables'
6
+ task cleanup: [
7
+ 'doorkeeper:db:cleanup:revoked_tokens',
8
+ 'doorkeeper:db:cleanup:expired_tokens',
9
+ 'doorkeeper:db:cleanup:revoked_grants',
10
+ 'doorkeeper:db:cleanup:expired_grants'
11
+ ]
12
+
13
+ namespace :cleanup do
14
+ desc 'Removes stale access tokens'
15
+ task revoked_tokens: 'doorkeeper:setup' do
16
+ cleaner = Doorkeeper::StaleRecordsCleaner.new(Doorkeeper::AccessToken)
17
+ cleaner.clean_revoked
18
+ end
19
+
20
+ desc 'Removes expired (TTL passed) access tokens'
21
+ task expired_tokens: 'doorkeeper:setup' do
22
+ expirable_tokens = Doorkeeper::AccessToken.where(refresh_token: nil)
23
+ cleaner = Doorkeeper::StaleRecordsCleaner.new(expirable_tokens)
24
+ cleaner.clean_expired(Doorkeeper.configuration.access_token_expires_in)
25
+ end
26
+
27
+ desc 'Removes stale access grants'
28
+ task revoked_grants: 'doorkeeper:setup' do
29
+ cleaner = Doorkeeper::StaleRecordsCleaner.new(Doorkeeper::AccessGrant)
30
+ cleaner.clean_revoked
31
+ end
32
+
33
+ desc 'Removes expired (TTL passed) access grants'
34
+ task expired_grants: 'doorkeeper:setup' do
35
+ cleaner = Doorkeeper::StaleRecordsCleaner.new(Doorkeeper::AccessGrant)
36
+ cleaner.clean_expired(Doorkeeper.configuration.authorization_code_expires_in)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :doorkeeper do
4
+ task setup: :environment do
5
+ end
6
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper
4
+ module Rake
5
+ class << self
6
+ def load_tasks
7
+ glob = File.join(File.absolute_path(__dir__), 'rake', '*.rake')
8
+ Dir[glob].each do |rake_file|
9
+ load rake_file
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -1,4 +1,4 @@
1
- require 'doorkeeper/request/strategy'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Doorkeeper
4
4
  module Request
@@ -1,4 +1,4 @@
1
- require 'doorkeeper/request/strategy'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Doorkeeper
4
4
  module Request
@@ -1,4 +1,4 @@
1
- require 'doorkeeper/request/strategy'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Doorkeeper
4
4
  module Request
@@ -1,4 +1,4 @@
1
- require 'doorkeeper/request/strategy'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Doorkeeper
4
4
  module Request
@@ -1,4 +1,4 @@
1
- require 'doorkeeper/request/strategy'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Doorkeeper
4
4
  module Request
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module Request
3
5
  class Strategy
@@ -1,4 +1,4 @@
1
- require 'doorkeeper/request/strategy'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Doorkeeper
4
4
  module Request