class PassStation::DB

Password database handling

Password database handling

Password database handling

Password database handling

Password database handling

Constants

UPSTREAM_DATABASE

Attributes

data[R]

Get the password database in +Array<CSV::Row>+ format @return [Array<CSV::Row>] pasword database

database_name[RW]

Get / set the password database name @return [String] password database filename. Default to

+DefaultCreds-Cheat-Sheet.csv+.
storage_location[RW]

Get / set storage location, where will be stored the password database. @return [String] database storage location. Default to data/.

Public Class Methods

check_for_update() click to toggle source

Chek if an update is available @return [Boolean] true if there is, false else.

# File lib/pass_station/source.rb, line 30
def check_for_update
  file = download_file(UPSTREAM_DATABASE[:URL], Dir.mktmpdir)
  # Same hash = no update
  !check_hash(file, UPSTREAM_DATABASE[:HASH])
end
download_upstream(destination_path, opts = {}) click to toggle source

Download upstream password database @param destination_path [String] the destination path (may

overwrite existing file).

@param opts [Hash] the optional downlaod parameters. @option opts [String] :sha256 the SHA256 hash to check, if the file

already exist and the hash matches then the download will be skipped.

@return [String|nil] the saved file path.

# File lib/pass_station/source.rb, line 24
def download_upstream(destination_path, opts = {})
  download_file(UPSTREAM_DATABASE[:URL], destination_path, opts)
end
new() click to toggle source

A new instance of Pass Station

# File lib/pass_station.rb, line 32
def initialize
  @storage_location = 'data/'
  @database_name = 'DefaultCreds-Cheat-Sheet.csv'
  @database_path = absolute_db_path
  database_exists?
  @config = {}
  csv_config
  @data = nil
  @search_result = []
end

Protected Class Methods

check_hash(file, hash) click to toggle source

Check if a file match a SHA256 hash @param file [String] the path of the file. @param hash [String] tha SHA256 hash to check against. @return [Boolean] if the hash of the file matched the one provided (true)

or not (+false+).
# File lib/pass_station/source.rb, line 61
def check_hash(file, hash)
  if !hash.nil? && File.file?(file)
    computed_h = Digest::SHA256.file(file)
    true if hash.casecmp(computed_h.hexdigest).zero?
  else
    false
  end
end
download_file(file_url, destination_path, opts = {}) click to toggle source

Download a file. @param file_url [String] the URL of the file. @param destination_path [String] the destination path (may

overwrite existing file).

@param opts [Hash] the optional downlaod parameters. @option opts [String] :sha256 the SHA256 hash to check, if the file

already exist and the hash matches then the download will be skipped.

@return [String|nil] the saved file path.

# File lib/pass_station/source.rb, line 44
def download_file(file_url, destination_path, opts = {})
  opts[:sha256] ||= nil

  destination_path += '/' unless destination_path[-1] == '/'
  uri = URI(file_url)
  filename = uri.path.split('/').last
  destination_file = destination_path + filename
  # Verify hash to see if it is the latest
  skip_download = check_hash(destination_file, opts[:sha256])
  write_file(destination_file, fetch_file(uri)) unless skip_download
end
fetch_file(uri) click to toggle source

Just fetch a file @param uri [URI] the URI to of the file @return [Bytes] the content of the file

# File lib/pass_station/source.rb, line 73
def fetch_file(uri)
  res = Net::HTTP.get_response(uri)
  raise "#{file_url} ended with #{res.code} #{res.message}" unless res.is_a?(Net::HTTPSuccess)

  res.body
end
write_file(destination_file, file_content) click to toggle source

Write a file to disk @param destination_file [String] the file path where the fiel will be

written to disk

@param file_content [String] the content to write in the file @return [String] destination file path

# File lib/pass_station/source.rb, line 85
def write_file(destination_file, file_content)
  File.open(destination_file, 'wb') do |file|
    file.write(file_content)
  end
  destination_file
end

Public Instance Methods

highlight_found(term, text, sensitive) click to toggle source

Highlight (colorize) a searched term in the input When used with the search command, it will ignore in which column the search was made, and will instead colorize in every columns. @param term [String] the searched term @param text [String] the output in which the colorization must be made @param sensitive [Boolean] case sensitive or not @return [Array<String>] colorized output

# File lib/pass_station/output.rb, line 47
def highlight_found(term, text, sensitive)
  text.map do |x|
    rgxp = build_regexp(term, sensitive: sensitive)
    x.gsub(rgxp) { |s| Paint[s, :red] }
  end
end
output(formatter, data) click to toggle source

Output the data in the chosen format @param formatter [String] Engine to use to format the data: table, +'pretty-table'+, JSON, CSV, YAML @param data [Array<CSV::Row>] @return [Array<String>] formatted output

# File lib/pass_station/output.rb, line 18
def output(formatter, data)
  # Convert string to class
  Object.const_get("PassStation::Output::#{normalize(formatter)}").format(data)
end
output_list(formatter) click to toggle source

Output the data in the chosen format (list command) @param formatter [String] Engine to use to format the data: table, +'pretty-table'+, JSON, CSV, YAML @return [Array<String>] formatted output

# File lib/pass_station/output.rb, line 26
def output_list(formatter)
  data_nil?
  output(formatter, @data)
end
parse(sort = :productvendor) click to toggle source

Parse, sort and sanitize the password database @param sort [Symbol] column name to sort by: :productvendor, :username, :password @return [Array<CSV::Row>] table of CSV::Row, each row contains three

attributes: :productvendor, :username, :password
# File lib/pass_station/parse.rb, line 28
def parse(sort = :productvendor)
  @data = CSV.table(@database_path, **@config).sort_by do |s|
    s[sort].downcase
  end
end

Protected Instance Methods

absolute_db_path() click to toggle source

Find the absolute path of the DB from its relative location @return [String] absolute filename of the DB

# File lib/pass_station.rb, line 45
def absolute_db_path
  pn = Pathname.new(__FILE__)
  install_dir = pn.dirname.parent.to_s + Pathname::SEPARATOR_LIST
  install_dir + @storage_location + @database_name
end
build_regexp(term, opts = {}) click to toggle source

Manage search options to build a search regexp @param term [String] the searched term @param opts [Hash] the option hash @option opts [Boolean] :sensitive is the search case sensitive, default: false @return [Regexp] the search regexp

# File lib/pass_station/search.rb, line 54
def build_regexp(term, opts = {})
  opts[:sensitive] ||= false
  if opts[:sensitive]
    Regexp.new(term, Regexp::EXTENDED)
  else
    Regexp.new(term, Regexp::EXTENDED | Regexp::IGNORECASE)
  end
end
column_selector(col, rgxp) click to toggle source

Choose in which column the search will be performed and build the condition to use @param col [Symbol] the column to search in: :productvendor | :username | :password | :all (all columns) @param rgxp [Regexp] the search regexp (generated by {build_regexp}) @return [Proc] the proc condition to use for searching

# File lib/pass_station/search.rb, line 27
def column_selector(col, rgxp)
  proc { |row|
    if col == :all
      row[:productvendor].match?(rgxp) || row[:username].match?(rgxp) ||
        row[:password].match?(rgxp)
    else
      row[col].match?(rgxp)
    end
  }
end
csv_config() click to toggle source

Register CSV converters for parsing

# File lib/pass_station/parse.rb, line 11
def csv_config
  strip_converter = proc { |field| field.strip }
  CSV::Converters[:strip] = strip_converter
  # https://github.com/ruby/csv/issues/208
  # @config[:strip] = true
  # @config[:liberal_parsing] = true
  @config[:headers] = true
  @config[:converters] = :strip
  @config[:header_converters] = :symbol
  @config[:empty_value] = '<blank>'
  @config[:nil_value] = '<blank>'
end
data_nil?() click to toggle source

Raise an error is data attribute is nil

# File lib/pass_station/search.rb, line 64
def data_nil?
  raise 'You must use the parse method to polutate the data attribute first' if @data.nil?
end
database_exists?() click to toggle source

Check if the password database exists @return [Boolean] true if the file exists

# File lib/pass_station.rb, line 53
def database_exists?
  exists = File.file?(@database_path)
  raise "Database does not exist: #{@database_path}" unless exists

  exists
end
normalize(formatter) click to toggle source

Normalize string to be class name compatible Join splitted words and capitalize @param formatter [String] formatter name @return [String] normalized name (class compatible)

# File lib/pass_station/output.rb, line 58
def normalize(formatter)
  formatter.split('-').map(&:capitalize).join
end