module Trunk class CLI < Thor desc "add NAME PASSWORD", "add a key and password" def add(name, password = $stdin.gets.strip) storage.store(name, password) end desc "show NAME", "print the password associated with a key" def show(name) print storage.fetch(name) rescue OpenSSL::PKey::RSAError => error say error.message, :red end desc "version", "print the version" def version say Trunk::VERSION end desc "setup", "create the database and private key file." method_option :algorithm, type: :string, default: 'AES-256-CBC' def setup FileUtils.mkdir_p(dir) exit(0) unless file_collision(private_key_path) if File.exist?(private_key_path) IO.write(private_key_path, OpenSSL::PKey::RSA.new(4096).export(options[:algorithm], passphrase)) FileUtils.touch(database_path) [database_path, private_key_path].each { |x| FileUtils.chmod(0600, x) } end desc "import KDBX", "import a kdbx database" def import(file) Trunk::KDBX.new(file, password: ask("kdbx password:", echo: false)).each do |item| key = [item[:title], item[:url], item[:username]].compact.map { |x| x.gsub(/[^a-zA-Z0-9\-]/, '') }.join('.') storage.store(key, item[:password]) end end private def storage @storage ||= Trunk::YAMLStorage.new(database_path, private_key) end def database_path File.join(dir, '.trunk.enc') end def private_key_path File.join(dir, '.trunk.key.enc') end def dir ENV['TRUNK_HOME'] || File.join(Dir.home, '.trunk') end def private_key OpenSSL::PKey::RSA.new(IO.read(private_key_path), passphrase) end def passphrase @passphrase ||= ENV['TRUNK_PASSPHRASE'] || ask("passphrase:", echo: false) end end end