doorkeeper 3.1.0 → 4.4.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 (195) hide show
  1. checksums.yaml +4 -4
  2. data/.coveralls.yml +1 -0
  3. data/.github/ISSUE_TEMPLATE.md +25 -0
  4. data/.github/PULL_REQUEST_TEMPLATE.md +17 -0
  5. data/.gitignore +6 -1
  6. data/.hound.yml +2 -13
  7. data/.rubocop.yml +17 -0
  8. data/.travis.yml +26 -10
  9. data/Appraisals +18 -0
  10. data/CODE_OF_CONDUCT.md +46 -0
  11. data/CONTRIBUTING.md +2 -0
  12. data/Gemfile +5 -5
  13. data/NEWS.md +141 -2
  14. data/README.md +149 -66
  15. data/RELEASING.md +5 -12
  16. data/Rakefile +1 -1
  17. data/SECURITY.md +15 -0
  18. data/app/controllers/doorkeeper/application_controller.rb +4 -6
  19. data/app/controllers/doorkeeper/application_metal_controller.rb +3 -2
  20. data/app/controllers/doorkeeper/applications_controller.rb +18 -8
  21. data/app/controllers/doorkeeper/authorizations_controller.rb +1 -1
  22. data/app/controllers/doorkeeper/authorized_applications_controller.rb +1 -1
  23. data/app/controllers/doorkeeper/tokens_controller.rb +62 -15
  24. data/app/helpers/doorkeeper/dashboard_helper.rb +14 -10
  25. data/app/validators/redirect_uri_validator.rb +12 -2
  26. data/app/views/doorkeeper/applications/_delete_form.html.erb +1 -2
  27. data/app/views/doorkeeper/applications/_form.html.erb +13 -2
  28. data/app/views/doorkeeper/applications/index.html.erb +2 -0
  29. data/app/views/doorkeeper/applications/show.html.erb +4 -1
  30. data/app/views/doorkeeper/authorizations/new.html.erb +1 -1
  31. data/app/views/doorkeeper/authorized_applications/_delete_form.html.erb +1 -2
  32. data/app/views/doorkeeper/authorized_applications/index.html.erb +0 -1
  33. data/app/views/layouts/doorkeeper/admin.html.erb +1 -1
  34. data/config/locales/en.yml +12 -7
  35. data/doorkeeper.gemspec +16 -11
  36. data/gemfiles/rails_4_2.gemfile +13 -0
  37. data/gemfiles/rails_5_0.gemfile +12 -0
  38. data/gemfiles/rails_5_1.gemfile +12 -0
  39. data/gemfiles/rails_5_2.gemfile +12 -0
  40. data/gemfiles/rails_master.gemfile +14 -0
  41. data/lib/doorkeeper/config.rb +119 -46
  42. data/lib/doorkeeper/engine.rb +11 -7
  43. data/lib/doorkeeper/errors.rb +18 -0
  44. data/lib/doorkeeper/grape/helpers.rb +14 -8
  45. data/lib/doorkeeper/helpers/controller.rb +8 -19
  46. data/lib/doorkeeper/models/access_grant_mixin.rb +10 -21
  47. data/lib/doorkeeper/models/access_token_mixin.rb +147 -43
  48. data/lib/doorkeeper/models/application_mixin.rb +33 -35
  49. data/lib/doorkeeper/models/concerns/accessible.rb +4 -0
  50. data/lib/doorkeeper/models/concerns/expirable.rb +15 -5
  51. data/lib/doorkeeper/models/concerns/orderable.rb +13 -0
  52. data/lib/doorkeeper/models/concerns/ownership.rb +6 -1
  53. data/lib/doorkeeper/models/concerns/revocable.rb +37 -2
  54. data/lib/doorkeeper/oauth/authorization/token.rb +22 -18
  55. data/lib/doorkeeper/oauth/authorization/uri_builder.rb +20 -18
  56. data/lib/doorkeeper/oauth/authorization_code_request.rb +7 -5
  57. data/lib/doorkeeper/oauth/{request_concern.rb → base_request.rb} +9 -2
  58. data/lib/doorkeeper/oauth/base_response.rb +29 -0
  59. data/lib/doorkeeper/oauth/client/credentials.rb +21 -8
  60. data/lib/doorkeeper/oauth/client.rb +2 -3
  61. data/lib/doorkeeper/oauth/client_credentials/creator.rb +1 -1
  62. data/lib/doorkeeper/oauth/client_credentials/issuer.rb +3 -2
  63. data/lib/doorkeeper/oauth/client_credentials/validation.rb +1 -1
  64. data/lib/doorkeeper/oauth/client_credentials_request.rb +8 -8
  65. data/lib/doorkeeper/oauth/code_response.rb +16 -16
  66. data/lib/doorkeeper/oauth/error.rb +2 -2
  67. data/lib/doorkeeper/oauth/error_response.rb +10 -10
  68. data/lib/doorkeeper/oauth/forbidden_token_response.rb +1 -1
  69. data/lib/doorkeeper/oauth/helpers/scope_checker.rb +1 -1
  70. data/lib/doorkeeper/oauth/helpers/uri_checker.rb +17 -1
  71. data/lib/doorkeeper/oauth/invalid_token_response.rb +5 -4
  72. data/lib/doorkeeper/oauth/password_access_token_request.rb +8 -13
  73. data/lib/doorkeeper/oauth/pre_authorization.rb +5 -3
  74. data/lib/doorkeeper/oauth/refresh_token_request.rb +23 -14
  75. data/lib/doorkeeper/oauth/scopes.rb +18 -8
  76. data/lib/doorkeeper/oauth/token.rb +20 -21
  77. data/lib/doorkeeper/oauth/token_introspection.rb +128 -0
  78. data/lib/doorkeeper/oauth/token_request.rb +1 -2
  79. data/lib/doorkeeper/oauth/token_response.rb +1 -1
  80. data/lib/doorkeeper/orm/active_record/access_grant.rb +27 -0
  81. data/lib/doorkeeper/orm/active_record/access_token.rb +34 -8
  82. data/lib/doorkeeper/orm/active_record/application.rb +48 -11
  83. data/lib/doorkeeper/orm/active_record.rb +17 -22
  84. data/lib/doorkeeper/rails/helpers.rb +6 -9
  85. data/lib/doorkeeper/rails/routes/mapper.rb +4 -4
  86. data/lib/doorkeeper/rails/routes/mapping.rb +1 -1
  87. data/lib/doorkeeper/rails/routes.rb +17 -11
  88. data/lib/doorkeeper/request/authorization_code.rb +7 -1
  89. data/lib/doorkeeper/request/password.rb +2 -2
  90. data/lib/doorkeeper/request/refresh_token.rb +1 -1
  91. data/lib/doorkeeper/request.rb +7 -1
  92. data/lib/doorkeeper/server.rb +0 -8
  93. data/lib/doorkeeper/validations.rb +3 -2
  94. data/lib/doorkeeper/version.rb +34 -1
  95. data/lib/doorkeeper.rb +10 -2
  96. data/lib/generators/doorkeeper/add_client_confidentiality_generator.rb +31 -0
  97. data/lib/generators/doorkeeper/application_owner_generator.rb +11 -2
  98. data/lib/generators/doorkeeper/migration_generator.rb +13 -1
  99. data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +35 -0
  100. data/lib/generators/doorkeeper/templates/add_confidential_to_application_migration.rb.erb +11 -0
  101. data/{spec/dummy/db/migrate/20130902175349_add_owner_to_application.rb → lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb.erb} +1 -1
  102. data/lib/generators/doorkeeper/templates/add_previous_refresh_token_to_access_tokens.rb.erb +11 -0
  103. data/lib/generators/doorkeeper/templates/initializer.rb +38 -6
  104. data/lib/generators/doorkeeper/templates/migration.rb.erb +69 -0
  105. data/spec/controllers/application_metal_controller.rb +10 -0
  106. data/spec/controllers/applications_controller_spec.rb +15 -4
  107. data/spec/controllers/authorizations_controller_spec.rb +74 -27
  108. data/spec/controllers/protected_resources_controller_spec.rb +70 -32
  109. data/spec/controllers/token_info_controller_spec.rb +17 -13
  110. data/spec/controllers/tokens_controller_spec.rb +198 -12
  111. data/spec/dummy/app/controllers/full_protected_resources_controller.rb +4 -4
  112. data/spec/dummy/app/controllers/home_controller.rb +1 -1
  113. data/spec/dummy/app/controllers/metal_controller.rb +1 -1
  114. data/spec/dummy/app/controllers/semi_protected_resources_controller.rb +3 -3
  115. data/spec/dummy/app/models/user.rb +0 -4
  116. data/spec/dummy/config/application.rb +2 -36
  117. data/spec/dummy/config/environment.rb +1 -1
  118. data/spec/dummy/config/environments/test.rb +4 -15
  119. data/spec/dummy/config/initializers/doorkeeper.rb +19 -3
  120. data/spec/dummy/config/initializers/new_framework_defaults.rb +6 -0
  121. data/spec/dummy/config/initializers/secret_token.rb +0 -1
  122. data/spec/dummy/db/migrate/20111122132257_create_users.rb +3 -1
  123. data/spec/dummy/db/migrate/20120312140401_add_password_to_users.rb +3 -1
  124. data/{lib/generators/doorkeeper/templates/migration.rb → spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb} +16 -4
  125. data/{lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb → spec/dummy/db/migrate/20151223200000_add_owner_to_application.rb} +4 -2
  126. data/spec/dummy/db/migrate/20160320211015_add_previous_refresh_token_to_access_tokens.rb +13 -0
  127. data/spec/dummy/db/migrate/20180210183654_add_confidential_to_application.rb +13 -0
  128. data/spec/dummy/db/schema.rb +24 -22
  129. data/spec/factories.rb +4 -2
  130. data/spec/generators/application_owner_generator_spec.rb +24 -5
  131. data/spec/generators/migration_generator_spec.rb +24 -3
  132. data/spec/generators/previous_refresh_token_generator_spec.rb +57 -0
  133. data/spec/grape/grape_integration_spec.rb +135 -0
  134. data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +1 -1
  135. data/spec/lib/config_spec.rb +159 -14
  136. data/spec/lib/doorkeeper_spec.rb +135 -13
  137. data/spec/lib/models/expirable_spec.rb +0 -1
  138. data/spec/lib/models/revocable_spec.rb +27 -4
  139. data/spec/lib/oauth/authorization/uri_builder_spec.rb +1 -2
  140. data/spec/lib/oauth/authorization_code_request_spec.rb +55 -12
  141. data/spec/lib/oauth/base_request_spec.rb +155 -0
  142. data/spec/lib/oauth/base_response_spec.rb +45 -0
  143. data/spec/lib/oauth/client/credentials_spec.rb +45 -2
  144. data/spec/lib/oauth/client_credentials/creator_spec.rb +1 -1
  145. data/spec/lib/oauth/client_credentials_integration_spec.rb +1 -1
  146. data/spec/lib/oauth/client_credentials_request_spec.rb +1 -0
  147. data/spec/lib/oauth/code_request_spec.rb +1 -3
  148. data/spec/lib/oauth/code_response_spec.rb +34 -0
  149. data/spec/lib/oauth/error_response_spec.rb +9 -9
  150. data/spec/lib/oauth/error_spec.rb +1 -1
  151. data/spec/lib/oauth/helpers/uri_checker_spec.rb +115 -1
  152. data/spec/lib/oauth/invalid_token_response_spec.rb +36 -8
  153. data/spec/lib/oauth/password_access_token_request_spec.rb +14 -8
  154. data/spec/lib/oauth/pre_authorization_spec.rb +12 -7
  155. data/spec/lib/oauth/refresh_token_request_spec.rb +52 -9
  156. data/spec/lib/oauth/scopes_spec.rb +28 -2
  157. data/spec/lib/oauth/token_request_spec.rb +6 -8
  158. data/spec/lib/oauth/token_spec.rb +12 -5
  159. data/spec/lib/server_spec.rb +10 -3
  160. data/spec/models/doorkeeper/access_grant_spec.rb +1 -1
  161. data/spec/models/doorkeeper/access_token_spec.rb +116 -48
  162. data/spec/models/doorkeeper/application_spec.rb +145 -29
  163. data/spec/requests/applications/applications_request_spec.rb +5 -5
  164. data/spec/requests/endpoints/authorization_spec.rb +5 -6
  165. data/spec/requests/endpoints/token_spec.rb +8 -1
  166. data/spec/requests/flows/authorization_code_errors_spec.rb +11 -1
  167. data/spec/requests/flows/authorization_code_spec.rb +6 -13
  168. data/spec/requests/flows/client_credentials_spec.rb +29 -1
  169. data/spec/requests/flows/implicit_grant_errors_spec.rb +2 -2
  170. data/spec/requests/flows/password_spec.rb +118 -15
  171. data/spec/requests/flows/refresh_token_spec.rb +89 -19
  172. data/spec/requests/flows/revoke_token_spec.rb +105 -91
  173. data/spec/requests/protected_resources/metal_spec.rb +1 -1
  174. data/spec/requests/protected_resources/private_api_spec.rb +1 -1
  175. data/spec/routing/custom_controller_routes_spec.rb +4 -0
  176. data/spec/routing/default_routes_spec.rb +5 -1
  177. data/spec/spec_helper.rb +2 -0
  178. data/spec/spec_helper_integration.rb +22 -4
  179. data/spec/support/dependencies/factory_girl.rb +2 -2
  180. data/spec/support/helpers/access_token_request_helper.rb +1 -1
  181. data/spec/support/helpers/model_helper.rb +34 -7
  182. data/spec/support/helpers/request_spec_helper.rb +17 -5
  183. data/spec/support/helpers/url_helper.rb +9 -8
  184. data/spec/support/http_method_shim.rb +38 -0
  185. data/spec/support/shared/controllers_shared_context.rb +15 -10
  186. data/spec/support/shared/models_shared_examples.rb +5 -5
  187. data/spec/validators/redirect_uri_validator_spec.rb +51 -6
  188. data/spec/version/version_spec.rb +15 -0
  189. metadata +128 -46
  190. data/lib/doorkeeper/oauth/client/methods.rb +0 -18
  191. data/lib/generators/doorkeeper/application_scopes_generator.rb +0 -34
  192. data/lib/generators/doorkeeper/templates/add_scopes_to_oauth_applications.rb +0 -5
  193. data/spec/dummy/db/migrate/20130902165751_create_doorkeeper_tables.rb +0 -41
  194. data/spec/dummy/db/migrate/20141209001746_add_scopes_to_oauth_applications.rb +0 -5
  195. data/spec/lib/oauth/client/methods_spec.rb +0 -54
@@ -1,8 +1,6 @@
1
1
  module Doorkeeper
2
2
  module OAuth
3
- class RefreshTokenRequest
4
- include Validations
5
- include OAuth::RequestConcern
3
+ class RefreshTokenRequest < BaseRequest
6
4
  include OAuth::Helpers
7
5
 
8
6
  validate :token_presence, error: :invalid_request
@@ -29,16 +27,19 @@ module Doorkeeper
29
27
 
30
28
  private
31
29
 
32
- attr_reader :refresh_token_parameter
33
-
34
30
  def before_successful_response
35
31
  refresh_token.transaction do
36
32
  refresh_token.lock!
37
33
  raise Errors::InvalidTokenReuse if refresh_token.revoked?
38
34
 
39
- refresh_token.revoke
35
+ refresh_token.revoke unless refresh_token_revoked_on_use?
40
36
  create_access_token
41
37
  end
38
+ super
39
+ end
40
+
41
+ def refresh_token_revoked_on_use?
42
+ Doorkeeper::AccessToken.refresh_token_revoked_on_use?
42
43
  end
43
44
 
44
45
  def default_scopes
@@ -46,21 +47,29 @@ module Doorkeeper
46
47
  end
47
48
 
48
49
  def create_access_token
49
- expires_in = Authorization::Token.access_token_expires_in(
50
- server,
51
- client
52
- )
50
+ @access_token = AccessToken.create!(access_token_attributes)
51
+ end
53
52
 
54
- @access_token = AccessToken.create!(
53
+ def access_token_attributes
54
+ {
55
55
  application_id: refresh_token.application_id,
56
56
  resource_owner_id: refresh_token.resource_owner_id,
57
57
  scopes: scopes.to_s,
58
- expires_in: expires_in,
59
- use_refresh_token: true)
58
+ expires_in: access_token_expires_in,
59
+ use_refresh_token: true
60
+ }.tap do |attributes|
61
+ if refresh_token_revoked_on_use?
62
+ attributes[:previous_refresh_token] = refresh_token.refresh_token
63
+ end
64
+ end
65
+ end
66
+
67
+ def access_token_expires_in
68
+ Authorization::Token.access_token_expires_in(server, client)
60
69
  end
61
70
 
62
71
  def validate_token_presence
63
- refresh_token.present? || refresh_token_parameter.present?
72
+ refresh_token.present? || @refresh_token_parameter.present?
64
73
  end
65
74
 
66
75
  def validate_token
@@ -45,20 +45,30 @@ module Doorkeeper
45
45
  end
46
46
 
47
47
  def +(other)
48
- if other.is_a? Scopes
49
- self.class.from_array(self.all + other.all)
50
- else
51
- super(other)
52
- end
48
+ self.class.from_array(all + to_array(other))
53
49
  end
54
50
 
55
51
  def <=>(other)
56
- self.map(&:to_s).sort <=> other.map(&:to_s).sort
52
+ if other.respond_to?(:map)
53
+ map(&:to_s).sort <=> other.map(&:to_s).sort
54
+ else
55
+ super
56
+ end
57
57
  end
58
58
 
59
59
  def &(other)
60
- other_array = other.present? ? other.all : []
61
- self.class.from_array(all & other_array)
60
+ self.class.from_array(all & to_array(other))
61
+ end
62
+
63
+ private
64
+
65
+ def to_array(other)
66
+ case other
67
+ when Scopes
68
+ other.all
69
+ else
70
+ other.to_a
71
+ end
62
72
  end
63
73
  end
64
74
  end
@@ -1,7 +1,23 @@
1
1
  module Doorkeeper
2
2
  module OAuth
3
3
  class Token
4
- module Methods
4
+ class << self
5
+ def from_request(request, *methods)
6
+ methods.inject(nil) do |credentials, method|
7
+ method = self.method(method) if method.is_a?(Symbol)
8
+ credentials = method.call(request)
9
+ break credentials unless credentials.blank?
10
+ end
11
+ end
12
+
13
+ def authenticate(request, *methods)
14
+ if (token = from_request(request, *methods))
15
+ access_token = AccessToken.by_token(token)
16
+ access_token.revoke_previous_refresh_token! if access_token
17
+ access_token
18
+ end
19
+ end
20
+
5
21
  def from_access_token_param(request)
6
22
  request.parameters[:access_token]
7
23
  end
@@ -26,12 +42,11 @@ module Doorkeeper
26
42
 
27
43
  def token_from_basic_header(header, pattern)
28
44
  encoded_header = token_from_header(header, pattern)
29
- token, _ = decode_basic_credentials(encoded_header)
30
- token
45
+ decode_basic_credentials_token(encoded_header)
31
46
  end
32
47
 
33
- def decode_basic_credentials(encoded_header)
34
- Base64.decode64(encoded_header).split(/:/, 2)
48
+ def decode_basic_credentials_token(encoded_header)
49
+ Base64.decode64(encoded_header).split(/:/, 2).first
35
50
  end
36
51
 
37
52
  def token_from_header(header, pattern)
@@ -42,22 +57,6 @@ module Doorkeeper
42
57
  header && header.match(pattern)
43
58
  end
44
59
  end
45
-
46
- extend Methods
47
-
48
- def self.from_request(request, *methods)
49
- methods.inject(nil) do |credentials, method|
50
- method = self.method(method) if method.is_a?(Symbol)
51
- credentials = method.call(request)
52
- break credentials unless credentials.blank?
53
- end
54
- end
55
-
56
- def self.authenticate(request, *methods)
57
- if token = from_request(request, *methods)
58
- AccessToken.by_token(token)
59
- end
60
- end
61
60
  end
62
61
  end
63
62
  end
@@ -0,0 +1,128 @@
1
+ module Doorkeeper
2
+ module OAuth
3
+ # RFC7662 OAuth 2.0 Token Introspection
4
+ #
5
+ # @see https://tools.ietf.org/html/rfc7662
6
+ class TokenIntrospection
7
+ attr_reader :server, :token
8
+ attr_reader :error
9
+
10
+ def initialize(server, token)
11
+ @server = server
12
+ @token = token
13
+
14
+ authorize!
15
+ end
16
+
17
+ def authorized?
18
+ @error.blank?
19
+ end
20
+
21
+ def to_json
22
+ active? ? success_response : failure_response
23
+ end
24
+
25
+ private
26
+
27
+ # If the protected resource uses OAuth 2.0 client credentials to
28
+ # authenticate to the introspection endpoint and its credentials are
29
+ # invalid, the authorization server responds with an HTTP 401
30
+ # (Unauthorized) as described in Section 5.2 of OAuth 2.0 [RFC6749].
31
+ #
32
+ # Endpoint must first validate the authentication.
33
+ # If the authentication is invalid, the endpoint should respond with
34
+ # an HTTP 401 status code and an invalid_client response.
35
+ #
36
+ # @see https://www.oauth.com/oauth2-servers/token-introspection-endpoint/
37
+ #
38
+ def authorize!
39
+ # Requested client authorization
40
+ if server.credentials
41
+ @error = :invalid_client unless authorized_client
42
+ else
43
+ # Requested bearer token authorization
44
+ @error = :invalid_request unless authorized_token
45
+ end
46
+ end
47
+
48
+ # Client Authentication
49
+ def authorized_client
50
+ @_authorized_client ||= server.credentials && server.client
51
+ end
52
+
53
+ # Bearer Token Authentication
54
+ def authorized_token
55
+ @_authorized_token ||=
56
+ OAuth::Token.authenticate(server.context.request, :from_bearer_authorization)
57
+ end
58
+
59
+ # 2.2. Introspection Response
60
+ def success_response
61
+ {
62
+ active: true,
63
+ scope: @token.scopes_string,
64
+ client_id: @token.try(:application).try(:uid),
65
+ token_type: @token.token_type,
66
+ exp: @token.expires_at.to_i,
67
+ iat: @token.created_at.to_i
68
+ }
69
+ end
70
+
71
+ # If the introspection call is properly authorized but the token is not
72
+ # active, does not exist on this server, or the protected resource is
73
+ # not allowed to introspect this particular token, then the
74
+ # authorization server MUST return an introspection response with the
75
+ # "active" field set to "false". Note that to avoid disclosing too
76
+ # much of the authorization server's state to a third party, the
77
+ # authorization server SHOULD NOT include any additional information
78
+ # about an inactive token, including why the token is inactive.
79
+ #
80
+ # @see https://tools.ietf.org/html/rfc7662 2.2. Introspection Response
81
+ #
82
+ def failure_response
83
+ {
84
+ active: false
85
+ }
86
+ end
87
+
88
+ # Boolean indicator of whether or not the presented token
89
+ # is currently active. The specifics of a token's "active" state
90
+ # will vary depending on the implementation of the authorization
91
+ # server and the information it keeps about its tokens, but a "true"
92
+ # value return for the "active" property will generally indicate
93
+ # that a given token has been issued by this authorization server,
94
+ # has not been revoked by the resource owner, and is within its
95
+ # given time window of validity (e.g., after its issuance time and
96
+ # before its expiration time).
97
+ #
98
+ # Any other error is considered an "inactive" token.
99
+ #
100
+ # * The token requested does not exist or is invalid
101
+ # * The token expired
102
+ # * The token was issued to a different client than is making this request
103
+ #
104
+ def active?
105
+ if authorized_client
106
+ valid_token? && authorized_for_client?
107
+ else
108
+ valid_token?
109
+ end
110
+ end
111
+
112
+ # Token can be valid only if it is not expired or revoked.
113
+ def valid_token?
114
+ @token.present? && @token.accessible?
115
+ end
116
+
117
+ # If token doesn't belong to some client, then it is public.
118
+ # Otherwise in it required for token to be connected to the same client.
119
+ def authorized_for_client?
120
+ if @token.application.present?
121
+ @token.application == authorized_client.application
122
+ else
123
+ true
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
@@ -1,11 +1,10 @@
1
1
  module Doorkeeper
2
2
  module OAuth
3
3
  class TokenRequest
4
- attr_accessor :pre_auth, :resource_owner, :client
4
+ attr_accessor :pre_auth, :resource_owner
5
5
 
6
6
  def initialize(pre_auth, resource_owner)
7
7
  @pre_auth = pre_auth
8
- @client = pre_auth.client
9
8
  @resource_owner = resource_owner
10
9
  end
11
10
 
@@ -14,7 +14,7 @@ module Doorkeeper
14
14
  'expires_in' => token.expires_in_seconds,
15
15
  'refresh_token' => token.refresh_token,
16
16
  'scope' => token.scopes_string,
17
- 'created_at' => token.created_at.to_i,
17
+ 'created_at' => token.created_at.to_i
18
18
  }.reject { |_, value| value.blank? }
19
19
  end
20
20
 
@@ -3,5 +3,32 @@ module Doorkeeper
3
3
  self.table_name = "#{table_name_prefix}oauth_access_grants#{table_name_suffix}".to_sym
4
4
 
5
5
  include AccessGrantMixin
6
+ include ActiveModel::MassAssignmentSecurity if defined?(::ProtectedAttributes)
7
+
8
+ belongs_to_options = {
9
+ class_name: 'Doorkeeper::Application',
10
+ inverse_of: :access_grants
11
+ }
12
+
13
+ if defined?(ActiveRecord::Base) && ActiveRecord::VERSION::MAJOR >= 5
14
+ belongs_to_options[:optional] = true
15
+ end
16
+
17
+ belongs_to :application, belongs_to_options
18
+
19
+ validates :resource_owner_id, :application_id, :token, :expires_in, :redirect_uri, presence: true
20
+ validates :token, uniqueness: true
21
+
22
+ before_validation :generate_token, on: :create
23
+
24
+ private
25
+
26
+ # Generates token value with UniqueToken class.
27
+ #
28
+ # @return [String] token value
29
+ #
30
+ def generate_token
31
+ self.token = UniqueToken.generate
32
+ end
6
33
  end
7
34
  end
@@ -3,19 +3,45 @@ module Doorkeeper
3
3
  self.table_name = "#{table_name_prefix}oauth_access_tokens#{table_name_suffix}".to_sym
4
4
 
5
5
  include AccessTokenMixin
6
+ include ActiveModel::MassAssignmentSecurity if defined?(::ProtectedAttributes)
6
7
 
7
- def self.delete_all_for(application_id, resource_owner)
8
- where(application_id: application_id,
9
- resource_owner_id: resource_owner.id).delete_all
8
+ belongs_to_options = {
9
+ class_name: 'Doorkeeper::Application',
10
+ inverse_of: :access_tokens
11
+ }
12
+
13
+ if defined?(ActiveRecord::Base) && ActiveRecord::VERSION::MAJOR >= 5
14
+ belongs_to_options[:optional] = true
10
15
  end
11
- private_class_method :delete_all_for
12
16
 
13
- def self.order_method
14
- :order
17
+ belongs_to :application, belongs_to_options
18
+
19
+ validates :token, presence: true, uniqueness: true
20
+ validates :refresh_token, uniqueness: true, if: :use_refresh_token?
21
+
22
+ # @attr_writer [Boolean, nil] use_refresh_token
23
+ # indicates the possibility of using refresh token
24
+ attr_writer :use_refresh_token
25
+
26
+ before_validation :generate_token, on: :create
27
+ before_validation :generate_refresh_token,
28
+ on: :create, if: :use_refresh_token?
29
+
30
+ # Searches for not revoked Access Tokens associated with the
31
+ # specific Resource Owner.
32
+ #
33
+ # @param resource_owner [ActiveRecord::Base]
34
+ # Resource Owner model instance
35
+ #
36
+ # @return [ActiveRecord::Relation]
37
+ # active Access Tokens for Resource Owner
38
+ #
39
+ def self.active_for(resource_owner)
40
+ where(resource_owner_id: resource_owner.id, revoked_at: nil)
15
41
  end
16
42
 
17
- def self.created_at_desc
18
- 'created_at desc'
43
+ def self.refresh_token_revoked_on_use?
44
+ column_names.include?('previous_refresh_token')
19
45
  end
20
46
  end
21
47
  end
@@ -3,22 +3,59 @@ module Doorkeeper
3
3
  self.table_name = "#{table_name_prefix}oauth_applications#{table_name_suffix}".to_sym
4
4
 
5
5
  include ApplicationMixin
6
+ include ActiveModel::MassAssignmentSecurity if defined?(::ProtectedAttributes)
6
7
 
7
- if ActiveRecord::VERSION::MAJOR >= 4
8
- has_many :authorized_tokens, -> { where(revoked_at: nil) }, class_name: 'AccessToken'
9
- else
10
- has_many :authorized_tokens, class_name: 'AccessToken', conditions: { revoked_at: nil }
11
- end
8
+ has_many :access_grants, dependent: :delete_all, class_name: 'Doorkeeper::AccessGrant'
9
+ has_many :access_tokens, dependent: :delete_all, class_name: 'Doorkeeper::AccessToken'
10
+
11
+ validates :name, :secret, :uid, presence: true
12
+ validates :uid, uniqueness: true
13
+ validates :redirect_uri, redirect_uri: true
14
+ validates :confidential, inclusion: { in: [true, false] }
15
+
16
+ before_validation :generate_uid, :generate_secret, on: :create
17
+
18
+ has_many :authorized_tokens, -> { where(revoked_at: nil) }, class_name: 'AccessToken'
12
19
  has_many :authorized_applications, through: :authorized_tokens, source: :application
13
20
 
14
- def self.column_names_with_table
15
- self.column_names.map { |c| "#{table_name}.#{c}" }
21
+ # Returns Applications associated with active (not revoked) Access Tokens
22
+ # that are owned by the specific Resource Owner.
23
+ #
24
+ # @param resource_owner [ActiveRecord::Base]
25
+ # Resource Owner model instance
26
+ #
27
+ # @return [ActiveRecord::Relation]
28
+ # Applications authorized for the Resource Owner
29
+ #
30
+ def self.authorized_for(resource_owner)
31
+ resource_access_tokens = AccessToken.active_for(resource_owner)
32
+ where(id: resource_access_tokens.select(:application_id).distinct)
16
33
  end
17
34
 
18
- def self.authorized_for(resource_owner)
19
- joins(:authorized_applications).
20
- where(AccessToken.table_name => { resource_owner_id: resource_owner.id, revoked_at: nil }).
21
- group(column_names_with_table.join(','))
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
43
+ end
44
+
45
+ alias_method :confidential?, :confidential
46
+
47
+ def self.supports_confidentiality?
48
+ column_names.include?('confidential')
49
+ end
50
+
51
+ private
52
+
53
+ def generate_uid
54
+ self.uid = UniqueToken.generate if uid.blank?
55
+ end
56
+
57
+ def generate_secret
58
+ self.secret = UniqueToken.generate if secret.blank?
22
59
  end
23
60
  end
24
61
  end
@@ -1,38 +1,33 @@
1
+ require 'active_support/lazy_load_hooks'
2
+
1
3
  module Doorkeeper
2
4
  module Orm
3
5
  module ActiveRecord
4
6
  def self.initialize_models!
5
- require 'doorkeeper/orm/active_record/access_grant'
6
- require 'doorkeeper/orm/active_record/access_token'
7
- require 'doorkeeper/orm/active_record/application'
7
+ lazy_load do
8
+ require 'doorkeeper/orm/active_record/access_grant'
9
+ require 'doorkeeper/orm/active_record/access_token'
10
+ require 'doorkeeper/orm/active_record/application'
8
11
 
9
- if Doorkeeper.configuration.active_record_options[:establish_connection]
10
- [Doorkeeper::AccessGrant, Doorkeeper::AccessToken, Doorkeeper::Application].each do |c|
11
- c.send :establish_connection, Doorkeeper.configuration.active_record_options[:establish_connection]
12
+ if Doorkeeper.configuration.active_record_options[:establish_connection]
13
+ [Doorkeeper::AccessGrant, Doorkeeper::AccessToken, Doorkeeper::Application].each do |model|
14
+ options = Doorkeeper.configuration.active_record_options[:establish_connection]
15
+ model.establish_connection(options)
16
+ end
12
17
  end
13
18
  end
14
19
  end
15
20
 
16
21
  def self.initialize_application_owner!
17
- require 'doorkeeper/models/concerns/ownership'
22
+ lazy_load do
23
+ require 'doorkeeper/models/concerns/ownership'
18
24
 
19
- Doorkeeper::Application.send :include, Doorkeeper::Models::Ownership
25
+ Doorkeeper::Application.send :include, Doorkeeper::Models::Ownership
26
+ end
20
27
  end
21
28
 
22
- def self.check_requirements!(_config)
23
- if ::ActiveRecord::Base.connected? &&
24
- ::ActiveRecord::Base.connection.table_exists?(
25
- Doorkeeper::Application.table_name
26
- )
27
- unless Doorkeeper::Application.new.attributes.include?("scopes")
28
- migration_path = '../../../generators/doorkeeper/templates/add_scopes_to_oauth_applications.rb'
29
- puts <<-MSG.squish
30
- [doorkeeper] Missing column: `oauth_applications.scopes`.
31
- Create the following migration and run `rake db:migrate`.
32
- MSG
33
- puts File.read(File.expand_path(migration_path, __FILE__))
34
- end
35
- end
29
+ def self.lazy_load(&block)
30
+ ActiveSupport.on_load(:active_record, {}, &block)
36
31
  end
37
32
  end
38
33
  end
@@ -1,21 +1,17 @@
1
1
  module Doorkeeper
2
2
  module Rails
3
3
  module Helpers
4
- extend ActiveSupport::Concern
5
-
6
4
  def doorkeeper_authorize!(*scopes)
7
5
  @_doorkeeper_scopes = scopes.presence || Doorkeeper.configuration.default_scopes
8
6
 
9
- if !valid_doorkeeper_token?
7
+ unless valid_doorkeeper_token?
10
8
  doorkeeper_render_error
11
9
  end
12
10
  end
13
11
 
14
- def doorkeeper_unauthorized_render_options(error: nil)
15
- end
12
+ def doorkeeper_unauthorized_render_options(**); end
16
13
 
17
- def doorkeeper_forbidden_render_options(error: nil)
18
- end
14
+ def doorkeeper_forbidden_render_options(**); end
19
15
 
20
16
  def valid_doorkeeper_token?
21
17
  doorkeeper_token && doorkeeper_token.acceptable?(@_doorkeeper_scopes)
@@ -25,14 +21,15 @@ module Doorkeeper
25
21
 
26
22
  def doorkeeper_render_error
27
23
  error = doorkeeper_error
28
- headers.merge! error.headers.reject { |k| "Content-Type" == k }
24
+ headers.merge!(error.headers.reject { |k| k == "Content-Type" })
29
25
  doorkeeper_render_error_with(error)
30
26
  end
31
27
 
32
28
  def doorkeeper_render_error_with(error)
33
29
  options = doorkeeper_render_options(error) || {}
34
30
  status = doorkeeper_status_for_error(
35
- error, options.delete(:respond_not_found_when_forbidden))
31
+ error, options.delete(:respond_not_found_when_forbidden)
32
+ )
36
33
  if options.blank?
37
34
  head status
38
35
  else
@@ -1,13 +1,13 @@
1
1
  module Doorkeeper
2
2
  module Rails
3
- class Routes
3
+ class Routes # :nodoc:
4
4
  class Mapper
5
- def initialize(mapping = Mapping.new)
6
- @mapping = mapping
5
+ def initialize
6
+ @mapping = Mapping.new
7
7
  end
8
8
 
9
9
  def map(&block)
10
- self.instance_eval(&block) if block
10
+ instance_eval(&block) if block
11
11
  @mapping
12
12
  end
13
13
 
@@ -1,6 +1,6 @@
1
1
  module Doorkeeper
2
2
  module Rails
3
- class Routes
3
+ class Routes # :nodoc:
4
4
  class Mapping
5
5
  attr_accessor :controllers, :as, :skips
6
6