Comparing changes

v0.2.9 v0.2.10
6 commits 4 files changed
Changed files (4)
lib/saml/kit/bindings/http_redirect.rb
@@ -16,9 +16,9 @@ module Saml
         end
 
         def deserialize(params, configuration: Saml::Kit.configuration)
-          document = deserialize_document_from!(params, configuration)
-          ensure_valid_signature!(params, document)
-          document.signature_verified!
+          parameters = normalize(params)
+          document = deserialize_document_from!(parameters, configuration)
+          ensure_valid_signature!(parameters, document)
           document
         end
 
@@ -26,21 +26,24 @@ module Saml
 
         def deserialize_document_from!(params, configuration)
           xml = inflate(decode(unescape(saml_param_from(params))))
-          Saml::Kit.logger.debug(xml)
           Saml::Kit::Document.to_saml_document(xml, configuration: configuration)
         end
 
         def ensure_valid_signature!(params, document)
-          return if params['Signature'].blank? || params['SigAlg'].blank?
+          return if params[:Signature].blank? || params[:SigAlg].blank?
 
-          signature = decode(params['Signature'])
-          canonical_form = ['SAMLRequest', 'SAMLResponse', 'RelayState', 'SigAlg'].map do |key|
+          signature = decode(params[:Signature])
+          canonical_form = [:SAMLRequest, :SAMLResponse, :RelayState, :SigAlg].map do |key|
             value = params[key]
             value.present? ? "#{key}=#{value}" : nil
           end.compact.join('&')
 
-          valid = document.provider.verify(algorithm_for(params['SigAlg']), signature, canonical_form)
-          raise ArgumentError.new("Invalid Signature") unless valid
+          return if document.provider.nil?
+          if document.provider.verify(algorithm_for(params[:SigAlg]), signature, canonical_form)
+            document.signature_verified!
+          else
+            raise ArgumentError.new("Invalid Signature")
+          end
         end
 
         def algorithm_for(algorithm)
@@ -55,6 +58,23 @@ module Saml
             OpenSSL::Digest::SHA1.new
           end
         end
+
+        def normalize(params)
+          if params.respond_to? :inject
+            params.inject({}) do |memo, (key, value)|
+              memo[key.to_sym] = value
+              memo
+            end
+          else
+            {
+              SAMLRequest: params['SAMLRequest'] || params[:SAMLRequest],
+              SAMLResponse: params['SAMLResponse'] || params[:SAMLResponse],
+              RelayState: params['RelayState'] || params[:RelayState],
+              Signature: params['Signature'] || params[:Signature],
+              SigAlg: params['SigAlg'] || params[:SigAlg],
+            }
+          end
+        end
       end
     end
   end
lib/saml/kit/trustable.rb
@@ -6,11 +6,11 @@ module Saml
       included do
         validate :must_have_valid_signature, unless: :signature_manually_verified
         validate :must_be_registered
-        validate :must_be_trusted, unless: :signature_manually_verified
+        validate :must_be_trusted
       end
 
       def signed?
-        signature.present?
+        signature_manually_verified || signature.present?
       end
 
       def signature
@@ -19,6 +19,7 @@ module Saml
       end
 
       def trusted?
+        return true if signature_manually_verified
         return false unless signed?
         signature.trusted?(provider)
       end
lib/saml/kit/version.rb
@@ -1,5 +1,5 @@
 module Saml
   module Kit
-    VERSION = "0.2.9"
+    VERSION = "0.2.10"
   end
 end
spec/saml/bindings/http_redirect_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe Saml::Kit::Bindings::HttpRedirect do
     end
 
     it 'encodes the request using the HTTP-Redirect encoding' do
-      builder = Saml::Kit::AuthenticationRequest.builder_class.new(configuration: configuration)
+      builder = Saml::Kit::AuthenticationRequest.builder(configuration: configuration)
       url, _ = subject.serialize(builder, relay_state: relay_state)
       expect(url).to start_with(location)
       expect(url).to have_query_param('SAMLRequest')
@@ -32,13 +32,28 @@ RSpec.describe Saml::Kit::Bindings::HttpRedirect do
     end
 
     it 'deserializes the SAMLRequest to an AuthnRequest' do
-      url, _ = subject.serialize(Saml::Kit::AuthenticationRequest.builder_class.new)
+      url, _ = subject.serialize(Saml::Kit::AuthenticationRequest.builder)
       result = subject.deserialize(query_params_from(url))
       expect(result).to be_instance_of(Saml::Kit::AuthenticationRequest)
     end
 
     it 'deserializes the SAMLRequest to an AuthnRequest with symbols for keys' do
-      url, _ = subject.serialize(Saml::Kit::AuthenticationRequest.builder_class.new)
+      configuration = Saml::Kit::Configuration.new do |config|
+        config.issuer = issuer
+        config.generate_key_pair_for(use: :signing)
+      end
+      provider = Saml::Kit::IdentityProviderMetadata.build(configuration: configuration)
+      url, _ = subject.serialize(Saml::Kit::AuthenticationRequest.builder(configuration: configuration))
+      allow(configuration.registry).to receive(:metadata_for).with(issuer).and_return(provider)
+
+      result = subject.deserialize(query_params_from(url).symbolize_keys, configuration: configuration)
+      expect(result).to be_instance_of(Saml::Kit::AuthenticationRequest)
+      expect(result).to be_signed
+      expect(result).to be_trusted
+    end
+
+    it 'deserializes the SAMLRequest to an AuthnRequest with symbols for keys' do
+      url, _ = subject.serialize(Saml::Kit::AuthenticationRequest.builder)
       result = subject.deserialize(query_params_from(url).symbolize_keys)
       expect(result).to be_instance_of(Saml::Kit::AuthenticationRequest)
     end
@@ -53,14 +68,14 @@ RSpec.describe Saml::Kit::Bindings::HttpRedirect do
           @params[key]
         end
       end
-      url, _ = subject.serialize(Saml::Kit::AuthenticationRequest.builder_class.new)
+      url, _ = subject.serialize(Saml::Kit::AuthenticationRequest.builder)
       result = subject.deserialize(Parameters.new(query_params_from(url)))
       expect(result).to be_instance_of(Saml::Kit::AuthenticationRequest)
     end
 
     it 'deserializes the SAMLRequest to a LogoutRequest' do
       user = double(:user, name_id_for: SecureRandom.uuid)
-      url, _ = subject.serialize(Saml::Kit::LogoutRequest.builder_class.new(user))
+      url, _ = subject.serialize(Saml::Kit::LogoutRequest.builder(user))
       result = subject.deserialize(query_params_from(url))
       expect(result).to be_instance_of(Saml::Kit::LogoutRequest)
     end
@@ -74,7 +89,7 @@ RSpec.describe Saml::Kit::Bindings::HttpRedirect do
     it 'deserializes the SAMLResponse to a Response' do
       user = double(:user, name_id_for: SecureRandom.uuid, assertion_attributes_for: [])
       request = double(:request, id: SecureRandom.uuid, provider: nil, assertion_consumer_service_url: FFaker::Internet.http_url, name_id_format: Saml::Kit::Namespaces::PERSISTENT, issuer: issuer, signed?: true, trusted?: true)
-      url, _ = subject.serialize(Saml::Kit::Response.builder_class.new(user, request))
+      url, _ = subject.serialize(Saml::Kit::Response.builder(user, request))
       result = subject.deserialize(query_params_from(url))
       expect(result).to be_instance_of(Saml::Kit::Response)
     end
@@ -82,14 +97,14 @@ RSpec.describe Saml::Kit::Bindings::HttpRedirect do
     it 'deserializes the SAMLResponse to a LogoutResponse' do
       user = double(:user, name_id_for: SecureRandom.uuid, assertion_attributes_for: [])
       request = double(:request, id: SecureRandom.uuid, provider: provider, assertion_consumer_service_url: FFaker::Internet.http_url, name_id_format: Saml::Kit::Namespaces::PERSISTENT, issuer: FFaker::Internet.http_url)
-      url, _ = subject.serialize(Saml::Kit::LogoutResponse.builder_class.new(user, request))
+      url, _ = subject.serialize(Saml::Kit::LogoutResponse.builder(user, request))
       result = subject.deserialize(query_params_from(url))
       expect(result).to be_instance_of(Saml::Kit::LogoutResponse)
     end
 
-    it 'raise an error when the content is invalid' do
+    it 'raises an error when the content is invalid' do
       expect do
-        subject.deserialize({ 'SAMLResponse' => "nonsense" })
+        subject.deserialize('SAMLResponse' => "nonsense")
       end.to raise_error(Zlib::DataError)
     end
 
@@ -122,10 +137,24 @@ RSpec.describe Saml::Kit::Bindings::HttpRedirect do
       end
       allow(Saml::Kit.configuration.registry).to receive(:metadata_for).with(issuer).and_return(provider)
 
-      url, _ = subject.serialize(Saml::Kit::AuthenticationRequest.builder_class.new)
+      url, _ = subject.serialize(Saml::Kit::AuthenticationRequest.builder)
       result = subject.deserialize(query_params_from(url))
       expect(result).to be_instance_of(Saml::Kit::AuthenticationRequest)
       expect(result).to be_valid
     end
+
+    it 'returns an unverfied document when the provider is unknown' do
+      configuration = Saml::Kit::Configuration.new do |config|
+        config.generate_key_pair_for(use: :signing)
+      end
+      url, _ = subject.serialize(Saml::Kit::AuthenticationRequest.builder(configuration: configuration))
+
+      other_configuration = Saml::Kit::Configuration.new
+      allow(other_configuration.registry).to receive(:metadata_for).and_return(nil)
+
+      result = subject.deserialize(query_params_from(url), configuration: other_configuration)
+      expect(result).to_not be_signed
+      expect(result).to_not be_trusted
+    end
   end
 end