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.
- checksums.yaml +4 -4
- data/.coveralls.yml +1 -0
- data/.gitignore +5 -0
- data/.travis.yml +11 -6
- data/Appraisals +14 -0
- data/Gemfile +4 -8
- data/NEWS.md +10 -1
- data/README.md +6 -1
- data/app/controllers/doorkeeper/applications_controller.rb +1 -5
- data/app/views/doorkeeper/applications/_delete_form.html.erb +1 -2
- data/app/views/doorkeeper/authorized_applications/_delete_form.html.erb +1 -2
- data/config/locales/en.yml +2 -2
- data/doorkeeper.gemspec +9 -7
- data/gemfiles/rails_4_2.gemfile +11 -0
- data/gemfiles/rails_5_0.gemfile +12 -0
- data/gemfiles/rails_5_1.gemfile +13 -0
- data/lib/doorkeeper.rb +7 -1
- data/lib/doorkeeper/config.rb +55 -0
- data/lib/doorkeeper/grape/helpers.rb +2 -1
- data/lib/doorkeeper/helpers/controller.rb +6 -6
- data/lib/doorkeeper/models/access_grant_mixin.rb +12 -0
- data/lib/doorkeeper/models/access_token_mixin.rb +117 -1
- data/lib/doorkeeper/models/application_mixin.rb +18 -2
- data/lib/doorkeeper/models/concerns/accessible.rb +4 -0
- data/lib/doorkeeper/models/concerns/expirable.rb +8 -0
- data/lib/doorkeeper/models/concerns/revocable.rb +18 -0
- data/lib/doorkeeper/oauth/authorization/uri_builder.rb +20 -18
- data/lib/doorkeeper/oauth/authorization_code_request.rb +1 -4
- data/lib/doorkeeper/oauth/{request_concern.rb → base_request.rb} +3 -1
- data/lib/doorkeeper/oauth/base_response.rb +29 -0
- data/lib/doorkeeper/oauth/client.rb +0 -1
- data/lib/doorkeeper/oauth/client/credentials.rb +17 -6
- data/lib/doorkeeper/oauth/client_credentials/creator.rb +1 -1
- data/lib/doorkeeper/oauth/client_credentials/issuer.rb +1 -1
- data/lib/doorkeeper/oauth/client_credentials/validation.rb +1 -1
- data/lib/doorkeeper/oauth/client_credentials_request.rb +1 -4
- data/lib/doorkeeper/oauth/code_response.rb +7 -6
- data/lib/doorkeeper/oauth/error_response.rb +9 -8
- data/lib/doorkeeper/oauth/invalid_token_response.rb +2 -0
- data/lib/doorkeeper/oauth/password_access_token_request.rb +1 -3
- data/lib/doorkeeper/oauth/refresh_token_request.rb +3 -7
- data/lib/doorkeeper/oauth/token.rb +17 -19
- data/lib/doorkeeper/oauth/token_request.rb +1 -2
- data/lib/doorkeeper/orm/active_record/access_token.rb +17 -0
- data/lib/doorkeeper/orm/active_record/application.rb +10 -5
- data/lib/doorkeeper/rails/helpers.rb +0 -2
- data/lib/doorkeeper/rails/routes.rb +3 -4
- data/lib/doorkeeper/rails/routes/mapper.rb +3 -3
- data/lib/doorkeeper/rails/routes/mapping.rb +1 -1
- data/lib/doorkeeper/request/authorization_code.rb +7 -1
- data/lib/doorkeeper/request/refresh_token.rb +1 -1
- data/lib/doorkeeper/server.rb +0 -8
- data/lib/doorkeeper/version.rb +1 -1
- data/spec/controllers/authorizations_controller_spec.rb +17 -3
- data/spec/lib/doorkeeper_spec.rb +135 -13
- data/spec/lib/oauth/authorization/uri_builder_spec.rb +1 -2
- data/spec/lib/oauth/base_request_spec.rb +160 -0
- data/spec/lib/oauth/base_response_spec.rb +45 -0
- data/spec/lib/oauth/client/credentials_spec.rb +41 -0
- data/spec/lib/oauth/error_response_spec.rb +9 -9
- data/spec/lib/oauth/invalid_token_response_spec.rb +36 -8
- data/spec/lib/server_spec.rb +0 -3
- data/spec/requests/endpoints/authorization_spec.rb +5 -6
- data/spec/requests/flows/authorization_code_spec.rb +4 -12
- data/spec/spec_helper.rb +2 -0
- data/spec/spec_helper_integration.rb +5 -0
- data/spec/support/helpers/request_spec_helper.rb +12 -4
- data/spec/support/http_method_shim.rb +20 -6
- metadata +43 -21
- data/lib/doorkeeper/oauth/client/methods.rb +0 -18
- 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
|
-
#
|
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: :
|
11
|
-
has_many :access_tokens, dependent: :
|
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,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
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
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
|
-
|
17
|
-
uri = URI.parse(url)
|
18
|
-
uri.fragment = build_query(parameters)
|
19
|
-
uri.to_s
|
20
|
-
end
|
21
|
+
private
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
@@ -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
|
@@ -2,13 +2,24 @@ module Doorkeeper
|
|
2
2
|
module OAuth
|
3
3
|
class Client
|
4
4
|
class Credentials < Struct.new(:uid, :secret)
|
5
|
-
|
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
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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
|
|