doorkeeper 5.2.6 → 5.3.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of doorkeeper might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Appraisals +2 -2
- data/CHANGELOG.md +15 -14
- data/Gemfile +2 -2
- data/app/controllers/doorkeeper/application_controller.rb +2 -2
- data/app/controllers/doorkeeper/application_metal_controller.rb +2 -2
- data/app/controllers/doorkeeper/applications_controller.rb +3 -3
- data/app/controllers/doorkeeper/authorizations_controller.rb +2 -2
- data/app/controllers/doorkeeper/authorized_applications_controller.rb +3 -3
- data/gemfiles/rails_5_0.gemfile +2 -2
- data/gemfiles/rails_5_1.gemfile +2 -2
- data/gemfiles/rails_5_2.gemfile +2 -2
- data/gemfiles/rails_6_0.gemfile +2 -2
- data/gemfiles/rails_master.gemfile +2 -2
- data/lib/doorkeeper.rb +2 -3
- data/lib/doorkeeper/config.rb +71 -39
- data/lib/doorkeeper/grape/helpers.rb +1 -1
- data/lib/doorkeeper/helpers/controller.rb +10 -8
- data/lib/doorkeeper/models/access_grant_mixin.rb +7 -6
- data/lib/doorkeeper/models/access_token_mixin.rb +55 -18
- data/lib/doorkeeper/models/application_mixin.rb +3 -3
- data/lib/doorkeeper/models/concerns/ownership.rb +1 -1
- data/lib/doorkeeper/models/concerns/reusable.rb +1 -1
- data/lib/doorkeeper/models/concerns/revocable.rb +0 -27
- data/lib/doorkeeper/oauth/authorization/code.rb +4 -4
- data/lib/doorkeeper/oauth/authorization/token.rb +9 -6
- data/lib/doorkeeper/oauth/authorization_code_request.rb +13 -6
- data/lib/doorkeeper/oauth/base_request.rb +8 -4
- data/lib/doorkeeper/oauth/client.rb +7 -8
- data/lib/doorkeeper/oauth/client_credentials/creator.rb +16 -9
- data/lib/doorkeeper/oauth/client_credentials/issuer.rb +7 -7
- data/lib/doorkeeper/oauth/client_credentials/{validation.rb → validator.rb} +4 -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.rb +1 -1
- data/lib/doorkeeper/oauth/error_response.rb +5 -5
- data/lib/doorkeeper/oauth/helpers/scope_checker.rb +7 -5
- data/lib/doorkeeper/oauth/helpers/unique_token.rb +8 -5
- data/lib/doorkeeper/oauth/helpers/uri_checker.rb +1 -1
- data/lib/doorkeeper/oauth/invalid_request_response.rb +3 -3
- data/lib/doorkeeper/oauth/invalid_token_response.rb +5 -2
- data/lib/doorkeeper/oauth/password_access_token_request.rb +3 -3
- data/lib/doorkeeper/oauth/pre_authorization.rb +7 -5
- data/lib/doorkeeper/oauth/refresh_token_request.rb +5 -5
- data/lib/doorkeeper/oauth/token.rb +2 -2
- data/lib/doorkeeper/oauth/token_introspection.rb +6 -6
- data/lib/doorkeeper/orm/active_record.rb +3 -3
- data/lib/doorkeeper/orm/active_record/access_grant.rb +4 -43
- data/lib/doorkeeper/orm/active_record/access_token.rb +4 -35
- data/lib/doorkeeper/orm/active_record/application.rb +3 -155
- data/lib/doorkeeper/orm/active_record/mixins/access_grant.rb +53 -0
- data/lib/doorkeeper/orm/active_record/mixins/access_token.rb +47 -0
- data/lib/doorkeeper/orm/active_record/mixins/application.rb +128 -0
- data/lib/doorkeeper/orm/active_record/redirect_uri_validator.rb +3 -3
- data/lib/doorkeeper/rails/helpers.rb +4 -4
- data/lib/doorkeeper/rails/routes.rb +5 -7
- data/lib/doorkeeper/rake/db.rake +3 -3
- data/lib/doorkeeper/request.rb +1 -1
- data/lib/doorkeeper/request/authorization_code.rb +3 -3
- data/lib/doorkeeper/request/client_credentials.rb +2 -2
- data/lib/doorkeeper/request/password.rb +2 -2
- data/lib/doorkeeper/request/refresh_token.rb +3 -3
- data/lib/doorkeeper/server.rb +1 -1
- data/lib/doorkeeper/stale_records_cleaner.rb +1 -1
- data/lib/doorkeeper/version.rb +2 -2
- data/lib/generators/doorkeeper/application_owner_generator.rb +1 -1
- data/lib/generators/doorkeeper/confidential_applications_generator.rb +1 -1
- data/lib/generators/doorkeeper/migration_generator.rb +1 -1
- data/lib/generators/doorkeeper/pkce_generator.rb +1 -1
- data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +2 -2
- data/lib/generators/doorkeeper/templates/initializer.rb +39 -8
- data/spec/controllers/application_metal_controller_spec.rb +1 -1
- data/spec/controllers/applications_controller_spec.rb +3 -2
- data/spec/controllers/authorizations_controller_spec.rb +18 -18
- data/spec/controllers/protected_resources_controller_spec.rb +25 -17
- data/spec/controllers/token_info_controller_spec.rb +1 -1
- data/spec/controllers/tokens_controller_spec.rb +1 -1
- data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +3 -3
- data/spec/dummy/db/migrate/20160320211015_add_previous_refresh_token_to_access_tokens.rb +1 -1
- data/spec/dummy/db/migrate/20180210183654_add_confidential_to_applications.rb +1 -1
- data/spec/generators/install_generator_spec.rb +1 -1
- data/spec/generators/previous_refresh_token_generator_spec.rb +2 -2
- data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +1 -1
- data/spec/lib/config_spec.rb +61 -21
- data/spec/lib/doorkeeper_spec.rb +1 -1
- data/spec/lib/models/revocable_spec.rb +3 -3
- data/spec/lib/oauth/authorization_code_request_spec.rb +127 -125
- data/spec/lib/oauth/base_request_spec.rb +160 -158
- data/spec/lib/oauth/base_response_spec.rb +27 -29
- data/spec/lib/oauth/client/credentials_spec.rb +1 -1
- data/spec/lib/oauth/client_credentials/creator_spec.rb +42 -5
- data/spec/lib/oauth/client_credentials/issuer_spec.rb +12 -12
- data/spec/lib/oauth/client_credentials/validation_spec.rb +4 -4
- data/spec/lib/oauth/client_credentials_integration_spec.rb +16 -18
- data/spec/lib/oauth/client_credentials_request_spec.rb +78 -80
- data/spec/lib/oauth/client_spec.rb +26 -26
- data/spec/lib/oauth/code_request_spec.rb +34 -34
- data/spec/lib/oauth/code_response_spec.rb +21 -25
- data/spec/lib/oauth/error_response_spec.rb +42 -44
- data/spec/lib/oauth/error_spec.rb +12 -14
- data/spec/lib/oauth/forbidden_token_response_spec.rb +11 -13
- data/spec/lib/oauth/helpers/scope_checker_spec.rb +30 -18
- data/spec/lib/oauth/invalid_request_response_spec.rb +48 -50
- data/spec/lib/oauth/invalid_token_response_spec.rb +32 -34
- data/spec/lib/oauth/password_access_token_request_spec.rb +145 -147
- data/spec/lib/oauth/pre_authorization_spec.rb +159 -161
- data/spec/lib/oauth/refresh_token_request_spec.rb +138 -139
- data/spec/lib/oauth/scopes_spec.rb +104 -106
- data/spec/lib/oauth/token_request_spec.rb +115 -111
- data/spec/lib/oauth/token_response_spec.rb +71 -73
- data/spec/lib/oauth/token_spec.rb +121 -123
- data/spec/models/doorkeeper/access_grant_spec.rb +3 -5
- data/spec/models/doorkeeper/access_token_spec.rb +7 -7
- data/spec/models/doorkeeper/application_spec.rb +295 -373
- data/spec/requests/applications/applications_request_spec.rb +1 -1
- data/spec/requests/endpoints/authorization_spec.rb +5 -3
- data/spec/requests/flows/authorization_code_spec.rb +34 -22
- data/spec/requests/flows/client_credentials_spec.rb +1 -1
- data/spec/requests/flows/password_spec.rb +32 -12
- data/spec/requests/flows/refresh_token_spec.rb +19 -19
- data/spec/requests/flows/revoke_token_spec.rb +18 -12
- data/spec/spec_helper.rb +1 -4
- data/spec/support/shared/controllers_shared_context.rb +33 -23
- data/spec/validators/redirect_uri_validator_spec.rb +1 -1
- metadata +6 -5
- data/spec/support/http_method_shim.rb +0 -29
@@ -81,9 +81,7 @@ describe Doorkeeper::AccessGrant do
|
|
81
81
|
)
|
82
82
|
|
83
83
|
# Not all the ORM support :id PK
|
84
|
-
if grant.respond_to?(:id)
|
85
|
-
expect(clazz.by_token(plain_text_token).id).to eq(grant.id)
|
86
|
-
end
|
84
|
+
expect(clazz.by_token(plain_text_token).id).to eq(grant.id) if grant.respond_to?(:id)
|
87
85
|
|
88
86
|
# And it modifies the token value
|
89
87
|
grant.reload
|
@@ -141,7 +139,7 @@ describe Doorkeeper::AccessGrant do
|
|
141
139
|
it "matches application" do
|
142
140
|
access_grant_for_different_app = FactoryBot.create(
|
143
141
|
:access_grant,
|
144
|
-
default_attributes.merge(application: FactoryBot.create(:application))
|
142
|
+
default_attributes.merge(application: FactoryBot.create(:application)),
|
145
143
|
)
|
146
144
|
|
147
145
|
described_class.revoke_all_for(application.id, resource_owner)
|
@@ -152,7 +150,7 @@ describe Doorkeeper::AccessGrant do
|
|
152
150
|
it "matches resource owner" do
|
153
151
|
access_grant_for_different_owner = FactoryBot.create(
|
154
152
|
:access_grant,
|
155
|
-
default_attributes.merge(resource_owner_id: 90)
|
153
|
+
default_attributes.merge(resource_owner_id: 90),
|
156
154
|
)
|
157
155
|
|
158
156
|
described_class.revoke_all_for application.id, resource_owner
|
@@ -212,7 +212,7 @@ module Doorkeeper
|
|
212
212
|
end
|
213
213
|
|
214
214
|
expect { FactoryBot.create :access_token }.to(
|
215
|
-
raise_error(Doorkeeper::Errors::UnableToGenerateToken)
|
215
|
+
raise_error(Doorkeeper::Errors::UnableToGenerateToken),
|
216
216
|
)
|
217
217
|
end
|
218
218
|
|
@@ -234,7 +234,7 @@ module Doorkeeper
|
|
234
234
|
end
|
235
235
|
|
236
236
|
expect { FactoryBot.create :access_token }.to(
|
237
|
-
raise_error(LoadError)
|
237
|
+
raise_error(LoadError),
|
238
238
|
)
|
239
239
|
end
|
240
240
|
|
@@ -245,7 +245,7 @@ module Doorkeeper
|
|
245
245
|
end
|
246
246
|
|
247
247
|
expect { FactoryBot.create :access_token }.to(
|
248
|
-
raise_error(Doorkeeper::Errors::TokenGeneratorNotFound, /NotReal/)
|
248
|
+
raise_error(Doorkeeper::Errors::TokenGeneratorNotFound, /NotReal/),
|
249
249
|
)
|
250
250
|
end
|
251
251
|
end
|
@@ -468,7 +468,7 @@ module Doorkeeper
|
|
468
468
|
it "matches application" do
|
469
469
|
access_token_for_different_app = FactoryBot.create(
|
470
470
|
:access_token,
|
471
|
-
default_attributes.merge(application: FactoryBot.create(:application))
|
471
|
+
default_attributes.merge(application: FactoryBot.create(:application)),
|
472
472
|
)
|
473
473
|
|
474
474
|
AccessToken.revoke_all_for application.id, resource_owner
|
@@ -479,7 +479,7 @@ module Doorkeeper
|
|
479
479
|
it "matches resource owner" do
|
480
480
|
access_token_for_different_owner = FactoryBot.create(
|
481
481
|
:access_token,
|
482
|
-
default_attributes.merge(resource_owner_id: 90)
|
482
|
+
default_attributes.merge(resource_owner_id: 90),
|
483
483
|
)
|
484
484
|
|
485
485
|
AccessToken.revoke_all_for application.id, resource_owner
|
@@ -561,7 +561,7 @@ module Doorkeeper
|
|
561
561
|
|
562
562
|
it "excludes tokens with scopes that are not present in server scopes" do
|
563
563
|
FactoryBot.create :access_token, default_attributes.merge(
|
564
|
-
application: application, scopes: "public read"
|
564
|
+
application: application, scopes: "public read",
|
565
565
|
)
|
566
566
|
last_token = AccessToken.matching_token_for(application, resource_owner_id, scopes)
|
567
567
|
expect(last_token).to be_nil
|
@@ -570,7 +570,7 @@ module Doorkeeper
|
|
570
570
|
it "excludes tokens with scopes that are not present in application scopes" do
|
571
571
|
application = FactoryBot.create :application, scopes: "private read"
|
572
572
|
FactoryBot.create :access_token, default_attributes.merge(
|
573
|
-
application: application
|
573
|
+
application: application,
|
574
574
|
)
|
575
575
|
last_token = AccessToken.matching_token_for(application, resource_owner_id, scopes)
|
576
576
|
expect(last_token).to be_nil
|
@@ -3,480 +3,402 @@
|
|
3
3
|
require "spec_helper"
|
4
4
|
require "bcrypt"
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
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) }
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
+
let(:uid) { SecureRandom.hex(8) }
|
14
|
+
let(:secret) { SecureRandom.hex(8) }
|
13
15
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
it "is invalid without determining confidentiality" do
|
20
|
-
new_application.confidential = nil
|
21
|
-
expect(new_application).not_to be_valid
|
22
|
-
end
|
23
|
-
|
24
|
-
it "generates uid on create" do
|
25
|
-
expect(new_application.uid).to be_nil
|
26
|
-
new_application.save
|
27
|
-
expect(new_application.uid).not_to be_nil
|
28
|
-
end
|
29
|
-
|
30
|
-
it "generates uid on create if an empty string" do
|
31
|
-
new_application.uid = ""
|
32
|
-
new_application.save
|
33
|
-
expect(new_application.uid).not_to be_blank
|
34
|
-
end
|
16
|
+
it "is invalid without a name" do
|
17
|
+
new_application.name = nil
|
18
|
+
expect(new_application).not_to be_valid
|
19
|
+
end
|
35
20
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
end
|
21
|
+
it "is invalid without determining confidentiality" do
|
22
|
+
new_application.confidential = nil
|
23
|
+
expect(new_application).not_to be_valid
|
24
|
+
end
|
41
25
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
26
|
+
it "generates uid on create" do
|
27
|
+
expect(new_application.uid).to be_nil
|
28
|
+
new_application.save
|
29
|
+
expect(new_application.uid).not_to be_nil
|
30
|
+
end
|
47
31
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
end
|
32
|
+
it "generates uid on create if an empty string" do
|
33
|
+
new_application.uid = ""
|
34
|
+
new_application.save
|
35
|
+
expect(new_application.uid).not_to be_blank
|
36
|
+
end
|
54
37
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
end
|
38
|
+
it "generates uid on create unless one is set" do
|
39
|
+
new_application.uid = uid
|
40
|
+
new_application.save
|
41
|
+
expect(new_application.uid).to eq(uid)
|
42
|
+
end
|
61
43
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
44
|
+
it "is invalid without uid" do
|
45
|
+
new_application.save
|
46
|
+
new_application.uid = nil
|
47
|
+
expect(new_application).not_to be_valid
|
48
|
+
end
|
67
49
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
50
|
+
it "checks uniqueness of uid" do
|
51
|
+
app1 = FactoryBot.create(:application)
|
52
|
+
app2 = FactoryBot.create(:application)
|
53
|
+
app2.uid = app1.uid
|
54
|
+
expect(app2).not_to be_valid
|
55
|
+
end
|
73
56
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
57
|
+
it "expects database to throw an error when uids are the same" do
|
58
|
+
app1 = FactoryBot.create(:application)
|
59
|
+
app2 = FactoryBot.create(:application)
|
60
|
+
app2.uid = app1.uid
|
61
|
+
expect { app2.save!(validate: false) }.to raise_error(uniqueness_error)
|
62
|
+
end
|
79
63
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
64
|
+
it "generate secret on create" do
|
65
|
+
expect(new_application.secret).to be_nil
|
66
|
+
new_application.save
|
67
|
+
expect(new_application.secret).not_to be_nil
|
68
|
+
end
|
85
69
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
enable_application_owner
|
91
|
-
end
|
70
|
+
it "generate secret on create if is blank string" do
|
71
|
+
new_application.secret = ""
|
72
|
+
new_application.save
|
73
|
+
expect(new_application.secret).not_to be_blank
|
92
74
|
end
|
93
75
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
76
|
+
it "generate secret on create unless one is set" do
|
77
|
+
new_application.secret = secret
|
78
|
+
new_application.save
|
79
|
+
expect(new_application.secret).to eq(secret)
|
80
|
+
end
|
98
81
|
|
99
|
-
|
100
|
-
|
101
|
-
|
82
|
+
it "is invalid without secret" do
|
83
|
+
new_application.save
|
84
|
+
new_application.secret = nil
|
85
|
+
expect(new_application).not_to be_valid
|
102
86
|
end
|
103
87
|
|
104
|
-
context "
|
88
|
+
context "application_owner is enabled" do
|
105
89
|
before do
|
106
|
-
|
107
|
-
|
90
|
+
Doorkeeper.configure do
|
91
|
+
orm DOORKEEPER_ORM
|
92
|
+
enable_application_owner
|
93
|
+
end
|
108
94
|
end
|
109
95
|
|
110
|
-
|
111
|
-
|
112
|
-
|
96
|
+
context "application owner is not required" do
|
97
|
+
before(:each) do
|
98
|
+
unset_require_owner
|
99
|
+
end
|
113
100
|
|
114
|
-
|
115
|
-
|
116
|
-
|
101
|
+
it "is valid given valid attributes" do
|
102
|
+
expect(new_application).to be_valid
|
103
|
+
end
|
117
104
|
end
|
118
|
-
end
|
119
|
-
end
|
120
105
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
grant_flows %w[password client_credentials]
|
106
|
+
context "application owner is required" do
|
107
|
+
before(:each) do
|
108
|
+
require_owner
|
109
|
+
@owner = FactoryBot.build_stubbed(:doorkeeper_testing_user)
|
126
110
|
end
|
127
|
-
end
|
128
111
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
112
|
+
it "is invalid without an owner" do
|
113
|
+
expect(new_application).not_to be_valid
|
114
|
+
end
|
115
|
+
|
116
|
+
it "is valid with an owner" do
|
117
|
+
new_application.owner = @owner
|
118
|
+
expect(new_application).to be_valid
|
119
|
+
end
|
133
120
|
end
|
134
121
|
end
|
135
122
|
|
136
|
-
context "
|
137
|
-
|
138
|
-
|
139
|
-
|
123
|
+
context "redirect URI" do
|
124
|
+
context "when grant flows allow blank redirect URI" do
|
125
|
+
before do
|
126
|
+
Doorkeeper.configure do
|
127
|
+
grant_flows %w[password client_credentials]
|
128
|
+
end
|
140
129
|
end
|
141
|
-
end
|
142
130
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
131
|
+
it "is valid without redirect_uri" do
|
132
|
+
new_application.save
|
133
|
+
new_application.redirect_uri = nil
|
134
|
+
expect(new_application).to be_valid
|
135
|
+
end
|
147
136
|
end
|
148
|
-
end
|
149
137
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
138
|
+
context "when grant flows require redirect URI" do
|
139
|
+
before do
|
140
|
+
Doorkeeper.configure do
|
141
|
+
grant_flows %w[password client_credentials authorization_code]
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
it "is invalid without redirect_uri" do
|
146
|
+
new_application.save
|
147
|
+
new_application.redirect_uri = nil
|
148
|
+
expect(new_application).not_to be_valid
|
155
149
|
end
|
156
150
|
end
|
157
151
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
152
|
+
context "when blank URI option disabled" do
|
153
|
+
before do
|
154
|
+
Doorkeeper.configure do
|
155
|
+
grant_flows %w[password client_credentials]
|
156
|
+
allow_blank_redirect_uri false
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
it "is invalid without redirect_uri" do
|
161
|
+
new_application.save
|
162
|
+
new_application.redirect_uri = nil
|
163
|
+
expect(new_application).not_to be_valid
|
164
|
+
end
|
162
165
|
end
|
163
166
|
end
|
164
|
-
end
|
165
167
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
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 }
|
170
172
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
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))
|
177
|
+
end
|
176
178
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
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
|
183
186
|
end
|
184
|
-
end
|
185
187
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
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
|
191
194
|
end
|
192
|
-
end
|
193
195
|
|
194
|
-
|
195
|
-
|
196
|
-
|
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)
|
197
199
|
|
198
|
-
|
199
|
-
|
200
|
-
|
200
|
+
lookup = clazz.by_uid_and_secret(app.uid, app.plaintext_secret)
|
201
|
+
expect(lookup).to eq(app)
|
202
|
+
end
|
201
203
|
|
202
|
-
|
203
|
-
|
204
|
+
context "with fallback enabled" do
|
205
|
+
include_context "with token hashing and fallback lookup enabled"
|
204
206
|
|
205
|
-
|
206
|
-
|
207
|
-
|
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)
|
208
210
|
|
209
|
-
|
210
|
-
|
211
|
+
lookup = clazz.by_uid_and_secret(app.uid, app.plaintext_secret)
|
212
|
+
expect(lookup).to eq(app)
|
213
|
+
end
|
211
214
|
end
|
212
|
-
end
|
213
215
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
end
|
219
|
-
|
220
|
-
describe "destroy related models on cascade" do
|
221
|
-
before(:each) do
|
222
|
-
new_application.save
|
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
|
223
220
|
end
|
224
221
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
:access_grant,
|
230
|
-
application: new_application,
|
231
|
-
resource_owner_id: resource_owner.id,
|
232
|
-
)
|
222
|
+
describe "destroy related models on cascade" do
|
223
|
+
before(:each) do
|
224
|
+
new_application.save
|
225
|
+
end
|
233
226
|
|
234
|
-
|
235
|
-
|
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
|
236
231
|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
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
|
243
239
|
end
|
244
|
-
end
|
245
240
|
|
246
|
-
|
247
|
-
|
241
|
+
describe "#ordered_by" do
|
242
|
+
let(:applications) { FactoryBot.create_list(:application, 5) }
|
248
243
|
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
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
|
253
249
|
end
|
254
|
-
end
|
255
250
|
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
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)
|
255
|
+
end
|
260
256
|
end
|
261
257
|
end
|
262
|
-
end
|
263
258
|
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
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")
|
264
|
+
end
|
269
265
|
end
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
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")
|
270
|
+
end
|
275
271
|
end
|
276
272
|
end
|
277
|
-
end
|
278
273
|
|
279
|
-
|
280
|
-
|
274
|
+
describe "#renew_secret" do
|
275
|
+
let(:app) { FactoryBot.create :application }
|
281
276
|
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
277
|
+
it "should generate a new secret" do
|
278
|
+
old_secret = app.secret
|
279
|
+
app.renew_secret
|
280
|
+
expect(old_secret).not_to eq(app.secret)
|
281
|
+
end
|
286
282
|
end
|
287
|
-
end
|
288
283
|
|
289
|
-
|
290
|
-
|
291
|
-
let(:other_resource_owner) { FactoryBot.create(:doorkeeper_testing_user) }
|
284
|
+
describe "#authorized_for" do
|
285
|
+
let(:resource_owner) { double(:resource_owner, id: 10) }
|
292
286
|
|
293
|
-
|
294
|
-
|
295
|
-
|
287
|
+
it "is empty if the application is not authorized for anyone" do
|
288
|
+
expect(Application.authorized_for(resource_owner)).to be_empty
|
289
|
+
end
|
296
290
|
|
297
|
-
|
298
|
-
|
299
|
-
:access_token,
|
300
|
-
|
301
|
-
|
302
|
-
token = FactoryBot.create(
|
303
|
-
:access_token,
|
304
|
-
resource_owner_id: resource_owner.id,
|
305
|
-
)
|
306
|
-
expect(described_class.authorized_for(resource_owner)).to eq([token.application])
|
307
|
-
end
|
291
|
+
it "returns only application for a specific resource owner" do
|
292
|
+
FactoryBot.create(:access_token, resource_owner_id: resource_owner.id + 1)
|
293
|
+
token = FactoryBot.create(:access_token, resource_owner_id: resource_owner.id)
|
294
|
+
expect(Application.authorized_for(resource_owner)).to eq([token.application])
|
295
|
+
end
|
308
296
|
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
revoked_at: 2.days.ago,
|
314
|
-
)
|
315
|
-
expect(described_class.authorized_for(resource_owner)).to be_empty
|
316
|
-
end
|
297
|
+
it "excludes revoked tokens" do
|
298
|
+
FactoryBot.create(:access_token, resource_owner_id: resource_owner.id, revoked_at: 2.days.ago)
|
299
|
+
expect(Application.authorized_for(resource_owner)).to be_empty
|
300
|
+
end
|
317
301
|
|
318
|
-
|
319
|
-
|
320
|
-
:access_token,
|
321
|
-
|
322
|
-
|
323
|
-
token2 = FactoryBot.create(
|
324
|
-
:access_token,
|
325
|
-
resource_owner_id: resource_owner.id,
|
326
|
-
)
|
327
|
-
expect(described_class.authorized_for(resource_owner))
|
328
|
-
.to eq([token1.application, token2.application])
|
329
|
-
end
|
302
|
+
it "returns all applications that have been authorized" do
|
303
|
+
token1 = FactoryBot.create(:access_token, resource_owner_id: resource_owner.id)
|
304
|
+
token2 = FactoryBot.create(:access_token, resource_owner_id: resource_owner.id)
|
305
|
+
expect(Application.authorized_for(resource_owner)).to eq([token1.application, token2.application])
|
306
|
+
end
|
330
307
|
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
:access_token,
|
335
|
-
|
336
|
-
|
337
|
-
)
|
338
|
-
FactoryBot.create(
|
339
|
-
:access_token,
|
340
|
-
resource_owner_id: resource_owner.id,
|
341
|
-
application: application,
|
342
|
-
)
|
343
|
-
expect(described_class.authorized_for(resource_owner)).to eq([application])
|
308
|
+
it "returns only one application even if it has been authorized twice" do
|
309
|
+
application = FactoryBot.create(:application)
|
310
|
+
FactoryBot.create(:access_token, resource_owner_id: resource_owner.id, application: application)
|
311
|
+
FactoryBot.create(:access_token, resource_owner_id: resource_owner.id, application: application)
|
312
|
+
expect(Application.authorized_for(resource_owner)).to eq([application])
|
313
|
+
end
|
344
314
|
end
|
345
|
-
end
|
346
315
|
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
316
|
+
describe "#revoke_tokens_and_grants_for" do
|
317
|
+
it "revokes all access tokens and access grants" do
|
318
|
+
application_id = 42
|
319
|
+
resource_owner = double
|
320
|
+
expect(Doorkeeper::AccessToken)
|
321
|
+
.to receive(:revoke_all_for).with(application_id, resource_owner)
|
322
|
+
expect(Doorkeeper::AccessGrant)
|
323
|
+
.to receive(:revoke_all_for).with(application_id, resource_owner)
|
355
324
|
|
356
|
-
|
325
|
+
Application.revoke_tokens_and_grants_for(application_id, resource_owner)
|
326
|
+
end
|
357
327
|
end
|
358
|
-
end
|
359
328
|
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
app = FactoryBot.create :application
|
364
|
-
authenticated = described_class.by_uid_and_secret(app.uid, app.secret)
|
365
|
-
expect(authenticated).to eq(app)
|
366
|
-
end
|
367
|
-
context "when secret is wrong" do
|
368
|
-
it "should not find the application" do
|
329
|
+
describe "#by_uid_and_secret" do
|
330
|
+
context "when application is private/confidential" do
|
331
|
+
it "finds the application via uid/secret" do
|
369
332
|
app = FactoryBot.create :application
|
370
|
-
authenticated =
|
371
|
-
expect(authenticated).to eq(
|
333
|
+
authenticated = Application.by_uid_and_secret(app.uid, app.secret)
|
334
|
+
expect(authenticated).to eq(app)
|
335
|
+
end
|
336
|
+
context "when secret is wrong" do
|
337
|
+
it "should not find the application" do
|
338
|
+
app = FactoryBot.create :application
|
339
|
+
authenticated = Application.by_uid_and_secret(app.uid, "bad")
|
340
|
+
expect(authenticated).to eq(nil)
|
341
|
+
end
|
372
342
|
end
|
373
343
|
end
|
374
|
-
end
|
375
344
|
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
345
|
+
context "when application is public/non-confidential" do
|
346
|
+
context "when secret is blank" do
|
347
|
+
it "should find the application" do
|
348
|
+
app = FactoryBot.create :application, confidential: false
|
349
|
+
authenticated = Application.by_uid_and_secret(app.uid, nil)
|
350
|
+
expect(authenticated).to eq(app)
|
351
|
+
end
|
382
352
|
end
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
353
|
+
context "when secret is wrong" do
|
354
|
+
it "should not find the application" do
|
355
|
+
app = FactoryBot.create :application, confidential: false
|
356
|
+
authenticated = Application.by_uid_and_secret(app.uid, "bad")
|
357
|
+
expect(authenticated).to eq(nil)
|
358
|
+
end
|
389
359
|
end
|
390
360
|
end
|
391
361
|
end
|
392
|
-
end
|
393
|
-
|
394
|
-
describe "#confidential?" do
|
395
|
-
subject { FactoryBot.create(:application, confidential: confidential).confidential? }
|
396
362
|
|
397
|
-
|
398
|
-
|
399
|
-
it { expect(subject).to eq(true) }
|
400
|
-
end
|
363
|
+
describe "#confidential?" do
|
364
|
+
subject { FactoryBot.create(:application, confidential: confidential).confidential? }
|
401
365
|
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
end
|
407
|
-
|
408
|
-
describe "#as_json" do
|
409
|
-
let(:app) { FactoryBot.create :application, secret: "123123123" }
|
410
|
-
|
411
|
-
before do
|
412
|
-
allow(Doorkeeper.configuration)
|
413
|
-
.to receive(:application_secret_strategy).and_return(Doorkeeper::SecretStoring::Plain)
|
414
|
-
end
|
366
|
+
context "when application is private/confidential" do
|
367
|
+
let(:confidential) { true }
|
368
|
+
it { expect(subject).to eq(true) }
|
369
|
+
end
|
415
370
|
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
ActiveRecord::Base.include_root_in_json = true
|
420
|
-
expect(app.to_json(include_root_in_json: true)).to match(/application.+?:\{/)
|
421
|
-
ActiveRecord::Base.include_root_in_json = false
|
371
|
+
context "when application is public/non-confidential" do
|
372
|
+
let(:confidential) { false }
|
373
|
+
it { expect(subject).to eq(false) }
|
422
374
|
end
|
423
375
|
end
|
424
376
|
|
425
|
-
|
426
|
-
|
427
|
-
expect(app.as_json).to match(
|
428
|
-
"id" => app.id,
|
429
|
-
"name" => app.name,
|
430
|
-
"created_at" => an_instance_of(String),
|
431
|
-
)
|
432
|
-
end
|
377
|
+
describe "#as_json" do
|
378
|
+
let(:app) { FactoryBot.create :application, secret: "123123123" }
|
433
379
|
|
434
|
-
|
435
|
-
|
380
|
+
before do
|
381
|
+
allow(Doorkeeper.configuration)
|
382
|
+
.to receive(:application_secret_strategy).and_return(Doorkeeper::SecretStoring::Plain)
|
383
|
+
end
|
436
384
|
|
437
|
-
|
438
|
-
|
439
|
-
"name" => app.name,
|
440
|
-
"created_at" => an_instance_of(String),
|
441
|
-
"uid" => app.uid,
|
442
|
-
)
|
385
|
+
it "includes plaintext secret" do
|
386
|
+
expect(app.as_json).to include("secret" => "123123123")
|
443
387
|
end
|
444
388
|
|
445
389
|
it "respects custom options" do
|
446
|
-
expect(app.as_json(except: :
|
447
|
-
expect(app.as_json(only:
|
448
|
-
.to match(
|
449
|
-
"name" => app.name,
|
450
|
-
"created_at" => an_instance_of(String),
|
451
|
-
)
|
390
|
+
expect(app.as_json(except: :secret)).not_to include("secret")
|
391
|
+
expect(app.as_json(only: :id)).to match("id" => app.id)
|
452
392
|
end
|
453
|
-
end
|
454
393
|
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
Doorkeeper.configure do
|
462
|
-
orm DOORKEEPER_ORM
|
463
|
-
enable_application_owner confirmation: false
|
394
|
+
# AR specific
|
395
|
+
if DOORKEEPER_ORM == :active_record
|
396
|
+
it "correctly works with #to_json" do
|
397
|
+
ActiveRecord::Base.include_root_in_json = true
|
398
|
+
expect(app.to_json(include_root_in_json: true)).to match(/application.+?:\{/)
|
399
|
+
ActiveRecord::Base.include_root_in_json = false
|
464
400
|
end
|
465
401
|
end
|
466
|
-
|
467
|
-
it "includes all the attributes" do
|
468
|
-
expect(app.as_json(current_resource_owner: owner))
|
469
|
-
.to include(
|
470
|
-
"secret" => "123123123",
|
471
|
-
"redirect_uri" => app.redirect_uri,
|
472
|
-
"uid" => app.uid,
|
473
|
-
)
|
474
|
-
end
|
475
|
-
|
476
|
-
it "doesn't include unsafe attributes if current owner isn't the same as owner" do
|
477
|
-
expect(app.as_json(current_resource_owner: other_owner))
|
478
|
-
.not_to include("redirect_uri")
|
479
|
-
end
|
480
402
|
end
|
481
403
|
end
|
482
404
|
end
|