Commit f6b84da

mo <mo.khan@gmail.com>
2017-11-10 21:50:57
skip signing assertions if SP does not want it.
1 parent 3b8db92
lib/saml/kit/authentication_request.rb
@@ -66,6 +66,10 @@ module Saml
         provider.matches?(fingerprint, use: :signing)
       end
 
+      def provider
+        registry.metadata_for(issuer)
+      end
+
       private
 
       def registered_acs_url
@@ -74,10 +78,6 @@ module Saml
         return acs_urls.first[:location] if acs_urls.any?
       end
 
-      def provider
-        registry.metadata_for(issuer)
-      end
-
       def registry
         Saml::Kit.configuration.registry
       end
lib/saml/kit/response.rb
@@ -113,12 +113,12 @@ module Saml
         end
       end
 
-      private
-
       def provider
         registry.metadata_for(issuer)
       end
 
+      private
+
       def registry
         Saml::Kit.configuration.registry
       end
@@ -211,9 +211,14 @@ module Saml
           @issuer = configuration.issuer
         end
 
-        def to_xml
-          signature = Signature.new(id)
-          xml = ::Builder::XmlMarkup.new
+        def want_assertions_signed
+          request.provider.want_assertions_signed
+        rescue
+          true
+        end
+
+        def to_xml(xml = ::Builder::XmlMarkup.new)
+          signature = Signature.new(id, sign: want_assertions_signed)
           xml.Response response_options do
             xml.Issuer(issuer, xmlns: Namespaces::ASSERTION)
             signature.template(xml)
lib/saml/kit/service_provider_metadata.rb
@@ -14,10 +14,16 @@ module Saml
         end
       end
 
+      def want_assertions_signed
+        attribute = find_by("/md:EntityDescriptor/md:#{name}").attribute("WantAssertionsSigned")
+        attribute.text.downcase == "true"
+      end
+
       private
 
       class Builder
         attr_accessor :id, :entity_id, :acs_urls, :logout_urls, :name_id_formats, :sign
+        attr_accessor :want_assertions_signed
 
         def initialize(configuration = Saml::Kit.configuration)
           @id = SecureRandom.uuid
@@ -27,6 +33,7 @@ module Saml
           @logout_urls = []
           @name_id_formats = [Namespaces::PERSISTENT]
           @sign = true
+          @want_assertions_signed = true
         end
 
         def add_assertion_consumer_service(url, binding: :post)
@@ -83,7 +90,7 @@ module Saml
         def descriptor_options
           {
             AuthnRequestsSigned: "true",
-            WantAssertionsSigned: "true",
+            WantAssertionsSigned: want_assertions_signed,
             protocolSupportEnumeration: Namespaces::PROTOCOL,
           }
         end
spec/saml/response_spec.rb
@@ -4,7 +4,7 @@ RSpec.describe Saml::Kit::Response do
   describe "#acs_url" do
     let(:acs_url) { "https://#{FFaker::Internet.domain_name}/acs" }
     let(:user) { double(:user, name_id_for: SecureRandom.uuid, assertion_attributes_for: { }) }
-    let(:request) { double(id: SecureRandom.uuid, acs_url: acs_url, issuer: FFaker::Movie.title, name_id_format: Saml::Kit::Namespaces::EMAIL_ADDRESS) }
+    let(:request) { instance_double(Saml::Kit::AuthenticationRequest, id: SecureRandom.uuid, acs_url: acs_url, issuer: FFaker::Movie.title, name_id_format: Saml::Kit::Namespaces::EMAIL_ADDRESS, provider: nil) }
     subject { described_class::Builder.new(user, request).build }
 
     it 'returns the acs_url' do
@@ -15,7 +15,7 @@ RSpec.describe Saml::Kit::Response do
   describe "#to_xml" do
     subject { described_class::Builder.new(user, request) }
     let(:user) { double(:user, name_id_for: SecureRandom.uuid, assertion_attributes_for: { email: email, created_at: Time.now.utc.iso8601 }) }
-    let(:request) { double(id: SecureRandom.uuid, acs_url: acs_url, issuer: FFaker::Movie.title, name_id_format: Saml::Kit::Namespaces::EMAIL_ADDRESS) }
+    let(:request) { double(id: SecureRandom.uuid, acs_url: acs_url, issuer: FFaker::Movie.title, name_id_format: Saml::Kit::Namespaces::EMAIL_ADDRESS, provider: nil) }
     let(:acs_url) { "https://#{FFaker::Internet.domain_name}/acs" }
     let(:issuer) { FFaker::Movie.title }
     let(:email) { FFaker::Internet.email }
@@ -63,6 +63,16 @@ RSpec.describe Saml::Kit::Response do
       expect(hash['Response']['Assertion']['AttributeStatement']['Attribute'][1]['NameFormat']).to eql('urn:oasis:names:tc:SAML:2.0:attrname-format:uri')
       expect(hash['Response']['Assertion']['AttributeStatement']['Attribute'][1]['AttributeValue']).to be_present
     end
+
+    it 'does not add a signature when the SP does not want assertions signed' do
+      builder = Saml::Kit::ServiceProviderMetadata::Builder.new
+      builder.want_assertions_signed = false
+      metadata = builder.build
+      allow(request).to receive(:provider).and_return(metadata)
+
+      hash = Hash.from_xml(subject.to_xml)
+      expect(hash['Response']['Signature']).to be_nil
+    end
   end
 
   describe ".deserialize" do
@@ -90,7 +100,7 @@ RSpec.describe Saml::Kit::Response do
   end
 
   describe "#valid?" do
-    let(:request) { instance_double(Saml::Kit::AuthenticationRequest, id: "_#{SecureRandom.uuid}", issuer: FFaker::Internet.http_url, acs_url: FFaker::Internet.http_url, name_id_format: Saml::Kit::Namespaces::PERSISTENT) }
+    let(:request) { instance_double(Saml::Kit::AuthenticationRequest, id: "_#{SecureRandom.uuid}", issuer: FFaker::Internet.http_url, acs_url: FFaker::Internet.http_url, name_id_format: Saml::Kit::Namespaces::PERSISTENT, provider: nil) }
     let(:user) { double(:user, name_id_for: SecureRandom.uuid, assertion_attributes_for: { id: SecureRandom.uuid }) }
     let(:builder) { described_class::Builder.new(user, request) }
     let(:registry) { instance_double(Saml::Kit::DefaultRegistry) }
@@ -225,7 +235,7 @@ RSpec.describe Saml::Kit::Response do
 
   describe "#serialize" do
     let(:user) { double(:user, name_id_for: SecureRandom.uuid, assertion_attributes_for: { }) }
-    let(:request) { double(id: SecureRandom.uuid, acs_url: acs_url, issuer: issuer, name_id_format: Saml::Kit::Namespaces::PERSISTENT) }
+    let(:request) { double(id: SecureRandom.uuid, acs_url: acs_url, issuer: issuer, name_id_format: Saml::Kit::Namespaces::PERSISTENT, provider: nil) }
     let(:acs_url) { FFaker::Internet.http_url }
     let(:issuer) { FFaker::Internet.http_url }