doorkeeper-sequel 2.0.0 → 2.1.0
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/.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
|