carabiner 0.0.2.pre.alpha1 → 0.0.2.pre.alpha2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/Gemfile.lock +1 -1
- data/README.md +78 -4
- data/app/app_delegate.rb +1 -1
- data/app/models/user.rb +1 -1
- data/lib/carabiner/base_item.rb +160 -0
- data/lib/carabiner/internet_password_item.rb +152 -0
- data/lib/carabiner/password_item.rb +67 -0
- data/lib/carabiner/version.rb +1 -1
- data/spec/models/internet_password_item_spec.rb +38 -0
- data/spec/models/{password_keychain_item_spec.rb → password_item_spec.rb} +7 -3
- metadata +9 -5
- data/lib/carabiner/password_keychain_item.rb +0 -160
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MWQ1MTE4MzgxYWRjZjgzMjAyMzFjNWY4NGQ3Nzk1ZDE2YjI4MGZlYQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
MzNjZmNhODRkZTc2NmExMTlhMjU0ZDZlYzFiZDVmN2Q3MDhlNDI1ZA==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
ZTViMDU1MGRjMDZjNjYyNzg3YzMxOTYyZjZmODkzNzA3ZWViOTQ1ZjIwNThh
|
10
|
+
YzhhZjhmMGNhZjg3ODYyNDdmZjAyNDIyNDE2YWJlNWY1NTg3YjIyMjhmY2Vi
|
11
|
+
ZGIzZTQ3NmJlYWI3MzI4MmU4ZDU2MzY0MzU4NzZkY2E3NjVjZDQ=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ZWY3YjI3MmYyNDhiMzQ1YTJiNGFiYTQ2Yjc1Mzg1NTk3NGE1ODEwZWJmMmFi
|
14
|
+
OWMwZWM2N2QwYjdiZDM1MzgyMGU4YTY3ZTRiNTNiNzFlMWQxMDk0Y2ZhNDJl
|
15
|
+
ODFmOTNjZWNiMzE2ZDA1ZTAxNjFhZWFjMGMwOWIwNTBlOGEzYmQ=
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Carabiner [![Build Status](https://travis-ci.org/mordaroso/carabiner.png)](https://travis-ci.org/mordaroso/carabiner)
|
1
|
+
# Carabiner [![Build Status](https://travis-ci.org/mordaroso/carabiner.png)](https://travis-ci.org/mordaroso/carabiner) [![Gem Version](https://badge.fury.io/rb/carabiner.png)](http://badge.fury.io/rb/carabiner)
|
2
2
|
|
3
3
|
Rubymotion wrapper for the easy access to the keychain.
|
4
4
|
|
@@ -6,8 +6,82 @@ Rubymotion wrapper for the easy access to the keychain.
|
|
6
6
|
|
7
7
|
**This gem is still under heavy development and will be released soon. Please be paitent.**
|
8
8
|
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Install the gem
|
12
|
+
```bash
|
13
|
+
gem install carabiner
|
14
|
+
```
|
15
|
+
|
16
|
+
And add it to your Rakefile
|
17
|
+
```ruby
|
18
|
+
require 'carabiner'
|
19
|
+
```
|
20
|
+
|
21
|
+
Or use [Bundler](http://gembundler.com/) to manage your gem dependencies
|
22
|
+
```ruby
|
23
|
+
gem 'carabiner'
|
24
|
+
```
|
25
|
+
|
26
|
+
## Usage
|
27
|
+
|
28
|
+
First add the Security framwork and the entitlement setting to your Rakefile.
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
app.frameworks += ['Security']
|
32
|
+
|
33
|
+
app.entitlements['keychain-access-groups'] = [
|
34
|
+
app.seed_id + '.' + app.identifier
|
35
|
+
]
|
36
|
+
```
|
37
|
+
|
38
|
+
Initialize a keychain item with an unique identifier as a finder hash. If it already exists the keychain data will be available otherwise it will set it up.
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
@item = Carabiner::PasswordItem.new generic: 'YourKeyChainItemIdentifier'
|
42
|
+
```
|
43
|
+
|
44
|
+
After the initialization you have access to all kSecClassGenericPassword attributes with getter and setter methods.
|
45
|
+
*Note: These values are not secure.*
|
46
|
+
```ruby
|
47
|
+
@item.access_group # The corresponding value is of type CFStringRef and indicates which access group an item is in.
|
48
|
+
@item.creation_time # The corresponding value is of type CFDateRef and represents the date the item was created. Read only.
|
49
|
+
@item.modifaction_date # The corresponding value is of type CFDateRef and represents the last time the item was updated. Read only.
|
50
|
+
@item.description # The corresponding value is of type CFStringRef and specifies a user-visible string describing this kind of item (for example, "Disk image password").
|
51
|
+
@item.comment # The corresponding value is of type CFStringRef and contains the user-editable comment for this item.
|
52
|
+
@item.creator # The corresponding value is of type CFNumberRef and represents the item's creator. This number is the unsigned integer representation of a four-character code (for example, 'aCrt').
|
53
|
+
@item.type # The corresponding value is of type CFNumberRef and represents the item's type. This number is the unsigned integer representation of a four-character code (for example, 'aTyp').
|
54
|
+
@item.label # The corresponding value is of type CFStringRef and contains the user-visible label for this item.
|
55
|
+
@item.is_invisible # The corresponding value is of type CFBooleanRef and is kCFBooleanTrue if the item is invisible (that is, should not be displayed).
|
56
|
+
@item.is_negative # The corresponding value is of type CFBooleanRef and indicates whether there is a valid password associated with this keychain item. This is useful if your application doesn't want a password for some particular service to be stored in the keychain, but prefers that it always be entered by the user.
|
57
|
+
@item.account # The corresponding value is of type CFStringRef and contains an account name. Items of class kSecClassGenericPassword and kSecClassInternetPassword have this attribute.
|
58
|
+
@item.service # The corresponding value is a string of type CFStringRef that represents the service associated with this item. Items of class kSecClassGenericPassword have this attribute.
|
59
|
+
@item.generic # The corresponding value is of type CFDataRef and contains a user-defined attribute. Items of class kSecClassGenericPassword have this attribute.
|
60
|
+
```
|
61
|
+
|
62
|
+
Set and get the secure password
|
63
|
+
```ruby
|
64
|
+
@item.password = 'secure'
|
65
|
+
@item.password
|
66
|
+
```
|
67
|
+
|
68
|
+
And then save the item to the keychain.
|
69
|
+
```ruby
|
70
|
+
@item.save!
|
71
|
+
```
|
72
|
+
|
73
|
+
To delete the data use ```@item.delete!``` or ```@item.reset!```.
|
74
|
+
|
75
|
+
## Documentation
|
76
|
+
|
77
|
+
See Apples [Keychain Services Reference](https://developer.apple.com/library/mac/documentation/Security/Reference/keychainservices/Reference/reference.html) for more information
|
78
|
+
|
9
79
|
## TODOs
|
10
80
|
|
11
|
-
* Better
|
12
|
-
*
|
13
|
-
*
|
81
|
+
* Better Documentation
|
82
|
+
* OSX Support
|
83
|
+
* Secure Notes
|
84
|
+
* Certificates
|
85
|
+
* Keys
|
86
|
+
|
87
|
+
Feel free to fork and submit pull requests!
|
data/app/app_delegate.rb
CHANGED
data/app/models/user.rb
CHANGED
@@ -0,0 +1,160 @@
|
|
1
|
+
module Carabiner
|
2
|
+
class BaseItem
|
3
|
+
attr_accessor :query, :persistent, :identifiers
|
4
|
+
|
5
|
+
def self.attributes(attrs = nil)
|
6
|
+
if attrs
|
7
|
+
@attributes = attrs
|
8
|
+
attr_accessor *attrs.keys
|
9
|
+
else
|
10
|
+
@attributes ||= {}
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.sec_class sec_class = nil
|
15
|
+
@sec_class ||= sec_class
|
16
|
+
end
|
17
|
+
|
18
|
+
NoErr = 0
|
19
|
+
|
20
|
+
def initialize identifiers
|
21
|
+
@identifiers = identifiers
|
22
|
+
reset_to_identifiers
|
23
|
+
|
24
|
+
self.query = data
|
25
|
+
query[KSecClass] = self.class.sec_class
|
26
|
+
|
27
|
+
if BubbleWrap::Device.simulator?
|
28
|
+
query.delete(KSecAttrAccessGroup)
|
29
|
+
end
|
30
|
+
|
31
|
+
query[KSecMatchLimit] = KSecMatchLimitOne
|
32
|
+
query[KSecReturnAttributes] = KCFBooleanTrue
|
33
|
+
|
34
|
+
tempQuery = NSDictionary.dictionaryWithDictionary query
|
35
|
+
outDictionaryPtr = Pointer.new(:object)
|
36
|
+
if !(SecItemCopyMatching(tempQuery, outDictionaryPtr) == NoErr)
|
37
|
+
reset!
|
38
|
+
self.data = self.data.merge identifiers
|
39
|
+
self.persistent = false
|
40
|
+
|
41
|
+
unless Device.simulator?
|
42
|
+
query[KSecAttrAccessGroup] = identifiers[:access_group]
|
43
|
+
end
|
44
|
+
else
|
45
|
+
self.persistent = true
|
46
|
+
self.data = secItemFormatToDictionary(outDictionaryPtr[0])
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def save!
|
51
|
+
writeToKeychain
|
52
|
+
self.persistent = true
|
53
|
+
true
|
54
|
+
end
|
55
|
+
|
56
|
+
def delete!
|
57
|
+
if persistent?
|
58
|
+
tempDictionary = dictionaryToSecItemFormat(data)
|
59
|
+
result = SecItemDelete(tempDictionary)
|
60
|
+
if result != NoErr && result != ErrSecItemNotFound
|
61
|
+
raise KeychainReturnCodeException.new "Problem deleting current dictionary.", result
|
62
|
+
end
|
63
|
+
self.persistent = false
|
64
|
+
end
|
65
|
+
self.data = {}
|
66
|
+
true
|
67
|
+
end
|
68
|
+
|
69
|
+
def reset!
|
70
|
+
delete! unless data.empty?
|
71
|
+
reset_to_identifiers
|
72
|
+
true
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.key_accepted? key
|
76
|
+
attributes.values.include? key
|
77
|
+
end
|
78
|
+
|
79
|
+
def persistent?
|
80
|
+
persistent
|
81
|
+
end
|
82
|
+
|
83
|
+
def data
|
84
|
+
self.class.attributes.keys.inject({}) do |memo, getter_name|
|
85
|
+
constant = self.class.attributes[getter_name]
|
86
|
+
value = send(getter_name)
|
87
|
+
memo[constant] = value if value
|
88
|
+
memo
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def data=(hash)
|
93
|
+
self.class.attributes.each do |getter_name, constant|
|
94
|
+
send("#{getter_name}=", hash[constant])
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def reset_to_identifiers
|
101
|
+
identifiers.each do |getter_name, value|
|
102
|
+
send("#{getter_name}=", value)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def dictionaryToSecItemFormat dictionaryToConvert
|
107
|
+
returnDictionary = NSMutableDictionary.dictionaryWithDictionary dictionaryToConvert
|
108
|
+
returnDictionary[KSecClass] = self.class.sec_class
|
109
|
+
passwordString = dictionaryToConvert[KSecValueData]
|
110
|
+
|
111
|
+
returnDictionary[KSecValueData] = passwordString.dataUsingEncoding(NSUTF8StringEncoding)
|
112
|
+
returnDictionary
|
113
|
+
end
|
114
|
+
|
115
|
+
def secItemFormatToDictionary dictionaryToConvert
|
116
|
+
returnDictionary = NSMutableDictionary.dictionaryWithDictionary dictionaryToConvert
|
117
|
+
returnDictionary[KSecReturnData] = KCFBooleanTrue
|
118
|
+
returnDictionary[KSecClass] = self.class.sec_class
|
119
|
+
|
120
|
+
passwordDataPtr = Pointer.new(:object)
|
121
|
+
|
122
|
+
result = SecItemCopyMatching(returnDictionary, passwordDataPtr)
|
123
|
+
if result == NoErr
|
124
|
+
returnDictionary.delete KSecReturnData
|
125
|
+
passwordData = passwordDataPtr[0]
|
126
|
+
password = NSString.alloc.initWithBytes(passwordData.bytes, length: passwordData.length, encoding: NSUTF8StringEncoding)
|
127
|
+
returnDictionary[KSecValueData] = password
|
128
|
+
else
|
129
|
+
raise KeychainReturnCodeException.new "Serious error, no matching item found in the keychain.", result
|
130
|
+
end
|
131
|
+
|
132
|
+
returnDictionary
|
133
|
+
end
|
134
|
+
|
135
|
+
def writeToKeychain
|
136
|
+
attributesPtr = Pointer.new(:object)
|
137
|
+
if SecItemCopyMatching(query, attributesPtr) == NoErr
|
138
|
+
query = NSMutableDictionary.dictionaryWithDictionary attributesPtr[0]
|
139
|
+
|
140
|
+
query[KSecClass] = self.class.sec_class
|
141
|
+
attributes_to_update = dictionaryToSecItemFormat data
|
142
|
+
attributes_to_update.delete_if { |key, value| !self.class.key_accepted? key }
|
143
|
+
|
144
|
+
if Device.simulator?
|
145
|
+
attributes_to_update.delete KSecAttrAccessGroup
|
146
|
+
end
|
147
|
+
|
148
|
+
result = SecItemUpdate(query, attributes_to_update)
|
149
|
+
unless result == NoErr
|
150
|
+
raise KeychainReturnCodeException.new "Couldn't update the Keychain Item.", result
|
151
|
+
end
|
152
|
+
else
|
153
|
+
result = SecItemAdd(dictionaryToSecItemFormat(data), nil)
|
154
|
+
unless result == NoErr
|
155
|
+
raise KeychainReturnCodeException.new "Couldn't add the Keychain Item.", result
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
module Carabiner
|
2
|
+
class InternetPasswordItem < BaseItem
|
3
|
+
# These are the default constants and their respective types,
|
4
|
+
# available for the KSecClassInternetPassword Keychain Item class:
|
5
|
+
#
|
6
|
+
# See the header file Security/SecItem.h for more details.
|
7
|
+
attributes({
|
8
|
+
# The corresponding value is of type CFStringRef and indicates which access group an item is in. Access groups can be used to share keychain items among two or more applications. For applications to share a keychain item, the applications must have a common access group listed in their keychain-access-groups entitlement, and the application adding the shared item to the keychain must specify this shared access-group name as the value for this key in the dictionary passed to the SecItemAdd function.
|
9
|
+
# An application can be a member of any number of access groups. By default, the SecItemUpdate, SecItemDelete, and SecItemCopyMatching functions search all the access groups an application is a member of. Include this key in the search dictionary for these functions to specify which access group is searched.
|
10
|
+
# A keychain item can be in only a single access group.
|
11
|
+
access_group: KSecAttrAccessGroup,
|
12
|
+
|
13
|
+
# The corresponding value is of type CFDateRef and represents the date the item was created. Read only.
|
14
|
+
creation_time: KSecAttrCreationDate,
|
15
|
+
|
16
|
+
# The corresponding value is of type CFDateRef and represents the last time the item was updated. Read only.
|
17
|
+
modifaction_date: KSecAttrModificationDate,
|
18
|
+
|
19
|
+
# The corresponding value is of type CFStringRef and specifies a user-visible string describing this kind of item (for example, "Disk image password").
|
20
|
+
description: KSecAttrDescription,
|
21
|
+
|
22
|
+
# The corresponding value is of type CFStringRef and contains the user-editable comment for this item.
|
23
|
+
comment: KSecAttrComment,
|
24
|
+
|
25
|
+
# The corresponding value is of type CFNumberRef and represents the item's creator. This number is the unsigned integer representation of a four-character code (for example, 'aCrt').
|
26
|
+
creator: KSecAttrCreator,
|
27
|
+
|
28
|
+
# The corresponding value is of type CFStringRef and contains the user-visible label for this item.
|
29
|
+
type: KSecAttrType,
|
30
|
+
|
31
|
+
# The corresponding value is of type CFStringRef and contains the user-visible label for this item.
|
32
|
+
label: KSecAttrLabel,
|
33
|
+
|
34
|
+
# The corresponding value is of type CFBooleanRef and is kCFBooleanTrue if the item is invisible (that is, should not be displayed).
|
35
|
+
is_invisible: KSecAttrIsInvisible,
|
36
|
+
|
37
|
+
# The corresponding value is of type CFBooleanRef and indicates whether there is a valid password associated with this keychain item. This is useful if your application doesn't want a password for some particular service to be stored in the keychain, but prefers that it always be entered by the user.
|
38
|
+
is_negative: KSecAttrIsNegative,
|
39
|
+
|
40
|
+
# The corresponding value is of type CFStringRef and contains an account name. Items of class kSecClassGenericPassword and kSecClassInternetPassword have this attribute.
|
41
|
+
account: KSecAttrAccount,
|
42
|
+
|
43
|
+
# The corresponding value is of type CFStringRef and represents the Internet security domain. Items of class kSecClassInternetPassword have this attribute.
|
44
|
+
security_domain: KSecAttrSecurityDomain,
|
45
|
+
|
46
|
+
# The corresponding value is of type CFStringRef and contains the server's domain name or IP address. Items of class kSecClassInternetPassword have this attribute.
|
47
|
+
server: KSecAttrServer,
|
48
|
+
|
49
|
+
# The corresponding value is of type CFNumberRef and denotes the protocol for this item (see “Protocol Values”). Items of class kSecClassInternetPassword have this attribute.
|
50
|
+
protocol: KSecAttrProtocol,
|
51
|
+
|
52
|
+
# The corresponding value is of type CFNumberRef and denotes the authentication scheme for this item (see “Authentication Type Values”).
|
53
|
+
authentication_type: KSecAttrAuthenticationType,
|
54
|
+
|
55
|
+
# The corresponding value is of type CFNumberRef and represents an Internet port number. Items of class kSecClassInternetPassword have this attribute.
|
56
|
+
port: KSecAttrPort,
|
57
|
+
|
58
|
+
# The corresponding value is of type CFStringRef and represents a path, typically the path component of the URL. Items of class kSecClassInternetPassword have this attribute.
|
59
|
+
path: KSecAttrPath,
|
60
|
+
|
61
|
+
# Data attribute key. A persistent reference to a credential can be stored on disk for later use or passed to other processes.
|
62
|
+
# The corresponding value is of type CFDataRef. For keys and password items, the data is secret (encrypted) and may require the user to enter a password for access.
|
63
|
+
password: KSecValueData
|
64
|
+
})
|
65
|
+
|
66
|
+
sec_class KSecClassInternetPassword
|
67
|
+
|
68
|
+
def self.authentication_type_values
|
69
|
+
{
|
70
|
+
ntlm: KSecAttrAuthenticationTypeNTLM,
|
71
|
+
msn: KSecAttrAuthenticationTypeMSN,
|
72
|
+
dpa: KSecAttrAuthenticationTypeDPA,
|
73
|
+
rpa: KSecAttrAuthenticationTypeRPA,
|
74
|
+
http_basic: KSecAttrAuthenticationTypeHTTPBasic,
|
75
|
+
http_digest: KSecAttrAuthenticationTypeHTTPDigest,
|
76
|
+
html_form: KSecAttrAuthenticationTypeHTMLForm,
|
77
|
+
default: KSecAttrAuthenticationTypeDefault
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.protocol_values
|
82
|
+
{
|
83
|
+
ftp: KSecAttrProtocolFTP,
|
84
|
+
ftp_account: KSecAttrProtocolFTPAccount,
|
85
|
+
http: KSecAttrProtocolHTTP,
|
86
|
+
irc: KSecAttrProtocolIRC,
|
87
|
+
nntp: KSecAttrProtocolNNTP,
|
88
|
+
pop3: KSecAttrProtocolPOP3,
|
89
|
+
smtp: KSecAttrProtocolSMTP,
|
90
|
+
socks: KSecAttrProtocolSOCKS,
|
91
|
+
imap: KSecAttrProtocolIMAP,
|
92
|
+
ldap: KSecAttrProtocolLDAP,
|
93
|
+
apple_talk: KSecAttrProtocolAppleTalk,
|
94
|
+
afp: KSecAttrProtocolAFP,
|
95
|
+
telnet: KSecAttrProtocolTelnet,
|
96
|
+
ssh: KSecAttrProtocolSSH,
|
97
|
+
ftps: KSecAttrProtocolFTPS,
|
98
|
+
https: KSecAttrProtocolHTTPS,
|
99
|
+
http_proxy: KSecAttrProtocolHTTPProxy,
|
100
|
+
https_proxy: KSecAttrProtocolHTTPSProxy,
|
101
|
+
ftp_proxy: KSecAttrProtocolFTPProxy,
|
102
|
+
smb: KSecAttrProtocolSMB,
|
103
|
+
rtsp: KSecAttrProtocolRTSP,
|
104
|
+
rtsp_proxy: KSecAttrProtocolRTSPProxy,
|
105
|
+
daap: KSecAttrProtocolDAAP,
|
106
|
+
eppc: KSecAttrProtocolEPPC,
|
107
|
+
ipp: KSecAttrProtocolIPP,
|
108
|
+
nntps: KSecAttrProtocolNNTPS,
|
109
|
+
ldaps: KSecAttrProtocolLDAPS,
|
110
|
+
telnet_s: KSecAttrProtocolTelnetS,
|
111
|
+
imaps: KSecAttrProtocolIMAPS,
|
112
|
+
ircs: KSecAttrProtocolIRCS,
|
113
|
+
pop3_s: KSecAttrProtocolPOP3S
|
114
|
+
}
|
115
|
+
end
|
116
|
+
|
117
|
+
def protocol_sym
|
118
|
+
self.class.protocol_values.find {|k, v| v == @protocol }.first if @protocol
|
119
|
+
end
|
120
|
+
|
121
|
+
def protocol=(value)
|
122
|
+
if self.class.protocol_values.values.include? value
|
123
|
+
@protocol = value
|
124
|
+
else
|
125
|
+
new_protocol = self.class.protocol_values.find {|k, v| k == value }
|
126
|
+
@protocol = new_protocol.last if new_protocol
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def authentication_type_sym
|
131
|
+
self.class.authentication_type_values.find {|k, v| v == @authentication_type }.first if @authentication_type
|
132
|
+
end
|
133
|
+
|
134
|
+
def authentication_type=(value)
|
135
|
+
if self.class.authentication_type_values.values.include? value
|
136
|
+
@authentication_type = value
|
137
|
+
else
|
138
|
+
new_authentication_type = self.class.authentication_type_values.find {|k, v| k == value }
|
139
|
+
@authentication_type = new_authentication_type.last if new_authentication_type
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def reset!
|
144
|
+
super
|
145
|
+
self.account = ''
|
146
|
+
self.label = ''
|
147
|
+
self.description = ''
|
148
|
+
self.password = ''
|
149
|
+
true
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Carabiner
|
2
|
+
class PasswordItem < BaseItem
|
3
|
+
|
4
|
+
# These are the default constants and their respective types,
|
5
|
+
# available for the kSecClassGenericPassword Keychain Item class:
|
6
|
+
#
|
7
|
+
# See the header file Security/SecItem.h for more details.
|
8
|
+
attributes({
|
9
|
+
# The corresponding value is of type CFStringRef and indicates which access group an item is in. Access groups can be used to share keychain items among two or more applications. For applications to share a keychain item, the applications must have a common access group listed in their keychain-access-groups entitlement, and the application adding the shared item to the keychain must specify this shared access-group name as the value for this key in the dictionary passed to the SecItemAdd function.
|
10
|
+
# An application can be a member of any number of access groups. By default, the SecItemUpdate, SecItemDelete, and SecItemCopyMatching functions search all the access groups an application is a member of. Include this key in the search dictionary for these functions to specify which access group is searched.
|
11
|
+
# A keychain item can be in only a single access group.
|
12
|
+
access_group: KSecAttrAccessGroup,
|
13
|
+
|
14
|
+
# The corresponding value is of type CFDateRef and represents the date the item was created. Read only.
|
15
|
+
creation_time: KSecAttrCreationDate,
|
16
|
+
|
17
|
+
# The corresponding value is of type CFDateRef and represents the last time the item was updated. Read only.
|
18
|
+
modifaction_date: KSecAttrModificationDate,
|
19
|
+
|
20
|
+
# The corresponding value is of type CFStringRef and specifies a user-visible string describing this kind of item (for example, "Disk image password").
|
21
|
+
description: KSecAttrDescription,
|
22
|
+
|
23
|
+
# The corresponding value is of type CFStringRef and contains the user-editable comment for this item.
|
24
|
+
comment: KSecAttrComment,
|
25
|
+
|
26
|
+
# The corresponding value is of type CFNumberRef and represents the item's creator. This number is the unsigned integer representation of a four-character code (for example, 'aCrt').
|
27
|
+
creator: KSecAttrCreator,
|
28
|
+
|
29
|
+
# The corresponding value is of type CFNumberRef and represents the item's type. This number is the unsigned integer representation of a four-character code (for example, 'aTyp').
|
30
|
+
type: KSecAttrType,
|
31
|
+
|
32
|
+
# The corresponding value is of type CFStringRef and contains the user-visible label for this item.
|
33
|
+
label: KSecAttrLabel,
|
34
|
+
|
35
|
+
# The corresponding value is of type CFBooleanRef and is kCFBooleanTrue if the item is invisible (that is, should not be displayed).
|
36
|
+
is_invisible: KSecAttrIsInvisible,
|
37
|
+
|
38
|
+
# The corresponding value is of type CFBooleanRef and indicates whether there is a valid password associated with this keychain item. This is useful if your application doesn't want a password for some particular service to be stored in the keychain, but prefers that it always be entered by the user.
|
39
|
+
is_negative: KSecAttrIsNegative,
|
40
|
+
|
41
|
+
# The corresponding value is of type CFStringRef and contains an account name. Items of class kSecClassGenericPassword and kSecClassInternetPassword have this attribute.
|
42
|
+
account: KSecAttrAccount,
|
43
|
+
|
44
|
+
# The corresponding value is a string of type CFStringRef that represents the service associated with this item. Items of class kSecClassGenericPassword have this attribute.
|
45
|
+
service: KSecAttrService,
|
46
|
+
|
47
|
+
# The corresponding value is of type CFDataRef and contains a user-defined attribute. Items of class kSecClassGenericPassword have this attribute.
|
48
|
+
generic: KSecAttrGeneric,
|
49
|
+
|
50
|
+
# Data attribute key. A persistent reference to a credential can be stored on disk for later use or passed to other processes.
|
51
|
+
# The corresponding value is of type CFDataRef. For keys and password items, the data is secret (encrypted) and may require the user to enter a password for access.
|
52
|
+
password: KSecValueData
|
53
|
+
})
|
54
|
+
|
55
|
+
sec_class KSecClassGenericPassword
|
56
|
+
|
57
|
+
def reset!
|
58
|
+
super
|
59
|
+
self.account = ''
|
60
|
+
self.label = ''
|
61
|
+
self.description = ''
|
62
|
+
self.password = ''
|
63
|
+
true
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
data/lib/carabiner/version.rb
CHANGED
@@ -0,0 +1,38 @@
|
|
1
|
+
module Carabiner
|
2
|
+
describe InternetPasswordItem do
|
3
|
+
before do
|
4
|
+
@item = InternetPasswordItem.new :server => 'github.com', :protocol => :https
|
5
|
+
@item.password = 'secret'
|
6
|
+
@item.account = 'Test'
|
7
|
+
@item.authentication_type = :html_form
|
8
|
+
@item.save!
|
9
|
+
end
|
10
|
+
|
11
|
+
after do
|
12
|
+
@item.delete!
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'initialize attributes' do
|
16
|
+
new_item = InternetPasswordItem.new :server => 'github.com', :protocol => :https
|
17
|
+
new_item.account.should == 'Test'
|
18
|
+
new_item.password.should == 'secret'
|
19
|
+
new_item.server.should == 'github.com'
|
20
|
+
new_item.protocol_sym.should == :https
|
21
|
+
new_item.authentication_type_sym.should == :html_form
|
22
|
+
new_item.persistent?.should == true
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'reset!' do
|
26
|
+
@item.reset!
|
27
|
+
@item.account.should == ''
|
28
|
+
@item.password.should == ''
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'delete!' do
|
32
|
+
@item.delete!
|
33
|
+
@item.account.should == nil
|
34
|
+
@item.password.should == nil
|
35
|
+
@item.persistent?.should == false
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -1,14 +1,18 @@
|
|
1
1
|
module Carabiner
|
2
|
-
describe
|
2
|
+
describe PasswordItem do
|
3
3
|
before do
|
4
|
-
@item =
|
4
|
+
@item = PasswordItem.new :generic => 'TestPasswordItem'
|
5
5
|
@item.password = 'secret'
|
6
6
|
@item.account = 'Test'
|
7
7
|
@item.save!
|
8
8
|
end
|
9
9
|
|
10
|
+
after do
|
11
|
+
@item.delete!
|
12
|
+
end
|
13
|
+
|
10
14
|
it 'initialize attributes' do
|
11
|
-
new_item =
|
15
|
+
new_item = PasswordItem.new :generic => 'TestPasswordItem'
|
12
16
|
new_item.account.should == 'Test'
|
13
17
|
new_item.password.should == 'secret'
|
14
18
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: carabiner
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.2.pre.
|
4
|
+
version: 0.0.2.pre.alpha2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- mordaroso
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-09-
|
11
|
+
date: 2013-09-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -100,15 +100,18 @@ files:
|
|
100
100
|
- app/views/settings_view.rb
|
101
101
|
- carabiner.gemspec
|
102
102
|
- lib/carabiner.rb
|
103
|
+
- lib/carabiner/base_item.rb
|
104
|
+
- lib/carabiner/internet_password_item.rb
|
103
105
|
- lib/carabiner/keychain_return_code_exception.rb
|
104
|
-
- lib/carabiner/
|
106
|
+
- lib/carabiner/password_item.rb
|
105
107
|
- lib/carabiner/version.rb
|
106
108
|
- scripts/travis/add-key.sh
|
107
109
|
- scripts/travis/profile/4A8AC2EE-F24B-4F4E-A6F0-67F9BC46B924.mobileprovision
|
108
110
|
- scripts/travis/remove-key.sh
|
109
111
|
- spec/main_spec.rb
|
112
|
+
- spec/models/internet_password_item_spec.rb
|
110
113
|
- spec/models/keychain_return_code_exception_spec.rb
|
111
|
-
- spec/models/
|
114
|
+
- spec/models/password_item_spec.rb
|
112
115
|
- spec/models/user_spec.rb
|
113
116
|
homepage: http://rubygems.org/gems/carabiner
|
114
117
|
licenses: []
|
@@ -135,7 +138,8 @@ specification_version: 4
|
|
135
138
|
summary: Rubymotion wrapper for the keychain
|
136
139
|
test_files:
|
137
140
|
- spec/main_spec.rb
|
141
|
+
- spec/models/internet_password_item_spec.rb
|
138
142
|
- spec/models/keychain_return_code_exception_spec.rb
|
139
|
-
- spec/models/
|
143
|
+
- spec/models/password_item_spec.rb
|
140
144
|
- spec/models/user_spec.rb
|
141
145
|
has_rdoc:
|
@@ -1,160 +0,0 @@
|
|
1
|
-
module Carabiner
|
2
|
-
class PasswordKeychainItem
|
3
|
-
|
4
|
-
attr_accessor :generic_password_query
|
5
|
-
|
6
|
-
# These are the default constants and their respective types,
|
7
|
-
# available for the kSecClassGenericPassword Keychain Item class:
|
8
|
-
#
|
9
|
-
# See the header file Security/SecItem.h for more details.
|
10
|
-
ATTRIBUTES = {
|
11
|
-
:access_group => KSecAttrAccessGroup,
|
12
|
-
:creation_time => KSecAttrCreationDate,
|
13
|
-
:modifaction_date => KSecAttrModificationDate,
|
14
|
-
:description => KSecAttrDescription,
|
15
|
-
:comment => KSecAttrComment,
|
16
|
-
:creator => KSecAttrCreator,
|
17
|
-
:type => KSecAttrType,
|
18
|
-
:label => KSecAttrLabel,
|
19
|
-
:is_invisible => KSecAttrIsInvisible,
|
20
|
-
:is_negative => KSecAttrIsNegative,
|
21
|
-
:account => KSecAttrAccount,
|
22
|
-
:service => KSecAttrService,
|
23
|
-
:generic => KSecAttrGeneric,
|
24
|
-
:password => KSecValueData
|
25
|
-
}
|
26
|
-
|
27
|
-
ATTRIBUTES.keys.each do |key|
|
28
|
-
attr_accessor key
|
29
|
-
end
|
30
|
-
|
31
|
-
NoErr = 0
|
32
|
-
|
33
|
-
def initialize identifier, accessGroup = nil
|
34
|
-
self.generic_password_query = {}
|
35
|
-
generic_password_query[KSecClass] = KSecClassGenericPassword
|
36
|
-
generic_password_query[KSecAttrGeneric] = identifier
|
37
|
-
|
38
|
-
if !accessGroup.nil? && !Device.simulator?
|
39
|
-
generic_password_query[KSecAttrAccessGroup] = accessGroup
|
40
|
-
end
|
41
|
-
|
42
|
-
generic_password_query[KSecMatchLimit] = KSecMatchLimitOne
|
43
|
-
generic_password_query[KSecReturnAttributes] = KCFBooleanTrue
|
44
|
-
|
45
|
-
tempQuery = NSDictionary.dictionaryWithDictionary generic_password_query
|
46
|
-
outDictionaryPtr = Pointer.new(:object)
|
47
|
-
|
48
|
-
if !(SecItemCopyMatching(tempQuery, outDictionaryPtr) == NoErr)
|
49
|
-
reset!
|
50
|
-
self.generic = identifier
|
51
|
-
|
52
|
-
if !accessGroup.nil? && !Device.simulator?
|
53
|
-
generic_password_query[KSecAttrAccessGroup] = accessGroup
|
54
|
-
end
|
55
|
-
else
|
56
|
-
self.keychain_item_data = secItemFormatToDictionary(outDictionaryPtr[0])
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def reset!
|
61
|
-
delete! unless keychain_item_data.empty?
|
62
|
-
self.account = ''
|
63
|
-
self.label = ''
|
64
|
-
self.description = ''
|
65
|
-
self.password = ''
|
66
|
-
true
|
67
|
-
end
|
68
|
-
|
69
|
-
def save!
|
70
|
-
writeToKeychain
|
71
|
-
true
|
72
|
-
end
|
73
|
-
|
74
|
-
def delete!
|
75
|
-
tempDictionary = dictionaryToSecItemFormat(keychain_item_data)
|
76
|
-
result = SecItemDelete(tempDictionary)
|
77
|
-
if result != NoErr && result != ErrSecItemNotFound
|
78
|
-
raise KeychainReturnCodeException.new "Problem deleting current dictionary.", result
|
79
|
-
end
|
80
|
-
self.keychain_item_data = {}
|
81
|
-
true
|
82
|
-
end
|
83
|
-
|
84
|
-
def self.key_accepted? key
|
85
|
-
ATTRIBUTES.values.include? key
|
86
|
-
end
|
87
|
-
|
88
|
-
private
|
89
|
-
|
90
|
-
def keychain_item_data
|
91
|
-
ATTRIBUTES.keys.inject({}) do |memo, getter_name|
|
92
|
-
constant = ATTRIBUTES[getter_name]
|
93
|
-
value = send(getter_name)
|
94
|
-
memo[constant] = value if value
|
95
|
-
memo
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
def keychain_item_data=(hash)
|
100
|
-
ATTRIBUTES.each do |getter_name, constant|
|
101
|
-
send("#{getter_name}=", hash[constant])
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
def dictionaryToSecItemFormat dictionaryToConvert
|
106
|
-
returnDictionary = NSMutableDictionary.dictionaryWithDictionary dictionaryToConvert
|
107
|
-
returnDictionary[KSecClass] = KSecClassGenericPassword
|
108
|
-
passwordString = dictionaryToConvert[KSecValueData]
|
109
|
-
|
110
|
-
returnDictionary[KSecValueData] = passwordString.dataUsingEncoding(NSUTF8StringEncoding)
|
111
|
-
returnDictionary
|
112
|
-
end
|
113
|
-
|
114
|
-
def secItemFormatToDictionary dictionaryToConvert
|
115
|
-
returnDictionary = NSMutableDictionary.dictionaryWithDictionary dictionaryToConvert
|
116
|
-
returnDictionary[KSecReturnData] = KCFBooleanTrue
|
117
|
-
returnDictionary[KSecClass] = KSecClassGenericPassword
|
118
|
-
|
119
|
-
passwordDataPtr = Pointer.new(:object)
|
120
|
-
|
121
|
-
result = SecItemCopyMatching(returnDictionary, passwordDataPtr)
|
122
|
-
if result == NoErr
|
123
|
-
returnDictionary.delete KSecReturnData
|
124
|
-
passwordData = passwordDataPtr[0]
|
125
|
-
password = NSString.alloc.initWithBytes(passwordData.bytes, length: passwordData.length, encoding: NSUTF8StringEncoding)
|
126
|
-
returnDictionary[KSecValueData] = password
|
127
|
-
else
|
128
|
-
raise KeychainReturnCodeException.new "Serious error, no matching item found in the keychain.", result
|
129
|
-
end
|
130
|
-
|
131
|
-
returnDictionary
|
132
|
-
end
|
133
|
-
|
134
|
-
def writeToKeychain
|
135
|
-
attributesPtr = Pointer.new(:object)
|
136
|
-
if SecItemCopyMatching(generic_password_query, attributesPtr) == NoErr
|
137
|
-
query = NSMutableDictionary.dictionaryWithDictionary attributesPtr[0]
|
138
|
-
|
139
|
-
query[KSecClass] = generic_password_query[KSecClass]
|
140
|
-
attributes_to_update = dictionaryToSecItemFormat keychain_item_data
|
141
|
-
attributes_to_update.delete_if { |key, value| !self.class.key_accepted? key }
|
142
|
-
|
143
|
-
if Device.simulator?
|
144
|
-
attributes_to_update.delete KSecAttrAccessGroup
|
145
|
-
end
|
146
|
-
|
147
|
-
result = SecItemUpdate(query, attributes_to_update)
|
148
|
-
unless result == NoErr
|
149
|
-
raise KeychainReturnCodeException.new "Couldn't update the Keychain Item.", result
|
150
|
-
end
|
151
|
-
else
|
152
|
-
result = SecItemAdd(dictionaryToSecItemFormat(keychain_item_data), nil)
|
153
|
-
unless result == NoErr
|
154
|
-
raise KeychainReturnCodeException.new "Couldn't add the Keychain Item.", result
|
155
|
-
end
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
end
|
160
|
-
end
|