main
1# frozen_string_literal: true
2
3module Saml
4 module Kit
5 module Bindings
6 # This class is responsible for
7 # generating a url as per the
8 # rules for the HTTP redirect binding
9 # specification.
10 # https://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf
11 # {include:file:spec/saml/kit/bindings/url_builder_spec.rb}
12 class UrlBuilder
13 include Serializable
14 attr_reader :configuration
15
16 def initialize(configuration: Saml::Kit.configuration)
17 @configuration = configuration
18 end
19
20 def build(document, relay_state: nil)
21 destination = document.destination
22 if configuration.sign?
23 payload = canonicalize(document, relay_state)
24 "#{destination}?#{payload}&Signature=#{signature_for(payload)}"
25 else
26 "#{destination}?" + to_query_string(
27 document.query_string_parameter => serialize(document.to_xml),
28 'RelayState' => relay_state
29 )
30 end
31 end
32
33 private
34
35 def signature_for(payload)
36 private_key = configuration.private_keys(use: :signing).last
37 encode(private_key.sign(OpenSSL::Digest::SHA256.new, payload))
38 end
39
40 def canonicalize(saml_document, relay_state)
41 xml = saml_document.to_xml
42 to_query_string(
43 saml_document.query_string_parameter => serialize(xml),
44 'RelayState' => relay_state,
45 'SigAlg' => ::Xml::Kit::Namespaces::SHA256
46 )
47 end
48
49 def to_query_string(query_params)
50 query_params.map do |(key, value)|
51 value.present? ? "#{key}=#{escape(value)}" : nil
52 end.compact.join('&')
53 end
54
55 def serialize(value)
56 encode(deflate(value))
57 end
58 end
59 end
60 end
61end