class Keychain::Item
Constants
- ATTR_MAP
- INVERSE_ATTR_MAP
Public Class Methods
@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
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}
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
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
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
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
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
Whether the item has been persisted to the keychain @return [Boolean]
# File lib/keychain/item.rb, line 117 def persisted? !@ptr.null? end
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
# 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
# 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
# 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
# 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
# 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