saml_idp 0.7.2 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +5 -5
  2. data/Gemfile +1 -1
  3. data/README.md +59 -52
  4. data/lib/saml_idp/assertion_builder.rb +28 -3
  5. data/lib/saml_idp/configurator.rb +7 -1
  6. data/lib/saml_idp/controller.rb +21 -13
  7. data/lib/saml_idp/encryptor.rb +0 -1
  8. data/lib/saml_idp/fingerprint.rb +19 -0
  9. data/lib/saml_idp/incoming_metadata.rb +22 -1
  10. data/lib/saml_idp/metadata_builder.rb +23 -8
  11. data/lib/saml_idp/persisted_metadata.rb +4 -0
  12. data/lib/saml_idp/request.rb +26 -6
  13. data/lib/saml_idp/response_builder.rb +26 -6
  14. data/lib/saml_idp/saml_response.rb +62 -28
  15. data/lib/saml_idp/service_provider.rb +15 -6
  16. data/lib/saml_idp/signable.rb +1 -2
  17. data/lib/saml_idp/version.rb +1 -1
  18. data/lib/saml_idp/xml_security.rb +1 -1
  19. data/lib/saml_idp.rb +2 -1
  20. data/saml_idp.gemspec +45 -42
  21. data/spec/acceptance/idp_controller_spec.rb +5 -4
  22. data/spec/lib/saml_idp/algorithmable_spec.rb +6 -6
  23. data/spec/lib/saml_idp/assertion_builder_spec.rb +151 -8
  24. data/spec/lib/saml_idp/attribute_decorator_spec.rb +8 -8
  25. data/spec/lib/saml_idp/configurator_spec.rb +9 -7
  26. data/spec/lib/saml_idp/controller_spec.rb +53 -20
  27. data/spec/lib/saml_idp/encryptor_spec.rb +4 -4
  28. data/spec/lib/saml_idp/fingerprint_spec.rb +14 -0
  29. data/spec/lib/saml_idp/incoming_metadata_spec.rb +60 -0
  30. data/spec/lib/saml_idp/metadata_builder_spec.rb +30 -17
  31. data/spec/lib/saml_idp/name_id_formatter_spec.rb +3 -3
  32. data/spec/lib/saml_idp/request_spec.rb +78 -27
  33. data/spec/lib/saml_idp/response_builder_spec.rb +5 -3
  34. data/spec/lib/saml_idp/saml_response_spec.rb +127 -12
  35. data/spec/lib/saml_idp/service_provider_spec.rb +2 -2
  36. data/spec/lib/saml_idp/signable_spec.rb +1 -1
  37. data/spec/lib/saml_idp/signature_builder_spec.rb +2 -2
  38. data/spec/lib/saml_idp/signed_info_builder_spec.rb +3 -3
  39. data/spec/rails_app/app/controllers/saml_controller.rb +1 -1
  40. data/spec/rails_app/app/controllers/saml_idp_controller.rb +55 -3
  41. data/{app → spec/rails_app/app}/views/saml_idp/idp/new.html.erb +1 -5
  42. data/{app → spec/rails_app/app}/views/saml_idp/idp/saml_post.html.erb +1 -1
  43. data/spec/rails_app/config/application.rb +1 -6
  44. data/spec/rails_app/config/boot.rb +1 -1
  45. data/spec/rails_app/config/environments/development.rb +2 -5
  46. data/spec/rails_app/config/environments/production.rb +1 -0
  47. data/spec/rails_app/config/environments/test.rb +1 -0
  48. data/spec/spec_helper.rb +23 -1
  49. data/spec/support/certificates/sp_cert_req.csr +12 -0
  50. data/spec/support/certificates/sp_private_key.pem +16 -0
  51. data/spec/support/certificates/sp_x509_cert.crt +18 -0
  52. data/spec/support/saml_request_macros.rb +66 -4
  53. data/spec/support/security_helpers.rb +10 -0
  54. data/spec/xml_security_spec.rb +12 -12
  55. metadata +135 -81
  56. data/app/controllers/saml_idp/idp_controller.rb +0 -59
  57. data/spec/lib/saml_idp/.assertion_builder_spec.rb.swp +0 -0
@@ -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
  ]
@@ -1,7 +1,10 @@
1
1
  require 'spec_helper'
2
2
  module SamlIdp
3
3
  describe Request do
4
- let(:raw_authn_request) { "<samlp:AuthnRequest AssertionConsumerServiceURL='http://localhost:3000/saml/consume' Destination='http://localhost:1337/saml/auth' ID='_af43d1a0-e111-0130-661a-3c0754403fdb' IssueInstant='2013-08-06T22:01:35Z' Version='2.0' xmlns:samlp='urn:oasis:names:tc:SAML:2.0:protocol'><saml:Issuer xmlns:saml='urn:oasis:names:tc:SAML:2.0:assertion'>localhost:3000</saml:Issuer><samlp:NameIDPolicy AllowCreate='true' Format='urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress' xmlns:samlp='urn:oasis:names:tc:SAML:2.0:protocol'/><samlp:RequestedAuthnContext Comparison='exact'><saml:AuthnContextClassRef xmlns:saml='urn:oasis:names:tc:SAML:2.0:assertion'>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml:AuthnContextClassRef></samlp:RequestedAuthnContext></samlp:AuthnRequest>" }
4
+ let(:issuer) { 'localhost:3000' }
5
+ let(:raw_authn_request) do
6
+ "<samlp:AuthnRequest AssertionConsumerServiceURL='http://localhost:3000/saml/consume' Destination='http://localhost:1337/saml/auth' ID='_af43d1a0-e111-0130-661a-3c0754403fdb' IssueInstant='2013-08-06T22:01:35Z' Version='2.0' xmlns:samlp='urn:oasis:names:tc:SAML:2.0:protocol'><saml:Issuer xmlns:saml='urn:oasis:names:tc:SAML:2.0:assertion'>#{issuer}</saml:Issuer><samlp:NameIDPolicy AllowCreate='true' Format='urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress' xmlns:samlp='urn:oasis:names:tc:SAML:2.0:protocol'/><samlp:RequestedAuthnContext Comparison='exact'><saml:AuthnContextClassRef xmlns:saml='urn:oasis:names:tc:SAML:2.0:assertion'>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml:AuthnContextClassRef></samlp:RequestedAuthnContext></samlp:AuthnRequest>"
7
+ end
5
8
 
6
9
  describe "deflated request" do
7
10
  let(:deflated_request) { Base64.encode64(Zlib::Deflate.deflate(raw_authn_request, 9)[2..-5]) }
@@ -9,12 +12,12 @@ module SamlIdp
9
12
  subject { described_class.from_deflated_request deflated_request }
10
13
 
11
14
  it "inflates" do
12
- subject.request_id.should == "_af43d1a0-e111-0130-661a-3c0754403fdb"
15
+ expect(subject.request_id).to eq("_af43d1a0-e111-0130-661a-3c0754403fdb")
13
16
  end
14
17
 
15
18
  it "handles invalid SAML" do
16
19
  req = described_class.from_deflated_request "bang!"
17
- req.valid?.should == false
20
+ expect(req.valid?).to eq(false)
18
21
  end
19
22
  end
20
23
 
@@ -22,51 +25,99 @@ module SamlIdp
22
25
  subject { described_class.new raw_authn_request }
23
26
 
24
27
  it "has a valid request_id" do
25
- subject.request_id.should == "_af43d1a0-e111-0130-661a-3c0754403fdb"
28
+ expect(subject.request_id).to eq("_af43d1a0-e111-0130-661a-3c0754403fdb")
26
29
  end
27
30
 
28
31
  it "has a valid acs_url" do
29
- subject.acs_url.should == "http://localhost:3000/saml/consume"
32
+ expect(subject.acs_url).to eq("http://localhost:3000/saml/consume")
30
33
  end
31
34
 
32
35
  it "has a valid service_provider" do
33
- subject.service_provider.should be_a ServiceProvider
36
+ expect(subject.service_provider).to be_a ServiceProvider
34
37
  end
35
38
 
36
39
  it "has a valid service_provider" do
37
- subject.service_provider.should be_truthy
40
+ expect(subject.service_provider).to be_truthy
38
41
  end
39
42
 
40
43
  it "has a valid issuer" do
41
- subject.issuer.should == "localhost:3000"
44
+ expect(subject.issuer).to eq("localhost:3000")
42
45
  end
43
46
 
44
47
  it "has a valid valid_signature" do
45
- subject.valid_signature?.should be_truthy
48
+ expect(subject.valid_signature?).to be_truthy
46
49
  end
47
50
 
48
51
  it "should return acs_url for response_url" do
49
- subject.response_url.should == subject.acs_url
52
+ expect(subject.response_url).to eq(subject.acs_url)
50
53
  end
51
54
 
52
55
  it "is a authn request" do
53
- subject.authn_request?.should == true
56
+ expect(subject.authn_request?).to eq(true)
54
57
  end
55
58
 
56
59
  it "fetches internal request" do
57
- subject.request['ID'].should == subject.request_id
60
+ expect(subject.request['ID']).to eq(subject.request_id)
58
61
  end
59
62
 
60
- it "has a valid authn context" do
61
- subject.requested_authn_context.should == "urn:oasis:names:tc:SAML:2.0:ac:classes:Password"
63
+ it 'has a valid authn context' do
64
+ expect(subject.requested_authn_context).to eq('urn:oasis:names:tc:SAML:2.0:ac:classes:Password')
62
65
  end
63
66
 
64
- it "does not permit empty issuer" do
65
- raw_req = raw_authn_request.gsub('localhost:3000', '')
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
+ context 'the issuer is empty' do
68
+ let(:issuer) { nil }
69
+ let(:logger) { ->(msg) { puts msg } }
70
+
71
+ before do
72
+ allow(SamlIdp.config).to receive(:logger).and_return(logger)
73
+ end
74
+
75
+ it 'is invalid' do
76
+ expect(subject.issuer).to_not eq('')
77
+ expect(subject.issuer).to be_nil
78
+ expect(subject.valid?).to eq(false)
79
+ end
80
+
81
+ context 'a Ruby Logger is configured' do
82
+ let(:logger) { Logger.new($stdout) }
83
+
84
+ before do
85
+ allow(logger).to receive(:info)
86
+ end
87
+
88
+ it 'logs an error message' do
89
+ expect(subject.valid?).to be false
90
+ expect(logger).to have_received(:info).with('Unable to find service provider for issuer ')
91
+ end
92
+ end
93
+
94
+ context 'a Logger-like logger is configured' do
95
+ let(:logger) do
96
+ Class.new {
97
+ def info(msg); end
98
+ }.new
99
+ end
100
+
101
+ before do
102
+ allow(logger).to receive(:info)
103
+ end
104
+
105
+ it 'logs an error message' do
106
+ expect(subject.valid?).to be false
107
+ expect(logger).to have_received(:info).with('Unable to find service provider for issuer ')
108
+ end
109
+ end
110
+
111
+ context 'a logger lambda is configured' do
112
+ let(:logger) { double }
113
+
114
+ before { allow(logger).to receive(:call) }
115
+
116
+ it 'logs an error message' do
117
+ expect(subject.valid?).to be false
118
+ expect(logger).to have_received(:call).with('Unable to find service provider for issuer ')
119
+ end
120
+ end
70
121
  end
71
122
  end
72
123
 
@@ -76,31 +127,31 @@ module SamlIdp
76
127
  subject { described_class.new raw_logout_request }
77
128
 
78
129
  it "has a valid request_id" do
79
- subject.request_id.should == '_some_response_id'
130
+ expect(subject.request_id).to eq('_some_response_id')
80
131
  end
81
132
 
82
133
  it "should be flagged as a logout_request" do
83
- subject.logout_request?.should == true
134
+ expect(subject.logout_request?).to eq(true)
84
135
  end
85
136
 
86
137
  it "should have a valid name_id" do
87
- subject.name_id.should == 'some_name_id'
138
+ expect(subject.name_id).to eq('some_name_id')
88
139
  end
89
140
 
90
141
  it "should have a session index" do
91
- subject.session_index.should == 'abc123index'
142
+ expect(subject.session_index).to eq('abc123index')
92
143
  end
93
144
 
94
145
  it "should have a valid issuer" do
95
- subject.issuer.should == 'http://example.com'
146
+ expect(subject.issuer).to eq('http://example.com')
96
147
  end
97
148
 
98
149
  it "fetches internal request" do
99
- subject.request['ID'].should == subject.request_id
150
+ expect(subject.request['ID']).to eq(subject.request_id)
100
151
  end
101
152
 
102
153
  it "should return logout_url for response_url" do
103
- subject.response_url.should == subject.logout_url
154
+ expect(subject.response_url).to eq(subject.logout_url)
104
155
  end
105
156
  end
106
157
  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,10 @@ 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 }
29
+ let(:signed_assertion_opts) { true }
30
+ let(:compress_opts) { false }
27
31
  let(:subject_encrypted) { described_class.new(reference_id,
28
32
  response_id,
29
33
  issuer_uri,
@@ -35,7 +39,12 @@ module SamlIdp
35
39
  authn_context_classref,
36
40
  expiry,
37
41
  encryption_opts,
38
- session_expiry
42
+ session_expiry,
43
+ nil,
44
+ nil,
45
+ unsigned_response_opts,
46
+ signed_assertion_opts,
47
+ compress_opts
39
48
  )
40
49
  }
41
50
 
@@ -50,7 +59,12 @@ module SamlIdp
50
59
  authn_context_classref,
51
60
  expiry,
52
61
  nil,
53
- session_expiry
62
+ session_expiry,
63
+ nil,
64
+ nil,
65
+ signed_response_opts,
66
+ signed_assertion_opts,
67
+ compress_opts
54
68
  )
55
69
  }
56
70
 
@@ -63,23 +77,124 @@ module SamlIdp
63
77
  end
64
78
 
65
79
  it "has a valid build" do
66
- subject.build.should be_present
80
+ expect(subject.build).to be_present
81
+ end
82
+
83
+ context "encrypted" do
84
+ it "builds encrypted" do
85
+ expect(subject_encrypted.build).to_not match(audience_uri)
86
+ encoded_xml = subject_encrypted.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(encoded_xml, settings: resp_settings)
91
+ saml_resp.soft = false
92
+ expect(saml_resp.is_valid?).to eq(true)
93
+ end
94
+ end
95
+
96
+ context "signed response" do
97
+ let(:resp_settings) do
98
+ resp_settings = saml_settings(saml_acs_url)
99
+ resp_settings.private_key = Default::SECRET_KEY
100
+ resp_settings.issuer = audience_uri
101
+ resp_settings
102
+ end
103
+
104
+ it "will build signed valid response" do
105
+ expect { subject.build }.not_to raise_error
106
+ signed_encoded_xml = subject.build
107
+ saml_resp = OneLogin::RubySaml::Response.new(signed_encoded_xml, settings: resp_settings)
108
+ expect(
109
+ Nokogiri::XML(saml_resp.response).at_xpath(
110
+ "//p:Response//ds:Signature",
111
+ {
112
+ "p" => "urn:oasis:names:tc:SAML:2.0:protocol",
113
+ "ds" => "http://www.w3.org/2000/09/xmldsig#"
114
+ }
115
+ )).to be_present
116
+ expect(saml_resp.send(:validate_signature)).to eq(true)
117
+ expect(saml_resp.is_valid?).to eq(true)
118
+ end
119
+
120
+ context "when signed_assertion_opts is true" do
121
+ it "builds a signed assertion" do
122
+ expect { subject.build }.not_to raise_error
123
+ signed_encoded_xml = subject.build
124
+ saml_resp = OneLogin::RubySaml::Response.new(signed_encoded_xml, settings: resp_settings)
125
+ expect(
126
+ Nokogiri::XML(saml_resp.response).at_xpath(
127
+ "//p:Response//a:Assertion//ds:Signature",
128
+ {
129
+ "p" => "urn:oasis:names:tc:SAML:2.0:protocol",
130
+ "a" => "urn:oasis:names:tc:SAML:2.0:assertion",
131
+ "ds" => "http://www.w3.org/2000/09/xmldsig#"
132
+ }
133
+ )).to be_present
134
+ end
135
+ end
136
+
137
+ context "when signed_assertion_opts is false" do
138
+ let(:signed_assertion_opts) { false }
139
+
140
+ it "builds a raw assertion" do
141
+ expect { subject.build }.not_to raise_error
142
+ signed_encoded_xml = subject.build
143
+ saml_resp = OneLogin::RubySaml::Response.new(signed_encoded_xml, settings: resp_settings)
144
+ expect(
145
+ Nokogiri::XML(saml_resp.response).at_xpath(
146
+ "//p:Response//a:Assertion",
147
+ {
148
+ "p" => "urn:oasis:names:tc:SAML:2.0:protocol",
149
+ "a" => "urn:oasis:names:tc:SAML:2.0:assertion"
150
+ }
151
+ )).to be_present
152
+
153
+ expect(
154
+ Nokogiri::XML(saml_resp.response).at_xpath(
155
+ "//p:Response//Assertion//ds:Signature",
156
+ {
157
+ "p" => "urn:oasis:names:tc:SAML:2.0:protocol",
158
+ "ds" => "http://www.w3.org/2000/09/xmldsig#"
159
+ }
160
+ )).to_not be_present
161
+ end
162
+ end
163
+
164
+ context "when compress opts is true" do
165
+ let(:compress_opts) { true }
166
+ it "will build a compressed valid response" do
167
+ expect { subject.build }.not_to raise_error
168
+ compressed_signed_encoded_xml = subject.build
169
+ saml_resp = OneLogin::RubySaml::Response.new(compressed_signed_encoded_xml, settings: resp_settings)
170
+ expect(saml_resp.send(:validate_signature)).to eq(true)
171
+ expect(saml_resp.is_valid?).to eq(true)
172
+ end
173
+ end
67
174
  end
68
175
 
69
- it "builds encrypted" do
70
- subject_encrypted.build.should_not match(audience_uri)
71
- encoded_xml = subject_encrypted.build
176
+ it "will build signed valid response" do
177
+ expect { subject.build }.not_to raise_error
178
+ signed_encoded_xml = subject.build
72
179
  resp_settings = saml_settings(saml_acs_url)
73
180
  resp_settings.private_key = Default::SECRET_KEY
74
181
  resp_settings.issuer = audience_uri
75
- saml_resp = OneLogin::RubySaml::Response.new(encoded_xml, settings: resp_settings)
76
- saml_resp.soft = false
77
- saml_resp.is_valid?.should == true
182
+ saml_resp = OneLogin::RubySaml::Response.new(signed_encoded_xml, settings: resp_settings)
183
+ expect(
184
+ Nokogiri::XML(saml_resp.response).at_xpath(
185
+ "//p:Response//ds:Signature",
186
+ {
187
+ "p" => "urn:oasis:names:tc:SAML:2.0:protocol",
188
+ "ds" => "http://www.w3.org/2000/09/xmldsig#"
189
+ }
190
+ )).to be_present
191
+ expect(saml_resp.send(:validate_signature)).to eq(true)
192
+ expect(saml_resp.is_valid?).to eq(true)
78
193
  end
79
194
 
80
195
  it "sets session expiration" do
81
196
  saml_resp = OneLogin::RubySaml::Response.new(subject.build)
82
- saml_resp.session_expires_at.should == Time.local(1990, "jan", 2).iso8601
197
+ expect(saml_resp.session_expires_at).to eq Time.local(1990, "jan", 2).iso8601
83
198
  end
84
199
 
85
200
  context "session expiration is set to 0" do
@@ -89,14 +204,14 @@ module SamlIdp
89
204
  resp_settings = saml_settings(saml_acs_url)
90
205
  resp_settings.issuer = audience_uri
91
206
  saml_resp = OneLogin::RubySaml::Response.new(subject.build, settings: resp_settings)
92
- saml_resp.is_valid?.should == true
207
+ expect(saml_resp.is_valid?).to eq(true)
93
208
  end
94
209
 
95
210
  it "doesn't set a session expiration" do
96
211
  resp_settings = saml_settings(saml_acs_url)
97
212
  resp_settings.issuer = audience_uri
98
213
  saml_resp = OneLogin::RubySaml::Response.new(subject.build, settings: resp_settings)
99
- saml_resp.session_expires_at.should be_nil
214
+ expect(saml_resp.session_expires_at).to be_nil
100
215
  end
101
216
  end
102
217
  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 }
@@ -70,7 +70,7 @@ module SamlIdp
70
70
  end
71
71
 
72
72
  it "has a valid signed" do
73
- subject.signed.should match all_regex
73
+ expect(subject.signed).to match all_regex
74
74
  end
75
75
 
76
76
  end
@@ -9,11 +9,11 @@ module SamlIdp
9
9
  ) }
10
10
 
11
11
  before do
12
- Time.stub now: Time.parse("Jul 31 2013")
12
+ allow(Time).to receive(:now).and_return Time.parse("Jul 31 2013")
13
13
  end
14
14
 
15
15
  it "builds a legit raw XML file" do
16
- subject.raw.should == "<ds:Signature xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"><ds:SignedInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"><ds:CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/><ds:SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha256\"/><ds:Reference URI=\"#_abc\"><ds:Transforms><ds:Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"/><ds:Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/></ds:Transforms><ds:DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha256\"/><ds:DigestValue>em8csGAWynywpe8S4nN64o56/4DosXi2XWMY6RJ6YfA=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>jvEbD/rsiPKmoXy7Lhm+FGn88NPGlap4EcPZ2fvjBnk03YESs87FXAIiZZEzN5xq4sBZksUmZe2bV3rrr9sxQNgQawmrrvr66ot7cJiv0ETFArr6kQIZaR5g/V0M4ydxvrfefp6cQVI0hXvmxi830pq0tISiO4J7tyBNX/kvhZk=</ds:SignatureValue><KeyInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\"><ds:X509Data><ds:X509Certificate>MIIDqzCCAxSgAwIBAgIBATANBgkqhkiG9w0BAQsFADCBhjELMAkGA1UEBhMCQVUxDDAKBgNVBAgTA05TVzEPMA0GA1UEBxMGU3lkbmV5MQwwCgYDVQQKDANQSVQxCTAHBgNVBAsMADEYMBYGA1UEAwwPbGF3cmVuY2VwaXQuY29tMSUwIwYJKoZIhvcNAQkBDBZsYXdyZW5jZS5waXRAZ21haWwuY29tMB4XDTEyMDQyODAyMjIyOFoXDTMyMDQyMzAyMjIyOFowgYYxCzAJBgNVBAYTAkFVMQwwCgYDVQQIEwNOU1cxDzANBgNVBAcTBlN5ZG5leTEMMAoGA1UECgwDUElUMQkwBwYDVQQLDAAxGDAWBgNVBAMMD2xhd3JlbmNlcGl0LmNvbTElMCMGCSqGSIb3DQEJAQwWbGF3cmVuY2UucGl0QGdtYWlsLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuBywPNlC1FopGLYfF96SotiK8Nj6/nW084O4omRMifzy7x955RLEy673q2aiJNB3LvE6Xvkt9cGtxtNoOXw1g2UvHKpldQbr6bOEjLNeDNW7j0ob+JrRvAUOK9CRgdyw5MC6lwqVQQ5C1DnaT/2fSBFjasBFTR24dEpfTy8HfKECAwEAAaOCASUwggEhMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgUgMB0GA1UdDgQWBBQNBGmmt3ytKpcJaBaYNbnyU2xkazATBgNVHSUEDDAKBggrBgEFBQcDATAdBglghkgBhvhCAQ0EEBYOVGVzdCBYNTA5IGNlcnQwgbMGA1UdIwSBqzCBqIAUDQRpprd8rSqXCWgWmDW58lNsZGuhgYykgYkwgYYxCzAJBgNVBAYTAkFVMQwwCgYDVQQIEwNOU1cxDzANBgNVBAcTBlN5ZG5leTEMMAoGA1UECgwDUElUMQkwBwYDVQQLDAAxGDAWBgNVBAMMD2xhd3JlbmNlcGl0LmNvbTElMCMGCSqGSIb3DQEJAQwWbGF3cmVuY2UucGl0QGdtYWlsLmNvbYIBATANBgkqhkiG9w0BAQsFAAOBgQAEcVUPBX7uZmzqZJfy+tUPOT5ImNQj8VE2lerhnFjnGPHmHIqhpzgnwHQujJfs/a309Wm5qwcCaC1eO5cWjcG0x3OjdllsgYDatl5GAumtBx8J3NhWRqNUgitCIkQlxHIwUfgQaCushYgDDL5YbIQa++egCgpIZ+T0Dj5oRew//A==</ds:X509Certificate></ds:X509Data></KeyInfo></ds:Signature>"
16
+ expect(subject.raw).to eq("<ds:Signature xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"><ds:SignedInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"><ds:CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/><ds:SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha256\"/><ds:Reference URI=\"#_abc\"><ds:Transforms><ds:Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"/><ds:Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/></ds:Transforms><ds:DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha256\"/><ds:DigestValue>em8csGAWynywpe8S4nN64o56/4DosXi2XWMY6RJ6YfA=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>jvEbD/rsiPKmoXy7Lhm+FGn88NPGlap4EcPZ2fvjBnk03YESs87FXAIiZZEzN5xq4sBZksUmZe2bV3rrr9sxQNgQawmrrvr66ot7cJiv0ETFArr6kQIZaR5g/V0M4ydxvrfefp6cQVI0hXvmxi830pq0tISiO4J7tyBNX/kvhZk=</ds:SignatureValue><KeyInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\"><ds:X509Data><ds:X509Certificate>MIIDqzCCAxSgAwIBAgIBATANBgkqhkiG9w0BAQsFADCBhjELMAkGA1UEBhMCQVUxDDAKBgNVBAgTA05TVzEPMA0GA1UEBxMGU3lkbmV5MQwwCgYDVQQKDANQSVQxCTAHBgNVBAsMADEYMBYGA1UEAwwPbGF3cmVuY2VwaXQuY29tMSUwIwYJKoZIhvcNAQkBDBZsYXdyZW5jZS5waXRAZ21haWwuY29tMB4XDTEyMDQyODAyMjIyOFoXDTMyMDQyMzAyMjIyOFowgYYxCzAJBgNVBAYTAkFVMQwwCgYDVQQIEwNOU1cxDzANBgNVBAcTBlN5ZG5leTEMMAoGA1UECgwDUElUMQkwBwYDVQQLDAAxGDAWBgNVBAMMD2xhd3JlbmNlcGl0LmNvbTElMCMGCSqGSIb3DQEJAQwWbGF3cmVuY2UucGl0QGdtYWlsLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuBywPNlC1FopGLYfF96SotiK8Nj6/nW084O4omRMifzy7x955RLEy673q2aiJNB3LvE6Xvkt9cGtxtNoOXw1g2UvHKpldQbr6bOEjLNeDNW7j0ob+JrRvAUOK9CRgdyw5MC6lwqVQQ5C1DnaT/2fSBFjasBFTR24dEpfTy8HfKECAwEAAaOCASUwggEhMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgUgMB0GA1UdDgQWBBQNBGmmt3ytKpcJaBaYNbnyU2xkazATBgNVHSUEDDAKBggrBgEFBQcDATAdBglghkgBhvhCAQ0EEBYOVGVzdCBYNTA5IGNlcnQwgbMGA1UdIwSBqzCBqIAUDQRpprd8rSqXCWgWmDW58lNsZGuhgYykgYkwgYYxCzAJBgNVBAYTAkFVMQwwCgYDVQQIEwNOU1cxDzANBgNVBAcTBlN5ZG5leTEMMAoGA1UECgwDUElUMQkwBwYDVQQLDAAxGDAWBgNVBAMMD2xhd3JlbmNlcGl0LmNvbTElMCMGCSqGSIb3DQEJAQwWbGF3cmVuY2UucGl0QGdtYWlsLmNvbYIBATANBgkqhkiG9w0BAQsFAAOBgQAEcVUPBX7uZmzqZJfy+tUPOT5ImNQj8VE2lerhnFjnGPHmHIqhpzgnwHQujJfs/a309Wm5qwcCaC1eO5cWjcG0x3OjdllsgYDatl5GAumtBx8J3NhWRqNUgitCIkQlxHIwUfgQaCushYgDDL5YbIQa++egCgpIZ+T0Dj5oRew//A==</ds:X509Certificate></ds:X509Data></KeyInfo></ds:Signature>")
17
17
  end
18
18
  end
19
19
  end
@@ -11,15 +11,15 @@ module SamlIdp
11
11
  ) }
12
12
 
13
13
  before do
14
- Time.stub now: Time.parse("Jul 31 2013")
14
+ allow(Time).to receive(:now).and_return Time.parse("Jul 31 2013")
15
15
  end
16
16
 
17
17
  it "builds a legit raw XML file" do
18
- subject.raw.should == "<ds:SignedInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"><ds:CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"></ds:CanonicalizationMethod><ds:SignatureMethod Algorithm=\"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256\"></ds:SignatureMethod><ds:Reference URI=\"#_abc\"><ds:Transforms><ds:Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"></ds:Transform><ds:Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm=\"http://www.w3.org/2001/04/xmlenc#sha256\"></ds:DigestMethod><ds:DigestValue>em8csGAWynywpe8S4nN64o56/4DosXi2XWMY6RJ6YfA=</ds:DigestValue></ds:Reference></ds:SignedInfo>"
18
+ expect(subject.raw).to eq("<ds:SignedInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"><ds:CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"></ds:CanonicalizationMethod><ds:SignatureMethod Algorithm=\"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256\"></ds:SignatureMethod><ds:Reference URI=\"#_abc\"><ds:Transforms><ds:Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"></ds:Transform><ds:Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm=\"http://www.w3.org/2001/04/xmlenc#sha256\"></ds:DigestMethod><ds:DigestValue>em8csGAWynywpe8S4nN64o56/4DosXi2XWMY6RJ6YfA=</ds:DigestValue></ds:Reference></ds:SignedInfo>")
19
19
  end
20
20
 
21
21
  it "builds a legit digest of the XML file" do
22
- subject.signed.should == "hKLeWLRgatHcV6N5Fc8aKveqNp6Y/J4m2WSYp0awGFtsCTa/2nab32wI3du+3kuuIy59EDKeUhHVxEfyhoHUo6xTZuO2N7XcTpSonuZ/CB3WjozC2Q/9elss3z1rOC3154v5pW4puirLPRoG+Pwi8SmptxNRHczr6NvmfYmmGfo="
22
+ expect(subject.signed).to eq("hKLeWLRgatHcV6N5Fc8aKveqNp6Y/J4m2WSYp0awGFtsCTa/2nab32wI3du+3kuuIy59EDKeUhHVxEfyhoHUo6xTZuO2N7XcTpSonuZ/CB3WjozC2Q/9elss3z1rOC3154v5pW4puirLPRoG+Pwi8SmptxNRHczr6NvmfYmmGfo=")
23
23
  end
24
24
  end
25
25
  end
@@ -2,7 +2,7 @@ class SamlController < ApplicationController
2
2
 
3
3
  def consume
4
4
  response = OneLogin::RubySaml::Response.new(params[:SAMLResponse])
5
- render :text => response.name_id
5
+ render :plain => response.name_id
6
6
  end
7
7
 
8
8
  end