Commit 9c9fdae

mo <mo.khan@gmail.com>
2017-11-01 20:19:49
parse certificates from SP metadata.
1 parent e6443b7
lib/saml/kit/configuration.rb
@@ -18,6 +18,10 @@ module Saml
         signing_certificate_pem.to_s.gsub(BEGIN_CERT, '').gsub(END_CERT, '').gsub(/\n/, '')
       end
 
+      def signing_x509
+        OpenSSL::X509::Certificate.new(signing_certificate_pem)
+      end
+
       def signing_private_key
         OpenSSL::PKey::RSA.new(signing_private_key_pem, signing_private_key_password)
       end
lib/saml/kit/metadata.rb
@@ -0,0 +1,58 @@
+module Saml
+  module Kit
+    class Metadata
+      NAMESPACES = {
+        "NameFormat": Namespaces::Formats::Attr::SPLAT,
+        "ds": Namespaces::SIGNATURE,
+        "md": Namespaces::METADATA,
+        "saml": Namespaces::ASSERTION,
+      }.freeze
+
+      attr_reader :xml, :descriptor_name
+
+      def initialize(descriptor_name, xml)
+        @descriptor_name = descriptor_name
+        @xml = xml
+      end
+
+      def certificates
+        xpath = "/md:EntityDescriptor/md:#{descriptor_name}/md:KeyDescriptor"
+        find_all(xpath).map do |item|
+          cert = item.at_xpath("./ds:KeyInfo/ds:X509Data/ds:X509Certificate", NAMESPACES).text
+          {
+            fingerprint: fingerprint_for(cert, OpenSSL::Digest::SHA256),
+            text: cert,
+            use: item.attribute('use').value,
+          }
+        end
+      end
+
+      def to_xml
+        @xml
+      end
+
+      private
+
+      def document
+        @document ||= Nokogiri::XML(@xml)
+      end
+
+      def find_by(xpath)
+        document.at_xpath(xpath, NAMESPACES)
+      end
+
+      def find_all(xpath)
+        document.search(xpath, NAMESPACES)
+      end
+
+      def fingerprint_for(value, algorithm)
+        x509 = OpenSSL::X509::Certificate.new(Base64.decode64(value))
+        pretty_fingerprint(algorithm.new.hexdigest(x509.to_der))
+      end
+
+      def pretty_fingerprint(fingerprint)
+        fingerprint.upcase.scan(/../).join(":")
+      end
+    end
+  end
+end
lib/saml/kit/service_provider_metadata.rb
@@ -1,13 +1,11 @@
 module Saml
   module Kit
-    class ServiceProviderMetadata
+    class ServiceProviderMetadata < Metadata
       def initialize(xml)
-        @xml = xml
+        super("SPSSODescriptor", xml)
       end
 
-      def to_xml
-        @xml
-      end
+      private
 
       class Builder
         attr_accessor :id, :entity_id, :acs_urls
lib/saml/kit.rb
@@ -12,6 +12,7 @@ require "xmldsig"
 require "saml/kit/authentication_request"
 require "saml/kit/configuration"
 require "saml/kit/namespaces"
+require "saml/kit/metadata"
 require "saml/kit/request"
 require "saml/kit/response"
 require "saml/kit/service_provider_registry"
spec/saml/service_provider_metadata_spec.rb
@@ -42,10 +42,24 @@ RSpec.describe Saml::Kit::ServiceProviderMetadata do
   end
 
   describe described_class do
+    let(:entity_id) { FFaker::Movie.title }
+    let(:acs_url) { "https://#{FFaker::Internet.domain_name}/acs" }
     let(:builder) { described_class::Builder.new }
+    subject do
+      builder.entity_id = entity_id
+      builder.add_acs_url(acs_url)
+      builder.build
+    end
 
     it 'returns each of the certificates' do
-      
+      expected_sha256 = OpenSSL::Digest::SHA256.new.hexdigest(Saml::Kit.configuration.signing_x509.to_der)
+      expect(subject.certificates).to match_array([
+        {
+          fingerprint: expected_sha256.upcase.scan(/../).join(":"),
+          use: "signing",
+          text: Saml::Kit.configuration.stripped_signing_certificate
+        }
+      ])
     end
   end
 end