class Mellon::Keychain

Keychain provides simple methods for reading and storing keychain entries.

Constants

ENTRY_MISSING

Attributes

name[R]

@return [String] keychain name (without extension)

path[R]

@return [String] path to keychain

Public Class Methods

default() click to toggle source

@return [Keychain] default keychain

# File lib/mellon/keychain.rb, line 43
def default
  keychain_path = Utils.security("default-keychain")[KEYCHAIN_REGEXP, 1]
  new(keychain_path, ensure_exists: false)
end
find(name) click to toggle source

Find a keychain matching the given name.

@param [String] name @return [Keychain] @raise [KeyError] if no matching keychain was found

# File lib/mellon/keychain.rb, line 26
def find(name)
  quoted = Regexp.quote(name)
  regexp = Regexp.new(quoted, Regexp::IGNORECASE)
  keychains = list

  keychain = keychains.find do |keychain|
    keychain.name =~ regexp
  end

  if keychain.nil?
    raise KeyError, "Could not find keychain “#{name}” in #{keychains.map(&:name).join(", ")}"
  end

  keychain
end
list() click to toggle source

@return [Array<Keychain>] all available keychains

# File lib/mellon/keychain.rb, line 49
def list
  Utils.security("list-keychains").scan(KEYCHAIN_REGEXP).map do |(keychain_path)|
    new(keychain_path, ensure_exists: false)
  end
end
new(path, ensure_exists: true) click to toggle source

Initialize a keychain on the given path.

@param [String] path @param [Boolean] ensure_exists check if keychain exists or not

# File lib/mellon/keychain.rb, line 60
def initialize(path, ensure_exists: true)
  @path = path
  @name = File.basename(path, ".keychain")
  command "show-keychain-info" if ensure_exists
end

Public Instance Methods

==(other) click to toggle source

@param other @return [Boolean] true if the keychains have the same path

Calls superclass method
# File lib/mellon/keychain.rb, line 128
def ==(other)
  if other.is_a?(Keychain)
    path == other.path
  else
    super
  end
end
[](key) click to toggle source

@param [String] key @return [String, nil] contents of entry at key, or nil if not set

# File lib/mellon/keychain.rb, line 86
def [](key)
  _, data = read(key)
  data
end
[]=(key, data) click to toggle source

Write data to entry key, or updating existing one if it exists.

@param [String] key @param [String] data

# File lib/mellon/keychain.rb, line 95
def []=(key, data)
  info, _ = read(key)
  info ||= {}

  if data
    write(key, data, info)
  else
    delete(key, info)
  end
end
eql?(other) click to toggle source

@param other @return [Boolean] true if the keychains have the same path

Calls superclass method
# File lib/mellon/keychain.rb, line 122
def eql?(other)
  self == other or super
end
fetch(key, *args, &block) click to toggle source

Retrieve a value, but if it does not exist return the default value, or call the provided block, or raise an error. See Hash#fetch.

@param [String] key @param default @return [String] value for key, default, or value from block @yield if key does not exist, and block is given @raise [KeyError] if key does not exist, and no default is given

# File lib/mellon/keychain.rb, line 80
def fetch(key, *args, &block)
  self[key] or {}.fetch(key, *args, &block)
end
hash() click to toggle source

@return a hash unique to keychains of the same path

# File lib/mellon/keychain.rb, line 116
def hash
  path.hash
end
keys() click to toggle source

Retrieve all available keys.

@return [Array<String>]

# File lib/mellon/keychain.rb, line 109
def keys
  Utils.parse_dump(command "dump-keychain").map do |keychain, info|
    info[:label]
  end
end

Private Instance Methods

command(*command, &block) click to toggle source

Execute a command with the context of this keychain.

@param [Array<String>] command

# File lib/mellon/keychain.rb, line 199
def command(*command, &block)
  command += [path]
  Utils.security *command, &block
end
delete(key, options = {}) click to toggle source

Delete the entry matching key and options.

@param [String] key @param [Hash] options @option (see write)

# File lib/mellon/keychain.rb, line 185
def delete(key, options = {})
  info = Utils.build_info(key, options)

  command "delete-generic-password",
    "-a", info[:account_name],
    "-s", info[:service_name],
    "-l", info[:label],
    "-D", info[:kind],
    "-C", info[:type]
end
read(key) click to toggle source

Read a key from the keychain.

@param [String] key @return [Array<Hash, String>, nil] tuple of entry info, and text contents, or nil if key does not exist

# File lib/mellon/keychain.rb, line 142
def read(key)
  command "find-generic-password", "-g", "-l", key do |info, password_info|
    [Utils.parse_info(info), Utils.parse_contents(password_info)]
  end
rescue CommandError => e
  raise unless e.message =~ ENTRY_MISSING
  nil
end
write(key, data, options = {}) click to toggle source

Write data with given key to the keychain, or update existing key if it exists.

@note keychain entries are not unique by key, but also by the information

provided through options; two entries with same key but different
account name (for example), will become two different entries when
writing.

@param [String] key @param [String] data @param [Hash] options @option options [#to_s] :type (:note) one of Mellon::TYPES @option options [String] :account_name (“”) @option options [String] :service_name (key) @option options [String] :label (service_name) @raise [CommandError] if writing fails

# File lib/mellon/keychain.rb, line 166
def write(key, data, options = {})
  info = Utils.build_info(key, options)

  command "add-generic-password",
    "-a", info[:account_name],
    "-s", info[:service_name],
    "-l", info[:label],
    "-D", info[:kind],
    "-C", info[:type],
    "-T", "", # which applications have access (none)
    "-U", # upsert
    "-w", data
end