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,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DoorkeeperSequel
|
2
4
|
module MigrationActions
|
3
5
|
extend ::ActiveSupport::Concern
|
@@ -9,7 +11,7 @@ module DoorkeeperSequel
|
|
9
11
|
end
|
10
12
|
|
11
13
|
def migration_template
|
12
|
-
File.expand_path(
|
14
|
+
File.expand_path("templates/migration.rb", __dir__)
|
13
15
|
end
|
14
16
|
|
15
17
|
private
|
@@ -19,20 +21,20 @@ module DoorkeeperSequel
|
|
19
21
|
end
|
20
22
|
|
21
23
|
def new_migration_number
|
22
|
-
current_number = current_migration_number(
|
24
|
+
current_number = current_migration_number("db/migrate")
|
23
25
|
|
24
26
|
# possible numeric migration
|
25
|
-
if current_number
|
27
|
+
if current_number&.start_with?("0")
|
26
28
|
# generate the same name as used by the developer
|
27
|
-
"%.#{current_number.length}d"
|
29
|
+
format("%.#{current_number.length}d", (current_number.to_i + 1))
|
28
30
|
else
|
29
|
-
Time.now.utc.strftime(
|
31
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
30
32
|
end
|
31
33
|
end
|
32
34
|
|
33
35
|
def current_migration_number(dirname)
|
34
36
|
migration_lookup_at(dirname).collect do |file|
|
35
|
-
File.basename(file).split(
|
37
|
+
File.basename(file).split("_").first
|
36
38
|
end.max
|
37
39
|
end
|
38
40
|
|
@@ -1,14 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DoorkeeperSequel
|
2
4
|
class ConfidentialApplicationsGenerator < ::Thor::Group
|
3
5
|
include ::Thor::Actions
|
4
6
|
include MigrationActions
|
5
7
|
|
6
|
-
source_root File.expand_path(
|
8
|
+
source_root File.expand_path("templates", __dir__)
|
7
9
|
|
8
|
-
desc
|
10
|
+
desc "Add confidential column to Doorkeeper applications."
|
9
11
|
|
10
12
|
def install
|
11
|
-
create_migration
|
13
|
+
create_migration "add_confidential_to_application_migration"
|
12
14
|
end
|
13
15
|
end
|
14
16
|
end
|
@@ -1,14 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DoorkeeperSequel
|
2
4
|
class MigrationGenerator < ::Thor::Group
|
3
5
|
include ::Thor::Actions
|
4
6
|
include MigrationActions
|
5
7
|
|
6
|
-
source_root File.expand_path(
|
8
|
+
source_root File.expand_path("templates", __dir__)
|
7
9
|
|
8
|
-
desc
|
10
|
+
desc "Installs Doorkeeper Sequel migration file."
|
9
11
|
|
10
12
|
def install
|
11
|
-
create_migration
|
13
|
+
create_migration "create_doorkeeper_tables.rb"
|
12
14
|
end
|
13
15
|
end
|
14
16
|
end
|
@@ -1,14 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DoorkeeperSequel
|
2
4
|
class PkceGenerator < ::Thor::Group
|
3
5
|
include ::Thor::Actions
|
4
6
|
include MigrationActions
|
5
7
|
|
6
|
-
source_root File.expand_path(
|
8
|
+
source_root File.expand_path("templates", __dir__)
|
7
9
|
|
8
|
-
desc
|
10
|
+
desc "Provide support for PKCE."
|
9
11
|
|
10
12
|
def install
|
11
|
-
create_migration
|
13
|
+
create_migration "enable_pkce_migration.rb.erb"
|
12
14
|
end
|
13
15
|
end
|
14
16
|
end
|
@@ -1,14 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DoorkeeperSequel
|
2
4
|
class PreviousRefreshTokenGenerator < ::Thor::Group
|
3
5
|
include ::Thor::Actions
|
4
6
|
include MigrationActions
|
5
7
|
|
6
|
-
source_root File.expand_path(
|
8
|
+
source_root File.expand_path("templates", __dir__)
|
7
9
|
|
8
|
-
desc
|
10
|
+
desc "Support revoke refresh token on access token use"
|
9
11
|
|
10
12
|
def install
|
11
|
-
create_migration
|
13
|
+
create_migration "add_previous_refresh_token_to_access_tokens.rb"
|
12
14
|
end
|
13
15
|
end
|
14
16
|
end
|
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
Sequel.migration do
|
2
4
|
change do
|
3
5
|
alter_table(:oauth_applications) do
|
4
6
|
add_column :owner_id, Integer, null: true
|
5
7
|
add_column :owner_type, String, null: true
|
6
|
-
add_index [
|
8
|
+
add_index %i[owner_id owner_type]
|
7
9
|
end
|
8
10
|
end
|
9
11
|
end
|
data/lib/doorkeeper-sequel/generators/templates/add_previous_refresh_token_to_access_tokens.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
Sequel.migration do
|
2
4
|
change do
|
3
5
|
alter_table(:oauth_access_tokens) do
|
4
|
-
add_column :previous_refresh_token, String, default:
|
6
|
+
add_column :previous_refresh_token, String, default: "", null: false
|
5
7
|
end
|
6
8
|
end
|
7
9
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
Sequel.migration do
|
2
4
|
change do
|
3
5
|
create_table :oauth_applications do
|
@@ -7,7 +9,7 @@ Sequel.migration do
|
|
7
9
|
column :uid, String, size: 255, null: false, index: { unique: true }
|
8
10
|
column :secret, String, size: 255, null: false
|
9
11
|
|
10
|
-
column :scopes, String, size: 255, null: false, default:
|
12
|
+
column :scopes, String, size: 255, null: false, default: ""
|
11
13
|
column :redirect_uri, String
|
12
14
|
column :confidential, TrueClass, null: false, default: true
|
13
15
|
|
@@ -50,7 +52,7 @@ Sequel.migration do
|
|
50
52
|
# previous tokens are revoked as soon as a new access token is created.
|
51
53
|
# Comment out this line if you'd rather have refresh tokens
|
52
54
|
# instantly revoked.
|
53
|
-
column :previous_refresh_token, String, size: 255, null: false, default:
|
55
|
+
column :previous_refresh_token, String, size: 255, null: false, default: ""
|
54
56
|
column :expires_in, Integer
|
55
57
|
column :revoked_at, DateTime
|
56
58
|
column :created_at, DateTime, null: false
|
@@ -1,7 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
Sequel.migration do
|
2
4
|
change do
|
3
5
|
alter_table(:oauth_access_grants) do
|
4
|
-
add_column :code_challenge,
|
6
|
+
add_column :code_challenge, String, null: true
|
5
7
|
add_column :code_challenge_method, String, null: true
|
6
8
|
end
|
7
9
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DoorkeeperSequel
|
2
4
|
module AccessGrantMixin
|
3
5
|
extend ActiveSupport::Concern
|
@@ -7,16 +9,17 @@ module DoorkeeperSequel
|
|
7
9
|
include Doorkeeper::Models::Expirable
|
8
10
|
include Doorkeeper::Models::Revocable
|
9
11
|
include Doorkeeper::Models::Accessible
|
12
|
+
include Doorkeeper::Models::SecretStorable
|
10
13
|
include Doorkeeper::Models::Scopes
|
11
14
|
|
12
15
|
included do
|
13
16
|
plugin :validation_helpers
|
14
17
|
plugin :timestamps
|
15
|
-
|
16
|
-
many_to_one :application, class: 'Doorkeeper::Application'
|
18
|
+
many_to_one :application, class: "Doorkeeper::Application"
|
17
19
|
|
18
20
|
set_allowed_columns :resource_owner_id, :application_id,
|
19
|
-
:expires_in, :redirect_uri, :scopes, :code_challenge, :code_challenge_method
|
21
|
+
:expires_in, :redirect_uri, :scopes, :code_challenge, :code_challenge_method,
|
22
|
+
:token
|
20
23
|
|
21
24
|
def before_validation
|
22
25
|
generate_token if new?
|
@@ -25,23 +28,19 @@ module DoorkeeperSequel
|
|
25
28
|
|
26
29
|
def validate
|
27
30
|
super
|
28
|
-
validates_presence [
|
29
|
-
|
31
|
+
validates_presence %i[resource_owner_id application_id
|
32
|
+
token expires_in redirect_uri]
|
30
33
|
validates_unique [:token]
|
31
34
|
end
|
32
|
-
|
33
|
-
def uses_pkce?
|
34
|
-
pkce_supported? && code_challenge.present?
|
35
|
-
end
|
36
|
-
|
37
|
-
def pkce_supported?
|
38
|
-
respond_to? :code_challenge
|
39
|
-
end
|
40
35
|
end
|
41
36
|
|
42
37
|
module ClassMethods
|
43
38
|
def by_token(token)
|
44
|
-
|
39
|
+
find_by_plaintext_token(:token, token)
|
40
|
+
end
|
41
|
+
|
42
|
+
def find_by(params)
|
43
|
+
first(params)
|
45
44
|
end
|
46
45
|
|
47
46
|
def revoke_all_for(application_id, resource_owner, clock = Time)
|
@@ -53,18 +52,33 @@ module DoorkeeperSequel
|
|
53
52
|
|
54
53
|
def generate_code_challenge(code_verifier)
|
55
54
|
padded_result = Base64.urlsafe_encode64(Digest::SHA256.digest(code_verifier))
|
56
|
-
padded_result.split(
|
55
|
+
padded_result.split("=")[0] # Remove any trailing '='
|
57
56
|
end
|
58
57
|
|
59
58
|
def pkce_supported?
|
60
59
|
new.pkce_supported?
|
61
60
|
end
|
61
|
+
|
62
|
+
def secret_strategy
|
63
|
+
::Doorkeeper.configuration.token_secret_strategy
|
64
|
+
end
|
65
|
+
|
66
|
+
def fallback_secret_strategy
|
67
|
+
::Doorkeeper.configuration.token_secret_fallback_strategy
|
68
|
+
end
|
62
69
|
end
|
63
70
|
|
64
71
|
private
|
65
72
|
|
73
|
+
# Generates token value with UniqueToken class.
|
74
|
+
#
|
75
|
+
# @return [String] token value
|
76
|
+
#
|
66
77
|
def generate_token
|
67
|
-
self
|
78
|
+
return nil unless self[:token].nil?
|
79
|
+
|
80
|
+
@raw_token = UniqueToken.generate
|
81
|
+
secret_strategy.store_secret(self, :token, @raw_token)
|
68
82
|
end
|
69
83
|
end
|
70
84
|
end
|
@@ -1,24 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DoorkeeperSequel
|
2
4
|
module AccessTokenMixin
|
3
5
|
extend ActiveSupport::Concern
|
4
6
|
|
5
7
|
include SequelCompat
|
6
8
|
include Doorkeeper::OAuth::Helpers
|
7
|
-
include Doorkeeper::Models::Expirable
|
8
9
|
include Doorkeeper::Models::Revocable
|
10
|
+
include Doorkeeper::Models::Expirable
|
11
|
+
include Doorkeeper::Models::Reusable
|
9
12
|
include Doorkeeper::Models::Accessible
|
13
|
+
include Doorkeeper::Models::SecretStorable
|
10
14
|
include Doorkeeper::Models::Scopes
|
11
15
|
|
12
16
|
included do
|
13
17
|
plugin :validation_helpers
|
14
18
|
plugin :timestamps
|
15
19
|
|
16
|
-
many_to_one :application, class:
|
20
|
+
many_to_one :application, class: "Doorkeeper::Application"
|
17
21
|
|
18
22
|
attr_writer :use_refresh_token
|
19
23
|
|
20
24
|
set_allowed_columns :application_id, :resource_owner_id, :expires_in,
|
21
|
-
:scopes, :use_refresh_token, :previous_refresh_token
|
25
|
+
:scopes, :use_refresh_token, :previous_refresh_token,
|
26
|
+
:token, :refresh_token
|
22
27
|
|
23
28
|
def before_validation
|
24
29
|
if new?
|
@@ -44,11 +49,15 @@ module DoorkeeperSequel
|
|
44
49
|
|
45
50
|
module ClassMethods
|
46
51
|
def by_token(token)
|
47
|
-
|
52
|
+
find_by_plaintext_token(:token, token)
|
53
|
+
end
|
54
|
+
|
55
|
+
def find_by(params)
|
56
|
+
first(params)
|
48
57
|
end
|
49
58
|
|
50
59
|
def by_refresh_token(refresh_token)
|
51
|
-
|
60
|
+
find_by_plaintext_token(:refresh_token, refresh_token)
|
52
61
|
end
|
53
62
|
|
54
63
|
def revoke_all_for(application_id, resource_owner, clock = Time)
|
@@ -64,33 +73,46 @@ module DoorkeeperSequel
|
|
64
73
|
else
|
65
74
|
resource_owner_or_id
|
66
75
|
end
|
67
|
-
tokens = authorized_tokens_for(application.try(:id), resource_owner_id)
|
76
|
+
tokens = authorized_tokens_for(application.try(:id), resource_owner_id).all
|
68
77
|
tokens.detect do |token|
|
69
78
|
scopes_match?(token.scopes, scopes, application.try(:scopes))
|
70
79
|
end
|
71
80
|
end
|
72
81
|
|
82
|
+
def find_matching_token(relation, application, scopes)
|
83
|
+
return nil unless relation
|
84
|
+
|
85
|
+
matching_tokens = []
|
86
|
+
|
87
|
+
tokens = relation.select do |token|
|
88
|
+
scopes_match?(token.scopes, scopes, application.try(:scopes))
|
89
|
+
end
|
90
|
+
|
91
|
+
matching_tokens.concat(tokens)
|
92
|
+
matching_tokens.max_by(&:created_at)
|
93
|
+
end
|
94
|
+
|
73
95
|
def scopes_match?(token_scopes, param_scopes, app_scopes)
|
74
96
|
return true if token_scopes.empty? && param_scopes.empty?
|
97
|
+
|
75
98
|
(token_scopes.sort == param_scopes.sort) &&
|
76
99
|
Doorkeeper::OAuth::Helpers::ScopeChecker.valid?(
|
77
|
-
param_scopes.to_s,
|
78
|
-
Doorkeeper.configuration.scopes,
|
79
|
-
app_scopes
|
100
|
+
scope_str: param_scopes.to_s,
|
101
|
+
server_scopes: Doorkeeper.configuration.scopes,
|
102
|
+
app_scopes: app_scopes
|
80
103
|
)
|
81
104
|
end
|
82
105
|
|
83
106
|
def authorized_tokens_for(application_id, resource_owner_id)
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
revoked_at: nil)
|
107
|
+
where(application_id: application_id,
|
108
|
+
resource_owner_id: resource_owner_id,
|
109
|
+
revoked_at: nil).order(Sequel.desc(:created_at))
|
88
110
|
end
|
89
111
|
|
90
112
|
def find_or_create_for(application, resource_owner_id, scopes, expires_in, use_refresh_token)
|
91
113
|
if Doorkeeper.configuration.reuse_access_token
|
92
114
|
access_token = matching_token_for(application, resource_owner_id, scopes)
|
93
|
-
return access_token if access_token
|
115
|
+
return access_token if access_token&.reusable?
|
94
116
|
end
|
95
117
|
|
96
118
|
create!(
|
@@ -105,13 +127,22 @@ module DoorkeeperSequel
|
|
105
127
|
def last_authorized_token_for(application_id, resource_owner_id)
|
106
128
|
authorized_tokens_for(application_id, resource_owner_id).first
|
107
129
|
end
|
130
|
+
|
131
|
+
def secret_strategy
|
132
|
+
::Doorkeeper.configuration.token_secret_strategy
|
133
|
+
end
|
134
|
+
|
135
|
+
def fallback_secret_strategy
|
136
|
+
::Doorkeeper.configuration.token_secret_fallback_strategy
|
137
|
+
end
|
108
138
|
end
|
109
139
|
|
110
140
|
def token_type
|
111
|
-
|
141
|
+
"Bearer"
|
112
142
|
end
|
113
143
|
|
114
144
|
def use_refresh_token?
|
145
|
+
@use_refresh_token ||= false
|
115
146
|
!!@use_refresh_token
|
116
147
|
end
|
117
148
|
|
@@ -121,7 +152,7 @@ module DoorkeeperSequel
|
|
121
152
|
scope: scopes,
|
122
153
|
expires_in: expires_in_seconds,
|
123
154
|
application: { uid: application.try(:uid) },
|
124
|
-
created_at: created_at.to_i
|
155
|
+
created_at: created_at.to_i,
|
125
156
|
}
|
126
157
|
end
|
127
158
|
|
@@ -135,32 +166,50 @@ module DoorkeeperSequel
|
|
135
166
|
accessible? && includes_scope?(*scopes)
|
136
167
|
end
|
137
168
|
|
169
|
+
def plaintext_refresh_token
|
170
|
+
if secret_strategy.allows_restoring_secrets?
|
171
|
+
secret_strategy.restore_secret(self, :refresh_token)
|
172
|
+
else
|
173
|
+
@raw_refresh_token
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def plaintext_token
|
178
|
+
if secret_strategy.allows_restoring_secrets?
|
179
|
+
secret_strategy.restore_secret(self, :token)
|
180
|
+
else
|
181
|
+
@raw_token
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
138
185
|
private
|
139
186
|
|
140
187
|
def generate_refresh_token
|
141
|
-
|
188
|
+
@raw_refresh_token = UniqueToken.generate
|
189
|
+
secret_strategy.store_secret(self, :refresh_token, @raw_refresh_token)
|
142
190
|
end
|
143
191
|
|
144
192
|
def generate_token
|
145
193
|
self[:created_at] ||= Time.now.utc
|
146
194
|
|
147
|
-
|
148
|
-
unless generator.respond_to?(:generate)
|
149
|
-
raise Doorkeeper::Errors::UnableToGenerateToken, "#{generator} does not respond to `.generate`."
|
150
|
-
end
|
151
|
-
|
152
|
-
self[:token] = generator.generate(
|
195
|
+
@raw_token = token_generator.generate(
|
153
196
|
resource_owner_id: resource_owner_id,
|
154
197
|
scopes: scopes,
|
155
198
|
application: application,
|
156
199
|
expires_in: expires_in,
|
157
200
|
created_at: created_at
|
158
201
|
)
|
202
|
+
secret_strategy.store_secret(self, :token, @raw_token)
|
203
|
+
@raw_token
|
159
204
|
end
|
160
205
|
|
161
206
|
def token_generator
|
162
207
|
generator_name = Doorkeeper.configuration.access_token_generator
|
163
|
-
generator_name.constantize
|
208
|
+
generator = generator_name.constantize
|
209
|
+
|
210
|
+
return generator if generator.respond_to?(:generate)
|
211
|
+
|
212
|
+
raise Doorkeeper::Errors::UnableToGenerateToken, "#{generator} does not respond to `.generate`."
|
164
213
|
rescue NameError
|
165
214
|
raise Doorkeeper::Errors::TokenGeneratorNotFound, "#{generator_name} not found"
|
166
215
|
end
|