r-saml 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (140) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.gitignore +14 -0
  4. data/.travis.yml +23 -0
  5. data/Gemfile +6 -0
  6. data/LICENSE +19 -0
  7. data/README.md +584 -0
  8. data/Rakefile +27 -0
  9. data/changelog.md +75 -0
  10. data/gemfiles/nokogiri-1.5.gemfile +5 -0
  11. data/lib/onelogin/ruby-saml.rb +17 -0
  12. data/lib/onelogin/ruby-saml/attribute_service.rb +57 -0
  13. data/lib/onelogin/ruby-saml/attributes.rb +128 -0
  14. data/lib/onelogin/ruby-saml/authrequest.rb +165 -0
  15. data/lib/onelogin/ruby-saml/http_error.rb +7 -0
  16. data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +161 -0
  17. data/lib/onelogin/ruby-saml/logging.rb +30 -0
  18. data/lib/onelogin/ruby-saml/logoutrequest.rb +131 -0
  19. data/lib/onelogin/ruby-saml/logoutresponse.rb +241 -0
  20. data/lib/onelogin/ruby-saml/metadata.rb +123 -0
  21. data/lib/onelogin/ruby-saml/response.rb +735 -0
  22. data/lib/onelogin/ruby-saml/saml_message.rb +158 -0
  23. data/lib/onelogin/ruby-saml/settings.rb +165 -0
  24. data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +258 -0
  25. data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +136 -0
  26. data/lib/onelogin/ruby-saml/utils.rb +172 -0
  27. data/lib/onelogin/ruby-saml/validation_error.rb +7 -0
  28. data/lib/onelogin/ruby-saml/version.rb +5 -0
  29. data/lib/ruby-saml.rb +1 -0
  30. data/lib/schemas/saml-schema-assertion-2.0.xsd +283 -0
  31. data/lib/schemas/saml-schema-authn-context-2.0.xsd +23 -0
  32. data/lib/schemas/saml-schema-authn-context-types-2.0.xsd +821 -0
  33. data/lib/schemas/saml-schema-metadata-2.0.xsd +337 -0
  34. data/lib/schemas/saml-schema-protocol-2.0.xsd +302 -0
  35. data/lib/schemas/sstc-metadata-attr.xsd +35 -0
  36. data/lib/schemas/sstc-saml-attribute-ext.xsd +25 -0
  37. data/lib/schemas/sstc-saml-metadata-algsupport-v1.0.xsd +41 -0
  38. data/lib/schemas/sstc-saml-metadata-ui-v1.0.xsd +89 -0
  39. data/lib/schemas/xenc-schema.xsd +136 -0
  40. data/lib/schemas/xml.xsd +287 -0
  41. data/lib/schemas/xmldsig-core-schema.xsd +309 -0
  42. data/lib/xml_security.rb +368 -0
  43. data/r-saml.gemspec +64 -0
  44. data/test/certificates/certificate1 +12 -0
  45. data/test/certificates/certificate_without_head_foot +1 -0
  46. data/test/certificates/formatted_certificate +14 -0
  47. data/test/certificates/formatted_private_key +12 -0
  48. data/test/certificates/formatted_rsa_private_key +12 -0
  49. data/test/certificates/invalid_certificate1 +1 -0
  50. data/test/certificates/invalid_certificate2 +1 -0
  51. data/test/certificates/invalid_certificate3 +12 -0
  52. data/test/certificates/invalid_private_key1 +1 -0
  53. data/test/certificates/invalid_private_key2 +1 -0
  54. data/test/certificates/invalid_private_key3 +10 -0
  55. data/test/certificates/invalid_rsa_private_key1 +1 -0
  56. data/test/certificates/invalid_rsa_private_key2 +1 -0
  57. data/test/certificates/invalid_rsa_private_key3 +10 -0
  58. data/test/certificates/ruby-saml.crt +14 -0
  59. data/test/certificates/ruby-saml.key +15 -0
  60. data/test/idp_metadata_parser_test.rb +95 -0
  61. data/test/logging_test.rb +62 -0
  62. data/test/logout_requests/invalid_slo_request.xml +6 -0
  63. data/test/logout_requests/slo_request.xml +4 -0
  64. data/test/logout_requests/slo_request.xml.base64 +1 -0
  65. data/test/logout_requests/slo_request_deflated.xml.base64 +1 -0
  66. data/test/logout_requests/slo_request_with_session_index.xml +5 -0
  67. data/test/logout_responses/logoutresponse_fixtures.rb +67 -0
  68. data/test/logoutrequest_test.rb +211 -0
  69. data/test/logoutresponse_test.rb +258 -0
  70. data/test/metadata_test.rb +203 -0
  71. data/test/request_test.rb +282 -0
  72. data/test/response_test.rb +1159 -0
  73. data/test/responses/adfs_response_sha1.xml +46 -0
  74. data/test/responses/adfs_response_sha256.xml +46 -0
  75. data/test/responses/adfs_response_sha384.xml +46 -0
  76. data/test/responses/adfs_response_sha512.xml +46 -0
  77. data/test/responses/adfs_response_xmlns.xml +45 -0
  78. data/test/responses/attackxee.xml +13 -0
  79. data/test/responses/idp_descriptor.xml +3 -0
  80. data/test/responses/invalids/invalid_audience.xml.base64 +1 -0
  81. data/test/responses/invalids/invalid_issuer_assertion.xml.base64 +1 -0
  82. data/test/responses/invalids/invalid_issuer_message.xml.base64 +1 -0
  83. data/test/responses/invalids/invalid_signature_position.xml.base64 +1 -0
  84. data/test/responses/invalids/invalid_subjectconfirmation_inresponse.xml.base64 +1 -0
  85. data/test/responses/invalids/invalid_subjectconfirmation_nb.xml.base64 +1 -0
  86. data/test/responses/invalids/invalid_subjectconfirmation_noa.xml.base64 +1 -0
  87. data/test/responses/invalids/invalid_subjectconfirmation_recipient.xml.base64 +1 -0
  88. data/test/responses/invalids/multiple_assertions.xml.base64 +2 -0
  89. data/test/responses/invalids/multiple_signed.xml.base64 +1 -0
  90. data/test/responses/invalids/no_id.xml.base64 +1 -0
  91. data/test/responses/invalids/no_saml2.xml.base64 +1 -0
  92. data/test/responses/invalids/no_signature.xml.base64 +1 -0
  93. data/test/responses/invalids/no_status.xml.base64 +1 -0
  94. data/test/responses/invalids/no_status_code.xml.base64 +1 -0
  95. data/test/responses/invalids/no_subjectconfirmation_data.xml.base64 +1 -0
  96. data/test/responses/invalids/no_subjectconfirmation_method.xml.base64 +1 -0
  97. data/test/responses/invalids/response_encrypted_attrs.xml.base64 +1 -0
  98. data/test/responses/invalids/response_invalid_signed_element.xml.base64 +1 -0
  99. data/test/responses/invalids/status_code_responder.xml.base64 +1 -0
  100. data/test/responses/invalids/status_code_responer_and_msg.xml.base64 +1 -0
  101. data/test/responses/no_signature_ns.xml +48 -0
  102. data/test/responses/open_saml_response.xml +56 -0
  103. data/test/responses/response_assertion_wrapped.xml.base64 +93 -0
  104. data/test/responses/response_encrypted_nameid.xml.base64 +1 -0
  105. data/test/responses/response_eval.xml +7 -0
  106. data/test/responses/response_no_cert_and_encrypted_attrs.xml +29 -0
  107. data/test/responses/response_unsigned_xml_base64 +1 -0
  108. data/test/responses/response_with_ampersands.xml +139 -0
  109. data/test/responses/response_with_ampersands.xml.base64 +93 -0
  110. data/test/responses/response_with_multiple_attribute_values.xml +67 -0
  111. data/test/responses/response_with_saml2_namespace.xml.base64 +102 -0
  112. data/test/responses/response_with_signed_assertion.xml.base64 +66 -0
  113. data/test/responses/response_with_signed_assertion_2.xml.base64 +1 -0
  114. data/test/responses/response_with_undefined_recipient.xml.base64 +1 -0
  115. data/test/responses/response_without_attributes.xml.base64 +79 -0
  116. data/test/responses/response_without_reference_uri.xml.base64 +1 -0
  117. data/test/responses/response_wrapped.xml.base64 +150 -0
  118. data/test/responses/signed_message_encrypted_signed_assertion.xml.base64 +1 -0
  119. data/test/responses/signed_message_encrypted_unsigned_assertion.xml.base64 +1 -0
  120. data/test/responses/signed_nameid_in_atts.xml +47 -0
  121. data/test/responses/signed_unqual_nameid_in_atts.xml +47 -0
  122. data/test/responses/simple_saml_php.xml +71 -0
  123. data/test/responses/starfield_response.xml.base64 +1 -0
  124. data/test/responses/test_sign.xml +43 -0
  125. data/test/responses/unsigned_message_aes128_encrypted_signed_assertion.xml.base64 +1 -0
  126. data/test/responses/unsigned_message_aes192_encrypted_signed_assertion.xml.base64 +1 -0
  127. data/test/responses/unsigned_message_aes256_encrypted_signed_assertion.xml.base64 +1 -0
  128. data/test/responses/unsigned_message_des192_encrypted_signed_assertion.xml.base64 +1 -0
  129. data/test/responses/unsigned_message_encrypted_assertion_without_saml_namespace.xml.base64 +1 -0
  130. data/test/responses/unsigned_message_encrypted_signed_assertion.xml.base64 +1 -0
  131. data/test/responses/unsigned_message_encrypted_unsigned_assertion.xml.base64 +1 -0
  132. data/test/responses/valid_response.xml.base64 +1 -0
  133. data/test/saml_message_test.rb +56 -0
  134. data/test/settings_test.rb +218 -0
  135. data/test/slo_logoutrequest_test.rb +275 -0
  136. data/test/slo_logoutresponse_test.rb +185 -0
  137. data/test/test_helper.rb +257 -0
  138. data/test/utils_test.rb +145 -0
  139. data/test/xml_security_test.rb +328 -0
  140. metadata +421 -0
@@ -0,0 +1,203 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
2
+
3
+ require 'onelogin/ruby-saml/metadata'
4
+
5
+ class MetadataTest < Minitest::Test
6
+
7
+ describe 'Metadata' do
8
+ let(:settings) { OneLogin::RubySaml::Settings.new }
9
+ let(:xml_text) { OneLogin::RubySaml::Metadata.new.generate(settings, false) }
10
+ let(:xml_doc) { REXML::Document.new(xml_text) }
11
+ let(:spsso_descriptor) { REXML::XPath.first(xml_doc, "//md:SPSSODescriptor") }
12
+ let(:acs) { REXML::XPath.first(xml_doc, "//md:AssertionConsumerService") }
13
+
14
+ before do
15
+ settings.issuer = "https://example.com"
16
+ settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
17
+ settings.assertion_consumer_service_url = "https://foo.example/saml/consume"
18
+ end
19
+
20
+ it "generates Pretty Print Service Provider Metadata" do
21
+ xml_text = OneLogin::RubySaml::Metadata.new.generate(settings, true)
22
+ # assert correct xml declaration
23
+ start = "<?xml version='1.0' encoding='UTF-8'?>\n<md:EntityDescriptor"
24
+ assert_equal xml_text[0..start.length-1],start
25
+
26
+ assert_equal "https://example.com", REXML::XPath.first(xml_doc, "//md:EntityDescriptor").attribute("entityID").value
27
+
28
+ assert_equal "urn:oasis:names:tc:SAML:2.0:protocol", spsso_descriptor.attribute("protocolSupportEnumeration").value
29
+ assert_equal "false", spsso_descriptor.attribute("AuthnRequestsSigned").value
30
+ assert_equal "false", spsso_descriptor.attribute("WantAssertionsSigned").value
31
+
32
+ assert_equal "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", REXML::XPath.first(xml_doc, "//md:NameIDFormat").text.strip
33
+
34
+ assert_equal "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", acs.attribute("Binding").value
35
+ assert_equal "https://foo.example/saml/consume", acs.attribute("Location").value
36
+
37
+ assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd")
38
+ end
39
+
40
+ it "generates Service Provider Metadata" do
41
+ settings.single_logout_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
42
+ settings.single_logout_service_url = "https://foo.example/saml/sls"
43
+ xml_metadata = OneLogin::RubySaml::Metadata.new.generate(settings, false)
44
+
45
+ start = "<?xml version='1.0' encoding='UTF-8'?><md:EntityDescriptor"
46
+ assert_equal xml_metadata[0..start.length-1],start
47
+
48
+ doc_metadata = REXML::Document.new(xml_metadata)
49
+ sls = REXML::XPath.first(doc_metadata, "//md:SingleLogoutService")
50
+
51
+ assert_equal "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", sls.attribute("Binding").value
52
+ assert_equal "https://foo.example/saml/sls", sls.attribute("Location").value
53
+ assert_equal "https://foo.example/saml/sls", sls.attribute("ResponseLocation").value
54
+ assert_nil sls.attribute("isDefault")
55
+ assert_nil sls.attribute("index")
56
+
57
+ assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd")
58
+ end
59
+
60
+ it "generates Service Provider Metadata with single logout service" do
61
+ start = "<?xml version='1.0' encoding='UTF-8'?><md:EntityDescriptor"
62
+ assert_equal xml_text[0..start.length-1], start
63
+
64
+ assert_equal "https://example.com", REXML::XPath.first(xml_doc, "//md:EntityDescriptor").attribute("entityID").value
65
+
66
+ assert_equal "urn:oasis:names:tc:SAML:2.0:protocol", spsso_descriptor.attribute("protocolSupportEnumeration").value
67
+ assert_equal "false", spsso_descriptor.attribute("AuthnRequestsSigned").value
68
+ assert_equal "false", spsso_descriptor.attribute("WantAssertionsSigned").value
69
+
70
+ assert_equal "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", REXML::XPath.first(xml_doc, "//md:NameIDFormat").text.strip
71
+
72
+ assert_equal "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", acs.attribute("Binding").value
73
+ assert_equal "https://foo.example/saml/consume", acs.attribute("Location").value
74
+
75
+ assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd")
76
+ end
77
+
78
+ describe "when auth requests are signed" do
79
+ let(:key_descriptors) do
80
+ REXML::XPath.match(
81
+ xml_doc,
82
+ "//md:KeyDescriptor",
83
+ "md" => "urn:oasis:names:tc:SAML:2.0:metadata"
84
+ )
85
+ end
86
+ let(:cert_nodes) do
87
+ REXML::XPath.match(
88
+ xml_doc,
89
+ "//md:KeyDescriptor/ds:KeyInfo/ds:X509Data/ds:X509Certificate",
90
+ "md" => "urn:oasis:names:tc:SAML:2.0:metadata",
91
+ "ds" => "http://www.w3.org/2000/09/xmldsig#"
92
+ )
93
+ end
94
+ let(:cert) { OpenSSL::X509::Certificate.new(Base64.decode64(cert_nodes[0].text)) }
95
+
96
+ before do
97
+ settings.certificate = ruby_saml_cert_text
98
+ end
99
+
100
+ it "generates Service Provider Metadata with AuthnRequestsSigned" do
101
+ settings.security[:authn_requests_signed] = true
102
+ assert_equal "true", spsso_descriptor.attribute("AuthnRequestsSigned").value
103
+ assert_equal ruby_saml_cert.to_der, cert.to_der
104
+
105
+ assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd")
106
+ end
107
+
108
+ it "generates Service Provider Metadata with X509Certificate for sign and encrypt" do
109
+ assert_equal 2, key_descriptors.length
110
+ assert_equal "signing", key_descriptors[0].attribute("use").value
111
+ assert_equal "encryption", key_descriptors[1].attribute("use").value
112
+
113
+ assert_equal 2, cert_nodes.length
114
+ assert_equal ruby_saml_cert.to_der, cert.to_der
115
+ assert_equal cert_nodes[0].text, cert_nodes[1].text
116
+
117
+ assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd")
118
+ end
119
+ end
120
+
121
+ describe "when attribute service is configured" do
122
+ let(:attr_svc) { REXML::XPath.first(xml_doc, "//md:AttributeConsumingService") }
123
+ let(:req_attr) { REXML::XPath.first(xml_doc, "//md:RequestedAttribute") }
124
+
125
+ before do
126
+ settings.attribute_consuming_service.configure do
127
+ service_name "Test Service"
128
+ add_attribute(:name => "Name", :name_format => "Name Format", :friendly_name => "Friendly Name", :attribute_value => "Attribute Value")
129
+ end
130
+ end
131
+
132
+ it "generates attribute service" do
133
+ assert_equal "true", attr_svc.attribute("isDefault").value
134
+ assert_equal "1", attr_svc.attribute("index").value
135
+ assert_equal REXML::XPath.first(xml_doc, "//md:ServiceName").text.strip, "Test Service"
136
+
137
+ assert_equal "Name", req_attr.attribute("Name").value
138
+ assert_equal "Name Format", req_attr.attribute("NameFormat").value
139
+ assert_equal "Friendly Name", req_attr.attribute("FriendlyName").value
140
+ assert_equal "Attribute Value", REXML::XPath.first(xml_doc, "//saml:AttributeValue").text.strip
141
+
142
+ assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd")
143
+ end
144
+
145
+ describe "#service_name" do
146
+ before do
147
+ settings.attribute_consuming_service.service_name("Test2 Service")
148
+ end
149
+
150
+ it "change service name" do
151
+ assert_equal REXML::XPath.first(xml_doc, "//md:ServiceName").text.strip, "Test2 Service"
152
+ end
153
+ end
154
+
155
+ describe "#service_index" do
156
+ before do
157
+ settings.attribute_consuming_service.service_index(2)
158
+ end
159
+
160
+ it "change service index" do
161
+ assert_equal "2", attr_svc.attribute("index").value
162
+ end
163
+ end
164
+ end
165
+
166
+ describe "when the settings indicate to sign (embedded) metadata" do
167
+ before do
168
+ settings.security[:metadata_signed] = true
169
+ settings.certificate = ruby_saml_cert_text
170
+ settings.private_key = ruby_saml_key_text
171
+ end
172
+
173
+ it "creates a signed metadata" do
174
+ assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>]m, xml_text
175
+ assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], xml_text
176
+ assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], xml_text
177
+ signed_metadata = XMLSecurity::SignedDocument.new(xml_text)
178
+ assert signed_metadata.validate_document(ruby_saml_cert_fingerprint, false)
179
+
180
+ assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd")
181
+ end
182
+
183
+ describe "when digest and signature methods are specified" do
184
+ before do
185
+ settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA256
186
+ settings.security[:digest_method] = XMLSecurity::Document::SHA512
187
+ end
188
+
189
+ it "creates a signed metadata with specified digest and signature methods" do
190
+ assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>]m, xml_text
191
+ assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'/>], xml_text
192
+ assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2001/04/xmldsig-more#sha512'/>], xml_text
193
+
194
+ signed_metadata_2 = XMLSecurity::SignedDocument.new(xml_text)
195
+
196
+ assert signed_metadata_2.validate_document(ruby_saml_cert_fingerprint, false)
197
+
198
+ assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd")
199
+ end
200
+ end
201
+ end
202
+ end
203
+ end
@@ -0,0 +1,282 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
2
+
3
+ require 'onelogin/ruby-saml/authrequest'
4
+
5
+ class RequestTest < Minitest::Test
6
+
7
+ describe "Authrequest" do
8
+ let(:settings) { OneLogin::RubySaml::Settings.new }
9
+
10
+ before do
11
+ settings.idp_sso_target_url = "http://example.com"
12
+ end
13
+
14
+ it "create the deflated SAMLRequest URL parameter" do
15
+ auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
16
+ assert_match /^http:\/\/example\.com\?SAMLRequest=/, auth_url
17
+ payload = CGI.unescape(auth_url.split("=").last)
18
+ decoded = Base64.decode64(payload)
19
+
20
+ zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
21
+ inflated = zstream.inflate(decoded)
22
+ zstream.finish
23
+ zstream.close
24
+
25
+ assert_match /^<samlp:AuthnRequest/, inflated
26
+ end
27
+
28
+ it "create the deflated SAMLRequest URL parameter including the Destination" do
29
+ auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
30
+ payload = CGI.unescape(auth_url.split("=").last)
31
+ decoded = Base64.decode64(payload)
32
+
33
+ zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
34
+ inflated = zstream.inflate(decoded)
35
+ zstream.finish
36
+ zstream.close
37
+
38
+ assert_match /<samlp:AuthnRequest[^<]* Destination='http:\/\/example.com'/, inflated
39
+ end
40
+
41
+ it "create the SAMLRequest URL parameter without deflating" do
42
+ settings.compress_request = false
43
+ auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
44
+ assert_match /^http:\/\/example\.com\?SAMLRequest=/, auth_url
45
+ payload = CGI.unescape(auth_url.split("=").last)
46
+ decoded = Base64.decode64(payload)
47
+
48
+ assert_match /^<samlp:AuthnRequest/, decoded
49
+ end
50
+
51
+ it "create the SAMLRequest URL parameter with IsPassive" do
52
+ settings.passive = true
53
+ auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
54
+ assert_match /^http:\/\/example\.com\?SAMLRequest=/, auth_url
55
+ payload = CGI.unescape(auth_url.split("=").last)
56
+ decoded = Base64.decode64(payload)
57
+
58
+ zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
59
+ inflated = zstream.inflate(decoded)
60
+ zstream.finish
61
+ zstream.close
62
+
63
+ assert_match /<samlp:AuthnRequest[^<]* IsPassive='true'/, inflated
64
+ end
65
+
66
+ it "create the SAMLRequest URL parameter with ProtocolBinding" do
67
+ settings.protocol_binding = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
68
+ auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
69
+ assert_match /^http:\/\/example\.com\?SAMLRequest=/, auth_url
70
+ payload = CGI.unescape(auth_url.split("=").last)
71
+ decoded = Base64.decode64(payload)
72
+
73
+ zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
74
+ inflated = zstream.inflate(decoded)
75
+ zstream.finish
76
+ zstream.close
77
+
78
+ assert_match /<samlp:AuthnRequest[^<]* ProtocolBinding='urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'/, inflated
79
+ end
80
+
81
+ it "create the SAMLRequest URL parameter with AttributeConsumingServiceIndex" do
82
+ settings.attributes_index = 30
83
+ auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
84
+ assert_match /^http:\/\/example\.com\?SAMLRequest=/, auth_url
85
+ payload = CGI.unescape(auth_url.split("=").last)
86
+ decoded = Base64.decode64(payload)
87
+
88
+ zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
89
+ inflated = zstream.inflate(decoded)
90
+ zstream.finish
91
+ zstream.close
92
+ assert_match /<samlp:AuthnRequest[^<]* AttributeConsumingServiceIndex='30'/, inflated
93
+ end
94
+
95
+ it "create the SAMLRequest URL parameter with ForceAuthn" do
96
+ settings.force_authn = true
97
+ auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
98
+ assert_match /^http:\/\/example\.com\?SAMLRequest=/, auth_url
99
+ payload = CGI.unescape(auth_url.split("=").last)
100
+ decoded = Base64.decode64(payload)
101
+
102
+ zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
103
+ inflated = zstream.inflate(decoded)
104
+ zstream.finish
105
+ zstream.close
106
+ assert_match /<samlp:AuthnRequest[^<]* ForceAuthn='true'/, inflated
107
+ end
108
+
109
+ it "create the SAMLRequest URL parameter with NameID Format" do
110
+ settings.name_identifier_format = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
111
+ auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
112
+ assert_match /^http:\/\/example\.com\?SAMLRequest=/, auth_url
113
+ payload = CGI.unescape(auth_url.split("=").last)
114
+ decoded = Base64.decode64(payload)
115
+ zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
116
+ inflated = zstream.inflate(decoded)
117
+ zstream.finish
118
+ zstream.close
119
+
120
+ assert_match /<samlp:NameIDPolicy[^<]* AllowCreate='true'/, inflated
121
+ assert_match /<samlp:NameIDPolicy[^<]* Format='urn:oasis:names:tc:SAML:2.0:nameid-format:transient'/, inflated
122
+ end
123
+
124
+ it "accept extra parameters" do
125
+ auth_url = OneLogin::RubySaml::Authrequest.new.create(settings, { :hello => "there" })
126
+ assert_match /&hello=there$/, auth_url
127
+
128
+ auth_url = OneLogin::RubySaml::Authrequest.new.create(settings, { :hello => nil })
129
+ assert_match /&hello=$/, auth_url
130
+ end
131
+
132
+ describe "when the target url doesn't contain a query string" do
133
+ it "create the SAMLRequest parameter correctly" do
134
+
135
+ auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
136
+ assert_match /^http:\/\/example.com\?SAMLRequest/, auth_url
137
+ end
138
+ end
139
+
140
+ describe "when the target url contains a query string" do
141
+ it "create the SAMLRequest parameter correctly" do
142
+ settings.idp_sso_target_url = "http://example.com?field=value"
143
+
144
+ auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
145
+ assert_match /^http:\/\/example.com\?field=value&SAMLRequest/, auth_url
146
+ end
147
+ end
148
+
149
+ it "create the saml:AuthnContextClassRef element correctly" do
150
+ settings.authn_context = 'secure/name/password/uri'
151
+ auth_doc = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
152
+ assert_match /<saml:AuthnContextClassRef>secure\/name\/password\/uri<\/saml:AuthnContextClassRef>/, auth_doc.to_s
153
+ end
154
+
155
+ it "create the saml:AuthnContextClassRef with comparison exact" do
156
+ settings.authn_context = 'secure/name/password/uri'
157
+ auth_doc = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
158
+ assert_match /<samlp:RequestedAuthnContext[\S ]+Comparison='exact'/, auth_doc.to_s
159
+ assert_match /<saml:AuthnContextClassRef>secure\/name\/password\/uri<\/saml:AuthnContextClassRef>/, auth_doc.to_s
160
+ end
161
+
162
+ it "create the saml:AuthnContextClassRef with comparison minimun" do
163
+ settings.authn_context = 'secure/name/password/uri'
164
+ settings.authn_context_comparison = 'minimun'
165
+ auth_doc = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
166
+ assert_match /<samlp:RequestedAuthnContext[\S ]+Comparison='minimun'/, auth_doc.to_s
167
+ assert_match /<saml:AuthnContextClassRef>secure\/name\/password\/uri<\/saml:AuthnContextClassRef>/, auth_doc.to_s
168
+ end
169
+
170
+ it "create the saml:AuthnContextDeclRef element correctly" do
171
+ settings.authn_context_decl_ref = 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'
172
+ auth_doc = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
173
+ assert_match /<saml:AuthnContextDeclRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport<\/saml:AuthnContextDeclRef>/, auth_doc.to_s
174
+ end
175
+
176
+ describe "#create_params when the settings indicate to sign (embebed) the request" do
177
+ before do
178
+ settings.compress_request = false
179
+ settings.idp_sso_target_url = "http://example.com?field=value"
180
+ settings.security[:authn_requests_signed] = true
181
+ settings.security[:embed_sign] = true
182
+ settings.certificate = ruby_saml_cert_text
183
+ settings.private_key = ruby_saml_key_text
184
+ end
185
+
186
+ it "create a signed request" do
187
+ params = OneLogin::RubySaml::Authrequest.new.create_params(settings)
188
+ request_xml = Base64.decode64(params["SAMLRequest"])
189
+ assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], request_xml
190
+ assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], request_xml
191
+ end
192
+
193
+ it "create a signed request with 256 digest and signature methods" do
194
+ settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA256
195
+ settings.security[:digest_method] = XMLSecurity::Document::SHA512
196
+
197
+ params = OneLogin::RubySaml::Authrequest.new.create_params(settings)
198
+
199
+ request_xml = Base64.decode64(params["SAMLRequest"])
200
+ assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], request_xml
201
+ assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'/>], request_xml
202
+ assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2001/04/xmldsig-more#sha512'/>], request_xml
203
+ end
204
+ end
205
+
206
+ describe "#create_params when the settings indicate to sign the request" do
207
+ let(:cert) { OpenSSL::X509::Certificate.new(ruby_saml_cert_text) }
208
+
209
+ before do
210
+ settings.compress_request = false
211
+ settings.idp_sso_target_url = "http://example.com?field=value"
212
+ settings.assertion_consumer_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST-SimpleSign"
213
+ settings.security[:authn_requests_signed] = true
214
+ settings.security[:embed_sign] = false
215
+ settings.certificate = ruby_saml_cert_text
216
+ settings.private_key = ruby_saml_key_text
217
+ end
218
+
219
+ it "create a signature parameter with RSA_SHA1 and validate it" do
220
+ settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
221
+
222
+ params = OneLogin::RubySaml::Authrequest.new.create_params(settings, :RelayState => 'http://example.com')
223
+ assert params['SAMLRequest']
224
+ assert params[:RelayState]
225
+ assert params['Signature']
226
+ assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA1
227
+
228
+ query_string = "SAMLRequest=#{CGI.escape(params['SAMLRequest'])}"
229
+ query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
230
+ query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
231
+
232
+ signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
233
+ assert_equal signature_algorithm, OpenSSL::Digest::SHA1
234
+
235
+ assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
236
+ end
237
+
238
+ it "create a signature parameter with RSA_SHA256 and validate it" do
239
+ settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA256
240
+
241
+ params = OneLogin::RubySaml::Authrequest.new.create_params(settings, :RelayState => 'http://example.com')
242
+ assert params['Signature']
243
+ assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA256
244
+
245
+ query_string = "SAMLRequest=#{CGI.escape(params['SAMLRequest'])}"
246
+ query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
247
+ query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
248
+
249
+ signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
250
+ assert_equal signature_algorithm, OpenSSL::Digest::SHA256
251
+ assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
252
+ end
253
+ end
254
+
255
+ it "create the saml:AuthnContextClassRef element correctly" do
256
+ settings.authn_context = 'secure/name/password/uri'
257
+ auth_doc = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
258
+ assert auth_doc.to_s =~ /<saml:AuthnContextClassRef>secure\/name\/password\/uri<\/saml:AuthnContextClassRef>/
259
+ end
260
+
261
+ it "create the saml:AuthnContextClassRef with comparison exact" do
262
+ settings.authn_context = 'secure/name/password/uri'
263
+ auth_doc = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
264
+ assert auth_doc.to_s =~ /<samlp:RequestedAuthnContext[\S ]+Comparison='exact'/
265
+ assert auth_doc.to_s =~ /<saml:AuthnContextClassRef>secure\/name\/password\/uri<\/saml:AuthnContextClassRef>/
266
+ end
267
+
268
+ it "create the saml:AuthnContextClassRef with comparison minimun" do
269
+ settings.authn_context = 'secure/name/password/uri'
270
+ settings.authn_context_comparison = 'minimun'
271
+ auth_doc = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
272
+ assert auth_doc.to_s =~ /<samlp:RequestedAuthnContext[\S ]+Comparison='minimun'/
273
+ assert auth_doc.to_s =~ /<saml:AuthnContextClassRef>secure\/name\/password\/uri<\/saml:AuthnContextClassRef>/
274
+ end
275
+
276
+ it "create the saml:AuthnContextDeclRef element correctly" do
277
+ settings.authn_context_decl_ref = 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'
278
+ auth_doc = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
279
+ assert auth_doc.to_s =~ /<saml:AuthnContextDeclRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport<\/saml:AuthnContextDeclRef>/
280
+ end
281
+ end
282
+ end