blob: 17df454a5bff7f1421f66c0316f008a07856b978 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
# frozen_string_literal: true
module Xml
module Kit
# {include:file:spec/xml/kit/document_spec.rb}
class Document
include ActiveModel::Validations
NAMESPACES = { "ds": ::Xml::Kit::Namespaces::XMLDSIG }.freeze
validate :validate_signatures
validate :validate_certificates
def initialize(raw_xml, namespaces: NAMESPACES)
@raw_xml = raw_xml
@namespaces = namespaces
@document = ::Nokogiri::XML(raw_xml)
end
# Returns the first XML node found by searching the document with the provided XPath.
#
# @param xpath [String] the XPath to use to search the document
def find_by(xpath)
document.at_xpath(xpath, namespaces)
end
# Returns all XML nodes found by searching the document with the provided XPath.
#
# @param xpath [String] the XPath to use to search the document
def find_all(xpath)
document.search(xpath, namespaces)
end
# Return the XML document as a [String].
#
# @param pretty [Boolean] return the XML string in a human readable format if true.
def to_xml(pretty: true)
pretty ? document.to_xml(indent: 2) : raw_xml
end
private
attr_reader :raw_xml, :document, :namespaces
def validate_signatures
invalid_signatures.flat_map(&:errors).uniq.each do |error|
errors.add(error, 'is invalid')
end
end
def invalid_signatures(id_attr: 'ID=$uri or @Id')
Xmldsig::SignedDocument
.new(document, id_attr: id_attr)
.signatures.find_all do |signature|
x509_certificates.all? do |certificate|
!signature.valid?(certificate)
end
end
end
def validate_certificates(now = Time.current)
return if find_by('//ds:Signature').nil?
x509_certificates.each do |certificate|
errors.add(:certificate, "Not valid before #{certificate.not_before}") if now < certificate.not_before
errors.add(:certificate, "Not valid after #{certificate.not_after}") if now > certificate.not_after
end
end
def x509_certificates
find_all('//ds:KeyInfo/ds:X509Data/ds:X509Certificate').map do |item|
Certificate.to_x509(item.text)
end
end
end
end
end
|