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: sso.rb
    path of file: ./repos/rbvmomi/lib/rbvmomi
    
    the code of the file until where you have to start completion: # frozen_string_literal: true
require 'base64'
require 'net/https'
require 'nokogiri'
require 'openssl'
require 'securerandom'
require 'time'

module RbVmomi
  # Provides access to vCenter Single Sign-On
  class SSO
    BST_PROFILE = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3'.freeze
    C14N_CLASS = Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
    C14N_METHOD = 'http://www.w3.org/2001/10/xml-exc-c14n#'.freeze
    DIGEST_METHOD = 'http://www.w3.org/2001/04
    
    def sign_request(request)
      raise('Need SAML2 assertion') unless @assertion
      raise('No SAML2 assertion ID') unless @assertion_id

      request_id = generate_id
      timestamp_id = generate_id

      request = request.is_a?(String) ? Nokogiri::XML(request) : request
      builder = Nokogiri::XML::Builder.new do |xml|
        xml[:soap].Header(Hash[NAMESPACES.map { |ns, uri| ["xmlns:#{ns}", uri] }]) do
          xml[:wsse].Security do
            wsu_timestamp(xml, timestamp_id)
            ds_signature(xml, request_id, timestamp_id) do |x|
              x[:wsse].SecurityTokenReference('wsse11:TokenType' => TOKEN_PROFILE) do
                x[:wsse].KeyIdentifier(
                  @assertion_id,
                  'ValueType' => 'http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLID'
                )
              end
            end
          end
        end
      end

      # To avoid Nokogiri mangling the token, we replace it as a string
      # later on. Figure out a way around this.
      builder.doc.at_xpath('//soap:Header/wsse:Security/wsu:Timestamp').add_previous_sibling(Nokogiri::XML::Text.new('SAML_ASSERTION_PLACEHOLDER', builder.doc))

      request.at_xpath('//soap:Envelope', NAMESPACES).tap do |e|
        NAMESPACES.each do |ns, uri|
          e.add_namespace(ns.to_s, uri)
        end
      end
      request.xpath('//soap:Envelope/soap:Body').each do |body|
        body.add_previous_sibling(builder.doc.root)
        body.add_namespace('wsu', NAMESPACES[:wsu])
        body['wsu:Id'] = request_id
      end

      signed = sign(request)
      signed.gsub!('SAML_ASSERTION_PLACEHOLDER', @assertion.to_xml(indent: 0, save_with: Nokogiri::XML::Node::SaveOptions::AS_XML).strip)

      signed
    end