doorkeeper 5.0.0 → 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 +5 -5
- data/.hound.yml +2 -1
- data/.rubocop.yml +37 -4
- data/.travis.yml +16 -21
- data/Appraisals +29 -7
- data/Dangerfile +67 -0
- data/Gemfile +19 -5
- data/NEWS.md +107 -10
- data/README.md +75 -470
- data/Rakefile +10 -8
- data/app/controllers/doorkeeper/application_controller.rb +3 -2
- data/app/controllers/doorkeeper/application_metal_controller.rb +4 -13
- data/app/controllers/doorkeeper/applications_controller.rb +21 -7
- data/app/controllers/doorkeeper/authorizations_controller.rb +3 -3
- data/app/controllers/doorkeeper/authorized_applications_controller.rb +2 -0
- data/app/controllers/doorkeeper/token_info_controller.rb +4 -2
- data/app/controllers/doorkeeper/tokens_controller.rb +13 -11
- data/app/helpers/doorkeeper/dashboard_helper.rb +3 -1
- data/app/validators/redirect_uri_validator.rb +7 -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 +28 -24
- 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 +197 -76
- data/lib/doorkeeper/engine.rb +3 -1
- data/lib/doorkeeper/errors.rb +17 -0
- data/lib/doorkeeper/grape/authorization_decorator.rb +6 -4
- data/lib/doorkeeper/grape/helpers.rb +5 -3
- data/lib/doorkeeper/helpers/controller.rb +5 -2
- data/lib/doorkeeper/models/access_grant_mixin.rb +25 -6
- data/lib/doorkeeper/models/access_token_mixin.rb +68 -22
- data/lib/doorkeeper/models/application_mixin.rb +45 -1
- data/lib/doorkeeper/models/concerns/accessible.rb +2 -0
- data/lib/doorkeeper/models/concerns/expirable.rb +6 -2
- data/lib/doorkeeper/models/concerns/orderable.rb +2 -0
- data/lib/doorkeeper/models/concerns/ownership.rb +3 -6
- data/lib/doorkeeper/models/concerns/reusable.rb +19 -0
- data/lib/doorkeeper/models/concerns/revocable.rb +4 -1
- data/lib/doorkeeper/models/concerns/scopes.rb +7 -1
- data/lib/doorkeeper/models/concerns/secret_storable.rb +108 -0
- data/lib/doorkeeper/oauth/authorization/code.rb +4 -2
- data/lib/doorkeeper/oauth/authorization/context.rb +2 -0
- data/lib/doorkeeper/oauth/authorization/token.rb +14 -7
- data/lib/doorkeeper/oauth/authorization/uri_builder.rb +3 -1
- data/lib/doorkeeper/oauth/authorization_code_request.rb +8 -4
- data/lib/doorkeeper/oauth/base_request.rb +2 -0
- data/lib/doorkeeper/oauth/base_response.rb +2 -0
- data/lib/doorkeeper/oauth/client/credentials.rb +2 -0
- data/lib/doorkeeper/oauth/client.rb +3 -1
- data/lib/doorkeeper/oauth/client_credentials/creator.rb +2 -0
- data/lib/doorkeeper/oauth/client_credentials/issuer.rb +2 -0
- data/lib/doorkeeper/oauth/client_credentials/validation.rb +7 -4
- data/lib/doorkeeper/oauth/client_credentials_request.rb +3 -1
- data/lib/doorkeeper/oauth/code_request.rb +2 -0
- data/lib/doorkeeper/oauth/code_response.rb +4 -2
- data/lib/doorkeeper/oauth/error.rb +2 -0
- data/lib/doorkeeper/oauth/error_response.rb +20 -6
- data/lib/doorkeeper/oauth/forbidden_token_response.rb +10 -3
- data/lib/doorkeeper/oauth/helpers/scope_checker.rb +25 -8
- data/lib/doorkeeper/oauth/helpers/unique_token.rb +15 -1
- data/lib/doorkeeper/oauth/helpers/uri_checker.rb +42 -3
- data/lib/doorkeeper/oauth/invalid_token_response.rb +22 -0
- data/lib/doorkeeper/oauth/password_access_token_request.rb +9 -2
- data/lib/doorkeeper/oauth/pre_authorization.rb +13 -5
- data/lib/doorkeeper/oauth/refresh_token_request.rb +15 -4
- data/lib/doorkeeper/oauth/scopes.rb +7 -3
- data/lib/doorkeeper/oauth/token.rb +4 -2
- data/lib/doorkeeper/oauth/token_introspection.rb +76 -8
- data/lib/doorkeeper/oauth/token_request.rb +2 -0
- data/lib/doorkeeper/oauth/token_response.rb +11 -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 +26 -7
- data/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +10 -3
- data/lib/doorkeeper/orm/active_record.rb +8 -6
- data/lib/doorkeeper/rails/helpers.rb +5 -1
- data/lib/doorkeeper/rails/routes/mapper.rb +2 -0
- data/lib/doorkeeper/rails/routes/mapping.rb +9 -7
- data/lib/doorkeeper/rails/routes.rb +19 -12
- data/lib/doorkeeper/rake/db.rake +17 -17
- data/lib/doorkeeper/rake.rb +1 -1
- data/lib/doorkeeper/request/authorization_code.rb +2 -0
- data/lib/doorkeeper/request/client_credentials.rb +2 -0
- data/lib/doorkeeper/request/code.rb +2 -0
- data/lib/doorkeeper/request/password.rb +2 -0
- data/lib/doorkeeper/request/refresh_token.rb +2 -0
- data/lib/doorkeeper/request/strategy.rb +2 -0
- data/lib/doorkeeper/request/token.rb +2 -0
- data/lib/doorkeeper/request.rb +3 -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/server.rb +2 -0
- data/lib/doorkeeper/stale_records_cleaner.rb +20 -0
- data/lib/doorkeeper/validations.rb +2 -0
- data/lib/doorkeeper/version.rb +4 -2
- data/lib/doorkeeper.rb +69 -59
- 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 +138 -13
- data/lib/generators/doorkeeper/templates/migration.rb.erb +17 -10
- 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 +76 -78
- data/spec/controllers/authorizations_controller_spec.rb +189 -142
- data/spec/controllers/protected_resources_controller_spec.rb +111 -67
- data/spec/controllers/token_info_controller_spec.rb +14 -12
- data/spec/controllers/tokens_controller_spec.rb +170 -112
- 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 +3 -1
- data/spec/dummy/app/controllers/full_protected_resources_controller.rb +4 -2
- data/spec/dummy/app/controllers/home_controller.rb +6 -5
- 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 +26 -2
- data/spec/dummy/config/boot.rb +2 -4
- data/spec/dummy/config/environment.rb +1 -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 +4 -2
- data/spec/dummy/config/initializers/backtrace_silencers.rb +2 -0
- data/spec/dummy/config/initializers/doorkeeper.rb +7 -3
- 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 +20 -13
- data/spec/dummy/db/migrate/20151223200000_add_owner_to_application.rb +1 -1
- data/spec/dummy/db/migrate/20170822064514_enable_pkce.rb +2 -0
- data/spec/dummy/db/schema.rb +1 -1
- data/spec/dummy/script/rails +7 -4
- data/spec/factories.rb +11 -9
- 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 +19 -14
- 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 -1
- data/spec/generators/views_generator_spec.rb +14 -12
- data/spec/grape/grape_integration_spec.rb +35 -33
- data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +9 -7
- data/spec/lib/config_spec.rb +290 -104
- data/spec/lib/doorkeeper_spec.rb +8 -6
- 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 +43 -17
- data/spec/lib/oauth/base_request_spec.rb +24 -12
- data/spec/lib/oauth/base_response_spec.rb +3 -1
- data/spec/lib/oauth/client/credentials_spec.rb +26 -24
- data/spec/lib/oauth/client_credentials/creator_spec.rb +56 -10
- data/spec/lib/oauth/client_credentials/issuer_spec.rb +27 -20
- data/spec/lib/oauth/client_credentials/validation_spec.rb +21 -15
- data/spec/lib/oauth/client_credentials_integration_spec.rb +8 -6
- 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 +10 -8
- 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 +124 -93
- data/spec/lib/oauth/invalid_token_response_spec.rb +3 -1
- data/spec/lib/oauth/password_access_token_request_spec.rb +95 -34
- data/spec/lib/oauth/pre_authorization_spec.rb +80 -54
- data/spec/lib/oauth/refresh_token_request_spec.rb +46 -39
- data/spec/lib/oauth/scopes_spec.rb +63 -61
- data/spec/lib/oauth/token_request_spec.rb +80 -25
- data/spec/lib/oauth/token_response_spec.rb +40 -38
- data/spec/lib/oauth/token_spec.rb +64 -45
- 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 +20 -18
- data/spec/lib/{orm/active_record/stale_records_cleaner_spec.rb → stale_records_cleaner_spec.rb} +28 -18
- data/spec/models/doorkeeper/access_grant_spec.rb +80 -15
- data/spec/models/doorkeeper/access_token_spec.rb +212 -83
- data/spec/models/doorkeeper/application_spec.rb +146 -49
- data/spec/requests/applications/applications_request_spec.rb +100 -68
- data/spec/requests/applications/authorized_applications_spec.rb +20 -18
- data/spec/requests/endpoints/authorization_spec.rb +27 -25
- data/spec/requests/endpoints/token_spec.rb +38 -36
- data/spec/requests/flows/authorization_code_errors_spec.rb +27 -25
- data/spec/requests/flows/authorization_code_spec.rb +260 -158
- data/spec/requests/flows/client_credentials_spec.rb +54 -52
- data/spec/requests/flows/implicit_grant_errors_spec.rb +11 -9
- data/spec/requests/flows/implicit_grant_spec.rb +27 -25
- data/spec/requests/flows/password_spec.rb +64 -56
- data/spec/requests/flows/refresh_token_spec.rb +91 -32
- data/spec/requests/flows/revoke_token_spec.rb +49 -63
- 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 +37 -35
- 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 +5 -2
- 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 +11 -5
- data/spec/support/helpers/request_spec_helper.rb +5 -3
- data/spec/support/helpers/url_helper.rb +20 -18
- 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 +68 -10
- 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 +76 -47
- data/spec/version/version_spec.rb +7 -5
- metadata +70 -21
- data/gemfiles/rails_4_2.gemfile +0 -13
- data/spec/dummy/config/initializers/new_framework_defaults.rb +0 -10
|
@@ -1,31 +1,94 @@
|
|
|
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
|
|
|
15
18
|
module CustomGeneratorArgs
|
|
16
|
-
def self.generate
|
|
17
|
-
end
|
|
19
|
+
def self.generate; end
|
|
18
20
|
end
|
|
19
21
|
|
|
20
22
|
describe :generate_token do
|
|
21
|
-
it
|
|
23
|
+
it "generates a token using the default method" do
|
|
22
24
|
FactoryBot.create :access_token
|
|
23
25
|
|
|
24
26
|
token = FactoryBot.create :access_token
|
|
25
27
|
expect(token.token).to be_a(String)
|
|
26
28
|
end
|
|
27
29
|
|
|
28
|
-
|
|
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
|
|
29
92
|
eigenclass = class << CustomGeneratorArgs; self; end
|
|
30
93
|
eigenclass.class_eval do
|
|
31
94
|
remove_method :generate
|
|
@@ -42,10 +105,10 @@ module Doorkeeper
|
|
|
42
105
|
end
|
|
43
106
|
|
|
44
107
|
token = FactoryBot.create :access_token
|
|
45
|
-
expect(token.token).to match(
|
|
108
|
+
expect(token.token).to match(/custom_generator_token_\d+/)
|
|
46
109
|
end
|
|
47
110
|
|
|
48
|
-
it
|
|
111
|
+
it "allows the custom generator to access the application details" do
|
|
49
112
|
eigenclass = class << CustomGeneratorArgs; self; end
|
|
50
113
|
eigenclass.class_eval do
|
|
51
114
|
remove_method :generate
|
|
@@ -62,10 +125,10 @@ module Doorkeeper
|
|
|
62
125
|
end
|
|
63
126
|
|
|
64
127
|
token = FactoryBot.create :access_token
|
|
65
|
-
expect(token.token).to match(
|
|
128
|
+
expect(token.token).to match(/custom_generator_token_Application \d+/)
|
|
66
129
|
end
|
|
67
130
|
|
|
68
|
-
it
|
|
131
|
+
it "allows the custom generator to access the scopes" do
|
|
69
132
|
eigenclass = class << CustomGeneratorArgs; self; end
|
|
70
133
|
eigenclass.class_eval do
|
|
71
134
|
remove_method :generate
|
|
@@ -81,12 +144,12 @@ module Doorkeeper
|
|
|
81
144
|
access_token_generator "Doorkeeper::CustomGeneratorArgs"
|
|
82
145
|
end
|
|
83
146
|
|
|
84
|
-
token = FactoryBot.create :access_token, scopes:
|
|
147
|
+
token = FactoryBot.create :access_token, scopes: "public write"
|
|
85
148
|
|
|
86
|
-
expect(token.token).to eq
|
|
149
|
+
expect(token.token).to eq "custom_generator_token_2_public write"
|
|
87
150
|
end
|
|
88
151
|
|
|
89
|
-
it
|
|
152
|
+
it "allows the custom generator to access the expiry length" do
|
|
90
153
|
eigenclass = class << CustomGeneratorArgs; self; end
|
|
91
154
|
eigenclass.class_eval do
|
|
92
155
|
remove_method :generate
|
|
@@ -103,10 +166,10 @@ module Doorkeeper
|
|
|
103
166
|
end
|
|
104
167
|
|
|
105
168
|
token = FactoryBot.create :access_token
|
|
106
|
-
expect(token.token).to eq
|
|
169
|
+
expect(token.token).to eq "custom_generator_token_7200"
|
|
107
170
|
end
|
|
108
171
|
|
|
109
|
-
it
|
|
172
|
+
it "allows the custom generator to access the created time" do
|
|
110
173
|
module CustomGeneratorArgs
|
|
111
174
|
def self.generate(opts = {})
|
|
112
175
|
"custom_generator_token_#{opts[:created_at].to_i}"
|
|
@@ -123,7 +186,7 @@ module Doorkeeper
|
|
|
123
186
|
expect(token.token).to eq "custom_generator_token_#{created_at.to_i}"
|
|
124
187
|
end
|
|
125
188
|
|
|
126
|
-
it
|
|
189
|
+
it "raises an error if the custom object does not support generate" do
|
|
127
190
|
module NoGenerate
|
|
128
191
|
end
|
|
129
192
|
|
|
@@ -137,7 +200,7 @@ module Doorkeeper
|
|
|
137
200
|
)
|
|
138
201
|
end
|
|
139
202
|
|
|
140
|
-
it
|
|
203
|
+
it "raises original error if something went wrong in custom generator" do
|
|
141
204
|
eigenclass = class << CustomGeneratorArgs; self; end
|
|
142
205
|
eigenclass.class_eval do
|
|
143
206
|
remove_method :generate
|
|
@@ -145,7 +208,7 @@ module Doorkeeper
|
|
|
145
208
|
|
|
146
209
|
module CustomGeneratorArgs
|
|
147
210
|
def self.generate(_opts = {})
|
|
148
|
-
raise LoadError,
|
|
211
|
+
raise LoadError, "custom behaviour"
|
|
149
212
|
end
|
|
150
213
|
end
|
|
151
214
|
|
|
@@ -159,7 +222,7 @@ module Doorkeeper
|
|
|
159
222
|
)
|
|
160
223
|
end
|
|
161
224
|
|
|
162
|
-
it
|
|
225
|
+
it "raises an error if the custom object does not exist" do
|
|
163
226
|
Doorkeeper.configure do
|
|
164
227
|
orm DOORKEEPER_ORM
|
|
165
228
|
access_token_generator "Doorkeeper::NotReal"
|
|
@@ -172,24 +235,24 @@ module Doorkeeper
|
|
|
172
235
|
end
|
|
173
236
|
|
|
174
237
|
describe :refresh_token do
|
|
175
|
-
it
|
|
238
|
+
it "has empty refresh token if it was not required" do
|
|
176
239
|
token = FactoryBot.create :access_token
|
|
177
240
|
expect(token.refresh_token).to be_nil
|
|
178
241
|
end
|
|
179
242
|
|
|
180
|
-
it
|
|
243
|
+
it "generates a refresh token if it was requested" do
|
|
181
244
|
token = FactoryBot.create :access_token, use_refresh_token: true
|
|
182
245
|
expect(token.refresh_token).not_to be_nil
|
|
183
246
|
end
|
|
184
247
|
|
|
185
|
-
it
|
|
248
|
+
it "is not valid if token exists" do
|
|
186
249
|
token1 = FactoryBot.create :access_token, use_refresh_token: true
|
|
187
250
|
token2 = FactoryBot.create :access_token, use_refresh_token: true
|
|
188
251
|
token2.refresh_token = token1.refresh_token
|
|
189
252
|
expect(token2).not_to be_valid
|
|
190
253
|
end
|
|
191
254
|
|
|
192
|
-
it
|
|
255
|
+
it "expects database to raise an error if refresh tokens are the same" do
|
|
193
256
|
token1 = FactoryBot.create :access_token, use_refresh_token: true
|
|
194
257
|
token2 = FactoryBot.create :access_token, use_refresh_token: true
|
|
195
258
|
expect do
|
|
@@ -197,26 +260,85 @@ module Doorkeeper
|
|
|
197
260
|
token2.save(validate: false)
|
|
198
261
|
end.to raise_error(uniqueness_error)
|
|
199
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
|
|
200
324
|
end
|
|
201
325
|
|
|
202
|
-
describe
|
|
203
|
-
it
|
|
326
|
+
describe "validations" do
|
|
327
|
+
it "is valid without resource_owner_id" do
|
|
204
328
|
# For client credentials flow
|
|
205
329
|
subject.resource_owner_id = nil
|
|
206
330
|
expect(subject).to be_valid
|
|
207
331
|
end
|
|
208
332
|
|
|
209
|
-
it
|
|
333
|
+
it "is valid without application_id" do
|
|
210
334
|
# For resource owner credentials flow
|
|
211
335
|
subject.application_id = nil
|
|
212
336
|
expect(subject).to be_valid
|
|
213
337
|
end
|
|
214
338
|
end
|
|
215
339
|
|
|
216
|
-
describe
|
|
217
|
-
|
|
218
|
-
context 'with default parameters' do
|
|
219
|
-
|
|
340
|
+
describe "#same_credential?" do
|
|
341
|
+
context "with default parameters" do
|
|
220
342
|
let(:resource_owner_id) { 100 }
|
|
221
343
|
let(:application) { FactoryBot.create :application }
|
|
222
344
|
let(:default_attributes) do
|
|
@@ -224,80 +346,87 @@ module Doorkeeper
|
|
|
224
346
|
end
|
|
225
347
|
let(:access_token1) { FactoryBot.create :access_token, default_attributes }
|
|
226
348
|
|
|
227
|
-
context
|
|
349
|
+
context "the second token has the same owner and same app" do
|
|
228
350
|
let(:access_token2) { FactoryBot.create :access_token, default_attributes }
|
|
229
|
-
it
|
|
351
|
+
it "success" do
|
|
230
352
|
expect(access_token1.same_credential?(access_token2)).to be_truthy
|
|
231
353
|
end
|
|
232
354
|
end
|
|
233
355
|
|
|
234
|
-
context
|
|
356
|
+
context "the second token has same owner and different app" do
|
|
235
357
|
let(:other_application) { FactoryBot.create :application }
|
|
236
|
-
let(:access_token2)
|
|
358
|
+
let(:access_token2) do
|
|
359
|
+
FactoryBot.create :access_token,
|
|
360
|
+
application: other_application,
|
|
361
|
+
resource_owner_id: resource_owner_id
|
|
362
|
+
end
|
|
237
363
|
|
|
238
|
-
it
|
|
364
|
+
it "fail" do
|
|
239
365
|
expect(access_token1.same_credential?(access_token2)).to be_falsey
|
|
240
366
|
end
|
|
241
367
|
end
|
|
242
368
|
|
|
243
|
-
context
|
|
244
|
-
|
|
369
|
+
context "the second token has different owner and different app" do
|
|
245
370
|
let(:other_application) { FactoryBot.create :application }
|
|
246
|
-
let(:access_token2)
|
|
371
|
+
let(:access_token2) do
|
|
372
|
+
FactoryBot.create :access_token, application: other_application, resource_owner_id: 42
|
|
373
|
+
end
|
|
247
374
|
|
|
248
|
-
it
|
|
375
|
+
it "fail" do
|
|
249
376
|
expect(access_token1.same_credential?(access_token2)).to be_falsey
|
|
250
377
|
end
|
|
251
378
|
end
|
|
252
379
|
|
|
253
|
-
context
|
|
254
|
-
let(:access_token2)
|
|
380
|
+
context "the second token has different owner and same app" do
|
|
381
|
+
let(:access_token2) do
|
|
382
|
+
FactoryBot.create :access_token, application: application, resource_owner_id: 42
|
|
383
|
+
end
|
|
255
384
|
|
|
256
|
-
it
|
|
385
|
+
it "fail" do
|
|
257
386
|
expect(access_token1.same_credential?(access_token2)).to be_falsey
|
|
258
387
|
end
|
|
259
388
|
end
|
|
260
389
|
end
|
|
261
390
|
end
|
|
262
391
|
|
|
263
|
-
describe
|
|
264
|
-
context
|
|
392
|
+
describe "#acceptable?" do
|
|
393
|
+
context "a token that is not accessible" do
|
|
265
394
|
let(:token) { FactoryBot.create(:access_token, created_at: 6.hours.ago) }
|
|
266
395
|
|
|
267
|
-
it
|
|
396
|
+
it "should return false" do
|
|
268
397
|
expect(token.acceptable?(nil)).to be false
|
|
269
398
|
end
|
|
270
399
|
end
|
|
271
400
|
|
|
272
|
-
context
|
|
401
|
+
context "a token that has the incorrect scopes" do
|
|
273
402
|
let(:token) { FactoryBot.create(:access_token) }
|
|
274
403
|
|
|
275
|
-
it
|
|
276
|
-
expect(token.acceptable?([
|
|
404
|
+
it "should return false" do
|
|
405
|
+
expect(token.acceptable?(["public"])).to be false
|
|
277
406
|
end
|
|
278
407
|
end
|
|
279
408
|
|
|
280
|
-
context
|
|
409
|
+
context "a token is acceptable with the correct scopes" do
|
|
281
410
|
let(:token) do
|
|
282
411
|
token = FactoryBot.create(:access_token)
|
|
283
|
-
token[:scopes] =
|
|
412
|
+
token[:scopes] = "public"
|
|
284
413
|
token
|
|
285
414
|
end
|
|
286
415
|
|
|
287
|
-
it
|
|
288
|
-
expect(token.acceptable?([
|
|
416
|
+
it "should return true" do
|
|
417
|
+
expect(token.acceptable?(["public"])).to be true
|
|
289
418
|
end
|
|
290
419
|
end
|
|
291
420
|
end
|
|
292
421
|
|
|
293
|
-
describe
|
|
422
|
+
describe ".revoke_all_for" do
|
|
294
423
|
let(:resource_owner) { double(id: 100) }
|
|
295
424
|
let(:application) { FactoryBot.create :application }
|
|
296
425
|
let(:default_attributes) do
|
|
297
426
|
{ application: application, resource_owner_id: resource_owner.id }
|
|
298
427
|
end
|
|
299
428
|
|
|
300
|
-
it
|
|
429
|
+
it "revokes all tokens for given application and resource owner" do
|
|
301
430
|
FactoryBot.create :access_token, default_attributes
|
|
302
431
|
AccessToken.revoke_all_for application.id, resource_owner
|
|
303
432
|
AccessToken.all.each do |token|
|
|
@@ -305,7 +434,7 @@ module Doorkeeper
|
|
|
305
434
|
end
|
|
306
435
|
end
|
|
307
436
|
|
|
308
|
-
it
|
|
437
|
+
it "matches application" do
|
|
309
438
|
access_token_for_different_app = FactoryBot.create(
|
|
310
439
|
:access_token,
|
|
311
440
|
default_attributes.merge(application: FactoryBot.create(:application))
|
|
@@ -316,7 +445,7 @@ module Doorkeeper
|
|
|
316
445
|
expect(access_token_for_different_app.reload).not_to be_revoked
|
|
317
446
|
end
|
|
318
447
|
|
|
319
|
-
it
|
|
448
|
+
it "matches resource owner" do
|
|
320
449
|
access_token_for_different_owner = FactoryBot.create(
|
|
321
450
|
:access_token,
|
|
322
451
|
default_attributes.merge(resource_owner_id: 90)
|
|
@@ -328,15 +457,15 @@ module Doorkeeper
|
|
|
328
457
|
end
|
|
329
458
|
end
|
|
330
459
|
|
|
331
|
-
describe
|
|
460
|
+
describe ".matching_token_for" do
|
|
332
461
|
let(:resource_owner_id) { 100 }
|
|
333
462
|
let(:application) { FactoryBot.create :application }
|
|
334
|
-
let(:scopes) { Doorkeeper::OAuth::Scopes.from_string(
|
|
463
|
+
let(:scopes) { Doorkeeper::OAuth::Scopes.from_string("public write") }
|
|
335
464
|
let(:default_attributes) do
|
|
336
465
|
{
|
|
337
466
|
application: application,
|
|
338
467
|
resource_owner_id: resource_owner_id,
|
|
339
|
-
scopes: scopes.to_s
|
|
468
|
+
scopes: scopes.to_s,
|
|
340
469
|
}
|
|
341
470
|
end
|
|
342
471
|
|
|
@@ -344,26 +473,26 @@ module Doorkeeper
|
|
|
344
473
|
default_scopes_exist(*scopes.all)
|
|
345
474
|
end
|
|
346
475
|
|
|
347
|
-
it
|
|
476
|
+
it "returns only one token" do
|
|
348
477
|
token = FactoryBot.create :access_token, default_attributes
|
|
349
478
|
last_token = AccessToken.matching_token_for(application, resource_owner_id, scopes)
|
|
350
479
|
expect(last_token).to eq(token)
|
|
351
480
|
end
|
|
352
481
|
|
|
353
|
-
it
|
|
482
|
+
it "accepts resource owner as object" do
|
|
354
483
|
resource_owner = double(to_key: true, id: 100)
|
|
355
484
|
token = FactoryBot.create :access_token, default_attributes
|
|
356
485
|
last_token = AccessToken.matching_token_for(application, resource_owner, scopes)
|
|
357
486
|
expect(last_token).to eq(token)
|
|
358
487
|
end
|
|
359
488
|
|
|
360
|
-
it
|
|
489
|
+
it "accepts nil as resource owner" do
|
|
361
490
|
token = FactoryBot.create :access_token, default_attributes.merge(resource_owner_id: nil)
|
|
362
491
|
last_token = AccessToken.matching_token_for(application, nil, scopes)
|
|
363
492
|
expect(last_token).to eq(token)
|
|
364
493
|
end
|
|
365
494
|
|
|
366
|
-
it
|
|
495
|
+
it "excludes revoked tokens" do
|
|
367
496
|
FactoryBot.create :access_token, default_attributes.merge(revoked_at: 1.day.ago)
|
|
368
497
|
last_token = AccessToken.matching_token_for(application, resource_owner_id, scopes)
|
|
369
498
|
expect(last_token).to be_nil
|
|
@@ -382,32 +511,32 @@ module Doorkeeper
|
|
|
382
511
|
end
|
|
383
512
|
|
|
384
513
|
it "excludes tokens with fewer scopes" do
|
|
385
|
-
FactoryBot.create :access_token, default_attributes.merge(scopes:
|
|
514
|
+
FactoryBot.create :access_token, default_attributes.merge(scopes: "public")
|
|
386
515
|
last_token = AccessToken.matching_token_for(application, resource_owner_id, scopes)
|
|
387
516
|
expect(last_token).to be_nil
|
|
388
517
|
end
|
|
389
518
|
|
|
390
|
-
it
|
|
391
|
-
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")
|
|
392
521
|
last_token = AccessToken.matching_token_for(application, resource_owner_id, scopes)
|
|
393
522
|
expect(last_token).to be_nil
|
|
394
523
|
end
|
|
395
524
|
|
|
396
|
-
it
|
|
397
|
-
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")
|
|
398
527
|
last_token = AccessToken.matching_token_for(application, resource_owner_id, scopes)
|
|
399
528
|
expect(last_token).to be_nil
|
|
400
529
|
end
|
|
401
530
|
|
|
402
|
-
it
|
|
531
|
+
it "excludes tokens with scopes that are not present in server scopes" do
|
|
403
532
|
FactoryBot.create :access_token, default_attributes.merge(
|
|
404
|
-
application: application, scopes:
|
|
533
|
+
application: application, scopes: "public read"
|
|
405
534
|
)
|
|
406
535
|
last_token = AccessToken.matching_token_for(application, resource_owner_id, scopes)
|
|
407
536
|
expect(last_token).to be_nil
|
|
408
537
|
end
|
|
409
538
|
|
|
410
|
-
it
|
|
539
|
+
it "excludes tokens with scopes that are not present in application scopes" do
|
|
411
540
|
application = FactoryBot.create :application, scopes: "private read"
|
|
412
541
|
FactoryBot.create :access_token, default_attributes.merge(
|
|
413
542
|
application: application
|
|
@@ -416,11 +545,11 @@ module Doorkeeper
|
|
|
416
545
|
expect(last_token).to be_nil
|
|
417
546
|
end
|
|
418
547
|
|
|
419
|
-
it
|
|
548
|
+
it "does not match token if empty scope requested and token/app scopes present" do
|
|
420
549
|
application = FactoryBot.create :application, scopes: "sample:scope"
|
|
421
550
|
app_params = {
|
|
422
551
|
application_id: application.id, scopes: "sample:scope",
|
|
423
|
-
resource_owner_id: 100
|
|
552
|
+
resource_owner_id: 100,
|
|
424
553
|
}
|
|
425
554
|
FactoryBot.create :access_token, app_params
|
|
426
555
|
empty_scopes = Doorkeeper::OAuth::Scopes.from_string("")
|
|
@@ -428,17 +557,17 @@ module Doorkeeper
|
|
|
428
557
|
expect(last_token).to be_nil
|
|
429
558
|
end
|
|
430
559
|
|
|
431
|
-
it
|
|
560
|
+
it "matches token if empty scope requested and no token scopes present" do
|
|
432
561
|
empty_scopes = Doorkeeper::OAuth::Scopes.from_string("")
|
|
433
562
|
token = FactoryBot.create :access_token, default_attributes.merge(scopes: empty_scopes)
|
|
434
563
|
last_token = AccessToken.matching_token_for(application, 100, empty_scopes)
|
|
435
564
|
expect(last_token).to eq(token)
|
|
436
565
|
end
|
|
437
566
|
|
|
438
|
-
it
|
|
567
|
+
it "returns the last matching token" do
|
|
439
568
|
FactoryBot.create :access_token, default_attributes.merge(created_at: 1.day.ago)
|
|
440
569
|
matching_token = FactoryBot.create :access_token, default_attributes
|
|
441
|
-
FactoryBot.create :access_token, default_attributes.merge(scopes:
|
|
570
|
+
FactoryBot.create :access_token, default_attributes.merge(scopes: "public")
|
|
442
571
|
|
|
443
572
|
last_token = AccessToken.matching_token_for(application, resource_owner_id, scopes)
|
|
444
573
|
expect(last_token).to eq(matching_token)
|
|
@@ -449,11 +578,11 @@ module Doorkeeper
|
|
|
449
578
|
it "returns as_json hash" do
|
|
450
579
|
token = FactoryBot.create :access_token
|
|
451
580
|
token_hash = {
|
|
452
|
-
resource_owner_id:
|
|
453
|
-
scope:
|
|
454
|
-
expires_in:
|
|
455
|
-
application:
|
|
456
|
-
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,
|
|
457
586
|
}
|
|
458
587
|
expect(token.as_json).to eq token_hash
|
|
459
588
|
end
|