ruby-keychain 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,17 @@
1
+ # @markup markdown
2
+
1
3
  A set of ruby bindings for the OS X keychain, written using ffi
2
4
 
5
+ Installation
6
+ ============
7
+
8
+ gem install ruby-keychain
9
+
10
+ or in your gemfile,
11
+
12
+ gem 'ruby-keychain', :require => 'keychain'
13
+
14
+
3
15
  Introduction
4
16
  ============
5
17
 
@@ -13,12 +25,12 @@ Most operations will act on either the default keychain, or the default keychain
13
25
 
14
26
  Keychain.default #the default keychain, usually /Users/<username>/Library/Keychains/<username>.keychain
15
27
  Keychain.open(path) #opens a keychain file
16
- Keychain.create(path, password) #creates a new keychain at the specified path
17
-
28
+ Keychain.create(path, password) # creates a new keychain at the specified path, with the specified password
29
+ # omit the password to make the keychain prompt the user
18
30
 
19
31
 
20
32
  Searching for Keychain Items
21
- =========
33
+ =============================
22
34
 
23
35
  The top level constant `Keychain` as well as individual keychain objects have two methods `internet_passwords` and `generic_passwords` that return scope like objects. You can do
24
36
 
data/lib/keychain.rb CHANGED
@@ -14,17 +14,22 @@ module Keychain
14
14
  # See https://developer.apple.com/library/mac/documentation/security/Reference/keychainservices/Reference/reference.html#//apple_ref/c/func/SecKeychainCreate
15
15
  # @param [String] path The path to the keychain file to create
16
16
  # If it is not absolute it is interpreted relative to ~/Library/Keychains
17
- # @param [String] password The password to use for the keychain
17
+ # @param [optional, String] password The password to use for the keychain. if not supplied, the user will be prompted for a password
18
18
  # @return [Keychain::Keychain] a keychain object representing the newly created keychain
19
19
 
20
- def create(path, password)
21
- password = password.encode(Encoding::UTF_8)
20
+ def create(path, password=nil)
22
21
  path = path.encode(Encoding::UTF_8)
23
-
24
22
  out_buffer = FFI::MemoryPointer.new(:pointer)
25
- status = Sec.SecKeychainCreate(path, password.bytesize, FFI::MemoryPointer.from_string(password), 0,
23
+
24
+ if password
25
+ password = password.encode(Encoding::UTF_8)
26
+ status = Sec.SecKeychainCreate(path, password.bytesize, FFI::MemoryPointer.from_string(password), 0,
26
27
  nil, out_buffer)
27
28
 
29
+ else
30
+ status = Sec.SecKeychainCreate(path, 0, nil, 1, nil, out_buffer)
31
+ end
32
+
28
33
  Sec.check_osstatus(status)
29
34
  Keychain.new(out_buffer.read_pointer).release_on_gc
30
35
  end
@@ -68,5 +73,26 @@ module Keychain
68
73
  def generic_passwords
69
74
  Scope.new(Sec::Classes::GENERIC)
70
75
  end
76
+
77
+ # sets whether user interaction is allowed
78
+ # If false then operations that would require user interaction (for example prompting the user for a password to unlock a keychain)
79
+ # will raise InteractionNotAllowedError
80
+ # @param [Boolean] value
81
+ def user_interaction_allowed= value
82
+ status = Sec.SecKeychainSetUserInteractionAllowed( value ? 1 : 0)
83
+ Sec.check_osstatus(status)
84
+ value
85
+ end
86
+
87
+ # Returns whether user interaction is allowed
88
+ # If false then operations that would require user interaction (for example prompting the user for a password to unlock a keychain)
89
+ # will raise InteractionNotAllowedError
90
+ # @return whether interaction is allowed
91
+ def user_interaction_allowed?
92
+ out_buffer = FFI::MemoryPointer.new(:uchar)
93
+ status = Sec.SecKeychainGetUserInteractionAllowed(out_buffer)
94
+ Sec.check_osstatus(status)
95
+ out_buffer.read_uchar.nonzero?
96
+ end
71
97
  end
72
98
  end
@@ -31,4 +31,7 @@ class Keychain::AuthFailedError < Keychain::Error; end
31
31
  class Keychain::UserCancelledError < Keychain::Error; end
32
32
  # Raised when an action fails because the underlying keychain
33
33
  # does not exist
34
- class Keychain::NoSuchKeychainError < Keychain::Error; end
34
+ class Keychain::NoSuchKeychainError < Keychain::Error; end
35
+ # Raised when an action would rewuire user interaction but user interaction
36
+ # is not allowed. See Keychain.user_interaction_allowed=
37
+ class Keychain::InteractionNotAllowedError < Keychain::Error; end
@@ -24,6 +24,9 @@ module Sec
24
24
 
25
25
  attach_function 'SecKeychainGetStatus', [:keychainref, :pointer], :osstatus
26
26
 
27
+ attach_function 'SecKeychainSetUserInteractionAllowed', [:uchar], :osstatus
28
+
29
+ attach_function 'SecKeychainGetUserInteractionAllowed', [:pointer], :osstatus
27
30
  enum :keychainStatus, [
28
31
  :kSecUnlockStateStatus, 1,
29
32
  :kSecReadPermStatus, 2,
@@ -5,7 +5,7 @@ class Keychain::Scope
5
5
  def initialize(kind, keychain=nil)
6
6
  @kind = kind
7
7
  @limit = nil
8
- @keychains = [keychain]
8
+ @keychains = [keychain].compact
9
9
  @conditions = {}
10
10
  end
11
11
 
data/lib/keychain/sec.rb CHANGED
@@ -11,7 +11,8 @@ module Sec
11
11
  :errSecDuplicateItem, -25299,
12
12
  :errSecAuthFailed, -25293,
13
13
  :errSecNoSuchKeychain, -25294,
14
- :errCancelled, -128
14
+ :errCancelled, -128,
15
+ :errSecInteractionNotAllowed, -25308
15
16
  ]
16
17
 
17
18
  attach_variable 'kSecClassInternetPassword', :pointer
@@ -155,6 +156,8 @@ module Sec
155
156
  raise Keychain::AuthFailedError.new(result)
156
157
  when Sec.enum_value(:errSecNoSuchKeychain)
157
158
  raise Keychain::NoSuchKeychainError.new(result)
159
+ when Sec.enum_value(:errSecInteractionNotAllowed)
160
+ raise Keychain::InteractionNotAllowedError.new(result)
158
161
  else
159
162
  raise Keychain::Error.new(result)
160
163
  end
@@ -1,4 +1,4 @@
1
1
  module Keychain
2
2
  # The current version string
3
- VERSION = '0.1.0'
3
+ VERSION = '0.1.1'
4
4
  end
@@ -1,6 +1,20 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Keychain do
4
+
5
+ describe 'user interaction' do
6
+ it 'should be true by default' do
7
+ Keychain.user_interaction_allowed?.should be_true
8
+ end
9
+
10
+ it 'should be changeable' do
11
+ Keychain.user_interaction_allowed = false
12
+ Keychain.user_interaction_allowed?.should be_false
13
+ Keychain.user_interaction_allowed = true
14
+ Keychain.user_interaction_allowed?.should be_true
15
+ end
16
+ end
17
+
4
18
  describe 'default' do
5
19
  it "should return the login keychain" do
6
20
  Keychain.default.path.should == File.expand_path(File.join(ENV['HOME'], 'Library','Keychains', 'login.keychain'))
@@ -14,7 +28,7 @@ describe Keychain do
14
28
  end
15
29
  end
16
30
 
17
- describe 'new' do
31
+ describe 'create' do
18
32
  it 'should create the keychain' do
19
33
  begin
20
34
  keychain = Keychain.create(File.join(Dir.tmpdir, "other_keychain_spec_#{Time.now.to_i}_#{Time.now.usec}_#{rand(1000)}.keychain"),
@@ -24,6 +38,14 @@ describe Keychain do
24
38
  keychain.delete
25
39
  end
26
40
  end
41
+
42
+ context 'no password supplied' do
43
+ #we have to stub this out as it would trigger a dialog box prompting for a password
44
+ it 'should create a keychain by prompting the user' do
45
+ Sec.should_receive('SecKeychainCreate').with('akeychain', 0, nil, 1, nil,kind_of(FFI::Pointer)).and_return(0)
46
+ Keychain.create('akeychain')
47
+ end
48
+ end
27
49
  end
28
50
 
29
51
  describe 'exists?' do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-keychain
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-03 00:00:00.000000000 Z
12
+ date: 2012-11-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: ffi
@@ -124,7 +124,7 @@ files:
124
124
  - spec/keychain_item_spec.rb
125
125
  - spec/keychain_spec.rb
126
126
  - spec/spec_helper.rb
127
- - README
127
+ - README.markdown
128
128
  - LICENSE
129
129
  homepage: http://github.com/fcheung/keychain
130
130
  licenses: