Class: Saml::Util

Inherits:
Object
  • Object
show all
Defined in:
lib/saml/util.rb

Class Method Summary collapse

Class Method Details

.collect_extra_namespaces(raw_xml) ⇒ Object



128
129
130
131
# File 'lib/saml/util.rb', line 128

def collect_extra_namespaces(raw_xml)
  doc = Nokogiri::XML(raw_xml, nil, nil, Nokogiri::XML::ParseOptions::STRICT)
  doc.collect_namespaces.each_with_object({}) { |(prefix, path), hash| hash[prefix.gsub('xmlns:', '')] = path }
end

.decrypt_assertion(encrypted_assertion, private_key) ⇒ Object



89
90
91
92
93
94
95
# File 'lib/saml/util.rb', line 89

def decrypt_assertion(encrypted_assertion, private_key)
  encrypted_assertion_xml = encrypted_assertion.is_a?(Saml::Elements::EncryptedAssertion) ?
      encrypted_assertion.to_xml : encrypted_assertion.to_s
  encrypted_document      = Xmlenc::EncryptedDocument.new(encrypted_assertion_xml)

  Saml::Assertion.parse(encrypted_document.decrypt(private_key), single: true)
end

.decrypt_encrypted_id(encrypted_id, private_key, fail_silent = false) ⇒ Object



107
108
109
110
111
112
# File 'lib/saml/util.rb', line 107

def decrypt_encrypted_id(encrypted_id, private_key, fail_silent = false)
  encrypted_id_xml   = encrypted_id.is_a?(Saml::Elements::EncryptedID) ?
      encrypted_id.to_xml : encrypted_id.to_s
  encrypted_document = Xmlenc::EncryptedDocument.new(encrypted_id_xml)
  Saml::Elements::EncryptedID.parse(encrypted_document.decrypt(private_key, fail_silent))
end

.download_metadata_xml(location) ⇒ Object



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/saml/util.rb', line 133

def (location)
  uri = URI.parse(location)

  http             = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl     = uri.scheme == 'https'
  http.verify_mode = OpenSSL::SSL::VERIFY_PEER

  add_cacert_file(http)

  request = Net::HTTP::Get.new(uri.request_uri)

  response = http.request(request)
  if response.code == '200'
    response.body
  else
    fail Saml::Errors::MetadataDownloadFailed, "Cannot download metadata for: #{location}: #{response.body}"
  end
rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, Net::HTTPBadResponse,
    Net::HTTPHeaderSyntaxError, Net::ProtocolError => error
  raise Saml::Errors::MetadataDownloadFailed, "Cannot download metadata for: #{location}: #{error.message}"
end

.encrypt_assertion(assertion, key_descriptor_or_certificate) ⇒ Object



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/saml/util.rb', line 63

def encrypt_assertion(assertion, key_descriptor_or_certificate)
  case key_descriptor_or_certificate
  when OpenSSL::X509::Certificate
    certificate = key_descriptor_or_certificate
    key_name    = nil
  when Saml::Elements::KeyDescriptor
    certificate = key_descriptor_or_certificate.certificate
    key_name    = key_descriptor_or_certificate.key_info.key_name
  else
    fail ArgumentError, "Expecting Certificate or KeyDescriptor got: #{key_descriptor_or_certificate.class}"
  end

  assertion = assertion.to_xml(nil, nil, false) if assertion.is_a?(Assertion) # create xml without instruct

  encrypted_data = Xmlenc::Builder::EncryptedData.new
  encrypted_data.set_encryption_method(algorithm: 'http://www.w3.org/2001/04/xmlenc#aes128-cbc')

  encrypted_key = encrypted_data.encrypt(assertion.to_s)
  encrypted_key.set_encryption_method(algorithm:               'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p',
                                      digest_method_algorithm: 'http://www.w3.org/2000/09/xmldsig#sha1')
  encrypted_key.set_key_name(key_name)
  encrypted_key.encrypt(certificate.public_key)

  Saml::Elements::EncryptedAssertion.new(encrypted_data: encrypted_data, encrypted_keys: encrypted_key)
end

.encrypt_encrypted_id(encrypted_id, key_descriptor, key_options = {}) ⇒ Object



102
103
104
105
# File 'lib/saml/util.rb', line 102

def encrypt_encrypted_id(encrypted_id, key_descriptor, key_options = {})
  encrypted_id.encrypt(key_descriptor, key_options)
  encrypted_id
end

.encrypt_name_id(name_id, key_descriptor, key_options = {}) ⇒ Object



97
98
99
100
# File 'lib/saml/util.rb', line 97

def encrypt_name_id(name_id, key_descriptor, key_options = {})
  encrypted_id = Saml::Elements::EncryptedID.new(name_id: name_id)
  encrypt_encrypted_id(encrypted_id, key_descriptor, key_options)
end

.parse_params(url) ⇒ Object



4
5
6
7
8
9
10
11
12
13
14
15
# File 'lib/saml/util.rb', line 4

def parse_params(url)
  query = URI.parse(url).query
  return {} unless query

  params = {}
  query.split(/[&;]/).each do |pairs|
    key, value  = pairs.split('=', 2)
    params[key] = value
  end

  params
end

.post(location, message, additional_headers = {}, proxy = {}) ⇒ Object



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/saml/util.rb', line 17

def post(location, message, additional_headers = {}, proxy = {})
  uri = URI.parse(location)
  default_proxy_settings = { addr: :ENV, port: nil, user: nil, pass: nil }
  proxy = default_proxy_settings.merge(proxy)

  http             = Net::HTTP.new(uri.host, uri.port, proxy[:addr], proxy[:port], proxy[:user], proxy[:pass])
  http.use_ssl     = uri.scheme == 'https'
  http.verify_mode = OpenSSL::SSL::VERIFY_PEER

  add_cacert_file(http)
  add_ssl_certificate_and_key(http)

  request      = Net::HTTP::Post.new(uri.request_uri, merged_headers(additional_headers))
  request.body = message

  http.request(request)
end

.sign_xml(message, format = :xml, include_nested_prefixlist = false, &block) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/saml/util.rb', line 35

def sign_xml(message, format = :xml, include_nested_prefixlist = false, &block)
  message.add_signature

  document = Xmldsig::SignedDocument.new(message.send("to_#{format}"))

  if Saml::Config.include_nested_prefixlist || include_nested_prefixlist
    document.signatures.reverse.each_with_object([]) do |signature, nested_prefixlist|
      inclusive_namespaces = signature.signature.at_xpath('descendant::ec:InclusiveNamespaces', Xmldsig::NAMESPACES)

      if inclusive_namespaces
        nested_prefixlist.concat(inclusive_namespaces.get_attribute('PrefixList').to_s.split(' '))

        if signature.unsigned?
          inclusive_namespaces.set_attribute('PrefixList', nested_prefixlist.uniq.join(' '))
        end
      end
    end
  end

  if block_given?
    document.sign(&block)
  else
    document.sign do |data, signature_algorithm|
      message.provider.sign(signature_algorithm, data)
    end
  end
end

.verify_xml(message, raw_body) ⇒ Object



114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/saml/util.rb', line 114

def verify_xml(message, raw_body)
  document = Xmldsig::SignedDocument.new(raw_body)

  signature_valid = document.validate do |signature, data, signature_algorithm|
    message.provider.verify(signature_algorithm, signature, data, message.signature.key_name)
  end

  fail Saml::Errors::SignatureInvalid unless signature_valid

  signed_node = document.signed_nodes.find { |node| node['ID'] == message._id }

  message.class.parse(signed_node.canonicalize, single: true)
end