Comparing changes

v1.0.26 v1.0.27
10 commits 5 files changed

Commits

00ccb5e fix specs on travis. mo 2018-11-09 02:54:35
209d75d disable cache mo 2018-11-09 01:16:12
c4ef394 upgrade rubies. mo 2018-11-09 01:04:12
06537bb bump version. mo 2018-11-03 16:24:49
bc4da52 fix linter errors. mo 2018-11-03 16:01:14
9e3fd13 fix linter errors mo 2018-10-28 02:01:39
lib/saml/kit/configuration.rb
@@ -90,7 +90,7 @@ module Saml
       # @param use [Symbol] the type of key pair to return
       # `nil`, `:signing` or `:encryption`
       def key_pairs(use: nil)
-        use.present? ? @key_pairs.find_all { |xxx| xxx.for?(use) } : @key_pairs
+        use.present? ? active_key_pairs.find_all { |xxx| xxx.for?(use) } : active_key_pairs
       end
 
       # Return each certificate for a specific use.
@@ -122,6 +122,17 @@ module Saml
         error_message = 'Use must be either :signing or :encryption'
         raise ArgumentError, error_message
       end
+
+      def active_key_pairs
+        @key_pairs.find_all { |x| active?(x) }.sort_by { |x| x.certificate.not_after }.reverse
+      end
+
+      def active?(key_pair)
+        key_pair.certificate.active?
+      rescue OpenSSL::X509::CertificateError => error
+        Saml::Kit.logger.error(error)
+        false
+      end
     end
   end
 end
lib/saml/kit/null_assertion.rb
@@ -25,11 +25,11 @@ module Saml
       end
 
       def started_at
-        Time.at(0).to_datetime
+        Time.at(0)
       end
 
       def expired_at
-        Time.at(0).to_datetime
+        Time.at(0)
       end
 
       def audiences
lib/saml/kit/version.rb
@@ -2,6 +2,6 @@
 
 module Saml
   module Kit
-    VERSION = '1.0.26'.freeze
+    VERSION = '1.0.27'.freeze
   end
 end
spec/saml/kit/configuration_spec.rb
@@ -24,27 +24,128 @@ RSpec.describe Saml::Kit::Configuration do
   describe '#add_key_pair' do
     subject { described_class.new }
 
-    let(:certificate) do
+    let(:active_certificate) do
       certificate = OpenSSL::X509::Certificate.new
+      certificate.not_before = 1.minute.ago
+      certificate.not_after = 1.minute.from_now
+      certificate.public_key = private_key.public_key
+      certificate.sign(private_key, OpenSSL::Digest::SHA256.new)
+      certificate
+    end
+    let(:expired_certificate) do
+      certificate = OpenSSL::X509::Certificate.new
+      certificate.not_before = 2.minutes.ago
+      certificate.not_after = 1.minute.ago
+      certificate.public_key = private_key.public_key
+      certificate.sign(private_key, OpenSSL::Digest::SHA256.new)
+      certificate
+    end
+    let(:unsigned_certificate) do
+      certificate = OpenSSL::X509::Certificate.new
+      certificate.not_before = 2.minutes.ago
+      certificate.not_after = 1.minute.ago
       certificate.public_key = private_key.public_key
       certificate
     end
     let(:private_key) { OpenSSL::PKey::RSA.new(2048) }
 
-    it 'raises an error when the use is not known' do
-      expect do
-        subject.add_key_pair(certificate, private_key.export, use: :blah)
-      end.to raise_error(/:signing or :encryption/)
+    context 'when the use is not known' do
+      specify { expect { subject.add_key_pair(active_certificate, private_key.export, use: :blah) }.to raise_error(/:signing or :encryption/) }
     end
 
-    it 'adds a signing key pair' do
-      subject.add_key_pair(certificate.to_pem, private_key.export, use: :signing)
-      expect(subject.key_pairs(use: :signing).count).to be(1)
+    context 'when adding a signing key pair' do
+      before do
+        subject.add_key_pair(active_certificate.to_pem, private_key.export, use: :signing)
+        subject.add_key_pair(expired_certificate.to_pem, private_key.export, use: :signing)
+        subject.add_key_pair(unsigned_certificate.to_pem, private_key.export, use: :signing)
+      end
+
+      specify do
+        expect(subject.key_pairs(use: :signing).map(&:certificate).map(&:fingerprint).map(&:to_s)).to match_array([
+          Xml::Kit::Fingerprint.new(active_certificate.to_pem).to_s
+        ])
+      end
     end
 
-    it 'adds an encryption key pair' do
-      subject.add_key_pair(certificate.to_pem, private_key.export, use: :encryption)
-      expect(subject.key_pairs(use: :encryption).count).to be(1)
+    context 'when adding an encryption key pair' do
+      before do
+        subject.add_key_pair(active_certificate.to_pem, private_key.export, use: :encryption)
+        subject.add_key_pair(expired_certificate.to_pem, private_key.export, use: :encryption)
+        subject.add_key_pair(unsigned_certificate.to_pem, private_key.export, use: :encryption)
+      end
+
+      specify do
+        expect(subject.key_pairs(use: :encryption).map(&:certificate).map(&:fingerprint).map(&:to_s)).to match_array([
+          Xml::Kit::Fingerprint.new(active_certificate.to_pem).to_s
+        ])
+      end
+    end
+  end
+
+  describe '#key_pairs' do
+    context 'when a certificate expires' do
+      let(:active_certificate) do
+        certificate = OpenSSL::X509::Certificate.new
+        certificate.not_before = 1.minute.ago
+        certificate.not_after = 1.minute.from_now
+        certificate.public_key = private_key.public_key
+        certificate.sign(private_key, OpenSSL::Digest::SHA256.new)
+        certificate
+      end
+      let(:expired_certificate) do
+        certificate = OpenSSL::X509::Certificate.new
+        certificate.not_before = 2.minutes.ago
+        certificate.not_after = 1.minute.ago
+        certificate.public_key = private_key.public_key
+        certificate.sign(private_key, OpenSSL::Digest::SHA256.new)
+        certificate
+      end
+      let(:unsigned_certificate) do
+        certificate = OpenSSL::X509::Certificate.new
+        certificate.not_before = 2.minutes.ago
+        certificate.not_after = 1.minute.ago
+        certificate.public_key = private_key.public_key
+        certificate
+      end
+      let(:private_key) { OpenSSL::PKey::RSA.new(2048) }
+
+      before do
+        subject.add_key_pair(active_certificate.to_pem, private_key.export, use: :signing)
+        subject.add_key_pair(expired_certificate.to_pem, private_key.export, use: :signing)
+        subject.add_key_pair(unsigned_certificate.to_pem, private_key.export, use: :signing)
+      end
+
+      specify { expect(subject.key_pairs.count).to be(1) }
+      specify { expect(subject.key_pairs.map(&:certificate).map(&:fingerprint).map(&:to_s)).to match_array([Xml::Kit::Fingerprint.new(active_certificate).to_s]) }
+    end
+
+    context 'when there is more than one key pair' do
+      let(:oldest_certificate) do
+        certificate = OpenSSL::X509::Certificate.new
+        certificate.not_before = 45.minutes.ago
+        certificate.not_after = 15.minutes.from_now
+        certificate.public_key = private_key.public_key
+        certificate.sign(private_key, OpenSSL::Digest::SHA256.new)
+        certificate
+      end
+      let(:newest_certificate) do
+        certificate = OpenSSL::X509::Certificate.new
+        certificate.not_before = 30.minutes.ago
+        certificate.not_after = 30.minutes.from_now
+        certificate.public_key = private_key.public_key
+        certificate.sign(private_key, OpenSSL::Digest::SHA256.new)
+        certificate
+      end
+      let(:private_key) { OpenSSL::PKey::RSA.new(2048) }
+      let(:fingerprints) { subject.key_pairs.map(&:certificate).map(&:fingerprint).map(&:to_s) }
+
+      before do
+        subject.add_key_pair(oldest_certificate.to_pem, private_key.export, use: :signing)
+        subject.add_key_pair(newest_certificate.to_pem, private_key.export, use: :signing)
+      end
+
+      specify { expect(fingerprints[0]).to eql(Xml::Kit::Fingerprint.new(newest_certificate).to_s) }
+      specify { expect(fingerprints[1]).to eql(Xml::Kit::Fingerprint.new(oldest_certificate).to_s) }
     end
   end
 end
.travis.yml
@@ -1,10 +1,9 @@
 sudo: false
 language: ruby
-cache: bundler
 rvm:
   - 2.2.10
-  - 2.3.7
-  - 2.4.4
+  - 2.3.8
+  - 2.4.5
   - 2.5.3
 script:
   - bin/cibuild