doorkeeper 5.0.2 → 5.1.0.rc1
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.
- checksums.yaml +5 -5
- data/.travis.yml +7 -3
- data/Dangerfile +5 -2
- data/Gemfile +3 -1
- data/NEWS.md +18 -10
- data/README.md +1 -1
- data/app/controllers/doorkeeper/tokens_controller.rb +6 -6
- data/app/views/doorkeeper/applications/show.html.erb +1 -1
- data/app/views/layouts/doorkeeper/admin.html.erb +5 -3
- data/bin/console +15 -0
- data/gemfiles/rails_4_2.gemfile +1 -0
- data/gemfiles/rails_5_0.gemfile +1 -0
- data/gemfiles/rails_5_1.gemfile +1 -0
- data/gemfiles/rails_5_2.gemfile +2 -1
- data/gemfiles/rails_master.gemfile +1 -0
- data/lib/doorkeeper/config.rb +73 -6
- data/lib/doorkeeper/helpers/controller.rb +3 -2
- data/lib/doorkeeper/models/access_grant_mixin.rb +8 -1
- data/lib/doorkeeper/models/access_token_mixin.rb +40 -9
- data/lib/doorkeeper/models/application_mixin.rb +52 -1
- data/lib/doorkeeper/models/concerns/hashable.rb +137 -0
- data/lib/doorkeeper/models/concerns/scopes.rb +1 -1
- data/lib/doorkeeper/oauth/authorization/code.rb +1 -1
- data/lib/doorkeeper/oauth/authorization/token.rb +1 -1
- data/lib/doorkeeper/oauth/authorization_code_request.rb +1 -1
- data/lib/doorkeeper/oauth/client.rb +1 -1
- data/lib/doorkeeper/oauth/client_credentials/validation.rb +4 -3
- data/lib/doorkeeper/oauth/code_response.rb +2 -2
- data/lib/doorkeeper/oauth/helpers/scope_checker.rb +23 -8
- data/lib/doorkeeper/oauth/helpers/uri_checker.rb +32 -0
- data/lib/doorkeeper/oauth/password_access_token_request.rb +7 -2
- data/lib/doorkeeper/oauth/pre_authorization.rb +8 -3
- data/lib/doorkeeper/oauth/refresh_token_request.rb +4 -1
- data/lib/doorkeeper/oauth/token_response.rb +2 -2
- data/lib/doorkeeper/orm/active_record/access_grant.rb +22 -2
- data/lib/doorkeeper/orm/active_record/application.rb +16 -2
- data/lib/doorkeeper/version.rb +3 -3
- data/lib/doorkeeper.rb +1 -0
- data/lib/generators/doorkeeper/templates/initializer.rb +41 -1
- data/spec/controllers/application_metal_controller_spec.rb +18 -4
- data/spec/controllers/tokens_controller_spec.rb +7 -11
- data/spec/dummy/app/controllers/application_controller.rb +1 -1
- data/spec/factories.rb +3 -3
- data/spec/lib/config_spec.rb +84 -0
- data/spec/lib/models/hashable_spec.rb +183 -0
- data/spec/lib/oauth/base_request_spec.rb +7 -7
- data/spec/lib/oauth/client_credentials/validation_spec.rb +3 -0
- data/spec/lib/oauth/helpers/scope_checker_spec.rb +52 -17
- data/spec/lib/oauth/helpers/uri_checker_spec.rb +20 -2
- data/spec/lib/oauth/password_access_token_request_spec.rb +32 -11
- data/spec/lib/oauth/pre_authorization_spec.rb +24 -0
- data/spec/lib/oauth/token_response_spec.rb +13 -13
- data/spec/lib/oauth/token_spec.rb +14 -0
- data/spec/models/doorkeeper/access_grant_spec.rb +61 -0
- data/spec/models/doorkeeper/access_token_spec.rb +123 -0
- data/spec/models/doorkeeper/application_spec.rb +37 -0
- data/spec/requests/flows/authorization_code_spec.rb +40 -0
- data/spec/requests/flows/password_spec.rb +4 -2
- data/spec/requests/flows/revoke_token_spec.rb +14 -30
- data/spec/spec_helper.rb +2 -1
- data/spec/support/ruby_2_6_rails_4_2_patch.rb +14 -0
- data/spec/support/shared/hashing_shared_context.rb +29 -0
- metadata +13 -6
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Doorkeeper
|
|
4
|
+
module Models
|
|
5
|
+
##
|
|
6
|
+
# Hashable finder to provide lookups for input plaintext values which are
|
|
7
|
+
# mapped to their hashes before lookup.
|
|
8
|
+
module Hashable
|
|
9
|
+
extend ActiveSupport::Concern
|
|
10
|
+
|
|
11
|
+
delegate :perform_secret_hashing?,
|
|
12
|
+
:hashed_or_plain_token,
|
|
13
|
+
to: :class
|
|
14
|
+
|
|
15
|
+
# :nodoc
|
|
16
|
+
module ClassMethods
|
|
17
|
+
# Allow to override the hashing method by the including module
|
|
18
|
+
attr_accessor :secret_hash_function, :secret_comparer
|
|
19
|
+
|
|
20
|
+
# Compare the given plaintext with the secret
|
|
21
|
+
#
|
|
22
|
+
# @param input [String]
|
|
23
|
+
# The plain input to compare.
|
|
24
|
+
#
|
|
25
|
+
# @param secret [String]
|
|
26
|
+
# The secret value to compare with.
|
|
27
|
+
#
|
|
28
|
+
# @return [Boolean]
|
|
29
|
+
# If hashing is enabled: Whether the secret equals hashed input
|
|
30
|
+
# If hashing is disabled: Whether input matches secret
|
|
31
|
+
#
|
|
32
|
+
def secret_matches?(input, secret)
|
|
33
|
+
unless perform_secret_hashing?
|
|
34
|
+
return ActiveSupport::SecurityUtils.secure_compare input, secret
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
(secret_comparer || method(:default_comparer))
|
|
38
|
+
.call(input, secret)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Returns an instance of the Doorkeeper::AccessToken with
|
|
42
|
+
# specific token value.
|
|
43
|
+
#
|
|
44
|
+
# @param attr [Symbol]
|
|
45
|
+
# The token attribute we're looking with.
|
|
46
|
+
#
|
|
47
|
+
# @param token [#to_s]
|
|
48
|
+
# token value (any object that responds to `#to_s`)
|
|
49
|
+
#
|
|
50
|
+
# @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
|
|
51
|
+
# if there is no record with such token
|
|
52
|
+
#
|
|
53
|
+
def find_by_plaintext_token(attr, token)
|
|
54
|
+
token = token.to_s
|
|
55
|
+
|
|
56
|
+
find_by(attr => hashed_or_plain_token(token)) ||
|
|
57
|
+
find_by_fallback_token(attr, token)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Allow looking up previously plain tokens as a fallback
|
|
61
|
+
# IFF respective options are enabled
|
|
62
|
+
#
|
|
63
|
+
# @param attr [Symbol]
|
|
64
|
+
# The token attribute we're looking with.
|
|
65
|
+
#
|
|
66
|
+
# @param token [#to_s]
|
|
67
|
+
# token value (any object that responds to `#to_s`)
|
|
68
|
+
#
|
|
69
|
+
# @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
|
|
70
|
+
# if there is no record with such token
|
|
71
|
+
#
|
|
72
|
+
def find_by_fallback_token(attr, token)
|
|
73
|
+
return nil unless perform_secret_hashing?
|
|
74
|
+
return nil unless Doorkeeper.configuration.fallback_to_plain_secrets?
|
|
75
|
+
|
|
76
|
+
find_by(attr => token).tap do |fallback|
|
|
77
|
+
upgrade_fallback_value fallback, attr
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Hash the given input token
|
|
82
|
+
#
|
|
83
|
+
# @param plain_token [String]
|
|
84
|
+
# The plain text token to hash.
|
|
85
|
+
#
|
|
86
|
+
# @return [String]
|
|
87
|
+
# IFF secret hashing enabled, the hashed token,
|
|
88
|
+
# otherwise returns the plain token.
|
|
89
|
+
def hashed_or_plain_token(plain_token)
|
|
90
|
+
if perform_secret_hashing?
|
|
91
|
+
(secret_hash_function || method(:default_hash_function))
|
|
92
|
+
.call plain_token
|
|
93
|
+
else
|
|
94
|
+
plain_token
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Allow implementations in ORMs to replace a plain
|
|
99
|
+
# value falling back to to avoid it remaining as plain text.
|
|
100
|
+
#
|
|
101
|
+
# @param instance
|
|
102
|
+
# An instance of this model with a plain value token.
|
|
103
|
+
#
|
|
104
|
+
# @param attr
|
|
105
|
+
# The token attribute to upgrade
|
|
106
|
+
#
|
|
107
|
+
def upgrade_fallback_value(instance, attr)
|
|
108
|
+
plain_token = instance.public_send attr
|
|
109
|
+
instance.update_column(attr, hashed_or_plain_token(plain_token))
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Including classes can override this function to
|
|
113
|
+
# disable or enable secret hashing dynamically
|
|
114
|
+
def perform_secret_hashing?
|
|
115
|
+
true
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Return a default hashing function to be used when including
|
|
119
|
+
# module or user does not specify what to use
|
|
120
|
+
# @param plain_token [String]
|
|
121
|
+
# The plain text token to hash.
|
|
122
|
+
#
|
|
123
|
+
# @return [String] Hashed plain text token
|
|
124
|
+
#
|
|
125
|
+
def default_hash_function(plain_token)
|
|
126
|
+
::Digest::SHA256.hexdigest plain_token
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Return a default comparer for the given hash function
|
|
130
|
+
def default_comparer(plain, secret)
|
|
131
|
+
hashed = hashed_or_plain_token(plain)
|
|
132
|
+
ActiveSupport::SecurityUtils.secure_compare hashed, secret
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
@@ -18,7 +18,7 @@ module Doorkeeper
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def self.authenticate(credentials, method = Application.method(:by_uid_and_secret))
|
|
21
|
-
return
|
|
21
|
+
return if credentials.blank?
|
|
22
22
|
|
|
23
23
|
if (application = method.call(credentials.uid, credentials.secret))
|
|
24
24
|
new(application)
|
|
@@ -34,9 +34,10 @@ module Doorkeeper
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
ScopeChecker.valid?(
|
|
37
|
-
@request.scopes.to_s,
|
|
38
|
-
@server.scopes,
|
|
39
|
-
application_scopes
|
|
37
|
+
scope_str: @request.scopes.to_s,
|
|
38
|
+
server_scopes: @server.scopes,
|
|
39
|
+
app_scopes: application_scopes,
|
|
40
|
+
grant_type: Doorkeeper::OAuth::CLIENT_CREDENTIALS
|
|
40
41
|
)
|
|
41
42
|
end
|
|
42
43
|
end
|
|
@@ -23,7 +23,7 @@ module Doorkeeper
|
|
|
23
23
|
elsif response_on_fragment
|
|
24
24
|
Authorization::URIBuilder.uri_with_fragment(
|
|
25
25
|
pre_auth.redirect_uri,
|
|
26
|
-
access_token: auth.token.
|
|
26
|
+
access_token: auth.token.plaintext_token,
|
|
27
27
|
token_type: auth.token.token_type,
|
|
28
28
|
expires_in: auth.token.expires_in_seconds,
|
|
29
29
|
state: pre_auth.state
|
|
@@ -31,7 +31,7 @@ module Doorkeeper
|
|
|
31
31
|
else
|
|
32
32
|
Authorization::URIBuilder.uri_with_query(
|
|
33
33
|
pre_auth.redirect_uri,
|
|
34
|
-
code: auth.token.
|
|
34
|
+
code: auth.token.plaintext_token,
|
|
35
35
|
state: pre_auth.state
|
|
36
36
|
)
|
|
37
37
|
end
|
|
@@ -7,31 +7,46 @@ module Doorkeeper
|
|
|
7
7
|
class Validator
|
|
8
8
|
attr_reader :parsed_scopes, :scope_str
|
|
9
9
|
|
|
10
|
-
def initialize(scope_str, server_scopes,
|
|
10
|
+
def initialize(scope_str, server_scopes, app_scopes, grant_type)
|
|
11
11
|
@parsed_scopes = OAuth::Scopes.from_string(scope_str)
|
|
12
12
|
@scope_str = scope_str
|
|
13
|
-
@valid_scopes = valid_scopes(server_scopes,
|
|
13
|
+
@valid_scopes = valid_scopes(server_scopes, app_scopes)
|
|
14
|
+
|
|
15
|
+
if grant_type
|
|
16
|
+
@scopes_by_grant_type = Doorkeeper.configuration.scopes_by_grant_type[grant_type.to_sym]
|
|
17
|
+
end
|
|
14
18
|
end
|
|
15
19
|
|
|
16
20
|
def valid?
|
|
17
21
|
scope_str.present? &&
|
|
18
22
|
scope_str !~ /[\n\r\t]/ &&
|
|
19
|
-
@valid_scopes.has_scopes?(parsed_scopes)
|
|
23
|
+
@valid_scopes.has_scopes?(parsed_scopes) &&
|
|
24
|
+
permitted_to_grant_type?
|
|
20
25
|
end
|
|
21
26
|
|
|
22
27
|
private
|
|
23
28
|
|
|
24
|
-
def valid_scopes(server_scopes,
|
|
25
|
-
if
|
|
26
|
-
|
|
29
|
+
def valid_scopes(server_scopes, app_scopes)
|
|
30
|
+
if app_scopes.present?
|
|
31
|
+
app_scopes
|
|
27
32
|
else
|
|
28
33
|
server_scopes
|
|
29
34
|
end
|
|
30
35
|
end
|
|
36
|
+
|
|
37
|
+
def permitted_to_grant_type?
|
|
38
|
+
return true unless @scopes_by_grant_type
|
|
39
|
+
|
|
40
|
+
OAuth::Scopes.from_array(@scopes_by_grant_type)
|
|
41
|
+
.has_scopes?(parsed_scopes)
|
|
42
|
+
end
|
|
31
43
|
end
|
|
32
44
|
|
|
33
|
-
def self.valid?(scope_str
|
|
34
|
-
Validator.new(scope_str,
|
|
45
|
+
def self.valid?(scope_str:, server_scopes:, app_scopes: nil, grant_type: nil)
|
|
46
|
+
Validator.new(scope_str,
|
|
47
|
+
server_scopes,
|
|
48
|
+
app_scopes,
|
|
49
|
+
grant_type).valid?
|
|
35
50
|
end
|
|
36
51
|
end
|
|
37
52
|
end
|
|
@@ -1,6 +1,25 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
require 'ipaddr'
|
|
2
3
|
|
|
3
4
|
module Doorkeeper
|
|
5
|
+
module IPAddrLoopback
|
|
6
|
+
def loopback?
|
|
7
|
+
case @family
|
|
8
|
+
when Socket::AF_INET
|
|
9
|
+
@addr & 0xff000000 == 0x7f000000
|
|
10
|
+
when Socket::AF_INET6
|
|
11
|
+
@addr == 1
|
|
12
|
+
else
|
|
13
|
+
raise AddressFamilyError, "unsupported address family"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# For backward compatibility with old rubies
|
|
19
|
+
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.5.0")
|
|
20
|
+
IPAddr.send(:include, Doorkeeper::IPAddrLoopback)
|
|
21
|
+
end
|
|
22
|
+
|
|
4
23
|
module OAuth
|
|
5
24
|
module Helpers
|
|
6
25
|
module URIChecker
|
|
@@ -23,10 +42,23 @@ module Doorkeeper
|
|
|
23
42
|
client_url.query = nil
|
|
24
43
|
end
|
|
25
44
|
|
|
45
|
+
# RFC8252, Paragraph 7.3
|
|
46
|
+
# @see https://tools.ietf.org/html/rfc8252#section-7.3
|
|
47
|
+
if loopback_uri?(url) && loopback_uri?(client_url)
|
|
48
|
+
url.port = nil
|
|
49
|
+
client_url.port = nil
|
|
50
|
+
end
|
|
51
|
+
|
|
26
52
|
url.query = nil
|
|
27
53
|
url == client_url
|
|
28
54
|
end
|
|
29
55
|
|
|
56
|
+
def self.loopback_uri?(uri)
|
|
57
|
+
IPAddr.new(uri.host).loopback?
|
|
58
|
+
rescue IPAddr::Error
|
|
59
|
+
false
|
|
60
|
+
end
|
|
61
|
+
|
|
30
62
|
def self.valid_for_authorization?(url, client_url)
|
|
31
63
|
valid?(url) && client_url.split.any? { |other_url| matches?(url, other_url) }
|
|
32
64
|
end
|
|
@@ -32,7 +32,12 @@ module Doorkeeper
|
|
|
32
32
|
client_scopes = client.try(:scopes)
|
|
33
33
|
return true if scopes.blank?
|
|
34
34
|
|
|
35
|
-
ScopeChecker.valid?(
|
|
35
|
+
ScopeChecker.valid?(
|
|
36
|
+
scope_str: scopes.to_s,
|
|
37
|
+
server_scopes: server.scopes,
|
|
38
|
+
app_scopes: client_scopes,
|
|
39
|
+
grant_type: grant_type
|
|
40
|
+
)
|
|
36
41
|
end
|
|
37
42
|
|
|
38
43
|
def validate_resource_owner
|
|
@@ -40,7 +45,7 @@ module Doorkeeper
|
|
|
40
45
|
end
|
|
41
46
|
|
|
42
47
|
def validate_client
|
|
43
|
-
!parameters[:client_id] ||
|
|
48
|
+
!parameters[:client_id] || client.present?
|
|
44
49
|
end
|
|
45
50
|
end
|
|
46
51
|
end
|
|
@@ -77,12 +77,17 @@ module Doorkeeper
|
|
|
77
77
|
return true if scope.blank?
|
|
78
78
|
|
|
79
79
|
Helpers::ScopeChecker.valid?(
|
|
80
|
-
scope,
|
|
81
|
-
server.scopes,
|
|
82
|
-
client.application.scopes
|
|
80
|
+
scope_str: scope,
|
|
81
|
+
server_scopes: server.scopes,
|
|
82
|
+
app_scopes: client.application.scopes,
|
|
83
|
+
grant_type: grant_type
|
|
83
84
|
)
|
|
84
85
|
end
|
|
85
86
|
|
|
87
|
+
def grant_type
|
|
88
|
+
response_type == 'code' ? AUTHORIZATION_CODE : IMPLICIT
|
|
89
|
+
end
|
|
90
|
+
|
|
86
91
|
def validate_redirect_uri
|
|
87
92
|
return false if redirect_uri.blank?
|
|
88
93
|
|
|
@@ -99,7 +99,10 @@ module Doorkeeper
|
|
|
99
99
|
|
|
100
100
|
def validate_scope
|
|
101
101
|
if @original_scopes.present?
|
|
102
|
-
ScopeChecker.valid?(
|
|
102
|
+
ScopeChecker.valid?(
|
|
103
|
+
scope_str: @original_scopes,
|
|
104
|
+
server_scopes: refresh_token.scopes
|
|
105
|
+
)
|
|
103
106
|
else
|
|
104
107
|
true
|
|
105
108
|
end
|
|
@@ -11,10 +11,10 @@ module Doorkeeper
|
|
|
11
11
|
|
|
12
12
|
def body
|
|
13
13
|
{
|
|
14
|
-
'access_token' => token.
|
|
14
|
+
'access_token' => token.plaintext_token,
|
|
15
15
|
'token_type' => token.token_type,
|
|
16
16
|
'expires_in' => token.expires_in_seconds,
|
|
17
|
-
'refresh_token' => token.
|
|
17
|
+
'refresh_token' => token.plaintext_refresh_token,
|
|
18
18
|
'scope' => token.scopes_string,
|
|
19
19
|
'created_at' => token.created_at.to_i
|
|
20
20
|
}.reject { |_, value| value.blank? }
|
|
@@ -16,11 +16,30 @@ module Doorkeeper
|
|
|
16
16
|
|
|
17
17
|
belongs_to :application, belongs_to_options
|
|
18
18
|
|
|
19
|
-
validates :resource_owner_id,
|
|
19
|
+
validates :resource_owner_id,
|
|
20
|
+
:application_id,
|
|
21
|
+
:token,
|
|
22
|
+
:expires_in,
|
|
23
|
+
:redirect_uri,
|
|
24
|
+
presence: true
|
|
25
|
+
|
|
20
26
|
validates :token, uniqueness: true
|
|
21
27
|
|
|
22
28
|
before_validation :generate_token, on: :create
|
|
23
29
|
|
|
30
|
+
# Keep a reference to the generated token during generation
|
|
31
|
+
# of this access grant. The actual token may be mapped by
|
|
32
|
+
# the configuration hasher and may not be available in plaintext.
|
|
33
|
+
#
|
|
34
|
+
# If hash tokens are enabled, this will return nil on fetched tokens
|
|
35
|
+
def plaintext_token
|
|
36
|
+
if perform_secret_hashing?
|
|
37
|
+
@raw_token
|
|
38
|
+
else
|
|
39
|
+
token
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
24
43
|
private
|
|
25
44
|
|
|
26
45
|
# Generates token value with UniqueToken class.
|
|
@@ -28,7 +47,8 @@ module Doorkeeper
|
|
|
28
47
|
# @return [String] token value
|
|
29
48
|
#
|
|
30
49
|
def generate_token
|
|
31
|
-
|
|
50
|
+
@raw_token = UniqueToken.generate
|
|
51
|
+
self.token = hashed_or_plain_token(@raw_token)
|
|
32
52
|
end
|
|
33
53
|
end
|
|
34
54
|
end
|
|
@@ -45,6 +45,16 @@ module Doorkeeper
|
|
|
45
45
|
AccessGrant.revoke_all_for(id, resource_owner)
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
+
# We keep a volatile copy of the raw client_secret for initial communication
|
|
49
|
+
# The stored secret may be mapped and not available in cleartext.
|
|
50
|
+
def plaintext_secret
|
|
51
|
+
if perform_secret_hashing?
|
|
52
|
+
@raw_secret
|
|
53
|
+
else
|
|
54
|
+
secret
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
48
58
|
private
|
|
49
59
|
|
|
50
60
|
def generate_uid
|
|
@@ -52,12 +62,16 @@ module Doorkeeper
|
|
|
52
62
|
end
|
|
53
63
|
|
|
54
64
|
def generate_secret
|
|
55
|
-
|
|
65
|
+
return unless secret.blank?
|
|
66
|
+
|
|
67
|
+
@raw_secret = UniqueToken.generate
|
|
68
|
+
self.secret = hashed_or_plain_token(@raw_secret)
|
|
56
69
|
end
|
|
57
70
|
|
|
58
71
|
def scopes_match_configured
|
|
59
72
|
if scopes.present? &&
|
|
60
|
-
!ScopeChecker.valid?(scopes.to_s,
|
|
73
|
+
!ScopeChecker.valid?(scope_str: scopes.to_s,
|
|
74
|
+
server_scopes: Doorkeeper.configuration.scopes)
|
|
61
75
|
errors.add(:scopes, :not_match_configured)
|
|
62
76
|
end
|
|
63
77
|
end
|
data/lib/doorkeeper/version.rb
CHANGED
data/lib/doorkeeper.rb
CHANGED
|
@@ -56,6 +56,7 @@ require 'doorkeeper/models/concerns/scopes'
|
|
|
56
56
|
require 'doorkeeper/models/concerns/expirable'
|
|
57
57
|
require 'doorkeeper/models/concerns/revocable'
|
|
58
58
|
require 'doorkeeper/models/concerns/accessible'
|
|
59
|
+
require 'doorkeeper/models/concerns/hashable'
|
|
59
60
|
|
|
60
61
|
require 'doorkeeper/models/access_grant_mixin'
|
|
61
62
|
require 'doorkeeper/models/access_token_mixin'
|
|
@@ -75,8 +75,39 @@ Doorkeeper.configure do
|
|
|
75
75
|
# doesn't updates existing token expiration time, it will create a new token instead.
|
|
76
76
|
# Rationale: https://github.com/doorkeeper-gem/doorkeeper/issues/383
|
|
77
77
|
#
|
|
78
|
+
# You can not enable this option together with +hash_token_secrets+.
|
|
79
|
+
#
|
|
78
80
|
# reuse_access_token
|
|
79
81
|
|
|
82
|
+
# Hash access and refresh tokens before persisting them.
|
|
83
|
+
# Note: This will disable the possibility to use +reuse_access_token+
|
|
84
|
+
# since plain values can no longer be retrieved.
|
|
85
|
+
#
|
|
86
|
+
# hash_token_secrets
|
|
87
|
+
|
|
88
|
+
# Hash application secrets before persisting them.
|
|
89
|
+
#
|
|
90
|
+
# hash_application_secrets
|
|
91
|
+
|
|
92
|
+
# When the above option is enabled,
|
|
93
|
+
# and a hashed token or secret is not found,
|
|
94
|
+
# look up the plain text token as a fallback.
|
|
95
|
+
#
|
|
96
|
+
# This will ensure that old access tokens and secrets
|
|
97
|
+
# will remain valid even if the hashing above is enabled
|
|
98
|
+
#
|
|
99
|
+
# fallback_to_plain_secrets
|
|
100
|
+
|
|
101
|
+
#
|
|
102
|
+
# Since old values will not be re-hashed, lookups to tokens and secrets
|
|
103
|
+
# will fall back to plain value comparison so any existing tokens will
|
|
104
|
+
# not be invalidated.
|
|
105
|
+
#
|
|
106
|
+
# For example, to use SHA256 digests on plain values, uncomment these lines:
|
|
107
|
+
# hash_secrets do |plain_value|
|
|
108
|
+
# Digest::SHA256.hexdigest plain_value
|
|
109
|
+
# end
|
|
110
|
+
|
|
80
111
|
# Issue access tokens with refresh token (disabled by default), you may also
|
|
81
112
|
# pass a block which accepts `context` to customize when to give a refresh
|
|
82
113
|
# token or not. Similar to `custom_access_token_expires_in`, `context` has
|
|
@@ -108,6 +139,15 @@ Doorkeeper.configure do
|
|
|
108
139
|
# default_scopes :public
|
|
109
140
|
# optional_scopes :write, :update
|
|
110
141
|
|
|
142
|
+
# Define scopes_by_grant_type to restrict only certain scopes for grant_type
|
|
143
|
+
# By default, all the scopes will be available for all the grant types.
|
|
144
|
+
#
|
|
145
|
+
# Keys to this hash should be the name of grant_type and
|
|
146
|
+
# values should be the array of scopes for that grant type.
|
|
147
|
+
# Note: scopes should be from configured_scopes(i.e. deafult or optional)
|
|
148
|
+
#
|
|
149
|
+
# scopes_by_grant_type password: [:write], client_credentials: [:update]
|
|
150
|
+
|
|
111
151
|
# Change the way client credentials are retrieved from the request object.
|
|
112
152
|
# By default it retrieves first from the `HTTP_AUTHORIZATION` header, then
|
|
113
153
|
# falls back to the `:client_id` and `:client_secret` params from the `params` object.
|
|
@@ -195,7 +235,7 @@ Doorkeeper.configure do
|
|
|
195
235
|
# end
|
|
196
236
|
|
|
197
237
|
# Hook into Authorization flow in order to implement Single Sign Out
|
|
198
|
-
# or add
|
|
238
|
+
# or add any other functionality.
|
|
199
239
|
#
|
|
200
240
|
# before_successful_authorization do |controller|
|
|
201
241
|
# Rails.logger.info(params.inspect)
|
|
@@ -7,6 +7,10 @@ describe Doorkeeper::ApplicationMetalController do
|
|
|
7
7
|
def index
|
|
8
8
|
render json: {}, status: 200
|
|
9
9
|
end
|
|
10
|
+
|
|
11
|
+
def create
|
|
12
|
+
render json: {}, status: 200
|
|
13
|
+
end
|
|
10
14
|
end
|
|
11
15
|
|
|
12
16
|
it "lazy run hooks" do
|
|
@@ -22,13 +26,18 @@ describe Doorkeeper::ApplicationMetalController do
|
|
|
22
26
|
context 'enabled' do
|
|
23
27
|
let(:flag) { true }
|
|
24
28
|
|
|
25
|
-
it '200 for the
|
|
26
|
-
get :index, params: {}
|
|
29
|
+
it 'returns a 200 for the requests without body' do
|
|
30
|
+
get :index, params: {}
|
|
27
31
|
expect(response).to have_http_status 200
|
|
28
32
|
end
|
|
29
33
|
|
|
30
|
-
it 'returns a
|
|
31
|
-
|
|
34
|
+
it 'returns a 200 for the requests with body and correct media type' do
|
|
35
|
+
post :create, params: {}, as: :url_encoded_form
|
|
36
|
+
expect(response).to have_http_status 200
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'returns a 415 for the requests with body and incorrect media type' do
|
|
40
|
+
post :create, params: {}, as: :json
|
|
32
41
|
expect(response).to have_http_status 415
|
|
33
42
|
end
|
|
34
43
|
end
|
|
@@ -45,6 +54,11 @@ describe Doorkeeper::ApplicationMetalController do
|
|
|
45
54
|
get :index, as: :json
|
|
46
55
|
expect(response).to have_http_status 200
|
|
47
56
|
end
|
|
57
|
+
|
|
58
|
+
it 'returns a 200 for the requests with body and incorrect media type' do
|
|
59
|
+
post :create, params: {}, as: :json
|
|
60
|
+
expect(response).to have_http_status 200
|
|
61
|
+
end
|
|
48
62
|
end
|
|
49
63
|
end
|
|
50
64
|
end
|
|
@@ -28,7 +28,7 @@ describe Doorkeeper::TokensController do
|
|
|
28
28
|
describe 'when there is a failure due to a custom error' do
|
|
29
29
|
it 'returns the error response with a custom message' do
|
|
30
30
|
# I18n looks for `doorkeeper.errors.messages.custom_message` in locale files
|
|
31
|
-
custom_message =
|
|
31
|
+
custom_message = 'my_message'
|
|
32
32
|
allow(I18n).to receive(:translate)
|
|
33
33
|
.with(
|
|
34
34
|
custom_message,
|
|
@@ -60,21 +60,17 @@ describe Doorkeeper::TokensController do
|
|
|
60
60
|
let(:client) { FactoryBot.create(:application) }
|
|
61
61
|
let(:access_token) { FactoryBot.create(:access_token, application: client) }
|
|
62
62
|
|
|
63
|
-
before(:each) do
|
|
64
|
-
allow(controller).to receive(:token) { access_token }
|
|
65
|
-
end
|
|
66
|
-
|
|
67
63
|
context 'when associated app is public' do
|
|
68
64
|
let(:client) { FactoryBot.create(:application, confidential: false) }
|
|
69
65
|
|
|
70
66
|
it 'returns 200' do
|
|
71
|
-
post :revoke
|
|
67
|
+
post :revoke, params: { token: access_token.token }
|
|
72
68
|
|
|
73
69
|
expect(response.status).to eq 200
|
|
74
70
|
end
|
|
75
71
|
|
|
76
72
|
it 'revokes the access token' do
|
|
77
|
-
post :revoke
|
|
73
|
+
post :revoke, params: { token: access_token.token }
|
|
78
74
|
|
|
79
75
|
expect(access_token.reload).to have_attributes(revoked?: true)
|
|
80
76
|
end
|
|
@@ -89,13 +85,13 @@ describe Doorkeeper::TokensController do
|
|
|
89
85
|
end
|
|
90
86
|
|
|
91
87
|
it 'returns 200' do
|
|
92
|
-
post :revoke
|
|
88
|
+
post :revoke, params: { token: access_token.token }
|
|
93
89
|
|
|
94
90
|
expect(response.status).to eq 200
|
|
95
91
|
end
|
|
96
92
|
|
|
97
93
|
it 'revokes the access token' do
|
|
98
|
-
post :revoke
|
|
94
|
+
post :revoke, params: { token: access_token.token }
|
|
99
95
|
|
|
100
96
|
expect(access_token.reload).to have_attributes(revoked?: true)
|
|
101
97
|
end
|
|
@@ -105,13 +101,13 @@ describe Doorkeeper::TokensController do
|
|
|
105
101
|
let(:oauth_client) { Doorkeeper::OAuth::Client.new(some_other_client) }
|
|
106
102
|
|
|
107
103
|
it 'returns 200' do
|
|
108
|
-
post :revoke
|
|
104
|
+
post :revoke, params: { token: access_token.token }
|
|
109
105
|
|
|
110
106
|
expect(response.status).to eq 200
|
|
111
107
|
end
|
|
112
108
|
|
|
113
109
|
it 'does not revoke the access token' do
|
|
114
|
-
post :revoke
|
|
110
|
+
post :revoke, params: { token: access_token.token }
|
|
115
111
|
|
|
116
112
|
expect(access_token.reload).to have_attributes(revoked?: false)
|
|
117
113
|
end
|