Class: Keychain::Item
- Inherits:
-
Object
- Object
- Keychain::Item
- Defined in:
- lib/mr_keychain/item.rb
Overview
Only the #password method should ever cause an alert to pop up and require permission, and this should only happen for keychain items that were not created using the same program that is asking for the password.
@ todo Need to add some documentation to explain locally cached attributes and how they need to be #save!'d in order to persist changes and additions.
Represents an entry in the login keychain.
In order to be secure, this class will NEVER cache a password; any time that you change a password, it will be written to the keychain immeadiately.
Accessors (collapse)
-
- (Object) [](key)
Direct access to the attributes hash of the keychain item.
-
- (Object) []=(key, value)
Direct access to the attributes hash of the keychain item.
Alternate accessors (collapse)
-
- (Object) item_class
Equivalent to #[KSecClass].
-
- (Object) item_class=(value)
Equivalent to #[KSecClass] =.
-
- (Object) method_missing(meth, *args)
Dynamic get/set for the various attributes that a keychain item can have.
-
- (String) password
Returns the password for the item.
-
- (String) password=(new_password)
Updates the password associated with the keychain item.
Instance Method Summary (collapse)
-
- (Boolean) exists?
See if the item currently exists in the keychain.
-
- (Item) initialize(attrs = {})
constructor
Each Keychain::Item should contain a KSecClass at the very least; the default value is KSecClassInternetPassword.
-
- (Keychain::Item) reload!
Reload the cached item attributes from the keychain.
Constructor Details
- (Item) initialize(attrs = {})
Exploit hash lookup failure blocks to do dynamic attribute lookup
Each Keychain::Item should contain a KSecClass at the very least; the default value is KSecClassInternetPassword.
26 27 28 29 |
# File 'lib/mr_keychain/item.rb', line 26 def initialize attrs = {} @tainted = {} @attributes = { KSecClass => KSecClassInternetPassword }.merge(attrs) end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
- (Object) method_missing(meth, *args)
Dynamic get/set for the various attributes that a keychain item can have.
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/mr_keychain/item.rb', line 124 def method_missing meth, *args method = meth.to_s setter = method.chomp!('=') predicate = method.chomp!('?') const = "KSecAttr#{'Is' if predicate}#{method.camelize!}" if Kernel.const_defined?(const) const_value = Kernel.const_get(const) return (setter ? self.send(:[]=, const_value, *args) : self[const_value]) end const = "KSecAttrIs#{method}" if Kernel.const_defined?(const) const_value = Kernel.const_get(const) return (setter ? self.send(:[]=, const_value, *args) : self[const_value]) end super end |
Instance Method Details
- (Object) [](key)
Direct access to the attributes hash of the keychain item. You can get a list of all the attributes from Apple's documentation (see the README file).
44 45 46 |
# File 'lib/mr_keychain/item.rb', line 44 def [] key key == KSecAttrPassword ? self.password : @attributes[key] end |
- (Object) []=(key, value)
Direct access to the attributes hash of the keychain item. You can get a list of all the attributes from Apple's documentation (see the README file).
59 60 61 62 |
# File 'lib/mr_keychain/item.rb', line 59 def []= key, value @tainted[key] = true unless value == @attributes[key] @attributes[key] = value end |
- (Boolean) exists?
See if the item currently exists in the keychain.
145 146 147 |
# File 'lib/mr_keychain/item.rb', line 145 def exists? Keychain.item_exists?(@attributes) end |
- (Object) item_class
Equivalent to #[KSecClass]
67 |
# File 'lib/mr_keychain/item.rb', line 67 def item_class; self[KSecClass]; end |
- (Object) item_class=(value)
Equivalent to #[KSecClass] =
69 |
# File 'lib/mr_keychain/item.rb', line 69 def item_class= value; self[KSecClass] = value; end |
- (String) password
Blank passwords should come back as an empty string, but that hasn't been tested thoroughly.
Returns the password for the item.
This method will raise an error if no keychain item is found, which should only happen if the item was deleted since this object was instantiated or you changed some of the key/value pairs used to lookup the object.
84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/mr_keychain/item.rb', line 84 def password search = @attributes.merge( KSecMatchLimit => KSecMatchLimitOne, KSecReturnData => true ) result = Pointer.new(:id) case error_code = SecItemCopyMatching( search, result ) when ErrSecSuccess then result[0].to_str else raise KeychainException.new( 'Getting password', error_code ) end end |
- (String) password=(new_password)
Updates the password associated with the keychain item. If the item does not exist in the keychain it will be added first.
106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/mr_keychain/item.rb', line 106 def password= new_password password_data = { KSecValueData => (new_password || '').to_data } error_code = if exists? SecItemUpdate( @attributes, password_data ) else SecItemAdd( @attributes.merge(password_data), nil ) end case error_code when ErrSecSuccess then password_data[KSecValueData].to_str else raise KeychainException.new( 'Updating password', error_code ) end end |
- (Keychain::Item) reload!
Reload the cached item attributes from the keychain. An error will be raised if the item does not exist. If more than one item exists then the first one found will be reloaded.
156 157 158 159 160 161 162 163 164 165 |
# File 'lib/mr_keychain/item.rb', line 156 def reload! new_attributes = Pointer.new(:id) old_attributes = @attributes.merge( KSecReturnAttributes => true, KSecMatchLimit => KSecMatchLimitOne ) error_code = SecItemCopyMatching(old_attributes, new_attributes) case error_code when ErrSecSuccess then @attributes = new_attributes[0] else raise KeychainException.new( 'Reloading keychain item', error_code ) end end |