devise_saml_authenticatable 1.9.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/ci.yml +10 -25
- data/.ruby-version +1 -1
- data/Gemfile +6 -12
- data/README.md +19 -13
- data/app/controllers/devise/saml_sessions_controller.rb +4 -3
- data/devise_saml_authenticatable.gemspec +3 -2
- data/lib/devise_saml_authenticatable/model.rb +3 -24
- data/lib/devise_saml_authenticatable/saml_config.rb +1 -1
- data/lib/devise_saml_authenticatable/strategy.rb +3 -1
- data/lib/devise_saml_authenticatable/version.rb +1 -1
- data/lib/devise_saml_authenticatable.rb +4 -4
- data/spec/controllers/devise/saml_sessions_controller_spec.rb +17 -14
- data/spec/devise_saml_authenticatable/model_spec.rb +30 -30
- data/spec/devise_saml_authenticatable/saml_config_spec.rb +12 -12
- data/spec/devise_saml_authenticatable/strategy_spec.rb +21 -7
- data/spec/features/saml_authentication_spec.rb +9 -2
- data/spec/rails_helper.rb +14 -1
- data/spec/spec_helper.rb +4 -1
- data/spec/support/{Gemfile.rails5.2 → Gemfile.rails7.1} +7 -3
- data/spec/support/{Gemfile.rails6 → Gemfile.rails7.2} +7 -7
- data/spec/support/Gemfile.rails8.0 +18 -0
- data/spec/support/idp_settings_adapter.rb.erb +1 -1
- data/spec/support/idp_template.rb +3 -7
- data/spec/support/rails_app.rb +6 -4
- data/spec/support/saml_idp_controller.rb.erb +5 -8
- data/spec/support/sp_template.rb +7 -15
- metadata +14 -13
- data/spec/support/Gemfile.rails6.1 +0 -24
@@ -20,7 +20,7 @@ describe Devise::Models::SamlAuthenticatable do
|
|
20
20
|
|
21
21
|
# Fake out ActiveRecord and Devise API to satisfy verifiable mocks
|
22
22
|
class << self
|
23
|
-
def
|
23
|
+
def find_by(*args); end
|
24
24
|
def logger; end
|
25
25
|
end
|
26
26
|
end
|
@@ -64,12 +64,12 @@ describe Devise::Models::SamlAuthenticatable do
|
|
64
64
|
|
65
65
|
it "looks up the user by the configured default user key" do
|
66
66
|
user = Model.new(new_record: false)
|
67
|
-
expect(Model).to receive(:
|
67
|
+
expect(Model).to receive(:find_by).with({ email: 'user@example.com' }).and_return(user)
|
68
68
|
expect(Model.authenticate_with_saml(response, nil)).to eq(user)
|
69
69
|
end
|
70
70
|
|
71
71
|
it "returns nil if it cannot find a user" do
|
72
|
-
expect(Model).to receive(:
|
72
|
+
expect(Model).to receive(:find_by).with({ email: 'user@example.com' }).and_return(nil)
|
73
73
|
expect(Model.authenticate_with_saml(response, nil)).to be_nil
|
74
74
|
end
|
75
75
|
|
@@ -83,12 +83,12 @@ describe Devise::Models::SamlAuthenticatable do
|
|
83
83
|
|
84
84
|
it "looks up the user by the configured default user key" do
|
85
85
|
user = Model.new(new_record: false)
|
86
|
-
expect(Model).to receive(:
|
86
|
+
expect(Model).to receive(:find_by).with({ email: 'user@example.com' }).and_return(user)
|
87
87
|
expect(Model.authenticate_with_saml(response, nil)).to eq(user)
|
88
88
|
end
|
89
89
|
|
90
90
|
it "returns nil if it cannot find a user" do
|
91
|
-
expect(Model).to receive(:
|
91
|
+
expect(Model).to receive(:find_by).with({ email: 'user@example.com' }).and_return(nil)
|
92
92
|
expect(Model.authenticate_with_saml(response, nil)).to be_nil
|
93
93
|
end
|
94
94
|
|
@@ -98,7 +98,7 @@ describe Devise::Models::SamlAuthenticatable do
|
|
98
98
|
end
|
99
99
|
|
100
100
|
it "creates and returns a new user with the name identifier and given attributes" do
|
101
|
-
expect(Model).to receive(:
|
101
|
+
expect(Model).to receive(:find_by).with({ email: 'user@example.com' }).and_return(nil)
|
102
102
|
model = Model.authenticate_with_saml(response, nil)
|
103
103
|
expect(model.email).to eq('user@example.com')
|
104
104
|
expect(model.name).to eq('A User')
|
@@ -114,7 +114,7 @@ describe Devise::Models::SamlAuthenticatable do
|
|
114
114
|
|
115
115
|
context "when the proc returns true" do
|
116
116
|
it "creates and returns a new user with the name identifier and given attributes" do
|
117
|
-
expect(Model).to receive(:
|
117
|
+
expect(Model).to receive(:find_by).with({ email: name_id }).and_return(nil)
|
118
118
|
model = Model.authenticate_with_saml(response, nil)
|
119
119
|
expect(model.email).to eq('user@example.com')
|
120
120
|
expect(model.name).to eq('A User')
|
@@ -126,7 +126,7 @@ describe Devise::Models::SamlAuthenticatable do
|
|
126
126
|
let(:name_id) { 'do_not_create@example.com' }
|
127
127
|
|
128
128
|
it "does not creates new user" do
|
129
|
-
expect(Model).to receive(:
|
129
|
+
expect(Model).to receive(:find_by).with({ email: name_id }).and_return(nil)
|
130
130
|
expect(Model.authenticate_with_saml(response, nil)).to be_nil
|
131
131
|
end
|
132
132
|
end
|
@@ -139,7 +139,7 @@ describe Devise::Models::SamlAuthenticatable do
|
|
139
139
|
|
140
140
|
it "creates and returns a new user with the name identifier and given attributes" do
|
141
141
|
user = Model.new(email: "old_mail@mail.com", name: "old name", new_record: false)
|
142
|
-
expect(Model).to receive(:
|
142
|
+
expect(Model).to receive(:find_by).with({ email: 'user@example.com' }).and_return(user)
|
143
143
|
model = Model.authenticate_with_saml(response, nil)
|
144
144
|
expect(model.email).to eq('user@example.com')
|
145
145
|
expect(model.name).to eq('A User')
|
@@ -157,7 +157,7 @@ describe Devise::Models::SamlAuthenticatable do
|
|
157
157
|
|
158
158
|
context "when the proc returns true" do
|
159
159
|
it "updates user with given attributes" do
|
160
|
-
expect(Model).to receive(:
|
160
|
+
expect(Model).to receive(:find_by).with({ email: name_id }).and_return(user)
|
161
161
|
model = Model.authenticate_with_saml(response, nil)
|
162
162
|
expect(model.email).to eq('user@example.com')
|
163
163
|
expect(model.name).to eq('A User')
|
@@ -169,7 +169,7 @@ describe Devise::Models::SamlAuthenticatable do
|
|
169
169
|
let(:name_id) { 'do_not_update@example.com' }
|
170
170
|
|
171
171
|
it "does not update user" do
|
172
|
-
expect(Model).to receive(:
|
172
|
+
expect(Model).to receive(:find_by).with({ email: name_id }).and_return(user)
|
173
173
|
model = Model.authenticate_with_saml(response, nil)
|
174
174
|
expect(model.email).to eq('old_mail@mail.com')
|
175
175
|
expect(model.name).to eq('old name')
|
@@ -185,7 +185,7 @@ describe Devise::Models::SamlAuthenticatable do
|
|
185
185
|
end
|
186
186
|
|
187
187
|
it "creates and returns a new user with the given attributes" do
|
188
|
-
expect(Model).to receive(:
|
188
|
+
expect(Model).to receive(:find_by).with({ email: 'user@example.com' }).and_return(nil)
|
189
189
|
model = Model.authenticate_with_saml(response, nil)
|
190
190
|
expect(model.email).to eq('user@example.com')
|
191
191
|
expect(model.name).to eq('A User')
|
@@ -204,7 +204,7 @@ describe Devise::Models::SamlAuthenticatable do
|
|
204
204
|
let(:response) { double(:response, issuers: ['to_create_idp'], attributes: attributes, name_id: name_id) }
|
205
205
|
|
206
206
|
it "creates and returns a new user with the name identifier and given attributes" do
|
207
|
-
expect(Model).to receive(:
|
207
|
+
expect(Model).to receive(:find_by).with({ email: 'user@example.com' }).and_return(nil)
|
208
208
|
model = Model.authenticate_with_saml(response, nil)
|
209
209
|
expect(model.email).to eq('user@example.com')
|
210
210
|
expect(model.name).to eq('A User')
|
@@ -216,7 +216,7 @@ describe Devise::Models::SamlAuthenticatable do
|
|
216
216
|
let(:response) { double(:response, issuers: ['do_not_create_idp'], attributes: attributes, name_id: name_id) }
|
217
217
|
|
218
218
|
it "does not creates new user" do
|
219
|
-
expect(Model).to receive(:
|
219
|
+
expect(Model).to receive(:find_by).with({ email: 'user@example.com' }).and_return(nil)
|
220
220
|
expect(Model.authenticate_with_saml(response, nil)).to be_nil
|
221
221
|
end
|
222
222
|
end
|
@@ -228,13 +228,13 @@ describe Devise::Models::SamlAuthenticatable do
|
|
228
228
|
end
|
229
229
|
|
230
230
|
it "returns nil if the user is not found" do
|
231
|
-
expect(Model).to receive(:
|
231
|
+
expect(Model).to receive(:find_by).with({ email: 'user@example.com' }).and_return(nil)
|
232
232
|
expect(Model.authenticate_with_saml(response, nil)).to be_nil
|
233
233
|
end
|
234
234
|
|
235
235
|
it "updates the attributes if the user is found" do
|
236
236
|
user = Model.new(email: "old_mail@mail.com", name: "old name", new_record: false)
|
237
|
-
expect(Model).to receive(:
|
237
|
+
expect(Model).to receive(:find_by).with({ email: 'user@example.com' }).and_return(user)
|
238
238
|
model = Model.authenticate_with_saml(response, nil)
|
239
239
|
expect(model.email).to eq('user@example.com')
|
240
240
|
expect(model.name).to eq('A User')
|
@@ -254,7 +254,7 @@ describe Devise::Models::SamlAuthenticatable do
|
|
254
254
|
let(:response) { double(:response, issuers: ['to_update_idp'], attributes: attributes, name_id: name_id) }
|
255
255
|
|
256
256
|
it "updates user with given attributes" do
|
257
|
-
expect(Model).to receive(:
|
257
|
+
expect(Model).to receive(:find_by).with({ email: 'user@example.com' }).and_return(user)
|
258
258
|
model = Model.authenticate_with_saml(response, nil)
|
259
259
|
expect(model.email).to eq('user@example.com')
|
260
260
|
expect(model.name).to eq('A User')
|
@@ -266,7 +266,7 @@ describe Devise::Models::SamlAuthenticatable do
|
|
266
266
|
let(:response) { double(:response, issuers: ['do_not_update_idp'], attributes: attributes, name_id: name_id) }
|
267
267
|
|
268
268
|
it "does not update user" do
|
269
|
-
expect(Model).to receive(:
|
269
|
+
expect(Model).to receive(:find_by).with({ email: 'user@example.com' }).and_return(user)
|
270
270
|
model = Model.authenticate_with_saml(response, nil)
|
271
271
|
expect(model.email).to eq('old_mail@mail.com')
|
272
272
|
expect(model.name).to eq('old name')
|
@@ -282,7 +282,7 @@ describe Devise::Models::SamlAuthenticatable do
|
|
282
282
|
|
283
283
|
it "looks up the user with a downcased value" do
|
284
284
|
user = Model.new(new_record: false)
|
285
|
-
expect(Model).to receive(:
|
285
|
+
expect(Model).to receive(:find_by).with({ email: 'upper@example.com' }).and_return(user)
|
286
286
|
expect(Model.authenticate_with_saml(response, nil)).to eq(user)
|
287
287
|
end
|
288
288
|
end
|
@@ -320,7 +320,7 @@ describe Devise::Models::SamlAuthenticatable do
|
|
320
320
|
end
|
321
321
|
|
322
322
|
it "returns the user" do
|
323
|
-
expect(Model).to receive(:
|
323
|
+
expect(Model).to receive(:find_by).with({ email: 'user@example.com' }).and_return(user)
|
324
324
|
expect(Model.authenticate_with_saml(response, nil)).to eq(user)
|
325
325
|
end
|
326
326
|
end
|
@@ -331,7 +331,7 @@ describe Devise::Models::SamlAuthenticatable do
|
|
331
331
|
end
|
332
332
|
|
333
333
|
it "returns nil" do
|
334
|
-
expect(Model).to receive(:
|
334
|
+
expect(Model).to receive(:find_by).with({ email: 'user@example.com' }).and_return(user)
|
335
335
|
expect(Model.authenticate_with_saml(response, nil)).to be_nil
|
336
336
|
end
|
337
337
|
end
|
@@ -354,7 +354,7 @@ describe Devise::Models::SamlAuthenticatable do
|
|
354
354
|
end
|
355
355
|
|
356
356
|
it "returns the user" do
|
357
|
-
expect(Model).to receive(:
|
357
|
+
expect(Model).to receive(:find_by).with({ email: 'user@example.com' }).and_return(user)
|
358
358
|
expect(Model.authenticate_with_saml(response, nil)).to eq(user)
|
359
359
|
end
|
360
360
|
end
|
@@ -365,7 +365,7 @@ describe Devise::Models::SamlAuthenticatable do
|
|
365
365
|
end
|
366
366
|
|
367
367
|
it "returns nil" do
|
368
|
-
expect(Model).to receive(:
|
368
|
+
expect(Model).to receive(:find_by).with({ email: 'user@example.com' }).and_return(user)
|
369
369
|
expect(Model.authenticate_with_saml(response, nil)).to be_nil
|
370
370
|
end
|
371
371
|
end
|
@@ -412,7 +412,7 @@ describe Devise::Models::SamlAuthenticatable do
|
|
412
412
|
end
|
413
413
|
|
414
414
|
def configure_hook(&block)
|
415
|
-
allow(Model).to receive(:
|
415
|
+
allow(Model).to receive(:find_by).with({ email: 'user@example.com' }).and_return(nil)
|
416
416
|
allow(Devise).to receive(:saml_default_user_key).and_return(:email)
|
417
417
|
allow(Devise).to receive(:saml_create_user).and_return(true)
|
418
418
|
allow(Devise).to receive(:saml_update_resource_hook).and_return(block)
|
@@ -423,7 +423,7 @@ describe Devise::Models::SamlAuthenticatable do
|
|
423
423
|
let(:name_id) { 'SomeUsername' }
|
424
424
|
|
425
425
|
it "can replicate the default behaviour for a new user in a custom locator" do
|
426
|
-
allow(Model).to receive(:
|
426
|
+
allow(Model).to receive(:find_by).with({ email: attributes['saml-email-format'] }).and_return(nil)
|
427
427
|
|
428
428
|
configure_hook do |model, saml_response, auth_value|
|
429
429
|
Devise.saml_default_resource_locator.call(model, saml_response, auth_value)
|
@@ -439,7 +439,7 @@ describe Devise::Models::SamlAuthenticatable do
|
|
439
439
|
user = Model.new(email: attributes['saml-email-format'], name: attributes['saml-name-format'])
|
440
440
|
user.save!
|
441
441
|
|
442
|
-
allow(Model).to receive(:
|
442
|
+
allow(Model).to receive(:find_by).with({ email: attributes['saml-email-format'] }).and_return(user)
|
443
443
|
|
444
444
|
configure_hook do |model, saml_response, auth_value|
|
445
445
|
Devise.saml_default_resource_locator.call(model, saml_response, auth_value)
|
@@ -453,11 +453,11 @@ describe Devise::Models::SamlAuthenticatable do
|
|
453
453
|
end
|
454
454
|
|
455
455
|
it "can change the default behaviour for a new user from the saml response" do
|
456
|
-
allow(Model).to receive(:
|
456
|
+
allow(Model).to receive(:find_by).with({ foo: attributes['saml-email-format'], bar: name_id }).and_return(nil)
|
457
457
|
|
458
458
|
configure_hook do |model, saml_response, auth_value|
|
459
459
|
name_id = saml_response.raw_response.name_id
|
460
|
-
model.
|
460
|
+
model.find_by(foo: auth_value, bar: name_id)
|
461
461
|
end
|
462
462
|
|
463
463
|
new_user = Model.authenticate_with_saml(response, nil)
|
@@ -470,11 +470,11 @@ describe Devise::Models::SamlAuthenticatable do
|
|
470
470
|
user = Model.new(email: attributes['saml-email-format'], name: attributes['saml-name-format'])
|
471
471
|
user.save!
|
472
472
|
|
473
|
-
allow(Model).to receive(:
|
473
|
+
allow(Model).to receive(:find_by).with({ foo: attributes['saml-email-format'], bar: name_id }).and_return(user)
|
474
474
|
|
475
475
|
configure_hook do |model, saml_response, auth_value|
|
476
476
|
name_id = saml_response.raw_response.name_id
|
477
|
-
model.
|
477
|
+
model.find_by(foo: auth_value, bar: name_id)
|
478
478
|
end
|
479
479
|
|
480
480
|
new_user = Model.authenticate_with_saml(response, nil)
|
@@ -10,7 +10,7 @@ describe DeviseSamlAuthenticatable::SamlConfig do
|
|
10
10
|
context "when config/idp.yml does not exist" do
|
11
11
|
before do
|
12
12
|
allow(Rails).to receive(:root).and_return("/railsroot")
|
13
|
-
allow(File).to receive(:
|
13
|
+
allow(File).to receive(:exist?).with("/railsroot/config/idp.yml").and_return(false)
|
14
14
|
end
|
15
15
|
|
16
16
|
it "is the global devise SAML config" do
|
@@ -33,13 +33,13 @@ describe DeviseSamlAuthenticatable::SamlConfig do
|
|
33
33
|
|
34
34
|
def self.settings(idp_entity_id)
|
35
35
|
#some hash of stuff (by doing a fetch, in our case, but could also be a giant hash keyed by idp_entity_id)
|
36
|
-
if idp_entity_id == "
|
36
|
+
if idp_entity_id == "https://www.example.com"
|
37
37
|
base = {
|
38
38
|
assertion_consumer_service_url: "acs_url",
|
39
39
|
assertion_consumer_service_binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
|
40
40
|
name_identifier_format: "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
|
41
|
-
|
42
|
-
idp_entity_id: "
|
41
|
+
sp_entity_id: "sp_issuer",
|
42
|
+
idp_entity_id: "https://www.example.com",
|
43
43
|
authn_context: "",
|
44
44
|
idp_cert: "idp_cert"
|
45
45
|
}
|
@@ -55,13 +55,13 @@ describe DeviseSamlAuthenticatable::SamlConfig do
|
|
55
55
|
)
|
56
56
|
})
|
57
57
|
base
|
58
|
-
elsif idp_entity_id == "
|
58
|
+
elsif idp_entity_id == "https://www.example.com_other"
|
59
59
|
base = {
|
60
60
|
assertion_consumer_service_url: "acs_url_other",
|
61
61
|
assertion_consumer_service_binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST_other",
|
62
62
|
name_identifier_format: "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress_other",
|
63
|
-
|
64
|
-
idp_entity_id: "
|
63
|
+
sp_entity_id: "sp_issuer_other",
|
64
|
+
idp_entity_id: "https://www.example.com_other",
|
65
65
|
authn_context: "_other",
|
66
66
|
idp_cert: "idp_cert_other"
|
67
67
|
}
|
@@ -85,7 +85,7 @@ describe DeviseSamlAuthenticatable::SamlConfig do
|
|
85
85
|
}
|
86
86
|
|
87
87
|
context "when a specific idp_entity_id is requested" do
|
88
|
-
let(:idp_entity_id) { "
|
88
|
+
let(:idp_entity_id) { "https://www.example.com" }
|
89
89
|
it "uses the settings from the adapter for that idp" do
|
90
90
|
expect(saml_config.idp_entity_id).to eq (idp_entity_id)
|
91
91
|
with_ruby_saml_1_12_or_greater(proc {
|
@@ -98,7 +98,7 @@ describe DeviseSamlAuthenticatable::SamlConfig do
|
|
98
98
|
end
|
99
99
|
|
100
100
|
context "when another idp_entity_id is requested" do
|
101
|
-
let(:idp_entity_id) { "
|
101
|
+
let(:idp_entity_id) { "https://www.example.com_other" }
|
102
102
|
it "returns the other idp settings" do
|
103
103
|
expect(saml_config.idp_entity_id).to eq (idp_entity_id)
|
104
104
|
with_ruby_saml_1_12_or_greater(proc {
|
@@ -134,7 +134,7 @@ environment:
|
|
134
134
|
idp_cert_fingerprint: idp_cert_fingerprint
|
135
135
|
idp_cert_fingerprint_algorithm: idp_cert_fingerprint_algorithm
|
136
136
|
idp_entity_id: idp_entity_id
|
137
|
-
|
137
|
+
sp_entity_id: issuer
|
138
138
|
name_identifier_format: name_identifier_format
|
139
139
|
name_identifier_value: name_identifier_value
|
140
140
|
passive: passive
|
@@ -156,7 +156,7 @@ TARGET_URLS
|
|
156
156
|
before do
|
157
157
|
allow(Rails).to receive(:env).and_return("environment")
|
158
158
|
allow(Rails).to receive(:root).and_return("/railsroot")
|
159
|
-
allow(File).to receive(:
|
159
|
+
allow(File).to receive(:exist?).with("/railsroot/config/idp.yml").and_return(true)
|
160
160
|
allow(File).to receive(:read).with("/railsroot/config/idp.yml").and_return(idp_yaml)
|
161
161
|
end
|
162
162
|
|
@@ -185,7 +185,7 @@ TARGET_URLS
|
|
185
185
|
expect(saml_config.idp_slo_target_url).to eq('idp_slo_service_url')
|
186
186
|
expect(saml_config.idp_sso_target_url).to eq('idp_sso_service_url')
|
187
187
|
})
|
188
|
-
expect(saml_config.
|
188
|
+
expect(saml_config.sp_entity_id).to eq('issuer')
|
189
189
|
expect(saml_config.name_identifier_format).to eq('name_identifier_format')
|
190
190
|
expect(saml_config.name_identifier_value).to eq('name_identifier_value')
|
191
191
|
expect(saml_config.passive).to eq('passive')
|
@@ -23,8 +23,10 @@ describe Devise::Strategies::SamlAuthenticatable do
|
|
23
23
|
end
|
24
24
|
|
25
25
|
let(:params) { {} }
|
26
|
+
let(:session) { {} }
|
26
27
|
before do
|
27
28
|
allow(strategy).to receive(:params).and_return(params)
|
29
|
+
allow_any_instance_of(ActionDispatch::Request).to receive(:session).and_return(session)
|
28
30
|
end
|
29
31
|
|
30
32
|
context "with a login SAMLResponse parameter" do
|
@@ -37,10 +39,25 @@ describe Devise::Strategies::SamlAuthenticatable do
|
|
37
39
|
it "authenticates with the response" do
|
38
40
|
expect(OneLogin::RubySaml::Response).to receive(:new).with(params[:SAMLResponse], anything)
|
39
41
|
expect(user_class).to receive(:authenticate_with_saml).with(response, nil)
|
40
|
-
expect(user).to receive(:after_saml_authentication).with(response.sessionindex)
|
41
42
|
|
42
43
|
expect(strategy).to receive(:success!).with(user)
|
43
44
|
strategy.authenticate!
|
45
|
+
expect(session).to eq(Devise.saml_session_index_key => response.sessionindex)
|
46
|
+
end
|
47
|
+
|
48
|
+
context "when saml_session_index_key is not configured" do
|
49
|
+
before do
|
50
|
+
Devise.saml_session_index_key = nil
|
51
|
+
end
|
52
|
+
|
53
|
+
it "authenticates with the response" do
|
54
|
+
expect(OneLogin::RubySaml::Response).to receive(:new).with(params[:SAMLResponse], anything)
|
55
|
+
expect(user_class).to receive(:authenticate_with_saml).with(response, nil)
|
56
|
+
|
57
|
+
expect(strategy).to receive(:success!).with(user)
|
58
|
+
strategy.authenticate!
|
59
|
+
expect(session).to eq({})
|
60
|
+
end
|
44
61
|
end
|
45
62
|
|
46
63
|
context "and a RelayState parameter" do
|
@@ -61,8 +78,8 @@ describe Devise::Strategies::SamlAuthenticatable do
|
|
61
78
|
assertion_consumer_service_url: "acs url",
|
62
79
|
assertion_consumer_service_binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
|
63
80
|
name_identifier_format: "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
|
64
|
-
|
65
|
-
idp_entity_id: "
|
81
|
+
sp_entity_id: "sp_issuer",
|
82
|
+
idp_entity_id: "https://www.example.com",
|
66
83
|
authn_context: "",
|
67
84
|
idp_cert: "idp_cert"
|
68
85
|
}
|
@@ -95,10 +112,10 @@ describe Devise::Strategies::SamlAuthenticatable do
|
|
95
112
|
expect(OneLogin::RubySaml::Response).to receive(:new).with(params[:SAMLResponse], anything)
|
96
113
|
expect(idp_providers_adapter).to receive(:settings).with(idp_entity_id, anything)
|
97
114
|
expect(user_class).to receive(:authenticate_with_saml).with(response, params[:RelayState])
|
98
|
-
expect(user).to receive(:after_saml_authentication).with(response.sessionindex)
|
99
115
|
|
100
116
|
expect(strategy).to receive(:success!).with(user)
|
101
117
|
strategy.authenticate!
|
118
|
+
expect(session).to eq(Devise.saml_session_index_key => response.sessionindex)
|
102
119
|
end
|
103
120
|
end
|
104
121
|
|
@@ -173,7 +190,6 @@ describe Devise::Strategies::SamlAuthenticatable do
|
|
173
190
|
|
174
191
|
before do
|
175
192
|
allow(Devise).to receive(:saml_validate_in_response_to).and_return(true)
|
176
|
-
allow_any_instance_of(ActionDispatch::Request).to receive(:session).and_return(session)
|
177
193
|
end
|
178
194
|
|
179
195
|
context "when the session has a saml_transaction_id" do
|
@@ -193,8 +209,6 @@ describe Devise::Strategies::SamlAuthenticatable do
|
|
193
209
|
end
|
194
210
|
|
195
211
|
context "when the session is missing a saml_transaction_id" do
|
196
|
-
let(:session) { { } }
|
197
|
-
|
198
212
|
it "uses 'ID_MISSING' for matches_request_id so validation will fail" do
|
199
213
|
expect(OneLogin::RubySaml::Response).to receive(:new).with(params[:SAMLResponse], hash_including(matches_request_id: "ID_MISSING"))
|
200
214
|
strategy.authenticate!
|
@@ -10,11 +10,18 @@ Capybara.register_driver :chrome do |app|
|
|
10
10
|
options.add_argument('--headless')
|
11
11
|
options.add_argument('--allow-insecure-localhost')
|
12
12
|
options.add_argument('--ignore-certificate-errors')
|
13
|
+
# Headless Chrome 134 is failing randomly - optimizing here to try and avoid it
|
14
|
+
options.add_argument('--disable-background-timer-throttling')
|
15
|
+
options.add_argument('--disable-backgrounding-occluded-windows')
|
16
|
+
options.add_argument('--disable-renderer-backgrounding')
|
17
|
+
options.add_argument('--no-sandbox')
|
18
|
+
options.add_argument('--password-store=basic');
|
19
|
+
options.add_argument('--suppress-message-center-popups');
|
13
20
|
|
14
21
|
Capybara::Selenium::Driver.new(
|
15
22
|
app,
|
16
23
|
browser: :chrome,
|
17
|
-
|
24
|
+
options: options,
|
18
25
|
)
|
19
26
|
end
|
20
27
|
Capybara.default_driver = :chrome
|
@@ -202,7 +209,7 @@ describe "SAML Authentication", type: :feature do
|
|
202
209
|
fill_in "Password", with: "asdf"
|
203
210
|
click_on "Sign in"
|
204
211
|
expect(page).to have_content(:all, "Example Domain This domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.")
|
205
|
-
expect(current_url).to eq("
|
212
|
+
expect(current_url).to eq("https://www.example.com/")
|
206
213
|
end
|
207
214
|
end
|
208
215
|
end
|
data/spec/rails_helper.rb
CHANGED
@@ -6,12 +6,25 @@ create_app('sp', 'USE_SUBJECT_TO_AUTHENTICATE' => "false")
|
|
6
6
|
require "#{working_directory}/sp/config/environment"
|
7
7
|
require 'rspec/rails'
|
8
8
|
|
9
|
+
# Starting from Rails 8.0, routes are lazy-loaded by default in test and development environments.
|
10
|
+
# However, Devise's mappings are built during the routes loading phase.
|
11
|
+
# To ensure it works correctly, we need to load the routes first before accessing @@mappings.
|
12
|
+
if defined?(Rails) && Gem::Version.new(Rails.version) > Gem::Version.new('7.2')
|
13
|
+
require 'devise'
|
14
|
+
module Devise
|
15
|
+
def self.mappings
|
16
|
+
Rails.application.try(:reload_routes_unless_loaded)
|
17
|
+
@@mappings
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
9
22
|
ActiveRecord::Migration.verbose = false
|
10
23
|
ActiveRecord::Base.logger = Logger.new(nil)
|
11
24
|
if ActiveRecord::Base.connection.respond_to?(:migration_context)
|
12
25
|
ActiveRecord::Base.connection.migration_context.migrate
|
13
26
|
else
|
14
|
-
ActiveRecord::
|
27
|
+
ActiveRecord::MigrationContext.new("#{working_directory}/sp/db/migrate/").migrate
|
15
28
|
end
|
16
29
|
|
17
30
|
RSpec.configure do |config|
|
data/spec/spec_helper.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
1
|
require "fileutils"
|
2
|
+
require 'capybara/rspec'
|
2
3
|
|
3
4
|
RSpec.configure do |config|
|
4
5
|
config.run_all_when_everything_filtered = true
|
5
6
|
config.filter_run :focus
|
6
7
|
config.order = 'random'
|
8
|
+
# Specify a longer timeout for Capybara to allow for slower CI environments causing failing tests
|
9
|
+
Capybara.default_max_wait_time = 10 #
|
7
10
|
|
8
11
|
config.expect_with :rspec do |expectations|
|
9
12
|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
@@ -37,6 +40,6 @@ RSpec.configure do |config|
|
|
37
40
|
end
|
38
41
|
|
39
42
|
require 'support/rails_app'
|
40
|
-
|
43
|
+
require 'logger'
|
41
44
|
require "action_controller" # https://github.com/heartcombo/responders/pull/95
|
42
45
|
require 'devise_saml_authenticatable'
|
@@ -6,9 +6,13 @@ gemspec path: '../..'
|
|
6
6
|
group :test do
|
7
7
|
gem 'rake'
|
8
8
|
gem 'rspec', '~> 3.0'
|
9
|
-
gem 'rails', '~>
|
10
|
-
gem 'rspec-rails', '~>
|
11
|
-
gem 'sqlite3', '~>
|
9
|
+
gem 'rails', '~> 7.1.0'
|
10
|
+
gem 'rspec-rails', '~> 7.1'
|
11
|
+
gem 'sqlite3', '~> 2.6.0'
|
12
12
|
gem 'capybara'
|
13
13
|
gem 'selenium-webdriver'
|
14
|
+
gem 'webrick'
|
15
|
+
gem 'net-smtp', require: false
|
16
|
+
gem 'net-imap', require: false
|
17
|
+
gem 'net-pop', require: false
|
14
18
|
end
|
@@ -6,13 +6,13 @@ gemspec path: '../..'
|
|
6
6
|
group :test do
|
7
7
|
gem 'rake'
|
8
8
|
gem 'rspec', '~> 3.0'
|
9
|
-
gem 'rails', '~>
|
10
|
-
gem 'rspec-rails', '~>
|
11
|
-
gem 'sqlite3', '~>
|
9
|
+
gem 'rails', '~> 7.2.0'
|
10
|
+
gem 'rspec-rails', '~> 7.1'
|
11
|
+
gem 'sqlite3', '~> 2.6.0'
|
12
12
|
gem 'capybara'
|
13
13
|
gem 'selenium-webdriver'
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
gem 'webrick'
|
15
|
+
gem 'net-smtp', require: false
|
16
|
+
gem 'net-imap', require: false
|
17
|
+
gem 'net-pop', require: false
|
18
18
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in devise_saml_authenticatable.gemspec
|
4
|
+
gemspec path: '../..'
|
5
|
+
|
6
|
+
group :test do
|
7
|
+
gem 'rake'
|
8
|
+
gem 'rspec', '~> 3.0'
|
9
|
+
gem 'rails', '~> 8.0.0'
|
10
|
+
gem 'rspec-rails', '~> 7.1'
|
11
|
+
gem 'sqlite3', '~> 2.6.0'
|
12
|
+
gem 'capybara'
|
13
|
+
gem 'selenium-webdriver'
|
14
|
+
gem 'webrick'
|
15
|
+
gem 'net-smtp', require: false
|
16
|
+
gem 'net-imap', require: false
|
17
|
+
gem 'net-pop', require: false
|
18
|
+
end
|
@@ -5,7 +5,7 @@ class IdpSettingsAdapter
|
|
5
5
|
assertion_consumer_service_url: "http://localhost:8020/users/saml/auth",
|
6
6
|
assertion_consumer_service_binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
|
7
7
|
name_identifier_format: "urn:oasis:names:tc:SAML:2.0:nameid-format:transient",
|
8
|
-
|
8
|
+
sp_entity_id: "sp_issuer",
|
9
9
|
idp_entity_id: "http://localhost:8020/saml/metadata",
|
10
10
|
authn_context: "",
|
11
11
|
idp_cert_fingerprint: "9E:65:2E:03:06:8D:80:F2:86:C7:6C:77:A1:D9:14:97:0A:4D:F4:4D"
|
@@ -6,13 +6,9 @@
|
|
6
6
|
@valid_destination = ENV.fetch('VALID_DESTINATION', "true")
|
7
7
|
|
8
8
|
gem 'stub_saml_idp'
|
9
|
-
gem '
|
10
|
-
|
11
|
-
|
12
|
-
gem 'net-smtp', require: false
|
13
|
-
gem 'net-imap', require: false
|
14
|
-
gem 'net-pop', require: false
|
15
|
-
end
|
9
|
+
gem 'net-smtp', require: false
|
10
|
+
gem 'net-imap', require: false
|
11
|
+
gem 'net-pop', require: false
|
16
12
|
|
17
13
|
route "get '/saml/auth' => 'saml_idp#new'"
|
18
14
|
route "post '/saml/auth' => 'saml_idp#create'"
|
data/spec/support/rails_app.rb
CHANGED
@@ -2,6 +2,8 @@ require "open3"
|
|
2
2
|
require "socket"
|
3
3
|
require "tempfile"
|
4
4
|
require "timeout"
|
5
|
+
require "bundler/setup"
|
6
|
+
require "bundler/gem_tasks"
|
5
7
|
|
6
8
|
APP_READY_TIMEOUT ||= 30
|
7
9
|
|
@@ -36,7 +38,7 @@ def start_app(name, port, options = {})
|
|
36
38
|
|
37
39
|
with_clean_env do
|
38
40
|
Dir.chdir(app_dir(name)) do
|
39
|
-
pid = Process.spawn(app_env(name), "bundle exec rails server -p #{port} -e
|
41
|
+
pid = Process.spawn(app_env(name), "bundle exec rails server -p #{port} -e test", chdir: app_dir(name), out: "log/#{name}.log", err: "log/#{name}.err.log")
|
40
42
|
begin
|
41
43
|
Timeout.timeout(APP_READY_TIMEOUT) do
|
42
44
|
sleep 1 until app_ready?(pid, port)
|
@@ -75,9 +77,9 @@ def stop_app(name, pid)
|
|
75
77
|
warn "=== [#{name}] stderr"
|
76
78
|
warn File.read("log/#{name}.err.log")
|
77
79
|
end
|
78
|
-
if File.exist?("log/
|
80
|
+
if File.exist?("log/test.log")
|
79
81
|
puts "=== [#{name}] Rails logs"
|
80
|
-
puts File.read("log/
|
82
|
+
puts File.read("log/test.log")
|
81
83
|
end
|
82
84
|
end
|
83
85
|
end
|
@@ -121,7 +123,7 @@ def app_dir(name)
|
|
121
123
|
end
|
122
124
|
|
123
125
|
def app_env(name)
|
124
|
-
{"BUNDLE_GEMFILE" => File.join(app_dir(name), "Gemfile"), "RAILS_ENV" => "
|
126
|
+
{"BUNDLE_GEMFILE" => File.join(app_dir(name), "Gemfile"), "RAILS_ENV" => "test"}
|
125
127
|
end
|
126
128
|
|
127
129
|
def working_directory
|