boxen 0.8.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -8,3 +8,4 @@
8
8
  /boxen-*.gem
9
9
  /log
10
10
  /puppet
11
+ /script/Boxen.dSYM
data/boxen.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |gem|
4
4
  gem.name = "boxen"
5
- gem.version = "0.8.1"
5
+ gem.version = "1.0.0"
6
6
  gem.authors = ["John Barnette", "Will Farrington"]
7
7
  gem.email = ["jbarnette@github.com", "wfarr@github.com"]
8
8
  gem.description = "Manage Mac development boxes with love (and Puppet)."
data/lib/boxen/config.rb CHANGED
@@ -1,7 +1,9 @@
1
+ require "boxen/keychain"
2
+ require "boxen/project"
1
3
  require "fileutils"
2
4
  require "json"
3
5
  require "octokit"
4
- require "boxen/project"
6
+ require "shellwords"
5
7
 
6
8
  module Boxen
7
9
 
@@ -9,13 +11,6 @@ module Boxen
9
11
  # args, environment variables, config files, or the keychain.
10
12
 
11
13
  class Config
12
-
13
- # The service name to use when loading/saving config in the Keychain.
14
-
15
- KEYCHAIN_SERVICE = "Boxen"
16
-
17
- # Load config. Yields config if `block` is given.
18
-
19
14
  def self.load(&block)
20
15
  new do |config|
21
16
  file = "#{config.homedir}/config/boxen/defaults.json"
@@ -30,13 +25,9 @@ module Boxen
30
25
  end
31
26
  end
32
27
 
33
- cmd = "/usr/bin/security find-generic-password " +
34
- "-a #{config.user} -s '#{KEYCHAIN_SERVICE}' -w 2>/dev/null"
35
-
36
- password = `#{cmd}`.strip
37
- password = nil unless $?.success?
38
-
39
- config.password = password
28
+ keychain = Boxen::Keychain.new config.user
29
+ config.password = keychain.password
30
+ config.token = keychain.token
40
31
 
41
32
  yield config if block_given?
42
33
  end
@@ -56,7 +47,6 @@ module Boxen
56
47
  :repodir => config.repodir,
57
48
  :reponame => config.reponame,
58
49
  :srcdir => config.srcdir,
59
- :token => config.token,
60
50
  :user => config.user
61
51
  }
62
52
 
@@ -67,12 +57,9 @@ module Boxen
67
57
  f.write JSON.generate Hash[attrs.reject { |k, v| v.nil? }]
68
58
  end
69
59
 
70
- cmd = ["security", "add-generic-password",
71
- "-a", config.user, "-s", KEYCHAIN_SERVICE, "-U", "-w", config.password]
72
-
73
- unless system *cmd
74
- raise Boxen::Error, "Can't save config in the Keychain."
75
- end
60
+ keychain = Boxen::Keychain.new config.user
61
+ keychain.password = config.password
62
+ keychain.token = config.token
76
63
 
77
64
  config
78
65
  end
@@ -0,0 +1,64 @@
1
+ require "shellwords"
2
+
3
+ module Boxen
4
+ class Keychain
5
+
6
+ # The keychain proxy we use to provide isolation and a friendly
7
+ # message in security prompts.
8
+
9
+ HELPER = File.expand_path "../../../script/Boxen", __FILE__
10
+
11
+ # The service name to use when loading/saving passwords.
12
+
13
+ PASSWORD_SERVICE = "GitHub Password"
14
+
15
+ # The service name to use when loading/saving API keys.
16
+
17
+ TOKEN_SERVICE = "GitHub API Token"
18
+
19
+ def initialize(login)
20
+ @login = login
21
+ end
22
+
23
+ def password
24
+ get PASSWORD_SERVICE
25
+ end
26
+
27
+ def password=(password)
28
+ set PASSWORD_SERVICE, password
29
+ end
30
+
31
+ def token
32
+ get TOKEN_SERVICE
33
+ end
34
+
35
+ def token=(token)
36
+ set TOKEN_SERVICE, token
37
+ end
38
+
39
+ protected
40
+
41
+ attr_reader :login
42
+
43
+ def get(service)
44
+ cmd = shellescape(HELPER, service, login)
45
+
46
+ result = `#{cmd}`.strip
47
+ $?.success? ? result : nil
48
+ end
49
+
50
+ def set(service, password)
51
+ cmd = shellescape(HELPER, service, login, password)
52
+
53
+ unless system *cmd
54
+ raise Boxen::Error, "Can't save #{service} in the keychain."
55
+ end
56
+
57
+ password
58
+ end
59
+
60
+ def shellescape(*args)
61
+ args.map { |s| Shellwords.shellescape s }.join " "
62
+ end
63
+ end
64
+ end
data/script/Boxen ADDED
Binary file
@@ -0,0 +1,6 @@
1
+ #!/bin/sh
2
+
3
+ set -e
4
+
5
+ cd $(dirname "$0")/..
6
+ cc -g -O2 -Wall -framework Security -o script/Boxen src/keychain-helper.c
@@ -0,0 +1,67 @@
1
+ #include <stdio.h>
2
+ #include <stdlib.h>
3
+ #include <Security/Security.h>
4
+
5
+ int key_exists_p(
6
+ const char *service,
7
+ const char *login,
8
+ SecKeychainItemRef *item
9
+ ) {
10
+ void *buf;
11
+ UInt32 len;
12
+
13
+ OSStatus ret = SecKeychainFindGenericPassword(
14
+ NULL, strlen(service), service, strlen(login), login, &len, &buf, item
15
+ );
16
+
17
+ if (ret == 0) {
18
+ return 0;
19
+ } else {
20
+ fprintf(stderr, "Boxen Keychain Helper: Encountered error code: %d\n", ret);
21
+ return ret;
22
+ }
23
+ }
24
+
25
+ int main(int argc, char **argv) {
26
+ if ((argc < 3) || (argc > 4)) {
27
+ printf("Usage: %s <service> <account> [<password>]\n", argv[0]);
28
+ return 1;
29
+ }
30
+
31
+ const char *service = argv[1];
32
+ const char *login = argv[2];
33
+ const char *password = argc == 4 ? argv[3] : NULL;
34
+
35
+ void *buf;
36
+ UInt32 len;
37
+ SecKeychainItemRef item;
38
+
39
+ if (password != NULL) {
40
+ if (key_exists_p(service, login, &item) == 0) {
41
+ SecKeychainItemDelete(item);
42
+ }
43
+
44
+ OSStatus create_key = SecKeychainAddGenericPassword(
45
+ NULL, strlen(service), service, strlen(login), login, strlen(password),
46
+ password, &item
47
+ );
48
+
49
+ if (create_key != 0) {
50
+ fprintf(stderr, "Boxen Keychain Helper: Encountered error code: %d\n", create_key);
51
+ return 1;
52
+ }
53
+
54
+ } else {
55
+ OSStatus find_key = SecKeychainFindGenericPassword(
56
+ NULL, strlen(service), service, strlen(login), login, &len, &buf, &item);
57
+
58
+ if (find_key != 0) {
59
+ fprintf(stderr, "Boxen Keychain Helper: Encountered error code: %d\n", find_key);
60
+ return 1;
61
+ }
62
+
63
+ fwrite(buf, 1, len, stdout);
64
+ }
65
+
66
+ return 0;
67
+ }
@@ -0,0 +1,28 @@
1
+ require "boxen/test"
2
+ require "boxen/keychain"
3
+
4
+ class BoxenKeychainTest < Boxen::Test
5
+ def setup
6
+ @keychain = Boxen::Keychain.new('test')
7
+ end
8
+
9
+ def test_get_password
10
+ @keychain.expects(:get).with('GitHub Password').returns('foobar')
11
+ assert_equal 'foobar', @keychain.password
12
+ end
13
+
14
+ def test_set_password
15
+ @keychain.expects(:set).with('GitHub Password', 'foobar').returns('foobar')
16
+ assert_equal 'foobar', @keychain.password=('foobar')
17
+ end
18
+
19
+ def test_get_token
20
+ @keychain.expects(:get).with('GitHub API Token').returns('foobar')
21
+ assert_equal 'foobar', @keychain.token
22
+ end
23
+
24
+ def test_set_token
25
+ @keychain.expects(:set).with('GitHub API Token', 'foobar').returns('foobar')
26
+ assert_equal 'foobar', @keychain.token=('foobar')
27
+ end
28
+ end
metadata CHANGED
@@ -3,10 +3,10 @@ name: boxen
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
- - 0
7
- - 8
8
6
  - 1
9
- version: 0.8.1
7
+ - 0
8
+ - 0
9
+ version: 1.0.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - John Barnette
@@ -170,6 +170,7 @@ files:
170
170
  - lib/boxen/hook.rb
171
171
  - lib/boxen/hook/github_issue.rb
172
172
  - lib/boxen/hook/web.rb
173
+ - lib/boxen/keychain.rb
173
174
  - lib/boxen/postflight.rb
174
175
  - lib/boxen/postflight/active.rb
175
176
  - lib/boxen/postflight/env.rb
@@ -189,9 +190,12 @@ files:
189
190
  - lib/boxen/util.rb
190
191
  - lib/facter/boxen.rb
191
192
  - lib/system_timer.rb
193
+ - script/Boxen
192
194
  - script/bootstrap
195
+ - script/build-keychain-helper
193
196
  - script/release
194
197
  - script/tests
198
+ - src/keychain-helper.c
195
199
  - test/boxen/test.rb
196
200
  - test/boxen_check_test.rb
197
201
  - test/boxen_checkout_test.rb
@@ -201,6 +205,7 @@ files:
201
205
  - test/boxen_flags_test.rb
202
206
  - test/boxen_hook_github_issue_test.rb
203
207
  - test/boxen_hook_web_test.rb
208
+ - test/boxen_keychain_test.rb
204
209
  - test/boxen_postflight_active_test.rb
205
210
  - test/boxen_postflight_env_test.rb
206
211
  - test/boxen_preflight_etc_my_cnf_test.rb
@@ -254,6 +259,7 @@ test_files:
254
259
  - test/boxen_flags_test.rb
255
260
  - test/boxen_hook_github_issue_test.rb
256
261
  - test/boxen_hook_web_test.rb
262
+ - test/boxen_keychain_test.rb
257
263
  - test/boxen_postflight_active_test.rb
258
264
  - test/boxen_postflight_env_test.rb
259
265
  - test/boxen_preflight_etc_my_cnf_test.rb