Commit 56d026d
Changed files (5)
lib
spec
saml
lib/saml/kit/default_registry.rb
@@ -6,6 +6,7 @@ module Saml
end
def register(metadata)
+ Saml::Kit.logger.debug(metadata.to_xml(pretty: true))
@items[metadata.entity_id] = metadata
end
lib/saml/kit/document.rb
@@ -48,8 +48,8 @@ module Saml
@xml_hash
end
- def to_xml
- content
+ def to_xml(pretty: false)
+ pretty ? Nokogiri::XML(content).to_xml(indent: 2) : content
end
def to_s
lib/saml/kit/metadata.rb
@@ -78,6 +78,7 @@ module Saml
if :signing == use.to_sym
hash_value = fingerprint.algorithm(hash_algorithm)
signing_certificates.find do |signing_certificate|
+ Saml::Kit.logger.debug [hash_value, signing_certificate[:fingerprint]].inspect
hash_value == signing_certificate[:fingerprint]
end
end
@@ -87,8 +88,8 @@ module Saml
@xml_hash ||= Hash.from_xml(to_xml)
end
- def to_xml
- @xml
+ def to_xml(pretty: false)
+ pretty ? Nokogiri::XML(@xml).to_xml(indent: 2) : @xml
end
def to_s
lib/saml/kit/response.rb
@@ -42,6 +42,14 @@ module Saml
Time.current > started_at && !expired?
end
+ def signed?
+ super || to_h.fetch(name, {}).fetch('Assertion', {}).fetch('Signature', nil).present?
+ end
+
+ def certificate
+ super || to_h.fetch(name, {}).fetch('Assertion', {}).fetch('Signature', {}).fetch('KeyInfo', {}).fetch('X509Data', {}).fetch('X509Certificate', nil)
+ end
+
private
def must_be_active_session
@@ -77,7 +85,7 @@ module Saml
attr_reader :user, :request
attr_accessor :id, :reference_id, :now
attr_accessor :version, :status_code
- attr_accessor :issuer, :sign, :destination
+ attr_accessor :issuer, :sign, :destination, :encrypt
def initialize(user, request)
@user = user
@@ -90,6 +98,7 @@ module Saml
@issuer = configuration.issuer
@destination = destination_for(request)
@sign = want_assertions_signed
+ @encrypt = false
end
def want_assertions_signed
spec/saml/response_spec.rb
@@ -242,6 +242,167 @@ RSpec.describe Saml::Kit::Response do
end
end
+ describe "#signed?" do
+ let(:now) { Time.now.utc }
+ let(:id) { SecureRandom.uuid }
+ let(:url) { FFaker::Internet.uri("https") }
+
+ it 'returns true when the Assertion is signed' do
+ xml = <<-XML
+<?xml version="1.0"?>
+<samlp:Response xmlns:samlp="#{Saml::Kit::Namespaces::PROTOCOL}" ID="_#{id}" Version="2.0" IssueInstant="#{now.iso8601}" Destination="#{url}" Consent="urn:oasis:names:tc:SAML:2.0:consent:unspecified" InResponseTo="_#{SecureRandom.uuid}">
+ <Assertion xmlns="#{Saml::Kit::Namespaces::ASSERTION}" ID="_#{id}" IssueInstant="#{now.iso8601}" Version="2.0">
+ <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+ <ds:SignedInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+ <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+ <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
+ <ds:Reference URI="#_#{id}">
+ <ds:Transforms>
+ <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
+ <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+ </ds:Transforms>
+ <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
+ <ds:DigestValue></ds:DigestValue>
+ </ds:Reference>
+ </ds:SignedInfo>
+ <ds:SignatureValue></ds:SignatureValue>
+ <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
+ <ds:X509Data>
+ <ds:X509Certificate></ds:X509Certificate>
+ </ds:X509Data>
+ </KeyInfo>
+ </ds:Signature>
+ </Assertion>
+</samlp:Response>
+ XML
+ subject = described_class.new(xml)
+ expect(subject).to be_signed
+ end
+
+ it 'returns true when the Response is signed' do
+ xml = <<-XML
+<?xml version="1.0"?>
+<samlp:Response xmlns:samlp="#{Saml::Kit::Namespaces::PROTOCOL}" ID="_#{id}" Version="2.0" IssueInstant="#{now.iso8601}" Destination="#{url}" Consent="urn:oasis:names:tc:SAML:2.0:consent:unspecified" InResponseTo="_#{SecureRandom.uuid}">
+ <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+ <ds:SignedInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+ <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+ <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
+ <ds:Reference URI="#_#{id}">
+ <ds:Transforms>
+ <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
+ <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+ </ds:Transforms>
+ <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
+ <ds:DigestValue></ds:DigestValue>
+ </ds:Reference>
+ </ds:SignedInfo>
+ <ds:SignatureValue></ds:SignatureValue>
+ <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
+ <ds:X509Data>
+ <ds:X509Certificate></ds:X509Certificate>
+ </ds:X509Data>
+ </KeyInfo>
+ </ds:Signature>
+ <Assertion xmlns="#{Saml::Kit::Namespaces::ASSERTION}" ID="_#{id}" IssueInstant="#{now.iso8601}" Version="2.0"></Assertion>
+</samlp:Response>
+ XML
+ subject = described_class.new(xml)
+ expect(subject).to be_signed
+ end
+
+ it 'returns false when there is no signature' do
+ xml = <<-XML
+<?xml version="1.0"?>
+<samlp:Response xmlns:samlp="#{Saml::Kit::Namespaces::PROTOCOL}" ID="_#{id}" Version="2.0" IssueInstant="#{now.iso8601}" Destination="#{url}" Consent="urn:oasis:names:tc:SAML:2.0:consent:unspecified" InResponseTo="_#{SecureRandom.uuid}">
+ <Assertion xmlns="#{Saml::Kit::Namespaces::ASSERTION}" ID="_#{id}" IssueInstant="#{now.iso8601}" Version="2.0"></Assertion>
+</samlp:Response>
+ XML
+ subject = described_class.new(xml)
+ expect(subject).to_not be_signed
+ end
+ end
+
+ describe "#certificate" do
+ let(:now) { Time.now.utc }
+ let(:id) { SecureRandom.uuid }
+ let(:url) { FFaker::Internet.uri("https") }
+ let(:certificate) { FFaker::Movie.title }
+
+ it 'returns the certificate when the Assertion is signed' do
+ xml = <<-XML
+<?xml version="1.0"?>
+<samlp:Response xmlns:samlp="#{Saml::Kit::Namespaces::PROTOCOL}" ID="_#{id}" Version="2.0" IssueInstant="#{now.iso8601}" Destination="#{url}" Consent="urn:oasis:names:tc:SAML:2.0:consent:unspecified" InResponseTo="_#{SecureRandom.uuid}">
+ <Assertion xmlns="#{Saml::Kit::Namespaces::ASSERTION}" ID="_#{id}" IssueInstant="#{now.iso8601}" Version="2.0">
+ <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+ <ds:SignedInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+ <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+ <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
+ <ds:Reference URI="#_#{id}">
+ <ds:Transforms>
+ <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
+ <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+ </ds:Transforms>
+ <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
+ <ds:DigestValue></ds:DigestValue>
+ </ds:Reference>
+ </ds:SignedInfo>
+ <ds:SignatureValue></ds:SignatureValue>
+ <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
+ <ds:X509Data>
+ <ds:X509Certificate>#{certificate}</ds:X509Certificate>
+ </ds:X509Data>
+ </KeyInfo>
+ </ds:Signature>
+ </Assertion>
+</samlp:Response>
+ XML
+ subject = described_class.new(xml)
+ expect(subject.certificate).to eql(certificate)
+ end
+
+ it 'returns the certificate when the Response is signed' do
+ xml = <<-XML
+<?xml version="1.0"?>
+<samlp:Response xmlns:samlp="#{Saml::Kit::Namespaces::PROTOCOL}" ID="_#{id}" Version="2.0" IssueInstant="#{now.iso8601}" Destination="#{url}" Consent="urn:oasis:names:tc:SAML:2.0:consent:unspecified" InResponseTo="_#{SecureRandom.uuid}">
+ <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+ <ds:SignedInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+ <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+ <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
+ <ds:Reference URI="#_#{id}">
+ <ds:Transforms>
+ <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
+ <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+ </ds:Transforms>
+ <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
+ <ds:DigestValue></ds:DigestValue>
+ </ds:Reference>
+ </ds:SignedInfo>
+ <ds:SignatureValue></ds:SignatureValue>
+ <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
+ <ds:X509Data>
+ <ds:X509Certificate>#{certificate}</ds:X509Certificate>
+ </ds:X509Data>
+ </KeyInfo>
+ </ds:Signature>
+ <Assertion xmlns="#{Saml::Kit::Namespaces::ASSERTION}" ID="_#{id}" IssueInstant="#{now.iso8601}" Version="2.0"></Assertion>
+</samlp:Response>
+ XML
+ subject = described_class.new(xml)
+ expect(subject.certificate).to eql(certificate)
+ end
+
+ it 'returns nil when there is no signature' do
+ xml = <<-XML
+<?xml version="1.0"?>
+<samlp:Response xmlns:samlp="#{Saml::Kit::Namespaces::PROTOCOL}" ID="_#{id}" Version="2.0" IssueInstant="#{now.iso8601}" Destination="#{url}" Consent="urn:oasis:names:tc:SAML:2.0:consent:unspecified" InResponseTo="_#{SecureRandom.uuid}">
+ <Assertion xmlns="#{Saml::Kit::Namespaces::ASSERTION}" ID="_#{id}" IssueInstant="#{now.iso8601}" Version="2.0"></Assertion>
+</samlp:Response>
+ XML
+ subject = described_class.new(xml)
+ expect(subject.certificate).to be_nil
+ end
+ end
+
describe described_class::Builder do
subject { described_class.new(user, request) }
let(:user) { double(:user, name_id_for: SecureRandom.uuid, assertion_attributes_for: []) }
@@ -252,5 +413,13 @@ RSpec.describe Saml::Kit::Response do
expect(subject.build.request_id).to eql(request.id)
end
end
+
+ describe "#to_xml" do
+ xit 'generates an EncryptedAssertion' do
+ subject.encrypt = true
+ result = Hash.from_xml(subject.to_xml)
+ expect(result['Response']['EncryptedAssertion']).to be_present
+ end
+ end
end
end