module Azure::ARM::WindowsCredentials

Public Instance Methods

latest_credential_target(targets) click to toggle source
# File lib/azure/resource_management/windows_credentials.rb, line 156
def latest_credential_target(targets)
  case targets.size
  when 0
    raise "No Target was found for windows credentials"
  when 1
    targets.first.gsub("Target:", "").strip
  else
    latest_target = ""
    max_expiry_time = Time.new(0)

    # Using expiry_time to determine the latest credential
    targets.each do |target|
      target_obj = target.split("::")
      expiry_time_obj = target_obj.select { |obj| obj.include? "expiresOn" }
      expiry_time = expiry_time_obj[0].split("expiresOn:")[1].delete("\\")
      if Time.parse(expiry_time) > max_expiry_time
        latest_target = target
        max_expiry_time = Time.parse(expiry_time)
      end
    end

    latest_target.gsub("Target:", "").strip
  end
end
target_name() click to toggle source

Todo: For getting the complete refreshToken, both credentials (ending with –0-2 and –1-2) have to be read

# File lib/azure/resource_management/windows_credentials.rb, line 123
def target_name
  # cmdkey command is used for accessing windows credential manager.
  # Multiple credentials get created in windows credential manager for a single Azure account in xplat-cli
  # One of them is for common tenant id, which can't be used
  # Others end with --0-x,--1-x,--2-x etc, where x represents the total no. of credentials across which the token is divided
  # The one ending with --0-x has the complete accessToken in the credentialBlob.
  # Refresh Token is split across both credentials (ending with --0-x and --1-x).
  # Xplat splits the credentials based on the number of bytes of the tokens.
  # Hence the access token is always found in the one which start with --0-
  # So selecting the credential on the basis of --0-
  xplat_creds_cmd = Mixlib::ShellOut.new('cmdkey /list | findstr AzureXplatCli | findstr \--0- | findstr -v common')
  result = xplat_creds_cmd.run_command
  target_names = []

  if result.stdout.empty?
    Chef::Log.debug("Unable to find a credential with --0- and falling back to looking for any credential.")
    xplat_creds_cmd = Mixlib::ShellOut.new("cmdkey /list | findstr AzureXplatCli | findstr -v common")
    result = xplat_creds_cmd.run_command

    if result.stdout.empty?
      raise "Azure Credentials not found. Please run xplat's 'azure login' command"
    else
      target_names = result.stdout.split("\n")
    end
  else
    target_names = result.stdout.split("\n")
  end

  # If "azure login" is run for multiple users, there will be multiple credentials
  # Picking up the latest logged in user's credentials
  latest_credential_target target_names
end
token_details_from_WCM() click to toggle source
# File lib/azure/resource_management/windows_credentials.rb, line 80
def token_details_from_WCM
  target = target_name

  if target && !target.empty?
    target_pointer = wstring(target)
    info_ptr = FFI::MemoryPointer.new(:pointer)
    cred = CREDENTIAL_OBJECT.new info_ptr
    cred_result = CredReadW(target_pointer, CRED_TYPE_GENERIC, 0, cred)
    translated_cred = CREDENTIAL_OBJECT.new(info_ptr.read_pointer)

    target_obj = translated_cred[:TargetName].read_wstring.split("::") if translated_cred[:TargetName].read_wstring
    cred_blob = translated_cred[:CredentialBlob].get_bytes(0, translated_cred[:CredentialBlobSize]).split("::")

    tokentype = target_obj.select { |obj| obj.include? "tokenType" }
    user = target_obj.select { |obj| obj.include? "userId" }
    clientid = target_obj.select { |obj| obj.include? "clientId" }
    expiry_time = target_obj.select { |obj| obj.include? "expiresOn" }
    access_token = cred_blob.select { |obj| obj.include? "a:" }
    refresh_token = cred_blob.select { |obj| obj.include? "r:" }

    credential = {}
    credential[:tokentype] = tokentype[0].split(":")[1]
    credential[:user] = user[0].split(":")[1]
    credential[:token] = access_token[0].split(":")[1]
    # TODO: refresh_token is not complete currently
    # target_name method needs to be modified for that
    credential[:refresh_token] = refresh_token[0].split(":")[1]
    credential[:clientid] = clientid[0].split(":")[1]
    credential[:expiry_time] = expiry_time[0].split("expiresOn:")[1].delete("\\")

    # Free memory pointed by info_ptr
    info_ptr.free
  else
    raise "TargetName Not Found"
  end
  credential
rescue => error
  ui.error("#{error.message}")
  Chef::Log.debug("#{error.backtrace.join("\n")}")
  exit
end