Class: Pwrb::PasswordDB

Inherits:
Object
  • Object
show all
Defined in:
lib/pwrb.rb

Instance Method Summary collapse

Constructor Details

#initialize(options = nil) ⇒ PasswordDB

Returns a new instance of PasswordDB.



12
13
14
15
16
17
18
19
20
# File 'lib/pwrb.rb', line 12

def initialize(options=nil)
  @filename = File.expand_path('~/.pw')
  @filename = File.readlink(@filename) if File.symlink?(@filename)
  @options = options
  @crypto = GPGME::Crypto.new

  read_safe
  @selected = @data.select{ |item| match(item, @options) }
end

Instance Method Details

#addObject



164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/pwrb.rb', line 164

def add
  item = ask_for_item

  item[:id] = SecureRandom.uuid
  item[:password] = ask_for_password("Enter password", confirm = true)
  item[:date] = timestamp

  if ask_for_confirm
    @data << item
    write_safe
    puts "Added #{item[:user]}@#{item[:site]}!"
  end
end

#ask_for_confirmObject



105
106
107
108
# File 'lib/pwrb.rb', line 105

def ask_for_confirm
  confirm = ask("Are you sure? ") { |q| q.validate = /\A(y|yes|n|no)\Z/i }
  ['y', 'yes'].include?(confirm)
end

#ask_for_item(init = {}) ⇒ Object



94
95
96
97
98
99
100
101
102
103
# File 'lib/pwrb.rb', line 94

def ask_for_item(init={})
  item = init

  item[:user] = ask("User name? ") { |q| q.default = init[:user] || @options[:user]; q.validate = /\A\S+\Z/ }
  item[:email] = ask("Email? ") { |q| q.default = init[:email] }
  item[:site] = ask("Site? ") { |q| q.default = init[:site] || @options[:site]; q.validate = /\A.+\Z/ }
  item[:url] = ask("URL? ") { |q| q.default = init[:url] || @options[:site] }

  item
end

#ask_for_password(prompt, confirm = false) ⇒ Object



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/pwrb.rb', line 64

def ask_for_password(prompt, confirm=false)
  password = ask(prompt + ": ") { |q| q.echo = false; q.validate = /\A.+\Z/ }
  if confirm
    password_confirm = ask_for_password(prompt + " again")

    if password == password_confirm
      password
    else
      puts "Password mismatch, try again!"
      ask_for_password(prompt, true)
    end
  else
    password
  end
end

#ask_for_selectionObject



80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/pwrb.rb', line 80

def ask_for_selection
  case
    when @selected.empty?
      puts "Record not exists!"
      nil
    when @selected.length == 1
      @selected.first
    else
      list
      choose = ask('Which one? ', Integer) { |q| q.in = 0..@selected.length-1 }
      @selected[choose]
  end
end

#copyObject



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/pwrb.rb', line 133

def copy
  target = ask_for_selection
  seconds = 10

  if target
    original_clipboard_content = Clipboard.paste
    sleep 0.1
    Clipboard.copy target[:password]
    puts "Password for #{target[:site]} is in clipboard for #{seconds} seconds"
    begin
      sleep seconds
    rescue Interrupt
      Clipboard.copy original_clipboard_content
      raise
    end
    Clipboard.copy original_clipboard_content
  end
end

#create_safe(password = nil) ⇒ Object



38
39
40
41
42
43
44
45
46
# File 'lib/pwrb.rb', line 38

def create_safe(password=nil)
  puts "No password file detected, creating one at #{@filename}"
  @password = ask_for_password("Enter master passphrase", confirm = true) unless @password == password

  FileUtils.mkdir_p(File.dirname(@filename))
  @data = []
  write_safe
  File.chmod(0600, @filename)
end

#editObject



178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/pwrb.rb', line 178

def edit
  target = ask_for_selection

  if target
    ask_for_item(target)
    if ask_for_confirm
      target[:date] = timestamp
      write_safe
      puts "Update #{target[:user]}@#{target[:site]}!"
    end
  end
end

#listObject



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/pwrb.rb', line 114

def list
  return if @selected.empty?

  table = Tabularize.new
  table << %w[# ID User Password Site Date]
  table.separator!

  @selected.each_with_index do |item, index|
    user = item[:user]
    user += "<%s>" % item[:email] unless item[:email].empty?
    site = "%s[%s]" % [item[:site], item[:url]]
    password = (@options[:print] ? item[:password] : "***")

    table << [index.to_s, item[:id].split('-').first, user, password, site, item[:date]]
  end

  puts table
end

#match(item, options) ⇒ Object



60
61
62
# File 'lib/pwrb.rb', line 60

def match(item, options)
  user_match(item, options[:user]) && site_match(item, options[:site])
end

#read_safe(password = nil) ⇒ Object



22
23
24
25
26
27
28
29
30
# File 'lib/pwrb.rb', line 22

def read_safe(password=nil)
  if File.file?(@filename)
    @password = password || ask_for_password("Enter master passphrase")
    plain_data = @crypto.decrypt(File.open(@filename), :password => @password).to_s
    @data = JSON.parse(plain_data, :symbolize_names => true)
  else
    create_safe(password)
  end
end

#removeObject



191
192
193
194
195
196
197
198
199
# File 'lib/pwrb.rb', line 191

def remove
  target = ask_for_selection

  if target && ask_for_confirm
    @data.delete(target)
    write_safe
    puts "#{target[:user]}@#{target[:site]} Removed!"
  end
end

#site_match(item, site) ⇒ Object



56
57
58
# File 'lib/pwrb.rb', line 56

def site_match(item, site)
  /#{site}/i =~ item[:site] || /#{site}/i =~ item[:url]
end

#timestampObject



110
111
112
# File 'lib/pwrb.rb', line 110

def timestamp
  Date.today.strftime("%Y%m%d")
end

#to_sObject



48
49
50
# File 'lib/pwrb.rb', line 48

def to_s
  "<*******>"
end

#updateObject



152
153
154
155
156
157
158
159
160
161
# File 'lib/pwrb.rb', line 152

def update
  target = ask_for_selection

  if target
    target[:password] = ask_for_password("Enter new password", confirm = true)
    target[:date] = timestamp
    write_safe
    puts "Update complete!"
  end
end

#user_match(item, user) ⇒ Object



52
53
54
# File 'lib/pwrb.rb', line 52

def user_match(item, user)
  /#{user}/i =~ item[:user]
end

#write_safeObject



32
33
34
35
36
# File 'lib/pwrb.rb', line 32

def write_safe
  plain_data = JSON.generate(@data)
  plain_data.force_encoding('ASCII-8BIT')
  @crypto.encrypt(plain_data, :output => File.open(@filename, 'w+'))
end