doorkeeper 5.3.1 → 5.3.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of doorkeeper might be problematic. Click here for more details.

checksums.yaml 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