class Gem::CredentialStore::LinuxBackend

Stores credentials in the Secret Service API (GNOME Keyring, KWallet, …) via the secret-tool command line tool from libsecret.

Constants

ACCOUNT_ATTRIBUTE

secret-tool prints an item’s attributes to stderr, one per line.

Public Class Methods

# File lib/rubygems/credential_store/native/linux.rb, line 14
def self.available?
  return @available if defined?(@available)

  @available = ENV["PATH"].to_s.split(File::PATH_SEPARATOR).any? do |dir|
    File.executable?(File.join(dir, "secret-tool"))
  end
end
# File lib/rubygems/credential_store/native/linux.rb, line 65
def self.delete(service, account)
  _out, err, status = Open3.capture3(
    "secret-tool", "clear", "service", service, "account", account
  )
  return cleared?(service, account) if status.success?

  # secret-tool clear exits 1 with no stderr when nothing matched.
  status.exitstatus == 1 && err.to_s.strip.empty?
end
# File lib/rubygems/credential_store/native/linux.rb, line 75
def self.delete_all(service)
  _out, err, status = Open3.capture3(
    "secret-tool", "clear", "service", service
  )
  return cleared?(service) if status.success?

  status.exitstatus == 1 && err.to_s.strip.empty?
end
# File lib/rubygems/credential_store/native/linux.rb, line 29
def self.get(service, account)
  out, err, status = Open3.capture3(
    "secret-tool", "lookup", "service", service, "account", account
  )
  # secret-tool exits 1 with nothing on stderr when the entry is simply
  # absent. Anything else is a real failure.
  unless status.success?
    return nil if status.exitstatus == 1 && err.to_s.strip.empty?

    raise "secret-tool exited with #{status.exitstatus}: #{err.strip}"
  end

  secret = out.chomp
  secret.empty? ? nil : secret
end

secret-tool writes attributes to stderr and secrets to stdout, so accounts are read from stderr. Discarding stdout also keeps a secret containing a newline from being mistaken for an attribute line.

# File lib/rubygems/credential_store/native/linux.rb, line 56
def self.list(service)
  _out, err, status = Open3.capture3(
    "secret-tool", "search", "--all", "service", service
  )
  return [] unless status.success?

  err.scan(ACCOUNT_ATTRIBUTE).flatten.uniq
end

Clears the memoized available? result. Intended for tests only.

# File lib/rubygems/credential_store/native/linux.rb, line 25
def self.reset!
  remove_instance_variable(:@available) if defined?(@available)
end
# File lib/rubygems/credential_store/native/linux.rb, line 45
def self.set(service, account, secret)
  _out, status = Open3.capture2(
    "secret-tool", "store", "--label=RubyGems", "service", service, "account", account,
    stdin_data: secret
  )
  status.success?
end