devise_saml_authenticatable 1.3.1 → 1.6.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 +5 -5
- data/.gitignore +0 -2
- data/.travis.yml +37 -22
- data/Gemfile +2 -10
- data/README.md +127 -44
- data/app/controllers/devise/saml_sessions_controller.rb +38 -7
- data/devise_saml_authenticatable.gemspec +2 -1
- data/lib/devise_saml_authenticatable.rb +70 -0
- data/lib/devise_saml_authenticatable/default_attribute_map_resolver.rb +26 -0
- data/lib/devise_saml_authenticatable/default_idp_entity_id_reader.rb +10 -2
- data/lib/devise_saml_authenticatable/exception.rb +1 -1
- data/lib/devise_saml_authenticatable/model.rb +20 -32
- data/lib/devise_saml_authenticatable/routes.rb +17 -6
- data/lib/devise_saml_authenticatable/saml_mapped_attributes.rb +38 -0
- data/lib/devise_saml_authenticatable/saml_response.rb +16 -0
- data/lib/devise_saml_authenticatable/strategy.rb +10 -2
- data/lib/devise_saml_authenticatable/version.rb +1 -1
- data/spec/controllers/devise/saml_sessions_controller_spec.rb +118 -11
- data/spec/devise_saml_authenticatable/default_attribute_map_resolver_spec.rb +58 -0
- data/spec/devise_saml_authenticatable/default_idp_entity_id_reader_spec.rb +34 -4
- data/spec/devise_saml_authenticatable/model_spec.rb +199 -5
- data/spec/devise_saml_authenticatable/saml_mapped_attributes_spec.rb +50 -0
- data/spec/devise_saml_authenticatable/strategy_spec.rb +18 -0
- data/spec/features/saml_authentication_spec.rb +45 -21
- data/spec/rails_helper.rb +6 -2
- data/spec/routes/routes_spec.rb +102 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/support/Gemfile.rails4 +24 -6
- data/spec/support/Gemfile.rails5 +25 -0
- data/spec/support/Gemfile.rails5.1 +25 -0
- data/spec/support/Gemfile.rails5.2 +25 -0
- data/spec/support/attribute-map.yml +12 -0
- data/spec/support/attribute_map_resolver.rb.erb +14 -0
- data/spec/support/idp_settings_adapter.rb.erb +5 -5
- data/spec/support/idp_template.rb +8 -1
- data/spec/support/rails_app.rb +110 -16
- data/spec/support/saml_idp_controller.rb.erb +22 -10
- data/spec/support/sp_template.rb +52 -21
- metadata +26 -10
- data/spec/support/Gemfile.ruby-saml-1.3 +0 -23
@@ -0,0 +1,58 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "devise_saml_authenticatable/default_attribute_map_resolver"
|
3
|
+
|
4
|
+
describe DeviseSamlAuthenticatable::DefaultAttributeMapResolver do
|
5
|
+
let!(:rails) { class_double("Rails", env: "test", logger: logger, root: rails_root).as_stubbed_const }
|
6
|
+
let(:logger) { instance_double("Logger", info: nil) }
|
7
|
+
let(:rails_root) { Pathname.new("tmp") }
|
8
|
+
|
9
|
+
let(:saml_response) { instance_double("OneLogin::RubySaml::Response") }
|
10
|
+
let(:file_contents) {
|
11
|
+
<<YAML
|
12
|
+
---
|
13
|
+
firstname: first_name
|
14
|
+
lastname: last_name
|
15
|
+
YAML
|
16
|
+
}
|
17
|
+
before do
|
18
|
+
allow(File).to receive(:exist?).and_return(true)
|
19
|
+
allow(File).to receive(:read).and_return(file_contents)
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#attribute_map" do
|
23
|
+
it "reads the attribute map from the config file" do
|
24
|
+
expect(described_class.new(saml_response).attribute_map).to eq(
|
25
|
+
"firstname" => "first_name",
|
26
|
+
"lastname" => "last_name",
|
27
|
+
)
|
28
|
+
expect(File).to have_received(:read).with(Pathname.new("tmp").join("config", "attribute-map.yml"))
|
29
|
+
end
|
30
|
+
|
31
|
+
context "when the attribute map is broken down by environment" do
|
32
|
+
let(:file_contents) {
|
33
|
+
<<YAML
|
34
|
+
---
|
35
|
+
test:
|
36
|
+
first: first_name
|
37
|
+
last: last_name
|
38
|
+
YAML
|
39
|
+
}
|
40
|
+
it "reads the attribute map from the environment key" do
|
41
|
+
expect(described_class.new(saml_response).attribute_map).to eq(
|
42
|
+
"first" => "first_name",
|
43
|
+
"last" => "last_name",
|
44
|
+
)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "when the config file does not exist" do
|
49
|
+
before do
|
50
|
+
allow(File).to receive(:exist?).and_return(false)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "is an empty hash" do
|
54
|
+
expect(described_class.new(saml_response).attribute_map).to eq({})
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -5,18 +5,48 @@ describe DeviseSamlAuthenticatable::DefaultIdpEntityIdReader do
|
|
5
5
|
context "when there is a SAMLRequest in the params" do
|
6
6
|
let(:params) { {SAMLRequest: "logout request"} }
|
7
7
|
let(:slo_logout_request) { double('slo_logout_request', issuer: 'meow')}
|
8
|
+
before do
|
9
|
+
allow(OneLogin::RubySaml::SloLogoutrequest).to receive(:new).and_return(slo_logout_request)
|
10
|
+
end
|
11
|
+
|
8
12
|
it "uses an OneLogin::RubySaml::SloLogoutrequest to get the idp_entity_id" do
|
9
|
-
expect(OneLogin::RubySaml::SloLogoutrequest).to receive(:new).
|
10
|
-
described_class.entity_id(params)
|
13
|
+
expect(OneLogin::RubySaml::SloLogoutrequest).to receive(:new).with("logout request", hash_including)
|
14
|
+
expect(described_class.entity_id(params)).to eq("meow")
|
15
|
+
end
|
16
|
+
|
17
|
+
context "and allowed_clock_drift is configured" do
|
18
|
+
before do
|
19
|
+
allow(Devise).to receive(:allowed_clock_drift_in_seconds).and_return(30)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "allows the configured clock drift" do
|
23
|
+
expect(OneLogin::RubySaml::SloLogoutrequest).to receive(:new).with("logout request", hash_including(allowed_clock_drift: 30))
|
24
|
+
expect(described_class.entity_id(params)).to eq("meow")
|
25
|
+
end
|
11
26
|
end
|
12
27
|
end
|
13
28
|
|
14
29
|
context "when there is a SAMLResponse in the params" do
|
15
30
|
let(:params) { {SAMLResponse: "auth response"} }
|
16
31
|
let(:response) { double('response', issuers: ['meow'] )}
|
32
|
+
before do
|
33
|
+
allow(OneLogin::RubySaml::Response).to receive(:new).and_return(response)
|
34
|
+
end
|
35
|
+
|
17
36
|
it "uses an OneLogin::RubySaml::Response to get the idp_entity_id" do
|
18
|
-
expect(OneLogin::RubySaml::Response).to receive(:new).
|
19
|
-
described_class.entity_id(params)
|
37
|
+
expect(OneLogin::RubySaml::Response).to receive(:new).with("auth response", hash_including)
|
38
|
+
expect(described_class.entity_id(params)).to eq("meow")
|
39
|
+
end
|
40
|
+
|
41
|
+
context "and allowed_clock_drift is configured" do
|
42
|
+
before do
|
43
|
+
allow(Devise).to receive(:allowed_clock_drift_in_seconds).and_return(30)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "allows the configured clock drift" do
|
47
|
+
expect(OneLogin::RubySaml::Response).to receive(:new).with("auth response", hash_including(allowed_clock_drift: 30))
|
48
|
+
expect(described_class.entity_id(params)).to eq("meow")
|
49
|
+
end
|
20
50
|
end
|
21
51
|
end
|
22
52
|
end
|
@@ -32,6 +32,7 @@ describe Devise::Models::SamlAuthenticatable do
|
|
32
32
|
end
|
33
33
|
|
34
34
|
before do
|
35
|
+
allow(Devise).to receive(:saml_attribute_map_resolver).and_return(attribute_map_resolver)
|
35
36
|
allow(Devise).to receive(:saml_default_user_key).and_return(:email)
|
36
37
|
allow(Devise).to receive(:saml_create_user).and_return(false)
|
37
38
|
allow(Devise).to receive(:saml_use_subject).and_return(false)
|
@@ -39,13 +40,19 @@ describe Devise::Models::SamlAuthenticatable do
|
|
39
40
|
|
40
41
|
before do
|
41
42
|
allow(Rails).to receive(:root).and_return("/railsroot")
|
42
|
-
allow(File).to receive(:read).with("/railsroot/config/attribute-map.yml").and_return(<<-ATTRIBUTEMAP)
|
43
|
-
---
|
44
|
-
"saml-email-format": email
|
45
|
-
"saml-name-format": name
|
46
|
-
ATTRIBUTEMAP
|
47
43
|
end
|
48
44
|
|
45
|
+
let(:attribute_map_resolver) {
|
46
|
+
Class.new(::DeviseSamlAuthenticatable::DefaultAttributeMapResolver) do
|
47
|
+
def attribute_map
|
48
|
+
{
|
49
|
+
"saml-email-format" => "email",
|
50
|
+
"saml-name-format" => "name",
|
51
|
+
}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
}
|
55
|
+
let(:attributemap) { attribute_map_resolver.new(nil).attribute_map }
|
49
56
|
let(:response) { double(:response, attributes: attributes, name_id: name_id) }
|
50
57
|
let(:attributes) {
|
51
58
|
OneLogin::RubySaml::Attributes.new(
|
@@ -178,4 +185,191 @@ describe Devise::Models::SamlAuthenticatable do
|
|
178
185
|
include_examples "correct downcasing"
|
179
186
|
end
|
180
187
|
end
|
188
|
+
|
189
|
+
context "when configured with a resource validator class" do
|
190
|
+
let(:validator_class) { double("validator") }
|
191
|
+
let(:validator) { double("validator") }
|
192
|
+
let(:user) { Model.new(new_record: false) }
|
193
|
+
|
194
|
+
before do
|
195
|
+
allow(Devise).to receive(:saml_resource_validator).and_return(validator_class)
|
196
|
+
allow(validator_class).to receive(:new).and_return(validator)
|
197
|
+
end
|
198
|
+
|
199
|
+
context "and sent a valid value" do
|
200
|
+
before do
|
201
|
+
allow(validator).to receive(:validate).with(user, response).and_return(true)
|
202
|
+
end
|
203
|
+
|
204
|
+
it "returns the user" do
|
205
|
+
expect(Model).to receive(:where).with(email: 'user@example.com').and_return([user])
|
206
|
+
expect(Model.authenticate_with_saml(response, nil)).to eq(user)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
context "and sent an invalid value" do
|
211
|
+
before do
|
212
|
+
allow(validator).to receive(:validate).with(user, response).and_return(false)
|
213
|
+
end
|
214
|
+
|
215
|
+
it "returns nil" do
|
216
|
+
expect(Model).to receive(:where).with(email: 'user@example.com').and_return([user])
|
217
|
+
expect(Model.authenticate_with_saml(response, nil)).to be_nil
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
|
223
|
+
context "when configured with a resource validator hook" do
|
224
|
+
let(:validator_hook) { double("validator_hook") }
|
225
|
+
let(:decorated_response) { ::SamlAuthenticatable::SamlResponse.new(response, attributemap) }
|
226
|
+
let(:user) { Model.new(new_record: false) }
|
227
|
+
|
228
|
+
before do
|
229
|
+
allow(Devise).to receive(:saml_resource_validator_hook).and_return(validator_hook)
|
230
|
+
allow(::SamlAuthenticatable::SamlResponse).to receive(:new).with(response, attributemap).and_return(decorated_response)
|
231
|
+
end
|
232
|
+
|
233
|
+
context "and sent a valid value" do
|
234
|
+
before do
|
235
|
+
expect(validator_hook).to receive(:call).with(user, decorated_response, 'user@example.com').and_return(true)
|
236
|
+
end
|
237
|
+
|
238
|
+
it "returns the user" do
|
239
|
+
expect(Model).to receive(:where).with(email: 'user@example.com').and_return([user])
|
240
|
+
expect(Model.authenticate_with_saml(response, nil)).to eq(user)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
context "and sent an invalid value" do
|
245
|
+
before do
|
246
|
+
expect(validator_hook).to receive(:call).with(user, decorated_response, 'user@example.com').and_return(false)
|
247
|
+
end
|
248
|
+
|
249
|
+
it "returns nil" do
|
250
|
+
expect(Model).to receive(:where).with(email: 'user@example.com').and_return([user])
|
251
|
+
expect(Model.authenticate_with_saml(response, nil)).to be_nil
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
|
257
|
+
context "when configured to use a custom update hook" do
|
258
|
+
it "can replicate the default behaviour in a custom hook" do
|
259
|
+
configure_hook do |user, saml_response|
|
260
|
+
Devise.saml_default_update_resource_hook.call(user, saml_response)
|
261
|
+
end
|
262
|
+
|
263
|
+
new_user = Model.authenticate_with_saml(response, nil)
|
264
|
+
|
265
|
+
expect(new_user.name).to eq(attributes['saml-name-format'])
|
266
|
+
expect(new_user.email).to eq(attributes['saml-email-format'])
|
267
|
+
end
|
268
|
+
|
269
|
+
it "can extend the default behaviour with custom transformations" do
|
270
|
+
configure_hook do |user, saml_response|
|
271
|
+
Devise.saml_default_update_resource_hook.call(user, saml_response)
|
272
|
+
|
273
|
+
user.email = "ext+#{user.email}"
|
274
|
+
end
|
275
|
+
|
276
|
+
new_user = Model.authenticate_with_saml(response, nil)
|
277
|
+
|
278
|
+
expect(new_user.name).to eq(attributes['saml-name-format'])
|
279
|
+
expect(new_user.email).to eq("ext+#{attributes['saml-email-format']}")
|
280
|
+
end
|
281
|
+
|
282
|
+
it "can extend the default behaviour using information from the saml response" do
|
283
|
+
configure_hook do |user, saml_response|
|
284
|
+
Devise.saml_default_update_resource_hook.call(user, saml_response)
|
285
|
+
|
286
|
+
name_id = saml_response.raw_response.name_id
|
287
|
+
user.name += "@#{name_id}"
|
288
|
+
end
|
289
|
+
|
290
|
+
new_user = Model.authenticate_with_saml(response, nil)
|
291
|
+
|
292
|
+
expect(new_user.name).to eq("#{attributes['saml-name-format']}@#{response.name_id}")
|
293
|
+
expect(new_user.email).to eq(attributes['saml-email-format'])
|
294
|
+
end
|
295
|
+
|
296
|
+
def configure_hook(&block)
|
297
|
+
allow(Model).to receive(:where).with(email: 'user@example.com').and_return([])
|
298
|
+
allow(Devise).to receive(:saml_default_user_key).and_return(:email)
|
299
|
+
allow(Devise).to receive(:saml_create_user).and_return(true)
|
300
|
+
allow(Devise).to receive(:saml_update_resource_hook).and_return(block)
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
context "when configured to use a custom user locator" do
|
305
|
+
let(:name_id) { 'SomeUsername' }
|
306
|
+
|
307
|
+
it "can replicate the default behaviour for a new user in a custom locator" do
|
308
|
+
allow(Model).to receive(:where).with(email: attributes['saml-email-format']).and_return([])
|
309
|
+
|
310
|
+
configure_hook do |model, saml_response, auth_value|
|
311
|
+
Devise.saml_default_resource_locator.call(model, saml_response, auth_value)
|
312
|
+
end
|
313
|
+
|
314
|
+
new_user = Model.authenticate_with_saml(response, nil)
|
315
|
+
|
316
|
+
expect(new_user.name).to eq(attributes['saml-name-format'])
|
317
|
+
expect(new_user.email).to eq(attributes['saml-email-format'])
|
318
|
+
end
|
319
|
+
|
320
|
+
it "can replicate the default behaviour for an existing user in a custom locator" do
|
321
|
+
user = Model.new(email: attributes['saml-email-format'], name: attributes['saml-name-format'])
|
322
|
+
user.save!
|
323
|
+
|
324
|
+
allow(Model).to receive(:where).with(email: attributes['saml-email-format']).and_return([user])
|
325
|
+
|
326
|
+
configure_hook do |model, saml_response, auth_value|
|
327
|
+
Devise.saml_default_resource_locator.call(model, saml_response, auth_value)
|
328
|
+
end
|
329
|
+
|
330
|
+
new_user = Model.authenticate_with_saml(response, nil)
|
331
|
+
|
332
|
+
expect(new_user).to eq(user)
|
333
|
+
expect(new_user.name).to eq(attributes['saml-name-format'])
|
334
|
+
expect(new_user.email).to eq(attributes['saml-email-format'])
|
335
|
+
end
|
336
|
+
|
337
|
+
it "can change the default behaviour for a new user from the saml response" do
|
338
|
+
allow(Model).to receive(:where).with(foo: attributes['saml-email-format'], bar: name_id).and_return([])
|
339
|
+
|
340
|
+
configure_hook do |model, saml_response, auth_value|
|
341
|
+
name_id = saml_response.raw_response.name_id
|
342
|
+
model.where(foo: auth_value, bar: name_id).first
|
343
|
+
end
|
344
|
+
|
345
|
+
new_user = Model.authenticate_with_saml(response, nil)
|
346
|
+
|
347
|
+
expect(new_user.name).to eq(attributes['saml-name-format'])
|
348
|
+
expect(new_user.email).to eq(attributes['saml-email-format'])
|
349
|
+
end
|
350
|
+
|
351
|
+
it "can change the default behaviour for an existing user from the saml response" do
|
352
|
+
user = Model.new(email: attributes['saml-email-format'], name: attributes['saml-name-format'])
|
353
|
+
user.save!
|
354
|
+
|
355
|
+
allow(Model).to receive(:where).with(foo: attributes['saml-email-format'], bar: name_id).and_return([user])
|
356
|
+
|
357
|
+
configure_hook do |model, saml_response, auth_value|
|
358
|
+
name_id = saml_response.raw_response.name_id
|
359
|
+
model.where(foo: auth_value, bar: name_id).first
|
360
|
+
end
|
361
|
+
|
362
|
+
new_user = Model.authenticate_with_saml(response, nil)
|
363
|
+
|
364
|
+
expect(new_user).to eq(user)
|
365
|
+
expect(new_user.name).to eq(attributes['saml-name-format'])
|
366
|
+
expect(new_user.email).to eq(attributes['saml-email-format'])
|
367
|
+
end
|
368
|
+
|
369
|
+
def configure_hook(&block)
|
370
|
+
allow(Devise).to receive(:saml_default_user_key).and_return(:email)
|
371
|
+
allow(Devise).to receive(:saml_create_user).and_return(true)
|
372
|
+
allow(Devise).to receive(:saml_resource_locator).and_return(block)
|
373
|
+
end
|
374
|
+
end
|
181
375
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'devise_saml_authenticatable/saml_mapped_attributes'
|
3
|
+
|
4
|
+
describe SamlAuthenticatable::SamlMappedAttributes do
|
5
|
+
let(:instance) { described_class.new(saml_attributes, attribute_map) }
|
6
|
+
let(:attribute_map_file) { File.join(File.dirname(__FILE__), '../support/attribute-map.yml') }
|
7
|
+
let(:attribute_map) { YAML.load(File.read(attribute_map_file)) }
|
8
|
+
let(:saml_attributes) do
|
9
|
+
{
|
10
|
+
"first_name" => ["John"],
|
11
|
+
"last_name"=>["Smith"],
|
12
|
+
"email"=>["john.smith@example.com"]
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "#value_by_resource_key" do
|
17
|
+
RSpec.shared_examples "correctly maps the value of the resource key" do |saml_key, resource_key, expected_value|
|
18
|
+
subject(:perform) { instance.value_by_resource_key(resource_key) }
|
19
|
+
|
20
|
+
it "correctly maps the resource key, #{resource_key}, to the value of the '#{saml_key}' SAML key" do
|
21
|
+
saml_attributes[saml_key] = saml_attributes.delete(resource_key)
|
22
|
+
expect(perform).to eq(expected_value)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "first_name" do
|
27
|
+
saml_keys = ['urn:mace:dir:attribute-def:first_name', 'first_name', 'firstName', 'firstname']
|
28
|
+
|
29
|
+
saml_keys.each do |saml_key|
|
30
|
+
include_examples 'correctly maps the value of the resource key', saml_key, 'first_name', ['John']
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'last_name' do
|
35
|
+
saml_keys = ['urn:mace:dir:attribute-def:last_name', 'last_name', 'lastName', 'lastname']
|
36
|
+
|
37
|
+
saml_keys.each do |saml_key|
|
38
|
+
include_examples 'correctly maps the value of the resource key', saml_key, 'last_name', ['Smith']
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'email' do
|
43
|
+
saml_keys = ['urn:mace:dir:attribute-def:email', 'email_address', 'emailAddress', 'email']
|
44
|
+
|
45
|
+
saml_keys.each do |saml_key|
|
46
|
+
include_examples 'correctly maps the value of the resource key', saml_key, 'email', ['john.smith@example.com']
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -134,6 +134,24 @@ describe Devise::Strategies::SamlAuthenticatable do
|
|
134
134
|
strategy.authenticate!
|
135
135
|
end
|
136
136
|
end
|
137
|
+
|
138
|
+
context "when allowed_clock_drift is configured" do
|
139
|
+
before do
|
140
|
+
allow(Devise).to receive(:allowed_clock_drift_in_seconds).and_return(30)
|
141
|
+
end
|
142
|
+
|
143
|
+
it "is valid with the configured clock drift" do
|
144
|
+
expect(OneLogin::RubySaml::Response).to receive(:new).with(params[:SAMLResponse], hash_including(allowed_clock_drift: 30))
|
145
|
+
expect(strategy).to be_valid
|
146
|
+
end
|
147
|
+
|
148
|
+
it "authenticates with the configured clock drift" do
|
149
|
+
expect(OneLogin::RubySaml::Response).to receive(:new).with(params[:SAMLResponse], hash_including(allowed_clock_drift: 30))
|
150
|
+
|
151
|
+
expect(strategy).to receive(:success!).with(user)
|
152
|
+
strategy.authenticate!
|
153
|
+
end
|
154
|
+
end
|
137
155
|
end
|
138
156
|
|
139
157
|
it "is not valid without a SAMLResponse parameter" do
|
@@ -5,6 +5,7 @@ require 'uri'
|
|
5
5
|
require 'capybara/rspec'
|
6
6
|
require 'capybara/poltergeist'
|
7
7
|
Capybara.default_driver = :poltergeist
|
8
|
+
Capybara.server = :webrick
|
8
9
|
|
9
10
|
describe "SAML Authentication", type: :feature do
|
10
11
|
let(:idp_port) { 8009 }
|
@@ -56,7 +57,7 @@ describe "SAML Authentication", type: :feature do
|
|
56
57
|
expect(current_url).to eq("http://localhost:8020/")
|
57
58
|
|
58
59
|
click_on "Log out"
|
59
|
-
#confirm the logout response redirected to the SP which in turn attempted to sign
|
60
|
+
# confirm the logout response redirected to the SP which in turn attempted to sign the user back in
|
60
61
|
expect(current_url).to match(%r(\Ahttp://localhost:8009/saml/auth\?SAMLRequest=))
|
61
62
|
|
62
63
|
# prove user is now signed out
|
@@ -84,8 +85,8 @@ describe "SAML Authentication", type: :feature do
|
|
84
85
|
@sp_pid = start_app('sp', sp_port)
|
85
86
|
end
|
86
87
|
after(:each) do
|
87
|
-
stop_app(@idp_pid)
|
88
|
-
stop_app(@sp_pid)
|
88
|
+
stop_app("idp", @idp_pid)
|
89
|
+
stop_app("sp", @sp_pid)
|
89
90
|
end
|
90
91
|
|
91
92
|
it_behaves_like "it authenticates and creates users"
|
@@ -99,8 +100,8 @@ describe "SAML Authentication", type: :feature do
|
|
99
100
|
@sp_pid = start_app('sp', sp_port)
|
100
101
|
end
|
101
102
|
after(:each) do
|
102
|
-
stop_app(@idp_pid)
|
103
|
-
stop_app(@sp_pid)
|
103
|
+
stop_app("idp", @idp_pid)
|
104
|
+
stop_app("sp", @sp_pid)
|
104
105
|
end
|
105
106
|
|
106
107
|
it_behaves_like "it authenticates and creates users"
|
@@ -114,8 +115,8 @@ describe "SAML Authentication", type: :feature do
|
|
114
115
|
@sp_pid = start_app('sp', sp_port)
|
115
116
|
end
|
116
117
|
after(:each) do
|
117
|
-
stop_app(@idp_pid)
|
118
|
-
stop_app(@sp_pid)
|
118
|
+
stop_app("idp", @idp_pid)
|
119
|
+
stop_app("sp", @sp_pid)
|
119
120
|
end
|
120
121
|
|
121
122
|
it_behaves_like "it authenticates and creates users"
|
@@ -130,33 +131,33 @@ describe "SAML Authentication", type: :feature do
|
|
130
131
|
@sp_pid = start_app('sp', sp_port)
|
131
132
|
end
|
132
133
|
after(:each) do
|
133
|
-
stop_app(@idp_pid)
|
134
|
-
stop_app(@sp_pid)
|
134
|
+
stop_app("idp", @idp_pid)
|
135
|
+
stop_app("sp", @sp_pid)
|
135
136
|
end
|
136
137
|
|
137
138
|
it_behaves_like "it authenticates and creates users"
|
138
139
|
end
|
139
140
|
|
140
141
|
context "when the idp_settings_adapter key is set" do
|
141
|
-
|
142
142
|
before(:each) do
|
143
143
|
create_app('idp', 'INCLUDE_SUBJECT_IN_ATTRIBUTES' => "false")
|
144
144
|
create_app('sp', 'USE_SUBJECT_TO_AUTHENTICATE' => "true", 'IDP_SETTINGS_ADAPTER' => "IdpSettingsAdapter", 'IDP_ENTITY_ID_READER' => "OurEntityIdReader")
|
145
145
|
|
146
|
-
|
146
|
+
# use a different port for this entity ID; configured in spec/support/idp_settings_adapter.rb.erb
|
147
|
+
@idp_pid = start_app('idp', 8010)
|
147
148
|
@sp_pid = start_app('sp', sp_port)
|
148
149
|
end
|
149
150
|
|
150
151
|
after(:each) do
|
151
|
-
stop_app(@idp_pid)
|
152
|
-
stop_app(@sp_pid)
|
152
|
+
stop_app("idp", @idp_pid)
|
153
|
+
stop_app("sp", @sp_pid)
|
153
154
|
end
|
154
155
|
|
155
156
|
it "authenticates an existing user on a SP via an IdP" do
|
156
157
|
create_user("you@example.com")
|
157
158
|
|
158
159
|
visit 'http://localhost:8020/users/saml/sign_in/?entity_id=http%3A%2F%2Flocalhost%3A8020%2Fsaml%2Fmetadata'
|
159
|
-
expect(current_url).to match(%r(\Ahttp://
|
160
|
+
expect(current_url).to match(%r(\Ahttp://localhost:8010/saml/auth\?SAMLRequest=))
|
160
161
|
end
|
161
162
|
end
|
162
163
|
|
@@ -171,8 +172,8 @@ describe "SAML Authentication", type: :feature do
|
|
171
172
|
end
|
172
173
|
|
173
174
|
after(:each) do
|
174
|
-
stop_app(@idp_pid)
|
175
|
-
stop_app(@sp_pid)
|
175
|
+
stop_app("idp", @idp_pid)
|
176
|
+
stop_app("sp", @sp_pid)
|
176
177
|
end
|
177
178
|
|
178
179
|
it_behaves_like "it authenticates and creates users"
|
@@ -187,24 +188,47 @@ describe "SAML Authentication", type: :feature do
|
|
187
188
|
fill_in "Email", with: "you@example.com"
|
188
189
|
fill_in "Password", with: "asdf"
|
189
190
|
click_on "Sign in"
|
190
|
-
expect(page).to have_content("Example Domain This domain is
|
191
|
+
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.")
|
191
192
|
expect(current_url).to eq("http://www.example.com/")
|
192
193
|
end
|
193
194
|
end
|
194
195
|
end
|
195
196
|
|
197
|
+
context "when the saml_attribute_map is set" do
|
198
|
+
before(:each) do
|
199
|
+
create_app(
|
200
|
+
"idp",
|
201
|
+
"EMAIL_ADDRESS_ATTRIBUTE_KEY" => "myemailaddress",
|
202
|
+
"NAME_ATTRIBUTE_KEY" => "myname",
|
203
|
+
"INCLUDE_SUBJECT_IN_ATTRIBUTES" => "false",
|
204
|
+
)
|
205
|
+
create_app(
|
206
|
+
"sp",
|
207
|
+
"ATTRIBUTE_MAP_RESOLVER" => "AttributeMapResolver",
|
208
|
+
"USE_SUBJECT_TO_AUTHENTICATE" => "true",
|
209
|
+
)
|
210
|
+
@idp_pid = start_app("idp", idp_port)
|
211
|
+
@sp_pid = start_app("sp", sp_port)
|
212
|
+
end
|
213
|
+
after(:each) do
|
214
|
+
stop_app("idp", @idp_pid)
|
215
|
+
stop_app("sp", @sp_pid)
|
216
|
+
end
|
217
|
+
|
218
|
+
it_behaves_like "it authenticates and creates users"
|
219
|
+
end
|
220
|
+
|
196
221
|
def create_user(email)
|
197
222
|
response = Net::HTTP.post_form(URI('http://localhost:8020/users'), email: email)
|
198
223
|
expect(response.code).to eq('201')
|
199
224
|
end
|
200
225
|
|
201
|
-
def sign_in
|
202
|
-
visit
|
203
|
-
expect(current_url).to match(%r(\Ahttp://localhost:8009/saml/auth\?SAMLRequest=))
|
226
|
+
def sign_in(entity_id: "")
|
227
|
+
visit "http://localhost:8020/users/saml/sign_in/?entity_id=#{URI.escape(entity_id)}"
|
204
228
|
fill_in "Email", with: "you@example.com"
|
205
229
|
fill_in "Password", with: "asdf"
|
206
230
|
click_on "Sign in"
|
207
|
-
Timeout.timeout(Capybara.
|
231
|
+
Timeout.timeout(Capybara.default_max_wait_time) do
|
208
232
|
loop do
|
209
233
|
sleep 0.1
|
210
234
|
break if current_url == "http://localhost:8020/"
|