main
 1# frozen_string_literal: true
 2
 3module Saml
 4  module Kit
 5    module Builders
 6      # This class is responsible for building a SAML Assertion
 7      # {include:file:lib/saml/kit/builders/templates/assertion.builder}
 8      class Assertion
 9        include XmlTemplatable
10
11        attr_reader :user, :request, :configuration
12        attr_accessor :reference_id
13        attr_accessor :now, :destination
14        attr_accessor :issuer, :version
15        attr_accessor :default_name_id_format
16
17        def initialize(user, request, configuration: Saml::Kit.configuration)
18          @user = user
19          @request = request
20          @configuration = configuration
21          @issuer = configuration.entity_id
22          @reference_id = ::Xml::Kit::Id.generate
23          @version = '2.0'
24          @now = Time.now.utc
25          self.default_name_id_format = Saml::Kit::Namespaces::UNSPECIFIED_NAMEID
26        end
27
28        def name_id_format
29          request.try(:name_id_format)
30        end
31
32        def name_id
33          user.name_id_for(name_id_format)
34        end
35
36        def assertion_attributes
37          return {} unless user.respond_to?(:assertion_attributes_for)
38
39          user.assertion_attributes_for(request)
40        end
41
42        def build
43          Saml::Kit::Assertion.new(to_xml, configuration: configuration)
44        end
45
46        private
47
48        def assertion_options
49          {
50            ID: reference_id,
51            IssueInstant: now.iso8601,
52            Version: version,
53            xmlns: Namespaces::ASSERTION,
54          }
55        end
56
57        def subject_confirmation_data_options
58          options = {}
59          options[:InResponseTo] = request.id if request.present?
60          options[:Recipient] = destination if destination.present?
61          options[:NotOnOrAfter] = (now + 5.minutes).utc.iso8601
62          options
63        end
64
65        def conditions_options
66          {
67            NotBefore: now.utc.iso8601,
68            NotOnOrAfter: not_on_or_after.iso8601,
69          }
70        end
71
72        def authn_statement_options
73          {
74            AuthnInstant: now.iso8601,
75            SessionIndex: reference_id,
76          }
77        end
78
79        def name_id_options
80          { Format: name_id_format || default_name_id_format }
81        end
82
83        def not_on_or_after
84          configuration.session_timeout.since(now).utc
85        end
86      end
87    end
88  end
89end