doorkeeper 5.1.0 → 5.1.1
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/NEWS.md +3 -2
- 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/application.rb +66 -0
- data/lib/doorkeeper/version.rb +1 -1
- data/spec/controllers/protected_resources_controller_spec.rb +3 -3
- data/spec/models/doorkeeper/application_spec.rb +373 -268
- 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: 213817ff28244685026d50922c3e64957442deac4deca7fd31e8265b19654ea8
|
4
|
+
data.tar.gz: 95ac43c52b0c01b5b6f5b72af3ccad07cb6d2fa67da0cb1a698a017d7cb9a059
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ef803e064768d3e04d9604acf5d32984da43dfbe1a79c6d672f8775776365865b6b42ffb46c924008a57ce27380b627834106b59a705713707e7ecb3a33b214e
|
7
|
+
data.tar.gz: 57e5cd9e2554ef3aa92b6aa9395494aba610ca969f65a3def3b9978b554c2b72ef839c186f19b2cd1b275126a9ba60c66c9c09479926b7acc9fe4e653ab01467
|
data/NEWS.md
CHANGED
@@ -5,9 +5,10 @@ upgrade guides.
|
|
5
5
|
|
6
6
|
User-visible changes worth mentioning.
|
7
7
|
|
8
|
-
##
|
8
|
+
## 5.1.1
|
9
9
|
|
10
|
-
|
10
|
+
[#1371] Backport: add #as_json method and attributes serialization restriction for Application model.
|
11
|
+
Fixes information disclosure vulnerability (CVE-2020-10187).
|
11
12
|
|
12
13
|
## 5.1.0
|
13
14
|
|
@@ -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
|
|
@@ -35,7 +35,7 @@ module Doorkeeper
|
|
35
35
|
|
36
36
|
respond_to do |format|
|
37
37
|
format.html { redirect_to oauth_application_url(@application) }
|
38
|
-
format.json { render json: @application }
|
38
|
+
format.json { render json: @application, as_owner: true }
|
39
39
|
end
|
40
40
|
else
|
41
41
|
respond_to do |format|
|
@@ -57,7 +57,7 @@ module Doorkeeper
|
|
57
57
|
|
58
58
|
respond_to do |format|
|
59
59
|
format.html { redirect_to oauth_application_url(@application) }
|
60
|
-
format.json { render json: @application }
|
60
|
+
format.json { render json: @application, as_owner: true }
|
61
61
|
end
|
62
62
|
else
|
63
63
|
respond_to do |format|
|
@@ -60,6 +60,29 @@ module Doorkeeper
|
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
63
|
+
# Represents client as set of it's attributes in JSON format.
|
64
|
+
# This is the right way how we want to override ActiveRecord #to_json.
|
65
|
+
#
|
66
|
+
# Respects privacy settings and serializes minimum set of attributes
|
67
|
+
# for public/private clients and full set for authorized owners.
|
68
|
+
#
|
69
|
+
# @return [Hash] entity attributes for JSON
|
70
|
+
#
|
71
|
+
def as_json(options = {})
|
72
|
+
# if application belongs to some owner we need to check if it's the same as
|
73
|
+
# the one passed in the options or check if we render the client as an owner
|
74
|
+
if (respond_to?(:owner) && owner && owner == options[:current_resource_owner]) ||
|
75
|
+
options[:as_owner]
|
76
|
+
# Owners can see all the client attributes, fallback to ActiveModel serialization
|
77
|
+
super
|
78
|
+
else
|
79
|
+
# if application has no owner or it's owner doesn't match one from the options
|
80
|
+
# we render only minimum set of attributes that could be exposed to a public
|
81
|
+
only = extract_serializable_attributes(options)
|
82
|
+
super(options.merge(only: only))
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
63
86
|
private
|
64
87
|
|
65
88
|
def generate_uid
|
@@ -84,5 +107,48 @@ module Doorkeeper
|
|
84
107
|
def enforce_scopes?
|
85
108
|
Doorkeeper.configuration.enforce_configured_scopes?
|
86
109
|
end
|
110
|
+
|
111
|
+
# Helper method to extract collection of serializable attribute names
|
112
|
+
# considering serialization options (like `only`, `except` and so on).
|
113
|
+
#
|
114
|
+
# @param options [Hash] serialization options
|
115
|
+
#
|
116
|
+
# @return [Array<String>]
|
117
|
+
# collection of attributes to be serialized using #as_json
|
118
|
+
#
|
119
|
+
def extract_serializable_attributes(options = {})
|
120
|
+
opts = options.try(:dup) || {}
|
121
|
+
only = Array.wrap(opts[:only]).map(&:to_s)
|
122
|
+
|
123
|
+
only = if only.blank?
|
124
|
+
serializable_attributes
|
125
|
+
else
|
126
|
+
only & serializable_attributes
|
127
|
+
end
|
128
|
+
|
129
|
+
only -= Array.wrap(opts[:except]).map(&:to_s) if opts.key?(:except)
|
130
|
+
only.uniq
|
131
|
+
end
|
132
|
+
|
133
|
+
# We need to hook into this method to allow serializing plan-text secrets
|
134
|
+
# when secrets hashing enabled.
|
135
|
+
#
|
136
|
+
# @param key [String] attribute name
|
137
|
+
#
|
138
|
+
def read_attribute_for_serialization(key)
|
139
|
+
return super unless key.to_s == "secret"
|
140
|
+
|
141
|
+
plaintext_secret || secret
|
142
|
+
end
|
143
|
+
|
144
|
+
# Collection of attributes that could be serialized for public.
|
145
|
+
# Override this method if you need additional attributes to be serialized.
|
146
|
+
#
|
147
|
+
# @return [Array<String>] collection of serializable attributes
|
148
|
+
def serializable_attributes
|
149
|
+
attributes = %w[id name created_at]
|
150
|
+
attributes << "uid" unless confidential?
|
151
|
+
attributes
|
152
|
+
end
|
87
153
|
end
|
88
154
|
end
|
data/lib/doorkeeper/version.rb
CHANGED
@@ -166,7 +166,7 @@ describe "doorkeeper authorize filter" do
|
|
166
166
|
it "it renders a custom JSON response", token: :invalid do
|
167
167
|
get :index, params: { access_token: token_string }
|
168
168
|
expect(response.status).to eq 401
|
169
|
-
expect(response.content_type).to
|
169
|
+
expect(response.content_type).to match(/application\/json/)
|
170
170
|
expect(response.header["WWW-Authenticate"]).to match(/^Bearer/)
|
171
171
|
|
172
172
|
expect(json_response).not_to be_nil
|
@@ -196,7 +196,7 @@ describe "doorkeeper authorize filter" do
|
|
196
196
|
it "it renders a custom text response", token: :invalid do
|
197
197
|
get :index, params: { access_token: token_string }
|
198
198
|
expect(response.status).to eq 401
|
199
|
-
expect(response.content_type).to
|
199
|
+
expect(response.content_type).to match(/text\/plain/)
|
200
200
|
expect(response.header["WWW-Authenticate"]).to match(/^Bearer/)
|
201
201
|
expect(response.body).to eq("Unauthorized")
|
202
202
|
end
|
@@ -246,7 +246,7 @@ describe "doorkeeper authorize filter" do
|
|
246
246
|
it "renders a custom JSON response" do
|
247
247
|
get :index, params: { access_token: token_string }
|
248
248
|
expect(response.header).to_not include("WWW-Authenticate")
|
249
|
-
expect(response.content_type).to
|
249
|
+
expect(response.content_type).to match(/application\/json/)
|
250
250
|
expect(response.status).to eq 403
|
251
251
|
|
252
252
|
expect(json_response).not_to be_nil
|
@@ -3,364 +3,469 @@
|
|
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.configuration.instance_variable_set("@confirm_application_owner", true) }
|
8
|
+
let(:unset_require_owner) { Doorkeeper.configuration.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
|
-
|
20
|
-
enable_application_owner
|
21
|
-
end
|
22
|
-
end
|
14
|
+
it "is invalid without a name" do
|
15
|
+
new_application.name = nil
|
16
|
+
expect(new_application).not_to be_valid
|
17
|
+
end
|
23
18
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
19
|
+
it "is invalid without determining confidentiality" do
|
20
|
+
new_application.confidential = nil
|
21
|
+
expect(new_application).not_to be_valid
|
22
|
+
end
|
28
23
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
33
29
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
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
|
39
35
|
|
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
|
-
|
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
|
47
|
+
|
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
|
54
|
+
|
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
|
61
|
+
|
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
|
67
|
+
|
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
|
73
|
+
|
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
|
79
|
+
|
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
|
85
|
+
|
86
|
+
context "application_owner is enabled" do
|
87
|
+
before do
|
88
|
+
Doorkeeper.configure do
|
89
|
+
orm DOORKEEPER_ORM
|
90
|
+
enable_application_owner
|
48
91
|
end
|
49
92
|
end
|
50
93
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
94
|
+
context "application owner is not required" do
|
95
|
+
before(:each) do
|
96
|
+
unset_require_owner
|
97
|
+
end
|
55
98
|
|
56
|
-
|
57
|
-
|
58
|
-
|
99
|
+
it "is valid given valid attributes" do
|
100
|
+
expect(new_application).to be_valid
|
101
|
+
end
|
59
102
|
end
|
60
103
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
104
|
+
context "application owner is required" do
|
105
|
+
before do
|
106
|
+
require_owner
|
107
|
+
@owner = FactoryBot.build_stubbed(:doorkeeper_testing_user)
|
108
|
+
end
|
66
109
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
expect(new_application.uid).not_to be_blank
|
71
|
-
end
|
110
|
+
it "is invalid without an owner" do
|
111
|
+
expect(new_application).not_to be_valid
|
112
|
+
end
|
72
113
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
114
|
+
it "is valid with an owner" do
|
115
|
+
new_application.owner = @owner
|
116
|
+
expect(new_application).to be_valid
|
117
|
+
end
|
77
118
|
end
|
119
|
+
end
|
78
120
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
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]
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
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
|
133
|
+
end
|
83
134
|
end
|
84
135
|
|
85
|
-
context "redirect URI" do
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
grant_flows %w[password client_credentials]
|
90
|
-
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]
|
91
140
|
end
|
141
|
+
end
|
92
142
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
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
|
98
147
|
end
|
148
|
+
end
|
99
149
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
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
|
105
155
|
end
|
156
|
+
end
|
106
157
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
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
|
112
162
|
end
|
163
|
+
end
|
164
|
+
end
|
113
165
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
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 }
|
170
|
+
|
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
|
121
176
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
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"
|
126
183
|
end
|
127
184
|
end
|
185
|
+
|
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
|
191
|
+
end
|
128
192
|
end
|
129
193
|
|
130
|
-
it "
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
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)
|
197
|
+
|
198
|
+
lookup = described_class.by_uid_and_secret(app.uid, app.plaintext_secret)
|
199
|
+
expect(lookup).to eq(app)
|
135
200
|
end
|
136
201
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
202
|
+
context "with fallback enabled" do
|
203
|
+
include_context "with token hashing and fallback lookup enabled"
|
204
|
+
|
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)
|
208
|
+
|
209
|
+
lookup = described_class.by_uid_and_secret(app.uid, app.plaintext_secret)
|
210
|
+
expect(lookup).to eq(app)
|
211
|
+
end
|
142
212
|
end
|
143
213
|
|
144
|
-
it "
|
145
|
-
|
146
|
-
|
147
|
-
expect(new_application.secret).not_to be_nil
|
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
|
148
217
|
end
|
218
|
+
end
|
149
219
|
|
150
|
-
|
151
|
-
|
220
|
+
describe "destroy related models on cascade" do
|
221
|
+
before(:each) do
|
152
222
|
new_application.save
|
153
|
-
expect(new_application.secret).not_to be_blank
|
154
223
|
end
|
155
224
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
225
|
+
let(:resource_owner) { FactoryBot.create(:doorkeeper_testing_user) }
|
226
|
+
|
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)
|
160
235
|
end
|
161
236
|
|
162
|
-
it "
|
163
|
-
new_application
|
164
|
-
|
165
|
-
expect
|
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)
|
166
243
|
end
|
244
|
+
end
|
167
245
|
|
168
|
-
|
169
|
-
|
170
|
-
let(:app) { FactoryBot.create :application }
|
171
|
-
let(:default_strategy) { Doorkeeper::SecretStoring::Sha256Hash }
|
246
|
+
describe "#ordered_by" do
|
247
|
+
let(:applications) { FactoryBot.create_list(:application, 5) }
|
172
248
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
expect(
|
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)
|
177
253
|
end
|
254
|
+
end
|
178
255
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
Doorkeeper.configure do
|
184
|
-
hash_application_secrets using: "Doorkeeper::SecretStoring::BCrypt"
|
185
|
-
end
|
186
|
-
end
|
187
|
-
|
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
|
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)
|
194
260
|
end
|
261
|
+
end
|
262
|
+
end
|
195
263
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
expect(lookup).to eq(app)
|
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")
|
202
269
|
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")
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
203
278
|
|
204
|
-
|
205
|
-
|
206
|
-
|
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)
|
279
|
+
describe "#authorized_for" do
|
280
|
+
let(:resource_owner) { FactoryBot.create(:doorkeeper_testing_user) }
|
281
|
+
let(:other_resource_owner) { FactoryBot.create(:doorkeeper_testing_user) }
|
210
282
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
end
|
283
|
+
it "is empty if the application is not authorized for anyone" do
|
284
|
+
expect(described_class.authorized_for(resource_owner)).to be_empty
|
285
|
+
end
|
215
286
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
287
|
+
it "returns only application for a specific resource owner" do
|
288
|
+
FactoryBot.create(
|
289
|
+
:access_token,
|
290
|
+
resource_owner_id: other_resource_owner.id,
|
291
|
+
)
|
292
|
+
token = FactoryBot.create(
|
293
|
+
:access_token,
|
294
|
+
resource_owner_id: resource_owner.id,
|
295
|
+
)
|
296
|
+
expect(described_class.authorized_for(resource_owner)).to eq([token.application])
|
220
297
|
end
|
221
298
|
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
299
|
+
it "excludes revoked tokens" do
|
300
|
+
FactoryBot.create(
|
301
|
+
:access_token,
|
302
|
+
resource_owner_id: resource_owner.id,
|
303
|
+
revoked_at: 2.days.ago,
|
304
|
+
)
|
305
|
+
expect(described_class.authorized_for(resource_owner)).to be_empty
|
306
|
+
end
|
226
307
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
308
|
+
it "returns all applications that have been authorized" do
|
309
|
+
token1 = FactoryBot.create(
|
310
|
+
:access_token,
|
311
|
+
resource_owner_id: resource_owner.id,
|
312
|
+
)
|
313
|
+
token2 = FactoryBot.create(
|
314
|
+
:access_token,
|
315
|
+
resource_owner_id: resource_owner.id,
|
316
|
+
)
|
317
|
+
expect(described_class.authorized_for(resource_owner))
|
318
|
+
.to eq([token1.application, token2.application])
|
319
|
+
end
|
231
320
|
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
321
|
+
it "returns only one application even if it has been authorized twice" do
|
322
|
+
application = FactoryBot.create(:application)
|
323
|
+
FactoryBot.create(
|
324
|
+
:access_token,
|
325
|
+
resource_owner_id: resource_owner.id,
|
326
|
+
application: application,
|
327
|
+
)
|
328
|
+
FactoryBot.create(
|
329
|
+
:access_token,
|
330
|
+
resource_owner_id: resource_owner.id,
|
331
|
+
application: application,
|
332
|
+
)
|
333
|
+
expect(described_class.authorized_for(resource_owner)).to eq([application])
|
239
334
|
end
|
335
|
+
end
|
240
336
|
|
241
|
-
|
242
|
-
|
337
|
+
describe "#revoke_tokens_and_grants_for" do
|
338
|
+
it "revokes all access tokens and access grants" do
|
339
|
+
application_id = 42
|
340
|
+
resource_owner = double
|
341
|
+
expect(Doorkeeper::AccessToken)
|
342
|
+
.to receive(:revoke_all_for).with(application_id, resource_owner)
|
343
|
+
expect(Doorkeeper::AccessGrant)
|
344
|
+
.to receive(:revoke_all_for).with(application_id, resource_owner)
|
243
345
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
expect(Application.ordered_by(:name).map(&:name)).to eq(names)
|
248
|
-
end
|
249
|
-
end
|
346
|
+
described_class.revoke_tokens_and_grants_for(application_id, resource_owner)
|
347
|
+
end
|
348
|
+
end
|
250
349
|
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
350
|
+
describe "#by_uid_and_secret" do
|
351
|
+
context "when application is private/confidential" do
|
352
|
+
it "finds the application via uid/secret" do
|
353
|
+
app = FactoryBot.create :application
|
354
|
+
authenticated = described_class.by_uid_and_secret(app.uid, app.secret)
|
355
|
+
expect(authenticated).to eq(app)
|
356
|
+
end
|
357
|
+
context "when secret is wrong" do
|
358
|
+
it "should not find the application" do
|
359
|
+
app = FactoryBot.create :application
|
360
|
+
authenticated = described_class.by_uid_and_secret(app.uid, "bad")
|
361
|
+
expect(authenticated).to eq(nil)
|
255
362
|
end
|
256
363
|
end
|
257
364
|
end
|
258
365
|
|
259
|
-
|
260
|
-
context "when
|
261
|
-
it "should
|
262
|
-
|
263
|
-
|
366
|
+
context "when application is public/non-confidential" do
|
367
|
+
context "when secret is blank" do
|
368
|
+
it "should find the application" do
|
369
|
+
app = FactoryBot.create :application, confidential: false
|
370
|
+
authenticated = described_class.by_uid_and_secret(app.uid, nil)
|
371
|
+
expect(authenticated).to eq(app)
|
264
372
|
end
|
265
373
|
end
|
266
|
-
context "when
|
267
|
-
it "should
|
268
|
-
|
269
|
-
|
374
|
+
context "when secret is wrong" do
|
375
|
+
it "should not find the application" do
|
376
|
+
app = FactoryBot.create :application, confidential: false
|
377
|
+
authenticated = described_class.by_uid_and_secret(app.uid, "bad")
|
378
|
+
expect(authenticated).to eq(nil)
|
270
379
|
end
|
271
380
|
end
|
272
381
|
end
|
382
|
+
end
|
273
383
|
|
274
|
-
|
275
|
-
|
384
|
+
describe "#confidential?" do
|
385
|
+
subject { FactoryBot.create(:application, confidential: confidential).confidential? }
|
276
386
|
|
277
|
-
|
278
|
-
|
279
|
-
|
387
|
+
context "when application is private/confidential" do
|
388
|
+
let(:confidential) { true }
|
389
|
+
it { expect(subject).to eq(true) }
|
390
|
+
end
|
280
391
|
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
392
|
+
context "when application is public/non-confidential" do
|
393
|
+
let(:confidential) { false }
|
394
|
+
it { expect(subject).to eq(false) }
|
395
|
+
end
|
396
|
+
end
|
286
397
|
|
287
|
-
|
288
|
-
|
289
|
-
expect(Application.authorized_for(resource_owner)).to be_empty
|
290
|
-
end
|
398
|
+
describe "#as_json" do
|
399
|
+
let(:app) { FactoryBot.create :application, secret: "123123123" }
|
291
400
|
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
end
|
401
|
+
before do
|
402
|
+
allow(Doorkeeper.configuration)
|
403
|
+
.to receive(:application_secret_strategy).and_return(Doorkeeper::SecretStoring::Plain)
|
404
|
+
end
|
297
405
|
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
expect(
|
406
|
+
# AR specific feature
|
407
|
+
if DOORKEEPER_ORM == :active_record
|
408
|
+
it "correctly works with #to_json" do
|
409
|
+
ActiveRecord::Base.include_root_in_json = true
|
410
|
+
expect(app.to_json(include_root_in_json: true)).to match(/application.+?:\{/)
|
411
|
+
ActiveRecord::Base.include_root_in_json = false
|
303
412
|
end
|
304
413
|
end
|
305
414
|
|
306
|
-
|
307
|
-
it "
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
.to receive(:revoke_all_for).with(application_id, resource_owner)
|
314
|
-
|
315
|
-
Application.revoke_tokens_and_grants_for(application_id, resource_owner)
|
415
|
+
context "when called without authorized resource owner" do
|
416
|
+
it "includes minimal set of attributes" do
|
417
|
+
expect(app.as_json).to match(
|
418
|
+
"id" => app.id,
|
419
|
+
"name" => app.name,
|
420
|
+
"created_at" => an_instance_of(String),
|
421
|
+
)
|
316
422
|
end
|
317
|
-
end
|
318
423
|
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
app = FactoryBot.create :application
|
329
|
-
authenticated = Application.by_uid_and_secret(app.uid, "bad")
|
330
|
-
expect(authenticated).to eq(nil)
|
331
|
-
end
|
332
|
-
end
|
424
|
+
it "includes application UID if it's public" do
|
425
|
+
app = FactoryBot.create :application, secret: "123123123", confidential: false
|
426
|
+
|
427
|
+
expect(app.as_json).to match(
|
428
|
+
"id" => app.id,
|
429
|
+
"name" => app.name,
|
430
|
+
"created_at" => an_instance_of(String),
|
431
|
+
"uid" => app.uid,
|
432
|
+
)
|
333
433
|
end
|
334
434
|
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
end
|
343
|
-
context "when secret is wrong" do
|
344
|
-
it "should not find the application" do
|
345
|
-
app = FactoryBot.create :application, confidential: false
|
346
|
-
authenticated = Application.by_uid_and_secret(app.uid, "bad")
|
347
|
-
expect(authenticated).to eq(nil)
|
348
|
-
end
|
349
|
-
end
|
435
|
+
it "respects custom options" do
|
436
|
+
expect(app.as_json(except: :id)).not_to include("id")
|
437
|
+
expect(app.as_json(only: %i[name created_at secret]))
|
438
|
+
.to match(
|
439
|
+
"name" => app.name,
|
440
|
+
"created_at" => an_instance_of(String),
|
441
|
+
)
|
350
442
|
end
|
351
443
|
end
|
352
444
|
|
353
|
-
|
354
|
-
|
445
|
+
context "when called with authorized resource owner" do
|
446
|
+
let(:owner) { FactoryBot.create(:doorkeeper_testing_user) }
|
447
|
+
let(:other_owner) { FactoryBot.create(:doorkeeper_testing_user) }
|
448
|
+
let(:app) { FactoryBot.create(:application, secret: "123123123", owner: owner) }
|
449
|
+
|
450
|
+
before do
|
451
|
+
Doorkeeper.configure do
|
452
|
+
orm DOORKEEPER_ORM
|
453
|
+
enable_application_owner confirmation: false
|
454
|
+
end
|
455
|
+
end
|
355
456
|
|
356
|
-
|
357
|
-
|
358
|
-
|
457
|
+
it "includes all the attributes" do
|
458
|
+
expect(app.as_json(current_resource_owner: owner))
|
459
|
+
.to include(
|
460
|
+
"secret" => "123123123",
|
461
|
+
"redirect_uri" => app.redirect_uri,
|
462
|
+
"uid" => app.uid,
|
463
|
+
)
|
359
464
|
end
|
360
465
|
|
361
|
-
|
362
|
-
|
363
|
-
|
466
|
+
it "doesn't include unsafe attributes if current owner isn't the same as owner" do
|
467
|
+
expect(app.as_json(current_resource_owner: other_owner))
|
468
|
+
.not_to include("redirect_uri")
|
364
469
|
end
|
365
470
|
end
|
366
471
|
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.1.
|
4
|
+
version: 5.1.1
|
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:
|
14
|
+
date: 2020-05-02 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: railties
|