doorkeeper 5.3.0 → 5.4.0.rc1
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 +0 -14
- data/CHANGELOG.md +44 -1
- data/Dangerfile +7 -7
- data/Dockerfile +2 -2
- data/Gemfile +9 -9
- data/README.md +6 -4
- data/app/controllers/doorkeeper/applications_controller.rb +4 -4
- data/app/controllers/doorkeeper/authorizations_controller.rb +31 -12
- data/app/controllers/doorkeeper/authorized_applications_controller.rb +2 -2
- data/app/controllers/doorkeeper/tokens_controller.rb +57 -20
- data/app/views/doorkeeper/applications/show.html.erb +19 -2
- data/bin/console +14 -0
- data/config/locales/en.yml +3 -1
- data/doorkeeper.gemspec +1 -1
- data/gemfiles/rails_5_0.gemfile +8 -7
- data/gemfiles/rails_5_1.gemfile +8 -7
- data/gemfiles/rails_5_2.gemfile +8 -7
- data/gemfiles/rails_6_0.gemfile +8 -7
- data/gemfiles/rails_master.gemfile +8 -7
- data/lib/doorkeeper/config/abstract_builder.rb +28 -0
- data/lib/doorkeeper/config/option.rb +28 -14
- data/lib/doorkeeper/config.rb +41 -17
- data/lib/doorkeeper/grape/helpers.rb +1 -1
- data/lib/doorkeeper/models/access_grant_mixin.rb +9 -11
- data/lib/doorkeeper/models/access_token_mixin.rb +102 -42
- data/lib/doorkeeper/models/concerns/resource_ownerable.rb +47 -0
- data/lib/doorkeeper/models/concerns/revocable.rb +1 -1
- data/lib/doorkeeper/models/concerns/scopes.rb +5 -1
- data/lib/doorkeeper/models/concerns/secret_storable.rb +1 -3
- data/lib/doorkeeper/oauth/authorization/code.rb +14 -5
- data/lib/doorkeeper/oauth/authorization/context.rb +2 -2
- data/lib/doorkeeper/oauth/authorization/token.rb +7 -11
- data/lib/doorkeeper/oauth/authorization/uri_builder.rb +4 -4
- data/lib/doorkeeper/oauth/authorization_code_request.rb +18 -8
- data/lib/doorkeeper/oauth/base_request.rb +11 -19
- data/lib/doorkeeper/oauth/client/credentials.rb +2 -4
- data/lib/doorkeeper/oauth/client.rb +1 -1
- data/lib/doorkeeper/oauth/client_credentials/creator.rb +25 -7
- data/lib/doorkeeper/oauth/client_credentials/issuer.rb +3 -2
- data/lib/doorkeeper/oauth/client_credentials/validator.rb +1 -1
- data/lib/doorkeeper/oauth/client_credentials_request.rb +8 -7
- data/lib/doorkeeper/oauth/code_request.rb +1 -1
- data/lib/doorkeeper/oauth/code_response.rb +6 -2
- data/lib/doorkeeper/oauth/error_response.rb +2 -4
- data/lib/doorkeeper/oauth/helpers/scope_checker.rb +1 -5
- data/lib/doorkeeper/oauth/hooks/context.rb +21 -0
- data/lib/doorkeeper/oauth/invalid_token_response.rb +2 -2
- data/lib/doorkeeper/oauth/password_access_token_request.rb +3 -5
- data/lib/doorkeeper/oauth/pre_authorization.rb +32 -27
- data/lib/doorkeeper/oauth/refresh_token_request.rb +18 -22
- data/lib/doorkeeper/oauth/token.rb +1 -1
- data/lib/doorkeeper/oauth/token_introspection.rb +3 -3
- data/lib/doorkeeper/oauth/token_request.rb +2 -2
- data/lib/doorkeeper/oauth/token_response.rb +1 -1
- data/lib/doorkeeper/orm/active_record/mixins/access_grant.rb +7 -2
- data/lib/doorkeeper/orm/active_record/mixins/access_token.rb +6 -2
- data/lib/doorkeeper/orm/active_record/mixins/application.rb +6 -2
- data/lib/doorkeeper/rails/routes/abstract_router.rb +35 -0
- data/lib/doorkeeper/rails/routes/mapper.rb +2 -2
- data/lib/doorkeeper/rails/routes/registry.rb +45 -0
- data/lib/doorkeeper/rails/routes.rb +13 -17
- data/lib/doorkeeper/request/strategy.rb +2 -2
- data/lib/doorkeeper/server.rb +3 -3
- data/lib/doorkeeper/version.rb +2 -2
- data/lib/doorkeeper.rb +106 -79
- data/lib/generators/doorkeeper/confidential_applications_generator.rb +1 -1
- data/lib/generators/doorkeeper/enable_polymorphic_resource_owner_generator.rb +39 -0
- data/lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb.erb +2 -0
- data/lib/generators/doorkeeper/templates/add_previous_refresh_token_to_access_tokens.rb.erb +2 -0
- data/lib/generators/doorkeeper/templates/enable_pkce_migration.rb.erb +2 -0
- data/lib/generators/doorkeeper/templates/enable_polymorphic_resource_owner_migration.rb.erb +17 -0
- data/lib/generators/doorkeeper/templates/initializer.rb +47 -3
- data/lib/generators/doorkeeper/templates/migration.rb.erb +2 -0
- data/spec/controllers/applications_controller_spec.rb +2 -2
- data/spec/controllers/authorizations_controller_spec.rb +165 -30
- data/spec/controllers/tokens_controller_spec.rb +6 -5
- data/spec/dummy/app/helpers/application_helper.rb +1 -1
- data/spec/dummy/app/models/user.rb +5 -1
- data/spec/dummy/config/application.rb +6 -4
- data/spec/dummy/config/boot.rb +4 -4
- data/spec/dummy/config/environment.rb +1 -1
- data/spec/dummy/config/routes.rb +4 -4
- data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +2 -2
- data/spec/dummy/db/schema.rb +3 -1
- data/spec/factories.rb +1 -1
- data/spec/generators/enable_polymorphic_resource_owner_generator_spec.rb +47 -0
- data/spec/lib/config_spec.rb +30 -11
- data/spec/lib/models/revocable_spec.rb +2 -3
- data/spec/lib/models/scopes_spec.rb +8 -0
- data/spec/lib/oauth/authorization_code_request_spec.rb +25 -15
- data/spec/lib/oauth/base_request_spec.rb +6 -20
- data/spec/lib/oauth/client_credentials/creator_spec.rb +90 -89
- data/spec/lib/oauth/client_credentials/issuer_spec.rb +84 -86
- data/spec/lib/oauth/client_credentials/validation_spec.rb +38 -40
- data/spec/lib/oauth/client_credentials_request_spec.rb +5 -4
- data/spec/lib/oauth/code_request_spec.rb +1 -1
- data/spec/lib/oauth/code_response_spec.rb +5 -1
- data/spec/lib/oauth/error_response_spec.rb +1 -1
- data/spec/lib/oauth/password_access_token_request_spec.rb +24 -13
- data/spec/lib/oauth/pre_authorization_spec.rb +13 -18
- data/spec/lib/oauth/refresh_token_request_spec.rb +19 -30
- data/spec/lib/oauth/token_request_spec.rb +14 -7
- data/spec/lib/option_spec.rb +51 -0
- data/spec/lib/stale_records_cleaner_spec.rb +18 -5
- data/spec/models/doorkeeper/access_grant_spec.rb +18 -4
- data/spec/models/doorkeeper/access_token_spec.rb +507 -479
- data/spec/models/doorkeeper/application_spec.rb +338 -300
- data/spec/requests/endpoints/token_spec.rb +5 -1
- data/spec/requests/flows/authorization_code_errors_spec.rb +4 -1
- data/spec/requests/flows/authorization_code_spec.rb +6 -1
- data/spec/requests/flows/client_credentials_spec.rb +41 -0
- data/spec/requests/flows/refresh_token_spec.rb +16 -8
- data/spec/requests/flows/revoke_token_spec.rb +143 -104
- data/spec/support/helpers/access_token_request_helper.rb +1 -0
- data/spec/support/helpers/authorization_request_helper.rb +4 -4
- data/spec/support/helpers/config_helper.rb +1 -1
- data/spec/support/shared/controllers_shared_context.rb +2 -2
- data/spec/support/shared/models_shared_examples.rb +6 -4
- metadata +15 -4
|
@@ -3,401 +3,439 @@
|
|
|
3
3
|
require "spec_helper"
|
|
4
4
|
require "bcrypt"
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
let(:unset_require_owner) { Doorkeeper.configuration.instance_variable_set("@confirm_application_owner", false) }
|
|
11
|
-
let(:new_application) { FactoryBot.build(:application) }
|
|
6
|
+
describe Doorkeeper::Application do
|
|
7
|
+
let(:require_owner) { Doorkeeper.config.instance_variable_set("@confirm_application_owner", true) }
|
|
8
|
+
let(:unset_require_owner) { Doorkeeper.config.instance_variable_set("@confirm_application_owner", false) }
|
|
9
|
+
let(:new_application) { FactoryBot.build(:application) }
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
let(:uid) { SecureRandom.hex(8) }
|
|
12
|
+
let(:secret) { SecureRandom.hex(8) }
|
|
15
13
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
it "is invalid without a name" do
|
|
15
|
+
new_application.name = nil
|
|
16
|
+
expect(new_application).not_to be_valid
|
|
17
|
+
end
|
|
20
18
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
it "is invalid without determining confidentiality" do
|
|
20
|
+
new_application.confidential = nil
|
|
21
|
+
expect(new_application).not_to be_valid
|
|
22
|
+
end
|
|
25
23
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
|
31
29
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
|
37
35
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
36
|
+
it "generates uid on create unless one is set" do
|
|
37
|
+
new_application.uid = uid
|
|
38
|
+
new_application.save
|
|
39
|
+
expect(new_application.uid).to eq(uid)
|
|
40
|
+
end
|
|
43
41
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
it "is invalid without uid" do
|
|
43
|
+
new_application.save
|
|
44
|
+
new_application.uid = nil
|
|
45
|
+
expect(new_application).not_to be_valid
|
|
46
|
+
end
|
|
49
47
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
48
|
+
it "checks uniqueness of uid" do
|
|
49
|
+
app1 = FactoryBot.create(:application)
|
|
50
|
+
app2 = FactoryBot.create(:application)
|
|
51
|
+
app2.uid = app1.uid
|
|
52
|
+
expect(app2).not_to be_valid
|
|
53
|
+
end
|
|
56
54
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
55
|
+
it "expects database to throw an error when uids are the same" do
|
|
56
|
+
app1 = FactoryBot.create(:application)
|
|
57
|
+
app2 = FactoryBot.create(:application)
|
|
58
|
+
app2.uid = app1.uid
|
|
59
|
+
expect { app2.save!(validate: false) }.to raise_error(uniqueness_error)
|
|
60
|
+
end
|
|
63
61
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
62
|
+
it "generate secret on create" do
|
|
63
|
+
expect(new_application.secret).to be_nil
|
|
64
|
+
new_application.save
|
|
65
|
+
expect(new_application.secret).not_to be_nil
|
|
66
|
+
end
|
|
69
67
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
68
|
+
it "generate secret on create if is blank string" do
|
|
69
|
+
new_application.secret = ""
|
|
70
|
+
new_application.save
|
|
71
|
+
expect(new_application.secret).not_to be_blank
|
|
72
|
+
end
|
|
75
73
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
74
|
+
it "generate secret on create unless one is set" do
|
|
75
|
+
new_application.secret = secret
|
|
76
|
+
new_application.save
|
|
77
|
+
expect(new_application.secret).to eq(secret)
|
|
78
|
+
end
|
|
81
79
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
80
|
+
it "is invalid without secret" do
|
|
81
|
+
new_application.save
|
|
82
|
+
new_application.secret = nil
|
|
83
|
+
expect(new_application).not_to be_valid
|
|
84
|
+
end
|
|
87
85
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
end
|
|
86
|
+
context "application_owner is enabled" do
|
|
87
|
+
before do
|
|
88
|
+
Doorkeeper.configure do
|
|
89
|
+
orm DOORKEEPER_ORM
|
|
90
|
+
enable_application_owner
|
|
94
91
|
end
|
|
92
|
+
end
|
|
95
93
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
94
|
+
context "application owner is not required" do
|
|
95
|
+
before(:each) do
|
|
96
|
+
unset_require_owner
|
|
97
|
+
end
|
|
100
98
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
end
|
|
99
|
+
it "is valid given valid attributes" do
|
|
100
|
+
expect(new_application).to be_valid
|
|
104
101
|
end
|
|
102
|
+
end
|
|
105
103
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
104
|
+
context "application owner is required" do
|
|
105
|
+
before(:each) do
|
|
106
|
+
require_owner
|
|
107
|
+
@owner = FactoryBot.build_stubbed(:doorkeeper_testing_user)
|
|
108
|
+
end
|
|
111
109
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
110
|
+
it "is invalid without an owner" do
|
|
111
|
+
expect(new_application).not_to be_valid
|
|
112
|
+
end
|
|
115
113
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
end
|
|
114
|
+
it "is valid with an owner" do
|
|
115
|
+
new_application.owner = @owner
|
|
116
|
+
expect(new_application).to be_valid
|
|
120
117
|
end
|
|
121
118
|
end
|
|
119
|
+
end
|
|
122
120
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
end
|
|
121
|
+
context "redirect URI" do
|
|
122
|
+
context "when grant flows allow blank redirect URI" do
|
|
123
|
+
before do
|
|
124
|
+
Doorkeeper.configure do
|
|
125
|
+
grant_flows %w[password client_credentials]
|
|
129
126
|
end
|
|
127
|
+
end
|
|
130
128
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
end
|
|
129
|
+
it "is valid without redirect_uri" do
|
|
130
|
+
new_application.save
|
|
131
|
+
new_application.redirect_uri = nil
|
|
132
|
+
expect(new_application).to be_valid
|
|
136
133
|
end
|
|
134
|
+
end
|
|
137
135
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
end
|
|
136
|
+
context "when grant flows require redirect URI" do
|
|
137
|
+
before do
|
|
138
|
+
Doorkeeper.configure do
|
|
139
|
+
grant_flows %w[password client_credentials authorization_code]
|
|
143
140
|
end
|
|
141
|
+
end
|
|
144
142
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
end
|
|
143
|
+
it "is invalid without redirect_uri" do
|
|
144
|
+
new_application.save
|
|
145
|
+
new_application.redirect_uri = nil
|
|
146
|
+
expect(new_application).not_to be_valid
|
|
150
147
|
end
|
|
148
|
+
end
|
|
151
149
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
end
|
|
150
|
+
context "when blank URI option disabled" do
|
|
151
|
+
before do
|
|
152
|
+
Doorkeeper.configure do
|
|
153
|
+
grant_flows %w[password client_credentials]
|
|
154
|
+
allow_blank_redirect_uri false
|
|
158
155
|
end
|
|
156
|
+
end
|
|
159
157
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
end
|
|
158
|
+
it "is invalid without redirect_uri" do
|
|
159
|
+
new_application.save
|
|
160
|
+
new_application.redirect_uri = nil
|
|
161
|
+
expect(new_application).not_to be_valid
|
|
165
162
|
end
|
|
166
163
|
end
|
|
164
|
+
end
|
|
167
165
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
166
|
+
context "with hashing enabled" do
|
|
167
|
+
include_context "with application hashing enabled"
|
|
168
|
+
let(:app) { FactoryBot.create :application }
|
|
169
|
+
let(:default_strategy) { Doorkeeper::SecretStoring::Sha256Hash }
|
|
172
170
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
171
|
+
it "uses SHA256 to avoid additional dependencies" do
|
|
172
|
+
# Ensure token was generated
|
|
173
|
+
app.validate
|
|
174
|
+
expect(app.secret).to eq(default_strategy.transform_secret(app.plaintext_secret))
|
|
175
|
+
end
|
|
178
176
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
end
|
|
177
|
+
context "when bcrypt strategy is configured" do
|
|
178
|
+
# In this text context, we have bcrypt loaded so `bcrypt_present?`
|
|
179
|
+
# will always be true
|
|
180
|
+
before do
|
|
181
|
+
Doorkeeper.configure do
|
|
182
|
+
hash_application_secrets using: "Doorkeeper::SecretStoring::BCrypt"
|
|
186
183
|
end
|
|
184
|
+
end
|
|
187
185
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
end
|
|
186
|
+
it "holds a volatile plaintext and BCrypt secret" do
|
|
187
|
+
expect(app.secret_strategy).to eq Doorkeeper::SecretStoring::BCrypt
|
|
188
|
+
expect(app.plaintext_secret).to be_a(String)
|
|
189
|
+
expect(app.secret).not_to eq(app.plaintext_secret)
|
|
190
|
+
expect { ::BCrypt::Password.create(app.secret) }.not_to raise_error
|
|
194
191
|
end
|
|
192
|
+
end
|
|
195
193
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
194
|
+
it "does not fallback to plain lookup by default" do
|
|
195
|
+
lookup = described_class.by_uid_and_secret(app.uid, app.secret)
|
|
196
|
+
expect(lookup).to eq(nil)
|
|
199
197
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
198
|
+
lookup = described_class.by_uid_and_secret(app.uid, app.plaintext_secret)
|
|
199
|
+
expect(lookup).to eq(app)
|
|
200
|
+
end
|
|
203
201
|
|
|
204
|
-
|
|
205
|
-
|
|
202
|
+
context "with fallback enabled" do
|
|
203
|
+
include_context "with token hashing and fallback lookup enabled"
|
|
206
204
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
205
|
+
it "provides plain and hashed lookup" do
|
|
206
|
+
lookup = described_class.by_uid_and_secret(app.uid, app.secret)
|
|
207
|
+
expect(lookup).to eq(app)
|
|
210
208
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
end
|
|
209
|
+
lookup = described_class.by_uid_and_secret(app.uid, app.plaintext_secret)
|
|
210
|
+
expect(lookup).to eq(app)
|
|
214
211
|
end
|
|
212
|
+
end
|
|
215
213
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
end
|
|
214
|
+
it "does not provide access to secret after loading" do
|
|
215
|
+
lookup = described_class.by_uid_and_secret(app.uid, app.plaintext_secret)
|
|
216
|
+
expect(lookup.plaintext_secret).to be_nil
|
|
220
217
|
end
|
|
218
|
+
end
|
|
221
219
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
220
|
+
describe "destroy related models on cascade" do
|
|
221
|
+
before(:each) do
|
|
222
|
+
new_application.save
|
|
223
|
+
end
|
|
226
224
|
|
|
227
|
-
|
|
228
|
-
FactoryBot.create(:access_grant, application: new_application)
|
|
229
|
-
expect { new_application.destroy }.to change { Doorkeeper::AccessGrant.count }.by(-1)
|
|
230
|
-
end
|
|
225
|
+
let(:resource_owner) { FactoryBot.create(:resource_owner) }
|
|
231
226
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
227
|
+
it "should destroy its access grants" do
|
|
228
|
+
FactoryBot.create(
|
|
229
|
+
:access_grant,
|
|
230
|
+
application: new_application,
|
|
231
|
+
resource_owner_id: resource_owner.id,
|
|
232
|
+
resource_owner_type: resource_owner.class.name,
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
expect { new_application.destroy }.to change { Doorkeeper::AccessGrant.count }.by(-1)
|
|
239
236
|
end
|
|
240
237
|
|
|
241
|
-
|
|
242
|
-
|
|
238
|
+
it "should destroy its access tokens" do
|
|
239
|
+
FactoryBot.create(:access_token, application: new_application)
|
|
240
|
+
FactoryBot.create(:access_token, application: new_application, revoked_at: Time.now.utc)
|
|
241
|
+
expect do
|
|
242
|
+
new_application.destroy
|
|
243
|
+
end.to change { Doorkeeper::AccessToken.count }.by(-2)
|
|
244
|
+
end
|
|
245
|
+
end
|
|
243
246
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
247
|
+
describe "#ordered_by" do
|
|
248
|
+
let(:applications) { FactoryBot.create_list(:application, 5) }
|
|
249
|
+
|
|
250
|
+
context "when a direction is not specified" do
|
|
251
|
+
it "calls order with a default order of asc" do
|
|
252
|
+
names = applications.map(&:name).sort
|
|
253
|
+
expect(described_class.ordered_by(:name).map(&:name)).to eq(names)
|
|
249
254
|
end
|
|
255
|
+
end
|
|
250
256
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
end
|
|
257
|
+
context "when a direction is specified" do
|
|
258
|
+
it "calls order with specified direction" do
|
|
259
|
+
names = applications.map(&:name).sort.reverse
|
|
260
|
+
expect(described_class.ordered_by(:name, :desc).map(&:name)).to eq(names)
|
|
256
261
|
end
|
|
257
262
|
end
|
|
263
|
+
end
|
|
258
264
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
end
|
|
265
|
+
describe "#redirect_uri=" do
|
|
266
|
+
context "when array of valid redirect_uris" do
|
|
267
|
+
it "should join by newline" do
|
|
268
|
+
new_application.redirect_uri = ["http://localhost/callback1", "http://localhost/callback2"]
|
|
269
|
+
expect(new_application.redirect_uri).to eq("http://localhost/callback1\nhttp://localhost/callback2")
|
|
265
270
|
end
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
+
end
|
|
272
|
+
context "when string of valid redirect_uris" do
|
|
273
|
+
it "should store as-is" do
|
|
274
|
+
new_application.redirect_uri = "http://localhost/callback1\nhttp://localhost/callback2"
|
|
275
|
+
expect(new_application.redirect_uri).to eq("http://localhost/callback1\nhttp://localhost/callback2")
|
|
271
276
|
end
|
|
272
277
|
end
|
|
278
|
+
end
|
|
273
279
|
|
|
274
|
-
|
|
275
|
-
|
|
280
|
+
describe "#renew_secret" do
|
|
281
|
+
let(:app) { FactoryBot.create :application }
|
|
276
282
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
end
|
|
283
|
+
it "should generate a new secret" do
|
|
284
|
+
old_secret = app.secret
|
|
285
|
+
app.renew_secret
|
|
286
|
+
expect(old_secret).not_to eq(app.secret)
|
|
282
287
|
end
|
|
288
|
+
end
|
|
283
289
|
|
|
284
|
-
|
|
285
|
-
|
|
290
|
+
describe "#authorized_for" do
|
|
291
|
+
let(:resource_owner) { FactoryBot.create(:resource_owner) }
|
|
292
|
+
let(:other_resource_owner) { FactoryBot.create(:resource_owner) }
|
|
286
293
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
294
|
+
it "is empty if the application is not authorized for anyone" do
|
|
295
|
+
expect(described_class.authorized_for(resource_owner)).to be_empty
|
|
296
|
+
end
|
|
290
297
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
298
|
+
it "returns only application for a specific resource owner" do
|
|
299
|
+
FactoryBot.create(
|
|
300
|
+
:access_token,
|
|
301
|
+
resource_owner_id: other_resource_owner.id,
|
|
302
|
+
resource_owner_type: other_resource_owner.class.name,
|
|
303
|
+
)
|
|
304
|
+
token = FactoryBot.create(
|
|
305
|
+
:access_token,
|
|
306
|
+
resource_owner_id: resource_owner.id,
|
|
307
|
+
resource_owner_type: resource_owner.class.name,
|
|
308
|
+
)
|
|
309
|
+
expect(described_class.authorized_for(resource_owner)).to eq([token.application])
|
|
310
|
+
end
|
|
296
311
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
312
|
+
it "excludes revoked tokens" do
|
|
313
|
+
FactoryBot.create(
|
|
314
|
+
:access_token,
|
|
315
|
+
resource_owner_id: resource_owner.id,
|
|
316
|
+
resource_owner_type: resource_owner.class.name,
|
|
317
|
+
revoked_at: 2.days.ago,
|
|
318
|
+
)
|
|
319
|
+
expect(described_class.authorized_for(resource_owner)).to be_empty
|
|
320
|
+
end
|
|
301
321
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
322
|
+
it "returns all applications that have been authorized" do
|
|
323
|
+
token1 = FactoryBot.create(
|
|
324
|
+
:access_token,
|
|
325
|
+
resource_owner_id: resource_owner.id,
|
|
326
|
+
resource_owner_type: resource_owner.class.name,
|
|
327
|
+
)
|
|
328
|
+
token2 = FactoryBot.create(
|
|
329
|
+
:access_token,
|
|
330
|
+
resource_owner_id: resource_owner.id,
|
|
331
|
+
resource_owner_type: resource_owner.class.name,
|
|
332
|
+
)
|
|
333
|
+
expect(described_class.authorized_for(resource_owner))
|
|
334
|
+
.to eq([token1.application, token2.application])
|
|
335
|
+
end
|
|
307
336
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
337
|
+
it "returns only one application even if it has been authorized twice" do
|
|
338
|
+
application = FactoryBot.create(:application)
|
|
339
|
+
FactoryBot.create(
|
|
340
|
+
:access_token,
|
|
341
|
+
resource_owner_id: resource_owner.id,
|
|
342
|
+
resource_owner_type: resource_owner.class.name,
|
|
343
|
+
application: application,
|
|
344
|
+
)
|
|
345
|
+
FactoryBot.create(
|
|
346
|
+
:access_token,
|
|
347
|
+
resource_owner_id: resource_owner.id,
|
|
348
|
+
resource_owner_type: resource_owner.class.name,
|
|
349
|
+
application: application,
|
|
350
|
+
)
|
|
351
|
+
expect(described_class.authorized_for(resource_owner)).to eq([application])
|
|
314
352
|
end
|
|
353
|
+
end
|
|
315
354
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
355
|
+
describe "#revoke_tokens_and_grants_for" do
|
|
356
|
+
it "revokes all access tokens and access grants" do
|
|
357
|
+
application_id = 42
|
|
358
|
+
resource_owner = double
|
|
359
|
+
expect(Doorkeeper::AccessToken)
|
|
360
|
+
.to receive(:revoke_all_for).with(application_id, resource_owner)
|
|
361
|
+
expect(Doorkeeper::AccessGrant)
|
|
362
|
+
.to receive(:revoke_all_for).with(application_id, resource_owner)
|
|
324
363
|
|
|
325
|
-
|
|
326
|
-
end
|
|
364
|
+
described_class.revoke_tokens_and_grants_for(application_id, resource_owner)
|
|
327
365
|
end
|
|
366
|
+
end
|
|
328
367
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
368
|
+
describe "#by_uid_and_secret" do
|
|
369
|
+
context "when application is private/confidential" do
|
|
370
|
+
it "finds the application via uid/secret" do
|
|
371
|
+
app = FactoryBot.create :application
|
|
372
|
+
authenticated = described_class.by_uid_and_secret(app.uid, app.secret)
|
|
373
|
+
expect(authenticated).to eq(app)
|
|
374
|
+
end
|
|
375
|
+
context "when secret is wrong" do
|
|
376
|
+
it "should not find the application" do
|
|
332
377
|
app = FactoryBot.create :application
|
|
333
|
-
authenticated =
|
|
334
|
-
expect(authenticated).to eq(
|
|
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
|
|
378
|
+
authenticated = described_class.by_uid_and_secret(app.uid, "bad")
|
|
379
|
+
expect(authenticated).to eq(nil)
|
|
342
380
|
end
|
|
343
381
|
end
|
|
382
|
+
end
|
|
344
383
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
end
|
|
384
|
+
context "when application is public/non-confidential" do
|
|
385
|
+
context "when secret is blank" do
|
|
386
|
+
it "should find the application" do
|
|
387
|
+
app = FactoryBot.create :application, confidential: false
|
|
388
|
+
authenticated = described_class.by_uid_and_secret(app.uid, nil)
|
|
389
|
+
expect(authenticated).to eq(app)
|
|
352
390
|
end
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
391
|
+
end
|
|
392
|
+
context "when secret is wrong" do
|
|
393
|
+
it "should not find the application" do
|
|
394
|
+
app = FactoryBot.create :application, confidential: false
|
|
395
|
+
authenticated = described_class.by_uid_and_secret(app.uid, "bad")
|
|
396
|
+
expect(authenticated).to eq(nil)
|
|
359
397
|
end
|
|
360
398
|
end
|
|
361
399
|
end
|
|
400
|
+
end
|
|
362
401
|
|
|
363
|
-
|
|
364
|
-
|
|
402
|
+
describe "#confidential?" do
|
|
403
|
+
subject { FactoryBot.create(:application, confidential: confidential).confidential? }
|
|
365
404
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
405
|
+
context "when application is private/confidential" do
|
|
406
|
+
let(:confidential) { true }
|
|
407
|
+
it { expect(subject).to eq(true) }
|
|
408
|
+
end
|
|
370
409
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
end
|
|
410
|
+
context "when application is public/non-confidential" do
|
|
411
|
+
let(:confidential) { false }
|
|
412
|
+
it { expect(subject).to eq(false) }
|
|
375
413
|
end
|
|
414
|
+
end
|
|
376
415
|
|
|
377
|
-
|
|
378
|
-
|
|
416
|
+
describe "#as_json" do
|
|
417
|
+
let(:app) { FactoryBot.create :application, secret: "123123123" }
|
|
379
418
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
419
|
+
before do
|
|
420
|
+
allow(Doorkeeper.configuration)
|
|
421
|
+
.to receive(:application_secret_strategy).and_return(Doorkeeper::SecretStoring::Plain)
|
|
422
|
+
end
|
|
384
423
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
424
|
+
it "includes plaintext secret" do
|
|
425
|
+
expect(app.as_json).to include("secret" => "123123123")
|
|
426
|
+
end
|
|
388
427
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
428
|
+
it "respects custom options" do
|
|
429
|
+
expect(app.as_json(except: :secret)).not_to include("secret")
|
|
430
|
+
expect(app.as_json(only: :id)).to match("id" => app.id)
|
|
431
|
+
end
|
|
393
432
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
end
|
|
433
|
+
# AR specific
|
|
434
|
+
if DOORKEEPER_ORM == :active_record
|
|
435
|
+
it "correctly works with #to_json" do
|
|
436
|
+
ActiveRecord::Base.include_root_in_json = true
|
|
437
|
+
expect(app.to_json(include_root_in_json: true)).to match(/application.+?:\{/)
|
|
438
|
+
ActiveRecord::Base.include_root_in_json = false
|
|
401
439
|
end
|
|
402
440
|
end
|
|
403
441
|
end
|