module Keepassx::Database::Loader
Attributes
entries[R]
groups[R]
header[R]
Public Class Methods
new(opts)
click to toggle source
rubocop:disable Metrics/MethodLength
# File lib/keepassx/database/loader.rb, line 12 def initialize(opts) @password = nil @groups = [] @entries = [] raw_db = '' if opts.is_a?(File) @path = opts.path raw_db = read_file(opts) load_database(raw_db) elsif opts.is_a?(String) @path = opts raw_db = read_file(opts) if File.exist?(opts) load_database(raw_db) elsif opts.is_a?(Array) @path = nil load_database(raw_db) opts.each { |item| parse_data(item) } end end
Public Instance Methods
checksum()
click to toggle source
Get actual payload checksum.
@return [String]
# File lib/keepassx/database/loader.rb, line 78 def checksum Digest::SHA256.digest(payload) end
length()
click to toggle source
Get Entries and Groups total number.
@return [Fixnum]
# File lib/keepassx/database/loader.rb, line 91 def length length = 0 [@groups, @entries].each do |items| items.each do |item| length += item.length end end length end
payload()
click to toggle source
# File lib/keepassx/database/loader.rb, line 83 def payload @payload ||= initialize_payload end
unlock(password, keyfile = nil)
click to toggle source
Unlock database.
@param password [String] Database
password. @return [Boolean] Whether or not password validation successfull. rubocop:disable Metrics/MethodLength
# File lib/keepassx/database/loader.rb, line 42 def unlock(password, keyfile = nil) return true unless locked? # Store password as we'll need it to dump/save database @password = password keyfile_data = keyfile ? read_file(keyfile) : nil # Uncrypt database final_key = header.final_key(password, keyfile_data) @payload = decrypt_payload(@encrypted_payload, final_key) payload_io = StringIO.new(@payload) # Load it groups = Group.extract_from_payload(header, payload_io) @groups = initialize_groups(groups) @entries = Entry.extract_from_payload(header, payload_io) # Make groups <-> entries association @entries.each do |entry| group = @groups.detect { |g| g.id == entry.group_id } group.entries << entry entry.group = group end @locked = false true rescue OpenSSL::Cipher::CipherError false end
Private Instance Methods
decrypt_payload(payload, final_key)
click to toggle source
rubocop:enable Metrics/MethodLength
# File lib/keepassx/database/loader.rb, line 152 def decrypt_payload(payload, final_key) Keepassx::AESCrypt.decrypt(payload, final_key, header.encryption_iv, 'AES-256-CBC') end
encrypt_payload(payload, final_key)
click to toggle source
# File lib/keepassx/database/loader.rb, line 157 def encrypt_payload(payload, final_key) Keepassx::AESCrypt.encrypt(payload, final_key, header.encryption_iv, 'AES-256-CBC') end
initialize_groups(list)
click to toggle source
Set parents for groups
@param list [Array] Array of groups. @return [Array] Updated array of groups.
rubocop:disable Metrics/MethodLength
# File lib/keepassx/database/loader.rb, line 167 def initialize_groups(list) list.each_with_index do |group, index| previous_group = index == 0 ? nil : list[index - 1] # If group is first entry or has level equal 0, # it gets parent set to nil if previous_group.nil? || group.level == 0 group.parent = nil # If group has same level than the previous group, # then is has the same parent elsif group.level == previous_group.level group.parent = previous_group.parent # If group has level greater than parent's level by one, # it gets parent set to the first previous group with level less # than group's level by one elsif group.level == previous_group.level + 1 group.parent = previous_group # If group has level less than or equal the level of the previous # group and its level is no less than zero, then need to backward # search for the first group which level is less than group's # level by 1 and set it as a parent of the group elsif group.level > 0 && group.level <= previous_group.level group.parent = (index - 2).downto 0 do |i| parent_candidate = list[i] break parent_candidate if parent_candidate.level + 1 == group.level end # Invalid level else raise "Unexpected level '#{group.level}' for group '#{group.name}'" end end list end
initialize_payload()
click to toggle source
rubocop:enable Metrics/MethodLength
# File lib/keepassx/database/loader.rb, line 208 def initialize_payload result = +'' @groups.each { |group| result << group.encode } @entries.each { |entry| result << entry.encode } result end
load_database(db)
click to toggle source
# File lib/keepassx/database/loader.rb, line 111 def load_database(db) if db.empty? @header = Header.new @encrypted_payload = '' @locked = false else @header = Header.new(db[0..124]) @encrypted_payload = db[124..-1] @locked = true end @locked end
parse_data(opts)
click to toggle source
See spec/fixtures/test_data_array.yaml for data example rubocop:disable Metrics/MethodLength
# File lib/keepassx/database/loader.rb, line 128 def parse_data(opts) groups = opts[:groups] || [] entries = opts[:entries] || [] parent = opts[:parent] # Remove groups and entries from options, so new group could be # initialized from incoming Hash fields = Keepassx::Group.fields group_opts = opts.select { |k, _| fields.include?(k.to_s) } group = add_group group_opts group.parent = parent entries.each do |e| add_entry e.merge(group: group) end # Recursively proceed each child group groups.each do |g| parse_data g.merge(parent: group) end end
read_file(file)
click to toggle source
# File lib/keepassx/database/loader.rb, line 105 def read_file(file) read_method = File.respond_to?(:binread) && :binread || :read File.send(read_method, file) end