icn_saml_idp 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (129) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +2 -0
  3. data/LICENSE +22 -0
  4. data/README.md +238 -0
  5. data/app/controllers/saml_idp/idp_controller.rb +53 -0
  6. data/app/views/saml_idp/idp/new.html.erb +22 -0
  7. data/app/views/saml_idp/idp/saml_post.html.erb +14 -0
  8. data/lib/saml_idp.rb +92 -0
  9. data/lib/saml_idp/algorithmable.rb +19 -0
  10. data/lib/saml_idp/assertion_builder.rb +172 -0
  11. data/lib/saml_idp/attribute_decorator.rb +27 -0
  12. data/lib/saml_idp/attributeable.rb +24 -0
  13. data/lib/saml_idp/configurator.rb +48 -0
  14. data/lib/saml_idp/controller.rb +128 -0
  15. data/lib/saml_idp/default.rb +49 -0
  16. data/lib/saml_idp/encryptor.rb +86 -0
  17. data/lib/saml_idp/engine.rb +5 -0
  18. data/lib/saml_idp/hashable.rb +26 -0
  19. data/lib/saml_idp/incoming_metadata.rb +144 -0
  20. data/lib/saml_idp/logout_builder.rb +42 -0
  21. data/lib/saml_idp/logout_request_builder.rb +34 -0
  22. data/lib/saml_idp/logout_response_builder.rb +35 -0
  23. data/lib/saml_idp/metadata_builder.rb +160 -0
  24. data/lib/saml_idp/name_id_formatter.rb +68 -0
  25. data/lib/saml_idp/persisted_metadata.rb +10 -0
  26. data/lib/saml_idp/request.rb +180 -0
  27. data/lib/saml_idp/response_builder.rb +62 -0
  28. data/lib/saml_idp/saml_response.rb +79 -0
  29. data/lib/saml_idp/service_provider.rb +76 -0
  30. data/lib/saml_idp/signable.rb +131 -0
  31. data/lib/saml_idp/signature_builder.rb +42 -0
  32. data/lib/saml_idp/signed_info_builder.rb +88 -0
  33. data/lib/saml_idp/version.rb +4 -0
  34. data/lib/saml_idp/xml_security.rb +181 -0
  35. data/saml_idp.gemspec +65 -0
  36. data/spec/acceptance/acceptance_helper.rb +9 -0
  37. data/spec/acceptance/idp_controller_spec.rb +16 -0
  38. data/spec/lib/saml_idp/algorithmable_spec.rb +48 -0
  39. data/spec/lib/saml_idp/assertion_builder_spec.rb +106 -0
  40. data/spec/lib/saml_idp/attribute_decorator_spec.rb +53 -0
  41. data/spec/lib/saml_idp/configurator_spec.rb +43 -0
  42. data/spec/lib/saml_idp/controller_spec.rb +94 -0
  43. data/spec/lib/saml_idp/encryptor_spec.rb +27 -0
  44. data/spec/lib/saml_idp/logout_request_builder_spec.rb +41 -0
  45. data/spec/lib/saml_idp/logout_response_builder_spec.rb +41 -0
  46. data/spec/lib/saml_idp/metadata_builder_spec.rb +19 -0
  47. data/spec/lib/saml_idp/name_id_formatter_spec.rb +42 -0
  48. data/spec/lib/saml_idp/request_spec.rb +106 -0
  49. data/spec/lib/saml_idp/response_builder_spec.rb +42 -0
  50. data/spec/lib/saml_idp/saml_response_spec.rb +68 -0
  51. data/spec/lib/saml_idp/service_provider_spec.rb +27 -0
  52. data/spec/lib/saml_idp/signable_spec.rb +77 -0
  53. data/spec/lib/saml_idp/signature_builder_spec.rb +19 -0
  54. data/spec/lib/saml_idp/signed_info_builder_spec.rb +25 -0
  55. data/spec/rails_app/.gitignore +15 -0
  56. data/spec/rails_app/README.rdoc +261 -0
  57. data/spec/rails_app/Rakefile +7 -0
  58. data/spec/rails_app/app/assets/images/rails.png +0 -0
  59. data/spec/rails_app/app/assets/javascripts/application.js +15 -0
  60. data/spec/rails_app/app/assets/stylesheets/application.css +13 -0
  61. data/spec/rails_app/app/controllers/application_controller.rb +3 -0
  62. data/spec/rails_app/app/controllers/saml_controller.rb +8 -0
  63. data/spec/rails_app/app/controllers/saml_idp_controller.rb +11 -0
  64. data/spec/rails_app/app/helpers/application_helper.rb +2 -0
  65. data/spec/rails_app/app/mailers/.gitkeep +0 -0
  66. data/spec/rails_app/app/models/.gitkeep +0 -0
  67. data/spec/rails_app/app/views/layouts/application.html.erb +14 -0
  68. data/spec/rails_app/config.ru +4 -0
  69. data/spec/rails_app/config/application.rb +60 -0
  70. data/spec/rails_app/config/boot.rb +6 -0
  71. data/spec/rails_app/config/database.yml +25 -0
  72. data/spec/rails_app/config/environment.rb +5 -0
  73. data/spec/rails_app/config/environments/development.rb +37 -0
  74. data/spec/rails_app/config/environments/production.rb +67 -0
  75. data/spec/rails_app/config/environments/test.rb +37 -0
  76. data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  77. data/spec/rails_app/config/initializers/inflections.rb +15 -0
  78. data/spec/rails_app/config/initializers/mime_types.rb +5 -0
  79. data/spec/rails_app/config/initializers/secret_token.rb +7 -0
  80. data/spec/rails_app/config/initializers/session_store.rb +8 -0
  81. data/spec/rails_app/config/initializers/wrap_parameters.rb +14 -0
  82. data/spec/rails_app/config/locales/en.yml +5 -0
  83. data/spec/rails_app/config/routes.rb +6 -0
  84. data/spec/rails_app/db/seeds.rb +7 -0
  85. data/spec/rails_app/doc/README_FOR_APP +2 -0
  86. data/spec/rails_app/lib/assets/.gitkeep +0 -0
  87. data/spec/rails_app/lib/tasks/.gitkeep +0 -0
  88. data/spec/rails_app/log/.gitkeep +0 -0
  89. data/spec/rails_app/public/404.html +26 -0
  90. data/spec/rails_app/public/422.html +26 -0
  91. data/spec/rails_app/public/500.html +25 -0
  92. data/spec/rails_app/public/favicon.ico +0 -0
  93. data/spec/rails_app/public/index.html +241 -0
  94. data/spec/rails_app/public/robots.txt +5 -0
  95. data/spec/rails_app/script/rails +6 -0
  96. data/spec/rails_app/test/fixtures/.gitkeep +0 -0
  97. data/spec/rails_app/test/functional/.gitkeep +0 -0
  98. data/spec/rails_app/test/integration/.gitkeep +0 -0
  99. data/spec/rails_app/test/performance/browsing_test.rb +12 -0
  100. data/spec/rails_app/test/test_helper.rb +13 -0
  101. data/spec/rails_app/test/unit/.gitkeep +0 -0
  102. data/spec/rails_app/vendor/assets/javascripts/.gitkeep +0 -0
  103. data/spec/rails_app/vendor/assets/stylesheets/.gitkeep +0 -0
  104. data/spec/rails_app/vendor/plugins/.gitkeep +0 -0
  105. data/spec/spec_helper.rb +49 -0
  106. data/spec/support/certificates/certificate1 +12 -0
  107. data/spec/support/certificates/r1_certificate2_base64 +1 -0
  108. data/spec/support/responses/adfs_response_sha1.xml +46 -0
  109. data/spec/support/responses/adfs_response_sha256.xml +46 -0
  110. data/spec/support/responses/adfs_response_sha384.xml +46 -0
  111. data/spec/support/responses/adfs_response_sha512.xml +46 -0
  112. data/spec/support/responses/logoutresponse_fixtures.rb +67 -0
  113. data/spec/support/responses/no_signature_ns.xml +48 -0
  114. data/spec/support/responses/open_saml_response.xml +56 -0
  115. data/spec/support/responses/r1_response6.xml.base64 +1 -0
  116. data/spec/support/responses/response1.xml.base64 +1 -0
  117. data/spec/support/responses/response2.xml.base64 +79 -0
  118. data/spec/support/responses/response3.xml.base64 +66 -0
  119. data/spec/support/responses/response4.xml.base64 +93 -0
  120. data/spec/support/responses/response5.xml.base64 +102 -0
  121. data/spec/support/responses/response_with_ampersands.xml +139 -0
  122. data/spec/support/responses/response_with_ampersands.xml.base64 +93 -0
  123. data/spec/support/responses/simple_saml_php.xml +71 -0
  124. data/spec/support/responses/starfield_response.xml.base64 +1 -0
  125. data/spec/support/responses/wrapped_response_2.xml.base64 +150 -0
  126. data/spec/support/saml_request_macros.rb +38 -0
  127. data/spec/support/security_helpers.rb +61 -0
  128. data/spec/xml_security_spec.rb +137 -0
  129. metadata +465 -0
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+ require 'saml_idp/logout_request_builder'
3
+
4
+ module SamlIdp
5
+ describe LogoutRequestBuilder do
6
+ before do
7
+ Timecop.freeze(Time.local(1990))
8
+ end
9
+
10
+ after do
11
+ Timecop.return
12
+ end
13
+
14
+ let(:response_id) { 'some_response_id' }
15
+ let(:issuer_uri) { 'http://example.com' }
16
+ let(:saml_slo_url) { 'http://localhost:3000/saml/logout' }
17
+ let(:name_id) { 'some_name_id' }
18
+ let(:algorithm) { OpenSSL::Digest::SHA256 }
19
+
20
+ subject do
21
+ described_class.new(
22
+ response_id,
23
+ issuer_uri,
24
+ saml_slo_url,
25
+ name_id,
26
+ algorithm
27
+ )
28
+ end
29
+
30
+ it "is a valid SloLogoutrequest" do
31
+ Timecop.travel(Time.zone.local(2010, 6, 1, 13, 0, 0)) do
32
+ slo_request = OneLogin::RubySaml::SloLogoutrequest.new(
33
+ subject.encoded,
34
+ settings: saml_settings('localhost:3000')
35
+ )
36
+ slo_request.soft = false
37
+ expect(slo_request.is_valid?).to eq true
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+ require 'saml_idp/logout_response_builder'
3
+
4
+ module SamlIdp
5
+ describe LogoutResponseBuilder do
6
+ before do
7
+ Timecop.freeze(Time.local(1990))
8
+ end
9
+
10
+ after do
11
+ Timecop.return
12
+ end
13
+
14
+ let(:response_id) { 'some_response_id' }
15
+ let(:issuer_uri) { 'http://example.com' }
16
+ let(:saml_slo_url) { 'http://localhost:3000/saml/logout' }
17
+ let(:request_id) { 'some_request_id' }
18
+ let(:algorithm) { OpenSSL::Digest::SHA256 }
19
+
20
+ subject do
21
+ described_class.new(
22
+ response_id,
23
+ issuer_uri,
24
+ saml_slo_url,
25
+ request_id,
26
+ algorithm
27
+ )
28
+ end
29
+
30
+ it "is a valid LogoutResponse" do
31
+ Timecop.travel(Time.zone.local(2010, 6, 1, 13, 0, 0)) do
32
+ logout_response = OneLogin::RubySaml::Logoutresponse.new(
33
+ subject.encoded,
34
+ saml_settings('localhost:3000')
35
+ )
36
+ logout_response.soft = false
37
+ expect(logout_response.validate).to eq true
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+ module SamlIdp
3
+ describe MetadataBuilder do
4
+ it "has a valid fresh" do
5
+ subject.fresh.should_not be_empty
6
+ end
7
+
8
+ it "signs valid xml" do
9
+ Saml::XML::Document.parse(subject.signed).valid_signature?(Default::FINGERPRINT).should be_truthy
10
+ end
11
+
12
+ it "includes logout element" do
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
+ )
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+ module SamlIdp
3
+ describe NameIdFormatter do
4
+ subject { described_class.new list }
5
+
6
+ describe "with one item" do
7
+ let(:list) { { email_address: ->() { "foo@example.com" } } }
8
+
9
+ it "has a valid all" do
10
+ subject.all.should == ["urn:oasis:names:tc:SAML:2.0:nameid-format:emailAddress"]
11
+ end
12
+
13
+ end
14
+
15
+ describe "with hash describing versions" do
16
+ let(:list) {
17
+ {
18
+ "1.1" => { email_address: -> {} },
19
+ "2.0" => { undefined: -> {} },
20
+ }
21
+ }
22
+
23
+ it "has a valid all" do
24
+ subject.all.should == [
25
+ "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
26
+ "urn:oasis:names:tc:SAML:2.0:nameid-format:undefined",
27
+ ]
28
+ end
29
+ end
30
+
31
+ describe "with actual list" do
32
+ let(:list) { [:email_address, :undefined] }
33
+
34
+ it "has a valid all" do
35
+ subject.all.should == [
36
+ "urn:oasis:names:tc:SAML:2.0:nameid-format:emailAddress",
37
+ "urn:oasis:names:tc:SAML:2.0:nameid-format:undefined",
38
+ ]
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,106 @@
1
+ require 'spec_helper'
2
+ module SamlIdp
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>" }
5
+
6
+ describe "deflated request" do
7
+ let(:deflated_request) { Base64.encode64(Zlib::Deflate.deflate(raw_authn_request, 9)[2..-5]) }
8
+
9
+ subject { described_class.from_deflated_request deflated_request }
10
+
11
+ it "inflates" do
12
+ subject.request_id.should == "_af43d1a0-e111-0130-661a-3c0754403fdb"
13
+ end
14
+
15
+ it "handles invalid SAML" do
16
+ req = described_class.from_deflated_request "bang!"
17
+ req.valid?.should == false
18
+ end
19
+ end
20
+
21
+ describe "authn request" do
22
+ subject { described_class.new raw_authn_request }
23
+
24
+ it "has a valid request_id" do
25
+ subject.request_id.should == "_af43d1a0-e111-0130-661a-3c0754403fdb"
26
+ end
27
+
28
+ it "has a valid acs_url" do
29
+ subject.acs_url.should == "http://localhost:3000/saml/consume"
30
+ end
31
+
32
+ it "has a valid service_provider" do
33
+ subject.service_provider.should be_a ServiceProvider
34
+ end
35
+
36
+ it "has a valid service_provider" do
37
+ subject.service_provider.should be_truthy
38
+ end
39
+
40
+ it "has a valid issuer" do
41
+ subject.issuer.should == "localhost:3000"
42
+ end
43
+
44
+ it "has a valid valid_signature" do
45
+ subject.valid_signature?.should be_truthy
46
+ end
47
+
48
+ it "should return acs_url for response_url" do
49
+ subject.response_url.should == subject.acs_url
50
+ end
51
+
52
+ it "is a authn request" do
53
+ subject.authn_request?.should == true
54
+ end
55
+
56
+ it "fetches internal request" do
57
+ subject.request['ID'].should == subject.request_id
58
+ end
59
+
60
+ it "has a valid authn context" do
61
+ subject.requested_authn_context.should == "urn:oasis:names:tc:SAML:2.0:ac:classes:Password"
62
+ end
63
+
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
+ end
70
+ end
71
+
72
+ describe "logout request" do
73
+ let(:raw_logout_request) { "<LogoutRequest ID='_some_response_id' Version='2.0' IssueInstant='2010-06-01T13:00:00Z' Destination='http://localhost:3000/saml/logout' xmlns='urn:oasis:names:tc:SAML:2.0:protocol'><Issuer xmlns='urn:oasis:names:tc:SAML:2.0:assertion'>http://example.com</Issuer><NameID xmlns='urn:oasis:names:tc:SAML:2.0:assertion' Format='urn:oasis:names:tc:SAML:2.0:nameid-format:persistent'>some_name_id</NameID><SessionIndex>abc123index</SessionIndex></LogoutRequest>" }
74
+
75
+ subject { described_class.new raw_logout_request }
76
+
77
+ it "has a valid request_id" do
78
+ subject.request_id.should == '_some_response_id'
79
+ end
80
+
81
+ it "should be flagged as a logout_request" do
82
+ subject.logout_request?.should == true
83
+ end
84
+
85
+ it "should have a valid name_id" do
86
+ subject.name_id.should == 'some_name_id'
87
+ end
88
+
89
+ it "should have a session index" do
90
+ subject.session_index.should == 'abc123index'
91
+ end
92
+
93
+ it "should have a valid issuer" do
94
+ subject.issuer.should == 'http://example.com'
95
+ end
96
+
97
+ it "fetches internal request" do
98
+ subject.request['ID'].should == subject.request_id
99
+ end
100
+
101
+ it "should return logout_url for response_url" do
102
+ subject.response_url.should == subject.logout_url
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+ module SamlIdp
3
+ describe ResponseBuilder do
4
+ let(:response_id) { "abc" }
5
+ let(:issuer_uri) { "http://example.com" }
6
+ let(:saml_acs_url) { "http://sportngin.com" }
7
+ let(:saml_request_id) { "134" }
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
+ subject { described_class.new(
10
+ response_id,
11
+ issuer_uri,
12
+ saml_acs_url,
13
+ saml_request_id,
14
+ assertion_and_signature
15
+ ) }
16
+
17
+ before do
18
+ Timecop.freeze(Time.local(1990))
19
+ end
20
+
21
+ after do
22
+ Timecop.return
23
+ end
24
+
25
+
26
+ it "builds a legit raw XML file" do
27
+ 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>"
29
+ end
30
+ end
31
+
32
+ context "No request ID" do
33
+ let(:saml_request_id) { nil }
34
+
35
+ it "builds a legit raw XML file without a request ID" do
36
+ 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>"
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+ module SamlIdp
3
+ describe SamlResponse do
4
+ let(:reference_id) { "123" }
5
+ let(:response_id) { "abc" }
6
+ let(:issuer_uri) { "localhost" }
7
+ let(:name_id) { "name" }
8
+ let(:audience_uri) { "localhost/audience" }
9
+ let(:saml_request_id) { "abc123" }
10
+ let(:saml_acs_url) { "localhost/acs" }
11
+ let(:algorithm) { :sha1 }
12
+ let(:secret_key) { Default::SECRET_KEY }
13
+ let(:x509_certificate) { Default::X509_CERTIFICATE }
14
+ let(:xauthn) { Default::X509_CERTIFICATE }
15
+ let(:authn_context_classref) {
16
+ Saml::XML::Namespaces::AuthnContext::ClassRef::PASSWORD
17
+ }
18
+ let(:expiry) { 3 * 60 * 60 }
19
+ let (:encryption_opts) do
20
+ {
21
+ cert: Default::X509_CERTIFICATE,
22
+ block_encryption: 'aes256-cbc',
23
+ key_transport: 'rsa-oaep-mgf1p',
24
+ }
25
+ end
26
+ let(:subject_encrypted) { described_class.new(reference_id,
27
+ response_id,
28
+ issuer_uri,
29
+ name_id,
30
+ audience_uri,
31
+ saml_request_id,
32
+ saml_acs_url,
33
+ algorithm,
34
+ authn_context_classref,
35
+ expiry,
36
+ encryption_opts
37
+ )
38
+ }
39
+
40
+ subject { described_class.new(reference_id,
41
+ response_id,
42
+ issuer_uri,
43
+ name_id,
44
+ audience_uri,
45
+ saml_request_id,
46
+ saml_acs_url,
47
+ algorithm,
48
+ authn_context_classref,
49
+ expiry
50
+ )
51
+ }
52
+
53
+ it "has a valid build" do
54
+ subject.build.should be_present
55
+ end
56
+
57
+ it "builds encrypted" do
58
+ subject_encrypted.build.should_not match(audience_uri)
59
+ encoded_xml = subject_encrypted.build
60
+ resp_settings = saml_settings(saml_acs_url)
61
+ resp_settings.private_key = Default::SECRET_KEY
62
+ resp_settings.issuer = audience_uri
63
+ saml_resp = OneLogin::RubySaml::Response.new(encoded_xml, settings: resp_settings)
64
+ saml_resp.soft = false
65
+ saml_resp.is_valid?.should == true
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+ module SamlIdp
3
+ describe ServiceProvider do
4
+ subject { described_class.new attributes }
5
+ let(:attributes) { {} }
6
+
7
+ it { should respond_to :fingerprint }
8
+ it { should respond_to :metadata_url }
9
+ it { should_not be_valid }
10
+
11
+ describe "with attributes" do
12
+ let(:attributes) { { fingerprint: fingerprint, metadata_url: metadata_url } }
13
+ let(:fingerprint) { Default::FINGERPRINT }
14
+ let(:metadata_url) { "http://localhost:3000/metadata" }
15
+
16
+ it "has a valid fingerprint" do
17
+ subject.fingerprint.should == fingerprint
18
+ end
19
+
20
+ it "has a valid metadata_url" do
21
+ subject.metadata_url.should == metadata_url
22
+ end
23
+
24
+ it { should be_valid }
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,77 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+ class MockSignable
4
+ include SamlIdp::Signable
5
+
6
+ def raw
7
+ builder = Builder::XmlMarkup.new
8
+ builder.body do |body|
9
+ sign body
10
+ end
11
+ end
12
+
13
+ def reference_id
14
+ "abc"
15
+ end
16
+
17
+ def digest
18
+ algorithm.digest raw
19
+ end
20
+
21
+ def algorithm
22
+ OpenSSL::Digest::SHA1
23
+ end
24
+ end
25
+
26
+ module SamlIdp
27
+ describe MockSignable do
28
+ let(:signature_regex) { %r{<ds:Signature xmlns:ds=\"http:\/\/www.w3.org\/2000\/09\/xmldsig#\">} }
29
+ let(:info_regex) { %r{<ds:SignedInfo xmlns:ds=\"http:\/\/www.w3.org\/2000\/09\/xmldsig#\">} }
30
+ let(:canon) do
31
+ %r{<ds:CanonicalizationMethod Algorithm=\"http:\/\/www.w3.org\/2001\/10\/xml-exc-c14n#\"><\/ds:CanonicalizationMethod>}
32
+ end
33
+ let(:sig_method) do
34
+ %r{<ds:SignatureMethod Algorithm=\"http:\/\/www.w3.org\/2000\/09\/xmldsig#rsa-sha1\"><\/ds:SignatureMethod>}
35
+ end
36
+ let(:reference) { %r{<ds:Reference URI=\"#_abc\">} }
37
+ let(:transforms) { %r{<ds:Transforms>} }
38
+ let(:enveloped) { %r{<ds:Transform Algorithm=\"http:\/\/www.w3.org\/2000\/09\/xmldsig#enveloped-signature\"><\/ds:Transform>} }
39
+ let(:c14n) { %r{<ds:Transform Algorithm=\"http:\/\/www.w3.org\/2001\/10\/xml-exc-c14n#\"><\/ds:Transform>} }
40
+ let(:end_transforms) { %r{<\/ds:Transforms>} }
41
+ let(:digest_method) { %r{<ds:DigestMethod Algorithm=\"http:\/\/www.w3.org\/2000\/09\/xmldsig#sha1\"><\/ds:DigestMethod>} }
42
+ let(:digest_value) { %r{<ds:DigestValue>\S+<\/ds:DigestValue>} }
43
+ let(:end_reference) { %r{<\/ds:Reference>} }
44
+ let(:end_info) { %r{<\/ds:SignedInfo>} }
45
+ let(:sig_val) { %r{<ds:SignatureValue>\S+<\/ds:SignatureValue>} }
46
+ let(:key_info) { %r{<KeyInfo xmlns=\"http:\/\/www.w3.org\/2000\/09\/xmldsig#\">} }
47
+ let(:x509) { %r{<ds:X509Data><ds:X509Certificate>\S+<\/ds:X509Certificate><\/ds:X509Data>} }
48
+ let(:end_rest) { %r{<\/KeyInfo><\/ds:Signature>} }
49
+
50
+ let(:all_regex) do
51
+ Regexp.new [
52
+ signature_regex,
53
+ info_regex,
54
+ canon,
55
+ sig_method,
56
+ reference,
57
+ transforms,
58
+ enveloped,
59
+ c14n,
60
+ end_transforms,
61
+ digest_method,
62
+ digest_value,
63
+ end_reference,
64
+ end_info,
65
+ sig_val,
66
+ key_info,
67
+ x509,
68
+ end_rest,
69
+ ].map(&:to_s).join(".*")
70
+ end
71
+
72
+ it "has a valid signed" do
73
+ subject.signed.should match all_regex
74
+ end
75
+
76
+ end
77
+ end