doorkeeper 5.2.5 → 5.3.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/Appraisals +2 -2
- data/CHANGELOG.md +16 -11
- 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/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/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/orm/active_record.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/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/request.rb +1 -1
- 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/doorkeeper.rb +2 -3
- 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
|
@@ -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
|