doorkeeper 5.0.3 → 5.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 +4 -4
- data/.hound.yml +2 -1
- data/.rubocop.yml +37 -4
- data/.travis.yml +11 -21
- data/Appraisals +29 -7
- data/Dangerfile +5 -2
- data/Gemfile +19 -5
- data/NEWS.md +81 -21
- data/README.md +75 -485
- data/Rakefile +10 -8
- data/app/controllers/doorkeeper/application_controller.rb +1 -2
- data/app/controllers/doorkeeper/application_metal_controller.rb +2 -13
- data/app/controllers/doorkeeper/applications_controller.rb +20 -8
- data/app/controllers/doorkeeper/authorized_applications_controller.rb +1 -1
- data/app/controllers/doorkeeper/token_info_controller.rb +2 -2
- data/app/controllers/doorkeeper/tokens_controller.rb +11 -11
- data/app/helpers/doorkeeper/dashboard_helper.rb +1 -1
- data/app/validators/redirect_uri_validator.rb +5 -2
- data/app/views/doorkeeper/applications/_form.html.erb +6 -0
- data/app/views/doorkeeper/applications/show.html.erb +1 -1
- data/app/views/layouts/doorkeeper/admin.html.erb +5 -3
- data/bin/console +16 -0
- data/config/locales/en.yml +1 -0
- data/doorkeeper.gemspec +24 -21
- data/gemfiles/rails_5_0.gemfile +9 -4
- data/gemfiles/rails_5_1.gemfile +9 -4
- data/gemfiles/rails_5_2.gemfile +10 -5
- data/gemfiles/rails_6_0.gemfile +17 -0
- data/gemfiles/rails_master.gemfile +9 -9
- data/lib/doorkeeper/config/option.rb +64 -0
- data/lib/doorkeeper/config.rb +191 -75
- data/lib/doorkeeper/engine.rb +1 -1
- data/lib/doorkeeper/grape/authorization_decorator.rb +4 -4
- data/lib/doorkeeper/grape/helpers.rb +3 -3
- data/lib/doorkeeper/helpers/controller.rb +3 -2
- data/lib/doorkeeper/models/access_grant_mixin.rb +20 -3
- data/lib/doorkeeper/models/access_token_mixin.rb +63 -19
- data/lib/doorkeeper/models/application_mixin.rb +43 -1
- data/lib/doorkeeper/models/concerns/expirable.rb +4 -2
- data/lib/doorkeeper/models/concerns/ownership.rb +1 -6
- data/lib/doorkeeper/models/concerns/reusable.rb +19 -0
- data/lib/doorkeeper/models/concerns/revocable.rb +2 -1
- data/lib/doorkeeper/models/concerns/scopes.rb +5 -1
- data/lib/doorkeeper/models/concerns/secret_storable.rb +108 -0
- data/lib/doorkeeper/oauth/authorization/code.rb +2 -2
- data/lib/doorkeeper/oauth/authorization/token.rb +12 -7
- data/lib/doorkeeper/oauth/authorization/uri_builder.rb +1 -1
- data/lib/doorkeeper/oauth/authorization_code_request.rb +6 -4
- data/lib/doorkeeper/oauth/client.rb +1 -1
- data/lib/doorkeeper/oauth/client_credentials/validation.rb +5 -4
- data/lib/doorkeeper/oauth/client_credentials_request.rb +1 -1
- data/lib/doorkeeper/oauth/code_response.rb +2 -2
- data/lib/doorkeeper/oauth/error_response.rb +10 -6
- data/lib/doorkeeper/oauth/forbidden_token_response.rb +1 -1
- data/lib/doorkeeper/oauth/helpers/scope_checker.rb +23 -8
- data/lib/doorkeeper/oauth/helpers/unique_token.rb +13 -1
- data/lib/doorkeeper/oauth/helpers/uri_checker.rb +37 -1
- data/lib/doorkeeper/oauth/invalid_token_response.rb +5 -1
- data/lib/doorkeeper/oauth/password_access_token_request.rb +7 -2
- data/lib/doorkeeper/oauth/pre_authorization.rb +11 -5
- data/lib/doorkeeper/oauth/refresh_token_request.rb +5 -2
- data/lib/doorkeeper/oauth/scopes.rb +5 -3
- data/lib/doorkeeper/oauth/token.rb +2 -2
- data/lib/doorkeeper/oauth/token_introspection.rb +74 -8
- data/lib/doorkeeper/oauth/token_response.rb +9 -9
- data/lib/doorkeeper/oauth.rb +5 -5
- data/lib/doorkeeper/orm/active_record/access_grant.rb +27 -13
- data/lib/doorkeeper/orm/active_record/access_token.rb +6 -13
- data/lib/doorkeeper/orm/active_record/application.rb +21 -57
- data/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +10 -3
- data/lib/doorkeeper/orm/active_record.rb +6 -6
- data/lib/doorkeeper/rails/helpers.rb +1 -1
- data/lib/doorkeeper/rails/routes/mapping.rb +7 -7
- data/lib/doorkeeper/rails/routes.rb +11 -11
- data/lib/doorkeeper/rake/db.rake +13 -13
- data/lib/doorkeeper/rake.rb +1 -1
- data/lib/doorkeeper/request.rb +1 -1
- data/lib/doorkeeper/secret_storing/base.rb +64 -0
- data/lib/doorkeeper/secret_storing/bcrypt.rb +60 -0
- data/lib/doorkeeper/secret_storing/plain.rb +33 -0
- data/lib/doorkeeper/secret_storing/sha256_hash.rb +26 -0
- data/lib/doorkeeper/stale_records_cleaner.rb +1 -1
- data/lib/doorkeeper/version.rb +3 -3
- data/lib/doorkeeper.rb +69 -60
- data/lib/generators/doorkeeper/application_owner_generator.rb +10 -9
- data/lib/generators/doorkeeper/confidential_applications_generator.rb +10 -9
- data/lib/generators/doorkeeper/install_generator.rb +11 -9
- data/lib/generators/doorkeeper/migration_generator.rb +9 -9
- data/lib/generators/doorkeeper/pkce_generator.rb +10 -9
- data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +10 -9
- data/lib/generators/doorkeeper/templates/initializer.rb +120 -13
- data/lib/generators/doorkeeper/templates/migration.rb.erb +15 -7
- data/lib/generators/doorkeeper/views_generator.rb +6 -4
- data/spec/controllers/application_metal_controller_spec.rb +24 -10
- data/spec/controllers/applications_controller_spec.rb +54 -52
- data/spec/controllers/authorizations_controller_spec.rb +139 -145
- data/spec/controllers/protected_resources_controller_spec.rb +78 -76
- data/spec/controllers/token_info_controller_spec.rb +14 -12
- data/spec/controllers/tokens_controller_spec.rb +167 -108
- data/spec/dummy/Rakefile +3 -1
- data/spec/dummy/app/controllers/application_controller.rb +3 -1
- data/spec/dummy/app/controllers/custom_authorizations_controller.rb +2 -0
- data/spec/dummy/app/controllers/full_protected_resources_controller.rb +4 -2
- data/spec/dummy/app/controllers/home_controller.rb +5 -3
- data/spec/dummy/app/controllers/metal_controller.rb +2 -0
- data/spec/dummy/app/controllers/semi_protected_resources_controller.rb +4 -2
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/models/user.rb +2 -0
- data/spec/dummy/config/application.rb +25 -1
- data/spec/dummy/config/environments/development.rb +2 -0
- data/spec/dummy/config/environments/production.rb +2 -0
- data/spec/dummy/config/environments/test.rb +3 -1
- data/spec/dummy/config/initializers/backtrace_silencers.rb +2 -0
- data/spec/dummy/config/initializers/doorkeeper.rb +5 -2
- data/spec/dummy/config/initializers/secret_token.rb +3 -1
- data/spec/dummy/config/initializers/session_store.rb +3 -1
- data/spec/dummy/config/initializers/wrap_parameters.rb +2 -0
- data/spec/dummy/config.ru +3 -1
- data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +17 -10
- data/spec/dummy/db/migrate/20170822064514_enable_pkce.rb +2 -0
- data/spec/dummy/db/schema.rb +1 -1
- data/spec/dummy/script/rails +5 -3
- data/spec/factories.rb +8 -6
- data/spec/generators/application_owner_generator_spec.rb +13 -26
- data/spec/generators/confidential_applications_generator_spec.rb +12 -28
- data/spec/generators/install_generator_spec.rb +17 -15
- data/spec/generators/migration_generator_spec.rb +13 -26
- data/spec/generators/pkce_generator_spec.rb +11 -26
- data/spec/generators/previous_refresh_token_generator_spec.rb +16 -29
- data/spec/generators/templates/routes.rb +2 -0
- data/spec/generators/views_generator_spec.rb +14 -12
- data/spec/grape/grape_integration_spec.rb +34 -32
- data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +9 -7
- data/spec/lib/config_spec.rb +268 -99
- data/spec/lib/doorkeeper_spec.rb +3 -1
- data/spec/lib/models/expirable_spec.rb +22 -8
- data/spec/lib/models/reusable_spec.rb +40 -0
- data/spec/lib/models/revocable_spec.rb +8 -6
- data/spec/lib/models/scopes_spec.rb +27 -13
- data/spec/lib/models/secret_storable_spec.rb +135 -0
- data/spec/lib/oauth/authorization/uri_builder_spec.rb +17 -15
- data/spec/lib/oauth/authorization_code_request_spec.rb +34 -11
- data/spec/lib/oauth/base_request_spec.rb +20 -8
- data/spec/lib/oauth/base_response_spec.rb +3 -1
- data/spec/lib/oauth/client/credentials_spec.rb +24 -22
- data/spec/lib/oauth/client_credentials/creator_spec.rb +56 -10
- data/spec/lib/oauth/client_credentials/issuer_spec.rb +27 -18
- data/spec/lib/oauth/client_credentials/validation_spec.rb +20 -15
- data/spec/lib/oauth/client_credentials_integration_spec.rb +7 -5
- data/spec/lib/oauth/client_credentials_request_spec.rb +27 -21
- data/spec/lib/oauth/client_spec.rb +15 -13
- data/spec/lib/oauth/code_request_spec.rb +8 -6
- data/spec/lib/oauth/code_response_spec.rb +9 -7
- data/spec/lib/oauth/error_response_spec.rb +19 -11
- data/spec/lib/oauth/error_spec.rb +4 -2
- data/spec/lib/oauth/forbidden_token_response_spec.rb +7 -5
- data/spec/lib/oauth/helpers/scope_checker_spec.rb +68 -31
- data/spec/lib/oauth/helpers/unique_token_spec.rb +8 -6
- data/spec/lib/oauth/helpers/uri_checker_spec.rb +115 -95
- data/spec/lib/oauth/invalid_token_response_spec.rb +3 -1
- data/spec/lib/oauth/password_access_token_request_spec.rb +79 -30
- data/spec/lib/oauth/pre_authorization_spec.rb +80 -54
- data/spec/lib/oauth/refresh_token_request_spec.rb +36 -33
- data/spec/lib/oauth/scopes_spec.rb +63 -61
- data/spec/lib/oauth/token_request_spec.rb +79 -24
- data/spec/lib/oauth/token_response_spec.rb +40 -38
- data/spec/lib/oauth/token_spec.rb +60 -44
- data/spec/lib/request/strategy_spec.rb +3 -1
- data/spec/lib/secret_storing/base_spec.rb +60 -0
- data/spec/lib/secret_storing/bcrypt_spec.rb +49 -0
- data/spec/lib/secret_storing/plain_spec.rb +44 -0
- data/spec/lib/secret_storing/sha256_hash_spec.rb +48 -0
- data/spec/lib/server_spec.rb +16 -14
- data/spec/lib/stale_records_cleaner_spec.rb +17 -17
- data/spec/models/doorkeeper/access_grant_spec.rb +80 -15
- data/spec/models/doorkeeper/access_token_spec.rb +198 -73
- data/spec/models/doorkeeper/application_spec.rb +280 -288
- data/spec/requests/applications/applications_request_spec.rb +98 -66
- data/spec/requests/applications/authorized_applications_spec.rb +20 -18
- data/spec/requests/endpoints/authorization_spec.rb +25 -23
- data/spec/requests/endpoints/token_spec.rb +38 -36
- data/spec/requests/flows/authorization_code_errors_spec.rb +26 -24
- data/spec/requests/flows/authorization_code_spec.rb +193 -151
- data/spec/requests/flows/client_credentials_spec.rb +54 -52
- data/spec/requests/flows/implicit_grant_errors_spec.rb +10 -8
- data/spec/requests/flows/implicit_grant_spec.rb +27 -25
- data/spec/requests/flows/password_spec.rb +59 -55
- data/spec/requests/flows/refresh_token_spec.rb +45 -43
- data/spec/requests/flows/revoke_token_spec.rb +40 -54
- data/spec/requests/flows/skip_authorization_spec.rb +23 -21
- data/spec/requests/protected_resources/metal_spec.rb +7 -5
- data/spec/requests/protected_resources/private_api_spec.rb +35 -33
- data/spec/routing/custom_controller_routes_spec.rb +71 -65
- data/spec/routing/default_routes_spec.rb +22 -20
- data/spec/routing/scoped_routes_spec.rb +20 -18
- data/spec/spec_helper.rb +15 -13
- data/spec/spec_helper_integration.rb +3 -1
- data/spec/support/dependencies/factory_bot.rb +3 -1
- data/spec/support/doorkeeper_rspec.rb +3 -1
- data/spec/support/helpers/access_token_request_helper.rb +3 -1
- data/spec/support/helpers/authorization_request_helper.rb +4 -2
- data/spec/support/helpers/config_helper.rb +2 -0
- data/spec/support/helpers/model_helper.rb +3 -1
- data/spec/support/helpers/request_spec_helper.rb +5 -3
- data/spec/support/helpers/url_helper.rb +9 -7
- data/spec/support/http_method_shim.rb +4 -9
- data/spec/support/orm/active_record.rb +3 -1
- data/spec/support/shared/controllers_shared_context.rb +18 -16
- data/spec/support/shared/hashing_shared_context.rb +36 -0
- data/spec/support/shared/models_shared_examples.rb +12 -10
- data/spec/validators/redirect_uri_validator_spec.rb +74 -45
- data/spec/version/version_spec.rb +7 -5
- metadata +60 -26
- data/gemfiles/rails_4_2.gemfile +0 -13
- data/spec/dummy/config/initializers/new_framework_defaults.rb +0 -8
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
2
4
|
|
|
3
5
|
module Doorkeeper
|
|
4
6
|
describe AccessToken do
|
|
7
|
+
let(:clazz) { Doorkeeper::AccessToken }
|
|
5
8
|
subject { FactoryBot.build(:access_token) }
|
|
6
9
|
|
|
7
10
|
it { expect(subject).to be_valid }
|
|
8
11
|
|
|
9
|
-
it_behaves_like
|
|
10
|
-
it_behaves_like
|
|
11
|
-
it_behaves_like
|
|
12
|
+
it_behaves_like "an accessible token"
|
|
13
|
+
it_behaves_like "a revocable token"
|
|
14
|
+
it_behaves_like "a unique token" do
|
|
12
15
|
let(:factory_name) { :access_token }
|
|
13
16
|
end
|
|
14
17
|
|
|
@@ -17,14 +20,75 @@ module Doorkeeper
|
|
|
17
20
|
end
|
|
18
21
|
|
|
19
22
|
describe :generate_token do
|
|
20
|
-
it
|
|
23
|
+
it "generates a token using the default method" do
|
|
21
24
|
FactoryBot.create :access_token
|
|
22
25
|
|
|
23
26
|
token = FactoryBot.create :access_token
|
|
24
27
|
expect(token.token).to be_a(String)
|
|
25
28
|
end
|
|
26
29
|
|
|
27
|
-
|
|
30
|
+
context "with hashing enabled" do
|
|
31
|
+
let(:token) { FactoryBot.create :access_token }
|
|
32
|
+
include_context "with token hashing enabled"
|
|
33
|
+
|
|
34
|
+
it "holds a volatile plaintext token when created" do
|
|
35
|
+
expect(token.plaintext_token).to be_a(String)
|
|
36
|
+
expect(token.token)
|
|
37
|
+
.to eq(hashed_or_plain_token_func.call(token.plaintext_token))
|
|
38
|
+
|
|
39
|
+
# Finder method only finds the hashed token
|
|
40
|
+
loaded = clazz.find_by(token: token.token)
|
|
41
|
+
expect(loaded).to eq(token)
|
|
42
|
+
expect(loaded.plaintext_token).to be_nil
|
|
43
|
+
expect(loaded.token).to eq(token.token)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "does not find_by plain text tokens" do
|
|
47
|
+
expect(clazz.find_by(token: token.plaintext_token)).to be_nil
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
describe "with having a plain text token" do
|
|
51
|
+
let(:plain_text_token) { "plain text token" }
|
|
52
|
+
let(:access_token) { FactoryBot.create :access_token }
|
|
53
|
+
|
|
54
|
+
before do
|
|
55
|
+
# Assume we have a plain text token from before activating the option
|
|
56
|
+
access_token.update_column(:token, plain_text_token)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
context "without fallback lookup" do
|
|
60
|
+
it "does not provide lookups with either through by_token" do
|
|
61
|
+
expect(clazz.by_token(plain_text_token)).to eq(nil)
|
|
62
|
+
expect(clazz.by_token(access_token.token)).to eq(nil)
|
|
63
|
+
|
|
64
|
+
# And it does not touch the token
|
|
65
|
+
access_token.reload
|
|
66
|
+
expect(access_token.token).to eq(plain_text_token)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
context "with fallback lookup" do
|
|
71
|
+
include_context "with token hashing and fallback lookup enabled"
|
|
72
|
+
|
|
73
|
+
it "upgrades a plain token when falling back to it" do
|
|
74
|
+
# Side-effect: This will automatically upgrade the token
|
|
75
|
+
expect(clazz).to receive(:upgrade_fallback_value).and_call_original
|
|
76
|
+
expect(clazz.by_token(plain_text_token)).to eq(access_token)
|
|
77
|
+
|
|
78
|
+
# Will find subsequently by hashing the token
|
|
79
|
+
expect(clazz.by_token(plain_text_token)).to eq(access_token)
|
|
80
|
+
|
|
81
|
+
# And it modifies the token value
|
|
82
|
+
access_token.reload
|
|
83
|
+
expect(access_token.token).not_to eq(plain_text_token)
|
|
84
|
+
expect(clazz.find_by(token: plain_text_token)).to eq(nil)
|
|
85
|
+
expect(clazz.find_by(token: access_token.token)).not_to be_nil
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it "generates a token using a custom object" do
|
|
28
92
|
eigenclass = class << CustomGeneratorArgs; self; end
|
|
29
93
|
eigenclass.class_eval do
|
|
30
94
|
remove_method :generate
|
|
@@ -44,7 +108,7 @@ module Doorkeeper
|
|
|
44
108
|
expect(token.token).to match(/custom_generator_token_\d+/)
|
|
45
109
|
end
|
|
46
110
|
|
|
47
|
-
it
|
|
111
|
+
it "allows the custom generator to access the application details" do
|
|
48
112
|
eigenclass = class << CustomGeneratorArgs; self; end
|
|
49
113
|
eigenclass.class_eval do
|
|
50
114
|
remove_method :generate
|
|
@@ -64,7 +128,7 @@ module Doorkeeper
|
|
|
64
128
|
expect(token.token).to match(/custom_generator_token_Application \d+/)
|
|
65
129
|
end
|
|
66
130
|
|
|
67
|
-
it
|
|
131
|
+
it "allows the custom generator to access the scopes" do
|
|
68
132
|
eigenclass = class << CustomGeneratorArgs; self; end
|
|
69
133
|
eigenclass.class_eval do
|
|
70
134
|
remove_method :generate
|
|
@@ -80,12 +144,12 @@ module Doorkeeper
|
|
|
80
144
|
access_token_generator "Doorkeeper::CustomGeneratorArgs"
|
|
81
145
|
end
|
|
82
146
|
|
|
83
|
-
token = FactoryBot.create :access_token, scopes:
|
|
147
|
+
token = FactoryBot.create :access_token, scopes: "public write"
|
|
84
148
|
|
|
85
|
-
expect(token.token).to eq
|
|
149
|
+
expect(token.token).to eq "custom_generator_token_2_public write"
|
|
86
150
|
end
|
|
87
151
|
|
|
88
|
-
it
|
|
152
|
+
it "allows the custom generator to access the expiry length" do
|
|
89
153
|
eigenclass = class << CustomGeneratorArgs; self; end
|
|
90
154
|
eigenclass.class_eval do
|
|
91
155
|
remove_method :generate
|
|
@@ -102,10 +166,10 @@ module Doorkeeper
|
|
|
102
166
|
end
|
|
103
167
|
|
|
104
168
|
token = FactoryBot.create :access_token
|
|
105
|
-
expect(token.token).to eq
|
|
169
|
+
expect(token.token).to eq "custom_generator_token_7200"
|
|
106
170
|
end
|
|
107
171
|
|
|
108
|
-
it
|
|
172
|
+
it "allows the custom generator to access the created time" do
|
|
109
173
|
module CustomGeneratorArgs
|
|
110
174
|
def self.generate(opts = {})
|
|
111
175
|
"custom_generator_token_#{opts[:created_at].to_i}"
|
|
@@ -122,7 +186,7 @@ module Doorkeeper
|
|
|
122
186
|
expect(token.token).to eq "custom_generator_token_#{created_at.to_i}"
|
|
123
187
|
end
|
|
124
188
|
|
|
125
|
-
it
|
|
189
|
+
it "raises an error if the custom object does not support generate" do
|
|
126
190
|
module NoGenerate
|
|
127
191
|
end
|
|
128
192
|
|
|
@@ -136,7 +200,7 @@ module Doorkeeper
|
|
|
136
200
|
)
|
|
137
201
|
end
|
|
138
202
|
|
|
139
|
-
it
|
|
203
|
+
it "raises original error if something went wrong in custom generator" do
|
|
140
204
|
eigenclass = class << CustomGeneratorArgs; self; end
|
|
141
205
|
eigenclass.class_eval do
|
|
142
206
|
remove_method :generate
|
|
@@ -144,7 +208,7 @@ module Doorkeeper
|
|
|
144
208
|
|
|
145
209
|
module CustomGeneratorArgs
|
|
146
210
|
def self.generate(_opts = {})
|
|
147
|
-
raise LoadError,
|
|
211
|
+
raise LoadError, "custom behaviour"
|
|
148
212
|
end
|
|
149
213
|
end
|
|
150
214
|
|
|
@@ -158,7 +222,7 @@ module Doorkeeper
|
|
|
158
222
|
)
|
|
159
223
|
end
|
|
160
224
|
|
|
161
|
-
it
|
|
225
|
+
it "raises an error if the custom object does not exist" do
|
|
162
226
|
Doorkeeper.configure do
|
|
163
227
|
orm DOORKEEPER_ORM
|
|
164
228
|
access_token_generator "Doorkeeper::NotReal"
|
|
@@ -171,24 +235,24 @@ module Doorkeeper
|
|
|
171
235
|
end
|
|
172
236
|
|
|
173
237
|
describe :refresh_token do
|
|
174
|
-
it
|
|
238
|
+
it "has empty refresh token if it was not required" do
|
|
175
239
|
token = FactoryBot.create :access_token
|
|
176
240
|
expect(token.refresh_token).to be_nil
|
|
177
241
|
end
|
|
178
242
|
|
|
179
|
-
it
|
|
243
|
+
it "generates a refresh token if it was requested" do
|
|
180
244
|
token = FactoryBot.create :access_token, use_refresh_token: true
|
|
181
245
|
expect(token.refresh_token).not_to be_nil
|
|
182
246
|
end
|
|
183
247
|
|
|
184
|
-
it
|
|
248
|
+
it "is not valid if token exists" do
|
|
185
249
|
token1 = FactoryBot.create :access_token, use_refresh_token: true
|
|
186
250
|
token2 = FactoryBot.create :access_token, use_refresh_token: true
|
|
187
251
|
token2.refresh_token = token1.refresh_token
|
|
188
252
|
expect(token2).not_to be_valid
|
|
189
253
|
end
|
|
190
254
|
|
|
191
|
-
it
|
|
255
|
+
it "expects database to raise an error if refresh tokens are the same" do
|
|
192
256
|
token1 = FactoryBot.create :access_token, use_refresh_token: true
|
|
193
257
|
token2 = FactoryBot.create :access_token, use_refresh_token: true
|
|
194
258
|
expect do
|
|
@@ -196,24 +260,85 @@ module Doorkeeper
|
|
|
196
260
|
token2.save(validate: false)
|
|
197
261
|
end.to raise_error(uniqueness_error)
|
|
198
262
|
end
|
|
263
|
+
|
|
264
|
+
context "with hashing enabled" do
|
|
265
|
+
include_context "with token hashing enabled"
|
|
266
|
+
let(:token) { FactoryBot.create :access_token, use_refresh_token: true }
|
|
267
|
+
|
|
268
|
+
it "holds a volatile refresh token when created" do
|
|
269
|
+
expect(token.plaintext_refresh_token).to be_a(String)
|
|
270
|
+
expect(token.refresh_token)
|
|
271
|
+
.to eq(hashed_or_plain_token_func.call(token.plaintext_refresh_token))
|
|
272
|
+
|
|
273
|
+
# Finder method only finds the hashed token
|
|
274
|
+
loaded = clazz.find_by(refresh_token: token.refresh_token)
|
|
275
|
+
expect(loaded).to eq(token)
|
|
276
|
+
expect(loaded.plaintext_refresh_token).to be_nil
|
|
277
|
+
expect(loaded.refresh_token).to eq(token.refresh_token)
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
it "does not find_by plain text refresh tokens" do
|
|
281
|
+
expect(clazz.find_by(refresh_token: token.plaintext_refresh_token)).to be_nil
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
describe "with having a plain text token" do
|
|
285
|
+
let(:plain_refresh_token) { "plain refresh token" }
|
|
286
|
+
let(:access_token) { FactoryBot.create :access_token }
|
|
287
|
+
|
|
288
|
+
before do
|
|
289
|
+
# Assume we have a plain text token from before activating the option
|
|
290
|
+
access_token.update_column(:refresh_token, plain_refresh_token)
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
context "without fallback lookup" do
|
|
294
|
+
it "does not provide lookups with either through by_token" do
|
|
295
|
+
expect(clazz.by_refresh_token(plain_refresh_token)).to eq(nil)
|
|
296
|
+
expect(clazz.by_refresh_token(access_token.refresh_token)).to eq(nil)
|
|
297
|
+
|
|
298
|
+
# And it does not touch the token
|
|
299
|
+
access_token.reload
|
|
300
|
+
expect(access_token.refresh_token).to eq(plain_refresh_token)
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
context "with fallback lookup" do
|
|
305
|
+
include_context "with token hashing and fallback lookup enabled"
|
|
306
|
+
|
|
307
|
+
it "upgrades a plain token when falling back to it" do
|
|
308
|
+
# Side-effect: This will automatically upgrade the token
|
|
309
|
+
expect(clazz).to receive(:upgrade_fallback_value).and_call_original
|
|
310
|
+
expect(clazz.by_refresh_token(plain_refresh_token)).to eq(access_token)
|
|
311
|
+
|
|
312
|
+
# Will find subsequently by hashing the token
|
|
313
|
+
expect(clazz.by_refresh_token(plain_refresh_token)).to eq(access_token)
|
|
314
|
+
|
|
315
|
+
# And it modifies the token value
|
|
316
|
+
access_token.reload
|
|
317
|
+
expect(access_token.refresh_token).not_to eq(plain_refresh_token)
|
|
318
|
+
expect(clazz.find_by(refresh_token: plain_refresh_token)).to eq(nil)
|
|
319
|
+
expect(clazz.find_by(refresh_token: access_token.refresh_token)).not_to be_nil
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
end
|
|
199
324
|
end
|
|
200
325
|
|
|
201
|
-
describe
|
|
202
|
-
it
|
|
326
|
+
describe "validations" do
|
|
327
|
+
it "is valid without resource_owner_id" do
|
|
203
328
|
# For client credentials flow
|
|
204
329
|
subject.resource_owner_id = nil
|
|
205
330
|
expect(subject).to be_valid
|
|
206
331
|
end
|
|
207
332
|
|
|
208
|
-
it
|
|
333
|
+
it "is valid without application_id" do
|
|
209
334
|
# For resource owner credentials flow
|
|
210
335
|
subject.application_id = nil
|
|
211
336
|
expect(subject).to be_valid
|
|
212
337
|
end
|
|
213
338
|
end
|
|
214
339
|
|
|
215
|
-
describe
|
|
216
|
-
context
|
|
340
|
+
describe "#same_credential?" do
|
|
341
|
+
context "with default parameters" do
|
|
217
342
|
let(:resource_owner_id) { 100 }
|
|
218
343
|
let(:application) { FactoryBot.create :application }
|
|
219
344
|
let(:default_attributes) do
|
|
@@ -221,14 +346,14 @@ module Doorkeeper
|
|
|
221
346
|
end
|
|
222
347
|
let(:access_token1) { FactoryBot.create :access_token, default_attributes }
|
|
223
348
|
|
|
224
|
-
context
|
|
349
|
+
context "the second token has the same owner and same app" do
|
|
225
350
|
let(:access_token2) { FactoryBot.create :access_token, default_attributes }
|
|
226
|
-
it
|
|
351
|
+
it "success" do
|
|
227
352
|
expect(access_token1.same_credential?(access_token2)).to be_truthy
|
|
228
353
|
end
|
|
229
354
|
end
|
|
230
355
|
|
|
231
|
-
context
|
|
356
|
+
context "the second token has same owner and different app" do
|
|
232
357
|
let(:other_application) { FactoryBot.create :application }
|
|
233
358
|
let(:access_token2) do
|
|
234
359
|
FactoryBot.create :access_token,
|
|
@@ -236,72 +361,72 @@ module Doorkeeper
|
|
|
236
361
|
resource_owner_id: resource_owner_id
|
|
237
362
|
end
|
|
238
363
|
|
|
239
|
-
it
|
|
364
|
+
it "fail" do
|
|
240
365
|
expect(access_token1.same_credential?(access_token2)).to be_falsey
|
|
241
366
|
end
|
|
242
367
|
end
|
|
243
368
|
|
|
244
|
-
context
|
|
369
|
+
context "the second token has different owner and different app" do
|
|
245
370
|
let(:other_application) { FactoryBot.create :application }
|
|
246
371
|
let(:access_token2) do
|
|
247
372
|
FactoryBot.create :access_token, application: other_application, resource_owner_id: 42
|
|
248
373
|
end
|
|
249
374
|
|
|
250
|
-
it
|
|
375
|
+
it "fail" do
|
|
251
376
|
expect(access_token1.same_credential?(access_token2)).to be_falsey
|
|
252
377
|
end
|
|
253
378
|
end
|
|
254
379
|
|
|
255
|
-
context
|
|
380
|
+
context "the second token has different owner and same app" do
|
|
256
381
|
let(:access_token2) do
|
|
257
382
|
FactoryBot.create :access_token, application: application, resource_owner_id: 42
|
|
258
383
|
end
|
|
259
384
|
|
|
260
|
-
it
|
|
385
|
+
it "fail" do
|
|
261
386
|
expect(access_token1.same_credential?(access_token2)).to be_falsey
|
|
262
387
|
end
|
|
263
388
|
end
|
|
264
389
|
end
|
|
265
390
|
end
|
|
266
391
|
|
|
267
|
-
describe
|
|
268
|
-
context
|
|
392
|
+
describe "#acceptable?" do
|
|
393
|
+
context "a token that is not accessible" do
|
|
269
394
|
let(:token) { FactoryBot.create(:access_token, created_at: 6.hours.ago) }
|
|
270
395
|
|
|
271
|
-
it
|
|
396
|
+
it "should return false" do
|
|
272
397
|
expect(token.acceptable?(nil)).to be false
|
|
273
398
|
end
|
|
274
399
|
end
|
|
275
400
|
|
|
276
|
-
context
|
|
401
|
+
context "a token that has the incorrect scopes" do
|
|
277
402
|
let(:token) { FactoryBot.create(:access_token) }
|
|
278
403
|
|
|
279
|
-
it
|
|
280
|
-
expect(token.acceptable?([
|
|
404
|
+
it "should return false" do
|
|
405
|
+
expect(token.acceptable?(["public"])).to be false
|
|
281
406
|
end
|
|
282
407
|
end
|
|
283
408
|
|
|
284
|
-
context
|
|
409
|
+
context "a token is acceptable with the correct scopes" do
|
|
285
410
|
let(:token) do
|
|
286
411
|
token = FactoryBot.create(:access_token)
|
|
287
|
-
token[:scopes] =
|
|
412
|
+
token[:scopes] = "public"
|
|
288
413
|
token
|
|
289
414
|
end
|
|
290
415
|
|
|
291
|
-
it
|
|
292
|
-
expect(token.acceptable?([
|
|
416
|
+
it "should return true" do
|
|
417
|
+
expect(token.acceptable?(["public"])).to be true
|
|
293
418
|
end
|
|
294
419
|
end
|
|
295
420
|
end
|
|
296
421
|
|
|
297
|
-
describe
|
|
422
|
+
describe ".revoke_all_for" do
|
|
298
423
|
let(:resource_owner) { double(id: 100) }
|
|
299
424
|
let(:application) { FactoryBot.create :application }
|
|
300
425
|
let(:default_attributes) do
|
|
301
426
|
{ application: application, resource_owner_id: resource_owner.id }
|
|
302
427
|
end
|
|
303
428
|
|
|
304
|
-
it
|
|
429
|
+
it "revokes all tokens for given application and resource owner" do
|
|
305
430
|
FactoryBot.create :access_token, default_attributes
|
|
306
431
|
AccessToken.revoke_all_for application.id, resource_owner
|
|
307
432
|
AccessToken.all.each do |token|
|
|
@@ -309,7 +434,7 @@ module Doorkeeper
|
|
|
309
434
|
end
|
|
310
435
|
end
|
|
311
436
|
|
|
312
|
-
it
|
|
437
|
+
it "matches application" do
|
|
313
438
|
access_token_for_different_app = FactoryBot.create(
|
|
314
439
|
:access_token,
|
|
315
440
|
default_attributes.merge(application: FactoryBot.create(:application))
|
|
@@ -320,7 +445,7 @@ module Doorkeeper
|
|
|
320
445
|
expect(access_token_for_different_app.reload).not_to be_revoked
|
|
321
446
|
end
|
|
322
447
|
|
|
323
|
-
it
|
|
448
|
+
it "matches resource owner" do
|
|
324
449
|
access_token_for_different_owner = FactoryBot.create(
|
|
325
450
|
:access_token,
|
|
326
451
|
default_attributes.merge(resource_owner_id: 90)
|
|
@@ -332,15 +457,15 @@ module Doorkeeper
|
|
|
332
457
|
end
|
|
333
458
|
end
|
|
334
459
|
|
|
335
|
-
describe
|
|
460
|
+
describe ".matching_token_for" do
|
|
336
461
|
let(:resource_owner_id) { 100 }
|
|
337
462
|
let(:application) { FactoryBot.create :application }
|
|
338
|
-
let(:scopes) { Doorkeeper::OAuth::Scopes.from_string(
|
|
463
|
+
let(:scopes) { Doorkeeper::OAuth::Scopes.from_string("public write") }
|
|
339
464
|
let(:default_attributes) do
|
|
340
465
|
{
|
|
341
466
|
application: application,
|
|
342
467
|
resource_owner_id: resource_owner_id,
|
|
343
|
-
scopes: scopes.to_s
|
|
468
|
+
scopes: scopes.to_s,
|
|
344
469
|
}
|
|
345
470
|
end
|
|
346
471
|
|
|
@@ -348,26 +473,26 @@ module Doorkeeper
|
|
|
348
473
|
default_scopes_exist(*scopes.all)
|
|
349
474
|
end
|
|
350
475
|
|
|
351
|
-
it
|
|
476
|
+
it "returns only one token" do
|
|
352
477
|
token = FactoryBot.create :access_token, default_attributes
|
|
353
478
|
last_token = AccessToken.matching_token_for(application, resource_owner_id, scopes)
|
|
354
479
|
expect(last_token).to eq(token)
|
|
355
480
|
end
|
|
356
481
|
|
|
357
|
-
it
|
|
482
|
+
it "accepts resource owner as object" do
|
|
358
483
|
resource_owner = double(to_key: true, id: 100)
|
|
359
484
|
token = FactoryBot.create :access_token, default_attributes
|
|
360
485
|
last_token = AccessToken.matching_token_for(application, resource_owner, scopes)
|
|
361
486
|
expect(last_token).to eq(token)
|
|
362
487
|
end
|
|
363
488
|
|
|
364
|
-
it
|
|
489
|
+
it "accepts nil as resource owner" do
|
|
365
490
|
token = FactoryBot.create :access_token, default_attributes.merge(resource_owner_id: nil)
|
|
366
491
|
last_token = AccessToken.matching_token_for(application, nil, scopes)
|
|
367
492
|
expect(last_token).to eq(token)
|
|
368
493
|
end
|
|
369
494
|
|
|
370
|
-
it
|
|
495
|
+
it "excludes revoked tokens" do
|
|
371
496
|
FactoryBot.create :access_token, default_attributes.merge(revoked_at: 1.day.ago)
|
|
372
497
|
last_token = AccessToken.matching_token_for(application, resource_owner_id, scopes)
|
|
373
498
|
expect(last_token).to be_nil
|
|
@@ -386,32 +511,32 @@ module Doorkeeper
|
|
|
386
511
|
end
|
|
387
512
|
|
|
388
513
|
it "excludes tokens with fewer scopes" do
|
|
389
|
-
FactoryBot.create :access_token, default_attributes.merge(scopes:
|
|
514
|
+
FactoryBot.create :access_token, default_attributes.merge(scopes: "public")
|
|
390
515
|
last_token = AccessToken.matching_token_for(application, resource_owner_id, scopes)
|
|
391
516
|
expect(last_token).to be_nil
|
|
392
517
|
end
|
|
393
518
|
|
|
394
|
-
it
|
|
395
|
-
FactoryBot.create :access_token, default_attributes.merge(scopes:
|
|
519
|
+
it "excludes tokens with different scopes" do
|
|
520
|
+
FactoryBot.create :access_token, default_attributes.merge(scopes: "public email")
|
|
396
521
|
last_token = AccessToken.matching_token_for(application, resource_owner_id, scopes)
|
|
397
522
|
expect(last_token).to be_nil
|
|
398
523
|
end
|
|
399
524
|
|
|
400
|
-
it
|
|
401
|
-
FactoryBot.create :access_token, default_attributes.merge(scopes:
|
|
525
|
+
it "excludes tokens with additional scopes" do
|
|
526
|
+
FactoryBot.create :access_token, default_attributes.merge(scopes: "public write email")
|
|
402
527
|
last_token = AccessToken.matching_token_for(application, resource_owner_id, scopes)
|
|
403
528
|
expect(last_token).to be_nil
|
|
404
529
|
end
|
|
405
530
|
|
|
406
|
-
it
|
|
531
|
+
it "excludes tokens with scopes that are not present in server scopes" do
|
|
407
532
|
FactoryBot.create :access_token, default_attributes.merge(
|
|
408
|
-
application: application, scopes:
|
|
533
|
+
application: application, scopes: "public read"
|
|
409
534
|
)
|
|
410
535
|
last_token = AccessToken.matching_token_for(application, resource_owner_id, scopes)
|
|
411
536
|
expect(last_token).to be_nil
|
|
412
537
|
end
|
|
413
538
|
|
|
414
|
-
it
|
|
539
|
+
it "excludes tokens with scopes that are not present in application scopes" do
|
|
415
540
|
application = FactoryBot.create :application, scopes: "private read"
|
|
416
541
|
FactoryBot.create :access_token, default_attributes.merge(
|
|
417
542
|
application: application
|
|
@@ -420,11 +545,11 @@ module Doorkeeper
|
|
|
420
545
|
expect(last_token).to be_nil
|
|
421
546
|
end
|
|
422
547
|
|
|
423
|
-
it
|
|
548
|
+
it "does not match token if empty scope requested and token/app scopes present" do
|
|
424
549
|
application = FactoryBot.create :application, scopes: "sample:scope"
|
|
425
550
|
app_params = {
|
|
426
551
|
application_id: application.id, scopes: "sample:scope",
|
|
427
|
-
resource_owner_id: 100
|
|
552
|
+
resource_owner_id: 100,
|
|
428
553
|
}
|
|
429
554
|
FactoryBot.create :access_token, app_params
|
|
430
555
|
empty_scopes = Doorkeeper::OAuth::Scopes.from_string("")
|
|
@@ -432,17 +557,17 @@ module Doorkeeper
|
|
|
432
557
|
expect(last_token).to be_nil
|
|
433
558
|
end
|
|
434
559
|
|
|
435
|
-
it
|
|
560
|
+
it "matches token if empty scope requested and no token scopes present" do
|
|
436
561
|
empty_scopes = Doorkeeper::OAuth::Scopes.from_string("")
|
|
437
562
|
token = FactoryBot.create :access_token, default_attributes.merge(scopes: empty_scopes)
|
|
438
563
|
last_token = AccessToken.matching_token_for(application, 100, empty_scopes)
|
|
439
564
|
expect(last_token).to eq(token)
|
|
440
565
|
end
|
|
441
566
|
|
|
442
|
-
it
|
|
567
|
+
it "returns the last matching token" do
|
|
443
568
|
FactoryBot.create :access_token, default_attributes.merge(created_at: 1.day.ago)
|
|
444
569
|
matching_token = FactoryBot.create :access_token, default_attributes
|
|
445
|
-
FactoryBot.create :access_token, default_attributes.merge(scopes:
|
|
570
|
+
FactoryBot.create :access_token, default_attributes.merge(scopes: "public")
|
|
446
571
|
|
|
447
572
|
last_token = AccessToken.matching_token_for(application, resource_owner_id, scopes)
|
|
448
573
|
expect(last_token).to eq(matching_token)
|
|
@@ -453,11 +578,11 @@ module Doorkeeper
|
|
|
453
578
|
it "returns as_json hash" do
|
|
454
579
|
token = FactoryBot.create :access_token
|
|
455
580
|
token_hash = {
|
|
456
|
-
resource_owner_id:
|
|
457
|
-
scope:
|
|
458
|
-
expires_in:
|
|
459
|
-
application:
|
|
460
|
-
created_at:
|
|
581
|
+
resource_owner_id: token.resource_owner_id,
|
|
582
|
+
scope: token.scopes,
|
|
583
|
+
expires_in: token.expires_in_seconds,
|
|
584
|
+
application: { uid: token.application.uid },
|
|
585
|
+
created_at: token.created_at.to_i,
|
|
461
586
|
}
|
|
462
587
|
expect(token.as_json).to eq token_hash
|
|
463
588
|
end
|