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 +4 -4
- data/CHANGELOG.md +10 -0
- data/app/controllers/doorkeeper/applications_controller.rb +3 -3
- data/app/controllers/doorkeeper/authorized_applications_controller.rb +1 -1
- data/lib/doorkeeper/orm/active_record/mixins/application.rb +61 -6
- data/lib/doorkeeper/version.rb +1 -1
- data/spec/models/doorkeeper/application_spec.rb +373 -295
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2a2e558c16d91012fda8543a405ee8e107ec3e100edc255ef9c0453e15fee34b
|
4
|
+
data.tar.gz: 0e9ffe0268ccfb370ec23cd5c5b124e8febdec591cd0d5443974bd73942079f2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 11b2350bfbe3e18b7500b9a159096dda35fc2307f66ab5ccec9e1266c18a4fc34e1ac9a8ffa763f713cd431dbb9a5fee7734bcd9d6576b41749101e3f149e969
|
7
|
+
data.tar.gz: 7fdb1df4a142ac870a3a37838c4131a7514f81571319339c115754bf92ad3ed97c9e5a543008b94da6117e854104e418052bb634a5e5fcfb460063ecb83c6b5d
|
data/CHANGELOG.md
CHANGED
@@ -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|
|
@@ -61,15 +61,27 @@ module Doorkeeper::Orm::ActiveRecord::Mixins
|
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
64
|
-
#
|
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
|
-
#
|
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
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
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
|
data/lib/doorkeeper/version.rb
CHANGED
@@ -3,402 +3,480 @@
|
|
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 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(:doorkeeper_testing_user) }
|
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
|
+
)
|
233
|
+
|
234
|
+
expect { new_application.destroy }.to change { Doorkeeper::AccessGrant.count }.by(-1)
|
239
235
|
end
|
240
236
|
|
241
|
-
|
242
|
-
|
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
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
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
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
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
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
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
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
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
|
-
|
275
|
-
|
279
|
+
describe "#renew_secret" do
|
280
|
+
let(:app) { FactoryBot.create :application }
|
276
281
|
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
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
|
-
|
285
|
-
|
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
|
-
|
288
|
-
|
289
|
-
|
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
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
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
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
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
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
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
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
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
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
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
|
-
|
326
|
-
end
|
356
|
+
described_class.revoke_tokens_and_grants_for(application_id, resource_owner)
|
327
357
|
end
|
358
|
+
end
|
328
359
|
|
329
|
-
|
330
|
-
|
331
|
-
|
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 =
|
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
|
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
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
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
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
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
|
-
|
364
|
-
|
394
|
+
describe "#confidential?" do
|
395
|
+
subject { FactoryBot.create(:application, confidential: confidential).confidential? }
|
365
396
|
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
397
|
+
context "when application is private/confidential" do
|
398
|
+
let(:confidential) { true }
|
399
|
+
it { expect(subject).to eq(true) }
|
400
|
+
end
|
370
401
|
|
371
|
-
|
372
|
-
|
373
|
-
|
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
|
-
|
378
|
-
|
408
|
+
describe "#as_json" do
|
409
|
+
let(:app) { FactoryBot.create :application, secret: "123123123" }
|
379
410
|
|
380
|
-
|
381
|
-
|
382
|
-
|
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
|
-
|
386
|
-
|
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: :
|
391
|
-
expect(app.as_json(only:
|
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
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
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.
|
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
|
14
|
+
date: 2020-05-02 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: railties
|