class PassStation::DB
Password database handling
Password database handling
Password database handling
Password database handling
Password database handling
Constants
- UPSTREAM_DATABASE
Attributes
Get the password database in +Array<CSV::Row>+ format @return [Array<CSV::Row>] pasword database
Get / set the password database name @return [String] password database filename. Default to
+DefaultCreds-Cheat-Sheet.csv+.
Get / set storage location, where will be stored the password database. @return [String] database storage location. Default to data/
.
Public Class Methods
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 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
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 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 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
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 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 (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
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
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
Output
the data in the chosen format (search 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 34 def output_search(formatter) return [] if @search_result.empty? output(formatter, @search_result) end
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
Search term in the data table @param term [String] the searched term @param col [Symbol] the column to search in: :productvendor | :username | :password | :all (all columns) @see build_regexp
for opts
param description @return [Array<CSV::Row>] table of CSV::Row
, each row contains three
attributes: :productvendor, :username, :password
# File lib/pass_station/search.rb, line 13 def search(term, col, opts = {}) r1 = prepare_search(term, opts) condition = column_selector(col, r1) @data.each do |row| @search_result.push(row) if condition.call(row) end @search_result end
Protected Instance Methods
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
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
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
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
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
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 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
Prepare search query Check if data available, prepare the regexp, and clean previous search results @see build_regexp
for parameters and return value
# File lib/pass_station/search.rb, line 42 def prepare_search(term, opts = {}) data_nil? r1 = build_regexp(term, opts) @search_result = [] r1 end