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,13 +1,8 @@
1
1
  module Doorkeeper
2
2
  class Engine < Rails::Engine
3
3
  initializer "doorkeeper.params.filter" do |app|
4
- app.config.filter_parameters += [:client_secret, :code, :token]
5
- end
6
-
7
- initializer "doorkeeper.locales" do |app|
8
- if app.config.i18n.fallbacks.blank?
9
- app.config.i18n.fallbacks = [:en]
10
- end
4
+ parameters = %w[client_secret code authentication_token access_token refresh_token]
5
+ app.config.filter_parameters << /^(#{Regexp.union parameters})$/
11
6
  end
12
7
 
13
8
  initializer "doorkeeper.routes" do
@@ -19,5 +14,14 @@ module Doorkeeper
19
14
  include Doorkeeper::Rails::Helpers
20
15
  end
21
16
  end
17
+
18
+ if defined?(Sprockets) && Sprockets::VERSION.chr.to_i >= 4
19
+ initializer 'doorkeeper.assets.precompile' do |app|
20
+ app.config.assets.precompile += %w[
21
+ doorkeeper/application.css
22
+ doorkeeper/admin/application.css
23
+ ]
24
+ end
25
+ end
22
26
  end
23
27
  end
@@ -1,21 +1,39 @@
1
1
  module Doorkeeper
2
2
  module Errors
3
3
  class DoorkeeperError < StandardError
4
+ def type
5
+ message
6
+ end
4
7
  end
5
8
 
6
9
  class InvalidAuthorizationStrategy < DoorkeeperError
10
+ def type
11
+ :unsupported_response_type
12
+ end
7
13
  end
8
14
 
9
15
  class InvalidTokenReuse < DoorkeeperError
16
+ def type
17
+ :invalid_request
18
+ end
10
19
  end
11
20
 
12
21
  class InvalidGrantReuse < DoorkeeperError
22
+ def type
23
+ :invalid_grant
24
+ end
13
25
  end
14
26
 
15
27
  class InvalidTokenStrategy < DoorkeeperError
28
+ def type
29
+ :unsupported_grant_type
30
+ end
16
31
  end
17
32
 
18
33
  class MissingRequestStrategy < DoorkeeperError
34
+ def type
35
+ :invalid_request
36
+ end
19
37
  end
20
38
 
21
39
  class UnableToGenerateToken < DoorkeeperError
@@ -3,12 +3,13 @@ require 'doorkeeper/grape/authorization_decorator'
3
3
  module Doorkeeper
4
4
  module Grape
5
5
  module Helpers
6
+ # These helpers are for grape >= 0.10
6
7
  extend ::Grape::API::Helpers
7
8
  include Doorkeeper::Rails::Helpers
8
9
 
9
10
  # endpoint specific scopes > parameter scopes > default scopes
10
11
  def doorkeeper_authorize!(*scopes)
11
- endpoint_scopes = env['api.endpoint'].options[:route_options][:scopes]
12
+ endpoint_scopes = endpoint.route_setting(:scopes) || endpoint.options[:route_options][:scopes]
12
13
  scopes = if endpoint_scopes
13
14
  Doorkeeper::OAuth::Scopes.from_array(endpoint_scopes)
14
15
  elsif scopes && !scopes.empty?
@@ -19,18 +20,16 @@ module Doorkeeper
19
20
  end
20
21
 
21
22
  def doorkeeper_render_error_with(error)
22
- status_code = case error.status
23
- when :unauthorized
24
- 401
25
- when :forbidden
26
- 403
27
- end
28
-
23
+ status_code = error_status_codes[error.status]
29
24
  error!({ error: error.description }, status_code, error.headers)
30
25
  end
31
26
 
32
27
  private
33
28
 
29
+ def endpoint
30
+ env['api.endpoint']
31
+ end
32
+
34
33
  def doorkeeper_token
35
34
  @_doorkeeper_token ||= OAuth::Token.authenticate(
36
35
  decorated_request,
@@ -41,6 +40,13 @@ module Doorkeeper
41
40
  def decorated_request
42
41
  AuthorizationDecorator.new(request)
43
42
  end
43
+
44
+ def error_status_codes
45
+ {
46
+ unauthorized: 401,
47
+ forbidden: 403
48
+ }
49
+ end
44
50
  end
45
51
  end
46
52
  end
@@ -1,14 +1,16 @@
1
+ # Define methods that can be called in any controller that inherits from
2
+ # Doorkeeper::ApplicationMetalController or Doorkeeper::ApplicationController
1
3
  module Doorkeeper
2
4
  module Helpers
3
5
  module Controller
4
- extend ActiveSupport::Concern
5
-
6
6
  private
7
7
 
8
+ # :doc:
8
9
  def authenticate_resource_owner!
9
10
  current_resource_owner
10
11
  end
11
12
 
13
+ # :doc:
12
14
  def current_resource_owner
13
15
  instance_eval(&Doorkeeper.configuration.authenticate_resource_owner)
14
16
  end
@@ -17,6 +19,7 @@ module Doorkeeper
17
19
  instance_eval(&Doorkeeper.configuration.resource_owner_from_credentials)
18
20
  end
19
21
 
22
+ # :doc:
20
23
  def authenticate_admin!
21
24
  instance_eval(&Doorkeeper.configuration.authenticate_admin)
22
25
  end
@@ -25,6 +28,7 @@ module Doorkeeper
25
28
  @server ||= Server.new(self)
26
29
  end
27
30
 
31
+ # :doc:
28
32
  def doorkeeper_token
29
33
  @token ||= OAuth::Token.authenticate request, *config_methods
30
34
  end
@@ -34,27 +38,12 @@ module Doorkeeper
34
38
  end
35
39
 
36
40
  def get_error_response_from_exception(exception)
37
- error_name = case exception
38
- when Errors::InvalidTokenStrategy
39
- :unsupported_grant_type
40
- when Errors::InvalidAuthorizationStrategy
41
- :unsupported_response_type
42
- when Errors::MissingRequestStrategy
43
- :invalid_request
44
- when Errors::InvalidTokenReuse
45
- :invalid_request
46
- when Errors::InvalidGrantReuse
47
- :invalid_grant
48
- when Errors::DoorkeeperError
49
- exception.message
50
- end
51
-
52
- OAuth::ErrorResponse.new name: error_name, state: params[:state]
41
+ OAuth::ErrorResponse.new name: exception.type, state: params[:state]
53
42
  end
54
43
 
55
44
  def handle_token_exception(exception)
56
45
  error = get_error_response_from_exception exception
57
- self.headers.merge! error.headers
46
+ headers.merge! error.headers
58
47
  self.response_body = error.body.to_json
59
48
  self.status = error.status
60
49
  end
@@ -6,32 +6,21 @@ module Doorkeeper
6
6
  include Models::Expirable
7
7
  include Models::Revocable
8
8
  include Models::Accessible
9
+ include Models::Orderable
9
10
  include Models::Scopes
10
- include ActiveModel::MassAssignmentSecurity if defined?(::ProtectedAttributes)
11
-
12
- included do
13
- belongs_to :application, class_name: 'Doorkeeper::Application', inverse_of: :access_grants
14
-
15
- if respond_to?(:attr_accessible)
16
- attr_accessible :resource_owner_id, :application_id, :expires_in, :redirect_uri, :scopes
17
- end
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
- end
24
11
 
25
12
  module ClassMethods
13
+ # Searches for Doorkeeper::AccessGrant record with the
14
+ # specific token value.
15
+ #
16
+ # @param token [#to_s] token value (any object that responds to `#to_s`)
17
+ #
18
+ # @return [Doorkeeper::AccessGrant, nil] AccessGrant object or nil
19
+ # if there is no record with such token
20
+ #
26
21
  def by_token(token)
27
- where(token: token.to_s).limit(1).to_a.first
22
+ find_by(token: token.to_s)
28
23
  end
29
24
  end
30
-
31
- private
32
-
33
- def generate_token
34
- self.token = UniqueToken.generate
35
- end
36
25
  end
37
26
  end
@@ -6,46 +6,64 @@ module Doorkeeper
6
6
  include Models::Expirable
7
7
  include Models::Revocable
8
8
  include Models::Accessible
9
+ include Models::Orderable
9
10
  include Models::Scopes
10
- include ActiveModel::MassAssignmentSecurity if defined?(::ProtectedAttributes)
11
-
12
- included do
13
- belongs_to :application,
14
- class_name: 'Doorkeeper::Application',
15
- inverse_of: :access_tokens
16
-
17
- validates :token, presence: true, uniqueness: true
18
- validates :refresh_token, uniqueness: true, if: :use_refresh_token?
19
-
20
- attr_writer :use_refresh_token
21
-
22
- if respond_to?(:attr_accessible)
23
- attr_accessible :application_id, :resource_owner_id, :expires_in,
24
- :scopes, :use_refresh_token
25
- end
26
-
27
- before_validation :generate_token, on: :create
28
- before_validation :generate_refresh_token,
29
- on: :create,
30
- if: :use_refresh_token?
31
- end
32
11
 
33
12
  module ClassMethods
13
+ # Returns an instance of the Doorkeeper::AccessToken with
14
+ # specific token value.
15
+ #
16
+ # @param token [#to_s]
17
+ # token value (any object that responds to `#to_s`)
18
+ #
19
+ # @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
20
+ # if there is no record with such token
21
+ #
34
22
  def by_token(token)
35
- where(token: token.to_s).limit(1).to_a.first
23
+ find_by(token: token.to_s)
36
24
  end
37
25
 
26
+ # Returns an instance of the Doorkeeper::AccessToken
27
+ # with specific token value.
28
+ #
29
+ # @param refresh_token [#to_s]
30
+ # refresh token value (any object that responds to `#to_s`)
31
+ #
32
+ # @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
33
+ # if there is no record with such refresh token
34
+ #
38
35
  def by_refresh_token(refresh_token)
39
- where(refresh_token: refresh_token.to_s).first
36
+ find_by(refresh_token: refresh_token.to_s)
40
37
  end
41
38
 
42
- def revoke_all_for(application_id, resource_owner)
39
+ # Revokes AccessToken records that have not been revoked and associated
40
+ # with the specific Application and Resource Owner.
41
+ #
42
+ # @param application_id [Integer]
43
+ # ID of the Application
44
+ # @param resource_owner [ActiveRecord::Base]
45
+ # instance of the Resource Owner model
46
+ #
47
+ def revoke_all_for(application_id, resource_owner, clock = Time)
43
48
  where(application_id: application_id,
44
49
  resource_owner_id: resource_owner.id,
45
50
  revoked_at: nil).
46
- map(&:revoke)
51
+ update_all(revoked_at: clock.now.utc)
47
52
  end
48
53
 
54
+ # Looking for not expired Access Token with a matching set of scopes
55
+ # that belongs to specific Application and Resource Owner.
56
+ #
57
+ # @param application [Doorkeeper::Application]
58
+ # Application instance
59
+ # @param resource_owner_or_id [ActiveRecord::Base, Integer]
60
+ # Resource Owner model instance or it's ID
61
+ # @param scopes [String, Doorkeeper::OAuth::Scopes]
62
+ # set of scopes
63
+ #
64
+ # @return [Doorkeeper::AccessToken, nil] Access Token instance or
65
+ # nil if matching record was not found
66
+ #
49
67
  def matching_token_for(application, resource_owner_or_id, scopes)
50
68
  resource_owner_id = if resource_owner_or_id.respond_to?(:to_key)
51
69
  resource_owner_or_id.id
@@ -58,6 +76,19 @@ module Doorkeeper
58
76
  end
59
77
  end
60
78
 
79
+ # Checks whether the token scopes match the scopes from the parameters or
80
+ # Application scopes (if present).
81
+ #
82
+ # @param token_scopes [#to_s]
83
+ # set of scopes (any object that responds to `#to_s`)
84
+ # @param param_scopes [String]
85
+ # scopes from params
86
+ # @param app_scopes [String]
87
+ # Application scopes
88
+ #
89
+ # @return [Boolean] true if all scopes are blank or matches
90
+ # and false in other cases
91
+ #
61
92
  def scopes_match?(token_scopes, param_scopes, app_scopes)
62
93
  (!token_scopes.present? && !param_scopes.present?) ||
63
94
  Doorkeeper::OAuth::Helpers::ScopeChecker.match?(
@@ -67,6 +98,23 @@ module Doorkeeper
67
98
  )
68
99
  end
69
100
 
101
+ # Looking for not expired AccessToken record with a matching set of
102
+ # scopes that belongs to specific Application and Resource Owner.
103
+ # If it doesn't exists - then creates it.
104
+ #
105
+ # @param application [Doorkeeper::Application]
106
+ # Application instance
107
+ # @param resource_owner_id [ActiveRecord::Base, Integer]
108
+ # Resource Owner model instance or it's ID
109
+ # @param scopes [#to_s]
110
+ # set of scopes (any object that responds to `#to_s`)
111
+ # @param expires_in [Integer]
112
+ # token lifetime in seconds
113
+ # @param use_refresh_token [Boolean]
114
+ # whether to use the refresh token
115
+ #
116
+ # @return [Doorkeeper::AccessToken] existing record or a new one
117
+ #
70
118
  def find_or_create_for(application, resource_owner_id, scopes, expires_in, use_refresh_token)
71
119
  if Doorkeeper.configuration.reuse_access_token
72
120
  access_token = matching_token_for(application, resource_owner_id, scopes)
@@ -74,6 +122,7 @@ module Doorkeeper
74
122
  return access_token
75
123
  end
76
124
  end
125
+
77
126
  create!(
78
127
  application_id: application.try(:id),
79
128
  resource_owner_id: resource_owner_id,
@@ -83,60 +132,115 @@ module Doorkeeper
83
132
  )
84
133
  end
85
134
 
135
+ # Looking for not revoked Access Token record that belongs to specific
136
+ # Application and Resource Owner.
137
+ #
138
+ # @param application_id [Integer]
139
+ # ID of the Application model instance
140
+ # @param resource_owner_id [Integer]
141
+ # ID of the Resource Owner model instance
142
+ #
143
+ # @return [Doorkeeper::AccessToken, nil] matching AccessToken object or
144
+ # nil if nothing was found
145
+ #
86
146
  def last_authorized_token_for(application_id, resource_owner_id)
87
- where(application_id: application_id,
88
- resource_owner_id: resource_owner_id,
89
- revoked_at: nil).
90
- send(order_method, created_at_desc).
91
- limit(1).
92
- to_a.
93
- first
147
+ ordered_by(:created_at, :desc).
148
+ find_by(application_id: application_id,
149
+ resource_owner_id: resource_owner_id,
150
+ revoked_at: nil)
94
151
  end
95
152
  end
96
153
 
154
+ # Access Token type: Bearer.
155
+ # @see https://tools.ietf.org/html/rfc6750
156
+ # The OAuth 2.0 Authorization Framework: Bearer Token Usage
157
+ #
97
158
  def token_type
98
- 'bearer'
159
+ 'Bearer'
99
160
  end
100
161
 
101
162
  def use_refresh_token?
163
+ @use_refresh_token ||= false
102
164
  !!@use_refresh_token
103
165
  end
104
166
 
167
+ # JSON representation of the Access Token instance.
168
+ #
169
+ # @return [Hash] hash with token data
105
170
  def as_json(_options = {})
106
171
  {
107
172
  resource_owner_id: resource_owner_id,
108
173
  scopes: scopes,
109
174
  expires_in_seconds: expires_in_seconds,
110
175
  application: { uid: application.try(:uid) },
111
- created_at: created_at.to_i,
176
+ created_at: created_at.to_i
112
177
  }
113
178
  end
114
179
 
115
- # It indicates whether the tokens have the same credential
180
+ # Indicates whether the token instance have the same credential
181
+ # as the other Access Token.
182
+ #
183
+ # @param access_token [Doorkeeper::AccessToken] other token
184
+ #
185
+ # @return [Boolean] true if credentials are same of false in other cases
186
+ #
116
187
  def same_credential?(access_token)
117
188
  application_id == access_token.application_id &&
118
189
  resource_owner_id == access_token.resource_owner_id
119
190
  end
120
191
 
192
+ # Indicates if token is acceptable for specific scopes.
193
+ #
194
+ # @param scopes [Array<String>] scopes
195
+ #
196
+ # @return [Boolean] true if record is accessible and includes scopes or
197
+ # false in other cases
198
+ #
121
199
  def acceptable?(scopes)
122
200
  accessible? && includes_scope?(*scopes)
123
201
  end
124
202
 
125
203
  private
126
204
 
205
+ # Generates refresh token with UniqueToken generator.
206
+ #
207
+ # @return [String] refresh token value
208
+ #
127
209
  def generate_refresh_token
128
- write_attribute :refresh_token, UniqueToken.generate
210
+ self.refresh_token = UniqueToken.generate
129
211
  end
130
212
 
213
+ # Generates and sets the token value with the
214
+ # configured Generator class (see Doorkeeper.configuration).
215
+ #
216
+ # @return [String] generated token value
217
+ #
218
+ # @raise [Doorkeeper::Errors::UnableToGenerateToken]
219
+ # custom class doesn't implement .generate method
220
+ # @raise [Doorkeeper::Errors::TokenGeneratorNotFound]
221
+ # custom class doesn't exist
222
+ #
131
223
  def generate_token
132
- generator = Doorkeeper.configuration.access_token_generator.constantize
133
- self.token = generator.generate(resource_owner_id: resource_owner_id,
134
- scopes: scopes, application: application,
135
- expires_in: expires_in)
136
- rescue NoMethodError
224
+ self.created_at ||= Time.now.utc
225
+
226
+ self.token = token_generator.generate(
227
+ resource_owner_id: resource_owner_id,
228
+ scopes: scopes,
229
+ application: application,
230
+ expires_in: expires_in,
231
+ created_at: created_at
232
+ )
233
+ end
234
+
235
+ def token_generator
236
+ generator_name = Doorkeeper.configuration.access_token_generator
237
+ generator = generator_name.constantize
238
+
239
+ return generator if generator.respond_to?(:generate)
240
+
137
241
  raise Errors::UnableToGenerateToken, "#{generator} does not respond to `.generate`."
138
242
  rescue NameError
139
- raise Errors::TokenGeneratorNotFound, "#{generator} not found"
243
+ raise Errors::TokenGeneratorNotFound, "#{generator_name} not found"
140
244
  end
141
245
  end
142
246
  end
@@ -3,51 +3,49 @@ module Doorkeeper
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  include OAuth::Helpers
6
+ include Models::Orderable
6
7
  include Models::Scopes
7
- include ActiveModel::MassAssignmentSecurity if defined?(::ProtectedAttributes)
8
-
9
- included do
10
- has_many :access_grants, dependent: :destroy, class_name: 'Doorkeeper::AccessGrant'
11
- has_many :access_tokens, dependent: :destroy, class_name: 'Doorkeeper::AccessToken'
12
-
13
- validates :name, :secret, :uid, presence: true
14
- validates :uid, uniqueness: true
15
- validates :redirect_uri, redirect_uri: true
16
-
17
- before_validation :generate_uid, :generate_secret, on: :create
18
-
19
- if respond_to?(:attr_accessible)
20
- attr_accessible :name, :redirect_uri, :scopes
21
- end
22
- end
23
8
 
24
9
  module ClassMethods
10
+ # Returns an instance of the Doorkeeper::Application with
11
+ # specific UID and secret.
12
+ #
13
+ # Public/Non-confidential applications will only find by uid if secret is
14
+ # blank.
15
+ #
16
+ # @param uid [#to_s] UID (any object that responds to `#to_s`)
17
+ # @param secret [#to_s] secret (any object that responds to `#to_s`)
18
+ #
19
+ # @return [Doorkeeper::Application, nil] Application instance or nil
20
+ # if there is no record with such credentials
21
+ #
25
22
  def by_uid_and_secret(uid, secret)
26
- where(uid: uid.to_s, secret: secret.to_s).limit(1).to_a.first
23
+ app = by_uid(uid)
24
+ return unless app
25
+ return app if secret.blank? && !app.confidential?
26
+ return unless app.secret == secret
27
+ app
27
28
  end
28
29
 
30
+ # Returns an instance of the Doorkeeper::Application with specific UID.
31
+ #
32
+ # @param uid [#to_s] UID (any object that responds to `#to_s`)
33
+ #
34
+ # @return [Doorkeeper::Application, nil] Application instance or nil
35
+ # if there is no record with such UID
36
+ #
29
37
  def by_uid(uid)
30
- where(uid: uid.to_s).limit(1).to_a.first
38
+ find_by(uid: uid.to_s)
31
39
  end
32
40
  end
33
41
 
34
- private
35
-
36
- def has_scopes?
37
- Doorkeeper.configuration.orm != :active_record ||
38
- Application.new.attributes.include?("scopes")
39
- end
40
-
41
- def generate_uid
42
- if uid.blank?
43
- self.uid = UniqueToken.generate
44
- end
45
- end
46
-
47
- def generate_secret
48
- if secret.blank?
49
- self.secret = UniqueToken.generate
50
- end
42
+ # Set an application's valid redirect URIs.
43
+ #
44
+ # @param uris [String, Array] Newline-separated string or array the URI(s)
45
+ #
46
+ # @return [String] The redirect URI(s) seperated by newlines.
47
+ def redirect_uri=(uris)
48
+ super(uris.is_a?(Array) ? uris.join("\n") : uris)
51
49
  end
52
50
  end
53
51
  end
@@ -1,6 +1,10 @@
1
1
  module Doorkeeper
2
2
  module Models
3
3
  module Accessible
4
+ # Indicates whether the object is accessible (not expired and not revoked).
5
+ #
6
+ # @return [Boolean] true if object accessible or false in other case
7
+ #
4
8
  def accessible?
5
9
  !expired? && !revoked?
6
10
  end
@@ -1,20 +1,30 @@
1
1
  module Doorkeeper
2
2
  module Models
3
3
  module Expirable
4
+ # Indicates whether the object is expired (`#expires_in` present and
5
+ # expiration time has come).
6
+ #
7
+ # @return [Boolean] true if object expired and false in other case
4
8
  def expired?
5
- expires_in && Time.now > expired_time
9
+ expires_in && Time.now.utc > expires_at
6
10
  end
7
11
 
12
+ # Calculates expiration time in seconds.
13
+ #
14
+ # @return [Integer, nil] number of seconds if object has expiration time
15
+ # or nil if object never expires.
8
16
  def expires_in_seconds
9
17
  return nil if expires_in.nil?
10
- expires = (created_at + expires_in.seconds) - Time.now
18
+ expires = expires_at - Time.now.utc
11
19
  expires_sec = expires.seconds.round(0)
12
20
  expires_sec > 0 ? expires_sec : 0
13
21
  end
14
22
 
15
- private
16
-
17
- def expired_time
23
+ # Expiration time (date time of creation + TTL).
24
+ #
25
+ # @return [Time] expiration time in UTC
26
+ #
27
+ def expires_at
18
28
  created_at + expires_in.seconds
19
29
  end
20
30
  end
@@ -0,0 +1,13 @@
1
+ module Doorkeeper
2
+ module Models
3
+ module Orderable
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+ def ordered_by(attribute, direction = :asc)
8
+ order(attribute => direction)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -4,7 +4,12 @@ module Doorkeeper
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  included do
7
- belongs_to :owner, polymorphic: true
7
+ belongs_to_options = { polymorphic: true }
8
+ if defined?(ActiveRecord::Base) && ActiveRecord::VERSION::MAJOR >= 5
9
+ belongs_to_options[:optional] = true
10
+ end
11
+
12
+ belongs_to :owner, belongs_to_options
8
13
  validates :owner, presence: true, if: :validate_owner?
9
14
  end
10
15