mirror of
https://github.com/nishiki/manage-password.git
synced 2025-02-20 01:50:04 +00:00
merge to server branch
This commit is contained in:
commit
a8b4ca29c9
10 changed files with 863 additions and 208 deletions
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
1.0.0 - stable
|
1.1.0 - dev
|
||||||
|
|
30
i18n/en.yml
30
i18n/en.yml
|
@ -1,8 +1,6 @@
|
||||||
---
|
---
|
||||||
en:
|
en:
|
||||||
error:
|
error:
|
||||||
add:
|
|
||||||
name_empty: "You must define a name!"
|
|
||||||
config:
|
config:
|
||||||
write: "Can't write the config file!"
|
write: "Can't write the config file!"
|
||||||
check: "Checkconfig failed!"
|
check: "Checkconfig failed!"
|
||||||
|
@ -18,7 +16,12 @@ en:
|
||||||
bad_format: "Can't import, the file is badly formated!"
|
bad_format: "Can't import, the file is badly formated!"
|
||||||
read: "Can't import, unable to read %{file}!"
|
read: "Can't import, unable to read %{file}!"
|
||||||
update:
|
update:
|
||||||
id_no_exist: "Can't update the item, the item %{id} doesn't exist!"
|
name_empty: "You must define a name!"
|
||||||
|
sync:
|
||||||
|
connection: "Connection fail!"
|
||||||
|
no_data: "Nothing data!"
|
||||||
|
not_authorized: "You haven't the access to remote file!"
|
||||||
|
unknown: "An unknown error is occured!"
|
||||||
cli:
|
cli:
|
||||||
option:
|
option:
|
||||||
usage: "Usage"
|
usage: "Usage"
|
||||||
|
@ -101,6 +104,8 @@ en:
|
||||||
port: "Port"
|
port: "Port"
|
||||||
protocol: "Protocol"
|
protocol: "Protocol"
|
||||||
server: "Server"
|
server: "Server"
|
||||||
|
sync:
|
||||||
|
not_connect: "The server connection fail!"
|
||||||
ssh:
|
ssh:
|
||||||
option:
|
option:
|
||||||
usage: "Usage"
|
usage: "Usage"
|
||||||
|
@ -111,6 +116,25 @@ en:
|
||||||
display:
|
display:
|
||||||
connect: "Connection to:"
|
connect: "Connection to:"
|
||||||
nothing: "Nothing result!"
|
nothing: "Nothing result!"
|
||||||
|
server:
|
||||||
|
option:
|
||||||
|
usage: "Usage"
|
||||||
|
config: "Specifie the configuration file"
|
||||||
|
checkconfig: "Check the configuration"
|
||||||
|
setup: "Setup a new configuration file"
|
||||||
|
help: "Show this message help"
|
||||||
|
checkconfig:
|
||||||
|
fail: "Checkconfig failed:!"
|
||||||
|
empty: "ERROR: an importe option is missing!"
|
||||||
|
datadir: "ERROR: le data directory doesn't exist!"
|
||||||
|
form:
|
||||||
|
setup:
|
||||||
|
title: "Serveur configuration"
|
||||||
|
host: "IP listen: "
|
||||||
|
port: "Port listen: "
|
||||||
|
data_dir: "Data directory: "
|
||||||
|
timeout: "Timeout to second: "
|
||||||
|
not_valid: "ERROR: Impossible to write the configuration file!"
|
||||||
formats:
|
formats:
|
||||||
default: ! '%Y-%m-%d'
|
default: ! '%Y-%m-%d'
|
||||||
long: ! '%B %d, %Y'
|
long: ! '%B %d, %Y'
|
||||||
|
|
30
i18n/fr.yml
30
i18n/fr.yml
|
@ -1,8 +1,6 @@
|
||||||
---
|
---
|
||||||
fr:
|
fr:
|
||||||
error:
|
error:
|
||||||
add:
|
|
||||||
name_empty: "Vous devez définir un nom!"
|
|
||||||
config:
|
config:
|
||||||
write: "Impossible d'écrire le fichier de configuration!"
|
write: "Impossible d'écrire le fichier de configuration!"
|
||||||
check: "Le fichier de configuration est invalide!"
|
check: "Le fichier de configuration est invalide!"
|
||||||
|
@ -18,7 +16,12 @@ fr:
|
||||||
bad_format: "Impossible d'importer le fichier car son format est incorrect!"
|
bad_format: "Impossible d'importer le fichier car son format est incorrect!"
|
||||||
read: "Impossible d'importer le fichier %{file}, car il n'est pas lisible!"
|
read: "Impossible d'importer le fichier %{file}, car il n'est pas lisible!"
|
||||||
update:
|
update:
|
||||||
id_no_exist: "Impossible de mettre à jour l'élément %{id}, car il n'existe pas!"
|
name_empty: "Vous devez définir un nom!"
|
||||||
|
sync:
|
||||||
|
connection: "La connexion n'a pu être établie"
|
||||||
|
no_data: "Aucune data!"
|
||||||
|
not_authorized: "Vous n'avez pas les autorisations d'accès au fichier distant!"
|
||||||
|
unknown: "Une erreur inconnue est survenue!"
|
||||||
cli:
|
cli:
|
||||||
option:
|
option:
|
||||||
usage: "Utilisation"
|
usage: "Utilisation"
|
||||||
|
@ -101,6 +104,8 @@ fr:
|
||||||
port: "Port"
|
port: "Port"
|
||||||
protocol: "Protocol"
|
protocol: "Protocol"
|
||||||
server: "Serveur"
|
server: "Serveur"
|
||||||
|
sync:
|
||||||
|
not_connect: "La connexion au serveur n'a pu être établie!"
|
||||||
ssh:
|
ssh:
|
||||||
option:
|
option:
|
||||||
usage: "Utilisation"
|
usage: "Utilisation"
|
||||||
|
@ -111,6 +116,25 @@ fr:
|
||||||
display:
|
display:
|
||||||
connect: "Connexion à:"
|
connect: "Connexion à:"
|
||||||
nothing: "Aucun résultat!"
|
nothing: "Aucun résultat!"
|
||||||
|
server:
|
||||||
|
option:
|
||||||
|
usage: "Utilisation"
|
||||||
|
config: "Spécifie le fichier de configuration"
|
||||||
|
checkconfig: "Vérifie le fichier de configuration"
|
||||||
|
setup: "Permet de générer un nouveau fichier de configuration"
|
||||||
|
help: "Affiche ce message d'aide"
|
||||||
|
checkconfig:
|
||||||
|
fail: "Le fichier de configuration est invalide!"
|
||||||
|
empty: "ERREUR: Une option importante est manquante!"
|
||||||
|
datadir: "ERREUR: Le répertoire des données n'existe pas!"
|
||||||
|
form:
|
||||||
|
setup:
|
||||||
|
title: "Configuration du serveur"
|
||||||
|
host: "IP d'écoute: "
|
||||||
|
port: "Port d'écoute: "
|
||||||
|
data_dir: "Répertoire des données: "
|
||||||
|
timeout: "Timeout en seconde: "
|
||||||
|
not_valid: "ERREUR: Impossible d'écire le fichier de configuration!"
|
||||||
formats:
|
formats:
|
||||||
default: ! '%Y-%m-%d'
|
default: ! '%Y-%m-%d'
|
||||||
long: ! '%B %d, %Y'
|
long: ! '%B %d, %Y'
|
||||||
|
|
103
lib/Cli.rb
103
lib/Cli.rb
|
@ -8,24 +8,47 @@ require 'highline/import'
|
||||||
require 'pathname'
|
require 'pathname'
|
||||||
require 'readline'
|
require 'readline'
|
||||||
require 'i18n'
|
require 'i18n'
|
||||||
|
require 'yaml'
|
||||||
|
|
||||||
require "#{APP_ROOT}/lib/MPW.rb"
|
require "#{APP_ROOT}/lib/MPW.rb"
|
||||||
|
require "#{APP_ROOT}/lib/MPWConfig.rb"
|
||||||
|
require "#{APP_ROOT}/lib/Sync.rb"
|
||||||
|
|
||||||
class Cli
|
class Cli
|
||||||
|
|
||||||
# Constructor
|
# Constructor
|
||||||
# @args: lang -> the operating system language
|
# @args: lang -> the operating system language
|
||||||
# config_file -> a specify config file
|
# config_file -> a specify config file
|
||||||
def initialize(lang, config_file=nil)
|
def initialize(lang, config)
|
||||||
@m = MPW.new(config_file)
|
@config = config
|
||||||
|
|
||||||
if not @m.checkconfig()
|
@mpw = MPW.new(@config.file_gpg, @config.key)
|
||||||
self.setup(lang)
|
if not decrypt()
|
||||||
|
puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
|
||||||
|
exit 2
|
||||||
end
|
end
|
||||||
|
|
||||||
if not self.decrypt()
|
@sync = Sync.new()
|
||||||
puts "#{I18n.t('cli.display.error')}: #{@m.error_msg}"
|
if @config.sync_host.nil? || @config.sync_port.nil?
|
||||||
exit 2
|
@sync.disable()
|
||||||
|
elsif !@sync.connect(@config.sync_host, @config.sync_port, @config.key, @config.sync_pwd, @config.sync_suffix)
|
||||||
|
puts "#{I18n.t('cli.sync.not_connect')}:\n#{@sync.error_msg}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Destructor
|
||||||
|
def finalize()
|
||||||
|
@sync.close()
|
||||||
|
end
|
||||||
|
|
||||||
|
# Sync the data with the server
|
||||||
|
def sync()
|
||||||
|
begin
|
||||||
|
@mpw.sync(@sync.get(@passwd), @config.last_update)
|
||||||
|
@sync.update(File.open(@config.file_gpg).read)
|
||||||
|
@config.setLastUpdate()
|
||||||
|
rescue Exception => e
|
||||||
|
puts "#{I18n.t('cli.sync.error')}:\n#{e}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -44,35 +67,35 @@ class Cli
|
||||||
end
|
end
|
||||||
I18n.locale = language.to_sym
|
I18n.locale = language.to_sym
|
||||||
|
|
||||||
if @m.setup(key, language, file_gpg, timeout_pwd)
|
if @config.setup(key, language, file_gpg, timeout_pwd)
|
||||||
puts I18n.t('cli.form.setup.valid')
|
puts I18n.t('cli.form.setup.valid')
|
||||||
else
|
else
|
||||||
puts "#{I18n.t('cli.display.error')}: #{@m.error_msg}"
|
puts "#{I18n.t('cli.display.error')}: #{@config.error_msg}"
|
||||||
end
|
end
|
||||||
|
|
||||||
if not @m.checkconfig()
|
if not @config.checkconfig()
|
||||||
puts "#{I18n.t('cli.display.error')}: #{@m.error_msg}"
|
puts "#{I18n.t('cli.display.error')}: #{@config.error_msg}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Request the GPG password and decrypt the file
|
# Request the GPG password and decrypt the file
|
||||||
def decrypt()
|
def decrypt()
|
||||||
@passwd = ask(I18n.t('cli.display.gpg_password')) {|q| q.echo = false}
|
@passwd = ask(I18n.t('cli.display.gpg_password')) {|q| q.echo = false}
|
||||||
return @m.decrypt(@passwd)
|
return @mpw.decrypt(@passwd)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Display the query's result
|
# Display the query's result
|
||||||
# @args: search -> the string to search
|
# @args: search -> the string to search
|
||||||
# protocol -> search from a particular protocol
|
# protocol -> search from a particular protocol
|
||||||
def display(search, protocol=nil, group=nil, format=nil)
|
def display(search, protocol=nil, group=nil, format=nil)
|
||||||
result = @m.search(search, group, protocol)
|
result = @mpw.search(search, group, protocol)
|
||||||
|
|
||||||
if not result.empty?
|
if not result.empty?
|
||||||
result.each do |r|
|
result.each do |r|
|
||||||
if format.nil? || !format
|
if format.nil? || !format
|
||||||
self.displayFormat(r)
|
displayFormat(r)
|
||||||
else
|
else
|
||||||
self.displayFormatAlt(r)
|
displayFormatAlt(r)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
@ -125,21 +148,21 @@ class Cli
|
||||||
port = ask(I18n.t('cli.form.add.port')).to_s
|
port = ask(I18n.t('cli.form.add.port')).to_s
|
||||||
comment = ask(I18n.t('cli.form.add.comment')).to_s
|
comment = ask(I18n.t('cli.form.add.comment')).to_s
|
||||||
|
|
||||||
if @m.add(name, group, server, protocol, login, passwd, port, comment)
|
if @mpw.update(name, group, server, protocol, login, passwd, port, comment)
|
||||||
if @m.encrypt()
|
if @mpw.encrypt()
|
||||||
puts I18n.t('cli.form.add.valid')
|
puts I18n.t('cli.form.add.valid')
|
||||||
else
|
else
|
||||||
puts "#{I18n.t('cli.display.error')}: #{@m.error_msg}"
|
puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
puts "#{I18n.t('cli.display.error')}: #{@m.error_msg}"
|
puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Update an item
|
# Update an item
|
||||||
# @args: id -> the item's id
|
# @args: id -> the item's id
|
||||||
def update(id)
|
def update(id)
|
||||||
row = @m.searchById(id)
|
row = @mpw.searchById(id)
|
||||||
|
|
||||||
if not row.empty?
|
if not row.empty?
|
||||||
puts I18n.t('cli.form.update.title')
|
puts I18n.t('cli.form.update.title')
|
||||||
|
@ -153,14 +176,14 @@ class Cli
|
||||||
port = ask(I18n.t('cli.form.update.port' , :port => row[MPW::PORT])).to_s
|
port = ask(I18n.t('cli.form.update.port' , :port => row[MPW::PORT])).to_s
|
||||||
comment = ask(I18n.t('cli.form.update.comment' , :comment => row[MPW::COMMENT])).to_s
|
comment = ask(I18n.t('cli.form.update.comment' , :comment => row[MPW::COMMENT])).to_s
|
||||||
|
|
||||||
if @m.update(id, name, group, server, protocol, login, passwd, port, comment)
|
if @mpw.update(name, group, server, protocol, login, passwd, port, comment, id)
|
||||||
if @m.encrypt()
|
if @mpw.encrypt()
|
||||||
puts I18n.t('cli.form.update.valid')
|
puts I18n.t('cli.form.update.valid')
|
||||||
else
|
else
|
||||||
puts "#{I18n.t('cli.display.error')}: #{@m.error_msg}"
|
puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
puts "#{I18n.t('cli.display.error')}: #{@m.error_msg}"
|
puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
puts I18n.t('cli.display.nothing')
|
puts I18n.t('cli.display.nothing')
|
||||||
|
@ -172,10 +195,10 @@ class Cli
|
||||||
# force -> no resquest a validation
|
# force -> no resquest a validation
|
||||||
def remove(id, force=false)
|
def remove(id, force=false)
|
||||||
if not force
|
if not force
|
||||||
result = @m.searchById(id)
|
result = @mpw.searchById(id)
|
||||||
|
|
||||||
if result.length > 0
|
if result.length > 0
|
||||||
self.displayFormat(result)
|
displayFormat(result)
|
||||||
|
|
||||||
confirm = ask("#{I18n.t('cli.form.delete.ask', :id => id)} (y/N) ").to_s
|
confirm = ask("#{I18n.t('cli.form.delete.ask', :id => id)} (y/N) ").to_s
|
||||||
if confirm =~ /^(y|yes|YES|Yes|Y)$/
|
if confirm =~ /^(y|yes|YES|Yes|Y)$/
|
||||||
|
@ -187,11 +210,11 @@ class Cli
|
||||||
end
|
end
|
||||||
|
|
||||||
if force
|
if force
|
||||||
if @m.remove(id)
|
if @mpw.remove(id)
|
||||||
if @m.encrypt()
|
if @mpw.encrypt()
|
||||||
puts I18n.t('cli.form.delete.valid', :id => id)
|
puts I18n.t('cli.form.delete.valid', :id => id)
|
||||||
else
|
else
|
||||||
puts "#{I18n.t('cli.display.error')}: #{@m.error_msg}"
|
puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
puts I18n.t('cli.form.delete.not_valid')
|
puts I18n.t('cli.form.delete.not_valid')
|
||||||
|
@ -202,10 +225,10 @@ class Cli
|
||||||
# Export the items in a CSV file
|
# Export the items in a CSV file
|
||||||
# @args: file -> the destination file
|
# @args: file -> the destination file
|
||||||
def export(file)
|
def export(file)
|
||||||
if @m.export(file)
|
if @mpw.export(file)
|
||||||
puts "The export in #{file} is succesfull!"
|
puts "The export in #{file} is succesfull!"
|
||||||
else
|
else
|
||||||
puts "#{I18n.t('cli.display.error')}: #{@m.error_msg}"
|
puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -214,12 +237,12 @@ class Cli
|
||||||
# @args: file -> the import file
|
# @args: file -> the import file
|
||||||
# force -> no resquest a validation
|
# force -> no resquest a validation
|
||||||
def import(file, force=false)
|
def import(file, force=false)
|
||||||
result = @m.importPreview(file)
|
result = @mpw.importPreview(file)
|
||||||
|
|
||||||
if not force
|
if not force
|
||||||
if result.is_a?(Array) && !result.empty?
|
if result.is_a?(Array) && !result.empty?
|
||||||
result.each do |r|
|
result.each do |r|
|
||||||
self.displayFormat(r)
|
displayFormat(r)
|
||||||
end
|
end
|
||||||
|
|
||||||
confirm = ask("#{I18n.t('cli.form.import.ask', :file => file)} (y/N) ").to_s
|
confirm = ask("#{I18n.t('cli.form.import.ask', :file => file)} (y/N) ").to_s
|
||||||
|
@ -232,10 +255,10 @@ class Cli
|
||||||
end
|
end
|
||||||
|
|
||||||
if force
|
if force
|
||||||
if @m.import(file) && @m.encrypt()
|
if @mpw.import(file) && @mpw.encrypt()
|
||||||
puts I18n.t('cli.form.import.valid')
|
puts I18n.t('cli.form.import.valid')
|
||||||
else
|
else
|
||||||
puts "#{I18n.t('cli.display.error')}: #{@m.error_msg}"
|
puts "#{I18n.t('cli.display.error')}: #{@mpw.error_msg}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -247,7 +270,7 @@ class Cli
|
||||||
|
|
||||||
while buf = Readline.readline('<mpw> ', true)
|
while buf = Readline.readline('<mpw> ', true)
|
||||||
|
|
||||||
if @m.timeout_pwd < Time.now.to_i - last_access
|
if @config.timeout_pwd < Time.now.to_i - last_access
|
||||||
passwd_confirm = ask(I18n.t('cli.interactive.ask_password')) {|q| q.echo = false}
|
passwd_confirm = ask(I18n.t('cli.interactive.ask_password')) {|q| q.echo = false}
|
||||||
|
|
||||||
if @passwd.eql?(passwd_confirm)
|
if @passwd.eql?(passwd_confirm)
|
||||||
|
@ -265,17 +288,17 @@ class Cli
|
||||||
case command[0]
|
case command[0]
|
||||||
when 'display', 'show', 'd', 's'
|
when 'display', 'show', 'd', 's'
|
||||||
if !command[1].nil? && !command[1].empty?
|
if !command[1].nil? && !command[1].empty?
|
||||||
self.display(command[1], group, command[2])
|
display(command[1], group, command[2])
|
||||||
end
|
end
|
||||||
when 'add', 'a'
|
when 'add', 'a'
|
||||||
add()
|
add()
|
||||||
when 'update', 'u'
|
when 'update', 'u'
|
||||||
if !command[1].nil? && !command[1].empty?
|
if !command[1].nil? && !command[1].empty?
|
||||||
self.update(command[1])
|
update(command[1])
|
||||||
end
|
end
|
||||||
when 'remove', 'delete', 'r', 'd'
|
when 'remove', 'delete', 'r', 'd'
|
||||||
if !command[1].nil? && !command[1].empty?
|
if !command[1].nil? && !command[1].empty?
|
||||||
self.remove(command[1])
|
remove(command[1])
|
||||||
end
|
end
|
||||||
when 'group', 'g'
|
when 'group', 'g'
|
||||||
if !command[1].nil? && !command[1].empty?
|
if !command[1].nil? && !command[1].empty?
|
||||||
|
|
265
lib/MPW.rb
265
lib/MPW.rb
|
@ -6,7 +6,6 @@
|
||||||
require 'rubygems'
|
require 'rubygems'
|
||||||
require 'gpgme'
|
require 'gpgme'
|
||||||
require 'csv'
|
require 'csv'
|
||||||
require 'yaml'
|
|
||||||
require 'i18n'
|
require 'i18n'
|
||||||
|
|
||||||
class MPW
|
class MPW
|
||||||
|
@ -20,80 +19,15 @@ class MPW
|
||||||
PASSWORD = 6
|
PASSWORD = 6
|
||||||
PORT = 7
|
PORT = 7
|
||||||
COMMENT = 8
|
COMMENT = 8
|
||||||
|
DATE = 9
|
||||||
|
|
||||||
attr_accessor :error_msg
|
attr_accessor :error_msg
|
||||||
attr_accessor :timeout_pwd
|
|
||||||
|
|
||||||
# Constructor
|
# Constructor
|
||||||
# @args: file_config -> the specify config file
|
def initialize(file_gpg, key=nil)
|
||||||
def initialize(file_config=nil)
|
@error_msg = nil
|
||||||
@error_msg = nil
|
@file_gpg = file_gpg
|
||||||
@file_config = "#{Dir.home()}/.mpw.cfg"
|
@key = key
|
||||||
|
|
||||||
if !file_config.nil? && !file_config.empty?
|
|
||||||
@file_config = file_config
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Create a new config file
|
|
||||||
# @args: key -> the gpg key to encrypt
|
|
||||||
# lang -> the software language
|
|
||||||
# file_gpg -> the file who is encrypted
|
|
||||||
# timeout_pwd -> time to save the password
|
|
||||||
# @rtrn: true if le config file is create
|
|
||||||
def setup(key, lang, file_gpg, timeout_pwd)
|
|
||||||
|
|
||||||
if not key =~ /[a-zA-Z0-9.-_]+\@[a-zA-Z0-9]+\.[a-zA-Z]+/
|
|
||||||
@error_msg = I18n.t('error.config.key_bad_format')
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
if file_gpg.empty?
|
|
||||||
file_gpg = "#{Dir.home()}/.mpw.gpg"
|
|
||||||
end
|
|
||||||
|
|
||||||
timeout_pwd.empty? ? (timeout_pwd = 60) : (timeout_pwd = timeout_pwd.to_i)
|
|
||||||
|
|
||||||
config = {'config' => {'key' => key,
|
|
||||||
'lang' => lang,
|
|
||||||
'file_gpg' => file_gpg,
|
|
||||||
'timeout_pwd' => timeout_pwd}}
|
|
||||||
|
|
||||||
begin
|
|
||||||
File.open(@file_config, 'w') do |file|
|
|
||||||
file << config.to_yaml
|
|
||||||
end
|
|
||||||
rescue Exception => e
|
|
||||||
@error_msg = "#{I18n.t('error.config.write')}\n#{e}"
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
# Check the config file
|
|
||||||
# @rtrn: true if the config file is correct
|
|
||||||
def checkconfig()
|
|
||||||
begin
|
|
||||||
config = YAML::load_file(@file_config)
|
|
||||||
@key = config['config']['key']
|
|
||||||
@lang = config['config']['lang']
|
|
||||||
@file_gpg = config['config']['file_gpg']
|
|
||||||
@timeout_pwd = config['config']['timeout_pwd'].to_i
|
|
||||||
|
|
||||||
if @key.empty? || @file_gpg.empty?
|
|
||||||
@error_msg = I18n.t('error.config.check')
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
I18n.locale = @lang.to_sym
|
|
||||||
|
|
||||||
rescue Exception => e
|
|
||||||
@error_msg = "#{I18n.t('error.config.check')}\n#{e}"
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Decrypt a gpg file
|
# Decrypt a gpg file
|
||||||
|
@ -107,10 +41,8 @@ class MPW
|
||||||
crypto = GPGME::Crypto.new(:armor => true)
|
crypto = GPGME::Crypto.new(:armor => true)
|
||||||
data_decrypt = crypto.decrypt(IO.read(@file_gpg), :password => passwd).read
|
data_decrypt = crypto.decrypt(IO.read(@file_gpg), :password => passwd).read
|
||||||
|
|
||||||
id = 0
|
|
||||||
data_decrypt.lines do |line|
|
data_decrypt.lines do |line|
|
||||||
@data[id] = line.parse_csv.unshift(id)
|
@data.push(line.parse_csv)
|
||||||
id += 1;
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -130,7 +62,7 @@ class MPW
|
||||||
|
|
||||||
data_to_encrypt = ''
|
data_to_encrypt = ''
|
||||||
@data.each do |row|
|
@data.each do |row|
|
||||||
data_to_encrypt << row.drop(1).to_csv
|
data_to_encrypt << row.to_csv
|
||||||
end
|
end
|
||||||
|
|
||||||
crypto.encrypt(data_to_encrypt, :recipients => @key, :output => file_gpg)
|
crypto.encrypt(data_to_encrypt, :recipients => @key, :output => file_gpg)
|
||||||
|
@ -147,7 +79,7 @@ class MPW
|
||||||
# @args: search -> the string to search
|
# @args: search -> the string to search
|
||||||
# protocol -> the connection protocol (ssh, web, other)
|
# protocol -> the connection protocol (ssh, web, other)
|
||||||
# @rtrn: a list with the resultat of the search
|
# @rtrn: a list with the resultat of the search
|
||||||
def search(search, group=nil, protocol=nil)
|
def search(search='', group=nil, protocol=nil)
|
||||||
result = Array.new()
|
result = Array.new()
|
||||||
|
|
||||||
if !search.nil?
|
if !search.nil?
|
||||||
|
@ -174,57 +106,15 @@ class MPW
|
||||||
# @args: id -> the id item
|
# @args: id -> the id item
|
||||||
# @rtrn: a row with the resultat of the search
|
# @rtrn: a row with the resultat of the search
|
||||||
def searchById(id)
|
def searchById(id)
|
||||||
if not @data[id.to_i].nil?
|
@data.each do |row|
|
||||||
return @data[id.to_i]
|
if row[ID] == id
|
||||||
else
|
return row
|
||||||
return Array.new
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return Array.new()
|
||||||
end
|
end
|
||||||
|
|
||||||
# Add a new item
|
|
||||||
# @args: name -> the item name
|
|
||||||
# group -> the item group
|
|
||||||
# server -> the ip or server
|
|
||||||
# protocol -> the protocol
|
|
||||||
# login -> the login
|
|
||||||
# passwd -> the password
|
|
||||||
# port -> the port
|
|
||||||
# comment -> a comment
|
|
||||||
# @rtrn: true if it works
|
|
||||||
def add(name, group=nil, server=nil, protocol=nil, login=nil, passwd=nil, port=nil, comment=nil)
|
|
||||||
row = Array.new()
|
|
||||||
|
|
||||||
if name.nil? || name.empty?
|
|
||||||
@error_msg = I18n.t('error.add.name_empty')
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
if port.to_i <= 0
|
|
||||||
port = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
if not @data.last.nil?
|
|
||||||
id = @data.last
|
|
||||||
id = id[ID].to_i + 1
|
|
||||||
else
|
|
||||||
id = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
row[ID] = id
|
|
||||||
row[PORT] = port
|
|
||||||
row[NAME] = name.force_encoding('ASCII-8BIT')
|
|
||||||
group.nil? || group.empty? ? (row[GROUP] = nil) : (row[GROUP] = group.force_encoding('ASCII-8BIT'))
|
|
||||||
server.nil? || server.empty? ? (row[SERVER] = nil) : (row[SERVER] = server.force_encoding('ASCII-8BIT'))
|
|
||||||
protocol.nil? || protocol.empty? ? (row[PROTOCOL] = nil) : (row[PROTOCOL] = protocol.force_encoding('ASCII-8BIT'))
|
|
||||||
login.nil? || login.empty? ? (row[LOGIN] = nil) : (row[LOGIN] = login.force_encoding('ASCII-8BIT'))
|
|
||||||
passwd.nil? || passwd.empty? ? (row[PASSWORD] = nil) : (row[PASSWORD] = passwd.force_encoding('ASCII-8BIT'))
|
|
||||||
comment.nil? || comment.empty? ? (row[COMMENT] = nil) : (row[COMMENT] = comment.force_encoding('ASCII-8BIT'))
|
|
||||||
|
|
||||||
@data[id] = row
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
# Update an item
|
# Update an item
|
||||||
# @args: id -> the item's identifiant
|
# @args: id -> the item's identifiant
|
||||||
# name -> the item name
|
# name -> the item name
|
||||||
|
@ -236,46 +126,66 @@ class MPW
|
||||||
# port -> the port
|
# port -> the port
|
||||||
# comment -> a comment
|
# comment -> a comment
|
||||||
# @rtrn: true if the item has been updated
|
# @rtrn: true if the item has been updated
|
||||||
def update(id, name=nil, group=nil, server=nil, protocol=nil, login=nil, passwd=nil, port=nil, comment=nil)
|
def update(name, group, server, protocol, login, passwd, port, comment, id=nil)
|
||||||
id = id.to_i
|
row = Array.new()
|
||||||
|
update = false
|
||||||
|
|
||||||
if not @data[id].nil?
|
i = 0
|
||||||
|
@data.each do |r|
|
||||||
if port.to_i <= 0
|
if r[ID] == id
|
||||||
port = nil
|
row = r
|
||||||
|
update = true
|
||||||
|
break
|
||||||
end
|
end
|
||||||
|
i += 1
|
||||||
|
end
|
||||||
|
|
||||||
row = @data[id]
|
if port.to_i <= 0
|
||||||
row_update = Array.new()
|
port = nil
|
||||||
|
end
|
||||||
|
|
||||||
name.nil? || name.empty? ? (row_update[NAME] = row[NAME]) : (row_update[NAME] = name)
|
row_update = Array.new()
|
||||||
group.nil? || group.empty? ? (row_update[GROUP] = row[GROUP]) : (row_update[GROUP] = group)
|
row_update[DATE] = Time.now.to_i
|
||||||
server.nil? || server.empty? ? (row_update[SERVER] = row[SERVER]) : (row_update[SERVER] = server)
|
|
||||||
protocol.nil? || protocol.empty? ? (row_update[PROTOCOL] = row[PROTOCOL]) : (row_update[PROTOCOL] = protocol)
|
|
||||||
login.nil? || login.empty? ? (row_update[LOGIN] = row[LOGIN]) : (row_update[LOGIN] = login)
|
|
||||||
passwd.nil? || passwd.empty? ? (row_update[PASSWORD] = row[PASSWORD]) : (row_update[PASSWORD] = passwd)
|
|
||||||
port.nil? || port.empty? ? (row_update[PORT] = row[PORT]) : (row_update[PORT] = port)
|
|
||||||
comment.nil? || comment.empty? ? (row_update[COMMENT] = row[COMMENT]) : (row_update[COMMENT] = comment)
|
|
||||||
|
|
||||||
@data[id] = row_update
|
|
||||||
|
|
||||||
return true
|
id.nil? || id.empty? ? (row_update[ID] = MPW.generatePassword(16)) : (row_update[ID] = row[ID])
|
||||||
else
|
name.nil? || name.empty? ? (row_update[NAME] = row[NAME]) : (row_update[NAME] = name)
|
||||||
@error_msg = I18n.t('error.update.id_no_exist', :id => id)
|
group.nil? || group.empty? ? (row_update[GROUP] = row[GROUP]) : (row_update[GROUP] = group)
|
||||||
|
server.nil? || server.empty? ? (row_update[SERVER] = row[SERVER]) : (row_update[SERVER] = server)
|
||||||
|
protocol.nil? || protocol.empty? ? (row_update[PROTOCOL] = row[PROTOCOL]) : (row_update[PROTOCOL] = protocol)
|
||||||
|
login.nil? || login.empty? ? (row_update[LOGIN] = row[LOGIN]) : (row_update[LOGIN] = login)
|
||||||
|
passwd.nil? || passwd.empty? ? (row_update[PASSWORD] = row[PASSWORD]) : (row_update[PASSWORD] = passwd)
|
||||||
|
port.nil? || port.empty? ? (row_update[PORT] = row[PORT]) : (row_update[PORT] = port)
|
||||||
|
comment.nil? || comment.empty? ? (row_update[COMMENT] = row[COMMENT]) : (row_update[COMMENT] = comment)
|
||||||
|
|
||||||
|
if row_update[NAME].nil? || row_update[NAME].empty?
|
||||||
|
@error_msg = I18n.t('error.update.name_empty')
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if update
|
||||||
|
@data[i] = row_update
|
||||||
|
else
|
||||||
|
@data.push(row_update)
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
# Remove an item
|
# Remove an item
|
||||||
# @args: id -> the item's identifiant
|
# @args: id -> the item's identifiant
|
||||||
# @rtrn: true if the item has been deleted
|
# @rtrn: true if the item has been deleted
|
||||||
def remove(id)
|
def remove(id)
|
||||||
if not @data.delete_at(id.to_i).nil?
|
i = 0
|
||||||
return true
|
@data.each do |row|
|
||||||
else
|
if row[ID] == id
|
||||||
@error_msg = I18n.t('error.delete.id_no_exist', :id => id)
|
@data.delete_at(i)
|
||||||
return false
|
return true
|
||||||
|
end
|
||||||
|
i += 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@error_msg = I18n.t('error.delete.id_no_exist', :id => id)
|
||||||
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
# Export to csv
|
# Export to csv
|
||||||
|
@ -285,7 +195,7 @@ class MPW
|
||||||
begin
|
begin
|
||||||
File.open(file, 'w+') do |file|
|
File.open(file, 'w+') do |file|
|
||||||
@data.each do |row|
|
@data.each do |row|
|
||||||
row.delete_at(ID)
|
row.delete_at(ID).delete_at(DATE)
|
||||||
file << row.to_csv
|
file << row.to_csv
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -309,7 +219,7 @@ class MPW
|
||||||
return false
|
return false
|
||||||
else
|
else
|
||||||
row = line.parse_csv.unshift(0)
|
row = line.parse_csv.unshift(0)
|
||||||
if not add(row[NAME], row[GROUP], row[SERVER], row[PROTOCOL], row[LOGIN], row[PASSWORD], row[PORT], row[COMMENT])
|
if not update(row[NAME], row[GROUP], row[SERVER], row[PROTOCOL], row[LOGIN], row[PASSWORD], row[PORT], row[COMMENT])
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -322,7 +232,7 @@ class MPW
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Return
|
# Return a preview import
|
||||||
# @args: file -> path to file import
|
# @args: file -> path to file import
|
||||||
# @rtrn: an array with the items to import, if there is an error return false
|
# @rtrn: an array with the items to import, if there is an error return false
|
||||||
def importPreview(file)
|
def importPreview(file)
|
||||||
|
@ -349,6 +259,50 @@ class MPW
|
||||||
end
|
end
|
||||||
end
|
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)
|
||||||
|
if !data_remote.instance_of?(Array)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
@data.each do |l|
|
||||||
|
j = 0
|
||||||
|
update = false
|
||||||
|
|
||||||
|
# Update item
|
||||||
|
data_remote.each do |r|
|
||||||
|
if l[ID] == r[ID]
|
||||||
|
if l[DATE].to_i < r[DATE].to_i
|
||||||
|
update(r[NAME], r[GROUP], r[SERVER], r[PROTOCOL], r[LOGIN], r[PASSWORD], r[PORT], r[COMMENT], l[ID])
|
||||||
|
end
|
||||||
|
update = true
|
||||||
|
data_remote.delete_at(j)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
j += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
# Delete an old item
|
||||||
|
if !update && l[DATE].to_i < last_update
|
||||||
|
remove(l[ID])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Add item
|
||||||
|
data_remote.each do |r|
|
||||||
|
if r[DATE].to_i > last_update
|
||||||
|
update(r[NAME], r[GROUP], r[SERVER], r[PROTOCOL], r[LOGIN], r[PASSWORD], r[PORT], r[COMMENT], r[ID])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
encrypt()
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
# Generate a random password
|
# Generate a random password
|
||||||
# @args: length -> the length password
|
# @args: length -> the length password
|
||||||
# @rtrn: a random string
|
# @rtrn: a random string
|
||||||
|
@ -367,7 +321,6 @@ class MPW
|
||||||
result << ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(length).join
|
result << ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(length).join
|
||||||
|
|
||||||
return result
|
return result
|
||||||
#return ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]-%w(0 1 I O l i o)).sample(length).join
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
130
lib/MPWConfig.rb
Normal file
130
lib/MPWConfig.rb
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
#!/usr/bin/ruby
|
||||||
|
# author: nishiki
|
||||||
|
# mail: nishiki@yaegashi.fr
|
||||||
|
# info: a simple script who manage your passwords
|
||||||
|
|
||||||
|
require 'rubygems'
|
||||||
|
require 'yaml'
|
||||||
|
require 'i18n'
|
||||||
|
|
||||||
|
class MPWConfig
|
||||||
|
|
||||||
|
attr_accessor :error_msg
|
||||||
|
|
||||||
|
attr_accessor :key
|
||||||
|
attr_accessor :lang
|
||||||
|
attr_accessor :file_gpg
|
||||||
|
attr_accessor :timeout_pwd
|
||||||
|
attr_accessor :last_update
|
||||||
|
attr_accessor :sync_host
|
||||||
|
attr_accessor :sync_port
|
||||||
|
attr_accessor :sync_pwd
|
||||||
|
attr_accessor :sync_suffix
|
||||||
|
attr_accessor :last_update
|
||||||
|
|
||||||
|
# Constructor
|
||||||
|
# @args: file_config -> the specify config file
|
||||||
|
def initialize(file_config=nil)
|
||||||
|
@error_msg = nil
|
||||||
|
@file_config = "#{Dir.home()}/.mpw.cfg"
|
||||||
|
|
||||||
|
if !file_config.nil? && !file_config.empty?
|
||||||
|
@file_config = file_config
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Create a new config file
|
||||||
|
# @args: key -> the gpg key to encrypt
|
||||||
|
# lang -> the software language
|
||||||
|
# file_gpg -> the file who is encrypted
|
||||||
|
# timeout_pwd -> time to save the password
|
||||||
|
# @rtrn: true if le config file is create
|
||||||
|
def setup(key, lang, file_gpg, timeout_pwd)
|
||||||
|
|
||||||
|
if not key =~ /[a-zA-Z0-9.-_]+\@[a-zA-Z0-9]+\.[a-zA-Z]+/
|
||||||
|
@error_msg = I18n.t('error.config.key_bad_format')
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if file_gpg.empty?
|
||||||
|
file_gpg = "#{Dir.home()}/.mpw.gpg"
|
||||||
|
end
|
||||||
|
|
||||||
|
timeout_pwd.empty? ? (timeout_pwd = 60) : (timeout_pwd = timeout_pwd.to_i)
|
||||||
|
|
||||||
|
config = {'config' => {'key' => key,
|
||||||
|
'lang' => lang,
|
||||||
|
'file_gpg' => file_gpg,
|
||||||
|
'timeout_pwd' => timeout_pwd,
|
||||||
|
'sync_host' => host,
|
||||||
|
'sync_port' => port,
|
||||||
|
'sync_pwd' => password,
|
||||||
|
'sync_suffix' => suffix,
|
||||||
|
'last_update' => 0 }}
|
||||||
|
|
||||||
|
begin
|
||||||
|
File.open(@file_config, 'w') do |file|
|
||||||
|
file << config.to_yaml
|
||||||
|
end
|
||||||
|
rescue Exception => e
|
||||||
|
@error_msg = "#{I18n.t('error.config.write')}\n#{e}"
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check the config file
|
||||||
|
# @rtrn: true if the config file is correct
|
||||||
|
def checkconfig()
|
||||||
|
begin
|
||||||
|
config = YAML::load_file(@file_config)
|
||||||
|
@key = config['config']['key']
|
||||||
|
@lang = config['config']['lang']
|
||||||
|
@file_gpg = config['config']['file_gpg']
|
||||||
|
@timeout_pwd = config['config']['timeout_pwd'].to_i
|
||||||
|
@sync_host = config['config']['sync_host']
|
||||||
|
@sync_port = config['config']['sync_port']
|
||||||
|
@sync_pwd = config['config']['sync_pwd']
|
||||||
|
@sync_suffix = config['config']['sync_suffix']
|
||||||
|
@last_update = config['config']['last_update'].to_i
|
||||||
|
|
||||||
|
if @key.empty? || @file_gpg.empty?
|
||||||
|
@error_msg = I18n.t('error.config.check')
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
I18n.locale = @lang.to_sym
|
||||||
|
|
||||||
|
rescue Exception => e
|
||||||
|
@error_msg = "#{I18n.t('error.config.check')}\n#{e}"
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
def setLastUpdate()
|
||||||
|
config = {'config' => {'key' => @key,
|
||||||
|
'lang' => @lang,
|
||||||
|
'file_gpg' => @file_gpg,
|
||||||
|
'timeout_pwd' => @timeout_pwd,
|
||||||
|
'sync_host' => @sync_host,
|
||||||
|
'sync_port' => @sync_port,
|
||||||
|
'sync_pwd' => @sync_pwd,
|
||||||
|
'sync_suffix' => @sync_suffix,
|
||||||
|
'last_update' => Time.now.to_i }}
|
||||||
|
|
||||||
|
begin
|
||||||
|
File.open(@file_config, 'w') do |file|
|
||||||
|
file << config.to_yaml
|
||||||
|
end
|
||||||
|
rescue Exception => e
|
||||||
|
@error_msg = "#{I18n.t('error.config.write')}\n#{e}"
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
295
lib/Server.rb
Normal file
295
lib/Server.rb
Normal file
|
@ -0,0 +1,295 @@
|
||||||
|
#!/usr/bin/ruby
|
||||||
|
|
||||||
|
require 'socket'
|
||||||
|
require 'json'
|
||||||
|
require 'highline/import'
|
||||||
|
require 'digest'
|
||||||
|
|
||||||
|
require "#{APP_ROOT}/lib/MPW.rb"
|
||||||
|
|
||||||
|
class Server
|
||||||
|
|
||||||
|
attr_accessor :error_msg
|
||||||
|
|
||||||
|
# Constructor
|
||||||
|
def initialize()
|
||||||
|
YAML::ENGINE.yamler='syck'
|
||||||
|
end
|
||||||
|
|
||||||
|
# Start the server
|
||||||
|
def start()
|
||||||
|
server = TCPServer.open(@host, @port)
|
||||||
|
loop do
|
||||||
|
Thread.start(server.accept) do |client|
|
||||||
|
while true do
|
||||||
|
msg = getClientMessage(client)
|
||||||
|
|
||||||
|
if !msg
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
if msg['gpg_key'].nil? || msg['gpg_key'].empty? || msg['password'].nil? || msg['password'].empty?
|
||||||
|
closeConnection(client)
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
case msg['action']
|
||||||
|
when 'get'
|
||||||
|
client.puts getFile(msg)
|
||||||
|
when 'update'
|
||||||
|
client.puts updateFile(msg)
|
||||||
|
when 'delete'
|
||||||
|
client.puts deleteFile(msg)
|
||||||
|
when 'close'
|
||||||
|
closeConnection(client)
|
||||||
|
else
|
||||||
|
client.puts 'Unknown command'
|
||||||
|
closeConnection(client)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get a gpg file
|
||||||
|
# @args: msg -> message puts by the client
|
||||||
|
# @rtrn: json message
|
||||||
|
def getFile(msg)
|
||||||
|
gpg_key = msg['gpg_key'].sub('@', '_')
|
||||||
|
|
||||||
|
if msg['suffix'].nil? || msg['suffix'].empty?
|
||||||
|
file_gpg = "#{@data_dir}/#{gpg_key}.yml"
|
||||||
|
else
|
||||||
|
file_gpg = "#{@data_dir}/#{gpg_key}-#{msg['suffix']}.yml"
|
||||||
|
end
|
||||||
|
|
||||||
|
if File.exist?(file_gpg)
|
||||||
|
gpg_data = YAML::load_file(file_gpg)
|
||||||
|
salt = gpg_data['gpg']['salt']
|
||||||
|
hash = gpg_data['gpg']['hash']
|
||||||
|
data = gpg_data['gpg']['data']
|
||||||
|
|
||||||
|
if isAuthorized?(msg['password'], salt, hash)
|
||||||
|
send_msg = {:action => 'get',
|
||||||
|
:gpg_key => msg['gpg_key'],
|
||||||
|
:msg => 'done',
|
||||||
|
:data => data}
|
||||||
|
else
|
||||||
|
send_msg = {:action => 'get',
|
||||||
|
:gpg_key => msg['gpg_key'],
|
||||||
|
:msg => 'fail',
|
||||||
|
:error => 'not_authorized'}
|
||||||
|
end
|
||||||
|
else
|
||||||
|
send_msg = {:action => 'get',
|
||||||
|
:gpg_key => msg['gpg_key'],
|
||||||
|
:data => '',
|
||||||
|
:msg => 'fail',
|
||||||
|
:error => 'file_not_exist'}
|
||||||
|
end
|
||||||
|
|
||||||
|
return send_msg.to_json
|
||||||
|
end
|
||||||
|
|
||||||
|
# Update a file
|
||||||
|
# @args: msg -> message puts by the client
|
||||||
|
# @rtrn: json message
|
||||||
|
def updateFile(msg)
|
||||||
|
gpg_key = msg['gpg_key'].sub('@', '_')
|
||||||
|
data = msg['data']
|
||||||
|
|
||||||
|
if data.nil? || data.empty?
|
||||||
|
send_msg = {:action => 'update',
|
||||||
|
:gpg_key => msg['gpg_key'],
|
||||||
|
:msg => 'fail',
|
||||||
|
:error => 'no_data'}
|
||||||
|
|
||||||
|
return send_msg.to_json
|
||||||
|
end
|
||||||
|
|
||||||
|
if msg['suffix'].nil? || msg['suffix'].empty?
|
||||||
|
file_gpg = "#{@data_dir}/#{gpg_key}.yml"
|
||||||
|
else
|
||||||
|
file_gpg = "#{@data_dir}/#{gpg_key}-#{msg['suffix']}.yml"
|
||||||
|
end
|
||||||
|
|
||||||
|
if File.exist?(file_gpg)
|
||||||
|
gpg_data = YAML::load_file(file_gpg)
|
||||||
|
salt = gpg_data['gpg']['salt']
|
||||||
|
hash = gpg_data['gpg']['hash']
|
||||||
|
|
||||||
|
else
|
||||||
|
salt = MPW.generatePassword(4)
|
||||||
|
hash = Digest::SHA256.hexdigest(salt + msg['password'])
|
||||||
|
end
|
||||||
|
|
||||||
|
if isAuthorized?(msg['password'], salt, hash)
|
||||||
|
begin
|
||||||
|
config = {'gpg' => {'salt' => salt,
|
||||||
|
'hash' => hash,
|
||||||
|
'data' => data}}
|
||||||
|
|
||||||
|
File.open(file_gpg, 'w+') do |file|
|
||||||
|
file << config.to_yaml
|
||||||
|
end
|
||||||
|
|
||||||
|
send_msg = {:action => 'update',
|
||||||
|
:gpg_key => msg['gpg_key'],
|
||||||
|
:msg => 'done'}
|
||||||
|
rescue Exception => e
|
||||||
|
send_msg = {:action => 'update',
|
||||||
|
:gpg_key => msg['gpg_key'],
|
||||||
|
:msg => 'fail',
|
||||||
|
:error => 'server_error'}
|
||||||
|
end
|
||||||
|
else
|
||||||
|
send_msg = {:action => 'update',
|
||||||
|
:gpg_key => msg['gpg_key'],
|
||||||
|
:msg => 'fail',
|
||||||
|
:error => 'not_autorized'}
|
||||||
|
end
|
||||||
|
|
||||||
|
return send_msg.to_json
|
||||||
|
end
|
||||||
|
|
||||||
|
# Remove a gpg file
|
||||||
|
# @args: msg -> message puts by the client
|
||||||
|
# @rtrn: json message
|
||||||
|
def deleteFile(msg)
|
||||||
|
gpg_key = msg['gpg_key'].sub('@', '_')
|
||||||
|
|
||||||
|
if msg['suffix'].nil? || msg['suffix'].empty?
|
||||||
|
file_gpg = "#{@data_dir}/#{gpg_key}.yml"
|
||||||
|
else
|
||||||
|
file_gpg = "#{@data_dir}/#{gpg_key}-#{msg['suffix']}.yml"
|
||||||
|
end
|
||||||
|
|
||||||
|
if !File.exist?(file_gpg)
|
||||||
|
send_msg = {:action => 'delete',
|
||||||
|
:gpg_key => msg['gpg_key'],
|
||||||
|
:msg => 'delete_fail',
|
||||||
|
:error => 'file_not_exist'}
|
||||||
|
|
||||||
|
return send_msg.to_json
|
||||||
|
end
|
||||||
|
|
||||||
|
gpg_data = YAML::load_file(file_gpg)
|
||||||
|
salt = gpg_data['gpg']['salt']
|
||||||
|
hash = gpg_data['gpg']['hash']
|
||||||
|
|
||||||
|
if isAuthorized?(msg['password'], salt, hash)
|
||||||
|
begin
|
||||||
|
File.unlink(file_gpg)
|
||||||
|
|
||||||
|
send_msg = {:action => 'delete',
|
||||||
|
:gpg_key => msg['gpg_key'],
|
||||||
|
:msg => 'delete_done'}
|
||||||
|
rescue Exception => e
|
||||||
|
send_msg = {:action => 'delete',
|
||||||
|
:gpg_key => msg['gpg_key'],
|
||||||
|
:msg => 'delete_fail',
|
||||||
|
:error => e}
|
||||||
|
end
|
||||||
|
else
|
||||||
|
send_msg = {:action => 'delete',
|
||||||
|
:gpg_key => msg['gpg_key'],
|
||||||
|
:msg => 'delete_fail',
|
||||||
|
:error => 'not_autorized'}
|
||||||
|
end
|
||||||
|
|
||||||
|
return send_msg.to_json
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check is the hash equal the password with the salt
|
||||||
|
# @args: password -> the user password
|
||||||
|
# salt -> the salt
|
||||||
|
# hash -> the hash of the password with the salt
|
||||||
|
# @rtrn: true is is good, else false
|
||||||
|
def isAuthorized?(password, salt, hash)
|
||||||
|
if hash == Digest::SHA256.hexdigest(salt + password)
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get message to client
|
||||||
|
# @args: client -> client connection
|
||||||
|
# @rtrn: array of the json string, or false if isn't json message
|
||||||
|
def getClientMessage(client)
|
||||||
|
begin
|
||||||
|
msg = client.gets
|
||||||
|
return JSON.parse(msg)
|
||||||
|
rescue
|
||||||
|
closeConnection(client)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Close the client connection
|
||||||
|
# @args: client -> client connection
|
||||||
|
def closeConnection(client)
|
||||||
|
client.puts "Closing the connection. Bye!"
|
||||||
|
client.close
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check the config file
|
||||||
|
# @args: file_config -> the configuration file
|
||||||
|
# @rtrn: true if the config file is correct
|
||||||
|
def checkconfig(file_config)
|
||||||
|
begin
|
||||||
|
config = YAML::load_file(file_config)
|
||||||
|
@host = config['config']['host']
|
||||||
|
@port = config['config']['port'].to_i
|
||||||
|
@data_dir = config['config']['data_dir']
|
||||||
|
@timeout = config['config']['timeout'].to_i
|
||||||
|
|
||||||
|
if @host.empty? || @port <= 0 || @data_dir.empty?
|
||||||
|
puts I18n.t('server.checkconfig.fail')
|
||||||
|
puts I18n.t('server.checkconfig.empty')
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if !Dir.exist?(@data_dir)
|
||||||
|
puts I18n.t('server.checkconfig.fail')
|
||||||
|
puts I18n.t('server.checkconfig.datadir')
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
rescue Exception => e
|
||||||
|
puts "#{I18n.t('server.checkconfig.fail')}\n#{e}"
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
# Create a new config file
|
||||||
|
# @args: file_config -> the configuration file
|
||||||
|
# @rtrn: true if le config file is create
|
||||||
|
def setup(file_config)
|
||||||
|
puts I18n.t('server.form.setup.title')
|
||||||
|
puts '--------------------'
|
||||||
|
host = ask(I18n.t('server.form.setup.host')).to_s
|
||||||
|
port = ask(I18n.t('server.form.setup.port')).to_s
|
||||||
|
data_dir = ask(I18n.t('server.form.setup.data_dir')).to_s
|
||||||
|
timeout = ask(I18n.t('server.form.setup.timeout')).to_s
|
||||||
|
|
||||||
|
config = {'config' => {'host' => host,
|
||||||
|
'port' => port,
|
||||||
|
'data_dir' => data_dir,
|
||||||
|
'timeout' => timeout}}
|
||||||
|
|
||||||
|
begin
|
||||||
|
File.open(file_config, 'w') do |file|
|
||||||
|
file << config.to_yaml
|
||||||
|
end
|
||||||
|
rescue Exception => e
|
||||||
|
puts "#{I18n.t('server.formsetup.not_valid')}\n#{e}"
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
133
lib/Sync.rb
Normal file
133
lib/Sync.rb
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
#!/usr/bin/ruby
|
||||||
|
# author: nishiki
|
||||||
|
# mail: nishiki@yaegashi.fr
|
||||||
|
# info: a simple script who manage your passwords
|
||||||
|
|
||||||
|
require 'rubygems'
|
||||||
|
require 'i18n'
|
||||||
|
require 'socket'
|
||||||
|
require 'json'
|
||||||
|
|
||||||
|
require "#{APP_ROOT}/lib/MPW.rb"
|
||||||
|
|
||||||
|
class Sync
|
||||||
|
|
||||||
|
attr_accessor :error_msg
|
||||||
|
|
||||||
|
# Constructor
|
||||||
|
def initialize()
|
||||||
|
@error_msg = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# Disable the sync
|
||||||
|
def disable()
|
||||||
|
@sync = false
|
||||||
|
end
|
||||||
|
|
||||||
|
# Connect to server
|
||||||
|
# @args: host -> the server host
|
||||||
|
# port -> ther connection port
|
||||||
|
# gpg_key -> the gpg key
|
||||||
|
# password -> the remote password
|
||||||
|
# suffix -> the suffix file
|
||||||
|
# @rtrn: false if the connection fail
|
||||||
|
def connect(host, port, gpg_key, password, suffix=nil)
|
||||||
|
@gpg_key = gpg_key
|
||||||
|
@password = password
|
||||||
|
@suffix = suffix
|
||||||
|
|
||||||
|
begin
|
||||||
|
@socket= TCPSocket.new(host, port)
|
||||||
|
@sync = true
|
||||||
|
rescue Exception => e
|
||||||
|
@error_msg = "#{I18n.t('error.sync.connection')}\n#{e}"
|
||||||
|
@sync = false
|
||||||
|
end
|
||||||
|
|
||||||
|
return @sync
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get data on server
|
||||||
|
# @args: gpg_password -> the gpg password
|
||||||
|
# @rtrn: nil if nothing data or error
|
||||||
|
def get(gpg_password)
|
||||||
|
if !@sync
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
send_msg = {:action => 'get',
|
||||||
|
:gpg_key => @gpg_key,
|
||||||
|
:password => @password,
|
||||||
|
:suffix => @suffix}
|
||||||
|
|
||||||
|
@socket.puts send_msg.to_json
|
||||||
|
msg = JSON.parse(@socket.gets)
|
||||||
|
|
||||||
|
case msg['error']
|
||||||
|
when nil, 'file_not_exist'
|
||||||
|
tmp_file = "/tmp/mpw-#{MPW.generatePassword()}.gpg"
|
||||||
|
File.open(tmp_file, 'w') do |file|
|
||||||
|
file << msg['data']
|
||||||
|
end
|
||||||
|
|
||||||
|
@mpw = MPW.new(tmp_file)
|
||||||
|
if !@mpw.decrypt(gpg_password)
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
File.unlink(tmp_file)
|
||||||
|
|
||||||
|
return @mpw.search()
|
||||||
|
when 'not_authorized'
|
||||||
|
@error_msg = "#{I18n.t('error.sync.not_authorized')}\n#{e}"
|
||||||
|
else
|
||||||
|
@error_msg = "#{I18n.t('error.sync.unknown')}\n#{e}"
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# Update the remote data
|
||||||
|
# @args: data -> the data to send on server
|
||||||
|
# @rtrn: false if there is a problem
|
||||||
|
def update(data)
|
||||||
|
if !@sync
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
send_msg = {:action => 'update',
|
||||||
|
:gpg_key => @gpg_key,
|
||||||
|
:password => @password,
|
||||||
|
:suffix => @suffix,
|
||||||
|
:data => data}
|
||||||
|
|
||||||
|
@socket.puts send_msg.to_json
|
||||||
|
msg = JSON.parse(@socket.gets)
|
||||||
|
|
||||||
|
case msg['error']
|
||||||
|
when nil
|
||||||
|
return true
|
||||||
|
when 'not_authorized'
|
||||||
|
@error_msg = "#{I18n.t('error.sync.not_authorized')}\n#{e}"
|
||||||
|
when 'no_data'
|
||||||
|
@error_msg = "#{I18n.t('error.sync.no_data')}\n#{e}"
|
||||||
|
else
|
||||||
|
@error_msg = "#{I18n.t('error.sync.unknown')}\n#{e}"
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete()
|
||||||
|
end
|
||||||
|
|
||||||
|
# Close the connection
|
||||||
|
def close()
|
||||||
|
if !@sync
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
send_msg = {:action => 'close'}
|
||||||
|
@socket.puts send_msg.to_json
|
||||||
|
end
|
||||||
|
end
|
23
mpw
23
mpw
|
@ -93,13 +93,17 @@ OptionParser.new do |opts|
|
||||||
end
|
end
|
||||||
end.parse!
|
end.parse!
|
||||||
|
|
||||||
|
config = MPWConfig.new(options[:config])
|
||||||
|
check_error = config.checkconfig()
|
||||||
|
|
||||||
cli = Cli.new(lang, options[:config])
|
cli = Cli.new(lang, config)
|
||||||
|
cli.sync()
|
||||||
|
|
||||||
|
# Setup a new config
|
||||||
|
if !check_error || !options[:setup].nil?
|
||||||
|
cli.setup(lang)
|
||||||
|
|
||||||
# Display the item's informations
|
# Display the item's informations
|
||||||
if not options[:setup].nil?
|
|
||||||
cli.setup()
|
|
||||||
|
|
||||||
elsif not options[:display].nil?
|
elsif not options[:display].nil?
|
||||||
cli.display(options[:display], options[:group], options[:type], options[:format])
|
cli.display(options[:display], options[:group], options[:type], options[:format])
|
||||||
|
|
||||||
|
@ -125,7 +129,16 @@ elsif not options[:import].nil?
|
||||||
|
|
||||||
# Interactive mode
|
# Interactive mode
|
||||||
else
|
else
|
||||||
cli.interactive
|
begin
|
||||||
|
cli.interactive()
|
||||||
|
rescue SystemExit, Interrupt
|
||||||
|
cli.sync()
|
||||||
|
cli = nil
|
||||||
|
return 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
cli.sync()
|
||||||
|
cli = nil
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
|
60
mpw-server
Executable file
60
mpw-server
Executable file
|
@ -0,0 +1,60 @@
|
||||||
|
#!/usr/bin/ruby
|
||||||
|
# author: nishiki
|
||||||
|
# mail: nishiki@yaegashi.fr
|
||||||
|
# info: a simple script who manage your passwords
|
||||||
|
|
||||||
|
require 'rubygems'
|
||||||
|
require 'optparse'
|
||||||
|
require 'pathname'
|
||||||
|
require 'locale'
|
||||||
|
require 'i18n'
|
||||||
|
|
||||||
|
APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)
|
||||||
|
require "#{APP_ROOT}/lib/Server.rb"
|
||||||
|
|
||||||
|
lang = Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
|
||||||
|
|
||||||
|
I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
|
||||||
|
I18n.load_path = Dir["#{APP_ROOT}/i18n/*.yml"]
|
||||||
|
I18n.default_locale = :en
|
||||||
|
I18n.locale = lang.to_sym
|
||||||
|
|
||||||
|
options = {}
|
||||||
|
OptionParser.new do |opts|
|
||||||
|
opts.banner = "#{I18n.t('server.option.usage')}: mpw-server -c CONFIG [options]"
|
||||||
|
|
||||||
|
opts.on("-c", "--config CONFIG", I18n.t('server.option.config')) do |config|
|
||||||
|
options[:config] = config
|
||||||
|
end
|
||||||
|
|
||||||
|
opts.on("-t", "--checkconfig", I18n.t('server.option.checkconfig')) do |b|
|
||||||
|
options[:checkconfig] = b
|
||||||
|
end
|
||||||
|
|
||||||
|
opts.on("-s", "--setup", I18n.t('server.option.setup')) do |b|
|
||||||
|
options[:setup] = b
|
||||||
|
end
|
||||||
|
|
||||||
|
opts.on("-h", "--help", I18n.t('server.option.help')) do |b|
|
||||||
|
puts opts
|
||||||
|
exit 0
|
||||||
|
end
|
||||||
|
end.parse!
|
||||||
|
|
||||||
|
if options[:config].nil? || options[:config].empty?
|
||||||
|
puts "#{I18n.t('server.option.usage')}: mpw-server -c CONFIG [options]"
|
||||||
|
exit 2
|
||||||
|
end
|
||||||
|
|
||||||
|
server = Server.new
|
||||||
|
|
||||||
|
if options[:checkconfig]
|
||||||
|
server.checkconfig(options[:config])
|
||||||
|
elsif options[:setup]
|
||||||
|
server.setup(options[:config])
|
||||||
|
else
|
||||||
|
server.checkconfig(options[:config])
|
||||||
|
server.start()
|
||||||
|
end
|
||||||
|
|
||||||
|
exit 0
|
Loading…
Add table
Reference in a new issue