class Keychain::Item

Constants

ATTR_MAP
INVERSE_ATTR_MAP

Public Class Methods

from_dictionary_of_attributes(cf_dict) click to toggle source

@private

# File lib/keychain/item.rb, line 111
def self.from_dictionary_of_attributes(cf_dict)
  new(0).tap {|item| item.send :update_self_from_dictionary, cf_dict}
end
new(attrs_or_pointer) click to toggle source

Creates a new keychain item either from an FFI::Pointer or a hash of attributes

@param [FFI::Pointer, Hash] attrs_or_pointer Either an FFI::Pointer to an existing

SecKeychainItemRef to wrap or hash of attributes to create a new, unsaved Keychain::Item from
see {Keychain::Scope#create}
Calls superclass method Sec::Base::new
# File lib/keychain/item.rb, line 52
def self.new(attrs_or_pointer)
  if attrs_or_pointer.is_a? Hash
    super(0).tap do |result|
      attrs_or_pointer.each {|k,v| result.send("#{k}=", v)}
    end
  else
    super
  end
end

Public Instance Methods

delete() click to toggle source

Removes the item from the associated keychain

# File lib/keychain/item.rb, line 64
def delete
  status = Sec.SecKeychainItemDelete(self)
  Sec.check_osstatus(status)
  self
end
inspect() click to toggle source

returns a programmer friendly description of the item @return [String]

# File lib/keychain/item.rb, line 42
def inspect
  "<SecKeychainItem 0x#{@ptr.address.to_s(16)} #{service ? "service: #{service}" : "server: #{server}"} account: #{account}>"
end
password() click to toggle source

Fetches the password data associated with the item. This may cause the user to be asked for access @return [String] The password data, an ASCII_8BIT encoded string

# File lib/keychain/item.rb, line 80
def password
  return @unsaved_password if @unsaved_password
  out_buffer = FFI::MemoryPointer.new(:pointer)
  status = Sec.SecItemCopyMatching({Sec::Query::ITEM_LIST => CF::Array.immutable([self]),
                                    Sec::Query::SEARCH_LIST => [self.keychain],
                                    Sec::Query::CLASS => self.klass,
                                    Sec::Query::RETURN_DATA => true}.to_cf, out_buffer)
  Sec.check_osstatus(status)
  CF::Base.typecast(out_buffer.read_pointer).release_on_gc.to_s
end
password=(value) click to toggle source

Set a new password for the item @note The new password is not saved into the keychain until you call {Keychain::Item#save!} @param [String] value The new value for the password @return [String] The set value

# File lib/keychain/item.rb, line 74
def password=(value)
  @unsaved_password = value
end
persisted?() click to toggle source

Whether the item has been persisted to the keychain @return [Boolean]

# File lib/keychain/item.rb, line 117
def persisted?
  !@ptr.null?
end
save!(options={}) click to toggle source

Attempts to update the keychain with any changes made to the item or saves a previously unpersisted item @param [optional, Hash] options extra options when saving the item @option options [Keychain::Keychain] :keychain when saving an unsaved item, they keychain to save it in @return [Keychain::Item] returns the item

# File lib/keychain/item.rb, line 96
def save!(options={})
  if persisted?
    cf_dict = update
  else
    cf_dict = create(options)
    self.ptr = cf_dict[Sec::Value::REF].to_ptr
    self.retain.release_on_gc
  end
  @unsaved_password = nil
  update_self_from_dictionary(cf_dict)
  cf_dict.release
  self
end

Private Instance Methods

build_create_query(options) click to toggle source
# File lib/keychain/item.rb, line 145
def build_create_query options
  query = CF::Dictionary.mutable
  query[Sec::Value::DATA] = CF::Data.from_string(@unsaved_password) if @unsaved_password
  query[Sec::Query::KEYCHAIN] = options[:keychain] if options[:keychain] 
  query[Sec::Query::RETURN_ATTRIBUTES] = CF::Boolean::TRUE
  query[Sec::Query::RETURN_REF] = CF::Boolean::TRUE
  query
end
build_new_attributes() click to toggle source
# File lib/keychain/item.rb, line 164
def build_new_attributes
  new_attributes = CF::Dictionary.mutable
  @attributes.each do |k,v|
    next if k == :created_at || k == :updated_at
    next if k == :klass && persisted?
    k = self.class::INVERSE_ATTR_MAP[k]
    new_attributes[k] = v.to_cf
  end
  new_attributes[Sec::Value::DATA] = CF::Data.from_string(@unsaved_password) if @unsaved_password
  new_attributes
end
build_refresh_query() click to toggle source
# File lib/keychain/item.rb, line 154
def build_refresh_query
  query = CF::Dictionary.mutable
  query[Sec::Query::SEARCH_LIST] = CF::Array.immutable([self.keychain])
  query[Sec::Query::ITEM_LIST] = CF::Array.immutable([self])
  query[Sec::Query::RETURN_ATTRIBUTES] = CF::Boolean::TRUE
  query[Sec::Query::RETURN_REF] = CF::Boolean::TRUE
  query[Sec::Query::CLASS] = klass.to_cf
  query
end
create(options) click to toggle source
# File lib/keychain/item.rb, line 123
def create(options)
  result = FFI::MemoryPointer.new :pointer
  query = build_create_query(options)
  query.merge!(build_new_attributes)
  status = Sec.SecItemAdd(query, result);
  Sec.check_osstatus(status)
  cf_dict = CF::Base.typecast(result.read_pointer)
end
update() click to toggle source
# File lib/keychain/item.rb, line 132
def update
  status = Sec.SecItemUpdate({Sec::Query::SEARCH_LIST => [self.keychain],
                              Sec::Query::ITEM_LIST => [self],
                              Sec::Query::CLASS => klass}.to_cf, build_new_attributes);
  Sec.check_osstatus(status)

  result = FFI::MemoryPointer.new :pointer
  query = build_refresh_query
  status = Sec.SecItemCopyMatching(query, result);
  Sec.check_osstatus(status)
  cf_dict = CF::Base.typecast(result.read_pointer)
end