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.2.210911'
- 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: 14, # 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: 13}, 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 |
# 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 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 |
# 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 pp lst.select{|k,v|pattern.match? k} end |
Instance Method Details
#add_menu_item(name, color = nil) ⇒ Object
52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/gtk2passwordapp/gui.rb', line 52 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
105 106 107 108 109 110 |
# File 'lib/gtk2passwordapp/gui.rb', line 105 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
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
# File 'lib/gtk2passwordapp/gui.rb', line 147 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
202 203 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 |
# File 'lib/gtk2passwordapp/gui.rb', line 202 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 @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
17 18 19 20 21 22 23 24 25 26 27 28 29 |
# File 'lib/gtk2passwordapp/gui.rb', line 17 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
271 272 273 274 275 276 277 278 279 280 281 282 |
# File 'lib/gtk2passwordapp/gui.rb', line 271 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
118 119 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 |
# File 'lib/gtk2passwordapp/gui.rb', line 118 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
292 293 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 |
# File 'lib/gtk2passwordapp/gui.rb', line 292 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
72 73 74 75 76 77 78 79 |
# File 'lib/gtk2passwordapp/gui.rb', line 72 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
87 88 89 90 91 |
# File 'lib/gtk2passwordapp/gui.rb', line 87 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
99 100 101 102 103 |
# File 'lib/gtk2passwordapp/gui.rb', line 99 def hide_main_page @on_main_page = false @main_page.hide @toolbox.hide end |
#popup_accounts_menu ⇒ Object
41 42 43 44 45 46 47 48 49 50 |
# File 'lib/gtk2passwordapp/gui.rb', line 41 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
112 113 114 115 116 |
# File 'lib/gtk2passwordapp/gui.rb', line 112 def rehash(pwd) pwd += CONFIG[:Salt] if pwd.length < CONFIG[:LongPwd] pwd = Digest::SHA256.hexdigest(pwd).upcase H2Q.convert pwd end |
#reset_password ⇒ Object
31 32 33 34 35 36 37 38 39 |
# File 'lib/gtk2passwordapp/gui.rb', line 31 def reset_password ursure = Gtk3App::YesNoDialog.new :reset_ursure! Gtk3App.transient ursure if ursure.ok? @reset = true hide_main_page @password_page.show end end |
#selected_account(name) ⇒ Object
64 65 66 67 68 69 70 |
# File 'lib/gtk2passwordapp/gui.rb', line 64 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
263 264 265 266 267 268 269 |
# File 'lib/gtk2passwordapp/gui.rb', line 263 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
284 285 286 287 288 289 290 |
# File 'lib/gtk2passwordapp/gui.rb', line 284 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
93 94 95 96 97 |
# File 'lib/gtk2passwordapp/gui.rb', line 93 def show_main_page @on_main_page = true @main_page.show_all @toolbox.show_all end |
#show_toggleling(label) ⇒ Object
187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
# File 'lib/gtk2passwordapp/gui.rb', line 187 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
81 82 83 84 85 |
# File 'lib/gtk2passwordapp/gui.rb', line 81 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
172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/gtk2passwordapp/gui.rb', line 172 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 |