kl-ruby-saml 0.0.1

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 (137) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.gitignore +14 -0
  4. data/.travis.yml +17 -0
  5. data/Gemfile +9 -0
  6. data/LICENSE +19 -0
  7. data/README.md +575 -0
  8. data/Rakefile +41 -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 +156 -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 +722 -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 +358 -0
  43. data/ruby-saml.gemspec +57 -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 +1094 -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_wrapped.xml.base64 +150 -0
  117. data/test/responses/signed_message_encrypted_signed_assertion.xml.base64 +1 -0
  118. data/test/responses/signed_message_encrypted_unsigned_assertion.xml.base64 +1 -0
  119. data/test/responses/simple_saml_php.xml +71 -0
  120. data/test/responses/starfield_response.xml.base64 +1 -0
  121. data/test/responses/test_sign.xml +43 -0
  122. data/test/responses/unsigned_message_aes128_encrypted_signed_assertion.xml.base64 +1 -0
  123. data/test/responses/unsigned_message_aes192_encrypted_signed_assertion.xml.base64 +1 -0
  124. data/test/responses/unsigned_message_aes256_encrypted_signed_assertion.xml.base64 +1 -0
  125. data/test/responses/unsigned_message_des192_encrypted_signed_assertion.xml.base64 +1 -0
  126. data/test/responses/unsigned_message_encrypted_assertion_without_saml_namespace.xml.base64 +1 -0
  127. data/test/responses/unsigned_message_encrypted_signed_assertion.xml.base64 +1 -0
  128. data/test/responses/unsigned_message_encrypted_unsigned_assertion.xml.base64 +1 -0
  129. data/test/responses/valid_response.xml.base64 +1 -0
  130. data/test/saml_message_test.rb +56 -0
  131. data/test/settings_test.rb +218 -0
  132. data/test/slo_logoutrequest_test.rb +275 -0
  133. data/test/slo_logoutresponse_test.rb +185 -0
  134. data/test/test_helper.rb +252 -0
  135. data/test/utils_test.rb +145 -0
  136. data/test/xml_security_test.rb +329 -0
  137. metadata +415 -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