Commit ba799de

mo <mo@mokhan.ca>
2017-11-28 23:58:22
extract a certificate class.
1 parent 1381fb6
lib/saml/kit/certificate.rb
@@ -0,0 +1,52 @@
+module Saml
+  module Kit
+    class Certificate
+      attr_reader :value, :use
+
+      def initialize(value, use:)
+        @value = value
+        @use = use.downcase.to_sym
+      end
+
+      def fingerprint
+        Fingerprint.new(value)
+      end
+
+      def for?(use)
+        self.use == use.to_sym
+      end
+
+      def encryption?
+        :encryption == use
+      end
+
+      def signing?
+        :signing == use
+      end
+
+      def x509
+        OpenSSL::X509::Certificate.new(Base64.decode64(value))
+      end
+
+      def public_key
+        x509.public_key
+      end
+
+      def ==(other)
+        self.to_s == other.to_s
+      end
+
+      def eql?(other)
+        self == other
+      end
+
+      def hash
+        value.hash
+      end
+
+      def to_s
+        value
+      end
+    end
+  end
+end
lib/saml/kit/metadata.rb
@@ -30,20 +30,16 @@ module Saml
       def certificates
         @certificates ||= document.find_all("/md:EntityDescriptor/md:#{name}/md:KeyDescriptor").map do |item|
           cert = item.at_xpath("./ds:KeyInfo/ds:X509Data/ds:X509Certificate", Xml::NAMESPACES).text
-          {
-            text: cert,
-            fingerprint: Fingerprint.new(cert).algorithm(hash_algorithm),
-            use: item.attribute('use').value.to_sym,
-          }
+          Certificate.new(cert, use: item.attribute('use').value.to_sym)
         end
       end
 
       def encryption_certificates
-        certificates.find_all { |x| x[:use] == :encryption }
+        certificates.find_all(&:encryption?)
       end
 
       def signing_certificates
-        certificates.find_all { |x| x[:use] == :signing }
+        certificates.find_all(&:signing?)
       end
 
       def services(type)
@@ -68,12 +64,8 @@ module Saml
       end
 
       def matches?(fingerprint, use: :signing)
-        if :signing == use.to_sym
-          hash_value = fingerprint.algorithm(hash_algorithm)
-          signing_certificates.find do |signing_certificate|
-            Saml::Kit.logger.debug [hash_value, signing_certificate[:fingerprint]].inspect
-            hash_value == signing_certificate[:fingerprint]
-          end
+        certificates.find do |certificate|
+          certificate.for?(use) && certificate.fingerprint == fingerprint
         end
       end
 
@@ -91,9 +83,7 @@ module Saml
 
       def verify(algorithm, signature, data)
         signing_certificates.find do |cert|
-          x509 = OpenSSL::X509::Certificate.new(Base64.decode64(cert[:text]))
-          public_key = x509.public_key
-          public_key.verify(algorithm, signature, data)
+          cert.public_key.verify(algorithm, signature, data)
         end
       end
 
lib/saml/kit.rb
@@ -23,6 +23,7 @@ require "saml/kit/document"
 
 require "saml/kit/authentication_request"
 require "saml/kit/bindings"
+require "saml/kit/certificate"
 require "saml/kit/configuration"
 require "saml/kit/crypto"
 require "saml/kit/cryptography"
spec/saml/certificate_spec.rb
@@ -0,0 +1,11 @@
+require 'spec_helper'
+
+RSpec.describe Saml::Kit::Certificate do
+  subject { described_class.new(Saml::Kit.configuration.stripped_signing_certificate, use: :signing) }
+
+  describe "#fingerprint" do
+    it 'returns a fingerprint' do
+      expect(subject.fingerprint).to be_instance_of(Saml::Kit::Fingerprint)
+    end
+  end
+end
spec/saml/identity_provider_metadata_spec.rb
@@ -21,7 +21,8 @@ RSpec.describe Saml::Kit::IdentityProviderMetadata do
     it { expect(subject.single_logout_services).to be_empty }
     it do
       fingerprint = "9F:74:13:3B:BC:5A:7B:8B:2D:4F:8B:EF:1E:88:EB:D1:AE:BC:19:BF:CA:19:C6:2F:0F:4B:31:1D:68:98:B0:1B"
-      expect(subject.certificates).to match_array([use: :signing, text: certificate, fingerprint: fingerprint])
+      expect(subject.certificates).to match_array([Saml::Kit::Certificate.new(certificate, use: :signing)])
+      expect(subject.certificates.first.fingerprint.to_s).to eql(fingerprint)
     end
     it { expect(subject.attributes).to be_empty }
   end
@@ -60,8 +61,8 @@ RSpec.describe Saml::Kit::IdentityProviderMetadata do
     end
     it do
       expect(subject.certificates).to match_array([
-        { use: :signing, text: signing_certificate, fingerprint: "BE:12:70:84:AD:99:6A:58:28:2A:BC:DA:AB:E8:51:D3:FF:AB:58:30:E0:77:DB:23:57:15:01:B3:86:60:97:80" },
-        { use: :encryption, text: encryption_certificate, fingerprint: "5C:51:0C:8A:6A:02:24:3C:9E:96:96:18:2E:37:65:8F:CC:EA:51:0E:2C:C5:3F:1D:72:47:11:D0:7B:95:26:1F" },
+        Saml::Kit::Certificate.new(signing_certificate, use: :signing),
+        Saml::Kit::Certificate.new(encryption_certificate, use: :encryption),
       ])
     end
     it { expect(subject.attributes).to be_present }
spec/saml/service_provider_metadata_spec.rb
@@ -10,20 +10,6 @@ RSpec.describe Saml::Kit::ServiceProviderMetadata do
   describe described_class::Builder do
     let(:acs_url) { FFaker::Internet.http_url }
 
-    <<-XML
-<?xml version="1.0" encoding="UTF-8"?>
-<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
-  ID="_a94ad660-23cc-4491-8fe0-1429b7f5a6d8"
-  entityID="https://service.dev/metadata">
-  <md:SPSSODescriptor
-    AuthnRequestsSigned="true"
-    WantAssertionsSigned="true"
-    protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
-    <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat>
-    <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://service.dev/acs" index="0" isDefault="true"/>
-  </md:SPSSODescriptor>
-</md:EntityDescriptor>
-    XML
     it 'builds the service provider metadata' do
       subject.entity_id = entity_id
       subject.add_assertion_consumer_service(acs_url, binding: :http_post)
@@ -70,19 +56,9 @@ RSpec.describe Saml::Kit::ServiceProviderMetadata do
     end
 
     it 'returns each of the certificates' do
-      expected_sha256 = OpenSSL::Digest::SHA256.new.hexdigest(Saml::Kit.configuration.signing_x509.to_der)
-      expected_encryption_sha256 = OpenSSL::Digest::SHA256.new.hexdigest(Saml::Kit.configuration.encryption_x509.to_der)
       expect(subject.certificates).to match_array([
-        {
-          fingerprint: expected_sha256.upcase.scan(/../).join(":"),
-          use: :signing,
-          text: Saml::Kit.configuration.stripped_signing_certificate
-        },
-        {
-          fingerprint: expected_encryption_sha256.upcase.scan(/../).join(":"),
-          use: :encryption,
-          text: Saml::Kit.configuration.stripped_encryption_certificate
-        },
+        Saml::Kit::Certificate.new(Saml::Kit.configuration.stripped_signing_certificate, use: :signing),
+        Saml::Kit::Certificate.new(Saml::Kit.configuration.stripped_encryption_certificate, use: :encryption),
       ])
     end