doorkeeper-sequel 2.0.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.rubocop.yml +29 -2
- data/.travis.yml +3 -12
- data/Gemfile +13 -9
- data/Rakefile +22 -20
- data/config/locales/en.yml +8 -0
- data/doorkeeper-sequel.gemspec +27 -24
- 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 +18 -0
- data/lib/doorkeeper-sequel.rb +20 -17
- data/lib/doorkeeper-sequel/gem_version.rb +4 -2
- 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 +5 -3
- 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 +4 -2
- data/lib/doorkeeper-sequel/generators/templates/enable_pkce_migration.rb +3 -1
- data/lib/doorkeeper-sequel/mixins/access_grant_mixin.rb +30 -16
- data/lib/doorkeeper-sequel/mixins/access_token_mixin.rb +73 -24
- data/lib/doorkeeper-sequel/mixins/application_mixin.rb +52 -25
- data/lib/doorkeeper-sequel/mixins/concerns/ownership.rb +2 -0
- data/lib/doorkeeper-sequel/mixins/concerns/sequel_compat.rb +6 -4
- data/lib/doorkeeper-sequel/railtie.rb +3 -1
- data/lib/doorkeeper-sequel/tasks/doorkeeper-sequel.rake +7 -5
- data/lib/doorkeeper-sequel/validators/redirect_uri_validator.rb +29 -6
- data/lib/doorkeeper-sequel/version.rb +3 -1
- data/lib/doorkeeper/orm/sequel.rb +9 -5
- data/lib/doorkeeper/orm/sequel/access_grant.rb +18 -0
- data/lib/doorkeeper/orm/sequel/access_token.rb +2 -0
- data/lib/doorkeeper/orm/sequel/application.rb +26 -3
- data/lib/doorkeeper/redirect_uri_validator.rb +9 -0
- data/spec/stubs/spec_helper_integration.rb +8 -8
- metadata +177 -245
- 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,22 +17,24 @@ 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
|
@@ -41,13 +46,20 @@ module DoorkeeperSequel
|
|
41
46
|
end
|
42
47
|
end
|
43
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
|
+
|
44
57
|
protected
|
45
58
|
|
46
59
|
def validate_scopes_match_configured
|
47
|
-
if scopes.present? &&
|
48
|
-
|
49
|
-
|
50
|
-
scope = 'sequel.errors.models.doorkeeper/application.attributes.scopes'
|
60
|
+
if scopes.present? && !Doorkeeper::OAuth::Helpers::ScopeChecker.valid?(scope_str: scopes.to_s,
|
61
|
+
server_scopes: Doorkeeper.configuration.scopes)
|
62
|
+
scope = "sequel.errors.models.doorkeeper/application.attributes.scopes"
|
51
63
|
errors.add(:scopes, I18n.t(:not_match_configured, scope: scope))
|
52
64
|
end
|
53
65
|
end
|
@@ -62,7 +74,8 @@ module DoorkeeperSequel
|
|
62
74
|
app = by_uid(uid)
|
63
75
|
return unless app
|
64
76
|
return app if secret.blank? && !app.confidential?
|
65
|
-
return unless app.secret
|
77
|
+
return unless app.secret_matches?(secret)
|
78
|
+
|
66
79
|
app
|
67
80
|
end
|
68
81
|
|
@@ -70,33 +83,44 @@ module DoorkeeperSequel
|
|
70
83
|
first(uid: uid.to_s)
|
71
84
|
end
|
72
85
|
|
73
|
-
def
|
74
|
-
|
86
|
+
def find_by(params)
|
87
|
+
first(params)
|
75
88
|
end
|
76
89
|
|
77
90
|
def column_names
|
78
91
|
columns.map(&:to_s)
|
79
92
|
end
|
93
|
+
|
94
|
+
def secret_strategy
|
95
|
+
::Doorkeeper.configuration.application_secret_strategy
|
96
|
+
end
|
97
|
+
|
98
|
+
def fallback_secret_strategy
|
99
|
+
::Doorkeeper.configuration.application_secret_fallback_strategy
|
100
|
+
end
|
80
101
|
end
|
81
102
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
return
|
103
|
+
def secret_matches?(input)
|
104
|
+
# return false if either is nil, since secure_compare depends on strings
|
105
|
+
# but Application secrets MAY be nil depending on confidentiality.
|
106
|
+
return false if input.nil? || secret.nil?
|
86
107
|
|
87
|
-
|
88
|
-
|
89
|
-
'Doorkeeper::CVE_2018_1000211_WARNING'
|
108
|
+
# When matching the secret by comparer function, all is well.
|
109
|
+
return true if secret_strategy.secret_matches?(input, secret)
|
90
110
|
|
91
|
-
|
111
|
+
# When fallback lookup is enabled, ensure applications
|
112
|
+
# with plain secrets can still be found
|
113
|
+
if fallback_secret_strategy
|
114
|
+
fallback_secret_strategy.secret_matches?(input, secret)
|
115
|
+
else
|
116
|
+
false
|
117
|
+
end
|
92
118
|
end
|
93
119
|
|
94
|
-
alias_method :confidential?, :confidential
|
95
|
-
|
96
120
|
private
|
97
121
|
|
98
122
|
def has_scopes?
|
99
|
-
Doorkeeper::Application.columns.include?(
|
123
|
+
Doorkeeper::Application.columns.include?("scopes")
|
100
124
|
end
|
101
125
|
|
102
126
|
def generate_uid
|
@@ -104,7 +128,10 @@ module DoorkeeperSequel
|
|
104
128
|
end
|
105
129
|
|
106
130
|
def generate_secret
|
107
|
-
|
131
|
+
return unless secret.blank?
|
132
|
+
|
133
|
+
@raw_secret = UniqueToken.generate
|
134
|
+
secret_strategy.store_secret(self, :secret, @raw_secret)
|
108
135
|
end
|
109
136
|
end
|
110
137
|
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
|
@@ -36,7 +38,7 @@ module DoorkeeperSequel
|
|
36
38
|
end
|
37
39
|
|
38
40
|
def exists?
|
39
|
-
!
|
41
|
+
!empty?
|
40
42
|
end
|
41
43
|
|
42
44
|
alias_method :exist?, :exists?
|
@@ -1,26 +1,28 @@
|
|
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
|
17
19
|
|
18
|
-
desc
|
20
|
+
desc "Generate migration file for PKCE"
|
19
21
|
task :pkce do
|
20
22
|
DoorkeeperSequel::PkceGenerator.start
|
21
23
|
end
|
22
24
|
|
23
|
-
desc
|
25
|
+
desc "Add confidential column to Doorkeeper applications"
|
24
26
|
task :confidential_applications do
|
25
27
|
DoorkeeperSequel::ConfidentialApplicationsGenerator.start
|
26
28
|
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,28 @@ 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
|
+
relative_uri: relative_uri?(uri),
|
39
52
|
}.each do |error, condition|
|
40
53
|
add_error(attribute, error) if condition
|
41
54
|
end
|
42
55
|
end
|
43
56
|
|
57
|
+
def unspecified_scheme?(uri)
|
58
|
+
return true if uri.opaque.present?
|
59
|
+
|
60
|
+
%w[localhost].include?(uri.try(:scheme))
|
61
|
+
end
|
62
|
+
|
63
|
+
def relative_uri?(uri)
|
64
|
+
uri.scheme.nil? && uri.host.nil?
|
65
|
+
end
|
66
|
+
|
44
67
|
def invalid_ssl_uri?(uri)
|
45
68
|
forces_ssl = Doorkeeper.configuration.force_ssl_in_redirect_uri
|
46
|
-
non_https = uri.try(:scheme) ==
|
69
|
+
non_https = uri.try(:scheme) == "http"
|
47
70
|
|
48
71
|
if forces_ssl.respond_to?(:call)
|
49
72
|
forces_ssl.call(uri) && non_https
|
@@ -57,7 +80,7 @@ module DoorkeeperSequel
|
|
57
80
|
end
|
58
81
|
|
59
82
|
def add_error(attribute, error)
|
60
|
-
scope =
|
83
|
+
scope = "sequel.errors.models.doorkeeper/application.attributes.redirect_uri"
|
61
84
|
errors.add(attribute, I18n.t(error, scope: scope))
|
62
85
|
end
|
63
86
|
end
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "doorkeeper/orm/sequel/stale_records_cleaner"
|
2
4
|
|
3
5
|
module Doorkeeper
|
4
6
|
module Orm
|
@@ -9,18 +11,20 @@ module Doorkeeper
|
|
9
11
|
# all the rake tasks (db:create, db:migrate, etc) would be aborted due to error.
|
10
12
|
old_value = ::Sequel::Model.require_valid_table
|
11
13
|
::Sequel::Model.require_valid_table = false
|
14
|
+
::Sequel::Model.strict_param_setting = false
|
15
|
+
::Sequel::Model.plugin :json_serializer
|
12
16
|
|
13
17
|
begin
|
14
|
-
require
|
15
|
-
require
|
16
|
-
require
|
18
|
+
require "doorkeeper/orm/sequel/access_grant"
|
19
|
+
require "doorkeeper/orm/sequel/access_token"
|
20
|
+
require "doorkeeper/orm/sequel/application"
|
17
21
|
ensure
|
18
22
|
::Sequel::Model.require_valid_table = old_value
|
19
23
|
end
|
20
24
|
end
|
21
25
|
|
22
26
|
def self.initialize_application_owner!
|
23
|
-
require
|
27
|
+
require "doorkeeper-sequel/mixins/concerns/ownership"
|
24
28
|
|
25
29
|
Doorkeeper::Application.send :include, DoorkeeperSequel::Ownership
|
26
30
|
end
|
@@ -1,5 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Doorkeeper
|
2
4
|
class AccessGrant < Sequel::Model(:oauth_access_grants)
|
3
5
|
include DoorkeeperSequel::AccessGrantMixin
|
6
|
+
|
7
|
+
def plaintext_token
|
8
|
+
if secret_strategy.allows_restoring_secrets?
|
9
|
+
secret_strategy.restore_secret(self, :token)
|
10
|
+
else
|
11
|
+
@raw_token
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def uses_pkce?
|
16
|
+
pkce_supported? && code_challenge.present?
|
17
|
+
end
|
18
|
+
|
19
|
+
def pkce_supported?
|
20
|
+
respond_to?(:code_challenge)
|
21
|
+
end
|
4
22
|
end
|
5
23
|
end
|
@@ -1,15 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Doorkeeper
|
2
4
|
class Application < Sequel::Model(:oauth_applications)
|
3
5
|
include DoorkeeperSequel::ApplicationMixin
|
4
6
|
|
5
|
-
one_to_many :authorized_tokens,
|
6
|
-
|
7
|
-
|
7
|
+
one_to_many :authorized_tokens,
|
8
|
+
class: "Doorkeeper::AccessToken",
|
9
|
+
conditions: { revoked_at: nil }
|
10
|
+
|
11
|
+
many_to_many :authorized_applications,
|
12
|
+
join_table: :oauth_access_tokens,
|
13
|
+
class: self,
|
14
|
+
left_key: :id,
|
15
|
+
right_key: :application_id
|
8
16
|
|
9
17
|
def redirect_uri=(uris)
|
10
18
|
super(uris.is_a?(Array) ? uris.join("\n") : uris)
|
11
19
|
end
|
12
20
|
|
21
|
+
def plaintext_secret
|
22
|
+
if secret_strategy.allows_restoring_secrets?
|
23
|
+
secret_strategy.restore_secret(self, :secret)
|
24
|
+
else
|
25
|
+
@raw_secret
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_json(options = nil)
|
30
|
+
hash = to_hash.dup
|
31
|
+
hash.delete(:secret)
|
32
|
+
hash.merge(secret: plaintext_secret)
|
33
|
+
.to_json(options)
|
34
|
+
end
|
35
|
+
|
13
36
|
def self.authorized_for(resource_owner)
|
14
37
|
resource_access_tokens = AccessToken.active_for(resource_owner)
|
15
38
|
where(id: resource_access_tokens.select_map(:application_id)).all
|
@@ -1,17 +1,17 @@
|
|
1
|
-
ENV[
|
1
|
+
ENV["RAILS_ENV"] ||= "test"
|
2
2
|
|
3
3
|
DOORKEEPER_ORM = :sequel
|
4
4
|
|
5
5
|
$LOAD_PATH.unshift File.dirname(__FILE__)
|
6
6
|
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
7
|
+
require "capybara/rspec"
|
8
|
+
require "dummy/config/environment"
|
9
|
+
require "rspec/rails"
|
10
|
+
require "generator_spec/test_case"
|
11
11
|
|
12
12
|
# Load JRuby SQLite3 if in that platform
|
13
13
|
begin
|
14
|
-
require
|
14
|
+
require "jdbc/sqlite3"
|
15
15
|
Jdbc::SQLite3.load_driver
|
16
16
|
rescue LoadError
|
17
17
|
end
|
@@ -22,7 +22,7 @@ Rails.logger.info "====> Ruby version: #{RUBY_VERSION}"
|
|
22
22
|
|
23
23
|
require "support/orm/#{DOORKEEPER_ORM}"
|
24
24
|
|
25
|
-
ENGINE_RAILS_ROOT = File.join(File.dirname(__FILE__),
|
25
|
+
ENGINE_RAILS_ROOT = File.join(File.dirname(__FILE__), "../")
|
26
26
|
|
27
27
|
Dir["#{File.dirname(__FILE__)}/support/{dependencies,helpers,shared}/*.rb"].each { |f| require f }
|
28
28
|
|
@@ -45,5 +45,5 @@ RSpec.configure do |config|
|
|
45
45
|
DB.transaction(rollback: :always, auto_savepoint: true) { example.run }
|
46
46
|
end
|
47
47
|
|
48
|
-
config.order =
|
48
|
+
config.order = "random"
|
49
49
|
end
|