Class: Gtk2PasswordApp
- Inherits:
-
Object
- Object
- Gtk2PasswordApp
- Extended by:
- Rafini::Empty
- Defined in:
- lib/gtk2passwordapp.rb,
lib/gtk2passwordapp/cli.rb,
lib/gtk2passwordapp/gui.rb,
lib/gtk2passwordapp/config.rb,
lib/gtk2passwordapp/account.rb,
lib/gtk2passwordapp/accounts.rb
Defined Under Namespace
Constant Summary collapse
- VERSION =
'6.3.250921'- HELP =
<<~HELP Usage: gtk2passwordapp [:gui+] gtk2passwordapp :cli [<pattern> [<file>]] gtk2passwordapp :info Gui: --minime \t Real minime --notoggle \t Minime wont toggle decorated and keep above --notdecorated\t Dont decorate window Cli: --nogui Info: -v --version \tShow version and exit -h --help \tShow help and exit # Notes # With the --nogui cli-option, one can give a pattern to filter by account names. Default passwords data file is: ~/.cache/gtk3app/gtk2passwordapp/dump.yzb HELP
- H2Q =
BaseConvert::FromTo.new(base: 16, digits: '0123456789ABCDEF', to_base:91, to_digits: :qgraph)
- RND =
SuperRandom.new
- TOTPx =
/^[A-Z2-7]{16,}$/- CONFIG =
{ # Hashing Salt: s0, LongPwd: 16, # Miscellaneous Strings Logo: "#{UserSpace::XDG['data']}/gtk3app/gtk2passwordapp/logo.png", HiddenPwd: ' * * * ', # Overriding Gtk3App's window, main: main: { set_title: 'Password Manager' }, # Overriding Gtk3App's toolbar toolbar: { set_expanded: true, into: [:pack_start, expand:true, fill:true, padding:4], }, # Overriding Gtk3App's app_menu: app_menu: { add_menu_item: [:minime!,:about!,:quit!], }, # Colors Red: '#900', Green: '#090', Blue: '#009', White: '#FFF', TooOld: 60*60*24*365, # Year # Buttons tool_button: { set_width_request: 1, into: [:pack_start, expand:true, fill:true, padding:1], }, # Spin Buttons PWDLEN: [3,40,1], pwdlen: {set_value: 15}, pwdlen!: [:PWDLEN,:pwdlen], # Tools Labels ADD: [label: 'Add'], EDIT: [label: 'Edit'], GO: [label: 'Go'], CANCEL: [label: 'Cancel'], DELETE: [label: 'Delete'], SAVE: [label: 'Save'], CURRENT: [label: 'Current'], PREVIOUS: [label: 'Previous'], RAND: [label: 'Random'], # Initialize # Stage and Toolbar ClipboardTimeout: 15, PwdFile: "#{UserSpace::XDG['cache']}/gtk3app/gtk2passwordapp/dump.yzb", # Logo's Main Menu MAIN_MENU: a0, main_menu: h0, main_menu!: [:MAIN_MENU,:main_menu], main_menu_item: h0, # Toolbar's Toolbox TOOLBOX: [:horizontal], toolbox: h0, toolbox!: [:TOOLBOX,:toolbox], # Stage's Pages PAGES: [:vertical], pages: h0, pages!: [:PAGES,:pages], # Page PAGE: [:vertical], page: h0, page!: [:PAGE,:page], # Page Label page_label: h0, # Field Row FIELD_ROW: [:horizontal], field_row: h0, field_row!: [:FIELD_ROW,:field_row], # Field Label FIELD_LABEL: a0, field_label: { set_selectable: false, set_width_request: 80, set_alignment: [1.0,0.5], set_padding: [4,4], }, field_label!: [:FIELD_LABEL, :field_label], # Field View FIELD_VIEW: a0, field_view: { set_selectable: true, set_width_request: 250, set_alignment: [0.0,0.5], set_padding: [4,4], }, field_view!: [:FIELD_VIEW, :field_view], # Field Entry FIELD_ENTRY: a0, field_entry: { set_width_request: 250, into: [:pack_start, expand:true, fill:true, padding:4], }, field_entry!: [:FIELD_ENTRY,:field_entry], # Password Entry PASSWORD_ENTRY: a0, password_entry: { set_visibility: false, set_width_request: 250, into: [:pack_start, expand:true, fill:true, padding:4], }, password_entry!: [:PASSWORD_ENTRY,:password_entry], # Error Label ERROR_LABEL: a0, error_label: h0, error_label!: [:ERROR_LABEL,:error_label], # Password Page MinPwdLen: 7, Confirm: "Confirm password!", # Page Labels PASSWORD_PAGE_LABEL: ['Enter Master Password'], ADD_PAGE_LABEL: ['Add Account'], EDIT_PAGE_LABEL: ['Edit Account'], MAIN_PAGE_LABEL: ['View Account'], # Fields NAME: ['Name:'], URL: ['URL:'], NOTE: ['Note:'], USERNAME: ['Username:'], PASSWORD: ['Password:'], # About Dialog about_dialog: { set_program_name: 'Password Manager', set_version: VERSION.semantic(0..1), set_copyright: '(c) 2021 CarlosJHR64', set_comments: 'A Gtk3App Password Manager', set_website: 'https://github.com/carlosjhr64/gtk2passwordapp', set_website_label: 'See it at GitHub!', }, # Delete Dialog DELETE_URSURE: a0, delete_ursure: {add_label: 'Delete?'}, delete_ursure!: [:DELETE_URSURE,:delete_ursure], # Reset Dialog RESET_URSURE: a0, reset_ursure: {add_label: 'Reset Master Password?'}, reset_ursure!: [:RESET_URSURE,:reset_ursure], # Errors BadUrl: 'URL must be like http://site.', BadUsername: 'Username must be all graph.', BadPassword: 'Password must be all graph.', BadName: 'Account name must be a non-empty String.', CipherError: 'Decryption error.', AccountHit: 'Account exists.', AccountMiss: 'Account does NOT exist.', TooShort: "Password too short!", }
Class Method Summary collapse
Instance Method Summary collapse
- #add_menu_item(name, color = nil) ⇒ Object
- #bootstrap_setups ⇒ Object
- #build_add_page ⇒ Object
- #build_edit_page ⇒ Object
- #build_logo_menu ⇒ Object
- #build_main_page ⇒ Object
- #build_password_page ⇒ Object
- #build_tools ⇒ Object
- #copy2clipboard(pwd, user) ⇒ Object
- #field_row(page, label, entry = :field_entry!, &block) ⇒ Object
- #hide_main_page ⇒ Object
-
#initialize(stage, toolbar, options) ⇒ Gtk2PasswordApp
constructor
A new instance of Gtk2PasswordApp.
- #popup_accounts_menu ⇒ Object
- #rehash(pwd) ⇒ Object
- #reset_password ⇒ Object
- #selected_account(name) ⇒ Object
- #setup_edit_page(account) ⇒ Object
- #setup_main_page(account) ⇒ Object
- #show_main_page ⇒ Object
- #show_toggleling(label) ⇒ Object
- #view_row(page, label) ⇒ Object
- #visibility_toggleling(entry) ⇒ Object
Constructor Details
#initialize(stage, toolbar, options) ⇒ Gtk2PasswordApp
Returns a new instance of Gtk2PasswordApp.
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# File 'lib/gtk2passwordapp/gui.rb', line 2 def initialize(stage, , ) @primary = Gtk::Clipboard.get(Gdk::Selection::PRIMARY) @clipboard = Gtk::Clipboard.get(Gdk::Selection::CLIPBOARD) @accounts = Accounts.new(CONFIG[:PwdFile]) @toolbox = Such::Box.new , :toolbox! @pages = Such::Box.new stage, :pages! @recent = [] @reset = @on_main_page = false @red,@green,@blue,@white = [:Red,:Green,:Blue,:White].map{Gdk::RGBA.parse(CONFIG[_1])} @tools = @password_page = @add_page = @edit_page = @main_page = @menu = nil build_password_page @password_entry.grab_focus end |
Class Method Details
.cli ⇒ Object
24 25 26 27 28 29 30 31 32 33 34 35 |
# File 'lib/gtk2passwordapp.rb', line 24 def self.cli # User using cli may be experiencing system problems. begin require 'gtk2passwordapp/cli' require 'yaml_zlib_blowfish' require 'base_convert' rescue LoadError $stderr.puts 'Missing Gem:' $stderr.puts $!. exit 72 end end |
.gui ⇒ Object
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/gtk2passwordapp.rb', line 37 def self.gui # This is a Gtk3App. require 'gtk3app' # Helper Gems. require 'base32' require 'totp' require 'yaml_zlib_blowfish' require 'super_random' require 'base_convert' # This Gem. require_relative 'gtk2passwordapp/config.rb' require_relative 'gtk2passwordapp/account.rb' require_relative 'gtk2passwordapp/accounts.rb' require_relative 'gtk2passwordapp/gui.rb' end |
.run ⇒ Object
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/gtk2passwordapp/cli.rb', line 2 def self.run(pattern='.', dump=File.('~/.cache/gtk3app/gtk2passwordapp/dump.yzb'), *trash) unless trash.empty? $stderr.puts HELP $stderr.puts "Please match usage." exit 64 end begin pattern = Regexp.new pattern, Regexp::IGNORECASE rescue RegexpError $stderr.puts $!. exit 65 end unless File.exist? dump $stderr.puts "Passwords data file missing: #{dump}" exit 66 end system('clear; clear') print "Enter password: " pwd = $stdin.gets.strip system('clear; clear') print "Enter salt: " pwd << $stdin.gets.strip system('clear; clear') h2q = BaseConvert::FromTo.new base: 16, digits: '0123456789ABCDEF', to_base: 91, to_digits: :qgraph pwd = h2q.convert Digest::SHA256.hexdigest(pwd).upcase begin lst = YamlZlibBlowfish.new(pwd).load(dump) rescue OpenSSL::Cipher::CipherError $stderr.puts "Bad password+salt" exit 65 end lst.select{|k,v|pattern.match? k}.each do |k,v| puts <<~RECORD Account: #{k} Username: #{v[2]} Password: #{v[0]} Previous: #{v[1]} Note: #{v[4]} URL: #{v[3]} Updated: #{Time.at(v[5]).strftime('%Y-%m-%d %H:%M:%S')} RECORD end end |
Instance Method Details
#add_menu_item(name, color = nil) ⇒ Object
54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/gtk2passwordapp/gui.rb', line 54 def (name, color=nil) = Such::MenuItem.new [label:name], :main_menu_item, 'activate' do selected_account(name) end account = @accounts.get name unless color color = (Time.now.to_i - account.updated > CONFIG[:TooOld])? @red : @blue end .override_color :normal, color @menu.append end |
#bootstrap_setups ⇒ Object
107 108 109 110 111 112 |
# File 'lib/gtk2passwordapp/gui.rb', line 107 def bootstrap_setups names = @accounts.data.keys account = (names.empty?)? nil : @accounts.get(names[rand(names.length)]) setup_edit_page(account) if account setup_main_page(account) end |
#build_add_page ⇒ Object
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/gtk2passwordapp/gui.rb', line 149 def build_add_page @add_page = Such::Box.new @pages, :page! Such::Label.new @add_page, :ADD_PAGE_LABEL, :page_label error_label = nil # updates below add_account_entry = field_row(@add_page, :NAME) do name = add_account_entry.text.strip @accounts.add name @accounts.save account = @accounts.get name setup_edit_page(account) setup_main_page(account) @add_page.hide add_account_entry.text = '' @edit_page.show_all rescue error_label.text = $!. end Such::Button.new @add_page, :CANCEL, :tool_button do @add_page.hide add_account_entry.text = '' show_main_page end error_label = Such::Label.new @add_page, :error_label! end |
#build_edit_page ⇒ Object
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
# File 'lib/gtk2passwordapp/gui.rb', line 204 def build_edit_page @edit_page = Such::Box.new @pages, :page! Such::Label.new @edit_page, :EDIT_PAGE_LABEL, :page_label @edit_name = view_row @edit_page, :NAME @edit_url = field_row @edit_page, :URL @edit_note = field_row @edit_page, :NOTE @edit_username = field_row @edit_page, :USERNAME @edit_password = field_row @edit_page, :PASSWORD, :password_entry! visibility_toggleling @edit_password generator = Such::Box.new @edit_page, :toolbox! pwdlen=rndpwd=nil Such::Button.new generator, :RAND, :tool_button do rndpwd = H2Q.convert RND.hexadecimal.upcase @edit_password.text = rndpwd[0...pwdlen.value] end pwdlen = Such::SpinButton.new generator, :pwdlen!, 'value-changed' do @edit_password.text = rndpwd[0...pwdlen.value] if rndpwd end toolbox = Such::Box.new @edit_page, :toolbox! error_label = Such::Label.new @edit_page, :error_label! Such::Button.new toolbox, :SAVE, :tool_button do account = @accounts.get @edit_name.text.strip account.url = @edit_url.text.strip account.note = @edit_note.text.strip account.username = @edit_username.text.strip account.password = @edit_password.text.strip @accounts.save rndpwd = nil @edit_page.hide setup_main_page(account) show_main_page rescue error_label.text = $!. end Such::Button.new toolbox, :CANCEL, :tool_button do account = @accounts.get @edit_name.text rndpwd = nil @edit_page.hide setup_edit_page(account) # restore values show_main_page end Such::Button.new toolbox, :DELETE, :tool_button do ursure = Gtk3App::YesNoDialog.new :delete_ursure! Gtk3App.transient ursure if ursure.ok? @recent.delete @edit_name.text @accounts.delete @edit_name.text @accounts.save rndpwd = nil @edit_page.hide bootstrap_setups show_main_page end end end |
#build_logo_menu ⇒ Object
18 19 20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/gtk2passwordapp/gui.rb', line 18 def Gtk3App.logo_press_event do || next unless @on_main_page case when 1 unless @accounts.data.empty? when 2 reset_password when 3 # Gets captured by Gtk3App's main menu end end end |
#build_main_page ⇒ Object
273 274 275 276 277 278 279 280 281 282 283 284 |
# File 'lib/gtk2passwordapp/gui.rb', line 273 def build_main_page @main_page = Such::Box.new @pages, :page! Such::Label.new @main_page, :MAIN_PAGE_LABEL, :page_label @name = view_row @main_page, :NAME @url = view_row @main_page, :URL @note = view_row @main_page, :NOTE @username = view_row @main_page, :USERNAME @password = view_row @main_page, :PASSWORD show_toggleling @password end |
#build_password_page ⇒ Object
120 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 |
# File 'lib/gtk2passwordapp/gui.rb', line 120 def build_password_page @password_page = Such::Box.new @pages, :page! Such::Label.new @password_page, :PASSWORD_PAGE_LABEL, :page_label error_label,previous = nil,'' # updates below @password_entry = field_row(@password_page, :PASSWORD, :password_entry!) do pwd = @password_entry.text.strip @password_entry.text = '' raise CONFIG[:TooShort] if pwd.length < CONFIG[:MinPwdLen] if not @reset and @accounts.exist? @accounts.load rehash pwd else raise CONFIG[:Confirm] unless pwd==previous @accounts.save rehash pwd @reset = false end @password_page.hide build_add_page unless @add_page build_edit_page unless @edit_page build_main_page unless @main_page build_tools unless @tools bootstrap_setups show_main_page rescue error_label.text = $!. previous = pwd end error_label = Such::Label.new @password_page, :error_label! end |
#build_tools ⇒ Object
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 |
# File 'lib/gtk2passwordapp/gui.rb', line 294 def build_tools @tools = true Such::Button.new @toolbox, :ADD, :tool_button do hide_main_page @add_page.show_all end Such::Button.new @toolbox, :EDIT, :tool_button do unless (name=@name.text).empty? hide_main_page @edit_page.show_all end end Such::Button.new @toolbox, :GO, :tool_button do unless (name=@name.text).empty? url = @accounts.get(name).url system(Gtk3App::CONFIG[:Open], url) unless url.empty? end end Such::Button.new @toolbox, :CURRENT, :tool_button do unless (name=@name.text).empty? account = @accounts.get name copy2clipboard(account.password, account.username) end end Such::Button.new @toolbox, :PREVIOUS, :tool_button do unless (name=@name.text).empty? account = @accounts.get name copy2clipboard(account.previous, account.password) end end end |
#copy2clipboard(pwd, user) ⇒ Object
74 75 76 77 78 79 80 81 |
# File 'lib/gtk2passwordapp/gui.rb', line 74 def copy2clipboard(pwd, user) @primary.text, @clipboard.text = pwd, user GLib::Timeout.add_seconds(CONFIG[:ClipboardTimeout]) do @primary.text = '' if @primary.wait_for_text == pwd @clipboard.text = '' if @clipboard.wait_for_text == user false end end |
#field_row(page, label, entry = :field_entry!, &block) ⇒ Object
89 90 91 92 93 |
# File 'lib/gtk2passwordapp/gui.rb', line 89 def field_row(page, label, entry=:field_entry!, &block) row = Such::Box.new page, :field_row! Such::Label.new(row, label, :field_label) Such::Entry.new(row, entry, ((block)? 'activate' : ''), &block) end |
#hide_main_page ⇒ Object
101 102 103 104 105 |
# File 'lib/gtk2passwordapp/gui.rb', line 101 def hide_main_page @on_main_page = false @main_page.hide @toolbox.hide end |
#popup_accounts_menu ⇒ Object
43 44 45 46 47 48 49 50 51 52 |
# File 'lib/gtk2passwordapp/gui.rb', line 43 def @menu = Such::Menu.new :main_menu! @menu.override_background_color(:normal, @white) @recent.each do |name| (name, @green) end @accounts.data.keys.sort{|a,b|a.upcase<=>b.upcase}.each do |name| (name) end @menu.show_all @menu.popup_at_pointer end |
#rehash(pwd) ⇒ Object
114 115 116 117 118 |
# File 'lib/gtk2passwordapp/gui.rb', line 114 def rehash(pwd) pwd += CONFIG[:Salt] if pwd.length < CONFIG[:LongPwd] pwd = Digest::SHA256.hexdigest(pwd).upcase H2Q.convert pwd end |
#reset_password ⇒ Object
32 33 34 35 36 37 38 39 40 41 |
# File 'lib/gtk2passwordapp/gui.rb', line 32 def reset_password ursure = Gtk3App::YesNoDialog.new :reset_ursure! Gtk3App.transient ursure if ursure.ok? @reset = true hide_main_page @password_page.show @password_entry.grab_focus end end |
#selected_account(name) ⇒ Object
66 67 68 69 70 71 72 |
# File 'lib/gtk2passwordapp/gui.rb', line 66 def selected_account(name) @recent.unshift name; @recent.uniq!; @recent.pop if @recent.length>3 account = @accounts.get name setup_main_page account setup_edit_page account copy2clipboard(account.password, account.username) end |
#setup_edit_page(account) ⇒ Object
265 266 267 268 269 270 271 |
# File 'lib/gtk2passwordapp/gui.rb', line 265 def setup_edit_page(account) @edit_name.text = account.name @edit_url.text = account.url @edit_note.text = account.note @edit_username.text = account.username @edit_password.text = account.password end |
#setup_main_page(account) ⇒ Object
286 287 288 289 290 291 292 |
# File 'lib/gtk2passwordapp/gui.rb', line 286 def setup_main_page(account) @name.text = account&.name || '' @url.text = account&.url || '' @note.text = account&.note || '' @username.text = account&.username || '' @password.text =(account&.password&.>'')? CONFIG[:HiddenPwd] : '' end |
#show_main_page ⇒ Object
95 96 97 98 99 |
# File 'lib/gtk2passwordapp/gui.rb', line 95 def show_main_page @on_main_page = true @main_page.show_all @toolbox.show_all end |
#show_toggleling(label) ⇒ Object
189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/gtk2passwordapp/gui.rb', line 189 def show_toggleling(label) label.signal_connect('button-press-event') do |_,e| if e.==1 and not (pwd=label.text).empty? case pwd when CONFIG[:HiddenPwd] label.text = @accounts.get(@name.text).password when TOTPx label.text = TOTP.passwords(label.text)[1].to_s else label.text = CONFIG[:HiddenPwd] end end end end |
#view_row(page, label) ⇒ Object
83 84 85 86 87 |
# File 'lib/gtk2passwordapp/gui.rb', line 83 def view_row(page, label) row = Such::Box.new page, :field_row! Such::Label.new(row, label, :field_label) Such::Label.new(row, :field_view!) end |
#visibility_toggleling(entry) ⇒ Object
174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
# File 'lib/gtk2passwordapp/gui.rb', line 174 def visibility_toggleling(entry) entry.signal_connect('enter-notify-event') do entry.set_visibility true unless entry.has_focus? end entry.signal_connect('leave-notify-event') do entry.set_visibility false unless entry.has_focus? end entry.signal_connect('focus-in-event') do entry.set_visibility true end entry.signal_connect('focus-out-event') do entry.set_visibility false end end |