188 lines
4.7 KiB
Crystal
188 lines
4.7 KiB
Crystal
require "option_parser"
|
|
require "yaml"
|
|
require "colorize"
|
|
|
|
# password serializer
|
|
class Password
|
|
include YAML::Serializable
|
|
|
|
@[YAML::Field(key: "url")]
|
|
property url : String
|
|
@[YAML::Field(key: "email")]
|
|
property email : String
|
|
@[YAML::Field(key: "login")]
|
|
property login : String
|
|
@[YAML::Field(key: "password")]
|
|
property password : String
|
|
@[YAML::Field(key: "desc")]
|
|
property desc : String
|
|
@[YAML::Field(key: "profile_url")]
|
|
property profile_url : String
|
|
@[YAML::Field(key: "update")]
|
|
property update : Int32
|
|
end
|
|
|
|
VERSION = "0.2.2"
|
|
|
|
# program options
|
|
begin
|
|
OptionParser.parse do |parser|
|
|
parser.banner = "The very simple password manager for humans\n"
|
|
|
|
parser.on "-v", "--version", "Show version" do
|
|
puts "The very simple password manager for humans."
|
|
puts "Version #{VERSION}"
|
|
exit(0)
|
|
end
|
|
parser.on "-h", "--help", "Show help" do
|
|
puts parser
|
|
exit(0)
|
|
end
|
|
parser.on "-g", "--generate-password", "Generate password" do
|
|
puts Random::Secure.urlsafe_base64(16, padding: false).colorize(:black).back(:white)
|
|
puts Random::Secure.urlsafe_base64(16, padding: false).colorize(:white).back(:blue)
|
|
puts Random::Secure.urlsafe_base64(16, padding: false).colorize(:white).back(:red)
|
|
exit(0)
|
|
end
|
|
parser.on "-t", "--unixtime", "Return local timestamp" do
|
|
puts Time.local.to_unix.colorize(:yellow).mode(:bold)
|
|
exit(0)
|
|
end
|
|
end
|
|
rescue ex
|
|
puts ex.message, ""
|
|
end
|
|
|
|
# password_file_path = PROGRAM_NAME.split("/")
|
|
# password_file_path.pop
|
|
# password_file_path.insert(-1, "pwd.yml")
|
|
# password_file_path = password_file_path.join("/")
|
|
|
|
# TODO: Fix later
|
|
password_file_path = "#{ENV["HOME"]}/.pwd.yml"
|
|
|
|
# check password file exists
|
|
if File.exists?(password_file_path)
|
|
yaml = File.open(password_file_path) do |file|
|
|
YAML.parse(file)
|
|
end
|
|
else
|
|
puts "No password.yml file exists."
|
|
|
|
exit(1)
|
|
end
|
|
|
|
# fill passwords array
|
|
passwords_array = [] of Password
|
|
count = 0
|
|
while count < yaml.size
|
|
passwords_array << Password.from_yaml(yaml[count].to_yaml)
|
|
count += 1
|
|
end
|
|
|
|
# check file ppermissions
|
|
password_file_permissions = File.info(password_file_path).permissions.to_s
|
|
|
|
if /\d{3}/.match(password_file_permissions).try &.[0] != "600"
|
|
puts "Password file permissions is not RW for you.".colorize(:red)
|
|
|
|
exit(1)
|
|
end
|
|
|
|
# pmng title
|
|
system "clear"
|
|
puts "The very simple password manager for humans".colorize(:yellow).mode(:bold)
|
|
puts "-------------------------------------------".colorize(:yellow).mode(:bold)
|
|
|
|
# main loop
|
|
loop = true
|
|
while loop
|
|
# shell prompt
|
|
print "Enter URL (".colorize(:white).mode(:bold)
|
|
print ":h".colorize(:red).mode(:bold)
|
|
print " for help or ".colorize(:white).mode(:bold)
|
|
print ":q".colorize(:red).mode(:bold)
|
|
print " for exit)\n".colorize(:white).mode(:bold)
|
|
print "> ".colorize(:green).mode(:bold)
|
|
|
|
password_string = gets
|
|
|
|
if password_string.to_s == ":q"
|
|
# if ':q' to close program
|
|
system "clear"
|
|
puts "Bye! 👋"
|
|
exit(0)
|
|
elsif password_string.to_s.size == 0
|
|
# if puts empty, retry prompt
|
|
puts
|
|
elsif password_string.to_s == ":h"
|
|
# if ':h' to view help
|
|
system "clear"
|
|
|
|
puts "Help\n----".colorize(:yellow).mode(:bold)
|
|
print ":s".colorize(:red).mode(:bold)
|
|
puts " - Return stats"
|
|
elsif password_string.to_s == ":s"
|
|
# if ':s' to view Statistics
|
|
system "clear"
|
|
|
|
puts "Statistics\n----------".colorize(:yellow).mode(:bold)
|
|
|
|
print "All elements: ".colorize(:yellow).mode(:bold)
|
|
puts passwords_array.size
|
|
|
|
print "Passwords outdated: ".colorize(:red).mode(:bold)
|
|
outdated_count = 0
|
|
current_time = Time.local.to_unix
|
|
a = [] of String
|
|
passwords_array.each do |item|
|
|
if item.update + (2629743 * 3) < current_time # 2629743 * 3 -- 3 month
|
|
outdated_count += 1
|
|
a << item.url
|
|
end
|
|
end
|
|
|
|
puts outdated_count
|
|
else
|
|
# list search password
|
|
system "clear"
|
|
|
|
passwords_array.each do |item|
|
|
if item.url.includes?(password_string.to_s)
|
|
print "🌐 "
|
|
puts item.url.colorize(:magenta).mode(:bold).mode(:underline)
|
|
|
|
if !item.email.blank?
|
|
print "📧 "
|
|
puts item.email.colorize(:red)
|
|
end
|
|
|
|
if !item.login.blank?
|
|
# print "\u{1F3F7}\u{FE0F} "
|
|
print "🗿 "
|
|
puts item.login.colorize(:yellow)
|
|
end
|
|
|
|
if !item.password.blank?
|
|
print "🔐 "
|
|
puts item.password.colorize(:red).back(:red)
|
|
end
|
|
|
|
if !item.desc.blank?
|
|
print "📄 "
|
|
puts item.desc.colorize(:cyan)
|
|
end
|
|
|
|
if !item.profile_url.blank?
|
|
print "👦 "
|
|
puts item.profile_url.colorize(:green)
|
|
end
|
|
|
|
puts "-----".colorize(:dark_gray).mode(:bold)
|
|
end
|
|
end
|
|
end
|
|
|
|
puts
|
|
end
|