Comparing changes

v0.2.6 v0.2.7
10 commits 23 files changed
lib/saml/kit/bindings/http_post.rb
@@ -18,9 +18,9 @@ module Saml
           [location, saml_params]
         end
 
-        def deserialize(params)
+        def deserialize(params, configuration: Saml::Kit.configuration)
           xml = decode(saml_param_from(params))
-          Saml::Kit::Document.to_saml_document(xml)
+          Saml::Kit::Document.to_saml_document(xml, configuration: configuration)
         end
       end
     end
lib/saml/kit/bindings/http_redirect.rb
@@ -9,14 +9,14 @@ module Saml
         end
 
         def serialize(builder, relay_state: nil)
-          builder.sign = false
+          builder.embed_signature = false
           builder.destination = location
           document = builder.build
           [UrlBuilder.new(configuration: builder.configuration).build(document, relay_state: relay_state), {}]
         end
 
-        def deserialize(params)
-          document = deserialize_document_from!(params)
+        def deserialize(params, configuration: Saml::Kit.configuration)
+          document = deserialize_document_from!(params, configuration)
           ensure_valid_signature!(params, document)
           document.signature_verified!
           document
@@ -24,10 +24,10 @@ module Saml
 
         private
 
-        def deserialize_document_from!(params)
+        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)
+          Saml::Kit::Document.to_saml_document(xml, configuration: configuration)
         end
 
         def ensure_valid_signature!(params, document)
lib/saml/kit/builders/templates/identity_provider_metadata.builder
@@ -1,32 +1,21 @@
-xml.instruct!
-xml.EntityDescriptor entity_descriptor_options do
-  signature_for(reference_id: id, xml: xml)
-  xml.IDPSSODescriptor idp_sso_descriptor_options do
-    configuration.certificates(use: :signing).each do |certificate|
-      render certificate, xml: xml
-    end
-    configuration.certificates(use: :encryption).each do |certificate|
-      render certificate, xml: xml
-    end
-    logout_urls.each do |item|
-      xml.SingleLogoutService Binding: item[:binding], Location: item[:location]
-    end
-    name_id_formats.each do |format|
-      xml.NameIDFormat format
-    end
-    single_sign_on_urls.each do |item|
-      xml.SingleSignOnService Binding: item[:binding], Location: item[:location]
-    end
-    attributes.each do |attribute|
-      xml.tag! 'saml:Attribute', Name: attribute
-    end
+signature_for(reference_id: id, xml: xml)
+xml.IDPSSODescriptor descriptor_options do
+  configuration.certificates(use: :signing).each do |certificate|
+    render certificate, xml: xml
   end
-  xml.Organization do
-    xml.OrganizationName organization_name, 'xml:lang': "en"
-    xml.OrganizationDisplayName organization_name, 'xml:lang': "en"
-    xml.OrganizationURL organization_url, 'xml:lang': "en"
+  configuration.certificates(use: :encryption).each do |certificate|
+    render certificate, xml: xml
   end
-  xml.ContactPerson contactType: "technical" do
-    xml.Company "mailto:#{contact_email}"
+  logout_urls.each do |item|
+    xml.SingleLogoutService Binding: item[:binding], Location: item[:location]
+  end
+  name_id_formats.each do |format|
+    xml.NameIDFormat format
+  end
+  single_sign_on_urls.each do |item|
+    xml.SingleSignOnService Binding: item[:binding], Location: item[:location]
+  end
+  attributes.each do |attribute|
+    xml.tag! 'saml:Attribute', Name: attribute
   end
 end
lib/saml/kit/builders/templates/metadata.builder
@@ -0,0 +1,13 @@
+xml.instruct!
+xml.EntityDescriptor entity_descriptor_options do
+  render identity_provider, xml: xml
+  render service_provider, xml: xml
+  xml.Organization do
+    xml.OrganizationName organization_name, 'xml:lang': "en"
+    xml.OrganizationDisplayName organization_name, 'xml:lang': "en"
+    xml.OrganizationURL organization_url, 'xml:lang': "en"
+  end
+  xml.ContactPerson contactType: "technical" do
+    xml.Company "mailto:#{contact_email}"
+  end
+end
lib/saml/kit/builders/templates/service_provider_metadata.builder
@@ -1,29 +1,18 @@
-xml.instruct!
-xml.EntityDescriptor entity_descriptor_options do
-  signature_for(reference_id: id, xml: xml)
-  xml.SPSSODescriptor descriptor_options do
-    configuration.certificates(use: :signing).each do |certificate|
-      render certificate, xml: xml
-    end
-    configuration.certificates(use: :encryption).each do |certificate|
-      render certificate, xml: xml
-    end
-    logout_urls.each do |item|
-      xml.SingleLogoutService Binding: item[:binding], Location: item[:location]
-    end
-    name_id_formats.each do |format|
-      xml.NameIDFormat format
-    end
-    acs_urls.each_with_index do |item, index|
-      xml.AssertionConsumerService Binding: item[:binding], Location: item[:location], index: index, isDefault: index == 0 ? true : false
-    end
+signature_for(reference_id: id, xml: xml)
+xml.SPSSODescriptor descriptor_options do
+  configuration.certificates(use: :signing).each do |certificate|
+    render certificate, xml: xml
   end
-  xml.Organization do
-    xml.OrganizationName organization_name, 'xml:lang': "en"
-    xml.OrganizationDisplayName organization_name, 'xml:lang': "en"
-    xml.OrganizationURL organization_url, 'xml:lang': "en"
+  configuration.certificates(use: :encryption).each do |certificate|
+    render certificate, xml: xml
   end
-  xml.ContactPerson contactType: "technical" do
-    xml.Company "mailto:#{contact_email}"
+  logout_urls.each do |item|
+    xml.SingleLogoutService Binding: item[:binding], Location: item[:location]
+  end
+  name_id_formats.each do |format|
+    xml.NameIDFormat format
+  end
+  acs_urls.each_with_index do |item, index|
+    xml.AssertionConsumerService Binding: item[:binding], Location: item[:location], index: index, isDefault: index == 0 ? true : false
   end
 end
lib/saml/kit/builders/assertion.rb
@@ -5,7 +5,7 @@ module Saml
         include Templatable
         extend Forwardable
 
-        def_delegators :@response_builder, :encrypt, :sign, :request, :issuer, :reference_id, :now, :configuration, :user, :version, :destination, :encryption_certificate
+        def_delegators :@response_builder, :encrypt, :embed_signature, :request, :issuer, :reference_id, :now, :configuration, :user, :version, :destination, :encryption_certificate
 
         def initialize(response_builder)
           @response_builder = response_builder
lib/saml/kit/builders/identity_provider_metadata.rb
@@ -3,10 +3,13 @@ module Saml
     module Builders
       class IdentityProviderMetadata
         include Saml::Kit::Templatable
-        attr_accessor :id, :organization_name, :organization_url, :contact_email, :entity_id, :attributes, :name_id_formats
+        extend Forwardable
+        attr_accessor :attributes, :name_id_formats
         attr_accessor :want_authn_requests_signed
         attr_reader :logout_urls, :single_sign_on_urls
         attr_reader :configuration
+        attr_reader :metadata
+        def_delegators :metadata, :id, :id=, :entity_id, :entity_id=, :organization_name, :organization_name=, :organization_url, :organization_url=, :contact_email, :contact_email=, :to_xml
 
         def initialize(configuration: Saml::Kit.configuration)
           @attributes = []
@@ -17,6 +20,8 @@ module Saml
           @name_id_formats = [Namespaces::PERSISTENT]
           @single_sign_on_urls = []
           @want_authn_requests_signed = true
+          @metadata = Saml::Kit::Builders::Metadata.new(configuration: configuration)
+          @metadata.identity_provider = self
         end
 
         def add_single_sign_on_service(url, binding: :http_post)
@@ -43,7 +48,7 @@ module Saml
           }
         end
 
-        def idp_sso_descriptor_options
+        def descriptor_options
           {
             WantAuthnRequestsSigned: want_authn_requests_signed,
             protocolSupportEnumeration: Namespaces::PROTOCOL,
lib/saml/kit/builders/metadata.rb
@@ -0,0 +1,50 @@
+module Saml
+  module Kit
+    module Builders
+      class Metadata
+        include Templatable
+
+        attr_accessor :entity_id
+        attr_accessor :id
+        attr_accessor :identity_provider
+        attr_accessor :organization_name, :organization_url, :contact_email
+        attr_accessor :service_provider
+        attr_reader :configuration
+
+        def initialize(configuration: Saml::Kit.configuration)
+          @id = Id.generate
+          @entity_id = configuration.issuer
+          @configuration = configuration
+        end
+
+        def build_service_provider
+          @service_provider = Saml::Kit::ServiceProviderMetadata.builder(configuration: configuration) do |x|
+            yield x if block_given?
+          end
+        end
+
+        def build_identity_provider
+          @identity_provider = Saml::Kit::IdentityProviderMetadata.builder(configuration: configuration) do |x|
+            yield x if block_given?
+          end
+        end
+
+        def build
+          Saml::Kit::Metadata.from(to_xml)
+        end
+
+        private
+
+        def entity_descriptor_options
+          {
+            'xmlns': Namespaces::METADATA,
+            'xmlns:ds': Namespaces::XMLDSIG,
+            'xmlns:saml': Namespaces::ASSERTION,
+            ID: id,
+            entityID: entity_id,
+          }
+        end
+      end
+    end
+  end
+end
lib/saml/kit/builders/response.rb
@@ -18,7 +18,7 @@ module Saml
           @version = "2.0"
           @status_code = Namespaces::SUCCESS
           @issuer = configuration.issuer
-          @sign = want_assertions_signed
+          @embed_signature = want_assertions_signed
           @encrypt = encryption_certificate.present?
           @configuration = configuration
         end
lib/saml/kit/builders/service_provider_metadata.rb
@@ -3,19 +3,21 @@ module Saml
     module Builders
       class ServiceProviderMetadata
         include Saml::Kit::Templatable
-        attr_accessor :id, :entity_id, :acs_urls, :logout_urls, :name_id_formats
-        attr_accessor :organization_name, :organization_url, :contact_email
+        extend Forwardable
+        attr_accessor :acs_urls, :logout_urls, :name_id_formats
         attr_accessor :want_assertions_signed
         attr_reader :configuration
+        attr_reader :metadata
+        def_delegators :metadata, :id, :id=, :entity_id, :entity_id=, :organization_name, :organization_name=, :organization_url, :organization_url=, :contact_email, :contact_email=, :to_xml
 
         def initialize(configuration: Saml::Kit.configuration)
           @acs_urls = []
           @configuration = configuration
-          @entity_id = configuration.issuer
-          @id = Id.generate
           @logout_urls = []
           @name_id_formats = [Namespaces::PERSISTENT]
           @want_assertions_signed = true
+          @metadata = Saml::Kit::Builders::Metadata.new(configuration: configuration)
+          @metadata.service_provider = self
         end
 
         def add_assertion_consumer_service(url, binding: :http_post)
lib/saml/kit/builders/xml_signature.rb
@@ -17,7 +17,7 @@ module Saml
           SHA512: "http://www.w3.org/2001/04/xmlenc#sha512",
         }.freeze
 
-        attr_reader :sign, :configuration
+        attr_reader :embed_signature, :configuration
         attr_reader :reference_id
         attr_reader :x509_certificate
 
lib/saml/kit/authentication_request.rb
@@ -18,7 +18,7 @@ module Saml
       def response_for(user, binding:, relay_state: nil)
         response_binding = provider.assertion_consumer_service_for(binding: binding)
         builder = Saml::Kit::Response.builder(user, self) do |x|
-          x.sign = provider.want_assertions_signed
+          x.embed_signature = provider.want_assertions_signed
           yield x if block_given?
         end
         response_binding.serialize(builder, relay_state: relay_state)
lib/saml/kit/builders.rb
@@ -3,6 +3,7 @@ require 'saml/kit/builders/authentication_request'
 require 'saml/kit/builders/identity_provider_metadata'
 require 'saml/kit/builders/logout_request'
 require 'saml/kit/builders/logout_response'
+require 'saml/kit/builders/metadata'
 require 'saml/kit/builders/response'
 require 'saml/kit/builders/service_provider_metadata'
 require 'saml/kit/builders/xml_encryption'
lib/saml/kit/document.rb
@@ -64,16 +64,16 @@ module Saml
       end
 
       class << self
-        def to_saml_document(xml)
+        def to_saml_document(xml, configuration: Saml::Kit.configuration)
           hash = Hash.from_xml(xml)
           if hash['Response'].present?
-            Response.new(xml)
+            Response.new(xml, configuration: configuration)
           elsif hash['LogoutResponse'].present?
-            LogoutResponse.new(xml)
+            LogoutResponse.new(xml, configuration: configuration)
           elsif hash['AuthnRequest'].present?
-            AuthenticationRequest.new(xml)
+            AuthenticationRequest.new(xml, configuration: configuration)
           elsif hash['LogoutRequest'].present?
-            LogoutRequest.new(xml)
+            LogoutRequest.new(xml, configuration: configuration)
           end
         rescue => error
           Saml::Kit.logger.error(error)
lib/saml/kit/identity_provider_metadata.rb
@@ -31,7 +31,7 @@ module Saml
 
       def login_request_for(binding:, relay_state: nil)
         builder = Saml::Kit::AuthenticationRequest.builder do |x|
-          x.sign = want_authn_requests_signed
+          x.embed_signature = want_authn_requests_signed
           yield x if block_given?
         end
         request_binding = single_sign_on_service_for(binding: binding)
lib/saml/kit/logout_response.rb
@@ -3,9 +3,9 @@ module Saml
     class LogoutResponse < Document
       include Respondable
 
-      def initialize(xml, request_id: nil)
+      def initialize(xml, request_id: nil, configuration: Saml::Kit.configuration)
         @request_id = request_id
-        super(xml, name: "LogoutResponse")
+        super(xml, name: "LogoutResponse", configuration: configuration)
       end
 
       Builder = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Saml::Kit::LogoutResponse::Builder', 'Saml::Kit::Builders::LogoutResponse')
lib/saml/kit/metadata.rb
@@ -109,6 +109,10 @@ module Saml
         end
       end
 
+      def self.builder_class
+        Saml::Kit::Builders::Metadata
+      end
+
       private
 
       def document
lib/saml/kit/templatable.rb
@@ -1,7 +1,12 @@
 module Saml
   module Kit
     module Templatable
-      attr_accessor :sign
+      attr_accessor :embed_signature
+
+      def sign=(value)
+        Saml::Kit.deprecate("sign= is deprecated. Use embed_signature= instead")
+        self.embed_signature = value
+      end
 
       def to_xml(xml: ::Builder::XmlMarkup.new)
         signatures.complete(render(self, xml: xml))
@@ -13,7 +18,7 @@ module Saml
       end
 
       def sign?
-        sign.nil? ? configuration.sign? : sign && configuration.sign?
+        embed_signature.nil? ? configuration.sign? : embed_signature && configuration.sign?
       end
 
       def signatures
lib/saml/kit/version.rb
@@ -1,5 +1,5 @@
 module Saml
   module Kit
-    VERSION = "0.2.6"
+    VERSION = "0.2.7"
   end
 end
lib/saml/kit.rb
@@ -74,7 +74,7 @@ module Saml
       end
 
       def deprecate(message)
-        @deprecation ||= ActiveSupport::Deprecation.new('next-release', 'saml-kit')
+        @deprecation ||= ActiveSupport::Deprecation.new('1.0.0', 'saml-kit')
         @deprecation.deprecation_warning(message)
       end
     end
spec/saml/builders/logout_request_spec.rb
@@ -36,7 +36,7 @@ RSpec.describe Saml::Kit::Builders::LogoutRequest do
   end
 
   it 'excludes a signature' do
-    subject.sign = false
+    subject.embed_signature = false
     xml_hash = Hash.from_xml(subject.to_xml)
     expect(xml_hash['LogoutRequest']['Signature']).to be_nil
   end
spec/saml/builders/metadata_spec.rb
@@ -0,0 +1,56 @@
+RSpec.describe Saml::Kit::Builders::Metadata do
+  describe ".build" do
+    subject { Saml::Kit::Metadata }
+    let(:url) { FFaker::Internet.uri("https") }
+
+    it 'builds metadata for a service provider' do
+      result = subject.build do |builder|
+        builder.build_service_provider do |x|
+          x.add_assertion_consumer_service(url, binding: :http_post)
+        end
+      end
+
+      hash_result = Hash.from_xml(result.to_xml)
+      expect(hash_result['EntityDescriptor']).to be_present
+      expect(hash_result['EntityDescriptor']['SPSSODescriptor']).to be_present
+      expect(hash_result['EntityDescriptor']['SPSSODescriptor']['AssertionConsumerService']).to be_present
+      expect(hash_result['EntityDescriptor']['SPSSODescriptor']['AssertionConsumerService']['Location']).to eql(url)
+    end
+
+    it 'builds metadata for an identity provider' do
+      result = subject.build do |builder|
+        builder.build_identity_provider do |x|
+          x.add_single_sign_on_service(url, binding: :http_post)
+        end
+      end
+
+      hash_result = Hash.from_xml(result.to_xml)
+      expect(hash_result['EntityDescriptor']).to be_present
+      expect(hash_result['EntityDescriptor']['IDPSSODescriptor']).to be_present
+      expect(hash_result['EntityDescriptor']['IDPSSODescriptor']['SingleSignOnService']).to be_present
+      expect(hash_result['EntityDescriptor']['IDPSSODescriptor']['SingleSignOnService']['Location']).to eql(url)
+    end
+
+    it 'builds metadata for both IDP and SP' do
+      result = subject.build do |builder|
+        builder.build_service_provider do |x|
+          x.add_assertion_consumer_service(url, binding: :http_post)
+        end
+        builder.build_identity_provider do |x|
+          x.add_single_sign_on_service(url, binding: :http_post)
+        end
+      end
+
+      hash_result = Hash.from_xml(result.to_xml)
+      expect(hash_result['EntityDescriptor']).to be_present
+      expect(hash_result['EntityDescriptor']['IDPSSODescriptor']).to be_present
+      expect(hash_result['EntityDescriptor']['SPSSODescriptor']).to be_present
+
+      expect(hash_result['EntityDescriptor']['IDPSSODescriptor']['SingleSignOnService']).to be_present
+      expect(hash_result['EntityDescriptor']['IDPSSODescriptor']['SingleSignOnService']['Location']).to eql(url)
+      expect(hash_result['EntityDescriptor']['SPSSODescriptor']['AssertionConsumerService']).to be_present
+      expect(hash_result['EntityDescriptor']['SPSSODescriptor']['AssertionConsumerService']['Location']).to eql(url)
+    end
+
+  end
+end
spec/saml/builders/response_spec.rb
@@ -25,7 +25,7 @@ RSpec.describe Saml::Kit::Builders::Response do
       allow(configuration.registry).to receive(:metadata_for).with(issuer).and_return(provider)
       allow(provider).to receive(:matches?).and_return(true)
 
-      subject.sign = true
+      subject.embed_signature = true
       subject.encrypt = true
       result = subject.build
       expect(result).to be_valid
@@ -111,7 +111,7 @@ RSpec.describe Saml::Kit::Builders::Response do
 
     it 'generates a signed response and encrypted assertion' do
       subject.encrypt = true
-      subject.sign = true
+      subject.embed_signature = true
       result = Hash.from_xml(subject.to_xml)
       expect(result['Response']['Signature']).to be_present
       expect(result['Response']['EncryptedAssertion']).to be_present
@@ -119,7 +119,7 @@ RSpec.describe Saml::Kit::Builders::Response do
 
     it 'generates a signed response and assertion' do
       subject.encrypt = false
-      subject.sign = true
+      subject.embed_signature = true
       result = Hash.from_xml(subject.to_xml)
       expect(result['Response']['Signature']).to be_present
       expect(result['Response']['Assertion']['Signature']).to be_present
@@ -127,7 +127,7 @@ RSpec.describe Saml::Kit::Builders::Response do
 
     it 'generates a signed response and signed and encrypted assertion' do
       subject.encrypt = true
-      subject.sign = true
+      subject.embed_signature = true
 
       result = Saml::Kit::Response.new(subject.to_xml, configuration: configuration)
       expect(result).to be_signed
@@ -137,7 +137,7 @@ RSpec.describe Saml::Kit::Builders::Response do
 
     it 'generates an encrypted assertion' do
       subject.encrypt = true
-      subject.sign = false
+      subject.embed_signature = false
 
       result = Saml::Kit::Response.new(subject.to_xml, configuration: configuration)
       expect(result).to_not be_signed