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,374 +1,366 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "spec_helper"
|
|
4
|
+
require "bcrypt"
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
module Doorkeeper
|
|
7
|
+
describe Application do
|
|
8
|
+
let(:clazz) { Doorkeeper::Application }
|
|
9
|
+
let(:require_owner) { Doorkeeper.configuration.instance_variable_set("@confirm_application_owner", true) }
|
|
10
|
+
let(:unset_require_owner) { Doorkeeper.configuration.instance_variable_set("@confirm_application_owner", false) }
|
|
11
|
+
let(:new_application) { FactoryBot.build(:application) }
|
|
9
12
|
|
|
10
|
-
|
|
11
|
-
|
|
13
|
+
let(:uid) { SecureRandom.hex(8) }
|
|
14
|
+
let(:secret) { SecureRandom.hex(8) }
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
expect(new_application).not_to be_valid
|
|
21
|
-
end
|
|
16
|
+
context "application_owner is enabled" do
|
|
17
|
+
before do
|
|
18
|
+
Doorkeeper.configure do
|
|
19
|
+
orm DOORKEEPER_ORM
|
|
20
|
+
enable_application_owner
|
|
21
|
+
end
|
|
22
|
+
end
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
end
|
|
24
|
+
context "application owner is not required" do
|
|
25
|
+
before(:each) do
|
|
26
|
+
unset_require_owner
|
|
27
|
+
end
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
end
|
|
29
|
+
it "is valid given valid attributes" do
|
|
30
|
+
expect(new_application).to be_valid
|
|
31
|
+
end
|
|
32
|
+
end
|
|
34
33
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
context "application owner is required" do
|
|
35
|
+
before(:each) do
|
|
36
|
+
require_owner
|
|
37
|
+
@owner = FactoryBot.build_stubbed(:doorkeeper_testing_user)
|
|
38
|
+
end
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
expect(new_application).not_to be_valid
|
|
45
|
-
end
|
|
40
|
+
it "is invalid without an owner" do
|
|
41
|
+
expect(new_application).not_to be_valid
|
|
42
|
+
end
|
|
46
43
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
44
|
+
it "is valid with an owner" do
|
|
45
|
+
new_application.owner = @owner
|
|
46
|
+
expect(new_application).to be_valid
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
53
50
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
expect { app2.save!(validate: false) }.to raise_error(uniqueness_error)
|
|
59
|
-
end
|
|
51
|
+
it "is invalid without a name" do
|
|
52
|
+
new_application.name = nil
|
|
53
|
+
expect(new_application).not_to be_valid
|
|
54
|
+
end
|
|
60
55
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
end
|
|
56
|
+
it "is invalid without determining confidentiality" do
|
|
57
|
+
new_application.confidential = nil
|
|
58
|
+
expect(new_application).not_to be_valid
|
|
59
|
+
end
|
|
66
60
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
61
|
+
it "generates uid on create" do
|
|
62
|
+
expect(new_application.uid).to be_nil
|
|
63
|
+
new_application.save
|
|
64
|
+
expect(new_application.uid).not_to be_nil
|
|
65
|
+
end
|
|
72
66
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
67
|
+
it "generates uid on create if an empty string" do
|
|
68
|
+
new_application.uid = ""
|
|
69
|
+
new_application.save
|
|
70
|
+
expect(new_application.uid).not_to be_blank
|
|
71
|
+
end
|
|
78
72
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
73
|
+
it "generates uid on create unless one is set" do
|
|
74
|
+
new_application.uid = uid
|
|
75
|
+
new_application.save
|
|
76
|
+
expect(new_application.uid).to eq(uid)
|
|
77
|
+
end
|
|
84
78
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
enable_application_owner
|
|
90
|
-
end
|
|
79
|
+
it "is invalid without uid" do
|
|
80
|
+
new_application.save
|
|
81
|
+
new_application.uid = nil
|
|
82
|
+
expect(new_application).not_to be_valid
|
|
91
83
|
end
|
|
92
84
|
|
|
93
|
-
context "
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
85
|
+
context "redirect URI" do
|
|
86
|
+
context "when grant flows allow blank redirect URI" do
|
|
87
|
+
before do
|
|
88
|
+
Doorkeeper.configure do
|
|
89
|
+
grant_flows %w[password client_credentials]
|
|
90
|
+
end
|
|
91
|
+
end
|
|
97
92
|
|
|
98
|
-
|
|
99
|
-
|
|
93
|
+
it "is valid without redirect_uri" do
|
|
94
|
+
new_application.save
|
|
95
|
+
new_application.redirect_uri = nil
|
|
96
|
+
expect(new_application).to be_valid
|
|
97
|
+
end
|
|
100
98
|
end
|
|
101
|
-
end
|
|
102
99
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
100
|
+
context "when grant flows require redirect URI" do
|
|
101
|
+
before do
|
|
102
|
+
Doorkeeper.configure do
|
|
103
|
+
grant_flows %w[password client_credentials authorization_code]
|
|
104
|
+
end
|
|
105
|
+
end
|
|
108
106
|
|
|
109
|
-
|
|
110
|
-
|
|
107
|
+
it "is invalid without redirect_uri" do
|
|
108
|
+
new_application.save
|
|
109
|
+
new_application.redirect_uri = nil
|
|
110
|
+
expect(new_application).not_to be_valid
|
|
111
|
+
end
|
|
111
112
|
end
|
|
112
113
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
114
|
+
context "when blank URI option disabled" do
|
|
115
|
+
before do
|
|
116
|
+
Doorkeeper.configure do
|
|
117
|
+
grant_flows %w[password client_credentials]
|
|
118
|
+
allow_blank_redirect_uri false
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it "is invalid without redirect_uri" do
|
|
123
|
+
new_application.save
|
|
124
|
+
new_application.redirect_uri = nil
|
|
125
|
+
expect(new_application).not_to be_valid
|
|
126
|
+
end
|
|
116
127
|
end
|
|
117
128
|
end
|
|
118
|
-
end
|
|
119
129
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
expect(
|
|
130
|
+
it "checks uniqueness of uid" do
|
|
131
|
+
app1 = FactoryBot.create(:application)
|
|
132
|
+
app2 = FactoryBot.create(:application)
|
|
133
|
+
app2.uid = app1.uid
|
|
134
|
+
expect(app2).not_to be_valid
|
|
125
135
|
end
|
|
126
|
-
end
|
|
127
136
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
137
|
+
it "expects database to throw an error when uids are the same" do
|
|
138
|
+
app1 = FactoryBot.create(:application)
|
|
139
|
+
app2 = FactoryBot.create(:application)
|
|
140
|
+
app2.uid = app1.uid
|
|
141
|
+
expect { app2.save!(validate: false) }.to raise_error(uniqueness_error)
|
|
131
142
|
end
|
|
132
143
|
|
|
133
|
-
|
|
144
|
+
it "generate secret on create" do
|
|
145
|
+
expect(new_application.secret).to be_nil
|
|
146
|
+
new_application.save
|
|
147
|
+
expect(new_application.secret).not_to be_nil
|
|
148
|
+
end
|
|
134
149
|
|
|
135
|
-
it "
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
)
|
|
150
|
+
it "generate secret on create if is blank string" do
|
|
151
|
+
new_application.secret = ""
|
|
152
|
+
new_application.save
|
|
153
|
+
expect(new_application.secret).not_to be_blank
|
|
154
|
+
end
|
|
141
155
|
|
|
142
|
-
|
|
156
|
+
it "generate secret on create unless one is set" do
|
|
157
|
+
new_application.secret = secret
|
|
158
|
+
new_application.save
|
|
159
|
+
expect(new_application.secret).to eq(secret)
|
|
143
160
|
end
|
|
144
161
|
|
|
145
|
-
it "
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
expect
|
|
149
|
-
new_application.destroy
|
|
150
|
-
end.to change { Doorkeeper::AccessToken.count }.by(-2)
|
|
162
|
+
it "is invalid without secret" do
|
|
163
|
+
new_application.save
|
|
164
|
+
new_application.secret = nil
|
|
165
|
+
expect(new_application).not_to be_valid
|
|
151
166
|
end
|
|
152
|
-
end
|
|
153
167
|
|
|
154
|
-
|
|
155
|
-
|
|
168
|
+
context "with hashing enabled" do
|
|
169
|
+
include_context "with application hashing enabled"
|
|
170
|
+
let(:app) { FactoryBot.create :application }
|
|
171
|
+
let(:default_strategy) { Doorkeeper::SecretStoring::Sha256Hash }
|
|
156
172
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
expect(
|
|
173
|
+
it "uses SHA256 to avoid additional dependencies" do
|
|
174
|
+
# Ensure token was generated
|
|
175
|
+
app.validate
|
|
176
|
+
expect(app.secret).to eq(default_strategy.transform_secret(app.plaintext_secret))
|
|
161
177
|
end
|
|
162
|
-
end
|
|
163
178
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
179
|
+
context "when bcrypt strategy is configured" do
|
|
180
|
+
# In this text context, we have bcrypt loaded so `bcrypt_present?`
|
|
181
|
+
# will always be true
|
|
182
|
+
before do
|
|
183
|
+
Doorkeeper.configure do
|
|
184
|
+
hash_application_secrets using: "Doorkeeper::SecretStoring::BCrypt"
|
|
185
|
+
end
|
|
186
|
+
end
|
|
171
187
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
end
|
|
179
|
-
context "when string of valid redirect_uris" do
|
|
180
|
-
it "should store as-is" do
|
|
181
|
-
new_application.redirect_uri = "http://localhost/callback1\nhttp://localhost/callback2"
|
|
182
|
-
expect(new_application.redirect_uri).to eq("http://localhost/callback1\nhttp://localhost/callback2")
|
|
188
|
+
it "holds a volatile plaintext and BCrypt secret" do
|
|
189
|
+
expect(app.secret_strategy).to eq Doorkeeper::SecretStoring::BCrypt
|
|
190
|
+
expect(app.plaintext_secret).to be_a(String)
|
|
191
|
+
expect(app.secret).not_to eq(app.plaintext_secret)
|
|
192
|
+
expect { ::BCrypt::Password.create(app.secret) }.not_to raise_error
|
|
193
|
+
end
|
|
183
194
|
end
|
|
184
|
-
end
|
|
185
|
-
end
|
|
186
195
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
196
|
+
it "does not fallback to plain lookup by default" do
|
|
197
|
+
lookup = clazz.by_uid_and_secret(app.uid, app.secret)
|
|
198
|
+
expect(lookup).to eq(nil)
|
|
190
199
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
200
|
+
lookup = clazz.by_uid_and_secret(app.uid, app.plaintext_secret)
|
|
201
|
+
expect(lookup).to eq(app)
|
|
202
|
+
end
|
|
194
203
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
:access_token,
|
|
198
|
-
resource_owner_id: other_resource_owner.id,
|
|
199
|
-
)
|
|
200
|
-
token = FactoryBot.create(
|
|
201
|
-
:access_token,
|
|
202
|
-
resource_owner_id: resource_owner.id,
|
|
203
|
-
)
|
|
204
|
-
expect(described_class.authorized_for(resource_owner)).to eq([token.application])
|
|
205
|
-
end
|
|
204
|
+
context "with fallback enabled" do
|
|
205
|
+
include_context "with token hashing and fallback lookup enabled"
|
|
206
206
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
resource_owner_id: resource_owner.id,
|
|
211
|
-
revoked_at: 2.days.ago,
|
|
212
|
-
)
|
|
213
|
-
expect(described_class.authorized_for(resource_owner)).to be_empty
|
|
214
|
-
end
|
|
207
|
+
it "provides plain and hashed lookup" do
|
|
208
|
+
lookup = clazz.by_uid_and_secret(app.uid, app.secret)
|
|
209
|
+
expect(lookup).to eq(app)
|
|
215
210
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
)
|
|
221
|
-
token2 = FactoryBot.create(
|
|
222
|
-
:access_token,
|
|
223
|
-
resource_owner_id: resource_owner.id,
|
|
224
|
-
)
|
|
225
|
-
expect(described_class.authorized_for(resource_owner))
|
|
226
|
-
.to eq([token1.application, token2.application])
|
|
227
|
-
end
|
|
211
|
+
lookup = clazz.by_uid_and_secret(app.uid, app.plaintext_secret)
|
|
212
|
+
expect(lookup).to eq(app)
|
|
213
|
+
end
|
|
214
|
+
end
|
|
228
215
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
resource_owner_id: resource_owner.id,
|
|
234
|
-
application: application,
|
|
235
|
-
)
|
|
236
|
-
FactoryBot.create(
|
|
237
|
-
:access_token,
|
|
238
|
-
resource_owner_id: resource_owner.id,
|
|
239
|
-
application: application,
|
|
240
|
-
)
|
|
241
|
-
expect(described_class.authorized_for(resource_owner)).to eq([application])
|
|
216
|
+
it "does not provide access to secret after loading" do
|
|
217
|
+
lookup = clazz.by_uid_and_secret(app.uid, app.plaintext_secret)
|
|
218
|
+
expect(lookup.plaintext_secret).to be_nil
|
|
219
|
+
end
|
|
242
220
|
end
|
|
243
|
-
end
|
|
244
221
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
.to
|
|
222
|
+
describe "destroy related models on cascade" do
|
|
223
|
+
before(:each) do
|
|
224
|
+
new_application.save
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
it "should destroy its access grants" do
|
|
228
|
+
FactoryBot.create(:access_grant, application: new_application)
|
|
229
|
+
expect { new_application.destroy }.to change { Doorkeeper::AccessGrant.count }.by(-1)
|
|
230
|
+
end
|
|
253
231
|
|
|
254
|
-
|
|
232
|
+
it "should destroy its access tokens" do
|
|
233
|
+
FactoryBot.create(:access_token, application: new_application)
|
|
234
|
+
FactoryBot.create(:access_token, application: new_application, revoked_at: Time.now.utc)
|
|
235
|
+
expect do
|
|
236
|
+
new_application.destroy
|
|
237
|
+
end.to change { Doorkeeper::AccessToken.count }.by(-2)
|
|
238
|
+
end
|
|
255
239
|
end
|
|
256
|
-
end
|
|
257
240
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
241
|
+
describe :ordered_by do
|
|
242
|
+
let(:applications) { FactoryBot.create_list(:application, 5) }
|
|
243
|
+
|
|
244
|
+
context "when a direction is not specified" do
|
|
245
|
+
it "calls order with a default order of asc" do
|
|
246
|
+
names = applications.map(&:name).sort
|
|
247
|
+
expect(Application.ordered_by(:name).map(&:name)).to eq(names)
|
|
248
|
+
end
|
|
264
249
|
end
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
expect(
|
|
250
|
+
|
|
251
|
+
context "when a direction is specified" do
|
|
252
|
+
it "calls order with specified direction" do
|
|
253
|
+
names = applications.map(&:name).sort.reverse
|
|
254
|
+
expect(Application.ordered_by(:name, :desc).map(&:name)).to eq(names)
|
|
270
255
|
end
|
|
271
256
|
end
|
|
272
257
|
end
|
|
273
258
|
|
|
274
|
-
|
|
275
|
-
context "when
|
|
276
|
-
it "should
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
expect(authenticated).to eq(app)
|
|
259
|
+
describe "#redirect_uri=" do
|
|
260
|
+
context "when array of valid redirect_uris" do
|
|
261
|
+
it "should join by newline" do
|
|
262
|
+
new_application.redirect_uri = ["http://localhost/callback1", "http://localhost/callback2"]
|
|
263
|
+
expect(new_application.redirect_uri).to eq("http://localhost/callback1\nhttp://localhost/callback2")
|
|
280
264
|
end
|
|
281
265
|
end
|
|
282
|
-
context "when
|
|
283
|
-
it "should
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
expect(authenticated).to eq(nil)
|
|
266
|
+
context "when string of valid redirect_uris" do
|
|
267
|
+
it "should store as-is" do
|
|
268
|
+
new_application.redirect_uri = "http://localhost/callback1\nhttp://localhost/callback2"
|
|
269
|
+
expect(new_application.redirect_uri).to eq("http://localhost/callback1\nhttp://localhost/callback2")
|
|
287
270
|
end
|
|
288
271
|
end
|
|
289
272
|
end
|
|
290
|
-
end
|
|
291
273
|
|
|
292
|
-
|
|
293
|
-
|
|
274
|
+
describe :authorized_for do
|
|
275
|
+
let(:resource_owner) { double(:resource_owner, id: 10) }
|
|
294
276
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
end
|
|
299
|
-
|
|
300
|
-
context "when application is public/non-confidential" do
|
|
301
|
-
let(:confidential) { false }
|
|
302
|
-
it { expect(subject).to eq(false) }
|
|
303
|
-
end
|
|
304
|
-
end
|
|
305
|
-
|
|
306
|
-
describe "#as_json" do
|
|
307
|
-
let(:app) { FactoryBot.create :application, secret: "123123123" }
|
|
277
|
+
it "is empty if the application is not authorized for anyone" do
|
|
278
|
+
expect(Application.authorized_for(resource_owner)).to be_empty
|
|
279
|
+
end
|
|
308
280
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
expect(app.to_json(include_root_in_json: true)).to match(/application.+?:\{/)
|
|
314
|
-
ActiveRecord::Base.include_root_in_json = false
|
|
281
|
+
it "returns only application for a specific resource owner" do
|
|
282
|
+
FactoryBot.create(:access_token, resource_owner_id: resource_owner.id + 1)
|
|
283
|
+
token = FactoryBot.create(:access_token, resource_owner_id: resource_owner.id)
|
|
284
|
+
expect(Application.authorized_for(resource_owner)).to eq([token.application])
|
|
315
285
|
end
|
|
316
|
-
end
|
|
317
286
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
expect(
|
|
321
|
-
"id" => app.id,
|
|
322
|
-
"name" => app.name,
|
|
323
|
-
"created_at" => anything,
|
|
324
|
-
)
|
|
287
|
+
it "excludes revoked tokens" do
|
|
288
|
+
FactoryBot.create(:access_token, resource_owner_id: resource_owner.id, revoked_at: 2.days.ago)
|
|
289
|
+
expect(Application.authorized_for(resource_owner)).to be_empty
|
|
325
290
|
end
|
|
326
291
|
|
|
327
|
-
it "
|
|
328
|
-
|
|
292
|
+
it "returns all applications that have been authorized" do
|
|
293
|
+
token1 = FactoryBot.create(:access_token, resource_owner_id: resource_owner.id)
|
|
294
|
+
token2 = FactoryBot.create(:access_token, resource_owner_id: resource_owner.id)
|
|
295
|
+
expect(Application.authorized_for(resource_owner)).to eq([token1.application, token2.application])
|
|
296
|
+
end
|
|
329
297
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
)
|
|
298
|
+
it "returns only one application even if it has been authorized twice" do
|
|
299
|
+
application = FactoryBot.create(:application)
|
|
300
|
+
FactoryBot.create(:access_token, resource_owner_id: resource_owner.id, application: application)
|
|
301
|
+
FactoryBot.create(:access_token, resource_owner_id: resource_owner.id, application: application)
|
|
302
|
+
expect(Application.authorized_for(resource_owner)).to eq([application])
|
|
336
303
|
end
|
|
304
|
+
end
|
|
337
305
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
306
|
+
describe :revoke_tokens_and_grants_for do
|
|
307
|
+
it "revokes all access tokens and access grants" do
|
|
308
|
+
application_id = 42
|
|
309
|
+
resource_owner = double
|
|
310
|
+
expect(Doorkeeper::AccessToken)
|
|
311
|
+
.to receive(:revoke_all_for).with(application_id, resource_owner)
|
|
312
|
+
expect(Doorkeeper::AccessGrant)
|
|
313
|
+
.to receive(:revoke_all_for).with(application_id, resource_owner)
|
|
314
|
+
|
|
315
|
+
Application.revoke_tokens_and_grants_for(application_id, resource_owner)
|
|
345
316
|
end
|
|
346
317
|
end
|
|
347
318
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
319
|
+
describe :by_uid_and_secret do
|
|
320
|
+
context "when application is private/confidential" do
|
|
321
|
+
it "finds the application via uid/secret" do
|
|
322
|
+
app = FactoryBot.create :application
|
|
323
|
+
authenticated = Application.by_uid_and_secret(app.uid, app.secret)
|
|
324
|
+
expect(authenticated).to eq(app)
|
|
325
|
+
end
|
|
326
|
+
context "when secret is wrong" do
|
|
327
|
+
it "should not find the application" do
|
|
328
|
+
app = FactoryBot.create :application
|
|
329
|
+
authenticated = Application.by_uid_and_secret(app.uid, "bad")
|
|
330
|
+
expect(authenticated).to eq(nil)
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
end
|
|
352
334
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
335
|
+
context "when application is public/non-confidential" do
|
|
336
|
+
context "when secret is blank" do
|
|
337
|
+
it "should find the application" do
|
|
338
|
+
app = FactoryBot.create :application, confidential: false
|
|
339
|
+
authenticated = Application.by_uid_and_secret(app.uid, nil)
|
|
340
|
+
expect(authenticated).to eq(app)
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
context "when secret is wrong" do
|
|
344
|
+
it "should not find the application" do
|
|
345
|
+
app = FactoryBot.create :application, confidential: false
|
|
346
|
+
authenticated = Application.by_uid_and_secret(app.uid, "bad")
|
|
347
|
+
expect(authenticated).to eq(nil)
|
|
348
|
+
end
|
|
357
349
|
end
|
|
358
350
|
end
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
describe :confidential? do
|
|
354
|
+
subject { FactoryBot.create(:application, confidential: confidential).confidential? }
|
|
359
355
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
"secret" => "123123123",
|
|
364
|
-
"redirect_uri" => app.redirect_uri,
|
|
365
|
-
"uid" => app.uid,
|
|
366
|
-
)
|
|
356
|
+
context "when application is private/confidential" do
|
|
357
|
+
let(:confidential) { true }
|
|
358
|
+
it { expect(subject).to eq(true) }
|
|
367
359
|
end
|
|
368
360
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
361
|
+
context "when application is public/non-confidential" do
|
|
362
|
+
let(:confidential) { false }
|
|
363
|
+
it { expect(subject).to eq(false) }
|
|
372
364
|
end
|
|
373
365
|
end
|
|
374
366
|
end
|