doorkeeper-sequel 1.5.0 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.editorconfig +6 -0
- data/.gitignore +1 -0
- data/.gitmodules +0 -1
- data/.rubocop.yml +29 -2
- data/.travis.yml +3 -12
- data/CHANGELOG.md +13 -3
- data/Gemfile +13 -9
- data/README.md +18 -3
- data/Rakefile +22 -18
- data/config/locales/en.yml +11 -0
- data/doorkeeper-sequel.gemspec +27 -23
- data/gemfiles/rails-5.0.gemfile +9 -6
- data/gemfiles/rails-5.1.gemfile +9 -6
- data/gemfiles/rails-5.2.gemfile +9 -6
- data/gemfiles/rails-6.0.gemfile +14 -0
- data/lib/doorkeeper-sequel.rb +21 -17
- data/lib/doorkeeper-sequel/gem_version.rb +5 -3
- data/lib/doorkeeper-sequel/generators/application_owner_generator.rb +5 -3
- data/lib/doorkeeper-sequel/generators/concerns/migration_actions.rb +8 -6
- data/lib/doorkeeper-sequel/generators/confidential_applications_generator.rb +5 -3
- data/lib/doorkeeper-sequel/generators/migration_generator.rb +5 -3
- data/lib/doorkeeper-sequel/generators/pkce_generator.rb +16 -0
- data/lib/doorkeeper-sequel/generators/polymorphic_resource_owner_generator.rb +16 -0
- data/lib/doorkeeper-sequel/generators/previous_refresh_token_generator.rb +5 -3
- data/lib/doorkeeper-sequel/generators/templates/add_confidential_to_application_migration.rb +2 -0
- data/lib/doorkeeper-sequel/generators/templates/add_owner_to_application.rb +3 -1
- data/lib/doorkeeper-sequel/generators/templates/add_previous_refresh_token_to_access_tokens.rb +3 -1
- data/lib/doorkeeper-sequel/generators/templates/create_doorkeeper_tables.rb +7 -3
- data/lib/doorkeeper-sequel/generators/templates/enable_pkce_migration.rb +10 -0
- data/lib/doorkeeper-sequel/generators/templates/enable_polymorphic_resource_owner_migration.rb +15 -0
- data/lib/doorkeeper-sequel/mixins/access_grant_mixin.rb +45 -7
- data/lib/doorkeeper-sequel/mixins/access_token_mixin.rb +154 -42
- data/lib/doorkeeper-sequel/mixins/application_mixin.rb +116 -22
- data/lib/doorkeeper-sequel/mixins/concerns/ownership.rb +2 -0
- data/lib/doorkeeper-sequel/mixins/concerns/sequel_compat.rb +33 -3
- data/lib/doorkeeper-sequel/railtie.rb +3 -1
- data/lib/doorkeeper-sequel/tasks/doorkeeper-sequel.rake +20 -3
- data/lib/doorkeeper-sequel/validators/redirect_uri_validator.rb +35 -6
- data/lib/doorkeeper-sequel/version.rb +3 -1
- data/lib/doorkeeper/orm/sequel.rb +11 -4
- data/lib/doorkeeper/orm/sequel/access_grant.rb +18 -0
- data/lib/doorkeeper/orm/sequel/access_token.rb +6 -0
- data/lib/doorkeeper/orm/sequel/application.rb +34 -3
- data/lib/doorkeeper/orm/sequel/stale_records_cleaner.rb +26 -0
- data/lib/doorkeeper/redirect_uri_validator.rb +9 -0
- data/spec/stubs/config/initializers/db.rb +6 -1
- data/spec/stubs/spec_helper_integration.rb +9 -11
- metadata +225 -235
- data/gemfiles/rails-4.2.gemfile +0 -13
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../validators/redirect_uri_validator"
|
2
4
|
|
3
5
|
module DoorkeeperSequel
|
4
6
|
module ApplicationMixin
|
@@ -7,6 +9,7 @@ module DoorkeeperSequel
|
|
7
9
|
include SequelCompat
|
8
10
|
include Doorkeeper::OAuth::Helpers
|
9
11
|
include Doorkeeper::Models::Scopes
|
12
|
+
include Doorkeeper::Models::SecretStorable
|
10
13
|
include DoorkeeperSequel::RedirectUriValidator
|
11
14
|
|
12
15
|
included do
|
@@ -14,30 +17,86 @@ module DoorkeeperSequel
|
|
14
17
|
plugin :timestamps
|
15
18
|
plugin :association_dependencies
|
16
19
|
|
17
|
-
one_to_many :access_grants, class:
|
18
|
-
one_to_many :access_tokens, class:
|
20
|
+
one_to_many :access_grants, class: "Doorkeeper::AccessGrant"
|
21
|
+
one_to_many :access_tokens, class: "Doorkeeper::AccessToken"
|
19
22
|
|
20
23
|
add_association_dependencies access_grants: :delete, access_tokens: :delete
|
21
24
|
|
22
25
|
set_allowed_columns :name, :redirect_uri, :scopes, :confidential
|
23
26
|
|
24
27
|
def before_validation
|
25
|
-
|
26
|
-
|
28
|
+
if new?
|
29
|
+
generate_uid
|
30
|
+
generate_secret
|
31
|
+
end
|
27
32
|
super
|
28
33
|
end
|
29
34
|
|
30
35
|
def validate
|
31
36
|
super
|
32
|
-
validates_presence [
|
37
|
+
validates_presence %i[name secret uid]
|
33
38
|
validates_unique [:uid]
|
34
39
|
validates_redirect_uri :redirect_uri
|
35
40
|
validates_includes [true, false], :confidential, allow_missing: true
|
36
41
|
|
42
|
+
validate_scopes_match_configured if enforce_scopes?
|
43
|
+
|
37
44
|
if respond_to?(:validate_owner?)
|
38
45
|
validates_presence [:owner_id] if validate_owner?
|
39
46
|
end
|
40
47
|
end
|
48
|
+
|
49
|
+
# In some cases, Doorkeeper used as a proxy app. In this case database does not have any fields.
|
50
|
+
# Even table may not exists on source database.
|
51
|
+
# Aliasing this method throws NoMethod Error. Due to this we need to explicitly
|
52
|
+
# define confidential? here.
|
53
|
+
def confidential?
|
54
|
+
confidential.present? && !!confidential
|
55
|
+
end
|
56
|
+
|
57
|
+
def renew_secret
|
58
|
+
@raw_secret = Doorkeeper::OAuth::Helpers::UniqueToken.generate
|
59
|
+
secret_strategy.store_secret(self, :secret, @raw_secret)
|
60
|
+
end
|
61
|
+
|
62
|
+
def as_json(options = {})
|
63
|
+
if (respond_to?(:owner) && owner && owner == options[:current_resource_owner]) ||
|
64
|
+
options[:as_owner]
|
65
|
+
hash = JSON.parse(to_json, symbolize_names: false)
|
66
|
+
else
|
67
|
+
only = extract_serializable_attributes(options)
|
68
|
+
# TODO: Write our own serializer for Hash
|
69
|
+
hash = JSON.parse(to_json(options.merge(only: only)), symbolize_names: false)
|
70
|
+
end
|
71
|
+
hash["secret"] = plaintext_secret if hash.key?("secret")
|
72
|
+
hash
|
73
|
+
end
|
74
|
+
|
75
|
+
def plaintext_secret
|
76
|
+
if secret_strategy.allows_restoring_secrets?
|
77
|
+
secret_strategy.restore_secret(self, :secret)
|
78
|
+
else
|
79
|
+
@raw_secret
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def authorized_for_resource_owner?(resource_owner)
|
84
|
+
Doorkeeper.configuration.authorize_resource_owner_for_client.call(self, resource_owner)
|
85
|
+
end
|
86
|
+
|
87
|
+
protected
|
88
|
+
|
89
|
+
def validate_scopes_match_configured
|
90
|
+
if scopes.present? && !Doorkeeper::OAuth::Helpers::ScopeChecker.valid?(scope_str: scopes.to_s,
|
91
|
+
server_scopes: Doorkeeper.configuration.scopes)
|
92
|
+
scope = "sequel.errors.models.doorkeeper/application.attributes.scopes"
|
93
|
+
errors.add(:scopes, I18n.t(:not_match_configured, scope: scope))
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def enforce_scopes?
|
98
|
+
Doorkeeper.configuration.enforce_configured_scopes?
|
99
|
+
end
|
41
100
|
end
|
42
101
|
|
43
102
|
module ClassMethods
|
@@ -45,7 +104,8 @@ module DoorkeeperSequel
|
|
45
104
|
app = by_uid(uid)
|
46
105
|
return unless app
|
47
106
|
return app if secret.blank? && !app.confidential?
|
48
|
-
return unless app.secret
|
107
|
+
return unless app.secret_matches?(secret)
|
108
|
+
|
49
109
|
app
|
50
110
|
end
|
51
111
|
|
@@ -53,33 +113,64 @@ module DoorkeeperSequel
|
|
53
113
|
first(uid: uid.to_s)
|
54
114
|
end
|
55
115
|
|
56
|
-
def
|
57
|
-
|
116
|
+
def find_by(params)
|
117
|
+
first(params)
|
58
118
|
end
|
59
119
|
|
60
120
|
def column_names
|
61
121
|
columns.map(&:to_s)
|
62
122
|
end
|
123
|
+
|
124
|
+
def secret_strategy
|
125
|
+
::Doorkeeper.configuration.application_secret_strategy
|
126
|
+
end
|
127
|
+
|
128
|
+
def fallback_secret_strategy
|
129
|
+
::Doorkeeper.configuration.application_secret_fallback_strategy
|
130
|
+
end
|
63
131
|
end
|
64
132
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
return
|
133
|
+
def secret_matches?(input)
|
134
|
+
# return false if either is nil, since secure_compare depends on strings
|
135
|
+
# but Application secrets MAY be nil depending on confidentiality.
|
136
|
+
return false if input.nil? || secret.nil?
|
69
137
|
|
70
|
-
|
71
|
-
|
72
|
-
'Doorkeeper::CVE_2018_1000211_WARNING'
|
138
|
+
# When matching the secret by comparer function, all is well.
|
139
|
+
return true if secret_strategy.secret_matches?(input, secret)
|
73
140
|
|
74
|
-
|
141
|
+
# When fallback lookup is enabled, ensure applications
|
142
|
+
# with plain secrets can still be found
|
143
|
+
if fallback_secret_strategy
|
144
|
+
fallback_secret_strategy.secret_matches?(input, secret)
|
145
|
+
else
|
146
|
+
false
|
147
|
+
end
|
75
148
|
end
|
76
149
|
|
77
|
-
alias_method :confidential?, :confidential
|
78
|
-
|
79
150
|
private
|
80
|
-
|
151
|
+
|
152
|
+
def extract_serializable_attributes(options = {})
|
153
|
+
opts = options.try(:dup) || {}
|
154
|
+
only = Array.wrap(opts[:only]).map(&:to_s)
|
155
|
+
|
156
|
+
only = if only.blank?
|
157
|
+
serializable_attributes
|
158
|
+
else
|
159
|
+
only & serializable_attributes
|
160
|
+
end
|
161
|
+
|
162
|
+
only -= Array.wrap(opts[:except]).map(&:to_s) if opts.key?(:except)
|
163
|
+
only.uniq
|
164
|
+
end
|
165
|
+
|
166
|
+
def serializable_attributes
|
167
|
+
attributes = %w[id name created_at]
|
168
|
+
attributes << "uid" unless confidential?
|
169
|
+
attributes
|
170
|
+
end
|
171
|
+
|
81
172
|
def has_scopes?
|
82
|
-
Doorkeeper::Application.columns.include?(
|
173
|
+
Doorkeeper::Application.columns.include?("scopes")
|
83
174
|
end
|
84
175
|
|
85
176
|
def generate_uid
|
@@ -87,7 +178,10 @@ module DoorkeeperSequel
|
|
87
178
|
end
|
88
179
|
|
89
180
|
def generate_secret
|
90
|
-
|
181
|
+
return unless secret.blank?
|
182
|
+
|
183
|
+
@raw_secret = UniqueToken.generate
|
184
|
+
secret_strategy.store_secret(self, :secret, @raw_secret)
|
91
185
|
end
|
92
186
|
end
|
93
187
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DoorkeeperSequel
|
2
4
|
module SequelCompat
|
3
5
|
extend ActiveSupport::Concern
|
@@ -8,9 +10,7 @@ module DoorkeeperSequel
|
|
8
10
|
plugin :active_model
|
9
11
|
|
10
12
|
# Sequel 4.47 and higher deprecated #set_allowed_columns
|
11
|
-
if (::Sequel::MAJOR >= 4 && ::Sequel::MINOR >= 47) || ::Sequel::MAJOR >= 5
|
12
|
-
plugin :whitelist_security
|
13
|
-
end
|
13
|
+
plugin :whitelist_security if (::Sequel::MAJOR >= 4 && ::Sequel::MINOR >= 47) || ::Sequel::MAJOR >= 5
|
14
14
|
|
15
15
|
self.raise_on_save_failure = false
|
16
16
|
|
@@ -19,6 +19,8 @@ module DoorkeeperSequel
|
|
19
19
|
save(columns: [column.to_sym], validate: false)
|
20
20
|
end
|
21
21
|
|
22
|
+
alias_method :update_column, :update_attribute
|
23
|
+
|
22
24
|
def update_attributes(*args)
|
23
25
|
update(*args)
|
24
26
|
end
|
@@ -34,6 +36,26 @@ module DoorkeeperSequel
|
|
34
36
|
def invalid?
|
35
37
|
!valid?
|
36
38
|
end
|
39
|
+
|
40
|
+
def exists?
|
41
|
+
!empty?
|
42
|
+
end
|
43
|
+
|
44
|
+
def with_lock
|
45
|
+
return yield if @_tr_is_locked
|
46
|
+
@_tr_is_locked = true
|
47
|
+
|
48
|
+
begin
|
49
|
+
db.transaction do
|
50
|
+
lock!
|
51
|
+
yield
|
52
|
+
end
|
53
|
+
ensure
|
54
|
+
@_tr_is_locked = false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
alias_method :exist?, :exists?
|
37
59
|
end
|
38
60
|
|
39
61
|
module ClassMethods
|
@@ -58,6 +80,14 @@ module DoorkeeperSequel
|
|
58
80
|
super(id: args)
|
59
81
|
end
|
60
82
|
end
|
83
|
+
|
84
|
+
def exists?(*args)
|
85
|
+
if args.any?
|
86
|
+
!where(*args).empty?
|
87
|
+
else
|
88
|
+
!empty?
|
89
|
+
end
|
90
|
+
end
|
61
91
|
end
|
62
92
|
end
|
63
93
|
end
|
@@ -1,18 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
namespace :doorkeeper_sequel do
|
2
4
|
namespace :generate do
|
3
|
-
desc
|
5
|
+
desc "Generate main migration file"
|
4
6
|
task :migration do
|
5
7
|
DoorkeeperSequel::MigrationGenerator.start
|
6
8
|
end
|
7
9
|
|
8
|
-
desc
|
10
|
+
desc "Generate migration file for Application Owner functionality"
|
9
11
|
task :application_owner do
|
10
12
|
DoorkeeperSequel::ApplicationOwnerGenerator.start
|
11
13
|
end
|
12
14
|
|
13
|
-
desc
|
15
|
+
desc "Generate migration file for Previous Refresh Token functionality"
|
14
16
|
task :previous_refresh_token do
|
15
17
|
DoorkeeperSequel::PreviousRefreshTokenGenerator.start
|
16
18
|
end
|
19
|
+
|
20
|
+
desc "Generate migration file for PKCE"
|
21
|
+
task :pkce do
|
22
|
+
DoorkeeperSequel::PkceGenerator.start
|
23
|
+
end
|
24
|
+
|
25
|
+
desc "Add confidential column to Doorkeeper applications"
|
26
|
+
task :confidential_applications do
|
27
|
+
DoorkeeperSequel::ConfidentialApplicationsGenerator.start
|
28
|
+
end
|
29
|
+
|
30
|
+
desc "Add resource owner type to Access tokens and grants"
|
31
|
+
task :polymorphic_resource_owner do
|
32
|
+
DoorkeeperSequel::PolymorphicResourceOwnerGenerator.start
|
33
|
+
end
|
17
34
|
end
|
18
35
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DoorkeeperSequel
|
2
4
|
module RedirectUriValidator
|
3
5
|
extend ActiveSupport::Concern
|
@@ -6,12 +8,18 @@ module DoorkeeperSequel
|
|
6
8
|
def validates_redirect_uri(attribute)
|
7
9
|
value = self[attribute]
|
8
10
|
|
11
|
+
# Allow nil? but do not allow empty string
|
9
12
|
if value.blank?
|
10
|
-
|
13
|
+
if Doorkeeper.configuration.allow_blank_redirect_uri?(self)
|
14
|
+
true
|
15
|
+
else
|
16
|
+
add_error(attribute, :blank)
|
17
|
+
end
|
11
18
|
else
|
12
19
|
value.split.each do |val|
|
20
|
+
next if oob_redirect_uri?(val)
|
21
|
+
|
13
22
|
uri = ::URI.parse(val)
|
14
|
-
next if native_redirect_uri?(uri)
|
15
23
|
validate_uri(uri, attribute)
|
16
24
|
end
|
17
25
|
end
|
@@ -21,6 +29,10 @@ module DoorkeeperSequel
|
|
21
29
|
|
22
30
|
private
|
23
31
|
|
32
|
+
def oob_redirect_uri?(uri)
|
33
|
+
::Doorkeeper::OAuth::NonStandard::IETF_WG_OAUTH2_OOB_METHODS.include?(uri)
|
34
|
+
end
|
35
|
+
|
24
36
|
def native_redirect_uri?(uri)
|
25
37
|
native_redirect_uri.present? && uri.to_s == native_redirect_uri.to_s
|
26
38
|
end
|
@@ -33,17 +45,34 @@ module DoorkeeperSequel
|
|
33
45
|
def validate_uri(uri, attribute)
|
34
46
|
{
|
35
47
|
fragment_present: uri.fragment.present?,
|
36
|
-
relative_uri: uri.scheme.nil? || uri.host.nil?,
|
37
48
|
secured_uri: invalid_ssl_uri?(uri),
|
38
|
-
forbidden_uri: forbidden_uri?(uri)
|
49
|
+
forbidden_uri: forbidden_uri?(uri),
|
50
|
+
unspecified_scheme: unspecified_scheme?(uri),
|
51
|
+
unspecified_host: unspecified_host?(uri),
|
52
|
+
relative_uri: relative_uri?(uri),
|
39
53
|
}.each do |error, condition|
|
40
54
|
add_error(attribute, error) if condition
|
41
55
|
end
|
42
56
|
end
|
43
57
|
|
58
|
+
def unspecified_scheme?(uri)
|
59
|
+
return true if uri.opaque.present?
|
60
|
+
|
61
|
+
%w[localhost].include?(uri.try(:scheme))
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
def unspecified_host?(uri)
|
66
|
+
uri.is_a?(URI::HTTP) && uri.host.nil?
|
67
|
+
end
|
68
|
+
|
69
|
+
def relative_uri?(uri)
|
70
|
+
uri.scheme.nil? && uri.host.nil?
|
71
|
+
end
|
72
|
+
|
44
73
|
def invalid_ssl_uri?(uri)
|
45
74
|
forces_ssl = Doorkeeper.configuration.force_ssl_in_redirect_uri
|
46
|
-
non_https = uri.try(:scheme) ==
|
75
|
+
non_https = uri.try(:scheme) == "http"
|
47
76
|
|
48
77
|
if forces_ssl.respond_to?(:call)
|
49
78
|
forces_ssl.call(uri) && non_https
|
@@ -57,7 +86,7 @@ module DoorkeeperSequel
|
|
57
86
|
end
|
58
87
|
|
59
88
|
def add_error(attribute, error)
|
60
|
-
scope =
|
89
|
+
scope = "sequel.errors.models.doorkeeper/application.attributes.redirect_uri"
|
61
90
|
errors.add(attribute, I18n.t(error, scope: scope))
|
62
91
|
end
|
63
92
|
end
|
@@ -1,3 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "doorkeeper/orm/sequel/stale_records_cleaner"
|
4
|
+
|
1
5
|
module Doorkeeper
|
2
6
|
module Orm
|
3
7
|
module Sequel
|
@@ -7,18 +11,21 @@ module Doorkeeper
|
|
7
11
|
# all the rake tasks (db:create, db:migrate, etc) would be aborted due to error.
|
8
12
|
old_value = ::Sequel::Model.require_valid_table
|
9
13
|
::Sequel::Model.require_valid_table = false
|
14
|
+
::Sequel::Model.strict_param_setting = false
|
15
|
+
::Sequel::Model.plugin :json_serializer
|
16
|
+
::Sequel::Model.plugin :polymorphic
|
10
17
|
|
11
18
|
begin
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require
|
19
|
+
require "doorkeeper/orm/sequel/access_grant"
|
20
|
+
require "doorkeeper/orm/sequel/access_token"
|
21
|
+
require "doorkeeper/orm/sequel/application"
|
15
22
|
ensure
|
16
23
|
::Sequel::Model.require_valid_table = old_value
|
17
24
|
end
|
18
25
|
end
|
19
26
|
|
20
27
|
def self.initialize_application_owner!
|
21
|
-
require
|
28
|
+
require "doorkeeper-sequel/mixins/concerns/ownership"
|
22
29
|
|
23
30
|
Doorkeeper::Application.send :include, DoorkeeperSequel::Ownership
|
24
31
|
end
|