saml_idp 0.7.2 → 0.11.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.
Files changed (47) hide show
  1. checksums.yaml +5 -5
  2. data/Gemfile +1 -1
  3. data/README.md +41 -13
  4. data/lib/saml_idp/configurator.rb +5 -1
  5. data/lib/saml_idp/controller.rb +9 -5
  6. data/lib/saml_idp/incoming_metadata.rb +22 -1
  7. data/lib/saml_idp/metadata_builder.rb +23 -8
  8. data/lib/saml_idp/persisted_metadata.rb +4 -0
  9. data/lib/saml_idp/request.rb +22 -3
  10. data/lib/saml_idp/response_builder.rb +19 -5
  11. data/lib/saml_idp/saml_response.rb +15 -3
  12. data/lib/saml_idp/service_provider.rb +15 -6
  13. data/lib/saml_idp/signable.rb +1 -2
  14. data/lib/saml_idp/version.rb +1 -1
  15. data/lib/saml_idp/xml_security.rb +1 -1
  16. data/saml_idp.gemspec +25 -23
  17. data/spec/acceptance/idp_controller_spec.rb +5 -4
  18. data/spec/lib/saml_idp/algorithmable_spec.rb +6 -6
  19. data/spec/lib/saml_idp/assertion_builder_spec.rb +8 -8
  20. data/spec/lib/saml_idp/attribute_decorator_spec.rb +8 -8
  21. data/spec/lib/saml_idp/configurator_spec.rb +8 -7
  22. data/spec/lib/saml_idp/controller_spec.rb +47 -20
  23. data/spec/lib/saml_idp/encryptor_spec.rb +4 -4
  24. data/spec/lib/saml_idp/incoming_metadata_spec.rb +60 -0
  25. data/spec/lib/saml_idp/metadata_builder_spec.rb +30 -17
  26. data/spec/lib/saml_idp/name_id_formatter_spec.rb +3 -3
  27. data/spec/lib/saml_idp/request_spec.rb +22 -22
  28. data/spec/lib/saml_idp/response_builder_spec.rb +5 -3
  29. data/spec/lib/saml_idp/saml_response_spec.rb +31 -8
  30. data/spec/lib/saml_idp/service_provider_spec.rb +2 -2
  31. data/spec/lib/saml_idp/signable_spec.rb +1 -1
  32. data/spec/lib/saml_idp/signature_builder_spec.rb +2 -2
  33. data/spec/lib/saml_idp/signed_info_builder_spec.rb +3 -3
  34. data/spec/rails_app/app/controllers/saml_controller.rb +5 -1
  35. data/spec/rails_app/config/application.rb +0 -6
  36. data/spec/rails_app/config/environments/development.rb +1 -6
  37. data/spec/rails_app/config/environments/production.rb +1 -0
  38. data/spec/rails_app/config/environments/test.rb +1 -0
  39. data/spec/spec_helper.rb +22 -0
  40. data/spec/support/certificates/sp_cert_req.csr +12 -0
  41. data/spec/support/certificates/sp_private_key.pem +16 -0
  42. data/spec/support/certificates/sp_x509_cert.crt +18 -0
  43. data/spec/support/saml_request_macros.rb +64 -4
  44. data/spec/support/security_helpers.rb +10 -0
  45. data/spec/xml_security_spec.rb +12 -12
  46. metadata +89 -52
  47. data/spec/lib/saml_idp/.assertion_builder_spec.rb.swp +0 -0
@@ -5,11 +5,11 @@ require 'saml_idp/encryptor'
5
5
  module SamlIdp
6
6
  describe Encryptor do
7
7
  let (:encryption_opts) do
8
- {
8
+ {
9
9
  cert: Default::X509_CERTIFICATE,
10
10
  block_encryption: 'aes256-cbc',
11
11
  key_transport: 'rsa-oaep-mgf1p',
12
- }
12
+ }
13
13
  end
14
14
 
15
15
  subject { described_class.new encryption_opts }
@@ -17,11 +17,11 @@ module SamlIdp
17
17
  it "encrypts XML" do
18
18
  raw_xml = '<foo>bar</foo>'
19
19
  encrypted_xml = subject.encrypt(raw_xml)
20
- encrypted_xml.should_not match 'bar'
20
+ expect(encrypted_xml).to_not match raw_xml
21
21
  encrypted_doc = Nokogiri::XML::Document.parse(encrypted_xml)
22
22
  encrypted_data = Xmlenc::EncryptedData.new(encrypted_doc.at_xpath('//xenc:EncryptedData', Xmlenc::NAMESPACES))
23
23
  decrypted_xml = encrypted_data.decrypt(subject.encryption_key)
24
- decrypted_xml.should == raw_xml
24
+ expect(decrypted_xml).to eq(raw_xml)
25
25
  end
26
26
  end
27
27
  end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+ module SamlIdp
3
+
4
+ metadata_1 = <<-eos
5
+ <md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="test" entityID="https://test-saml.com/saml">
6
+ <md:SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol" AuthnRequestsSigned="false" WantAssertionsSigned="false">
7
+ </md:SPSSODescriptor>
8
+ </md:EntityDescriptor>
9
+ eos
10
+
11
+ metadata_2 = <<-eos
12
+ <md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="test" entityID="https://test-saml.com/saml">
13
+ <md:SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol" AuthnRequestsSigned="true" WantAssertionsSigned="true">
14
+ </md:SPSSODescriptor>
15
+ </md:EntityDescriptor>
16
+ eos
17
+
18
+ metadata_3 = <<-eos
19
+ <md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="test" entityID="https://test-saml.com/saml">
20
+ <md:SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol" AuthnRequestsSigned="true">
21
+ </md:SPSSODescriptor>
22
+ </md:EntityDescriptor>
23
+ eos
24
+
25
+ metadata_4 = <<-eos
26
+ <md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="test" entityID="https://test-saml.com/saml">
27
+ <md:SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
28
+ </md:SPSSODescriptor>
29
+ </md:EntityDescriptor>
30
+ eos
31
+
32
+ describe IncomingMetadata do
33
+ it 'should properly set sign_assertions to false' do
34
+ metadata = SamlIdp::IncomingMetadata.new(metadata_1)
35
+ expect(metadata.sign_assertions).to eq(false)
36
+ expect(metadata.sign_authn_request).to eq(false)
37
+ end
38
+
39
+ it 'should properly set entity_id as https://test-saml.com/saml' do
40
+ metadata = SamlIdp::IncomingMetadata.new(metadata_1)
41
+ expect(metadata.entity_id).to eq('https://test-saml.com/saml')
42
+ end
43
+
44
+ it 'should properly set sign_assertions to true' do
45
+ metadata = SamlIdp::IncomingMetadata.new(metadata_2)
46
+ expect(metadata.sign_assertions).to eq(true)
47
+ expect(metadata.sign_authn_request).to eq(true)
48
+ end
49
+
50
+ it 'should properly set sign_assertions to false when WantAssertionsSigned is not included' do
51
+ metadata = SamlIdp::IncomingMetadata.new(metadata_3)
52
+ expect(metadata.sign_assertions).to eq(false)
53
+ end
54
+
55
+ it 'should properly set sign_authn_request to false when AuthnRequestsSigned is not included' do
56
+ metadata = SamlIdp::IncomingMetadata.new(metadata_4)
57
+ expect(metadata.sign_authn_request).to eq(false)
58
+ end
59
+ end
60
+ end
@@ -2,18 +2,39 @@ require 'spec_helper'
2
2
  module SamlIdp
3
3
  describe MetadataBuilder do
4
4
  it "has a valid fresh" do
5
- subject.fresh.should_not be_empty
5
+ expect(subject.fresh).to_not be_empty
6
6
  end
7
7
 
8
8
  it "signs valid xml" do
9
- Saml::XML::Document.parse(subject.signed).valid_signature?(Default::FINGERPRINT).should be_truthy
9
+ expect(Saml::XML::Document.parse(subject.signed).valid_signature?(Default::FINGERPRINT)).to be_truthy
10
10
  end
11
11
 
12
12
  it "includes logout element" do
13
13
  subject.configurator.single_logout_service_post_location = 'https://example.com/saml/logout'
14
- subject.fresh.should match(
15
- '<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://example.com/saml/logout"/>'
16
- )
14
+ subject.configurator.single_logout_service_redirect_location = 'https://example.com/saml/logout'
15
+ expect(subject.fresh).to match('<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://example.com/saml/logout"/>')
16
+ expect(subject.fresh).to match('<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://example.com/saml/logout"/>')
17
+ end
18
+
19
+ it 'will not includes empty logout endpoint' do
20
+ subject.configurator.single_logout_service_post_location = ''
21
+ subject.configurator.single_logout_service_redirect_location = nil
22
+ expect(subject.fresh).not_to match('<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"')
23
+ expect(subject.fresh).not_to match('<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"')
24
+ end
25
+
26
+ it 'will includes sso element' do
27
+ subject.configurator.single_service_post_location = 'https://example.com/saml/sso'
28
+ subject.configurator.single_service_redirect_location = 'https://example.com/saml/sso'
29
+ expect(subject.fresh).to match('<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://example.com/saml/sso"/>')
30
+ expect(subject.fresh).to match('<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://example.com/saml/sso"/>')
31
+ end
32
+
33
+ it 'will not includes empty sso element' do
34
+ subject.configurator.single_service_post_location = ''
35
+ subject.configurator.single_service_redirect_location = nil
36
+ expect(subject.fresh).not_to match('<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"')
37
+ expect(subject.fresh).not_to match('<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"')
17
38
  end
18
39
 
19
40
  context "technical contact" do
@@ -32,31 +53,23 @@ module SamlIdp
32
53
  subject.configurator.technical_contact.telephone = "1-800-555-5555"
33
54
  subject.configurator.technical_contact.email_address = "acme@example.com"
34
55
 
35
- subject.fresh.should match(
36
- '<ContactPerson contactType="technical"><Company>ACME Corporation</Company><GivenName>Road</GivenName><SurName>Runner</SurName><EmailAddress>mailto:acme@example.com</EmailAddress><TelephoneNumber>1-800-555-5555</TelephoneNumber></ContactPerson>'
37
- )
56
+ expect(subject.fresh).to match('<ContactPerson contactType="technical"><Company>ACME Corporation</Company><GivenName>Road</GivenName><SurName>Runner</SurName><EmailAddress>mailto:acme@example.com</EmailAddress><TelephoneNumber>1-800-555-5555</TelephoneNumber></ContactPerson>')
38
57
  end
39
58
 
40
59
  it "no fields" do
41
- subject.fresh.should match(
42
- '<ContactPerson contactType="technical"></ContactPerson>'
43
- )
60
+ expect(subject.fresh).to match('<ContactPerson contactType="technical"></ContactPerson>')
44
61
  end
45
62
 
46
63
  it "just email" do
47
64
  subject.configurator.technical_contact.email_address = "acme@example.com"
48
- subject.fresh.should match(
49
- '<ContactPerson contactType="technical"><EmailAddress>mailto:acme@example.com</EmailAddress></ContactPerson>'
50
- )
65
+ expect(subject.fresh).to match('<ContactPerson contactType="technical"><EmailAddress>mailto:acme@example.com</EmailAddress></ContactPerson>')
51
66
  end
52
67
 
53
68
  end
54
69
 
55
70
  it "includes logout element as HTTP Redirect" do
56
71
  subject.configurator.single_logout_service_redirect_location = 'https://example.com/saml/logout'
57
- subject.fresh.should match(
58
- '<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://example.com/saml/logout"/>'
59
- )
72
+ expect(subject.fresh).to match('<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://example.com/saml/logout"/>')
60
73
  end
61
74
  end
62
75
  end
@@ -7,7 +7,7 @@ module SamlIdp
7
7
  let(:list) { { email_address: ->() { "foo@example.com" } } }
8
8
 
9
9
  it "has a valid all" do
10
- subject.all.should == ["urn:oasis:names:tc:SAML:2.0:nameid-format:emailAddress"]
10
+ expect(subject.all).to eq ["urn:oasis:names:tc:SAML:2.0:nameid-format:emailAddress"]
11
11
  end
12
12
 
13
13
  end
@@ -21,7 +21,7 @@ module SamlIdp
21
21
  }
22
22
 
23
23
  it "has a valid all" do
24
- subject.all.should == [
24
+ expect(subject.all).to eq [
25
25
  "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
26
26
  "urn:oasis:names:tc:SAML:2.0:nameid-format:undefined",
27
27
  ]
@@ -32,7 +32,7 @@ module SamlIdp
32
32
  let(:list) { [:email_address, :undefined] }
33
33
 
34
34
  it "has a valid all" do
35
- subject.all.should == [
35
+ expect(subject.all).to eq [
36
36
  "urn:oasis:names:tc:SAML:2.0:nameid-format:emailAddress",
37
37
  "urn:oasis:names:tc:SAML:2.0:nameid-format:undefined",
38
38
  ]
@@ -9,12 +9,12 @@ module SamlIdp
9
9
  subject { described_class.from_deflated_request deflated_request }
10
10
 
11
11
  it "inflates" do
12
- subject.request_id.should == "_af43d1a0-e111-0130-661a-3c0754403fdb"
12
+ expect(subject.request_id).to eq("_af43d1a0-e111-0130-661a-3c0754403fdb")
13
13
  end
14
14
 
15
15
  it "handles invalid SAML" do
16
16
  req = described_class.from_deflated_request "bang!"
17
- req.valid?.should == false
17
+ expect(req.valid?).to eq(false)
18
18
  end
19
19
  end
20
20
 
@@ -22,51 +22,51 @@ module SamlIdp
22
22
  subject { described_class.new raw_authn_request }
23
23
 
24
24
  it "has a valid request_id" do
25
- subject.request_id.should == "_af43d1a0-e111-0130-661a-3c0754403fdb"
25
+ expect(subject.request_id).to eq("_af43d1a0-e111-0130-661a-3c0754403fdb")
26
26
  end
27
27
 
28
28
  it "has a valid acs_url" do
29
- subject.acs_url.should == "http://localhost:3000/saml/consume"
29
+ expect(subject.acs_url).to eq("http://localhost:3000/saml/consume")
30
30
  end
31
31
 
32
32
  it "has a valid service_provider" do
33
- subject.service_provider.should be_a ServiceProvider
33
+ expect(subject.service_provider).to be_a ServiceProvider
34
34
  end
35
35
 
36
36
  it "has a valid service_provider" do
37
- subject.service_provider.should be_truthy
37
+ expect(subject.service_provider).to be_truthy
38
38
  end
39
39
 
40
40
  it "has a valid issuer" do
41
- subject.issuer.should == "localhost:3000"
41
+ expect(subject.issuer).to eq("localhost:3000")
42
42
  end
43
43
 
44
44
  it "has a valid valid_signature" do
45
- subject.valid_signature?.should be_truthy
45
+ expect(subject.valid_signature?).to be_truthy
46
46
  end
47
47
 
48
48
  it "should return acs_url for response_url" do
49
- subject.response_url.should == subject.acs_url
49
+ expect(subject.response_url).to eq(subject.acs_url)
50
50
  end
51
51
 
52
52
  it "is a authn request" do
53
- subject.authn_request?.should == true
53
+ expect(subject.authn_request?).to eq(true)
54
54
  end
55
55
 
56
56
  it "fetches internal request" do
57
- subject.request['ID'].should == subject.request_id
57
+ expect(subject.request['ID']).to eq(subject.request_id)
58
58
  end
59
59
 
60
60
  it "has a valid authn context" do
61
- subject.requested_authn_context.should == "urn:oasis:names:tc:SAML:2.0:ac:classes:Password"
61
+ expect(subject.requested_authn_context).to eq("urn:oasis:names:tc:SAML:2.0:ac:classes:Password")
62
62
  end
63
63
 
64
64
  it "does not permit empty issuer" do
65
65
  raw_req = raw_authn_request.gsub('localhost:3000', '')
66
66
  authn_request = described_class.new raw_req
67
- authn_request.issuer.should_not == ''
68
- authn_request.issuer.should == nil
69
- authn_request.valid?.should == false
67
+ expect(authn_request.issuer).to_not eq('')
68
+ expect(authn_request.issuer).to be_nil
69
+ expect(authn_request.valid?).to eq(false)
70
70
  end
71
71
  end
72
72
 
@@ -76,31 +76,31 @@ module SamlIdp
76
76
  subject { described_class.new raw_logout_request }
77
77
 
78
78
  it "has a valid request_id" do
79
- subject.request_id.should == '_some_response_id'
79
+ expect(subject.request_id).to eq('_some_response_id')
80
80
  end
81
81
 
82
82
  it "should be flagged as a logout_request" do
83
- subject.logout_request?.should == true
83
+ expect(subject.logout_request?).to eq(true)
84
84
  end
85
85
 
86
86
  it "should have a valid name_id" do
87
- subject.name_id.should == 'some_name_id'
87
+ expect(subject.name_id).to eq('some_name_id')
88
88
  end
89
89
 
90
90
  it "should have a session index" do
91
- subject.session_index.should == 'abc123index'
91
+ expect(subject.session_index).to eq('abc123index')
92
92
  end
93
93
 
94
94
  it "should have a valid issuer" do
95
- subject.issuer.should == 'http://example.com'
95
+ expect(subject.issuer).to eq('http://example.com')
96
96
  end
97
97
 
98
98
  it "fetches internal request" do
99
- subject.request['ID'].should == subject.request_id
99
+ expect(subject.request['ID']).to eq(subject.request_id)
100
100
  end
101
101
 
102
102
  it "should return logout_url for response_url" do
103
- subject.response_url.should == subject.logout_url
103
+ expect(subject.response_url).to eq(subject.logout_url)
104
104
  end
105
105
  end
106
106
  end
@@ -6,12 +6,14 @@ module SamlIdp
6
6
  let(:saml_acs_url) { "http://sportngin.com" }
7
7
  let(:saml_request_id) { "134" }
8
8
  let(:assertion_and_signature) { "<Assertion xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"_abc\" IssueInstant=\"2013-07-31T05:00:00Z\" Version=\"2.0\"><Issuer>http://sportngin.com</Issuer><signature>stuff</signature><Subject><NameID Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress\">jon.phenow@sportngin.com</NameID><SubjectConfirmation Method=\"urn:oasis:names:tc:SAML:2.0:cm:bearer\"><SubjectConfirmationData InResponseTo=\"123\" NotOnOrAfter=\"2013-07-31T05:03:00Z\" Recipient=\"http://saml.acs.url\"/></SubjectConfirmation></Subject><Conditions NotBefore=\"2013-07-31T04:59:55Z\" NotOnOrAfter=\"2013-07-31T06:00:00Z\"><AudienceRestriction><Audience>http://example.com</Audience></AudienceRestriction></Conditions><AttributeStatement><Attribute Name=\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress\"><AttributeValue>jon.phenow@sportngin.com</AttributeValue></Attribute></AttributeStatement><AuthnStatment AuthnInstant=\"2013-07-31T05:00:00Z\" SessionIndex=\"_abc\"><AuthnContext><AuthnContextClassRef>urn:federation:authentication:windows</AuthnContextClassRef></AuthnContext></AuthnStatment></Assertion>" }
9
+ let(:algorithm) { :sha256 }
9
10
  subject { described_class.new(
10
11
  response_id,
11
12
  issuer_uri,
12
13
  saml_acs_url,
13
14
  saml_request_id,
14
- assertion_and_signature
15
+ assertion_and_signature,
16
+ algorithm
15
17
  ) }
16
18
 
17
19
  before do
@@ -25,7 +27,7 @@ module SamlIdp
25
27
 
26
28
  it "builds a legit raw XML file" do
27
29
  Timecop.travel(Time.zone.local(2010, 6, 1, 13, 0, 0)) do
28
- subject.raw.should == "<samlp:Response ID=\"_abc\" Version=\"2.0\" IssueInstant=\"2010-06-01T13:00:00Z\" Destination=\"http://sportngin.com\" Consent=\"urn:oasis:names:tc:SAML:2.0:consent:unspecified\" InResponseTo=\"134\" xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\"><Issuer xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\">http://example.com</Issuer><samlp:Status><samlp:StatusCode Value=\"urn:oasis:names:tc:SAML:2.0:status:Success\"/></samlp:Status><Assertion xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"_abc\" IssueInstant=\"2013-07-31T05:00:00Z\" Version=\"2.0\"><Issuer>http://sportngin.com</Issuer><signature>stuff</signature><Subject><NameID Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress\">jon.phenow@sportngin.com</NameID><SubjectConfirmation Method=\"urn:oasis:names:tc:SAML:2.0:cm:bearer\"><SubjectConfirmationData InResponseTo=\"123\" NotOnOrAfter=\"2013-07-31T05:03:00Z\" Recipient=\"http://saml.acs.url\"/></SubjectConfirmation></Subject><Conditions NotBefore=\"2013-07-31T04:59:55Z\" NotOnOrAfter=\"2013-07-31T06:00:00Z\"><AudienceRestriction><Audience>http://example.com</Audience></AudienceRestriction></Conditions><AttributeStatement><Attribute Name=\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress\"><AttributeValue>jon.phenow@sportngin.com</AttributeValue></Attribute></AttributeStatement><AuthnStatment AuthnInstant=\"2013-07-31T05:00:00Z\" SessionIndex=\"_abc\"><AuthnContext><AuthnContextClassRef>urn:federation:authentication:windows</AuthnContextClassRef></AuthnContext></AuthnStatment></Assertion></samlp:Response>"
30
+ expect(subject.raw).to eq("<samlp:Response ID=\"_abc\" Version=\"2.0\" IssueInstant=\"2010-06-01T13:00:00Z\" Destination=\"http://sportngin.com\" Consent=\"urn:oasis:names:tc:SAML:2.0:consent:unspecified\" InResponseTo=\"134\" xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\"><Issuer xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\">http://example.com</Issuer><samlp:Status><samlp:StatusCode Value=\"urn:oasis:names:tc:SAML:2.0:status:Success\"/></samlp:Status><Assertion xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"_abc\" IssueInstant=\"2013-07-31T05:00:00Z\" Version=\"2.0\"><Issuer>http://sportngin.com</Issuer><signature>stuff</signature><Subject><NameID Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress\">jon.phenow@sportngin.com</NameID><SubjectConfirmation Method=\"urn:oasis:names:tc:SAML:2.0:cm:bearer\"><SubjectConfirmationData InResponseTo=\"123\" NotOnOrAfter=\"2013-07-31T05:03:00Z\" Recipient=\"http://saml.acs.url\"/></SubjectConfirmation></Subject><Conditions NotBefore=\"2013-07-31T04:59:55Z\" NotOnOrAfter=\"2013-07-31T06:00:00Z\"><AudienceRestriction><Audience>http://example.com</Audience></AudienceRestriction></Conditions><AttributeStatement><Attribute Name=\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress\"><AttributeValue>jon.phenow@sportngin.com</AttributeValue></Attribute></AttributeStatement><AuthnStatment AuthnInstant=\"2013-07-31T05:00:00Z\" SessionIndex=\"_abc\"><AuthnContext><AuthnContextClassRef>urn:federation:authentication:windows</AuthnContextClassRef></AuthnContext></AuthnStatment></Assertion></samlp:Response>")
29
31
  end
30
32
  end
31
33
 
@@ -34,7 +36,7 @@ module SamlIdp
34
36
 
35
37
  it "builds a legit raw XML file without a request ID" do
36
38
  Timecop.travel(Time.zone.local(2010, 6, 1, 13, 0, 0)) do
37
- subject.raw.should == "<samlp:Response ID=\"_abc\" Version=\"2.0\" IssueInstant=\"2010-06-01T13:00:00Z\" Destination=\"http://sportngin.com\" Consent=\"urn:oasis:names:tc:SAML:2.0:consent:unspecified\" xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\"><Issuer xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\">http://example.com</Issuer><samlp:Status><samlp:StatusCode Value=\"urn:oasis:names:tc:SAML:2.0:status:Success\"/></samlp:Status><Assertion xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"_abc\" IssueInstant=\"2013-07-31T05:00:00Z\" Version=\"2.0\"><Issuer>http://sportngin.com</Issuer><signature>stuff</signature><Subject><NameID Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress\">jon.phenow@sportngin.com</NameID><SubjectConfirmation Method=\"urn:oasis:names:tc:SAML:2.0:cm:bearer\"><SubjectConfirmationData InResponseTo=\"123\" NotOnOrAfter=\"2013-07-31T05:03:00Z\" Recipient=\"http://saml.acs.url\"/></SubjectConfirmation></Subject><Conditions NotBefore=\"2013-07-31T04:59:55Z\" NotOnOrAfter=\"2013-07-31T06:00:00Z\"><AudienceRestriction><Audience>http://example.com</Audience></AudienceRestriction></Conditions><AttributeStatement><Attribute Name=\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress\"><AttributeValue>jon.phenow@sportngin.com</AttributeValue></Attribute></AttributeStatement><AuthnStatment AuthnInstant=\"2013-07-31T05:00:00Z\" SessionIndex=\"_abc\"><AuthnContext><AuthnContextClassRef>urn:federation:authentication:windows</AuthnContextClassRef></AuthnContext></AuthnStatment></Assertion></samlp:Response>"
39
+ expect(subject.raw).to eq("<samlp:Response ID=\"_abc\" Version=\"2.0\" IssueInstant=\"2010-06-01T13:00:00Z\" Destination=\"http://sportngin.com\" Consent=\"urn:oasis:names:tc:SAML:2.0:consent:unspecified\" xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\"><Issuer xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\">http://example.com</Issuer><samlp:Status><samlp:StatusCode Value=\"urn:oasis:names:tc:SAML:2.0:status:Success\"/></samlp:Status><Assertion xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"_abc\" IssueInstant=\"2013-07-31T05:00:00Z\" Version=\"2.0\"><Issuer>http://sportngin.com</Issuer><signature>stuff</signature><Subject><NameID Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress\">jon.phenow@sportngin.com</NameID><SubjectConfirmation Method=\"urn:oasis:names:tc:SAML:2.0:cm:bearer\"><SubjectConfirmationData InResponseTo=\"123\" NotOnOrAfter=\"2013-07-31T05:03:00Z\" Recipient=\"http://saml.acs.url\"/></SubjectConfirmation></Subject><Conditions NotBefore=\"2013-07-31T04:59:55Z\" NotOnOrAfter=\"2013-07-31T06:00:00Z\"><AudienceRestriction><Audience>http://example.com</Audience></AudienceRestriction></Conditions><AttributeStatement><Attribute Name=\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress\"><AttributeValue>jon.phenow@sportngin.com</AttributeValue></Attribute></AttributeStatement><AuthnStatment AuthnInstant=\"2013-07-31T05:00:00Z\" SessionIndex=\"_abc\"><AuthnContext><AuthnContextClassRef>urn:federation:authentication:windows</AuthnContextClassRef></AuthnContext></AuthnStatment></Assertion></samlp:Response>")
38
40
  end
39
41
  end
40
42
  end
@@ -24,6 +24,8 @@ module SamlIdp
24
24
  key_transport: 'rsa-oaep-mgf1p',
25
25
  }
26
26
  end
27
+ let(:signed_response_opts) { true }
28
+ let(:unsigned_response_opts) { false }
27
29
  let(:subject_encrypted) { described_class.new(reference_id,
28
30
  response_id,
29
31
  issuer_uri,
@@ -35,7 +37,8 @@ module SamlIdp
35
37
  authn_context_classref,
36
38
  expiry,
37
39
  encryption_opts,
38
- session_expiry
40
+ session_expiry,
41
+ unsigned_response_opts
39
42
  )
40
43
  }
41
44
 
@@ -50,7 +53,8 @@ module SamlIdp
50
53
  authn_context_classref,
51
54
  expiry,
52
55
  nil,
53
- session_expiry
56
+ session_expiry,
57
+ signed_response_opts
54
58
  )
55
59
  }
56
60
 
@@ -63,23 +67,42 @@ module SamlIdp
63
67
  end
64
68
 
65
69
  it "has a valid build" do
66
- subject.build.should be_present
70
+ expect(subject.build).to be_present
67
71
  end
68
72
 
69
73
  it "builds encrypted" do
70
- subject_encrypted.build.should_not match(audience_uri)
74
+ expect(subject_encrypted.build).to_not match(audience_uri)
71
75
  encoded_xml = subject_encrypted.build
72
76
  resp_settings = saml_settings(saml_acs_url)
73
77
  resp_settings.private_key = Default::SECRET_KEY
74
78
  resp_settings.issuer = audience_uri
75
79
  saml_resp = OneLogin::RubySaml::Response.new(encoded_xml, settings: resp_settings)
76
80
  saml_resp.soft = false
77
- saml_resp.is_valid?.should == true
81
+ expect(saml_resp.is_valid?).to eq(true)
82
+ end
83
+
84
+ it "will build signed valid response" do
85
+ expect { subject.build }.not_to raise_error
86
+ signed_encoded_xml = subject.build
87
+ resp_settings = saml_settings(saml_acs_url)
88
+ resp_settings.private_key = Default::SECRET_KEY
89
+ resp_settings.issuer = audience_uri
90
+ saml_resp = OneLogin::RubySaml::Response.new(signed_encoded_xml, settings: resp_settings)
91
+ expect(
92
+ Nokogiri::XML(saml_resp.response).at_xpath(
93
+ "//p:Response//ds:Signature",
94
+ {
95
+ "p" => "urn:oasis:names:tc:SAML:2.0:protocol",
96
+ "ds" => "http://www.w3.org/2000/09/xmldsig#"
97
+ }
98
+ )).to be_present
99
+ expect(saml_resp.send(:validate_signature)).to eq(true)
100
+ expect(saml_resp.is_valid?).to eq(true)
78
101
  end
79
102
 
80
103
  it "sets session expiration" do
81
104
  saml_resp = OneLogin::RubySaml::Response.new(subject.build)
82
- saml_resp.session_expires_at.should == Time.local(1990, "jan", 2).iso8601
105
+ expect(saml_resp.session_expires_at).to eq Time.local(1990, "jan", 2).iso8601
83
106
  end
84
107
 
85
108
  context "session expiration is set to 0" do
@@ -89,14 +112,14 @@ module SamlIdp
89
112
  resp_settings = saml_settings(saml_acs_url)
90
113
  resp_settings.issuer = audience_uri
91
114
  saml_resp = OneLogin::RubySaml::Response.new(subject.build, settings: resp_settings)
92
- saml_resp.is_valid?.should == true
115
+ expect(saml_resp.is_valid?).to eq(true)
93
116
  end
94
117
 
95
118
  it "doesn't set a session expiration" do
96
119
  resp_settings = saml_settings(saml_acs_url)
97
120
  resp_settings.issuer = audience_uri
98
121
  saml_resp = OneLogin::RubySaml::Response.new(subject.build, settings: resp_settings)
99
- saml_resp.session_expires_at.should be_nil
122
+ expect(saml_resp.session_expires_at).to be_nil
100
123
  end
101
124
  end
102
125
  end
@@ -14,11 +14,11 @@ module SamlIdp
14
14
  let(:metadata_url) { "http://localhost:3000/metadata" }
15
15
 
16
16
  it "has a valid fingerprint" do
17
- subject.fingerprint.should == fingerprint
17
+ expect(subject.fingerprint).to eq(fingerprint)
18
18
  end
19
19
 
20
20
  it "has a valid metadata_url" do
21
- subject.metadata_url.should == metadata_url
21
+ expect(subject.metadata_url).to eq(metadata_url)
22
22
  end
23
23
 
24
24
  it { should be_valid }