yak 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (7) hide show
  1. data/History.txt +7 -0
  2. data/Manifest.txt +6 -0
  3. data/README.txt +65 -0
  4. data/Rakefile +16 -0
  5. data/bin/yak +5 -0
  6. data/lib/yak.rb +384 -0
  7. metadata +96 -0
data/History.txt ADDED
@@ -0,0 +1,7 @@
1
+ === 1.0.1 / 2010-02-22
2
+
3
+ * Renaming to Yak
4
+
5
+ === 1.0.0 / 2010-02-22
6
+
7
+ * First release!
data/Manifest.txt ADDED
@@ -0,0 +1,6 @@
1
+ bin/yak
2
+ lib/yak.rb
3
+ History.txt
4
+ Manifest.txt
5
+ Rakefile
6
+ README.txt
data/README.txt ADDED
@@ -0,0 +1,65 @@
1
+ = Yak
2
+
3
+ == Description
4
+
5
+ Yak is a simple command line app to store and retrieve passwords securely
6
+ under a master password, and allows one password repository per system user.
7
+ Retrieved passwords get copied to the clipboard by default.
8
+
9
+
10
+ == Configuration
11
+
12
+ Config can be set in ~/.yakrc.
13
+
14
+ Session is the length of time in seconds that Yak will remember the
15
+ master password:
16
+ :session: 30
17
+
18
+ If using sessions is not desired and you want to enter the master, set:
19
+ :session: false
20
+
21
+ Always set the password by default, use:
22
+ :password: plain_text_password
23
+
24
+ Turn off password confirmation prompts when a new password is entered:
25
+ :confirm_prompt: false
26
+
27
+
28
+ == Usage
29
+
30
+ Yak will always prompt you for the master password unless a yak session is
31
+ present, or the :password option is set in ~/.yakrc.
32
+ Yak sessions get refreshed everytime yak is called.
33
+
34
+ Adding a new password:
35
+ $ yak -a gmail
36
+ # prompts user for gmail password to save
37
+
38
+ $ yak -a gmail my_password
39
+ # uses my_password as gmail password and overwrites old value
40
+
41
+ Retrieving a saved password:
42
+ $ yak gmail
43
+ # copies the gmail password to the clipboard
44
+
45
+ $ yak --list gmail
46
+ >> gmail: my_password
47
+ # matches all password keys to /gmail/ and outputs to stdout
48
+
49
+ Removing a stored password:
50
+ $ yak -r gmail
51
+ # deletes gmail entry completely
52
+
53
+ Changing the master password:
54
+ $ yak -n
55
+ # prompts for old password first, then the new password
56
+
57
+ Listing key/password pairs:
58
+ $ yak --list
59
+ # returns all saved pairs
60
+
61
+ $ yak --list key
62
+ # returns all saved pairs with a key matching /key/
63
+
64
+ $ yak --list ^key$
65
+ # returns unique saved pair with a key matching /^key$/
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ # -*- ruby -*-
2
+ require 'rubygems'
3
+ require 'hoe'
4
+ require 'rake'
5
+ require 'rake/testtask'
6
+
7
+
8
+ Hoe.plugin :isolate
9
+
10
+ Hoe.spec 'yak' do |p|
11
+ developer('Jeremie Castagna', 'yaksnrainbows@gmail.com')
12
+ self.extra_deps << ['highline', '>= 1.5.1']
13
+ self.extra_deps << ['session', '>= 2.4.0']
14
+ end
15
+
16
+ # vim: syntax=Ruby
data/bin/yak ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby -w
2
+
3
+ require 'yak'
4
+
5
+ Yak.run
data/lib/yak.rb ADDED
@@ -0,0 +1,384 @@
1
+ require 'fileutils'
2
+ require 'openssl'
3
+ require 'digest/sha1'
4
+ require 'optparse'
5
+
6
+ require 'rubygems'
7
+ require 'highline'
8
+ require 'session'
9
+
10
+
11
+ ##
12
+ # Yak is a simple command line app to store and retrieve passwords securely.
13
+ # Retrieved passwords get copied to the clipboard by default.
14
+ # Config can be set in ~/.yakrc:
15
+ # :session: 30
16
+ # Session is the length of time in seconds that Yak will remember the
17
+ # master password. If using sessions is not desired, set:
18
+ # :session: false
19
+ # To always set the password by default, use:
20
+ # :password: plain_text_password
21
+ # To turn off password confirmation prompts:
22
+ # :confirm_prompt: false
23
+
24
+ class Yak
25
+
26
+ VERSION = "1.0.2"
27
+
28
+ DEFAULT_CONFIG = {:session => 30}
29
+
30
+ ##
31
+ # Run Yak with argv:
32
+ # Yak.run %w{key}
33
+ # Yak.run %w{--add key}
34
+ # ...
35
+
36
+ def self.run argv=ARGV
37
+ config = DEFAULT_CONFIG.merge load_config
38
+
39
+ options = parse_args argv
40
+
41
+ yak = new `whoami`.chomp, config
42
+
43
+ args = [options[:action], yak, options[:key], options[:value]].compact
44
+
45
+ self.send(*args)
46
+
47
+ rescue OpenSSL::CipherError => e
48
+ $stderr << "Bad password.\n"
49
+ exit 1
50
+ end
51
+
52
+
53
+ ##
54
+ # Load the ~/.yakrc file and return. Creates ~/.yakrc with the
55
+ # default config if missing.
56
+
57
+ def self.load_config
58
+ config_file = File.expand_path "~/.yakrc"
59
+
60
+ if !File.file?(config_file)
61
+ File.open(config_file, "w+"){|f| f.write DEFAULT_CONFIG.to_yaml }
62
+ $stderr << "Created Yak config file #{config_file}\n"
63
+ end
64
+
65
+ YAML.load_file config_file
66
+ end
67
+
68
+
69
+ def self.remove yak, name
70
+ yak.remove name
71
+ yak.write_data
72
+ end
73
+
74
+
75
+ def self.store yak, name, value=nil
76
+ yak.store name, value
77
+ yak.write_data
78
+ end
79
+
80
+
81
+ def self.retrieve yak, name
82
+ send_to_clipboard yak.retrieve(name)
83
+ end
84
+
85
+
86
+ def self.list yak, name=nil
87
+ key_regex = /#{name || ".+"}/
88
+
89
+ yak.data.each do |key, value|
90
+ $stdout << "#{key}: #{value}\n" if key =~ key_regex
91
+ end
92
+ end
93
+
94
+
95
+ def self.new_password yak, value=nil
96
+ yak.new_password value
97
+ yak.write_data
98
+ yak.start_session
99
+ end
100
+
101
+
102
+ def self.send_to_clipboard string
103
+ copy_cmd = case RUBY_PLATFORM
104
+ when /darwin/
105
+ "echo -n \"#{string}\" | pbcopy"
106
+ when /linux/
107
+ "echo -n \"#{string}\" | xclip"
108
+ when /cigwin/
109
+ "echo -n \"#{string}\" | putclip"
110
+ when /(win|mingw)/
111
+ "echo \"#{string}\" | clip"
112
+ else
113
+ $stderr << "No clipboad cmd for platform #{RUBY_PLATFORM}\n"
114
+ exit 1
115
+ end
116
+
117
+ Session::Bash.new.execute copy_cmd
118
+ end
119
+
120
+
121
+ def self.parse_args argv
122
+ options = {}
123
+
124
+ opts = OptionParser.new do |opt|
125
+ opt.program_name = File.basename $0
126
+ opt.version = VERSION
127
+ opt.release = nil
128
+
129
+ opt.banner = <<-EOF
130
+ #{opt.program_name} is a simple app to store and retrieve passwords securely.
131
+ Retrieved passwords get copied to the clipboard by default.
132
+
133
+ Usage:
134
+ #{opt.program_name} [options] [key] [password]
135
+
136
+ Examples:
137
+ #{opt.program_name} -a gmail [password]
138
+ #{opt.program_name} gmail
139
+ #{opt.program_name} -r gmail
140
+ #{opt.program_name} --list
141
+
142
+ Options:
143
+ EOF
144
+
145
+ opt.on('-a', '--add KEY',
146
+ 'Add a new password for a given key') do |key|
147
+ options[:action] = :store
148
+ options[:key] = key
149
+ end
150
+
151
+ opt.on('-r', '--remove KEY',
152
+ 'Remove the password for a given key') do |key|
153
+ options[:action] = :remove
154
+ options[:key] = key
155
+ end
156
+
157
+ opt.on('-l', '--list [REGEX]',
158
+ 'List key/password pairs to the stdout') do |key|
159
+ options[:action] = :list
160
+ options[:key] = key
161
+ end
162
+
163
+ opt.on('-n', '--new-password',
164
+ 'Update the password used for encryption') do |value|
165
+ options[:action] = :new_password
166
+ end
167
+ end
168
+
169
+ opts.parse! argv
170
+
171
+ options[:action] ||= :retrieve
172
+ options[:key] ||= argv.shift
173
+ options[:value] ||= argv.shift
174
+
175
+ options
176
+ end
177
+
178
+
179
+ attr_reader :user, :data
180
+
181
+ ##
182
+ # Create a new Yak instance for a given user:
183
+ # Yak.new "my_user"
184
+ # Yak.new "my_user", :session => 10
185
+ # Yak.new `whoami`.chomp, :session => false
186
+
187
+ def initialize user, options={}
188
+ @user = user
189
+ @input = HighLine.new $stdin, $stderr
190
+
191
+ @confirm_prompt = true
192
+ @confirm_prompt = options[:confirm_prompt] if
193
+ options.has_key? :confirm_prompt
194
+
195
+ @yak_dir = File.expand_path "~#{@user}/.yak"
196
+ FileUtils.mkdir @yak_dir unless File.directory? @yak_dir
197
+
198
+ @pid_file = File.join @yak_dir, "pid"
199
+ @password_file = File.join @yak_dir, "password"
200
+ @data_file = File.join @yak_dir, "data"
201
+
202
+ @session_pid = nil
203
+ @session_pid = File.read(@pid_file).to_i if File.file? @pid_file
204
+
205
+ @password = get_password options[:password]
206
+
207
+ @cipher = OpenSSL::Cipher::Cipher.new "aes-256-cbc"
208
+
209
+ @session_length = options.has_key?(:session) ? options[:session] : 30
210
+
211
+ connect_data
212
+ start_session
213
+ end
214
+
215
+
216
+ ##
217
+ # Start a new session during which Yak will remember the user's password.
218
+
219
+ def start_session
220
+ return unless @session_length
221
+
222
+ end_session if has_session?
223
+
224
+ pid = fork do
225
+ sleep @session_length
226
+ FileUtils.rm_f [@password_file, @pid_file]
227
+ end
228
+
229
+ File.open(@pid_file, "w+"){|f| f.write pid }
230
+ File.open(@password_file, "w+"){|f| f.write @password }
231
+
232
+ Process.detach pid
233
+ end
234
+
235
+
236
+ ##
237
+ # Stop a session.
238
+
239
+ def end_session
240
+ return unless @session_pid
241
+ Process.kill 9, @session_pid rescue false
242
+ FileUtils.rm_f [@password_file, @pid_file]
243
+ end
244
+
245
+
246
+ ##
247
+ # Check if a session is active.
248
+
249
+ def has_session?
250
+ Process.kill(0, @session_pid) && @session_pid rescue false
251
+ end
252
+
253
+
254
+ ##
255
+ # Get a password from either the password file or by prompting the
256
+ # user if a password file is unavailable. Returns a sha1 of the password
257
+ # passed as an arg.
258
+
259
+ def get_password plain_password=nil
260
+ password = File.read @password_file if File.file? @password_file
261
+
262
+ password ||=
263
+ Digest::SHA1.hexdigest(plain_password || request_password("Yak Password"))
264
+
265
+ password
266
+ end
267
+
268
+
269
+ ##
270
+ # Prompt the user for a new password (replacing and old one).
271
+ # Prompts for password confirmation as well.
272
+
273
+ def new_password password=nil
274
+ password ||= request_new_password "New Password"
275
+ @password = Digest::SHA1.hexdigest password if password
276
+ end
277
+
278
+
279
+ ##
280
+ # Loads and decrypts the data file into the @data attribute.
281
+
282
+ def connect_data
283
+ @data = if File.file? @data_file
284
+ data = ""
285
+ File.open(@data_file, "rb"){|f| data << f.read }
286
+ YAML.load decrypt(data)
287
+ else
288
+ {}
289
+ end
290
+ end
291
+
292
+
293
+ ##
294
+ # Remove a key/value pair.
295
+
296
+ def remove name
297
+ @data.delete(name)
298
+ end
299
+
300
+
301
+ ##
302
+ # Retrieve a value for a given key.
303
+
304
+ def retrieve name
305
+ @data[name]
306
+ end
307
+
308
+
309
+ ##
310
+ # Add a key/value pair. If no value is passed, will prompt the user for one.
311
+
312
+ def store name, value=nil
313
+ value ||= request_new_password "'#{name}' Password"
314
+ @data[name] = value
315
+ end
316
+
317
+
318
+ ##
319
+ # Decrypt a string with a given password.
320
+
321
+ def decrypt string, password=@password
322
+ @cipher.decrypt
323
+ @cipher.key = password
324
+ get_cypher_out string
325
+ end
326
+
327
+
328
+ ##
329
+ # Encrypt a string with a given password.
330
+
331
+ def encrypt string, password=@password
332
+ @cipher.encrypt
333
+ @cipher.key = password
334
+ get_cypher_out string
335
+ end
336
+
337
+
338
+ ##
339
+ # Encrypt and write the Yak data back to the data file.
340
+
341
+ def write_data password=@password
342
+ data = encrypt @data.to_yaml, password
343
+ File.open(@data_file, "w+"){|f| f.write data}
344
+ end
345
+
346
+
347
+ private
348
+
349
+
350
+ ##
351
+ # Prompts for a new password (password and confirmation).
352
+ # Doesn't prompt for confirmation if @confirm_prompt is false.
353
+
354
+ def request_new_password req_str="Password"
355
+ password = request_password "#{req_str}"
356
+
357
+ password_confirm = if @confirm_prompt
358
+ request_password "#{req_str} (confirm)"
359
+ else
360
+ password
361
+ end
362
+
363
+ if password != password_confirm
364
+ $stderr << "Password and password confirmation did not match.\n"
365
+ else
366
+ password.chomp
367
+ end
368
+ end
369
+
370
+
371
+ ##
372
+ # Prompt the user for a password.
373
+
374
+ def request_password req_str="Password"
375
+ @input.ask("#{req_str}:"){|q| q.echo = false}
376
+ end
377
+
378
+
379
+ def get_cypher_out string
380
+ out = @cipher.update string
381
+ out << @cipher.final
382
+ out
383
+ end
384
+ end
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: yak
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Jeremie Castagna
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-02-22 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: highline
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.5.1
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: session
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 2.4.0
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: hoe
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 2.3.3
44
+ version:
45
+ description: |-
46
+ Yak is a simple command line app to store and retrieve passwords securely
47
+ under a master password, and allows one password repository per system user.
48
+ Retrieved passwords get copied to the clipboard by default.
49
+ email:
50
+ - yaksnrainbows@gmail.com
51
+ executables:
52
+ - yak
53
+ extensions: []
54
+
55
+ extra_rdoc_files:
56
+ - History.txt
57
+ - Manifest.txt
58
+ - README.txt
59
+ files:
60
+ - bin/yak
61
+ - lib/yak.rb
62
+ - History.txt
63
+ - Manifest.txt
64
+ - Rakefile
65
+ - README.txt
66
+ has_rdoc: true
67
+ homepage:
68
+ licenses: []
69
+
70
+ post_install_message:
71
+ rdoc_options:
72
+ - --main
73
+ - README.txt
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: "0"
81
+ version:
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: "0"
87
+ version:
88
+ requirements: []
89
+
90
+ rubyforge_project: yak
91
+ rubygems_version: 1.3.5
92
+ signing_key:
93
+ specification_version: 3
94
+ summary: Yak is a simple command line app to store and retrieve passwords securely under a master password, and allows one password repository per system user
95
+ test_files: []
96
+