Commit 8942f86

mo <mo.khan@gmail.com>
2017-12-24 04:06:14
allow for clock drift.
1 parent 02c620c
lib/saml/kit/assertion.rb
@@ -32,7 +32,9 @@ module Saml
       end
 
       def active?
-        Time.current > started_at && !expired?
+        clock_drift = configuration.clock_drift
+        start = clock_drift.before(started_at)
+        Time.current > start && !expired?
       end
 
       def attributes
@@ -69,9 +71,11 @@ module Saml
 
       private
 
+      attr_reader :configuration
+
       def assertion
         @assertion ||= if encrypted?
-          decrypted = XmlDecryption.new(configuration: @configuration).decrypt(@xml_hash['Response']['EncryptedAssertion'])
+          decrypted = XmlDecryption.new(configuration: configuration).decrypt(@xml_hash['Response']['EncryptedAssertion'])
           Saml::Kit.logger.debug(decrypted)
           Hash.from_xml(decrypted)['Assertion']
         else
@@ -91,7 +95,7 @@ module Saml
       end
 
       def must_match_issuer
-        unless audiences.include?(@configuration.issuer)
+        unless audiences.include?(configuration.issuer)
           errors[:audience] << error_message(:must_match_issuer)
         end
       end
lib/saml/kit/configuration.rb
@@ -32,14 +32,17 @@ module Saml
       attr_accessor :session_timeout
       # The logger to write log messages to.
       attr_accessor :logger
+      # The total allowable clock drift for session timeout validation.
+      attr_accessor :clock_drift
 
       def initialize # :yields configuration
-        @signature_method = :SHA256
+        @clock_drift = 30.seconds
         @digest_method = :SHA256
+        @key_pairs = []
+        @logger = Logger.new(STDOUT)
         @registry = DefaultRegistry.new
         @session_timeout = 3.hours
-        @logger = Logger.new(STDOUT)
-        @key_pairs = []
+        @signature_method = :SHA256
         yield self if block_given?
       end
 
spec/saml/assertion_spec.rb
@@ -0,0 +1,29 @@
+RSpec.describe Saml::Kit::Assertion do
+  describe "#active?" do
+    let(:configuration) do
+      Saml::Kit::Configuration.new do |config|
+        config.session_timeout = 30.minutes
+        config.clock_drift = 30.seconds
+      end
+    end
+
+    it 'is valid after a valid session window + drift' do
+      now = Time.current
+      travel_to now
+      xml_hash = {
+        'Response' => {
+          'Assertion' => {
+            'Conditions' => {
+              'NotBefore' => now.utc.iso8601,
+              'NotOnOrAfter' => configuration.session_timeout.since(now).iso8601,
+            }
+          }
+        }
+      }
+      subject = described_class.new(xml_hash, configuration: configuration)
+      travel_to (configuration.clock_drift - 1.second).before(now)
+      expect(subject).to be_active
+      expect(subject).to_not be_expired
+    end
+  end
+end
spec/saml/response_spec.rb
@@ -125,7 +125,7 @@ RSpec.describe Saml::Kit::Response do
       allow(metadata).to receive(:matches?).and_return(true)
 
       subject = described_class.build(user, request)
-      travel_to 5.seconds.ago
+      travel_to (Saml::Kit.configuration.clock_drift + 1.second).before(Time.now)
       expect(subject).to be_invalid
       expect(subject.errors[:base]).to be_present
     end