Commit 8030342
Changed files (4)
lib
saml
spec
lib/saml/kit/document.rb
@@ -60,7 +60,7 @@ module Saml
#
# @param pretty [Boolean] formats the xml or returns the raw xml.
def to_xml(pretty: false)
- pretty ? Nokogiri::XML(content).to_xml(indent: 2) : content
+ pretty ? to_nokogiri.to_xml(indent: 2) : content
end
# Returns the SAML document as an XHTML string.
@@ -69,6 +69,10 @@ module Saml
Nokogiri::XML(content, &:noblanks).to_xhtml
end
+ def to_nokogiri
+ @nokogiri ||= Nokogiri::XML(content)
+ end
+
def to_s
to_xml
end
lib/saml/kit/signature.rb
@@ -3,15 +3,21 @@ module Saml
class Signature
include ActiveModel::Validations
+ validate :validate_signature
+ #validate :validate_certificate
+
def initialize(xml_hash)
- @xml_hash = xml_hash
+ if xml_hash.is_a?(Hash)
+ @xml_hash = xml_hash
+ else
+ @document = xml_hash
+ end
end
# Returns the embedded X509 Certificate
def certificate
- value = to_h.fetch('KeyInfo', {}).fetch('X509Data', {}).fetch('X509Certificate', nil)
- return if value.nil?
- ::Xml::Kit::Certificate.new(value, use: :signing)
+ item = @document.at_xpath("//ds:KeyInfo/ds:X509Data/ds:X509Certificate", "ds": ::Xml::Kit::Namespaces::XMLDSIG)
+ ::Xml::Kit::Certificate.new(item.text, use: :signing)
end
# Returns true when the fingerprint of the certificate matches one of the certificates registered in the metadata.
@@ -24,6 +30,22 @@ module Saml
def to_h
@xml_hash
end
+
+ private
+
+ def validate_signature
+ signature = Xmldsig::Signature.new(@document, 'ID=$uri or @Id')
+ unless signature.valid?(certificate.x509)
+ signature.errors.each { |error| errors.add(error, "is invalid") }
+ end
+ end
+
+ def validate_certificate(now = Time.current)
+ if certificate.expired?(now)
+ error_message = "Not valid before #{certificate.not_before}. Not valid after #{certificate.not_after}."
+ errors.add(:certificate, error_message)
+ end
+ end
end
end
end
lib/saml/kit/trustable.rb
@@ -17,9 +17,10 @@ module Saml
# @!visibility private
def signature
xml_hash = to_h.fetch(name, {}).fetch('Signature', nil)
- xml_hash ? Signature.new(xml_hash) : nil
+ xml_hash ? Signature.new(to_nokogiri.at_xpath('//ds:Signature', 'ds' => Xml::Kit::Namespaces::XMLDSIG)) : nil
end
+
# Returns true when documents is signed and the signing certificate belongs to a known service entity.
def trusted?
return true if signature_manually_verified
spec/saml/authentication_request_spec.rb
@@ -228,4 +228,16 @@ RSpec.describe Saml::Kit::AuthenticationRequest do
expect(response).to be_instance_of(Saml::Kit::Response)
end
end
+
+ describe "#signature.valid?" do
+ it 'returns true when the signature is valid' do
+ expect(subject.signature).to be_valid
+ end
+
+ it 'returns false when the signature does not match' do
+ raw_xml.gsub!(issuer, 'corrupt')
+ subject = described_class.new(raw_xml, configuration: configuration)
+ expect(subject.signature).to_not be_valid
+ end
+ end
end