gas 0.1.6 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -4,7 +4,7 @@ h1. gas - manage your git author accounts
4
4
 
5
5
  h2. Description
6
6
 
7
- Gas is a utility to keep track of your git authors. Add them to gas and switch at any time. Great if you use one author at work and one at home for instance.
7
+ Gas is a mighty utility that helps you keep track of your multiple git authors. Add them to gas and switch between them instantly! Great if you use one author at work and one at home or if you are doing pair programming.
8
8
 
9
9
  h2. Installation
10
10
 
@@ -20,19 +20,19 @@ $ rake install
20
20
 
21
21
  h2. Running
22
22
 
23
- Default task is to list authors
23
+ The default task is to list authors
24
24
 
25
25
  bc. $ gas
26
26
 
27
27
  bc. $ gas list
28
28
 
29
- This lists the authors that are set up.
29
+ This lists the authors that are set up in the ~/.gas/gas.authors file.
30
30
 
31
31
  You can import your current user by giving it a nickname
32
32
 
33
33
  bc. $ gas import current_user
34
34
 
35
- To add a author use, add
35
+ To add an author use, add
36
36
 
37
37
  bc. $ gas add walle "Fredrik Wallgren" fredrik.wallgren@gmail.com
38
38
 
@@ -44,4 +44,10 @@ To delete it again use, delete
44
44
 
45
45
  bc. $ gas delete walle
46
46
 
47
+ Gas can also juggle your id_rsa ssh keys, which is helpful for uploading to github between multiple accounts. Indespensible for teachers who need to instruct their students on how to use github!
48
+
49
+ bc. $ gas add Njax NotarySojac no@mail.com
50
+ Do you want gas to handle switching rsa keys for this user?
51
+ [y/n]
52
+
47
53
  View @gas -h@ to see all options.
data/bin/gas CHANGED
@@ -36,6 +36,11 @@ class GasRunner < Thor
36
36
  def delete(nickname)
37
37
  Gas.delete nickname
38
38
  end
39
+
40
+ desc "ssh", "Creates a new ssh key for an existing gas author"
41
+ def ssh(nickname=nil)
42
+ Gas.ssh nickname
43
+ end
39
44
 
40
45
  desc "version", "Prints Gas's version"
41
46
  def version
data/lib/gas.rb CHANGED
@@ -1,8 +1,17 @@
1
+ GAS_DIRECTORY = File.expand_path('~/.gas')
2
+ SSH_DIRECTORY = File.expand_path('~/.ssh')
3
+ GITHUB_SERVER = 'api.github.com'
4
+
5
+
6
+ require 'sshkey' #external
7
+
1
8
  require 'gas/version'
9
+ require 'gas/ssh'
2
10
  require 'gas/user'
3
11
  require 'gas/config'
4
12
  require 'gas/gitconfig'
5
13
 
14
+
6
15
  module Gas
7
16
 
8
17
  @config = Config.new
@@ -10,10 +19,13 @@ module Gas
10
19
 
11
20
  # Lists all authors
12
21
  def self.list
22
+ puts
13
23
  puts 'Available users:'
24
+ puts
14
25
  puts @config
26
+ puts
15
27
 
16
- self.show
28
+ # self.show # XXX: get rid of
17
29
  end
18
30
 
19
31
  # Shows the current user
@@ -33,9 +45,9 @@ module Gas
33
45
  def self.use(nickname)
34
46
  self.no_user? nickname
35
47
  user = @config[nickname]
36
-
37
- @gitconfig.change_user user.name, user.email
38
-
48
+
49
+ @gitconfig.change_user user # daring change made here! Heads up Walle
50
+
39
51
  self.show
40
52
  end
41
53
 
@@ -44,14 +56,54 @@ module Gas
44
56
  # @param [String] name The name of the author
45
57
  # @param [String] email The email of the author
46
58
  def self.add(nickname, name, email)
47
- self.has_user? nickname
59
+ return false if self.has_user?(nickname)
48
60
  user = User.new name, email, nickname
49
61
  @config.add user
50
62
  @config.save!
51
-
52
- puts 'Added author'
63
+
64
+ using_ssh = Ssh.setup_ssh_keys user
65
+
66
+ Ssh.upload_public_key_to_github(user) if using_ssh
67
+
68
+ puts 'Added new author'
53
69
  puts user
54
70
  end
71
+
72
+
73
+ # Adds an ssh key for the specified user
74
+ def self.ssh(nickname)
75
+ if nickname.nil?
76
+ puts "Oh, so you'd like an elaborate explanation on how ssh key juggling works? Well pull up a chair!"
77
+ puts
78
+ puts "Gas can juggle ssh keys for you. It works best in a unix based environment (so at least use git bash or cygwin on a windows platform)."
79
+ puts "You will be prompted if you would like to handle SSH keys when you create a new user."
80
+ puts "If you are a long time user of gas, you can add ssh to an author by the command..."
81
+ puts "\$ gas ssh NICKNAME"
82
+ puts
83
+ puts "Your ssh keys will be stored in ~/.gas/NICKNAME_id_rsa and automatically copied to ~/.ssh/id_rsa when you use the command..."
84
+ puts "\$ gas use NICKNAME"
85
+ puts "If ~/.ssh/id_rsa already exists, you will be prompted UNLESS that rsa file is already backed up in the .gas directory (I'm so sneaky, huh?)"
86
+ puts
87
+ puts "The unix command ssh-add is used in order to link up your rsa keys when you attempt to make an ssh connection (git push uses ssh keys of course)"
88
+ puts
89
+ puts "The ssh feature of gas offers you and the world ease of use, and even marginally enhanced privacy against corporate databases. Did you know that IBM built one of the first automated database systems? These ancient database machines (called tabulators) were used to facilitate the holocaust =("
90
+ else
91
+ user = @config[nickname]
92
+
93
+
94
+ # Prompt Remake this user's ssh keys?
95
+
96
+ # check for ssh keys
97
+ if !Ssh.corresponding_rsa_files_exist?(nickname)
98
+ Ssh.setup_ssh_keys user
99
+ Ssh.upload_public_key_to_github user
100
+ else
101
+ Ssh.setup_ssh_keys user
102
+ Ssh.upload_public_key_to_github user
103
+ end
104
+ end
105
+ end
106
+
55
107
 
56
108
  # Imports current user from .gitconfig to .gas
57
109
  # @param [String] nickname The nickname to give to the new user
@@ -72,14 +124,24 @@ module Gas
72
124
  end
73
125
  end
74
126
 
75
- # Deletes a author from the config using nickname
127
+ # Deletes an author from the config using nickname
76
128
  # @param [String] nickname The nickname of the author
77
129
  def self.delete(nickname)
78
- self.no_user? nickname
130
+
131
+ return false unless self.no_user? nickname # I re-engineered this section so I could use Gas.delete in a test even when that author didn't exist
132
+ # TODO: The name no_user? is now very confusing. It should be changed to something like "is_user?" now maybe?
133
+
134
+ Ssh.delete nickname # XXX: there are 2 calls to this in the method!
135
+
136
+
137
+ # exit
79
138
  @config.delete nickname
80
139
  @config.save!
140
+
141
+ #Ssh.delete nickname # TODO: delete this duplicate after...
81
142
 
82
143
  puts "Deleted author #{nickname}"
144
+ return true
83
145
  end
84
146
 
85
147
  # Prints the current version
@@ -92,17 +154,19 @@ module Gas
92
154
  def self.no_user?(nickname)
93
155
  if !@config.exists? nickname
94
156
  puts "Nickname #{nickname} does not exist"
95
- exit
157
+ return false
96
158
  end
159
+ return true
97
160
  end
98
161
 
99
162
  # Checks if the user exists and gives error and exit if so
100
163
  # @param [String] nickname
101
164
  def self.has_user?(nickname)
102
165
  if @config.exists? nickname
103
- puts "Nickname #{nickname} does already exist"
104
- exit
166
+ puts "Nickname #{nickname} already exists"
167
+ return true
105
168
  end
169
+ return false
106
170
  end
107
171
 
108
172
  end
@@ -6,15 +6,42 @@ module Gas
6
6
  class Config
7
7
  attr_reader :users
8
8
 
9
+ # This function checks for a ~/.gas FILE and if it exists, it puts it into memory and deletes it from the HDD
10
+ # then it creates the ~/.gas FOLDER and saves the old .gas file as ~/git.conf
11
+ #
12
+ def migrate_to_gas_dir!
13
+ old_config_file = File.expand_path('~/.gas')
14
+ config_dir = File.expand_path('~/.gas')
15
+ new_config_file = File.expand_path('~/.gas') + "/gas.authors"
16
+
17
+ if File.file? old_config_file
18
+ file = File.open(old_config_file, "rb")
19
+ contents = file.read
20
+ file.close
21
+
22
+ File.delete old_config_file
23
+
24
+ Dir::mkdir(config_dir)
25
+
26
+ file = File.new(new_config_file, "w")
27
+ file.puts contents
28
+ file.close
29
+ end
30
+ end
31
+
32
+
9
33
  # Initializes the object. If no users are supplied we look for a config file, if none then create it, and parse it to load users
10
34
  # @param [Array<User>] users The override users
11
35
  # @param [String] config The override config
12
36
  def initialize(users = nil, config = nil)
13
- @config_file = File.expand_path('~/.gas')
37
+ migrate_to_gas_dir! # Migrates old users to the new configuration file location, how thoughtful of me, I know
38
+ @config_file = File.expand_path('~/.gas/gas.authors')
39
+ @gas_dir = File.expand_path('~/.gas')
14
40
  @config = ''
15
41
 
16
42
  if config.nil?
17
43
  if !File.exists? @config_file
44
+ Dir::mkdir(@gas_dir)
18
45
  FileUtils.touch @config_file
19
46
  end
20
47
 
@@ -87,9 +114,34 @@ module Gas
87
114
  end
88
115
  end
89
116
 
117
+
90
118
  # Override to_s to output correct format
91
119
  def to_s
92
- @users.join("\n")
120
+ gc = Gitconfig.new
121
+
122
+ users = @users.map do |user|
123
+ if is_current_user(gc.current_user_object[:name], gc.current_user_object[:email], user.to_s)
124
+ " ==>" + user.to_s[5,user.to_s.length]
125
+ else
126
+ user.to_s
127
+ end
128
+ end.join("\n")
129
+
130
+ return users
131
+ end
132
+
133
+
134
+ # Scans the @users (a string containing info formatted identical to the gas.author file)
135
+ # ...and checks to see if it's name and email match what you're looking for
136
+ def is_current_user(name, email, object)
137
+ object.scan(/\[(.+)\]\s+name = (.+)\s+email = (.+)/) do |nicknamec, namec, emailc|
138
+ if namec == name and emailc == email
139
+ # check if ssh is active
140
+ # TODO: Check if its SSH key is setup, and indicate SSH ACTIVE
141
+ return true
142
+ end
143
+ end
144
+ return false # could not get a current user's nickname
93
145
  end
94
146
 
95
147
  end
@@ -2,7 +2,7 @@ module Gas
2
2
 
3
3
  # Class that class that interacts with the git config
4
4
  class Gitconfig
5
-
5
+ @@nickname = ''
6
6
  # Parse out the current user from the gitconfig
7
7
  # @param [String] gitconfig The git configuration
8
8
  # @return [User] The current user or nil if not present
@@ -12,15 +12,33 @@ module Gas
12
12
 
13
13
  return nil if name.nil? && email.nil?
14
14
 
15
- User.new name.delete("\n"), email.delete("\n") # git cli returns the name and email with \n at the end
15
+ User.new name.delete("\n"), email.delete("\n"), @@nickname # git cli returns the name and email with \n at the end
16
+ end
17
+
18
+ # Get current user
19
+ def current_user_object
20
+ name = `git config --global --get user.name`
21
+ email = `git config --global --get user.email`
22
+
23
+ return nil if name.nil? && email.nil?
24
+
25
+ return {:name => name.strip, :email => email.strip}
16
26
  end
17
27
 
18
28
  # Changes the user
19
29
  # @param [String] name The new name
20
30
  # @param [String] email The new email
21
- def change_user(name, email)
31
+ def change_user(user)
32
+ nickname = user.nickname
33
+ @@nickname = nickname # maybe we should make nickname a class variable?
34
+ name = user.name
35
+ email = user.email
36
+
22
37
  `git config --global user.name "#{name}"`
23
38
  `git config --global user.email "#{email}"`
39
+
40
+ # confirm that this user has an ssh and if so, swap it in safely
41
+ Ssh.swap_in_rsa nickname
24
42
  end
25
43
 
26
44
  end
@@ -0,0 +1,630 @@
1
+ module Gas
2
+
3
+ class Ssh
4
+ require 'highline/import'
5
+ require 'net/https'
6
+ require 'json'
7
+
8
+
9
+ # If the user says 'f', the system will
10
+ # report that there isn't an id_rsa already in gas. This causes a new key to overwrite automatically down the road.
11
+ # This is for checking if a .gas/rsa file already exists for a nickname which is being registered
12
+ # If the rsa exists, then we're goona need to ask if we should use it, or if we should delete it
13
+ #
14
+ # Returns true to indicate that the user would like to use the rsa file already in .gas ()
15
+ # Returns false when there is no naming conflicts.
16
+ def self.user_wants_to_use_key_already_in_gas?
17
+ if corresponding_rsa_files_exist?
18
+ puts "Gas has detected a key in its archive directory ~/.gas/#{@uid}_id_rsa. Should gas use this key or overwrite this key with a brand new one?"
19
+ puts "Keep current key? [y/n]"
20
+
21
+ while true
22
+ keep_current_file = STDIN.gets.strip
23
+
24
+ case keep_current_file
25
+
26
+ when "y"
27
+ return true # keep the files already in .gas, skip making key.
28
+ when "n"
29
+ return false
30
+ else
31
+ puts "please respond 'y' or 'n'"
32
+ end
33
+ end
34
+
35
+ else # no need to do anything if files don't exist
36
+ return false
37
+ end
38
+ end
39
+
40
+
41
+ def self.corresponding_rsa_files_exist?(nickname = '')
42
+ nickname = @uid if nickname == ''
43
+ return true if File.exists? "#{GAS_DIRECTORY}/#{nickname}_id_rsa" and File.exists? "#{GAS_DIRECTORY}/#{nickname}_id_rsa.pub"
44
+ false
45
+ end
46
+
47
+
48
+ # Copies a key pair from ~/.ssh to .gas/Nickname*
49
+ def self.use_current_rsa_files_for_this_user(test = nil)
50
+ @uid = test unless test.nil?
51
+ cmd_result = `cp ~/.ssh/id_rsa ~/.gas/#{@uid}_id_rsa`
52
+ cmd_result = `cp ~/.ssh/id_rsa.pub ~/.gas/#{@uid}_id_rsa.pub`
53
+ return true
54
+ end
55
+
56
+
57
+ def self.ssh_dir_contains_rsa?
58
+ return true if File.exists?(SSH_DIRECTORY + "/id_rsa") or File.exists?(SSH_DIRECTORY + "/id_rsa.pub")
59
+ return false
60
+ end
61
+
62
+
63
+
64
+ # Checks if the ~/.ssh directroy contains id_rsa and id_rsa.pub
65
+ # if it does, it asks the user if they would like to use that as their ssh key, instead of generating a new key pair.
66
+ #
67
+ def self.user_wants_to_use_key_already_in_ssh?
68
+ return false unless ssh_dir_contains_rsa?
69
+
70
+ #puts "Gas has detected that an ~/.ssh/id_rsa file already exists. Would you like to use this as your ssh key to connect with github? Otherwise a new key will be generated and stored in ~/.gas (no overwrite concerns until you 'gas use nickname')"
71
+ puts "Generate a brand new ssh key pair? (Choose 'n' to use key in ~/.ssh/id_rsa)"
72
+ puts "Default: 'y'"
73
+ puts "[Y/n]"
74
+
75
+ while true
76
+ generate_new_rsa = STDIN.gets.strip.downcase
77
+ case generate_new_rsa
78
+ when "y", ""
79
+ return false
80
+ when "n"
81
+ return true # return true if we aren't generating a new key
82
+ else
83
+ puts "plz answer 'y' or 'n'"
84
+ end
85
+ end
86
+ end
87
+
88
+
89
+
90
+ # Generates a new sshkey putting it in ~/.gas/nickname_id_rsa
91
+ # This function can get a little tricky. It's best to strip off the comment here,
92
+ # because github API doesn't retain the comment...
93
+ def self.generate_new_rsa_keys_in_gas_dir
94
+ puts "Generating new ssh key..."
95
+ # TODO: Prompt user if they'd like to use a more secure password if physical security to their computer is not possible (dumb imo)... Unless we do that thing where we store keys on github!
96
+
97
+ # Old ssh key method (relies on unix environment)
98
+ # puts `ssh-keygen -f ~/.gas/#{@uid}_id_rsa -t rsa -C "#{@email}" -N ""` # ssh-keygen style key creation
99
+
100
+ # new sshkey gem method...
101
+ begin
102
+ k = SSHKey.generate() # (:comment => "#{@email}")
103
+
104
+ publ = k.ssh_public_key
105
+ privl = k.private_key
106
+
107
+ my_file_privl = File.open(GAS_DIRECTORY + "/#{@uid}_id_rsa",'w',0700)
108
+ my_file_privl.write(privl)
109
+ my_file_privl.close
110
+
111
+ my_file_publ = File.open(GAS_DIRECTORY + "/#{@uid}_id_rsa.pub",'w',0700)
112
+ my_file_publ.write(publ)
113
+ my_file_publ.close
114
+
115
+ return true
116
+ rescue
117
+ puts "Fatal Error: Something unexpected happened while writing to #{GAS_DIRECTORY}/#{@uid}_id_rsa"
118
+ puts "SSH key not saved."
119
+ return false
120
+ end
121
+ end
122
+
123
+
124
+
125
+ def self.user_wants_gas_to_handle_rsa_keys?
126
+ puts "Do you want gas to handle switching rsa keys for this user?"
127
+ puts "[Y/n]"
128
+
129
+ while true
130
+ handle_rsa = STDIN.gets.strip
131
+
132
+ case handle_rsa
133
+ when "y", ""
134
+ return true
135
+ when "n"
136
+ puts
137
+ # check if ~/.gas/rsa exists, if it does, promt the user
138
+ if corresponding_rsa_files_exist? #in ~/.gas/
139
+
140
+ puts "Well... there's already a ~/.gas/#{@uid}_id_rsa configured and ready to go. Are you sure you don't want gas to handle rsa switching? (Clicking no will delete the key from the gas directory)"
141
+ puts "Just let gas handle ssh key for this user? [y/n]"
142
+
143
+ while true
144
+ keep_file = STDIN.gets.strip
145
+
146
+ case keep_file
147
+ when "n"
148
+ delete "~/.gas/#{@uid}_id_rsa", "~/.gas/#{@uid}_id_rsa.pub"
149
+ return false
150
+ when "y"
151
+ puts "Excelent! Gas will handle rsa keys for this user."
152
+ return nil
153
+ else
154
+ puts "Please use 'y' or 'n' or enter to choose default."
155
+ end
156
+ end
157
+ end
158
+ return false
159
+
160
+ else
161
+ puts "Please use 'y' or 'n'"
162
+ end
163
+ end
164
+
165
+
166
+ end
167
+
168
+ # This function creates the ssh keys if needed and puts them in ~/.gas/NICKNAME_id_rsa and ...rsa.pub
169
+ #
170
+ #
171
+ def self.setup_ssh_keys(user)
172
+ @uid = user.nickname
173
+ @email = user.email
174
+
175
+ wants_gas_handling_keys = user_wants_gas_to_handle_rsa_keys?
176
+
177
+ if wants_gas_handling_keys
178
+ puts
179
+
180
+ if user_wants_to_use_key_already_in_gas?
181
+ return true # We don't need to do anything because the .gas directory is already setup
182
+ elsif user_wants_to_use_key_already_in_ssh? # Check ~/.ssh for a current id_rsa file, if yes, "Do you want to use the current id_rsa file to be used as your key?"
183
+ use_current_rsa_files_for_this_user # copies the keys from ~/.ssh instead of generating new keys if desired/possible
184
+ return true
185
+ else
186
+ return generate_new_rsa_keys_in_gas_dir
187
+ end
188
+
189
+ elsif wants_gas_handling_keys.nil?
190
+ return true # if user_wants_gas_to_handle_rsa_keys? returns nill that means the user actually had ssh keys already in .gas, and they would like to use those.
191
+ else
192
+ return false # if user doesn't want gas to use ssh keys, that's fine too.
193
+ end
194
+
195
+ end
196
+
197
+
198
+ # This huge method handles the swapping of id_rsa files on the hdd
199
+ #
200
+ def self.swap_in_rsa(nickname)
201
+ @uid = nickname # woah, this is extremely sloppy I think... in order to use any other class methods,
202
+ # I need to write to @uid or it will
203
+ # Have the dumb information from the last time it registered a new git author?
204
+
205
+ if Ssh.corresponding_rsa_files_exist?
206
+
207
+ if ssh_dir_contains_rsa?
208
+ if current_key_already_backed_up?
209
+ write_to_ssh_dir!
210
+ else
211
+ puts "~/.ssh/id_rsa already exists. Overwrite?"
212
+ puts "[y/n]"
213
+
214
+ while true
215
+ overwrite = STDIN.gets.strip
216
+ case overwrite
217
+ when "y"
218
+ write_to_ssh_dir!
219
+ break
220
+ when "n"
221
+ puts "Proceeding without swapping rsa keys."
222
+ break
223
+ else
224
+ puts "please respond 'y' or 'n'"
225
+ end
226
+ end
227
+
228
+ end
229
+
230
+ else # if no ~/.ssh/id_rsa exists... no overwrite potential
231
+ write_to_ssh_dir!
232
+ end
233
+
234
+ end
235
+ end
236
+
237
+
238
+ def self.write_to_ssh_dir!
239
+ # remove the current key from the ssh-agent session (key will no longer be used with github)
240
+ system('ssh-add -d ~/.ssh/id_rsa > /dev/null 2>&1') if is_ssh_agent_there?
241
+
242
+ FileUtils.cp(GAS_DIRECTORY + "/#{@uid}_id_rsa", SSH_DIRECTORY + "/id_rsa")
243
+ FileUtils.cp(GAS_DIRECTORY + "/#{@uid}_id_rsa.pub", SSH_DIRECTORY + "/id_rsa.pub")
244
+
245
+ FileUtils.chmod(0700, SSH_DIRECTORY + "/id_rsa")
246
+ FileUtils.chmod(0700, SSH_DIRECTORY + "/id_rsa.pub")
247
+
248
+ if is_ssh_agent_there?
249
+ `ssh-add ~/.ssh/id_rsa > /dev/null 2>&1` # you need to run this command to get the private key to be set to active on unix based machines. Not sure what to do for windows yet...
250
+ if $?.exitstatus != 0
251
+ raise "Exit code on ssh-add command line was not zero!"
252
+ puts "Looks like there may have been a fatal error in registering the rsa key with ssh-agent. Might be worth looking into" if result != true
253
+ end
254
+ else
255
+ puts "Slight Error: The key should now be in ~/.ssh so that's good, BUT ssh-add could not be found. If you're using windows, you'll need to use git bash or cygwin to emulate this unix command and actually do uploads."
256
+ end
257
+ end
258
+
259
+
260
+ # This function scans each file in a directory to check to see if it is the same file which it's being compared against
261
+ # dir_to_scan The target directory you'd like to scan
262
+ # file_to_compare The file's path that you're expecting to find
263
+ def self.scan_for_file_match(file_to_compare, dir_to_scan)
264
+ require 'digest/md5'
265
+
266
+ pattern = get_md5_hash(file_to_compare)
267
+
268
+ @files = Dir.glob(dir_to_scan + "/*" + file_to_compare.split(//).last(1).to_s)
269
+
270
+ @files.each do |file|
271
+ return true if get_md5_hash(file) == pattern
272
+ end
273
+
274
+ return false
275
+ end
276
+
277
+
278
+ def self.current_key_already_backed_up?
279
+ if scan_for_file_match(SSH_DIRECTORY + "/id_rsa", GAS_DIRECTORY) and scan_for_file_match(SSH_DIRECTORY + "/id_rsa.pub", GAS_DIRECTORY)
280
+ return true
281
+ else
282
+ return false
283
+ end
284
+ end
285
+
286
+ def self.get_md5_hash(file_path)
287
+ if File.exists? file_path
288
+ return Digest::MD5.hexdigest(File.open(file_path, "rb").read)
289
+ end
290
+ return nil
291
+ end
292
+
293
+
294
+ def self.user_wants_to_install_key_to_github?
295
+ puts "Gas can automatically install this ssh key into the github account of your choice. Would you like gas to do this for you? (Requires inputting github username and password)"
296
+ puts "[Y/n]"
297
+
298
+ while true
299
+ upload_key = STDIN.gets.strip.downcase
300
+ case upload_key
301
+ when "y", ""
302
+ return true
303
+ when "n"
304
+ return false
305
+ else
306
+ puts "Plz respond 'y' or 'n'"
307
+ end
308
+ end
309
+ end
310
+
311
+
312
+ def self.upload_public_key_to_github(user)
313
+ @uid = user.nickname
314
+
315
+ if user_wants_to_install_key_to_github?
316
+ key_installation_routine!
317
+ end
318
+ end
319
+
320
+
321
+ def self.key_installation_routine!(user = nil, rsa_test = nil)
322
+ @uid = user.nickname unless user.nil? # allows for easy testing
323
+
324
+ rsa_key = get_associated_rsa_key(@uid)
325
+ rsa_key = rsa_test unless rsa_test.nil?
326
+ return false if rsa_key.nil?
327
+
328
+ # TODO: Impliment a key ring system where you store your key on your github in a repository, only it's encrypted. And to decrypt it, there is
329
+ # A file in your .gas folder!!! That sounds SO fun!
330
+ credentials = get_username_and_password_diligently
331
+
332
+ if !credentials
333
+ puts "Invalid credentials. Skipping upload of keys to github. "
334
+ puts "To try again, type $ gas ssh #{@uid}"
335
+ return false
336
+ end
337
+
338
+ result = post_key!(credentials, @uid, rsa_key)
339
+ if result
340
+ puts "Key uploaded successfully!"
341
+ return true
342
+ end
343
+ end
344
+
345
+
346
+ # Get's the ~/.gas/user_id_rsa associated with the specified user and returns it as a string
347
+ def self.get_associated_rsa_key(nickname)
348
+ file_path = "#{GAS_DIRECTORY}/#{nickname}_id_rsa.pub"
349
+
350
+ if File.exists? file_path
351
+ rsa = File.open(file_path, "rb").read.strip
352
+ if rsa.count(' ') == 2 # special trick to split off the trailing comment text because github API won't store it.
353
+ rsa = rsa.split(" ")
354
+ rsa = "#{rsa[0]} #{rsa[1]}"
355
+ end
356
+
357
+ return rsa
358
+ end
359
+ return nil
360
+ end
361
+
362
+
363
+ def self.get_username_and_password_and_authenticate
364
+ puts "Type your github.com user name:"
365
+ print "User: "
366
+ username = STDIN.gets.strip
367
+
368
+ puts "Type your github password:"
369
+ password = ask("Password: ") { |q| q.echo = false }
370
+ puts
371
+
372
+ credentials = {:username => username, :password => password}
373
+
374
+ if valid_github_username_and_pass?(credentials[:username], credentials[:password])
375
+ return credentials
376
+ else
377
+ return false
378
+ end
379
+ end
380
+
381
+ # Get's the username and password from the user, then authenticates. If it fails, it asks them if they'd like to try again.
382
+ # Returns false if aborted
383
+ def self.get_username_and_password_diligently
384
+ while true
385
+ credentials = get_username_and_password_and_authenticate
386
+ if !credentials
387
+ puts "Could not authenticate, try again?"
388
+ puts "y/n"
389
+
390
+ again = STDIN.gets.strip
391
+ case again.downcase
392
+ when "y"
393
+ when "n"
394
+ return false
395
+ end
396
+ else
397
+ return credentials
398
+ end
399
+ end
400
+ end
401
+
402
+
403
+ def self.valid_github_username_and_pass?(username, password)
404
+ path = '/user'
405
+
406
+ http = Net::HTTP.new(GITHUB_SERVER,443)
407
+ http.use_ssl = true
408
+
409
+ req = Net::HTTP::Get.new(path)
410
+ req.basic_auth username, password
411
+ response = http.request(req)
412
+
413
+ result = JSON.parse(response.body)["message"]
414
+
415
+ return false if result == "Bad credentials"
416
+ return true
417
+ end
418
+
419
+
420
+ def self.post_key!(credentials, nickname, rsa)
421
+ puts "Posting key to GitHub.com..."
422
+ rsa_key = rsa
423
+ username = credentials[:username]
424
+ password = credentials[:password]
425
+
426
+
427
+ # find key...
428
+ if has_key(username, password, rsa_key)
429
+ puts "Key already installed."
430
+ return false
431
+ end
432
+ title = "GAS: #{nickname}"
433
+ install_key(username, password, title, rsa_key)
434
+ end
435
+
436
+
437
+ def self.remove_key_by_id!(username, password, id)
438
+ server = 'api.github.com'
439
+ path = "/user/keys/#{id}"
440
+
441
+
442
+ http = Net::HTTP.new(server,443)
443
+ http.use_ssl = true
444
+ req = Net::HTTP::Delete.new(path)
445
+ req.basic_auth username, password
446
+
447
+ response = http.request(req)
448
+
449
+ return true if response.body.nil?
450
+ end
451
+
452
+ # Cycles through github, looking to see if rsa exists as a public key, then deletes it if it does
453
+ def self.has_key(username, password, rsa)
454
+ # get all keys
455
+ keys = get_keys(username, password)
456
+ # loop through arrays checking against 'key'
457
+ keys.each do |key|
458
+ if key["key"] == rsa
459
+ return true
460
+ end
461
+ end
462
+
463
+ return false # key not found
464
+ end
465
+
466
+
467
+ # Cycles through github, looking to see if rsa exists as a public key, then deletes it if it does
468
+ def self.remove_key!(username, password, rsa)
469
+ # get all keys
470
+ keys = get_keys(username, password)
471
+ # loop through arrays checking against 'key'
472
+ keys.each do |key|
473
+ if key["key"] == rsa
474
+ return remove_key_by_id!(username, password, key["id"])
475
+ end
476
+ end
477
+
478
+ return false # key not found
479
+ end
480
+
481
+ def self.get_keys(username, password)
482
+ server = 'api.github.com'
483
+ path = '/user/keys'
484
+
485
+
486
+ http = Net::HTTP.new(server,443)
487
+ req = Net::HTTP::Get.new(path)
488
+ http.use_ssl = true
489
+ req.basic_auth username, password
490
+ response = http.request(req)
491
+
492
+ return JSON.parse(response.body)
493
+ end
494
+
495
+
496
+ def self.install_key(username, password, title, rsa_key)
497
+ server = 'api.github.com'
498
+ path = '/user/keys'
499
+
500
+ http = Net::HTTP.new(server, 443) # 443 for ssl
501
+ http.use_ssl = true
502
+
503
+ req = Net::HTTP::Post.new(path)
504
+ req.basic_auth username, password
505
+ req.body = "{\"title\":\"#{title}\", \"key\":\"#{rsa_key}\"}"
506
+
507
+ response = http.start {|http| http.request(req) }
508
+ the_code = response.code
509
+
510
+ keys_end = get_keys(username, password).length
511
+
512
+ return true if the_code == "201"
513
+
514
+ puts "The key you are trying to use already exists in another github user's account. You need to use another key." if the_code == "already_exists"
515
+
516
+ # currently.. I think it always returns "already_exists" even if successful. API bug.
517
+ puts "Something may have gone wrong. Either github fixed their API, or your key couldn't be installed." if the_code != "already_exists"
518
+
519
+ #return true if my_hash.key?("errors") # this doesn't work due to it being a buggy API atm # false change me to false when they fix their API
520
+ puts "Server Response: #{response.body}"
521
+ return false
522
+ end
523
+
524
+
525
+ # Cross-platform way of finding an executable in the $PATH.
526
+ # returns nil if command not present
527
+ #
528
+ # which('ruby') #=> /usr/bin/ruby
529
+ def self.which(cmd)
530
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
531
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
532
+ exts.each { |ext|
533
+ exe = "#{path}/#{cmd}#{ext}"
534
+ return exe if File.executable? exe
535
+ }
536
+ end
537
+ return nil
538
+ end
539
+
540
+ def self.is_ssh_agent_there?
541
+ return false if which("ssh-add").nil?
542
+ return true
543
+ end
544
+
545
+ # deletes the ssh keys associated with a user
546
+ def self.delete(nickname)
547
+ return false unless user_has_ssh_keys?(nickname) # return if no keys
548
+
549
+ case user_wants_to_delete_all_ssh_data?
550
+ when "a"
551
+ delete_associated_github_keys!(nickname)
552
+ delete_associated_local_keys!(nickname)
553
+ when "l"
554
+ delete_associated_local_keys!(nickname)
555
+ when "g"
556
+ delete_associated_github_keys!(nickname)
557
+ when "n"
558
+ return false
559
+ end
560
+
561
+ end
562
+
563
+ def self.user_has_ssh_keys?(nickname)
564
+ return false if get_associated_rsa_key(nickname).nil?
565
+ return true
566
+ end
567
+
568
+
569
+ def self.delete_associated_github_keys!(nickname)
570
+ rsa = get_associated_rsa_key(nickname)
571
+ credentials = get_username_and_password_diligently
572
+ if !credentials
573
+ return false
574
+ end
575
+ result = remove_key!(credentials[:username], credentials[:password], rsa)
576
+ puts "The key for this user was not in the specified github account's public keys section." if !result
577
+ end
578
+
579
+
580
+ def self.delete_associated_local_keys!(nickname)
581
+ puts "Removing associated keys from local machine..."
582
+ puts
583
+
584
+ ssh_file = get_md5_hash("#{SSH_DIRECTORY}/id_rsa")
585
+ gas_file = get_md5_hash("#{GAS_DIRECTORY}/#{nickname}_id_rsa")
586
+
587
+ return false if gas_file.nil? # if the gas file doesn't exist, return from this function safely, otherwise both objects could be nil, pass this check, and then fuck up our interpreter with file not found errors
588
+
589
+ if ssh_file == gas_file
590
+ File.delete("#{SSH_DIRECTORY}/id_rsa")
591
+ File.delete("#{SSH_DIRECTORY}/id_rsa.pub")
592
+ end
593
+
594
+ File.delete("#{GAS_DIRECTORY}/#{nickname}_id_rsa")
595
+ File.delete("#{GAS_DIRECTORY}/#{nickname}_id_rsa.pub")
596
+ end
597
+
598
+ # This is another prompt function, but it returns a more complicated lexicon
599
+ #
600
+ # returns "a", "l", "g", or "n"
601
+ def self.user_wants_to_delete_all_ssh_data?
602
+ puts "Would you like to remove all ssh keys too!?! (github account keys can be removed as well!)"
603
+ puts "a: All, the local copy, and checks github too."
604
+ puts "l: Remove local keys only."
605
+ puts "g: Removes keys from github.com only."
606
+ puts "n: Don't remove this users keys."
607
+ puts "Default: l"
608
+
609
+ while true
610
+ delete_all_keys = STDIN.gets.strip
611
+
612
+ case delete_all_keys.downcase
613
+ when "a"
614
+ return "a"
615
+ when "l", ""
616
+ return "l"
617
+ when "g"
618
+ return "g"
619
+ when "n"
620
+ return "n"
621
+ else
622
+ puts "please use 'a', 'l', 'g' or 'n' for NONE."
623
+ end
624
+ end
625
+ end
626
+
627
+ end
628
+ end
629
+
630
+