doorkeeper 5.3.1 → 5.3.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bb82e22e68fdc6c80e3cb7b65d8a280e9893222a6bf3390f216f7bb534195682
4
- data.tar.gz: 9299736f29bce52700adeb7dde05d9669294ce03457c683c0a80d6f98ac73346
3
+ metadata.gz: 2a2e558c16d91012fda8543a405ee8e107ec3e100edc255ef9c0453e15fee34b
4
+ data.tar.gz: 0e9ffe0268ccfb370ec23cd5c5b124e8febdec591cd0d5443974bd73942079f2
5
5
  SHA512:
6
- metadata.gz: 7e951c0a2ebaa747f03afb968fc8750c166beb8cb73600fcf1ce47546e25528daf815d424b85c8ac723f9ba83b100bbf9f55acf9e3240f21db43bd463ae0838f
7
- data.tar.gz: f4e0bc0bf47e60407aae249b6d2eb7b485fe324e688caa43bb5db473c7b1393f973e00707edab007ddf5aee75007e1e6fcf4d2de8aa049ec16fa69cbbd082c7b
6
+ metadata.gz: 11b2350bfbe3e18b7500b9a159096dda35fc2307f66ab5ccec9e1266c18a4fc34e1ac9a8ffa763f713cd431dbb9a5fee7734bcd9d6576b41749101e3f149e969
7
+ data.tar.gz: 7fdb1df4a142ac870a3a37838c4131a7514f81571319339c115754bf92ad3ed97c9e5a543008b94da6117e854104e418052bb634a5e5fcfb460063ecb83c6b5d
@@ -5,6 +5,16 @@ upgrade guides.
5
5
 
6
6
  User-visible changes worth mentioning.
7
7
 
8
+ ## 5.3.2
9
+
10
+ - [#1371] Backport: add `#as_json` method and attributes serialization restriction for Application model.
11
+ Fixes information disclosure vulnerability (CVE-2020-10187).
12
+
13
+ **[IMPORTANT]** you need to re-implement `#as_json` method for Doorkeeper Application model
14
+ if you previously used `#to_json` serialization with custom options or attributes or rely on
15
+ JSON response from /oauth/applications.json or /oauth/authorized_applications.json. This change
16
+ is a breaking change which restricts serialized attributes to a very small set of columns.
17
+
8
18
  ## 5.3.1
9
19
 
10
20
  - [#1360] Backport: Increase `matching_token_for` batch lookup size to 10 000 and make it configurable.
@@ -19,7 +19,7 @@ module Doorkeeper
19
19
  def show
20
20
  respond_to do |format|
21
21
  format.html
22
- format.json { render json: @application }
22
+ format.json { render json: @application, as_owner: true }
23
23
  end
24
24
  end
25
25
 
@@ -36,7 +36,7 @@ module Doorkeeper
36
36
 
37
37
  respond_to do |format|
38
38
  format.html { redirect_to oauth_application_url(@application) }
39
- format.json { render json: @application }
39
+ format.json { render json: @application, as_owner: true }
40
40
  end
41
41
  else
42
42
  respond_to do |format|
@@ -58,7 +58,7 @@ module Doorkeeper
58
58
 
59
59
  respond_to do |format|
60
60
  format.html { redirect_to oauth_application_url(@application) }
61
- format.json { render json: @application }
61
+ format.json { render json: @application, as_owner: true }
62
62
  end
63
63
  else
64
64
  respond_to do |format|
@@ -9,7 +9,7 @@ module Doorkeeper
9
9
 
10
10
  respond_to do |format|
11
11
  format.html
12
- format.json { render json: @applications }
12
+ format.json { render json: @applications, current_resource_owner: current_resource_owner }
13
13
  end
14
14
  end
15
15
 
@@ -61,15 +61,27 @@ module Doorkeeper::Orm::ActiveRecord::Mixins
61
61
  end
62
62
  end
63
63
 
64
- # This is the right way how we want to override ActiveRecord #to_json
64
+ # Represents client as set of it's attributes in JSON format.
65
+ # This is the right way how we want to override ActiveRecord #to_json.
65
66
  #
66
- # @return [String] entity attributes as JSON
67
+ # Respects privacy settings and serializes minimum set of attributes
68
+ # for public/private clients and full set for authorized owners.
69
+ #
70
+ # @return [Hash] entity attributes for JSON
67
71
  #
68
72
  def as_json(options = {})
69
- hash = super
70
-
71
- hash["secret"] = plaintext_secret if hash.key?("secret")
72
- hash
73
+ # if application belongs to some owner we need to check if it's the same as
74
+ # the one passed in the options or check if we render the client as an owner
75
+ if (respond_to?(:owner) && owner && owner == options[:current_resource_owner]) ||
76
+ options[:as_owner]
77
+ # Owners can see all the client attributes, fallback to ActiveModel serialization
78
+ super
79
+ else
80
+ # if application has no owner or it's owner doesn't match one from the options
81
+ # we render only minimum set of attributes that could be exposed to a public
82
+ only = extract_serializable_attributes(options)
83
+ super(options.merge(only: only))
84
+ end
73
85
  end
74
86
 
75
87
  private
@@ -96,6 +108,49 @@ module Doorkeeper::Orm::ActiveRecord::Mixins
96
108
  def enforce_scopes?
97
109
  Doorkeeper.config.enforce_configured_scopes?
98
110
  end
111
+
112
+ # Helper method to extract collection of serializable attribute names
113
+ # considering serialization options (like `only`, `except` and so on).
114
+ #
115
+ # @param options [Hash] serialization options
116
+ #
117
+ # @return [Array<String>]
118
+ # collection of attributes to be serialized using #as_json
119
+ #
120
+ def extract_serializable_attributes(options = {})
121
+ opts = options.try(:dup) || {}
122
+ only = Array.wrap(opts[:only]).map(&:to_s)
123
+
124
+ only = if only.blank?
125
+ serializable_attributes
126
+ else
127
+ only & serializable_attributes
128
+ end
129
+
130
+ only -= Array.wrap(opts[:except]).map(&:to_s) if opts.key?(:except)
131
+ only.uniq
132
+ end
133
+
134
+ # We need to hook into this method to allow serializing plan-text secrets
135
+ # when secrets hashing enabled.
136
+ #
137
+ # @param key [String] attribute name
138
+ #
139
+ def read_attribute_for_serialization(key)
140
+ return super unless key.to_s == "secret"
141
+
142
+ plaintext_secret || secret
143
+ end
144
+
145
+ # Collection of attributes that could be serialized for public.
146
+ # Override this method if you need additional attributes to be serialized.
147
+ #
148
+ # @return [Array<String>] collection of serializable attributes
149
+ def serializable_attributes
150
+ attributes = %w[id name created_at]
151
+ attributes << "uid" unless confidential?
152
+ attributes
153
+ end
99
154
  end
100
155
 
101
156
  class_methods do
@@ -9,7 +9,7 @@ module Doorkeeper
9
9
  # Semantic versioning
10
10
  MAJOR = 5
11
11
  MINOR = 3
12
- TINY = 1
12
+ TINY = 2
13
13
  PRE = nil
14
14
 
15
15
  # Full version number
@@ -3,402 +3,480 @@
3
3
  require "spec_helper"
4
4
  require "bcrypt"
5
5
 
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) }
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
- let(:uid) { SecureRandom.hex(8) }
14
- let(:secret) { SecureRandom.hex(8) }
11
+ let(:uid) { SecureRandom.hex(8) }
12
+ let(:secret) { SecureRandom.hex(8) }
15
13
 
16
- it "is invalid without a name" do
17
- new_application.name = nil
18
- expect(new_application).not_to be_valid
19
- end
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
- it "is invalid without determining confidentiality" do
22
- new_application.confidential = nil
23
- expect(new_application).not_to be_valid
24
- end
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
- 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
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
- 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
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
- 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
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
- 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
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
- 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
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
- 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
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
- 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
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
- 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
74
- end
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
- 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
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
- it "is invalid without secret" do
83
- new_application.save
84
- new_application.secret = nil
85
- expect(new_application).not_to be_valid
86
- end
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
- context "application_owner is enabled" do
89
- before do
90
- Doorkeeper.configure do
91
- orm DOORKEEPER_ORM
92
- enable_application_owner
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
- context "application owner is not required" do
97
- before(:each) do
98
- unset_require_owner
99
- end
94
+ context "application owner is not required" do
95
+ before(:each) do
96
+ unset_require_owner
97
+ end
100
98
 
101
- it "is valid given valid attributes" do
102
- expect(new_application).to be_valid
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
- context "application owner is required" do
107
- before(:each) do
108
- require_owner
109
- @owner = FactoryBot.build_stubbed(:doorkeeper_testing_user)
110
- end
104
+ context "application owner is required" do
105
+ before do
106
+ require_owner
107
+ @owner = FactoryBot.build_stubbed(:doorkeeper_testing_user)
108
+ end
111
109
 
112
- it "is invalid without an owner" do
113
- expect(new_application).not_to be_valid
114
- end
110
+ it "is invalid without an owner" do
111
+ expect(new_application).not_to be_valid
112
+ end
115
113
 
116
- it "is valid with an owner" do
117
- new_application.owner = @owner
118
- expect(new_application).to be_valid
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
- 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
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
- 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
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
- 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
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
- 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
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
- 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
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
- 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
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
- 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 }
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
- 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
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
- 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
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
- 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
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
- 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)
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
- lookup = clazz.by_uid_and_secret(app.uid, app.plaintext_secret)
201
- expect(lookup).to eq(app)
202
- end
198
+ lookup = described_class.by_uid_and_secret(app.uid, app.plaintext_secret)
199
+ expect(lookup).to eq(app)
200
+ end
203
201
 
204
- context "with fallback enabled" do
205
- include_context "with token hashing and fallback lookup enabled"
202
+ context "with fallback enabled" do
203
+ include_context "with token hashing and fallback lookup enabled"
206
204
 
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)
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
- lookup = clazz.by_uid_and_secret(app.uid, app.plaintext_secret)
212
- expect(lookup).to eq(app)
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
- 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
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
- describe "destroy related models on cascade" do
223
- before(:each) do
224
- new_application.save
225
- end
220
+ describe "destroy related models on cascade" do
221
+ before(:each) do
222
+ new_application.save
223
+ end
226
224
 
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
225
+ let(:resource_owner) { FactoryBot.create(:doorkeeper_testing_user) }
231
226
 
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
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
+ )
233
+
234
+ expect { new_application.destroy }.to change { Doorkeeper::AccessGrant.count }.by(-1)
239
235
  end
240
236
 
241
- describe "#ordered_by" do
242
- let(:applications) { FactoryBot.create_list(:application, 5) }
237
+ it "should destroy its access tokens" do
238
+ FactoryBot.create(:access_token, application: new_application)
239
+ FactoryBot.create(:access_token, application: new_application, revoked_at: Time.now.utc)
240
+ expect do
241
+ new_application.destroy
242
+ end.to change { Doorkeeper::AccessToken.count }.by(-2)
243
+ end
244
+ end
243
245
 
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
246
+ describe "#ordered_by" do
247
+ let(:applications) { FactoryBot.create_list(:application, 5) }
248
+
249
+ context "when a direction is not specified" do
250
+ it "calls order with a default order of asc" do
251
+ names = applications.map(&:name).sort
252
+ expect(described_class.ordered_by(:name).map(&:name)).to eq(names)
249
253
  end
254
+ end
250
255
 
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
256
+ context "when a direction is specified" do
257
+ it "calls order with specified direction" do
258
+ names = applications.map(&:name).sort.reverse
259
+ expect(described_class.ordered_by(:name, :desc).map(&:name)).to eq(names)
256
260
  end
257
261
  end
262
+ end
258
263
 
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
264
+ describe "#redirect_uri=" do
265
+ context "when array of valid redirect_uris" do
266
+ it "should join by newline" do
267
+ new_application.redirect_uri = ["http://localhost/callback1", "http://localhost/callback2"]
268
+ expect(new_application.redirect_uri).to eq("http://localhost/callback1\nhttp://localhost/callback2")
265
269
  end
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
270
+ end
271
+ context "when string of valid redirect_uris" do
272
+ it "should store as-is" do
273
+ new_application.redirect_uri = "http://localhost/callback1\nhttp://localhost/callback2"
274
+ expect(new_application.redirect_uri).to eq("http://localhost/callback1\nhttp://localhost/callback2")
271
275
  end
272
276
  end
277
+ end
273
278
 
274
- describe "#renew_secret" do
275
- let(:app) { FactoryBot.create :application }
279
+ describe "#renew_secret" do
280
+ let(:app) { FactoryBot.create :application }
276
281
 
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
282
+ it "should generate a new secret" do
283
+ old_secret = app.secret
284
+ app.renew_secret
285
+ expect(old_secret).not_to eq(app.secret)
282
286
  end
287
+ end
283
288
 
284
- describe "#authorized_for" do
285
- let(:resource_owner) { double(:resource_owner, id: 10) }
289
+ describe "#authorized_for" do
290
+ let(:resource_owner) { FactoryBot.create(:doorkeeper_testing_user) }
291
+ let(:other_resource_owner) { FactoryBot.create(:doorkeeper_testing_user) }
286
292
 
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
293
+ it "is empty if the application is not authorized for anyone" do
294
+ expect(described_class.authorized_for(resource_owner)).to be_empty
295
+ end
290
296
 
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
297
+ it "returns only application for a specific resource owner" do
298
+ FactoryBot.create(
299
+ :access_token,
300
+ resource_owner_id: other_resource_owner.id,
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
296
308
 
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
309
+ it "excludes revoked tokens" do
310
+ FactoryBot.create(
311
+ :access_token,
312
+ resource_owner_id: resource_owner.id,
313
+ revoked_at: 2.days.ago,
314
+ )
315
+ expect(described_class.authorized_for(resource_owner)).to be_empty
316
+ end
301
317
 
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
318
+ it "returns all applications that have been authorized" do
319
+ token1 = FactoryBot.create(
320
+ :access_token,
321
+ resource_owner_id: resource_owner.id,
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
307
330
 
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
331
+ it "returns only one application even if it has been authorized twice" do
332
+ application = FactoryBot.create(:application)
333
+ FactoryBot.create(
334
+ :access_token,
335
+ resource_owner_id: resource_owner.id,
336
+ application: application,
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])
314
344
  end
345
+ end
315
346
 
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)
347
+ describe "#revoke_tokens_and_grants_for" do
348
+ it "revokes all access tokens and access grants" do
349
+ application_id = 42
350
+ resource_owner = double
351
+ expect(Doorkeeper::AccessToken)
352
+ .to receive(:revoke_all_for).with(application_id, resource_owner)
353
+ expect(Doorkeeper::AccessGrant)
354
+ .to receive(:revoke_all_for).with(application_id, resource_owner)
324
355
 
325
- Application.revoke_tokens_and_grants_for(application_id, resource_owner)
326
- end
356
+ described_class.revoke_tokens_and_grants_for(application_id, resource_owner)
327
357
  end
358
+ end
328
359
 
329
- describe "#by_uid_and_secret" do
330
- context "when application is private/confidential" do
331
- it "finds the application via uid/secret" do
360
+ describe "#by_uid_and_secret" do
361
+ context "when application is private/confidential" do
362
+ it "finds the application via uid/secret" do
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
332
369
  app = FactoryBot.create :application
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
370
+ authenticated = described_class.by_uid_and_secret(app.uid, "bad")
371
+ expect(authenticated).to eq(nil)
342
372
  end
343
373
  end
374
+ end
344
375
 
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
376
+ context "when application is public/non-confidential" do
377
+ context "when secret is blank" do
378
+ it "should find the application" do
379
+ app = FactoryBot.create :application, confidential: false
380
+ authenticated = described_class.by_uid_and_secret(app.uid, nil)
381
+ expect(authenticated).to eq(app)
352
382
  end
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
383
+ end
384
+ context "when secret is wrong" do
385
+ it "should not find the application" do
386
+ app = FactoryBot.create :application, confidential: false
387
+ authenticated = described_class.by_uid_and_secret(app.uid, "bad")
388
+ expect(authenticated).to eq(nil)
359
389
  end
360
390
  end
361
391
  end
392
+ end
362
393
 
363
- describe "#confidential?" do
364
- subject { FactoryBot.create(:application, confidential: confidential).confidential? }
394
+ describe "#confidential?" do
395
+ subject { FactoryBot.create(:application, confidential: confidential).confidential? }
365
396
 
366
- context "when application is private/confidential" do
367
- let(:confidential) { true }
368
- it { expect(subject).to eq(true) }
369
- end
397
+ context "when application is private/confidential" do
398
+ let(:confidential) { true }
399
+ it { expect(subject).to eq(true) }
400
+ end
370
401
 
371
- context "when application is public/non-confidential" do
372
- let(:confidential) { false }
373
- it { expect(subject).to eq(false) }
374
- end
402
+ context "when application is public/non-confidential" do
403
+ let(:confidential) { false }
404
+ it { expect(subject).to eq(false) }
375
405
  end
406
+ end
376
407
 
377
- describe "#as_json" do
378
- let(:app) { FactoryBot.create :application, secret: "123123123" }
408
+ describe "#as_json" do
409
+ let(:app) { FactoryBot.create :application, secret: "123123123" }
379
410
 
380
- before do
381
- allow(Doorkeeper.configuration)
382
- .to receive(:application_secret_strategy).and_return(Doorkeeper::SecretStoring::Plain)
411
+ before do
412
+ allow(Doorkeeper.configuration)
413
+ .to receive(:application_secret_strategy).and_return(Doorkeeper::SecretStoring::Plain)
414
+ end
415
+
416
+ # AR specific feature
417
+ if DOORKEEPER_ORM == :active_record
418
+ it "correctly works with #to_json" do
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
383
422
  end
423
+ end
384
424
 
385
- it "includes plaintext secret" do
386
- expect(app.as_json).to include("secret" => "123123123")
425
+ context "when called without authorized resource owner" do
426
+ it "includes minimal set of attributes" do
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
433
+
434
+ it "includes application UID if it's public" do
435
+ app = FactoryBot.create :application, secret: "123123123", confidential: false
436
+
437
+ expect(app.as_json).to match(
438
+ "id" => app.id,
439
+ "name" => app.name,
440
+ "created_at" => an_instance_of(String),
441
+ "uid" => app.uid,
442
+ )
387
443
  end
388
444
 
389
445
  it "respects custom options" do
390
- expect(app.as_json(except: :secret)).not_to include("secret")
391
- expect(app.as_json(only: :id)).to match("id" => app.id)
446
+ expect(app.as_json(except: :id)).not_to include("id")
447
+ expect(app.as_json(only: %i[name created_at secret]))
448
+ .to match(
449
+ "name" => app.name,
450
+ "created_at" => an_instance_of(String),
451
+ )
392
452
  end
453
+ end
393
454
 
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
455
+ context "when called with authorized resource owner" do
456
+ let(:owner) { FactoryBot.create(:doorkeeper_testing_user) }
457
+ let(:other_owner) { FactoryBot.create(:doorkeeper_testing_user) }
458
+ let(:app) { FactoryBot.create(:application, secret: "123123123", owner: owner) }
459
+
460
+ before do
461
+ Doorkeeper.configure do
462
+ orm DOORKEEPER_ORM
463
+ enable_application_owner confirmation: false
400
464
  end
401
465
  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
402
480
  end
403
481
  end
404
482
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: doorkeeper
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.3.1
4
+ version: 5.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Felipe Elias Philipp
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2020-02-09 00:00:00.000000000 Z
14
+ date: 2020-05-02 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: railties