boxen 2.7.1 → 2.7.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |gem|
4
4
  gem.name = "boxen"
5
- gem.version = "2.7.1"
5
+ gem.version = "2.7.2"
6
6
  gem.authors = ["John Barnette", "Will Farrington", "David Goodlad"]
7
7
  gem.email = ["jbarnette@github.com", "wfarr@github.com", "dgoodlad@github.com"]
8
8
  gem.description = "Manage Mac development boxes with love (and Puppet)."
@@ -16,7 +16,7 @@ Gem::Specification.new do |gem|
16
16
 
17
17
  gem.add_dependency "ansi", "~> 1.4"
18
18
  gem.add_dependency "hiera", "~> 1.0"
19
- gem.add_dependency "highline", "~> 1.6"
19
+ gem.add_dependency "highline", "~> 1.6.0"
20
20
  gem.add_dependency "json_pure", [">= 1.7.7", "< 2.0"]
21
21
  gem.add_dependency "librarian-puppet", "~> 1.0.0"
22
22
  gem.add_dependency "octokit", "~> 2.7", ">= 2.7.1"
@@ -1,6 +1,8 @@
1
1
  require "boxen/preflight"
2
2
  require "highline"
3
3
  require "octokit"
4
+ require "digest"
5
+ require "socket"
4
6
 
5
7
  # HACK: Unless this is `false`, HighLine has some really bizarre
6
8
  # problems with empty/expended streams at bizarre intervals.
@@ -68,12 +70,27 @@ class Boxen::Preflight::Creds < Boxen::Preflight
68
70
  fetch_login_and_password
69
71
  tokens = get_tokens
70
72
 
71
- unless auth = tokens.detect { |a| a.note == "Boxen" }
72
- auth = tmp_api.create_authorization \
73
- :note => "Boxen",
74
- :scopes => %w(repo user),
75
- :headers => headers
76
- end
73
+ # Boxen now supports the updated GitHub Authorizations API by using a unique
74
+ # `fingerprint` for each Boxen installation for a user. We delete any older
75
+ # authorization that does not make use of `fingerprint` so that the "legacy"
76
+ # authorization doesn't persist in the user's list of personal access
77
+ # tokens.
78
+ legacy_auth = tokens.detect { |a| a.note == "Boxen" && a.fingerprint == nil }
79
+ tmp_api.delete_authorization(legacy_auth.id, :headers => headers) if legacy_auth
80
+
81
+ # The updated GitHub authorizations API, in order to improve security, no
82
+ # longer returns a plaintext `token` for existing authorizations. So, if an
83
+ # authorization already exists for this machine we need to first delete it
84
+ # so that we can create a new one.
85
+ auth = tokens.detect { |a| a.note == note && a.fingerprint == fingerprint }
86
+ tmp_api.delete_authorization(auth.id, :headers => headers) if auth
87
+
88
+ auth = tmp_api.create_authorization(
89
+ :note => note,
90
+ :scopes => %w(repo user),
91
+ :fingerprint => fingerprint,
92
+ :headers => headers
93
+ )
77
94
 
78
95
  config.token = auth.token
79
96
 
@@ -105,4 +122,28 @@ class Boxen::Preflight::Creds < Boxen::Preflight
105
122
  warn "Oh, looks like you've provided your #{thing} as environmental variable..."
106
123
  found
107
124
  end
125
+
126
+ def fingerprint
127
+ @fingerprint ||= begin
128
+ # See Apple technical note TN1103, "Uniquely Identifying a Macintosh
129
+ # Computer."
130
+ serial_number_match_data = IO.popen(
131
+ ["ioreg", "-c", "IOPlatformExpertDevice", "-d", "2"]
132
+ ).read.match(/"IOPlatformSerialNumber" = "([[:alnum:]]+)"/)
133
+ if serial_number_match_data
134
+ # The fingerprint must be unique across all personal access tokens for a
135
+ # given user. We prefix the serial number with the application name to
136
+ # differentiate between any other personal access token that uses the
137
+ # Mac serial number for the fingerprint.
138
+ Digest::SHA256.hexdigest("Boxen: #{serial_number_match_data[1]}")
139
+ else
140
+ abort "Sorry, I was unable to obtain your Mac's serial number.",
141
+ "Boxen requires access to your Mac's serial number in order to generate a unique GitHub personal access token."
142
+ end
143
+ end
144
+ end
145
+
146
+ def note
147
+ @note ||= "Boxen: #{Socket.gethostname}"
148
+ end
108
149
  end
@@ -3,6 +3,9 @@ require 'boxen/config'
3
3
  require 'boxen/preflight/creds'
4
4
 
5
5
  class BoxenPreflightCredsTest < Boxen::Test
6
+ # Make a struct to use for stubbing out authorization objects.
7
+ Struct.new("Authorization", :id, :note, :fingerprint, :token)
8
+
6
9
  def setup
7
10
  @config = Boxen::Config.new do |c|
8
11
  c.user = 'mojombo'
@@ -77,4 +80,98 @@ class BoxenPreflightCredsTest < Boxen::Test
77
80
  assert_equal "l", @config.login
78
81
  assert_equal "p", preflight.instance_variable_get(:@password)
79
82
  end
83
+
84
+ def test_run_with_existing_token
85
+ preflight = Boxen::Preflight::Creds.new @config
86
+ note = "App1"
87
+ fingerprint = "Fingerprint1"
88
+ existing_token = Struct::Authorization.new(1, note, fingerprint, "Token1")
89
+
90
+ preflight.stubs(:fetch_login_and_password).returns("")
91
+ preflight.stubs(:get_tokens).returns([existing_token])
92
+ preflight.stubs(:note).returns(note)
93
+ preflight.stubs(:fingerprint).returns(fingerprint)
94
+ preflight.stubs(:ok?).returns(true)
95
+ preflight.tmp_api.expects(:delete_authorization).with(existing_token.id, :headers => {})
96
+ preflight.tmp_api.expects(:create_authorization).with(
97
+ :note => note,
98
+ :fingerprint => fingerprint,
99
+ :scopes => %w(repo user),
100
+ :headers => {}
101
+ ).returns(existing_token)
102
+
103
+ preflight.run
104
+ end
105
+
106
+ def test_run_with_no_existing_token
107
+ preflight = Boxen::Preflight::Creds.new @config
108
+ note = "App1"
109
+ fingerprint = "Fingerprint1"
110
+ new_token = Struct::Authorization.new(1, note, fingerprint, "Token1")
111
+
112
+ preflight.stubs(:fetch_login_and_password).returns("")
113
+ preflight.stubs(:get_tokens).returns([])
114
+ preflight.stubs(:note).returns(note)
115
+ preflight.stubs(:fingerprint).returns(fingerprint)
116
+ preflight.stubs(:ok?).returns(true)
117
+ preflight.tmp_api.expects(:delete_authorization).never
118
+ preflight.tmp_api.expects(:create_authorization).with(
119
+ :note => note,
120
+ :fingerprint => fingerprint,
121
+ :scopes => %w(repo user),
122
+ :headers => {}
123
+ ).returns(new_token)
124
+
125
+ preflight.run
126
+ end
127
+
128
+ def test_run_does_not_delete_unrelated_tokens
129
+ preflight = Boxen::Preflight::Creds.new @config
130
+ note = "App1"
131
+ fingerprint = "Fingerprint1"
132
+ new_token = Struct::Authorization.new(1, note, fingerprint, "Token1")
133
+ unrelated_token = Struct::Authorization.new(2, "App2", fingerprint, "Token2")
134
+ unrelated_token_with_fingerprint = Struct::Authorization.new(3, "App3", "Fingerprint3", "Token3")
135
+
136
+ preflight.stubs(:fetch_login_and_password).returns("")
137
+ preflight.stubs(:get_tokens).returns(
138
+ [unrelated_token, unrelated_token_with_fingerprint]
139
+ )
140
+ preflight.stubs(:note).returns(note)
141
+ preflight.stubs(:fingerprint).returns(fingerprint)
142
+ preflight.stubs(:ok?).returns(true)
143
+ preflight.tmp_api.expects(:delete_authorization).never
144
+ preflight.tmp_api.expects(:create_authorization).with(
145
+ :note => note,
146
+ :fingerprint => fingerprint,
147
+ :scopes => %w(repo user),
148
+ :headers => {}
149
+ ).returns(new_token)
150
+
151
+ preflight.run
152
+ end
153
+
154
+ def test_run_does_delete_legacy_token
155
+ preflight = Boxen::Preflight::Creds.new @config
156
+ note = "App1"
157
+ fingerprint = "Fingerprint1"
158
+ existing_token = Struct::Authorization.new(1, note, fingerprint, "Token1")
159
+ legacy_token = Struct::Authorization.new(2, "Boxen", nil, "Token2")
160
+
161
+ preflight.stubs(:fetch_login_and_password).returns("")
162
+ preflight.stubs(:get_tokens).returns([existing_token, legacy_token])
163
+ preflight.stubs(:note).returns(note)
164
+ preflight.stubs(:fingerprint).returns(fingerprint)
165
+ preflight.stubs(:ok?).returns(true)
166
+ preflight.tmp_api.expects(:delete_authorization).with(2, :headers => {})
167
+ preflight.tmp_api.expects(:delete_authorization).with(1, :headers => {})
168
+ preflight.tmp_api.expects(:create_authorization).with(
169
+ :note => note,
170
+ :fingerprint => fingerprint,
171
+ :scopes => %w(repo user),
172
+ :headers => {}
173
+ ).returns(existing_token)
174
+
175
+ preflight.run
176
+ end
80
177
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: boxen
3
3
  version: !ruby/object:Gem::Version
4
- hash: 17
4
+ hash: 23
5
5
  prerelease:
6
6
  segments:
7
7
  - 2
8
8
  - 7
9
- - 1
10
- version: 2.7.1
9
+ - 2
10
+ version: 2.7.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - John Barnette
@@ -17,7 +17,7 @@ autorequire:
17
17
  bindir: bin
18
18
  cert_chain: []
19
19
 
20
- date: 2015-03-10 00:00:00 +00:00
20
+ date: 2015-05-18 00:00:00 +01:00
21
21
  default_executable:
22
22
  dependencies:
23
23
  - !ruby/object:Gem::Dependency
@@ -58,11 +58,12 @@ dependencies:
58
58
  requirements:
59
59
  - - ~>
60
60
  - !ruby/object:Gem::Version
61
- hash: 3
61
+ hash: 15
62
62
  segments:
63
63
  - 1
64
64
  - 6
65
- version: "1.6"
65
+ - 0
66
+ version: 1.6.0
66
67
  type: :runtime
67
68
  version_requirements: *id003
68
69
  - !ruby/object:Gem::Dependency