1
0
Fork 0
mirror of https://github.com/nishiki/manage-password.git synced 2024-11-23 22:03:05 +00:00
mpw/lib/MPW.rb

339 lines
9.5 KiB
Ruby
Raw Normal View History

2014-01-29 19:49:39 +00:00
#!/usr/bin/ruby
# author: nishiki
# mail: nishiki@yaegashi.fr
# info: a simple script who manage your passwords
module MPW
require 'rubygems'
require 'gpgme'
require 'csv'
require 'i18n'
2014-09-01 18:34:25 +00:00
require 'fileutils'
2014-01-29 19:49:39 +00:00
class MPW
attr_accessor :error_msg
# Constructor
2014-02-02 16:34:05 +00:00
def initialize(file_gpg, key=nil, share_keys='')
@error_msg = nil
@file_gpg = file_gpg
@key = key
@share_keys = share_keys
2014-01-29 19:49:39 +00:00
end
# Decrypt a gpg file
# @args: password -> the GPG key password
# @rtrn: true if data has been decrypted
def decrypt(passwd=nil)
2014-08-31 15:40:37 +00:00
@data = []
2014-01-29 19:49:39 +00:00
2014-01-30 22:08:38 +00:00
if File.exist?(@file_gpg)
2014-08-31 10:28:20 +00:00
crypto = GPGME::Crypto.new(armor: true)
data_decrypt = crypto.decrypt(IO.read(@file_gpg), password: passwd).read
2014-01-29 19:49:39 +00:00
2014-08-31 10:28:20 +00:00
@data = CSV.parse(data_decrypt, {headers: true, header_converters: :symbol})
2014-01-29 19:49:39 +00:00
end
2014-01-30 22:08:38 +00:00
return true
rescue Exception => e
@error_msg = "#{I18n.t('error.gpg_file.decrypt')}\n#{e}"
return false
2014-01-29 19:49:39 +00:00
end
# Encrypt a file
# @rtrn: true if the file has been encrypted
2014-01-30 22:08:38 +00:00
def encrypt
2014-09-01 17:24:24 +00:00
FileUtils.cp(@file_gpg, "#{@file_gpg}.bk")
2014-08-31 10:28:20 +00:00
crypto = GPGME::Crypto.new(armor: true)
2014-01-30 22:08:38 +00:00
file_gpg = File.open(@file_gpg, 'w+')
2014-01-29 19:49:39 +00:00
2014-08-31 10:28:20 +00:00
data_to_encrypt = CSV.generate(write_headers: true,
headers: ['id', 'name', 'group', 'protocol', 'host', 'login', 'password', 'port', 'comment', 'date']) do |csv|
@data.each do |r|
csv << [r[:id], r[:name], r[:group], r[:protocol], r[:host], r[:login], r[:password], r[:port], r[:comment], r[:date]]
end
2014-01-30 22:08:38 +00:00
end
2014-01-29 19:49:39 +00:00
2014-08-31 16:31:14 +00:00
recipients = []
2014-02-02 16:34:05 +00:00
recipients.push(@key)
2014-11-16 18:39:38 +00:00
if not @share_keys.nil?
2014-02-02 16:34:05 +00:00
@share_keys.split.each { |k| recipients.push(k) }
end
2014-08-31 10:28:20 +00:00
crypto.encrypt(data_to_encrypt, recipients: recipients, output: file_gpg)
2014-01-30 22:08:38 +00:00
file_gpg.close
2014-01-29 19:49:39 +00:00
2014-09-01 17:24:24 +00:00
FileUtils.rm("#{@file_gpg}.bk")
2014-01-30 22:08:38 +00:00
return true
rescue Exception => e
@error_msg = "#{I18n.t('error.gpg_file.encrypt')}\n#{e}"
2014-09-01 17:24:24 +00:00
FileUtils.mv("#{@file_gpg}.bk", @file_gpg)
2014-01-30 22:08:38 +00:00
return false
2014-01-29 19:49:39 +00:00
end
# Search in some csv data
# @args: search -> the string to search
# protocol -> the connection protocol (ssh, web, other)
# @rtrn: a list with the resultat of the search
def search(search='', group=nil, protocol=nil)
2014-01-31 23:09:22 +00:00
result = []
2014-01-29 19:49:39 +00:00
2014-11-16 18:39:38 +00:00
if not search.nil?
2014-01-29 19:49:39 +00:00
search = search.downcase
end
search = search.force_encoding('ASCII-8BIT')
@data.each do |row|
2014-08-31 10:28:20 +00:00
name = row[:name].nil? ? nil : row[:name].downcase
server = row[:host].nil? ? nil : row[:host].downcase
comment = row[:comment].nil? ? nil : row[:comment].downcase
2014-01-29 19:49:39 +00:00
2014-11-16 18:39:38 +00:00
if name =~ /^.*#{search}.*$/ or server =~ /^.*#{search}.*$/ or comment =~ /^.*#{search}.*$/
if (protocol.nil? or protocol.eql?(row[:protocol])) and (group.nil? or group.eql?(row[:group]))
2014-01-29 19:49:39 +00:00
result.push(row)
end
end
end
return result
end
# Search in some csv data
# @args: id -> the id item
# @rtrn: a row with the resultat of the search
def search_by_id(id)
@data.each do |row|
2014-08-31 10:28:20 +00:00
if row[:id] == id
2014-01-29 19:49:39 +00:00
return row
end
end
2014-01-31 23:09:22 +00:00
return []
2014-01-29 19:49:39 +00:00
end
# Update an item
# @args: id -> the item's identifiant
# name -> the item name
# group -> the item group
# server -> the ip or hostname
# protocol -> the protocol
# login -> the login
# passwd -> the password
# port -> the port
# comment -> a comment
# @rtrn: true if the item has been updated
def update(name, group, server, protocol, login, passwd, port, comment, id=nil)
2014-08-31 10:28:20 +00:00
row = {}
2014-01-29 19:49:39 +00:00
update = false
i = 0
@data.each do |r|
2014-08-31 10:28:20 +00:00
if r[:id] == id
2014-01-29 19:49:39 +00:00
row = r
update = true
break
end
i += 1
end
if port.to_i <= 0
port = nil
end
2014-08-31 10:28:20 +00:00
row_update = {}
row_update[:date] = Time.now.to_i
2014-11-16 18:39:38 +00:00
row_update[:id] = id.nil? or id.empty? ? MPW.password(16) : id
row_update[:name] = name.nil? or name.empty? ? row[:name] : name
row_update[:group] = group.nil? or group.empty? ? row[:group] : group
row_update[:host] = server.nil? or server.empty? ? row[:host] : server
row_update[:protocol] = protocol.nil? or protocol.empty? ? row[:protocol] : protocol
row_update[:login] = login.nil? or login.empty? ? row[:login] : login
row_update[:password] = passwd.nil? or passwd.empty? ? row[:password] : passwd
row_update[:port] = port.nil? or port.empty? ? row[:port] : port
row_update[:comment] = comment.nil? or comment.empty? ? row[:comment] : comment
2014-01-29 19:49:39 +00:00
2014-08-31 10:28:20 +00:00
row_update[:name] = row_update[:name].nil? ? nil : row_update[:name].force_encoding('ASCII-8BIT')
row_update[:group] = row_update[:group].nil? ? nil : row_update[:group].force_encoding('ASCII-8BIT')
row_update[:host] = row_update[:host].nil? ? nil : row_update[:host].force_encoding('ASCII-8BIT')
row_update[:protocol] = row_update[:protocol].nil? ? nil : row_update[:protocol].force_encoding('ASCII-8BIT')
row_update[:login] = row_update[:login].nil? ? nil : row_update[:login].force_encoding('ASCII-8BIT')
row_update[:password] = row_update[:password].nil? ? nil : row_update[:password].force_encoding('ASCII-8BIT')
row_update[:comment] = row_update[:comment].nil? ? nil : row_update[:comment].force_encoding('ASCII-8BIT')
2014-11-16 18:39:38 +00:00
if row_update[:name].nil? or row_update[:name].empty?
2014-01-29 19:49:39 +00:00
@error_msg = I18n.t('error.update.name_empty')
return false
end
if update
@data[i] = row_update
else
@data.push(row_update)
end
return true
end
# Remove an item
# @args: id -> the item's identifiant
# @rtrn: true if the item has been deleted
def remove(id)
i = 0
@data.each do |row|
2014-08-31 10:28:20 +00:00
if row[:id] == id
2014-01-29 19:49:39 +00:00
@data.delete_at(i)
return true
end
i += 1
end
2014-08-31 10:28:20 +00:00
@error_msg = I18n.t('error.delete.id_no_exist', id: id)
2014-01-29 19:49:39 +00:00
return false
end
# Export to csv
2014-11-15 18:14:42 +00:00
# @args: file -> file where you export the data
# type -> data type
2014-01-29 19:49:39 +00:00
# @rtrn: true if export work
2014-11-15 18:14:42 +00:00
def export(file, type=:csv)
case type
when :csv
CSV.open(file, 'w', write_headers: true,
headers: ['name', 'group', 'protocol', 'host', 'login', 'password', 'port', 'comment']) do |csv|
@data.each do |r|
csv << [r[:name], r[:group], r[:protocol], r[:host], r[:login], r[:password], r[:port], r[:comment]]
end
end
when :yaml
data = {}
i = 0
2014-08-31 15:40:37 +00:00
@data.each do |r|
2014-11-15 18:14:42 +00:00
data.merge!({i => {'id' => r[:id],
'name' => r[:name],
'group' => r[:group],
'protocol' => r[:protocol],
'host' => r[:host],
'login' => r[:login],
'password' => r[:password],
'port' => r[:port],
'comment' => r[:comment]
}
}
)
i += 1
2014-01-29 19:49:39 +00:00
end
2014-11-15 18:14:42 +00:00
File.open(file, 'w') {|f| f << data.to_yaml}
else
@error_msg = "#{I18n.t('error.export.unknown_type', type: type)}"
return false
2014-01-29 19:49:39 +00:00
end
2014-08-31 15:40:37 +00:00
2014-01-30 22:08:38 +00:00
return true
rescue Exception => e
2014-08-31 10:28:20 +00:00
@error_msg = "#{I18n.t('error.export.write', file: file)}\n#{e}"
2014-01-30 22:08:38 +00:00
return false
2014-01-29 19:49:39 +00:00
end
# Import to csv
# @args: file -> path to file import
# @rtrn: true if the import work
def import(file)
2014-08-31 15:40:37 +00:00
CSV.foreach(file, {headers: true, header_converters: :symbol}) do |row|
if not update(row[:name], row[:group], row[:host], row[:protocol], row[:login], row[:password], row[:port], row[:comment])
2014-01-30 22:08:38 +00:00
return false
2014-01-29 19:49:39 +00:00
end
end
2014-01-30 22:08:38 +00:00
return true
rescue Exception => e
2014-08-31 10:28:20 +00:00
@error_msg = "#{I18n.t('error.import.read', file: file)}\n#{e}"
2014-01-30 22:08:38 +00:00
return false
2014-01-29 19:49:39 +00:00
end
# Return a preview import
# @args: file -> path to file import
# @rtrn: an array with the items to import, if there is an error return false
def import_preview(file)
2014-01-31 23:09:22 +00:00
result = []
2014-08-31 15:40:37 +00:00
CSV.foreach(file, {headers: true, header_converters: :symbol}) do |row|
result << row
2014-01-29 19:49:39 +00:00
end
2014-01-30 22:08:38 +00:00
return result
rescue Exception => e
2014-08-31 10:28:20 +00:00
@error_msg = "#{I18n.t('error.import.read', file: file)}\n#{e}"
2014-01-30 22:08:38 +00:00
return false
2014-01-29 19:49:39 +00:00
end
# Sync remote data and local data
# @args: data_remote -> array with the data remote
# last_update -> last update
# @rtrn: false if data_remote is nil
def sync(data_remote, last_update)
2014-11-16 18:39:38 +00:00
if not data_remote.instance_of?(Array)
2014-01-29 19:49:39 +00:00
return false
2014-11-16 18:39:38 +00:00
else not data_remote.nil? and not data_remote.empty?
@data.each do |l|
j = 0
update = false
# Update item
data_remote.each do |r|
2014-08-31 10:28:20 +00:00
if l[:id] == r[:id]
if l[:date].to_i < r[:date].to_i
update(r[:name], r[:group], r[:host], r[:protocol], r[:login], r[:password], r[:port], r[:comment], l[:id])
end
update = true
data_remote.delete_at(j)
break
2014-01-29 19:49:39 +00:00
end
j += 1
end
# Delete an old item
2014-11-16 18:39:38 +00:00
if not update and l[:date].to_i < last_update
2014-08-31 10:28:20 +00:00
remove(l[:id])
2014-01-29 19:49:39 +00:00
end
end
end
# Add item
data_remote.each do |r|
2014-08-31 10:28:20 +00:00
if r[:date].to_i > last_update
update(r[:name], r[:group], r[:host], r[:protocol], r[:login], r[:password], r[:port], r[:comment], r[:id])
2014-01-29 19:49:39 +00:00
end
end
2014-01-30 22:08:38 +00:00
return encrypt
2014-01-29 19:49:39 +00:00
end
# Generate a random password
# @args: length -> the length password
# @rtrn: a random string
def self.password(length=8)
if length.to_i <= 0
length = 8
else
length = length.to_i
end
result = ''
while length > 62 do
result << ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(62).join
length -= 62
end
result << ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(length).join
return result
end
end
end