doorkeeper 5.5.4 → 5.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +111 -8
  3. data/README.md +5 -9
  4. data/app/controllers/doorkeeper/authorizations_controller.rb +34 -11
  5. data/app/controllers/doorkeeper/tokens_controller.rb +28 -6
  6. data/app/views/doorkeeper/authorizations/error.html.erb +3 -1
  7. data/app/views/doorkeeper/authorizations/form_post.html.erb +1 -1
  8. data/app/views/doorkeeper/authorizations/new.html.erb +16 -16
  9. data/config/locales/en.yml +4 -1
  10. data/lib/doorkeeper/config/abstract_builder.rb +1 -1
  11. data/lib/doorkeeper/config/validations.rb +15 -3
  12. data/lib/doorkeeper/config.rb +95 -55
  13. data/lib/doorkeeper/engine.rb +10 -3
  14. data/lib/doorkeeper/errors.rb +32 -0
  15. data/lib/doorkeeper/helpers/controller.rb +1 -1
  16. data/lib/doorkeeper/models/access_token_mixin.rb +71 -9
  17. data/lib/doorkeeper/models/concerns/expiration_time_sql_math.rb +88 -0
  18. data/lib/doorkeeper/models/concerns/polymorphic_resource_owner.rb +30 -0
  19. data/lib/doorkeeper/oauth/authorization/code.rb +7 -1
  20. data/lib/doorkeeper/oauth/authorization/token.rb +7 -1
  21. data/lib/doorkeeper/oauth/authorization_code_request.rb +36 -12
  22. data/lib/doorkeeper/oauth/base_request.rb +14 -12
  23. data/lib/doorkeeper/oauth/client.rb +1 -1
  24. data/lib/doorkeeper/oauth/client_credentials/creator.rb +13 -13
  25. data/lib/doorkeeper/oauth/client_credentials/issuer.rb +5 -4
  26. data/lib/doorkeeper/oauth/client_credentials/validator.rb +4 -5
  27. data/lib/doorkeeper/oauth/client_credentials_request.rb +10 -2
  28. data/lib/doorkeeper/oauth/code_request.rb +1 -1
  29. data/lib/doorkeeper/oauth/error.rb +4 -3
  30. data/lib/doorkeeper/oauth/error_response.rb +19 -4
  31. data/lib/doorkeeper/oauth/helpers/uri_checker.rb +4 -4
  32. data/lib/doorkeeper/oauth/invalid_request_response.rb +4 -0
  33. data/lib/doorkeeper/oauth/password_access_token_request.rb +6 -6
  34. data/lib/doorkeeper/oauth/pre_authorization.rb +31 -23
  35. data/lib/doorkeeper/oauth/refresh_token_request.rb +17 -9
  36. data/lib/doorkeeper/oauth/scopes.rb +55 -1
  37. data/lib/doorkeeper/oauth/token_introspection.rb +34 -20
  38. data/lib/doorkeeper/oauth/token_request.rb +1 -1
  39. data/lib/doorkeeper/oauth/token_response.rb +5 -3
  40. data/lib/doorkeeper/orm/active_record/mixins/access_grant.rb +0 -6
  41. data/lib/doorkeeper/orm/active_record/mixins/access_token.rb +21 -4
  42. data/lib/doorkeeper/orm/active_record/mixins/application.rb +22 -4
  43. data/lib/doorkeeper/orm/active_record/redirect_uri_validator.rb +2 -2
  44. data/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +5 -2
  45. data/lib/doorkeeper/orm/active_record.rb +30 -37
  46. data/lib/doorkeeper/rails/routes.rb +12 -3
  47. data/lib/doorkeeper/rake/setup.rake +0 -5
  48. data/lib/doorkeeper/revocable_tokens/revocable_access_token.rb +21 -0
  49. data/lib/doorkeeper/revocable_tokens/revocable_refresh_token.rb +21 -0
  50. data/lib/doorkeeper/version.rb +2 -2
  51. data/lib/doorkeeper.rb +78 -5
  52. data/lib/generators/doorkeeper/remove_applications_secret_not_null_constraint_generator.rb +33 -0
  53. data/lib/generators/doorkeeper/templates/initializer.rb +44 -6
  54. data/lib/generators/doorkeeper/templates/migration.rb.erb +15 -4
  55. data/lib/generators/doorkeeper/templates/remove_applications_secret_not_null_constraint.rb.erb +7 -0
  56. metadata +28 -21
@@ -6,6 +6,8 @@ module Doorkeeper
6
6
  include Enumerable
7
7
  include Comparable
8
8
 
9
+ DYNAMIC_SCOPE_WILDCARD = "*"
10
+
9
11
  def self.from_string(string)
10
12
  string ||= ""
11
13
  new.tap do |scope|
@@ -26,7 +28,15 @@ module Doorkeeper
26
28
  end
27
29
 
28
30
  def exists?(scope)
29
- @scopes.include? scope.to_s
31
+ scope = scope.to_s
32
+
33
+ @scopes.any? do |allowed_scope|
34
+ if dynamic_scopes_enabled? && dynamic_scopes_present?(allowed_scope, scope)
35
+ dynamic_scope_match?(allowed_scope, scope)
36
+ else
37
+ allowed_scope == scope
38
+ end
39
+ end
30
40
  end
31
41
 
32
42
  def add(*scopes)
@@ -60,12 +70,56 @@ module Doorkeeper
60
70
  end
61
71
  end
62
72
 
73
+ # DEPRECATED: With dynamic scopes, #allowed should be called because
74
+ # A & B doesn't really make sense with dynamic scopes.
75
+ #
76
+ # For example, if A = user:* and B is user:1, A & B = [].
77
+ # If we modified this method to take dynamic scopes into an account, then order
78
+ # becomes important, and this would violate the principle that A & B = B & A.
63
79
  def &(other)
80
+ return allowed(other) if dynamic_scopes_enabled?
81
+
64
82
  self.class.from_array(all & to_array(other))
65
83
  end
66
84
 
85
+ # Returns a set of scopes that are allowed, taking dynamic
86
+ # scopes into account. This instance's scopes is taken as the allowed set,
87
+ # and the passed value is the set to filter.
88
+ #
89
+ # @param other The set of scopes to filter
90
+ def allowed(other)
91
+ filtered_scopes = other.select { |scope| self.exists?(scope) }
92
+ self.class.from_array(filtered_scopes)
93
+ end
94
+
67
95
  private
68
96
 
97
+ def dynamic_scopes_enabled?
98
+ Doorkeeper.config.enable_dynamic_scopes?
99
+ end
100
+
101
+ def dynamic_scope_delimiter
102
+ return unless dynamic_scopes_enabled?
103
+
104
+ @dynamic_scope_delimiter ||= Doorkeeper.config.dynamic_scopes_delimiter
105
+ end
106
+
107
+ def dynamic_scopes_present?(allowed, requested)
108
+ allowed.include?(dynamic_scope_delimiter) && requested.include?(dynamic_scope_delimiter)
109
+ end
110
+
111
+ def dynamic_scope_match?(allowed, requested)
112
+ allowed_pattern = allowed.split(dynamic_scope_delimiter, 2)
113
+ request_pattern = requested.split(dynamic_scope_delimiter, 2)
114
+
115
+ return false if allowed_pattern[0] != request_pattern[0]
116
+ return false if allowed_pattern[1].blank?
117
+ return false if request_pattern[1].blank?
118
+ return true if allowed_pattern[1] == DYNAMIC_SCOPE_WILDCARD && allowed_pattern[1].present?
119
+
120
+ allowed_pattern[1] == request_pattern[1]
121
+ end
122
+
69
123
  def to_array(other)
70
124
  case other
71
125
  when Scopes
@@ -6,26 +6,27 @@ module Doorkeeper
6
6
  #
7
7
  # @see https://datatracker.ietf.org/doc/html/rfc7662
8
8
  class TokenIntrospection
9
+ attr_reader :token, :error, :invalid_request_reason
10
+
9
11
  def initialize(server, token)
10
12
  @server = server
11
13
  @token = token
12
-
13
- authorize!
14
14
  end
15
15
 
16
16
  def authorized?
17
+ authorize!
17
18
  @error.blank?
18
19
  end
19
20
 
20
21
  def error_response
21
22
  return if @error.blank?
22
23
 
23
- if @error == :invalid_token
24
+ if @error == Errors::InvalidToken
24
25
  OAuth::InvalidTokenResponse.from_access_token(authorized_token)
25
- elsif @error == :invalid_request
26
+ elsif @error == Errors::InvalidRequest
26
27
  OAuth::InvalidRequestResponse.from_request(self)
27
28
  else
28
- OAuth::ErrorResponse.new(name: @error)
29
+ OAuth::ErrorResponse.from_request(self)
29
30
  end
30
31
  end
31
32
 
@@ -35,8 +36,7 @@ module Doorkeeper
35
36
 
36
37
  private
37
38
 
38
- attr_reader :server, :token
39
- attr_reader :error, :invalid_request_reason
39
+ attr_reader :server
40
40
 
41
41
  # If the protected resource uses OAuth 2.0 client credentials to
42
42
  # authenticate to the introspection endpoint and its credentials are
@@ -58,24 +58,38 @@ module Doorkeeper
58
58
  def authorize!
59
59
  # Requested client authorization
60
60
  if server.credentials
61
- @error = :invalid_client unless authorized_client
61
+ authorize_using_basic_auth!
62
62
  elsif authorized_token
63
- # Requested bearer token authorization
64
- #
65
- # If the protected resource uses an OAuth 2.0 bearer token to authorize
66
- # its call to the introspection endpoint and the token used for
67
- # authorization does not contain sufficient privileges or is otherwise
68
- # invalid for this request, the authorization server responds with an
69
- # HTTP 401 code as described in Section 3 of OAuth 2.0 Bearer Token
70
- # Usage [RFC6750].
71
- #
72
- @error = :invalid_token unless valid_authorized_token?
63
+ authorize_using_bearer_token!
73
64
  else
74
- @error = :invalid_request
65
+ @error = Errors::InvalidRequest
75
66
  @invalid_request_reason = :request_not_authorized
76
67
  end
77
68
  end
78
69
 
70
+ def authorize_using_basic_auth!
71
+ # Note that a properly formed and authorized query for an inactive or
72
+ # otherwise invalid token (or a token the protected resource is not
73
+ # allowed to know about) is not considered an error response by this
74
+ # specification. In these cases, the authorization server MUST instead
75
+ # respond with an introspection response with the "active" field set to
76
+ # "false" as described in Section 2.2.
77
+ @error = Errors::InvalidClient unless authorized_client
78
+ end
79
+
80
+ def authorize_using_bearer_token!
81
+ # Requested bearer token authorization
82
+ #
83
+ # If the protected resource uses an OAuth 2.0 bearer token to authorize
84
+ # its call to the introspection endpoint and the token used for
85
+ # authorization does not contain sufficient privileges or is otherwise
86
+ # invalid for this request, the authorization server responds with an
87
+ # HTTP 401 code as described in Section 3 of OAuth 2.0 Bearer Token
88
+ # Usage [RFC6750].
89
+ #
90
+ @error = Errors::InvalidToken unless valid_authorized_token?
91
+ end
92
+
79
93
  # Client Authentication
80
94
  def authorized_client
81
95
  @authorized_client ||= server.credentials && server.client
@@ -134,7 +148,7 @@ module Doorkeeper
134
148
  # Since resource servers using token introspection rely on the
135
149
  # authorization server to determine the state of a token, the
136
150
  # authorization server MUST perform all applicable checks against a
137
- # token's state. For instance, these tests include the following:
151
+ # token's state. For instance, these tests include the following:
138
152
  #
139
153
  # o If the token can expire, the authorization server MUST determine
140
154
  # whether or not the token has expired.
@@ -17,7 +17,7 @@ module Doorkeeper
17
17
  end
18
18
 
19
19
  def deny
20
- pre_auth.error = :access_denied
20
+ pre_auth.error = Errors::AccessDenied
21
21
  pre_auth.error_response
22
22
  end
23
23
  end
@@ -5,12 +5,14 @@ module Doorkeeper
5
5
  class TokenResponse
6
6
  attr_reader :token
7
7
 
8
+ alias issued_token token
9
+
8
10
  def initialize(token)
9
11
  @token = token
10
12
  end
11
13
 
12
14
  def body
13
- {
15
+ @body ||= {
14
16
  "access_token" => token.plaintext_token,
15
17
  "token_type" => token.token_type,
16
18
  "expires_in" => token.expires_in_seconds,
@@ -26,9 +28,9 @@ module Doorkeeper
26
28
 
27
29
  def headers
28
30
  {
29
- "Cache-Control" => "no-store",
30
- "Pragma" => "no-cache",
31
+ "Cache-Control" => "no-store, no-cache",
31
32
  "Content-Type" => "application/json; charset=utf-8",
33
+ "Pragma" => "no-cache",
32
34
  }
33
35
  end
34
36
  end
@@ -14,12 +14,6 @@ module Doorkeeper::Orm::ActiveRecord::Mixins
14
14
  optional: true,
15
15
  inverse_of: :access_grants
16
16
 
17
- if Doorkeeper.config.polymorphic_resource_owner?
18
- belongs_to :resource_owner, polymorphic: true, optional: false
19
- else
20
- validates :resource_owner_id, presence: true
21
- end
22
-
23
17
  validates :application_id,
24
18
  :token,
25
19
  :expires_in,
@@ -14,10 +14,6 @@ module Doorkeeper::Orm::ActiveRecord::Mixins
14
14
  inverse_of: :access_tokens,
15
15
  optional: true
16
16
 
17
- if Doorkeeper.config.polymorphic_resource_owner?
18
- belongs_to :resource_owner, polymorphic: true, optional: true
19
- end
20
-
21
17
  validates :token, presence: true, uniqueness: { case_sensitive: true }
22
18
  validates :refresh_token, uniqueness: { case_sensitive: true }, if: :use_refresh_token?
23
19
 
@@ -48,6 +44,27 @@ module Doorkeeper::Orm::ActiveRecord::Mixins
48
44
  column_names.include?("previous_refresh_token")
49
45
  end
50
46
 
47
+ # Returns non-expired and non-revoked access tokens
48
+ def not_expired
49
+ relation = where(revoked_at: nil)
50
+
51
+ if supports_expiration_time_math?
52
+ # have not reached the expiration time or it never expires
53
+ relation.where("#{expiration_time_sql} > ?", Time.now.utc).or(
54
+ relation.where(expires_in: nil)
55
+ )
56
+ else
57
+ ::Kernel.warn <<~WARNING.squish
58
+ [DOORKEEPER] Doorkeeper doesn't support expiration time math for your database adapter (#{adapter_name}).
59
+ Please add a class method `custom_expiration_time_sql` for your AccessToken class/mixin to provide a custom
60
+ SQL expression to calculate access token expiration time. See lib/doorkeeper/orm/active_record/mixins/access_token.rb
61
+ for more details.
62
+ WARNING
63
+
64
+ relation
65
+ end
66
+ end
67
+
51
68
  private
52
69
 
53
70
  def compute_doorkeeper_table_name
@@ -20,11 +20,13 @@ module Doorkeeper::Orm::ActiveRecord::Mixins
20
20
  dependent: :delete_all,
21
21
  class_name: Doorkeeper.config.access_token_class.to_s
22
22
 
23
- validates :name, :secret, :uid, presence: true
23
+ validates :name, :uid, presence: true
24
+ validates :secret, presence: true, if: -> { secret_required? }
24
25
  validates :uid, uniqueness: { case_sensitive: true }
25
- validates :redirect_uri, "doorkeeper/redirect_uri": true
26
26
  validates :confidential, inclusion: { in: [true, false] }
27
27
 
28
+ validates_with Doorkeeper::RedirectUriValidator, attributes: [:redirect_uri]
29
+
28
30
  validate :scopes_match_configured, if: :enforce_scopes?
29
31
 
30
32
  before_validation :generate_uid, :generate_secret, on: :create
@@ -44,7 +46,7 @@ module Doorkeeper::Orm::ActiveRecord::Mixins
44
46
  # @return [String] new transformed secret value
45
47
  #
46
48
  def renew_secret
47
- @raw_secret = Doorkeeper::OAuth::Helpers::UniqueToken.generate
49
+ @raw_secret = secret_generator.generate
48
50
  secret_strategy.store_secret(self, :secret, @raw_secret)
49
51
  end
50
52
 
@@ -102,12 +104,23 @@ module Doorkeeper::Orm::ActiveRecord::Mixins
102
104
 
103
105
  private
104
106
 
107
+ def secret_generator
108
+ generator_name = Doorkeeper.config.application_secret_generator
109
+ generator = generator_name.constantize
110
+
111
+ return generator if generator.respond_to?(:generate)
112
+
113
+ raise Errors::UnableToGenerateToken, "#{generator} does not respond to `.generate`."
114
+ rescue NameError
115
+ raise Errors::TokenGeneratorNotFound, "#{generator_name} not found"
116
+ end
117
+
105
118
  def generate_uid
106
119
  self.uid = Doorkeeper::OAuth::Helpers::UniqueToken.generate if uid.blank?
107
120
  end
108
121
 
109
122
  def generate_secret
110
- return if secret.present?
123
+ return if secret.present? || !secret_required?
111
124
 
112
125
  renew_secret
113
126
  end
@@ -125,6 +138,11 @@ module Doorkeeper::Orm::ActiveRecord::Mixins
125
138
  Doorkeeper.config.enforce_configured_scopes?
126
139
  end
127
140
 
141
+ def secret_required?
142
+ confidential? ||
143
+ !self.class.columns.detect { |column| column.name == "secret" }&.null
144
+ end
145
+
128
146
  # Helper method to extract collection of serializable attribute names
129
147
  # considering serialization options (like `only`, `except` and so on).
130
148
  #
@@ -45,11 +45,11 @@ module Doorkeeper
45
45
  end
46
46
 
47
47
  def unspecified_host?(uri)
48
- uri.is_a?(URI::HTTP) && uri.host.nil?
48
+ uri.is_a?(URI::HTTP) && uri.host.blank?
49
49
  end
50
50
 
51
51
  def relative_uri?(uri)
52
- uri.scheme.nil? && uri.host.nil?
52
+ uri.scheme.nil? && uri.host.blank?
53
53
  end
54
54
 
55
55
  def invalid_ssl_uri?(uri)
@@ -15,7 +15,8 @@ module Doorkeeper
15
15
  def clean_revoked
16
16
  table = @base_scope.arel_table
17
17
 
18
- @base_scope.where.not(revoked_at: nil)
18
+ @base_scope
19
+ .where.not(revoked_at: nil)
19
20
  .where(table[:revoked_at].lt(Time.current))
20
21
  .in_batches(&:delete_all)
21
22
  end
@@ -24,7 +25,9 @@ module Doorkeeper
24
25
  def clean_expired(ttl)
25
26
  table = @base_scope.arel_table
26
27
 
27
- @base_scope.where(table[:created_at].lt(Time.current - ttl))
28
+ @base_scope
29
+ .where.not(expires_in: nil)
30
+ .where(table[:created_at].lt(Time.current - ttl))
28
31
  .in_batches(&:delete_all)
29
32
  end
30
33
  end
@@ -1,51 +1,44 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/lazy_load_hooks"
4
-
5
3
  module Doorkeeper
4
+ autoload :AccessGrant, "doorkeeper/orm/active_record/access_grant"
5
+ autoload :AccessToken, "doorkeeper/orm/active_record/access_token"
6
+ autoload :Application, "doorkeeper/orm/active_record/application"
7
+ autoload :RedirectUriValidator, "doorkeeper/orm/active_record/redirect_uri_validator"
8
+
9
+ module Models
10
+ autoload :Ownership, "doorkeeper/models/concerns/ownership"
11
+ end
12
+
13
+ # ActiveRecord ORM for Doorkeeper entity models.
14
+ # Consists of three main OAuth entities:
15
+ # * Access Token
16
+ # * Access Grant
17
+ # * Application (client)
18
+ #
19
+ # Do a lazy loading of all the required and configured stuff.
20
+ #
6
21
  module Orm
7
- # ActiveRecord ORM for Doorkeeper entity models.
8
- # Consists of three main OAuth entities:
9
- # * Access Token
10
- # * Access Grant
11
- # * Application (client)
12
- #
13
- # Do a lazy loading of all the required and configured stuff.
14
- #
15
22
  module ActiveRecord
16
- def self.initialize_models!
17
- lazy_load do
18
- require "doorkeeper/orm/active_record/stale_records_cleaner"
19
- require "doorkeeper/orm/active_record/access_grant"
20
- require "doorkeeper/orm/active_record/access_token"
21
- require "doorkeeper/orm/active_record/application"
23
+ autoload :StaleRecordsCleaner, "doorkeeper/orm/active_record/stale_records_cleaner"
22
24
 
23
- if (options = Doorkeeper.config.active_record_options[:establish_connection])
24
- Doorkeeper::Orm::ActiveRecord.models.each do |model|
25
- model.establish_connection(options)
26
- end
27
- end
28
- end
25
+ module Mixins
26
+ autoload :AccessGrant, "doorkeeper/orm/active_record/mixins/access_grant"
27
+ autoload :AccessToken, "doorkeeper/orm/active_record/mixins/access_token"
28
+ autoload :Application, "doorkeeper/orm/active_record/mixins/application"
29
29
  end
30
30
 
31
- def self.initialize_application_owner!
32
- lazy_load do
33
- require "doorkeeper/models/concerns/ownership"
34
-
35
- Doorkeeper.config.application_model.include(Doorkeeper::Models::Ownership)
36
- end
31
+ def self.run_hooks
32
+ initialize_configured_associations
37
33
  end
38
34
 
39
- def self.lazy_load(&block)
40
- ActiveSupport.on_load(:active_record, {}, &block)
41
- end
35
+ def self.initialize_configured_associations
36
+ if Doorkeeper.config.enable_application_owner?
37
+ Doorkeeper.config.application_model.include ::Doorkeeper::Models::Ownership
38
+ end
42
39
 
43
- def self.models
44
- [
45
- Doorkeeper.config.access_grant_model,
46
- Doorkeeper.config.access_token_model,
47
- Doorkeeper.config.application_model,
48
- ]
40
+ Doorkeeper.config.access_grant_model.include ::Doorkeeper::Models::PolymorphicResourceOwner::ForAccessGrant
41
+ Doorkeeper.config.access_token_model.include ::Doorkeeper::Models::PolymorphicResourceOwner::ForAccessToken
49
42
  end
50
43
  end
51
44
  end
@@ -36,7 +36,7 @@ module Doorkeeper
36
36
  map_route(:authorizations, :authorization_routes)
37
37
  map_route(:tokens, :token_routes)
38
38
  map_route(:tokens, :revoke_routes)
39
- map_route(:tokens, :introspect_routes) unless Doorkeeper.config.allow_token_introspection.is_a?(FalseClass)
39
+ map_route(:tokens, :introspect_routes) if introspection_routes?
40
40
  map_route(:applications, :application_routes)
41
41
  map_route(:authorized_applications, :authorized_applications_routes)
42
42
  map_route(:token_info, :token_info_routes)
@@ -53,8 +53,8 @@ module Doorkeeper
53
53
  as: mapping[:as],
54
54
  controller: mapping[:controllers],
55
55
  ) do
56
- routes.get "/native", action: :show, on: :member
57
- routes.get "/", action: :new, on: :member
56
+ routes.get native_authorization_code_route, action: :show, on: :member
57
+ routes.get '/', action: :new, on: :member
58
58
  end
59
59
  end
60
60
 
@@ -96,6 +96,15 @@ module Doorkeeper
96
96
  only: %i[index destroy],
97
97
  controller: mapping[:controllers]
98
98
  end
99
+
100
+ def native_authorization_code_route
101
+ Doorkeeper.configuration.native_authorization_code_route
102
+ end
103
+
104
+ def introspection_routes?
105
+ Doorkeeper.configured? &&
106
+ !Doorkeeper.config.allow_token_introspection.is_a?(FalseClass)
107
+ end
99
108
  end
100
109
  end
101
110
  end
@@ -2,10 +2,5 @@
2
2
 
3
3
  namespace :doorkeeper do
4
4
  task setup: :environment do
5
- # Dirty hack to manually initialize AR because of lazy auto-loading,
6
- # in other case we'll see NameError: uninitialized constant Doorkeeper::AccessToken
7
- if Doorkeeper.config.orm == :active_record && defined?(::ActiveRecord::Base)
8
- Object.const_get("::ActiveRecord::Base")
9
- end
10
5
  end
11
6
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper
4
+ module RevocableTokens
5
+ class RevocableAccessToken
6
+ attr_reader :token
7
+
8
+ def initialize(token)
9
+ @token = token
10
+ end
11
+
12
+ def revocable?
13
+ token.accessible?
14
+ end
15
+
16
+ def revoke
17
+ token.revoke
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper
4
+ module RevocableTokens
5
+ class RevocableRefreshToken
6
+ attr_reader :token
7
+
8
+ def initialize(token)
9
+ @token = token
10
+ end
11
+
12
+ def revocable?
13
+ !token.revoked?
14
+ end
15
+
16
+ def revoke
17
+ token.revoke
18
+ end
19
+ end
20
+ end
21
+ end
@@ -4,8 +4,8 @@ module Doorkeeper
4
4
  module VERSION
5
5
  # Semantic versioning
6
6
  MAJOR = 5
7
- MINOR = 5
8
- TINY = 4
7
+ MINOR = 8
8
+ TINY = 1
9
9
  PRE = nil
10
10
 
11
11
  # Full version number
data/lib/doorkeeper.rb CHANGED
@@ -34,6 +34,11 @@ module Doorkeeper
34
34
  autoload :Token, "doorkeeper/request/token"
35
35
  end
36
36
 
37
+ module RevocableTokens
38
+ autoload :RevocableAccessToken, "doorkeeper/revocable_tokens/revocable_access_token"
39
+ autoload :RevocableRefreshToken, "doorkeeper/revocable_tokens/revocable_refresh_token"
40
+ end
41
+
37
42
  module OAuth
38
43
  autoload :BaseRequest, "doorkeeper/oauth/base_request"
39
44
  autoload :AuthorizationCodeRequest, "doorkeeper/oauth/authorization_code_request"
@@ -88,7 +93,9 @@ module Doorkeeper
88
93
  module Models
89
94
  autoload :Accessible, "doorkeeper/models/concerns/accessible"
90
95
  autoload :Expirable, "doorkeeper/models/concerns/expirable"
96
+ autoload :ExpirationTimeSqlMath, "doorkeeper/models/concerns/expiration_time_sql_math"
91
97
  autoload :Orderable, "doorkeeper/models/concerns/orderable"
98
+ autoload :PolymorphicResourceOwner, "doorkeeper/models/concerns/polymorphic_resource_owner"
92
99
  autoload :Scopes, "doorkeeper/models/concerns/scopes"
93
100
  autoload :Reusable, "doorkeeper/models/concerns/reusable"
94
101
  autoload :ResourceOwnerable, "doorkeeper/models/concerns/resource_ownerable"
@@ -112,11 +119,77 @@ module Doorkeeper
112
119
  autoload :BCrypt, "doorkeeper/secret_storing/bcrypt"
113
120
  end
114
121
 
115
- def self.authenticate(request, methods = Doorkeeper.config.access_token_methods)
116
- OAuth::Token.authenticate(request, *methods)
117
- end
122
+ class << self
123
+ attr_reader :orm_adapter
124
+
125
+ def configure(&block)
126
+ @config = Config::Builder.new(&block).build
127
+ setup
128
+ @config
129
+ end
130
+
131
+ # @return [Doorkeeper::Config] configuration instance
132
+ #
133
+ def configuration
134
+ @config || configure
135
+ end
136
+
137
+ def configured?
138
+ !@config.nil?
139
+ end
140
+
141
+ alias config configuration
142
+
143
+ def setup
144
+ setup_orm_adapter
145
+
146
+ # Deprecated, will be removed soon
147
+ unless configuration.orm == :active_record
148
+ setup_orm_models
149
+ setup_application_owner
150
+ end
151
+ end
118
152
 
119
- def self.gem_version
120
- ::Gem::Version.new(::Doorkeeper::VERSION::STRING)
153
+ def setup_orm_adapter
154
+ @orm_adapter = "doorkeeper/orm/#{configuration.orm}".classify.constantize
155
+ rescue NameError => e
156
+ raise e, "ORM adapter not found (#{configuration.orm})", <<-ERROR_MSG.strip_heredoc
157
+ [DOORKEEPER] ORM adapter not found (#{configuration.orm}), or there was an error
158
+ trying to load it.
159
+
160
+ You probably need to add the related gem for this adapter to work with
161
+ doorkeeper.
162
+ ERROR_MSG
163
+ end
164
+
165
+ def run_orm_hooks
166
+ config.clear_cache!
167
+
168
+ if @orm_adapter.respond_to?(:run_hooks)
169
+ @orm_adapter.run_hooks
170
+ else
171
+ ::Kernel.warn <<~MSG.strip_heredoc
172
+ [DOORKEEPER] ORM "#{configuration.orm}" should move all it's setup logic under `#run_hooks` method for
173
+ the #{@orm_adapter.name}. Later versions of Doorkeeper will no longer support `setup_orm_models` and
174
+ `setup_application_owner` API.
175
+ MSG
176
+ end
177
+ end
178
+
179
+ def setup_orm_models
180
+ @orm_adapter.initialize_models!
181
+ end
182
+
183
+ def setup_application_owner
184
+ @orm_adapter.initialize_application_owner!
185
+ end
186
+
187
+ def authenticate(request, methods = Doorkeeper.config.access_token_methods)
188
+ OAuth::Token.authenticate(request, *methods)
189
+ end
190
+
191
+ def gem_version
192
+ ::Gem::Version.new(::Doorkeeper::VERSION::STRING)
193
+ end
121
194
  end
122
195
  end