doorkeeper 4.2.0 → 4.2.5

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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +5 -0
  4. data/.travis.yml +11 -6
  5. data/Appraisals +14 -0
  6. data/Gemfile +4 -8
  7. data/NEWS.md +10 -1
  8. data/README.md +6 -1
  9. data/app/controllers/doorkeeper/applications_controller.rb +1 -5
  10. data/app/views/doorkeeper/applications/_delete_form.html.erb +1 -2
  11. data/app/views/doorkeeper/authorized_applications/_delete_form.html.erb +1 -2
  12. data/config/locales/en.yml +2 -2
  13. data/doorkeeper.gemspec +9 -7
  14. data/gemfiles/rails_4_2.gemfile +11 -0
  15. data/gemfiles/rails_5_0.gemfile +12 -0
  16. data/gemfiles/rails_5_1.gemfile +13 -0
  17. data/lib/doorkeeper.rb +7 -1
  18. data/lib/doorkeeper/config.rb +55 -0
  19. data/lib/doorkeeper/grape/helpers.rb +2 -1
  20. data/lib/doorkeeper/helpers/controller.rb +6 -6
  21. data/lib/doorkeeper/models/access_grant_mixin.rb +12 -0
  22. data/lib/doorkeeper/models/access_token_mixin.rb +117 -1
  23. data/lib/doorkeeper/models/application_mixin.rb +18 -2
  24. data/lib/doorkeeper/models/concerns/accessible.rb +4 -0
  25. data/lib/doorkeeper/models/concerns/expirable.rb +8 -0
  26. data/lib/doorkeeper/models/concerns/revocable.rb +18 -0
  27. data/lib/doorkeeper/oauth/authorization/uri_builder.rb +20 -18
  28. data/lib/doorkeeper/oauth/authorization_code_request.rb +1 -4
  29. data/lib/doorkeeper/oauth/{request_concern.rb → base_request.rb} +3 -1
  30. data/lib/doorkeeper/oauth/base_response.rb +29 -0
  31. data/lib/doorkeeper/oauth/client.rb +0 -1
  32. data/lib/doorkeeper/oauth/client/credentials.rb +17 -6
  33. data/lib/doorkeeper/oauth/client_credentials/creator.rb +1 -1
  34. data/lib/doorkeeper/oauth/client_credentials/issuer.rb +1 -1
  35. data/lib/doorkeeper/oauth/client_credentials/validation.rb +1 -1
  36. data/lib/doorkeeper/oauth/client_credentials_request.rb +1 -4
  37. data/lib/doorkeeper/oauth/code_response.rb +7 -6
  38. data/lib/doorkeeper/oauth/error_response.rb +9 -8
  39. data/lib/doorkeeper/oauth/invalid_token_response.rb +2 -0
  40. data/lib/doorkeeper/oauth/password_access_token_request.rb +1 -3
  41. data/lib/doorkeeper/oauth/refresh_token_request.rb +3 -7
  42. data/lib/doorkeeper/oauth/token.rb +17 -19
  43. data/lib/doorkeeper/oauth/token_request.rb +1 -2
  44. data/lib/doorkeeper/orm/active_record/access_token.rb +17 -0
  45. data/lib/doorkeeper/orm/active_record/application.rb +10 -5
  46. data/lib/doorkeeper/rails/helpers.rb +0 -2
  47. data/lib/doorkeeper/rails/routes.rb +3 -4
  48. data/lib/doorkeeper/rails/routes/mapper.rb +3 -3
  49. data/lib/doorkeeper/rails/routes/mapping.rb +1 -1
  50. data/lib/doorkeeper/request/authorization_code.rb +7 -1
  51. data/lib/doorkeeper/request/refresh_token.rb +1 -1
  52. data/lib/doorkeeper/server.rb +0 -8
  53. data/lib/doorkeeper/version.rb +1 -1
  54. data/spec/controllers/authorizations_controller_spec.rb +17 -3
  55. data/spec/lib/doorkeeper_spec.rb +135 -13
  56. data/spec/lib/oauth/authorization/uri_builder_spec.rb +1 -2
  57. data/spec/lib/oauth/base_request_spec.rb +160 -0
  58. data/spec/lib/oauth/base_response_spec.rb +45 -0
  59. data/spec/lib/oauth/client/credentials_spec.rb +41 -0
  60. data/spec/lib/oauth/error_response_spec.rb +9 -9
  61. data/spec/lib/oauth/invalid_token_response_spec.rb +36 -8
  62. data/spec/lib/server_spec.rb +0 -3
  63. data/spec/requests/endpoints/authorization_spec.rb +5 -6
  64. data/spec/requests/flows/authorization_code_spec.rb +4 -12
  65. data/spec/spec_helper.rb +2 -0
  66. data/spec/spec_helper_integration.rb +5 -0
  67. data/spec/support/helpers/request_spec_helper.rb +12 -4
  68. data/spec/support/http_method_shim.rb +20 -6
  69. metadata +43 -21
  70. data/lib/doorkeeper/oauth/client/methods.rb +0 -18
  71. data/spec/lib/oauth/client/methods_spec.rb +0 -54
@@ -27,6 +27,14 @@ module Doorkeeper
27
27
  end
28
28
 
29
29
  module ClassMethods
30
+ # Searches for Doorkeeper::AccessGrant record with the
31
+ # specific token value.
32
+ #
33
+ # @param token [#to_s] token value (any object that responds to `#to_s`)
34
+ #
35
+ # @return [Doorkeeper::AccessGrant, nil] AccessGrant object or nil
36
+ # if there is no record with such token
37
+ #
30
38
  def by_token(token)
31
39
  find_by(token: token.to_s)
32
40
  end
@@ -34,6 +42,10 @@ module Doorkeeper
34
42
 
35
43
  private
36
44
 
45
+ # Generates token value with UniqueToken class.
46
+ #
47
+ # @return [String] token value
48
+ #
37
49
  def generate_token
38
50
  self.token = UniqueToken.generate
39
51
  end
@@ -23,6 +23,8 @@ module Doorkeeper
23
23
  validates :token, presence: true, uniqueness: true
24
24
  validates :refresh_token, uniqueness: true, if: :use_refresh_token?
25
25
 
26
+ # @attr_writer [Boolean, nil] use_refresh_token
27
+ # indicates the possibility of using refresh token
26
28
  attr_writer :use_refresh_token
27
29
 
28
30
  before_validation :generate_token, on: :create
@@ -32,14 +34,40 @@ module Doorkeeper
32
34
  end
33
35
 
34
36
  module ClassMethods
37
+ # Returns an instance of the Doorkeeper::AccessToken with
38
+ # specific token value.
39
+ #
40
+ # @param token [#to_s]
41
+ # token value (any object that responds to `#to_s`)
42
+ #
43
+ # @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
44
+ # if there is no record with such token
45
+ #
35
46
  def by_token(token)
36
47
  find_by(token: token.to_s)
37
48
  end
38
49
 
50
+ # Returns an instance of the Doorkeeper::AccessToken
51
+ # with specific token value.
52
+ #
53
+ # @param refresh_token [#to_s]
54
+ # refresh token value (any object that responds to `#to_s`)
55
+ #
56
+ # @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
57
+ # if there is no record with such refresh token
58
+ #
39
59
  def by_refresh_token(refresh_token)
40
60
  find_by(refresh_token: refresh_token.to_s)
41
61
  end
42
62
 
63
+ # Revokes AccessToken records that have not been revoked and associated
64
+ # with the specific Application and Resource Owner.
65
+ #
66
+ # @param application_id [Integer]
67
+ # ID of the Application
68
+ # @param resource_owner [ActiveRecord::Base]
69
+ # instance of the Resource Owner model
70
+ #
43
71
  def revoke_all_for(application_id, resource_owner)
44
72
  where(application_id: application_id,
45
73
  resource_owner_id: resource_owner.id,
@@ -47,6 +75,19 @@ module Doorkeeper
47
75
  each(&:revoke)
48
76
  end
49
77
 
78
+ # Looking for not expired Access Token with a matching set of scopes
79
+ # that belongs to specific Application and Resource Owner.
80
+ #
81
+ # @param application [Doorkeeper::Application]
82
+ # Application instance
83
+ # @param resource_owner_or_id [ActiveRecord::Base, Integer]
84
+ # Resource Owner model instance or it's ID
85
+ # @param scopes [String, Doorkeeper::OAuth::Scopes]
86
+ # set of scopes
87
+ #
88
+ # @return [Doorkeeper::AccessToken, nil] Access Token instance or
89
+ # nil if matching record was not found
90
+ #
50
91
  def matching_token_for(application, resource_owner_or_id, scopes)
51
92
  resource_owner_id = if resource_owner_or_id.respond_to?(:to_key)
52
93
  resource_owner_or_id.id
@@ -59,6 +100,19 @@ module Doorkeeper
59
100
  end
60
101
  end
61
102
 
103
+ # Checks whether the token scopes match the scopes from the parameters or
104
+ # Application scopes (if present).
105
+ #
106
+ # @param token_scopes [#to_s]
107
+ # set of scopes (any object that responds to `#to_s`)
108
+ # @param param_scopes [String]
109
+ # scopes from params
110
+ # @param app_scopes [String]
111
+ # Application scopes
112
+ #
113
+ # @return [Boolean] true if all scopes and blank or matches
114
+ # and false in other cases
115
+ #
62
116
  def scopes_match?(token_scopes, param_scopes, app_scopes)
63
117
  (!token_scopes.present? && !param_scopes.present?) ||
64
118
  Doorkeeper::OAuth::Helpers::ScopeChecker.match?(
@@ -68,6 +122,23 @@ module Doorkeeper
68
122
  )
69
123
  end
70
124
 
125
+ # Looking for not expired AccessToken record with a matching set of
126
+ # scopes that belongs to specific Application and Resource Owner.
127
+ # If it doesn't exists - then creates it.
128
+ #
129
+ # @param application [Doorkeeper::Application]
130
+ # Application instance
131
+ # @param resource_owner_id [ActiveRecord::Base, Integer]
132
+ # Resource Owner model instance or it's ID
133
+ # @param scopes [#to_s]
134
+ # set of scopes (any object that responds to `#to_s`)
135
+ # @param expires_in [Integer]
136
+ # token lifetime in seconds
137
+ # @param use_refresh_token [Boolean]
138
+ # whether to use the refresh token
139
+ #
140
+ # @return [Doorkeeper::AccessToken] existing record or a new one
141
+ #
71
142
  def find_or_create_for(application, resource_owner_id, scopes, expires_in, use_refresh_token)
72
143
  if Doorkeeper.configuration.reuse_access_token
73
144
  access_token = matching_token_for(application, resource_owner_id, scopes)
@@ -85,6 +156,17 @@ module Doorkeeper
85
156
  )
86
157
  end
87
158
 
159
+ # Looking for not revoked Access Token record that belongs to specific
160
+ # Application and Resource Owner.
161
+ #
162
+ # @param application_id [Integer]
163
+ # ID of the Application model instance
164
+ # @param resource_owner_id [Integer]
165
+ # ID of the Resource Owner model instance
166
+ #
167
+ # @return [Doorkeeper::AccessToken, nil] matching AccessToken object or
168
+ # nil if nothing was found
169
+ #
88
170
  def last_authorized_token_for(application_id, resource_owner_id)
89
171
  send(order_method, created_at_desc).
90
172
  find_by(application_id: application_id,
@@ -93,6 +175,10 @@ module Doorkeeper
93
175
  end
94
176
  end
95
177
 
178
+ # Access Token type: Bearer.
179
+ # @see https://tools.ietf.org/html/rfc6750
180
+ # The OAuth 2.0 Authorization Framework: Bearer Token Usage
181
+ #
96
182
  def token_type
97
183
  'bearer'
98
184
  end
@@ -102,6 +188,9 @@ module Doorkeeper
102
188
  !!@use_refresh_token
103
189
  end
104
190
 
191
+ # JSON representation of the Access Token instance.
192
+ #
193
+ # @return [Hash] hash with token data
105
194
  def as_json(_options = {})
106
195
  {
107
196
  resource_owner_id: resource_owner_id,
@@ -112,22 +201,49 @@ module Doorkeeper
112
201
  }
113
202
  end
114
203
 
115
- # It indicates whether the tokens have the same credential
204
+ # Indicates whether the token instance have the same credential
205
+ # as the other Access Token.
206
+ #
207
+ # @param access_token [Doorkeeper::AccessToken] other token
208
+ #
209
+ # @return [Boolean] true if credentials are same of false in other cases
210
+ #
116
211
  def same_credential?(access_token)
117
212
  application_id == access_token.application_id &&
118
213
  resource_owner_id == access_token.resource_owner_id
119
214
  end
120
215
 
216
+ # Indicates if token is acceptable for specific scopes.
217
+ #
218
+ # @param scopes [Array<String>] scopes
219
+ #
220
+ # @return [Boolean] true if record is accessible and includes scopes or
221
+ # false in other cases
222
+ #
121
223
  def acceptable?(scopes)
122
224
  accessible? && includes_scope?(*scopes)
123
225
  end
124
226
 
125
227
  private
126
228
 
229
+ # Generates refresh token with UniqueToken generator.
230
+ #
231
+ # @return [String] refresh token value
232
+ #
127
233
  def generate_refresh_token
128
234
  write_attribute :refresh_token, UniqueToken.generate
129
235
  end
130
236
 
237
+ # Generates and sets the token value with the
238
+ # configured Generator class (see Doorkeeper.configuration).
239
+ #
240
+ # @return [String] generated token value
241
+ #
242
+ # @raise [Doorkeeper::Errors::UnableToGenerateToken]
243
+ # custom class doesn't implement .generate method
244
+ # @raise [Doorkeeper::Errors::TokenGeneratorNotFound]
245
+ # custom class doesn't exist
246
+ #
131
247
  def generate_token
132
248
  self.created_at ||= Time.now.utc
133
249
 
@@ -7,8 +7,8 @@ module Doorkeeper
7
7
  include ActiveModel::MassAssignmentSecurity if defined?(::ProtectedAttributes)
8
8
 
9
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'
10
+ has_many :access_grants, dependent: :delete_all, class_name: 'Doorkeeper::AccessGrant'
11
+ has_many :access_tokens, dependent: :delete_all, class_name: 'Doorkeeper::AccessToken'
12
12
 
13
13
  validates :name, :secret, :uid, presence: true
14
14
  validates :uid, uniqueness: true
@@ -18,10 +18,26 @@ module Doorkeeper
18
18
  end
19
19
 
20
20
  module ClassMethods
21
+ # Returns an instance of the Doorkeeper::Application with
22
+ # specific UID and secret.
23
+ #
24
+ # @param uid [#to_s] UID (any object that responds to `#to_s`)
25
+ # @param secret [#to_s] secret (any object that responds to `#to_s`)
26
+ #
27
+ # @return [Doorkeeper::Application, nil] Application instance or nil
28
+ # if there is no record with such credentials
29
+ #
21
30
  def by_uid_and_secret(uid, secret)
22
31
  find_by(uid: uid.to_s, secret: secret.to_s)
23
32
  end
24
33
 
34
+ # Returns an instance of the Doorkeeper::Application with specific UID.
35
+ #
36
+ # @param uid [#to_s] UID (any object that responds to `#to_s`)
37
+ #
38
+ # @return [Doorkeeper::Application, nil] Application instance or nil
39
+ # if there is no record with such UID
40
+ #
25
41
  def by_uid(uid)
26
42
  find_by(uid: uid.to_s)
27
43
  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,10 +1,18 @@
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
9
  expires_in && Time.now.utc > expired_time
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
18
  expires = (created_at + expires_in.seconds) - Time.now.utc
@@ -1,14 +1,26 @@
1
1
  module Doorkeeper
2
2
  module Models
3
3
  module Revocable
4
+ # Revokes the object (updates `:revoked_at` attribute setting its value
5
+ # to the specific time).
6
+ #
7
+ # @param clock [Time] time object
8
+ #
4
9
  def revoke(clock = Time)
5
10
  update_attribute :revoked_at, clock.now.utc
6
11
  end
7
12
 
13
+ # Indicates whether the object has been revoked.
14
+ #
15
+ # @return [Boolean] true if revoked, false in other case
16
+ #
8
17
  def revoked?
9
18
  !!(revoked_at && revoked_at <= Time.now.utc)
10
19
  end
11
20
 
21
+ # Revokes token with `:refresh_token` equal to `:previous_refresh_token`
22
+ # and clears `:previous_refresh_token` attribute.
23
+ #
12
24
  def revoke_previous_refresh_token!
13
25
  return unless refresh_token_revoked_on_use?
14
26
  old_refresh_token.revoke if old_refresh_token
@@ -17,6 +29,12 @@ module Doorkeeper
17
29
 
18
30
  private
19
31
 
32
+ # Searches for Access Token record with `:refresh_token` equal to
33
+ # `:previous_refresh_token` value.
34
+ #
35
+ # @return [Doorkeeper::AccessToken, nil]
36
+ # Access Token record or nil if nothing found
37
+ #
20
38
  def old_refresh_token
21
39
  @old_refresh_token ||=
22
40
  AccessToken.by_refresh_token(previous_refresh_token)
@@ -1,27 +1,29 @@
1
+ require 'rack/utils'
2
+
1
3
  module Doorkeeper
2
4
  module OAuth
3
5
  module Authorization
4
- module URIBuilder
5
- include Rack::Utils
6
-
7
- extend self
6
+ class URIBuilder
7
+ class << self
8
+ def uri_with_query(url, parameters = {})
9
+ uri = URI.parse(url)
10
+ original_query = Rack::Utils.parse_query(uri.query)
11
+ uri.query = build_query(original_query.merge(parameters))
12
+ uri.to_s
13
+ end
8
14
 
9
- def uri_with_query(url, parameters = {})
10
- uri = URI.parse(url)
11
- original_query = parse_query(uri.query)
12
- uri.query = build_query(original_query.merge(parameters))
13
- uri.to_s
14
- end
15
+ def uri_with_fragment(url, parameters = {})
16
+ uri = URI.parse(url)
17
+ uri.fragment = build_query(parameters)
18
+ uri.to_s
19
+ end
15
20
 
16
- def uri_with_fragment(url, parameters = {})
17
- uri = URI.parse(url)
18
- uri.fragment = build_query(parameters)
19
- uri.to_s
20
- end
21
+ private
21
22
 
22
- def build_query(parameters = {})
23
- parameters = parameters.reject { |_, v| v.blank? }
24
- super parameters
23
+ def build_query(parameters = {})
24
+ parameters = parameters.reject { |_, v| v.blank? }
25
+ Rack::Utils.build_query parameters
26
+ end
25
27
  end
26
28
  end
27
29
  end
@@ -1,9 +1,6 @@
1
1
  module Doorkeeper
2
2
  module OAuth
3
- class AuthorizationCodeRequest
4
- include Validations
5
- include OAuth::RequestConcern
6
-
3
+ class AuthorizationCodeRequest < BaseRequest
7
4
  validate :attributes, error: :invalid_request
8
5
  validate :client, error: :invalid_client
9
6
  validate :grant, error: :invalid_grant
@@ -1,6 +1,8 @@
1
1
  module Doorkeeper
2
2
  module OAuth
3
- module RequestConcern
3
+ class BaseRequest
4
+ include Validations
5
+
4
6
  def authorize
5
7
  validate
6
8
  if valid?
@@ -0,0 +1,29 @@
1
+ module Doorkeeper
2
+ module OAuth
3
+ class BaseResponse
4
+ def body
5
+ {}
6
+ end
7
+
8
+ def description
9
+ ""
10
+ end
11
+
12
+ def headers
13
+ {}
14
+ end
15
+
16
+ def redirectable?
17
+ false
18
+ end
19
+
20
+ def redirect_uri
21
+ ""
22
+ end
23
+
24
+ def status
25
+ :ok
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,4 +1,3 @@
1
- require 'doorkeeper/oauth/client/methods'
2
1
  require 'doorkeeper/oauth/client/credentials'
3
2
 
4
3
  module Doorkeeper
@@ -2,13 +2,24 @@ module Doorkeeper
2
2
  module OAuth
3
3
  class Client
4
4
  class Credentials < Struct.new(:uid, :secret)
5
- extend Methods
5
+ class << self
6
+ def from_request(request, *credentials_methods)
7
+ credentials_methods.inject(nil) do |credentials, method|
8
+ method = self.method(method) if method.is_a?(Symbol)
9
+ credentials = Credentials.new(*method.call(request))
10
+ break credentials unless credentials.blank?
11
+ end
12
+ end
13
+
14
+ def from_params(request)
15
+ request.parameters.values_at(:client_id, :client_secret)
16
+ end
6
17
 
7
- def self.from_request(request, *credentials_methods)
8
- credentials_methods.inject(nil) do |credentials, method|
9
- method = self.method(method) if method.is_a?(Symbol)
10
- credentials = Credentials.new(*method.call(request))
11
- break credentials unless credentials.blank?
18
+ def from_basic(request)
19
+ authorization = request.authorization
20
+ if authorization.present? && authorization =~ /^Basic (.*)/m
21
+ Base64.decode64($1).split(/:/, 2)
22
+ end
12
23
  end
13
24
  end
14
25