saml_idp 0.7.1 → 0.10.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 (42) hide show
  1. checksums.yaml +5 -5
  2. data/Gemfile +1 -1
  3. data/README.md +40 -12
  4. data/lib/saml_idp/configurator.rb +1 -0
  5. data/lib/saml_idp/controller.rb +6 -2
  6. data/lib/saml_idp/encryptor.rb +1 -1
  7. data/lib/saml_idp/incoming_metadata.rb +9 -1
  8. data/lib/saml_idp/request.rb +14 -0
  9. data/lib/saml_idp/response_builder.rb +19 -5
  10. data/lib/saml_idp/saml_response.rb +15 -3
  11. data/lib/saml_idp/service_provider.rb +14 -0
  12. data/lib/saml_idp/signable.rb +1 -2
  13. data/lib/saml_idp/version.rb +1 -1
  14. data/lib/saml_idp/xml_security.rb +1 -1
  15. data/saml_idp.gemspec +26 -23
  16. data/spec/acceptance/idp_controller_spec.rb +5 -4
  17. data/spec/lib/saml_idp/algorithmable_spec.rb +6 -6
  18. data/spec/lib/saml_idp/assertion_builder_spec.rb +8 -8
  19. data/spec/lib/saml_idp/attribute_decorator_spec.rb +8 -8
  20. data/spec/lib/saml_idp/configurator_spec.rb +7 -7
  21. data/spec/lib/saml_idp/controller_spec.rb +23 -20
  22. data/spec/lib/saml_idp/encryptor_spec.rb +4 -4
  23. data/spec/lib/saml_idp/incoming_metadata_spec.rb +46 -0
  24. data/spec/lib/saml_idp/metadata_builder_spec.rb +7 -17
  25. data/spec/lib/saml_idp/name_id_formatter_spec.rb +3 -3
  26. data/spec/lib/saml_idp/request_spec.rb +22 -22
  27. data/spec/lib/saml_idp/response_builder_spec.rb +5 -3
  28. data/spec/lib/saml_idp/saml_response_spec.rb +31 -8
  29. data/spec/lib/saml_idp/service_provider_spec.rb +2 -2
  30. data/spec/lib/saml_idp/signable_spec.rb +1 -1
  31. data/spec/lib/saml_idp/signature_builder_spec.rb +2 -2
  32. data/spec/lib/saml_idp/signed_info_builder_spec.rb +3 -3
  33. data/spec/rails_app/app/controllers/saml_controller.rb +5 -1
  34. data/spec/rails_app/config/application.rb +0 -6
  35. data/spec/rails_app/config/environments/development.rb +1 -6
  36. data/spec/rails_app/config/environments/production.rb +1 -0
  37. data/spec/rails_app/config/environments/test.rb +1 -0
  38. data/spec/spec_helper.rb +3 -0
  39. data/spec/support/saml_request_macros.rb +2 -1
  40. data/spec/xml_security_spec.rb +12 -12
  41. metadata +85 -40
  42. data/spec/lib/saml_idp/.assertion_builder_spec.rb.swp +0 -0
@@ -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 }
@@ -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,11 @@ 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
+ if Gem::Requirement.new('< 4.1') =~ Gem::Version.new(Rails.version)
6
+ render :text => response.name_id
7
+ else
8
+ render :plain => response.name_id
9
+ end
6
10
  end
7
11
 
8
12
  end
@@ -50,11 +50,5 @@ module RailsApp
50
50
  # in your app. As such, your models will need to explicitly whitelist or blacklist accessible
51
51
  # parameters by using an attr_accessible or attr_protected declaration.
52
52
  # config.active_record.whitelist_attributes = true
53
-
54
- # Enable the asset pipeline
55
- config.assets.enabled = true
56
-
57
- # Version of your assets, change this if you want to expire all your assets
58
- config.assets.version = '1.0'
59
53
  end
60
54
  end
@@ -4,6 +4,7 @@ RailsApp::Application.configure do
4
4
  # In the development environment your application's code is reloaded on
5
5
  # every request. This slows down response time but is perfect for development
6
6
  # since you don't have to restart the web server when you make code changes.
7
+ config.eager_load = false if config.respond_to?(:eager_load)
7
8
  config.cache_classes = false
8
9
 
9
10
  # Log error messages when you accidentally call methods on nil.
@@ -28,10 +29,4 @@ RailsApp::Application.configure do
28
29
  # Log the query plan for queries taking more than this (works
29
30
  # with SQLite, MySQL, and PostgreSQL)
30
31
  #config.active_record.auto_explain_threshold_in_seconds = 0.5
31
-
32
- # Do not compress assets
33
- config.assets.compress = false
34
-
35
- # Expands the lines which load the assets
36
- config.assets.debug = true
37
32
  end
@@ -2,6 +2,7 @@ RailsApp::Application.configure do
2
2
  # Settings specified here will take precedence over those in config/application.rb
3
3
 
4
4
  # Code is not reloaded between requests
5
+ config.eager_load = true if config.respond_to?(:eager_load)
5
6
  config.cache_classes = true
6
7
 
7
8
  # Full error reports are disabled and caching is turned on
@@ -5,6 +5,7 @@ RailsApp::Application.configure do
5
5
  # test suite. You never need to work with it otherwise. Remember that
6
6
  # your test database is "scratch space" for the test suite and is wiped
7
7
  # and recreated between test runs. Don't rely on the data there!
8
+ config.eager_load = true if config.respond_to?(:eager_load)
8
9
  config.cache_classes = true
9
10
 
10
11
  # Configure static asset server for tests with Cache-Control for performance
@@ -45,4 +45,7 @@ RSpec.configure do |config|
45
45
  end
46
46
  end
47
47
 
48
+ SamlIdp::Default::SERVICE_PROVIDER[:metadata_url] = 'https://example.com/meta'
49
+ SamlIdp::Default::SERVICE_PROVIDER[:response_hosts] = ['foo.example.com']
50
+ SamlIdp::Default::SERVICE_PROVIDER[:assertion_consumer_logout_service_url] = 'https://foo.example.com/saml/logout'
48
51
  Capybara.default_host = "https://app.example.com"
@@ -15,7 +15,7 @@ module SamlRequestMacros
15
15
  'some_name_id',
16
16
  OpenSSL::Digest::SHA256
17
17
  )
18
- request_builder.encoded
18
+ Base64.strict_encode64(request_builder.signed)
19
19
  end
20
20
 
21
21
  def saml_settings(saml_acs_url = "https://foo.example.com/saml/consume")
@@ -23,6 +23,7 @@ module SamlRequestMacros
23
23
  settings.assertion_consumer_service_url = saml_acs_url
24
24
  settings.issuer = "http://example.com/issuer"
25
25
  settings.idp_sso_target_url = "http://idp.com/saml/idp"
26
+ settings.assertion_consumer_logout_service_url = 'https://foo.example.com/saml/logout'
26
27
  settings.idp_cert_fingerprint = SamlIdp::Default::FINGERPRINT
27
28
  settings.name_identifier_format = SamlIdp::Default::NAME_ID_FORMAT
28
29
  settings
@@ -7,7 +7,7 @@ module SamlIdp
7
7
  let(:base64cert) { document.elements["//ds:X509Certificate"].text }
8
8
 
9
9
  it "it run validate without throwing NS related exceptions" do
10
- document.validate_doc(base64cert, true).should be_falsey
10
+ expect(document.validate_doc(base64cert, true)).to be_falsey
11
11
  end
12
12
 
13
13
  it "it run validate with throwing NS related exceptions" do
@@ -57,22 +57,22 @@ module SamlIdp
57
57
  describe "Algorithms" do
58
58
  it "validate using SHA1" do
59
59
  document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_sha1, false))
60
- document.validate("F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72").should be_truthy
60
+ expect(document.validate("F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72")).to be_truthy
61
61
  end
62
62
 
63
63
  it "validate using SHA256" do
64
64
  document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_sha256, false))
65
- document.validate("28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA").should be_truthy
65
+ expect(document.validate("28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA")).to be_truthy
66
66
  end
67
67
 
68
68
  it "validate using SHA384" do
69
69
  document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_sha384, false))
70
- document.validate("F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72").should be_truthy
70
+ expect(document.validate("F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72")).to be_truthy
71
71
  end
72
72
 
73
73
  it "validate using SHA512" do
74
74
  document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_sha512, false))
75
- document.validate("F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72").should be_truthy
75
+ expect(document.validate("F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72")).to be_truthy
76
76
  end
77
77
  end
78
78
 
@@ -83,7 +83,7 @@ module SamlIdp
83
83
  document = XMLSecurity::SignedDocument.new(response)
84
84
  inclusive_namespaces = document.send(:extract_inclusive_namespaces)
85
85
 
86
- inclusive_namespaces.should == %w[xs]
86
+ expect(inclusive_namespaces).to eq %w[xs]
87
87
  end
88
88
 
89
89
  it "support implicit namespace resolution for exclusive canonicalization" do
@@ -91,7 +91,7 @@ module SamlIdp
91
91
  document = XMLSecurity::SignedDocument.new(response)
92
92
  inclusive_namespaces = document.send(:extract_inclusive_namespaces)
93
93
 
94
- inclusive_namespaces.should == %w[#default saml ds xs xsi]
94
+ expect(inclusive_namespaces).to eq %w[#default saml ds xs xsi]
95
95
  end
96
96
 
97
97
  it "return an empty list when inclusive namespace element is missing" do
@@ -101,7 +101,7 @@ module SamlIdp
101
101
  document = XMLSecurity::SignedDocument.new(response)
102
102
  inclusive_namespaces = document.send(:extract_inclusive_namespaces)
103
103
 
104
- inclusive_namespaces.should be_empty
104
+ expect(inclusive_namespaces).to be_empty
105
105
  end
106
106
  end
107
107
 
@@ -116,20 +116,20 @@ module SamlIdp
116
116
 
117
117
  it "be able to validate a good response" do
118
118
  Timecop.freeze Time.parse('2012-11-28 17:55:00 UTC') do
119
- response.stub(:validate_subject_confirmation).and_return(true)
120
- response.should be_is_valid
119
+ allow(response).to receive(:validate_subject_confirmation).and_return(true)
120
+ expect(response).to be_is_valid
121
121
  end
122
122
  end
123
123
 
124
124
  it "fail before response is valid" do
125
125
  Timecop.freeze Time.parse('2012-11-20 17:55:00 UTC') do
126
- response.should_not be_is_valid
126
+ expect(response).to_not be_is_valid
127
127
  end
128
128
  end
129
129
 
130
130
  it "fail after response expires" do
131
131
  Timecop.freeze Time.parse('2012-11-30 17:55:00 UTC') do
132
- response.should_not be_is_valid
132
+ expect(response).to_not be_is_valid
133
133
  end
134
134
  end
135
135
  end