main
 1# frozen_string_literal: true
 2
 3module Saml
 4  module Kit
 5    # This class is responsible for validating and
 6    # parsing a SAML Response document.
 7    # {include:file:spec/examples/response_spec.rb}
 8    class Response < Document
 9      include Respondable
10      extend Forwardable
11
12      def_delegators :assertion, :name_id, :[], :attributes
13
14      validate :must_be_valid_assertion
15      validate :must_contain_single_assertion
16
17      def initialize(
18        xml,
19        request_id: nil,
20        configuration: Saml::Kit.configuration
21      )
22        @request_id = request_id
23        super(xml, name: 'Response', configuration: configuration)
24      end
25
26      def assertion(private_keys = configuration.private_keys(use: :encryption))
27        @assertion ||=
28          begin
29            node = assertion_nodes.last
30            if node.nil?
31              Saml::Kit::NullAssertion.new
32            else
33              Saml::Kit::Assertion.new(
34                node,
35                configuration: @configuration,
36                private_keys: private_keys
37              )
38            end
39          end
40      end
41
42      private
43
44      def signature_required_by_type?
45        true
46      end
47
48      # `signed?` and `trusted?` deliberately report only this element's own
49      # signature, but signing only the Assertion is a common identity provider
50      # configuration, and that signature covers the identity being asserted.
51      def signature_covers_document?
52        signed? || assertion.signed?
53      end
54
55      def signature_trusted?
56        super || (assertion.signed? && assertion.trusted?)
57      end
58
59      def must_be_valid_assertion
60        assertion.valid?
61        assertion.each_error do |attribute, error|
62          attribute = :assertion if attribute == :base
63          errors.add(attribute, error) unless errors.added?(attribute, error)
64        end
65      end
66
67      def must_contain_single_assertion
68        return if assertion_nodes.count <= 1
69
70        errors.add(:base, error_message(:must_contain_single_assertion))
71      end
72
73      def assertion_nodes
74        search(Saml::Kit::Assertion::XPATH)
75      end
76    end
77  end
78end