require File.expand_path(File.join(File.dirname(__FILE__), "test_helper")) require 'onelogin/ruby-saml/metadata' class MetadataTest < Minitest::Test describe 'Metadata' do let(:settings) { OneLogin::RubySaml::Settings.new } let(:xml_text) { OneLogin::RubySaml::Metadata.new.generate(settings, false) } let(:xml_doc) { REXML::Document.new(xml_text) } let(:spsso_descriptor) { REXML::XPath.first(xml_doc, "//md:SPSSODescriptor") } let(:acs) { REXML::XPath.first(xml_doc, "//md:AssertionConsumerService") } before do settings.sp_entity_id = "https://example.com" settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" settings.assertion_consumer_service_url = "https://foo.example/saml/consume" end it "generates Pretty Print Service Provider Metadata" do xml_text = OneLogin::RubySaml::Metadata.new.generate(settings, true) # assert correct xml declaration start = "\n "urn:oasis:names:tc:SAML:2.0:metadata" ) end let(:cert_nodes) do REXML::XPath.match( xml_doc, "//md:KeyDescriptor/ds:KeyInfo/ds:X509Data/ds:X509Certificate", "md" => "urn:oasis:names:tc:SAML:2.0:metadata", "ds" => "http://www.w3.org/2000/09/xmldsig#" ) end let(:cert) { OpenSSL::X509::Certificate.new(Base64.decode64(cert_nodes[0].text)) } before do settings.certificate = ruby_saml_cert_text end it "generates Service Provider Metadata with X509Certificate for sign" do assert_equal 1, key_descriptors.length assert_equal "signing", key_descriptors[0].attribute("use").value assert_equal 1, cert_nodes.length assert_equal ruby_saml_cert.to_der, cert.to_der assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd") end describe "and signed authentication requests" do before do settings.security[:authn_requests_signed] = true end it "generates Service Provider Metadata with AuthnRequestsSigned" do assert_equal "true", spsso_descriptor.attribute("AuthnRequestsSigned").value assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd") end end describe "and encrypted assertions" do before do settings.security[:want_assertions_encrypted] = true end it "generates Service Provider Metadata with X509Certificate for encrypt" do assert_equal 2, key_descriptors.length assert_equal "encryption", key_descriptors[1].attribute("use").value assert_equal 2, cert_nodes.length assert_equal cert_nodes[0].text, cert_nodes[1].text assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd") end end end describe "with a future SP certificate" do let(:key_descriptors) do REXML::XPath.match( xml_doc, "//md:KeyDescriptor", "md" => "urn:oasis:names:tc:SAML:2.0:metadata" ) end let(:cert_nodes) do REXML::XPath.match( xml_doc, "//md:KeyDescriptor/ds:KeyInfo/ds:X509Data/ds:X509Certificate", "md" => "urn:oasis:names:tc:SAML:2.0:metadata", "ds" => "http://www.w3.org/2000/09/xmldsig#" ) end before do settings.certificate = ruby_saml_cert_text settings.certificate_new = ruby_saml_cert_text2 end it "generates Service Provider Metadata with 2 X509Certificate for sign" do assert_equal 2, key_descriptors.length assert_equal "signing", key_descriptors[0].attribute("use").value assert_equal "signing", key_descriptors[1].attribute("use").value cert = OpenSSL::X509::Certificate.new(Base64.decode64(cert_nodes[0].text)) cert_new = OpenSSL::X509::Certificate.new(Base64.decode64(cert_nodes[1].text)) assert_equal 2, cert_nodes.length assert_equal ruby_saml_cert.to_der, cert.to_der assert_equal ruby_saml_cert2.to_der, cert_new.to_der assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd") end describe "and signed authentication requests" do before do settings.security[:authn_requests_signed] = true end it "generates Service Provider Metadata with AuthnRequestsSigned" do assert_equal "true", spsso_descriptor.attribute("AuthnRequestsSigned").value assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd") end end describe "and encrypted assertions" do before do settings.security[:want_assertions_encrypted] = true end it "generates Service Provider Metadata with X509Certificate for encrypt" do assert_equal 4, key_descriptors.length assert_equal "signing", key_descriptors[0].attribute("use").value assert_equal "encryption", key_descriptors[1].attribute("use").value assert_equal "signing", key_descriptors[2].attribute("use").value assert_equal "encryption", key_descriptors[3].attribute("use").value assert_equal 4, cert_nodes.length assert_equal cert_nodes[0].text, cert_nodes[1].text assert_equal cert_nodes[2].text, cert_nodes[3].text assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd") end end end describe "when attribute service is configured with multiple attribute values" do let(:attr_svc) { REXML::XPath.first(xml_doc, "//md:AttributeConsumingService") } let(:req_attr) { REXML::XPath.first(xml_doc, "//md:RequestedAttribute") } before do settings.attribute_consuming_service.configure do service_name "Test Service" add_attribute(:name => 'Name', :name_format => 'Name Format', :friendly_name => 'Friendly Name', :attribute_value => ['Attribute Value One', false]) end end it "generates attribute service" do assert_equal "true", attr_svc.attribute("isDefault").value assert_equal "1", attr_svc.attribute("index").value assert_equal REXML::XPath.first(xml_doc, "//md:ServiceName").text.strip, "Test Service" assert_equal "Name", req_attr.attribute("Name").value assert_equal "Name Format", req_attr.attribute("NameFormat").value assert_equal "Friendly Name", req_attr.attribute("FriendlyName").value attribute_values = REXML::XPath.match(xml_doc, "//saml:AttributeValue").map(&:text) assert_equal "Attribute Value One", attribute_values[0] assert_equal 'false', attribute_values[1] assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd") end end describe "when attribute service is configured" do let(:attr_svc) { REXML::XPath.first(xml_doc, '//md:AttributeConsumingService') } let(:req_attr) { REXML::XPath.first(xml_doc, '//md:RequestedAttribute') } before do settings.attribute_consuming_service.configure do service_name "Test Service" add_attribute(:name => 'active', :name_format => 'format', :friendly_name => 'Active', :attribute_value => true) end end it "generates attribute service" do assert_equal "true", attr_svc.attribute("isDefault").value assert_equal "1", attr_svc.attribute("index").value assert_equal REXML::XPath.first(xml_doc, "//md:ServiceName").text.strip, "Test Service" assert_equal 'active', req_attr.attribute('Name').value assert_equal 'format', req_attr.attribute('NameFormat').value assert_equal 'Active', req_attr.attribute('FriendlyName').value assert_equal 'true', REXML::XPath.first(xml_doc, '//saml:AttributeValue').text.strip assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd") end describe "#service_name" do before do settings.attribute_consuming_service.service_name("Test2 Service") end it "change service name" do assert_equal REXML::XPath.first(xml_doc, "//md:ServiceName").text.strip, "Test2 Service" end end describe "#service_index" do before do settings.attribute_consuming_service.service_index(2) end it "change service index" do assert_equal "2", attr_svc.attribute("index").value end end end describe "when the settings indicate to sign (embedded) metadata" do before do settings.security[:metadata_signed] = true settings.certificate = ruby_saml_cert_text settings.private_key = ruby_saml_key_text end it "creates a signed metadata" do assert_match %r[([a-zA-Z0-9/+=]+)]m, xml_text assert_match %r[], xml_text assert_match %r[], xml_text signed_metadata = XMLSecurity::SignedDocument.new(xml_text) assert signed_metadata.validate_document(ruby_saml_cert_fingerprint, false) assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd") end describe "when digest and signature methods are specified" do before do settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA256 settings.security[:digest_method] = XMLSecurity::Document::SHA512 end it "creates a signed metadata with specified digest and signature methods" do assert_match %r[([a-zA-Z0-9/+=]+)]m, xml_text assert_match %r[], xml_text assert_match %r[], xml_text signed_metadata = XMLSecurity::SignedDocument.new(xml_text) assert signed_metadata.validate_document(ruby_saml_cert_fingerprint, false) assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd") end end describe "when custom metadata elements have been inserted" do let(:xml_text) { subclass.new.generate(settings, false) } let(:subclass) do Class.new(OneLogin::RubySaml::Metadata) do def add_extras(root, _settings) idp = REXML::Element.new("md:IDPSSODescriptor") idp.attributes['protocolSupportEnumeration'] = 'urn:oasis:names:tc:SAML:2.0:protocol' nid = REXML::Element.new("md:NameIDFormat") nid.text = 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress' idp.add_element(nid) sso = REXML::Element.new("md:SingleSignOnService") sso.attributes['Binding'] = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST' sso.attributes['Location'] = 'https://foobar.com/sso' idp.add_element(sso) root.insert_before(root.children[0], idp) org = REXML::Element.new("md:Organization") org.add_element("md:OrganizationName", 'xml:lang' => "en-US").text = 'ACME Inc.' org.add_element("md:OrganizationDisplayName", 'xml:lang' => "en-US").text = 'ACME' org.add_element("md:OrganizationURL", 'xml:lang' => "en-US").text = 'https://www.acme.com' root.insert_after(root.children[3], org) end end end it "inserts signature as the first child of root element" do first_child = xml_doc.root.children[0] assert_equal first_child.prefix, 'ds' assert_equal first_child.name, 'Signature' assert_match %r[([a-zA-Z0-9/+=]+)]m, xml_text assert_match %r[], xml_text assert_match %r[], xml_text signed_metadata = XMLSecurity::SignedDocument.new(xml_text) assert signed_metadata.validate_document(ruby_saml_cert_fingerprint, false) assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd") end end end end end