class SAML2::Base

@abstract

Attributes

xml[R]

@return [Nokogiri::XML::Element]

Public Class Methods

from_xml(node) click to toggle source

Create an appropriate object to represent the given XML element.

@param node [Nokogiri::XML::Element, nil] @return [Base, nil]

# File lib/saml2/base.rb, line 18
def from_xml(node)
  return nil unless node

  result = new
  result.from_xml(node)
  result
end
load_object_array(node, element, klass = nil) click to toggle source
# File lib/saml2/base.rb, line 32
def load_object_array(node, element, klass = nil)
  node.xpath(element, Namespaces::ALL).map do |element_node|
    if klass.nil?
      SAML2.const_get(element_node.name, false).from_xml(element_node)
    elsif klass.is_a?(Hash)
      klass[element_node.name].from_xml(element_node)
    else
      klass.from_xml(element_node)
    end
  end
end
load_string_array(node, element) click to toggle source
# File lib/saml2/base.rb, line 26
def load_string_array(node, element)
  node.xpath(element, Namespaces::ALL).map do |element_node|
    element_node.content&.strip
  end
end
lookup_qname(qname, namespaces) click to toggle source
# File lib/saml2/base.rb, line 9
def lookup_qname(qname, namespaces)
  prefix, local_name = split_qname(qname)
  [lookup_namespace(prefix, namespaces), local_name]
end
new() click to toggle source
# File lib/saml2/base.rb, line 64
def initialize
  @pretty = true
end

Private Class Methods

lookup_namespace(prefix, namespaces) click to toggle source
# File lib/saml2/base.rb, line 54
def lookup_namespace(prefix, namespaces)
  return nil if namespaces.empty?

  namespaces[prefix.empty? ? "xmlns" : "xmlns:#{prefix}"]
end
split_qname(qname) click to toggle source
# File lib/saml2/base.rb, line 46
def split_qname(qname)
  if qname.include?(":")
    qname.split(":", 2)
  else
    [nil, qname]
  end
end

Public Instance Methods

build(builder) click to toggle source

Serialize this object to XML, as part of a larger document

@param builder [Nokogiri::XML::Builder] The builder helper object to serialize to. @return [void]

# File lib/saml2/base.rb, line 135
def build(builder); end
decrypt(keys = nil) { |allowed_certs| ... } click to toggle source

Decrypt (in-place) encrypted portions of this object

Either the keys parameter, or a block that returns key(s), should be provided.

@param keys optional [Array<OpenSSL::PKey, String>, OpenSSL::PKey, String, nil] @yield Optional block to fetch the necessary keys, given information

contained in the encrypted elements of which certificates it was
encrypted for.

@yieldparam allowed_certs [Array<OpenSSL::X509::Certificate, Hash, String, nil>]

An array of certificates describing who the node was encrypted for.
Identified by an X.509 Certificate, a hash with +:issuer+ and +:serial+
keys, or a string of SubjectName.

@yieldreturn [Array<OpenSSL::PKey, String>, OpenSSL::PKey, String, nil] @return [Boolean] If any nodes were present.

# File lib/saml2/base.rb, line 152
def decrypt(keys = nil)
  encrypted_nodes = self.encrypted_nodes
  encrypted_nodes.each do |node|
    this_nodes_keys = keys
    if keys.nil?
      allowed_certs = node.xpath("dsig:KeyInfo/xenc:EncryptedKey/dsig:KeyInfo/dsig:X509Data",
                                 SAML2::Namespaces::ALL).map do |x509data|
        if (cert = x509data.at_xpath("dsig:X509Certificate", SAML2::Namespaces::ALL)&.content&.strip)
          OpenSSL::X509::Certificate.new(Base64.decode64(cert))
        elsif (issuer_serial = x509data.at_xpath("dsig:X509IssuerSerial", SAML2::Namespaces::ALL))
          {
            issuer: issuer_serial.at_xpath("dsig:X509IssuerName", SAML2::Namespaces::ALL).content.strip,
            serial: issuer_serial.at_xpath("dsig:X509SerialNumber", SAML2::Namespaces::ALL).content.strip.to_i
          }
        elsif (subject_name = x509data.at_xpath("dsig:X509SubjectName", SAML2::Namespaces::ALL)&.content&.strip)
          subject_name
        end
      end
      this_nodes_keys = yield allowed_certs
    end
    this_nodes_keys = Array(this_nodes_keys)
    raise ArgumentError("no decryption key provided or found") if this_nodes_keys.empty?

    old_node = node.parent
    this_nodes_keys.each_with_index do |key, i|
      old_node.replace(node.decrypt_with(key: key))
    rescue XMLSec::DecryptionError
      # swallow errors on all but the last key
      raise if i - 1 == this_nodes_keys.length
    end
  end
  !encrypted_nodes.empty?
end
from_xml(node) click to toggle source

Parse an XML element into this object.

@param node [Nokogiri::XML::Element] @return [void]

# File lib/saml2/base.rb, line 72
def from_xml(node)
  @xml = node
end
inspect() click to toggle source

Inspect the object

The +@xml+ instance variable is omitted, keeping this useful. However, if an object lazily parses sub-objects, then their instance variables will not be created until their attribute is accessed. @return [String]

# File lib/saml2/base.rb, line 108
def inspect
  "#<#{self.class.name} #{instance_variables.filter_map do |iv|
                            next if iv == :@xml

                            "#{iv}=#{instance_variable_get(iv).inspect}"
                          end.join(", ")}>"
end
to_s(pretty: nil) click to toggle source

Returns the XML of this object as a string.

@param pretty optional [true, false, nil]

+true+ forces it to format it for easy reading. +nil+ will prefer to
format it pretty, but won't if e.g. it has been signed, and pretty
formatting would break the signature. If this object came from parsing
XML, it will default to exactly what it was parsed as.

@return [String]

# File lib/saml2/base.rb, line 84
def to_s(pretty: nil)
  pretty = @pretty if pretty.nil?
  if xml
    if pretty
      xml.to_s
    else
      xml.to_xml(save_with: Nokogiri::XML::Node::SaveOptions::AS_XML |
                            Nokogiri::XML::Node::SaveOptions::NO_DECLARATION)
    end
  elsif pretty
    to_xml.to_s
  else
    # make sure to not FORMAT it - it breaks signatures!
    to_xml.to_xml(save_with: Nokogiri::XML::Node::SaveOptions::AS_XML |
                             Nokogiri::XML::Node::SaveOptions::NO_DECLARATION)
  end
end
to_xml() click to toggle source

Serialize this object to XML

@return [Nokogiri::XML::Document]

# File lib/saml2/base.rb, line 119
def to_xml
  unless instance_variable_defined?(:@document)
    builder = Nokogiri::XML::Builder.new
    build(builder)
    @document = builder.doc
    # if we're re-serializing a parsed document (i.e. after mutating/parsing it),
    # forget the original document we parsed
    @xml = nil
  end
  @document
end

Private Instance Methods

encrypted_nodes() click to toggle source
# File lib/saml2/base.rb, line 196
def encrypted_nodes
  xml.xpath("//xenc:EncryptedData", Namespaces::ALL)
end
load_object_array(node, element, klass = nil) click to toggle source
# File lib/saml2/base.rb, line 192
def load_object_array(node, element, klass = nil)
  self.class.load_object_array(node, element, klass)
end
load_string_array(node, element) click to toggle source
# File lib/saml2/base.rb, line 188
def load_string_array(node, element)
  self.class.load_string_array(node, element)
end