Class: Yak
- Inherits:
-
Object
- Object
- Yak
- Defined in:
- lib/yak.rb
Overview
Yak is a simple command line app to store and retrieve passwords securely. Retrieved passwords get copied to the clipboard by default. Config can be set in ~/.yakrc:
:session: 30
Session is the length of time in seconds that Yak will remember the master password. If using sessions is not desired, set:
:session: false
To always set the password by default, use:
:password: plain_text_password
To turn off password confirmation prompts:
:confirm_prompt: false
Constant Summary collapse
- VERSION =
"1.0.2"
- DEFAULT_CONFIG =
{:session => 30}
Instance Attribute Summary collapse
-
#data ⇒ Object
readonly
Returns the value of attribute data.
-
#user ⇒ Object
readonly
Returns the value of attribute user.
Class Method Summary collapse
- .list(yak, name = nil) ⇒ Object
-
.load_config ⇒ Object
Load the ~/.yakrc file and return.
- .new_password(yak, value = nil) ⇒ Object
- .parse_args(argv) ⇒ Object
- .remove(yak, name) ⇒ Object
- .retrieve(yak, name) ⇒ Object
-
.run(argv = ARGV) ⇒ Object
Run Yak with argv: Yak.run %wkey Yak.run %wkey …
- .send_to_clipboard(string) ⇒ Object
- .store(yak, name, value = nil) ⇒ Object
Instance Method Summary collapse
-
#connect_data ⇒ Object
Loads and decrypts the data file into the @data attribute.
-
#decrypt(string, password = @password) ⇒ Object
Decrypt a string with a given password.
-
#encrypt(string, password = @password) ⇒ Object
Encrypt a string with a given password.
-
#end_session ⇒ Object
Stop a session.
-
#get_password(plain_password = nil) ⇒ Object
Get a password from either the password file or by prompting the user if a password file is unavailable.
-
#has_session? ⇒ Boolean
Check if a session is active.
-
#initialize(user, options = {}) ⇒ Yak
constructor
Create a new Yak instance for a given user: Yak.new “my_user” Yak.new “my_user”, :session => 10 Yak.new ‘whoami`.chomp, :session => false.
-
#new_password(password = nil) ⇒ Object
Prompt the user for a new password (replacing and old one).
-
#remove(name) ⇒ Object
Remove a key/value pair.
-
#retrieve(name) ⇒ Object
Retrieve a value for a given key.
-
#start_session ⇒ Object
Start a new session during which Yak will remember the user’s password.
-
#store(name, value = nil) ⇒ Object
Add a key/value pair.
-
#write_data(password = @password) ⇒ Object
Encrypt and write the Yak data back to the data file.
Constructor Details
#initialize(user, options = {}) ⇒ Yak
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 |
# File 'lib/yak.rb', line 187 def initialize user, ={} @user = user @input = HighLine.new $stdin, $stderr @confirm_prompt = true @confirm_prompt = [:confirm_prompt] if .has_key? :confirm_prompt @yak_dir = File. "~#{@user}/.yak" FileUtils.mkdir @yak_dir unless File.directory? @yak_dir @pid_file = File.join @yak_dir, "pid" @password_file = File.join @yak_dir, "password" @data_file = File.join @yak_dir, "data" @session_pid = nil @session_pid = File.read(@pid_file).to_i if File.file? @pid_file @password = get_password [:password] @cipher = OpenSSL::Cipher::Cipher.new "aes-256-cbc" @session_length = .has_key?(:session) ? [:session] : 30 connect_data start_session end |
Instance Attribute Details
#data ⇒ Object (readonly)
Returns the value of attribute data.
179 180 181 |
# File 'lib/yak.rb', line 179 def data @data end |
#user ⇒ Object (readonly)
Returns the value of attribute user.
179 180 181 |
# File 'lib/yak.rb', line 179 def user @user end |
Class Method Details
.list(yak, name = nil) ⇒ Object
86 87 88 89 90 91 92 |
# File 'lib/yak.rb', line 86 def self.list yak, name=nil key_regex = /#{name || ".+"}/ yak.data.each do |key, value| $stdout << "#{key}: #{value}\n" if key =~ key_regex end end |
.load_config ⇒ Object
Load the ~/.yakrc file and return. Creates ~/.yakrc with the default config if missing.
57 58 59 60 61 62 63 64 65 66 |
# File 'lib/yak.rb', line 57 def self.load_config config_file = File. "~/.yakrc" if !File.file?(config_file) File.open(config_file, "w+"){|f| f.write DEFAULT_CONFIG.to_yaml } $stderr << "Created Yak config file #{config_file}\n" end YAML.load_file config_file end |
.new_password(yak, value = nil) ⇒ Object
95 96 97 98 99 |
# File 'lib/yak.rb', line 95 def self.new_password yak, value=nil yak.new_password value yak.write_data yak.start_session end |
.parse_args(argv) ⇒ Object
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
# File 'lib/yak.rb', line 121 def self.parse_args argv = {} opts = OptionParser.new do |opt| opt.program_name = File.basename $0 opt.version = VERSION opt.release = nil opt. = <<-EOF #{opt.program_name} is a simple app to store and retrieve passwords securely. Retrieved passwords get copied to the clipboard by default. Usage: #{opt.program_name} [options] [key] [password] Examples: #{opt.program_name} -a gmail [password] #{opt.program_name} gmail #{opt.program_name} -r gmail #{opt.program_name} --list Options: EOF opt.on('-a', '--add KEY', 'Add a new password for a given key') do |key| [:action] = :store [:key] = key end opt.on('-r', '--remove KEY', 'Remove the password for a given key') do |key| [:action] = :remove [:key] = key end opt.on('-l', '--list [REGEX]', 'List key/password pairs to the stdout') do |key| [:action] = :list [:key] = key end opt.on('-n', '--new-password', 'Update the password used for encryption') do |value| [:action] = :new_password end end opts.parse! argv [:action] ||= :retrieve [:key] ||= argv.shift [:value] ||= argv.shift end |
.remove(yak, name) ⇒ Object
69 70 71 72 |
# File 'lib/yak.rb', line 69 def self.remove yak, name yak.remove name yak.write_data end |
.retrieve(yak, name) ⇒ Object
81 82 83 |
# File 'lib/yak.rb', line 81 def self.retrieve yak, name send_to_clipboard yak.retrieve(name) end |
.run(argv = ARGV) ⇒ Object
Run Yak with argv:
Yak.run %w{key}
Yak.run %w{--add key}
...
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/yak.rb', line 36 def self.run argv=ARGV config = DEFAULT_CONFIG.merge load_config = parse_args argv yak = new `whoami`.chomp, config args = [[:action], yak, [:key], [:value]].compact self.send(*args) rescue OpenSSL::CipherError => e $stderr << "Bad password.\n" exit 1 end |
.send_to_clipboard(string) ⇒ Object
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/yak.rb', line 102 def self.send_to_clipboard string copy_cmd = case RUBY_PLATFORM when /darwin/ "echo -n \"#{string}\" | pbcopy" when /linux/ "echo -n \"#{string}\" | xclip" when /cigwin/ "echo -n \"#{string}\" | putclip" when /(win|mingw)/ "echo \"#{string}\" | clip" else $stderr << "No clipboad cmd for platform #{RUBY_PLATFORM}\n" exit 1 end Session::Bash.new.execute copy_cmd end |
.store(yak, name, value = nil) ⇒ Object
75 76 77 78 |
# File 'lib/yak.rb', line 75 def self.store yak, name, value=nil yak.store name, value yak.write_data end |
Instance Method Details
#connect_data ⇒ Object
Loads and decrypts the data file into the @data attribute.
282 283 284 285 286 287 288 289 290 |
# File 'lib/yak.rb', line 282 def connect_data @data = if File.file? @data_file data = "" File.open(@data_file, "rb"){|f| data << f.read } YAML.load decrypt(data) else {} end end |
#decrypt(string, password = @password) ⇒ Object
Decrypt a string with a given password.
321 322 323 324 325 |
# File 'lib/yak.rb', line 321 def decrypt string, password=@password @cipher.decrypt @cipher.key = password get_cypher_out string end |
#encrypt(string, password = @password) ⇒ Object
Encrypt a string with a given password.
331 332 333 334 335 |
# File 'lib/yak.rb', line 331 def encrypt string, password=@password @cipher.encrypt @cipher.key = password get_cypher_out string end |
#end_session ⇒ Object
Stop a session.
239 240 241 242 243 |
# File 'lib/yak.rb', line 239 def end_session return unless @session_pid Process.kill 9, @session_pid rescue false FileUtils.rm_f [@password_file, @pid_file] end |
#get_password(plain_password = nil) ⇒ Object
Get a password from either the password file or by prompting the user if a password file is unavailable. Returns a sha1 of the password passed as an arg.
259 260 261 262 263 264 265 266 |
# File 'lib/yak.rb', line 259 def get_password plain_password=nil password = File.read @password_file if File.file? @password_file password ||= Digest::SHA1.hexdigest(plain_password || request_password("Yak Password")) password end |
#has_session? ⇒ Boolean
Check if a session is active.
249 250 251 |
# File 'lib/yak.rb', line 249 def has_session? Process.kill(0, @session_pid) && @session_pid rescue false end |
#new_password(password = nil) ⇒ Object
Prompt the user for a new password (replacing and old one). Prompts for password confirmation as well.
273 274 275 276 |
# File 'lib/yak.rb', line 273 def new_password password=nil password ||= request_new_password "New Password" @password = Digest::SHA1.hexdigest password if password end |
#remove(name) ⇒ Object
Remove a key/value pair.
296 297 298 |
# File 'lib/yak.rb', line 296 def remove name @data.delete(name) end |
#retrieve(name) ⇒ Object
Retrieve a value for a given key.
304 305 306 |
# File 'lib/yak.rb', line 304 def retrieve name @data[name] end |
#start_session ⇒ Object
Start a new session during which Yak will remember the user’s password.
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
# File 'lib/yak.rb', line 219 def start_session return unless @session_length end_session if has_session? pid = fork do sleep @session_length FileUtils.rm_f [@password_file, @pid_file] end File.open(@pid_file, "w+"){|f| f.write pid } File.open(@password_file, "w+"){|f| f.write @password } Process.detach pid end |
#store(name, value = nil) ⇒ Object
Add a key/value pair. If no value is passed, will prompt the user for one.
312 313 314 315 |
# File 'lib/yak.rb', line 312 def store name, value=nil value ||= request_new_password "'#{name}' Password" @data[name] = value end |
#write_data(password = @password) ⇒ Object
Encrypt and write the Yak data back to the data file.
341 342 343 344 |
# File 'lib/yak.rb', line 341 def write_data password=@password data = encrypt @data.to_yaml, password File.open(@data_file, "w+"){|f| f.write data} end |