class SAML2::Message
In the SAML Schema, Request
and Response
don’t technically share a common ancestor, but they have several things in common so it’s useful to represent that in this gem as a common base class. @abstract
Attributes
Public Class Methods
Create an appropriate {Message} subclass instance to represent the given XML element.
When called on a subclass, it behaves the same as {Base.from_xml}
@param node [Nokogiri::XML::Element] @return [Message] @raise [UnknownMessage] If the element doesn’t correspond to a known
SAML message type.
SAML2::Base::from_xml
# File lib/saml2/message.rb, line 60 def from_xml(node) return super unless self == Message klass = Message.known_messages[node.name] raise UnknownMessage, "Unknown message #{node.name}" unless klass klass.from_xml(node) end
SAML2::Base::new
# File lib/saml2/message.rb, line 101 def initialize super @errors = [] @id = "_#{SecureRandom.uuid}" @issue_instant = Time.now.utc end
Parses XML, and returns an appropriate {Message} subclass instance.
@param xml [String, IO] Anything that can be passed to Nokogiri::XML
. @return [Message] @raise [UnexpectedMessage]
If called on a subclass, will raise if the parsed message does not match the class is was called on.
# File lib/saml2/message.rb, line 76 def parse(xml) result = Message.from_xml(Nokogiri::XML(xml, &:strict).root) unless self == Message || result.instance_of?(self) raise UnexpectedMessage, "Expected a #{name}, but got a #{result.class.name}" end result rescue Nokogiri::XML::SyntaxError raise CorruptMessage end
Protected Class Methods
# File lib/saml2/message.rb, line 94 def inherited(klass) super # explicitly keep track of all messages in this base class Message.known_messages[klass.name.sub(/^SAML2::/, "")] = klass end
# File lib/saml2/message.rb, line 90 def known_messages @known_messages ||= {} end
Public Instance Methods
@return [String, nil]
# File lib/saml2/message.rb, line 151 def destination @destination = xml["Destination"] if xml && !instance_variable_defined?(:@destination) @destination end
(see Base#from_xml
)
SAML2::Base::from_xml
# File lib/saml2/message.rb, line 109 def from_xml(node) super @id = nil @issue_instant = nil end
@return [String]
# File lib/saml2/message.rb, line 141 def id @id ||= xml["ID"] end
@return [Time]
# File lib/saml2/message.rb, line 146 def issue_instant @issue_instant ||= Time.parse(xml["IssueInstant"]) end
@return [NameID, nil]
# File lib/saml2/message.rb, line 157 def issuer @issuer ||= NameID.from_xml(xml.at_xpath("saml:Issuer", Namespaces::ALL)) end
(see Signable#sign
)
SAML2::Signable#sign
# File lib/saml2/message.rb, line 129 def sign(x509_certificate, private_key, algorithm_name = :sha256) super xml = @document.root # the Signature element must be right after the Issuer, so put it there issuer = xml.at_xpath("saml:Issuer", Namespaces::ALL) signature = xml.at_xpath("dsig:Signature", Namespaces::ALL) issuer.add_next_sibling(signature) self end
If the XML is valid according to SAML XSDs. @return [Boolean]
# File lib/saml2/message.rb, line 122 def valid_schema? return false unless Schemas.protocol.valid?(xml.document) true end
# File lib/saml2/message.rb, line 115 def validate @errors = Schemas.protocol.validate(xml.document) errors end
Protected Instance Methods
should be called from inside the specific request element
# File lib/saml2/message.rb, line 164 def build(message) message.parent["ID"] = id message.parent["Version"] = "2.0" message.parent["IssueInstant"] = issue_instant.iso8601 message.parent["Destination"] = destination if destination issuer&.build(message, element: "Issuer") end