Commit 89a5b29
Changed files (5)
lib
saml
spec
lib/saml/kit/builders/service_provider_metadata.rb
@@ -0,0 +1,89 @@
+module Saml
+ module Kit
+ class ServiceProviderMetadata < Metadata
+ 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
+ @configuration = configuration
+ @entity_id = configuration.issuer
+ @acs_urls = []
+ @logout_urls = []
+ @name_id_formats = [Namespaces::PERSISTENT]
+ @sign = true
+ @want_assertions_signed = true
+ end
+
+ def add_assertion_consumer_service(url, binding: :http_post)
+ @acs_urls.push(location: url, binding: Bindings.binding_for(binding))
+ end
+
+ def add_single_logout_service(url, binding: :http_post)
+ @logout_urls.push(location: url, binding: Bindings.binding_for(binding))
+ end
+
+ def to_xml
+ Signature.sign(sign: sign) do |xml, signature|
+ xml.instruct!
+ xml.EntityDescriptor entity_descriptor_options do
+ signature.template(id)
+ xml.SPSSODescriptor descriptor_options do
+ if @configuration.signing_certificate_pem.present?
+ xml.KeyDescriptor use: "signing" do
+ xml.KeyInfo "xmlns": Namespaces::XMLDSIG do
+ xml.X509Data do
+ xml.X509Certificate @configuration.stripped_signing_certificate
+ end
+ end
+ end
+ end
+ if @configuration.encryption_certificate_pem.present?
+ xml.KeyDescriptor use: "encryption" do
+ xml.KeyInfo "xmlns": Namespaces::XMLDSIG do
+ xml.X509Data do
+ xml.X509Certificate @configuration.stripped_encryption_certificate
+ end
+ end
+ end
+ 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
+ end
+ end
+ end
+ end
+
+ def build
+ ServiceProviderMetadata.new(to_xml)
+ end
+
+ private
+
+ def entity_descriptor_options
+ {
+ 'xmlns': Namespaces::METADATA,
+ ID: "_#{id}",
+ entityID: entity_id,
+ }
+ end
+
+ def descriptor_options
+ {
+ AuthnRequestsSigned: sign,
+ WantAssertionsSigned: want_assertions_signed,
+ protocolSupportEnumeration: Namespaces::PROTOCOL,
+ }
+ end
+ end
+ end
+ end
+end
lib/saml/kit/builders.rb
@@ -3,3 +3,4 @@ require 'saml/kit/builders/identity_provider_metadata'
require 'saml/kit/builders/logout_request'
require 'saml/kit/builders/logout_response'
require 'saml/kit/builders/response'
+require 'saml/kit/builders/service_provider_metadata'
lib/saml/kit/service_provider_metadata.rb
@@ -17,90 +17,6 @@ module Saml
attribute = document.find_by("/md:EntityDescriptor/md:#{name}").attribute("WantAssertionsSigned")
attribute.text.downcase == "true"
end
-
- 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
- @configuration = configuration
- @entity_id = configuration.issuer
- @acs_urls = []
- @logout_urls = []
- @name_id_formats = [Namespaces::PERSISTENT]
- @sign = true
- @want_assertions_signed = true
- end
-
- def add_assertion_consumer_service(url, binding: :http_post)
- @acs_urls.push(location: url, binding: Bindings.binding_for(binding))
- end
-
- def add_single_logout_service(url, binding: :http_post)
- @logout_urls.push(location: url, binding: Bindings.binding_for(binding))
- end
-
- def to_xml
- Signature.sign(sign: sign) do |xml, signature|
- xml.instruct!
- xml.EntityDescriptor entity_descriptor_options do
- signature.template(id)
- xml.SPSSODescriptor descriptor_options do
- if @configuration.signing_certificate_pem.present?
- xml.KeyDescriptor use: "signing" do
- xml.KeyInfo "xmlns": Namespaces::XMLDSIG do
- xml.X509Data do
- xml.X509Certificate @configuration.stripped_signing_certificate
- end
- end
- end
- end
- if @configuration.encryption_certificate_pem.present?
- xml.KeyDescriptor use: "encryption" do
- xml.KeyInfo "xmlns": Namespaces::XMLDSIG do
- xml.X509Data do
- xml.X509Certificate @configuration.stripped_encryption_certificate
- end
- end
- end
- 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
- end
- end
- end
- end
-
- def build
- ServiceProviderMetadata.new(to_xml)
- end
-
- private
-
- def entity_descriptor_options
- {
- 'xmlns': Namespaces::METADATA,
- ID: "_#{id}",
- entityID: entity_id,
- }
- end
-
- def descriptor_options
- {
- AuthnRequestsSigned: sign,
- WantAssertionsSigned: want_assertions_signed,
- protocolSupportEnumeration: Namespaces::PROTOCOL,
- }
- end
- end
end
end
end
spec/saml/builders/service_provider_metadata_spec.rb
@@ -0,0 +1,39 @@
+require 'spec_helper'
+
+RSpec.describe Saml::Kit::ServiceProviderMetadata::Builder do
+ let(:acs_url) { FFaker::Internet.http_url }
+ let(:entity_id) { FFaker::Internet.uri("https") }
+
+ it 'builds the service provider metadata' do
+ subject.entity_id = entity_id
+ subject.add_assertion_consumer_service(acs_url, binding: :http_post)
+ subject.name_id_formats = [
+ Saml::Kit::Namespaces::PERSISTENT,
+ Saml::Kit::Namespaces::TRANSIENT,
+ Saml::Kit::Namespaces::EMAIL_ADDRESS,
+ ]
+ result = Hash.from_xml(subject.build.to_xml)
+
+ expect(result['EntityDescriptor']['xmlns']).to eql("urn:oasis:names:tc:SAML:2.0:metadata")
+ expect(result['EntityDescriptor']['ID']).to be_present
+ expect(result['EntityDescriptor']['entityID']).to eql(entity_id)
+ expect(result['EntityDescriptor']['SPSSODescriptor']['AuthnRequestsSigned']).to eql('true')
+ expect(result['EntityDescriptor']['SPSSODescriptor']['WantAssertionsSigned']).to eql('true')
+ expect(result['EntityDescriptor']['SPSSODescriptor']['protocolSupportEnumeration']).to eql('urn:oasis:names:tc:SAML:2.0:protocol')
+ expect(result['EntityDescriptor']['SPSSODescriptor']['NameIDFormat']).to match_array([
+ Saml::Kit::Namespaces::PERSISTENT,
+ Saml::Kit::Namespaces::TRANSIENT,
+ Saml::Kit::Namespaces::EMAIL_ADDRESS,
+ ])
+ expect(result['EntityDescriptor']['SPSSODescriptor']['AssertionConsumerService']['Binding']).to eql("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST")
+ expect(result['EntityDescriptor']['SPSSODescriptor']['AssertionConsumerService']['Location']).to eql(acs_url)
+ expect(result['EntityDescriptor']['SPSSODescriptor']['AssertionConsumerService']['isDefault']).to eql('true')
+ expect(result['EntityDescriptor']['SPSSODescriptor']['AssertionConsumerService']['index']).to eql('0')
+ expect(result['EntityDescriptor']['Signature']).to be_present
+ expect(result['EntityDescriptor']['SPSSODescriptor']['KeyDescriptor'].map { |x| x['use'] }).to match_array(['signing', 'encryption'])
+ expect(result['EntityDescriptor']['SPSSODescriptor']['KeyDescriptor'].map { |x| x['KeyInfo']['X509Data']['X509Certificate'] }).to match_array([
+ Saml::Kit.configuration.stripped_signing_certificate,
+ Saml::Kit.configuration.stripped_encryption_certificate,
+ ])
+ end
+end
spec/saml/service_provider_metadata_spec.rb
@@ -7,43 +7,6 @@ RSpec.describe Saml::Kit::ServiceProviderMetadata do
let(:logout_post_url) { FFaker::Internet.uri("https") }
let(:logout_redirect_url) { FFaker::Internet.uri("https") }
- describe described_class::Builder do
- let(:acs_url) { FFaker::Internet.http_url }
-
- it 'builds the service provider metadata' do
- subject.entity_id = entity_id
- subject.add_assertion_consumer_service(acs_url, binding: :http_post)
- subject.name_id_formats = [
- Saml::Kit::Namespaces::PERSISTENT,
- Saml::Kit::Namespaces::TRANSIENT,
- Saml::Kit::Namespaces::EMAIL_ADDRESS,
- ]
- result = Hash.from_xml(subject.build.to_xml)
-
- expect(result['EntityDescriptor']['xmlns']).to eql("urn:oasis:names:tc:SAML:2.0:metadata")
- expect(result['EntityDescriptor']['ID']).to be_present
- expect(result['EntityDescriptor']['entityID']).to eql(entity_id)
- expect(result['EntityDescriptor']['SPSSODescriptor']['AuthnRequestsSigned']).to eql('true')
- expect(result['EntityDescriptor']['SPSSODescriptor']['WantAssertionsSigned']).to eql('true')
- expect(result['EntityDescriptor']['SPSSODescriptor']['protocolSupportEnumeration']).to eql('urn:oasis:names:tc:SAML:2.0:protocol')
- expect(result['EntityDescriptor']['SPSSODescriptor']['NameIDFormat']).to match_array([
- Saml::Kit::Namespaces::PERSISTENT,
- Saml::Kit::Namespaces::TRANSIENT,
- Saml::Kit::Namespaces::EMAIL_ADDRESS,
- ])
- expect(result['EntityDescriptor']['SPSSODescriptor']['AssertionConsumerService']['Binding']).to eql("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST")
- expect(result['EntityDescriptor']['SPSSODescriptor']['AssertionConsumerService']['Location']).to eql(acs_url)
- expect(result['EntityDescriptor']['SPSSODescriptor']['AssertionConsumerService']['isDefault']).to eql('true')
- expect(result['EntityDescriptor']['SPSSODescriptor']['AssertionConsumerService']['index']).to eql('0')
- expect(result['EntityDescriptor']['Signature']).to be_present
- expect(result['EntityDescriptor']['SPSSODescriptor']['KeyDescriptor'].map { |x| x['use'] }).to match_array(['signing', 'encryption'])
- expect(result['EntityDescriptor']['SPSSODescriptor']['KeyDescriptor'].map { |x| x['KeyInfo']['X509Data']['X509Certificate'] }).to match_array([
- Saml::Kit.configuration.stripped_signing_certificate,
- Saml::Kit.configuration.stripped_encryption_certificate,
- ])
- end
- end
-
describe described_class do
let(:builder) { described_class::Builder.new }
subject do