mirror of
https://github.com/nishiki/manage-password.git
synced 2024-11-27 07:33:05 +00:00
add module MPW
This commit is contained in:
parent
7a50c4cf1c
commit
96fe71e07d
10 changed files with 642 additions and 632 deletions
140
MPW/Config.rb
Normal file
140
MPW/Config.rb
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
#!/usr/bin/ruby
|
||||||
|
# author: nishiki
|
||||||
|
# mail: nishiki@yaegashi.fr
|
||||||
|
# info: a simple script who manage your passwords
|
||||||
|
|
||||||
|
module MPW
|
||||||
|
|
||||||
|
require 'rubygems'
|
||||||
|
require 'yaml'
|
||||||
|
require 'i18n'
|
||||||
|
|
||||||
|
class Config
|
||||||
|
|
||||||
|
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
|
||||||
|
# sync_host -> the server host for synchronization
|
||||||
|
# sync_port -> the server port for synchronization
|
||||||
|
# sync_pwd -> the password for synchronization
|
||||||
|
# sync_suffix -> the suffix file (optionnal)
|
||||||
|
# @rtrn: true if le config file is create
|
||||||
|
def setup(key, lang, file_gpg, timeout_pwd, sync_host=nil, sync_port=nil, sync_pwd=nil, sync_suffix=nil)
|
||||||
|
|
||||||
|
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 = timeout_pwd.empty? ? 60 : timeout_pwd.to_i
|
||||||
|
|
||||||
|
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' => 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
|
||||||
|
|
||||||
|
# Set the last update when there is a sync
|
||||||
|
# @rtrn: true is the file has been updated
|
||||||
|
def set_last_update()
|
||||||
|
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
|
||||||
|
|
||||||
|
end
|
335
MPW/MPW.rb
Normal file
335
MPW/MPW.rb
Normal file
|
@ -0,0 +1,335 @@
|
||||||
|
#!/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'
|
||||||
|
|
||||||
|
class MPW
|
||||||
|
|
||||||
|
ID = 0
|
||||||
|
NAME = 1
|
||||||
|
GROUP = 2
|
||||||
|
PROTOCOL = 3
|
||||||
|
SERVER = 4
|
||||||
|
LOGIN = 5
|
||||||
|
PASSWORD = 6
|
||||||
|
PORT = 7
|
||||||
|
COMMENT = 8
|
||||||
|
DATE = 9
|
||||||
|
|
||||||
|
attr_accessor :error_msg
|
||||||
|
|
||||||
|
# Constructor
|
||||||
|
def initialize(file_gpg, key=nil)
|
||||||
|
@error_msg = nil
|
||||||
|
@file_gpg = file_gpg
|
||||||
|
@key = key
|
||||||
|
end
|
||||||
|
|
||||||
|
# Decrypt a gpg file
|
||||||
|
# @args: password -> the GPG key password
|
||||||
|
# @rtrn: true if data has been decrypted
|
||||||
|
def decrypt(passwd=nil)
|
||||||
|
@data = Array.new
|
||||||
|
|
||||||
|
begin
|
||||||
|
if File.exist?(@file_gpg)
|
||||||
|
crypto = GPGME::Crypto.new(:armor => true)
|
||||||
|
data_decrypt = crypto.decrypt(IO.read(@file_gpg), :password => passwd).read
|
||||||
|
|
||||||
|
data_decrypt.lines do |line|
|
||||||
|
@data.push(line.parse_csv)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
rescue Exception => e
|
||||||
|
@error_msg = "#{I18n.t('error.gpg_file.decrypt')}\n#{e}"
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Encrypt a file
|
||||||
|
# @rtrn: true if the file has been encrypted
|
||||||
|
def encrypt()
|
||||||
|
begin
|
||||||
|
crypto = GPGME::Crypto.new(:armor => true)
|
||||||
|
file_gpg = File.open(@file_gpg, 'w+')
|
||||||
|
|
||||||
|
data_to_encrypt = ''
|
||||||
|
@data.each do |row|
|
||||||
|
data_to_encrypt << row.to_csv
|
||||||
|
end
|
||||||
|
|
||||||
|
crypto.encrypt(data_to_encrypt, :recipients => @key, :output => file_gpg)
|
||||||
|
file_gpg.close
|
||||||
|
|
||||||
|
return true
|
||||||
|
rescue Exception => e
|
||||||
|
@error_msg = "#{I18n.t('error.gpg_file.encrypt')}\n#{e}"
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
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)
|
||||||
|
result = Array.new()
|
||||||
|
|
||||||
|
if !search.nil?
|
||||||
|
search = search.downcase
|
||||||
|
end
|
||||||
|
search = search.force_encoding('ASCII-8BIT')
|
||||||
|
|
||||||
|
@data.each do |row|
|
||||||
|
name = row[NAME].nil? ? nil : row[NAME].downcase
|
||||||
|
server = row[SERVER].nil? ? nil : row[SERVER].downcase
|
||||||
|
comment = row[COMMENT].nil? ? nil : row[COMMENT].downcase
|
||||||
|
|
||||||
|
if name =~ /^.*#{search}.*$/ || server =~ /^.*#{search}.*$/ || comment =~ /^.*#{search}.*$/
|
||||||
|
if (protocol.nil? || protocol.eql?(row[PROTOCOL])) && (group.nil? || group.eql?(row[GROUP]))
|
||||||
|
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|
|
||||||
|
if row[ID] == id
|
||||||
|
return row
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return Array.new()
|
||||||
|
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)
|
||||||
|
row = Array.new()
|
||||||
|
update = false
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
@data.each do |r|
|
||||||
|
if r[ID] == id
|
||||||
|
row = r
|
||||||
|
update = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
i += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if port.to_i <= 0
|
||||||
|
port = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
row_update = Array.new()
|
||||||
|
row_update[DATE] = Time.now.to_i
|
||||||
|
|
||||||
|
row_update[ID] = id.nil? || id.empty? ? MPW.password(16) : id
|
||||||
|
row_update[NAME] = name.nil? || name.empty? ? row[NAME] : name
|
||||||
|
row_update[GROUP] = group.nil? || group.empty? ? row[GROUP] : group
|
||||||
|
row_update[SERVER] = server.nil? || server.empty? ? row[SERVER] : server
|
||||||
|
row_update[PROTOCOL] = protocol.nil? || protocol.empty? ? row[PROTOCOL] : protocol
|
||||||
|
row_update[LOGIN] = login.nil? || login.empty? ? row[LOGIN] : login
|
||||||
|
row_update[PASSWORD] = passwd.nil? || passwd.empty? ? row[PASSWORD] : passwd
|
||||||
|
row_update[PORT] = port.nil? || port.empty? ? row[PORT] : port
|
||||||
|
row_update[COMMENT] = comment.nil? || comment.empty? ? row[COMMENT] : comment
|
||||||
|
|
||||||
|
row_update[] = row_update[NAME].nil? ? nil : row_update[NAME].force_encoding('ASCII-8BIT')
|
||||||
|
row_update[] = row_update[GROUP].nil? ? nil : row_update[GROUP].force_encoding('ASCII-8BIT')
|
||||||
|
row_update[] = row_update[SERVER].nil? ? nil : row_update[SERVER].force_encoding('ASCII-8BIT')
|
||||||
|
row_update[] = row_update[PROTOCOL].nil? ? nil : row_update[PROTOCOL].force_encoding('ASCII-8BIT')
|
||||||
|
row_update[] = row_update[LOGIN].nil? ? nil : row_update[LOGIN].force_encoding('ASCII-8BIT')
|
||||||
|
row_update[] = row_update[PASSWORD].nil? ? nil : row_update[PASSWORD].force_encoding('ASCII-8BIT')
|
||||||
|
row_update[] = row_update[COMMENT].nil? ? nil : row_update[COMMENT].force_encoding('ASCII-8BIT')
|
||||||
|
|
||||||
|
if row_update[NAME].nil? || row_update[NAME].empty?
|
||||||
|
@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|
|
||||||
|
if row[ID] == id
|
||||||
|
@data.delete_at(i)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
i += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
@error_msg = I18n.t('error.delete.id_no_exist', :id => id)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
# Export to csv
|
||||||
|
# @args: file -> a string to match
|
||||||
|
# @rtrn: true if export work
|
||||||
|
def export(file)
|
||||||
|
begin
|
||||||
|
File.open(file, 'w+') do |file|
|
||||||
|
@data.each do |row|
|
||||||
|
row.delete_at(ID).delete_at(DATE)
|
||||||
|
file << row.to_csv
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
rescue Exception => e
|
||||||
|
@error_msg = "#{I18n.t('error.export.write', :file => file)}\n#{e}"
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Import to csv
|
||||||
|
# @args: file -> path to file import
|
||||||
|
# @rtrn: true if the import work
|
||||||
|
def import(file)
|
||||||
|
begin
|
||||||
|
data_new = IO.read(file)
|
||||||
|
data_new.lines do |line|
|
||||||
|
if not line =~ /(.*,){6}/
|
||||||
|
@error_msg = I18n.t('error.import.bad_format')
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
row = line.parse_csv.unshift(0)
|
||||||
|
if not update(row[NAME], row[GROUP], row[SERVER], row[PROTOCOL], row[LOGIN], row[PASSWORD], row[PORT], row[COMMENT])
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
rescue Exception => e
|
||||||
|
@error_msg = "#{I18n.t('error.import.read', :file => file)}\n#{e}"
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
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)
|
||||||
|
begin
|
||||||
|
result = Array.new()
|
||||||
|
id = 0
|
||||||
|
|
||||||
|
data = IO.read(file)
|
||||||
|
data.lines do |line|
|
||||||
|
if not line =~ /(.*,){6}/
|
||||||
|
@error_msg = I18n.t('error.import.bad_format')
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
result.push(line.parse_csv.unshift(id))
|
||||||
|
end
|
||||||
|
|
||||||
|
id += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
return result
|
||||||
|
rescue Exception => e
|
||||||
|
@error_msg = "#{I18n.t('error.import.read', :file => file)}\n#{e}"
|
||||||
|
return false
|
||||||
|
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
|
||||||
|
|
||||||
|
return encrypt()
|
||||||
|
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
|
130
MPW/Sync/MPW.rb
Normal file
130
MPW/Sync/MPW.rb
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
#!/usr/bin/ruby
|
||||||
|
# author: nishiki
|
||||||
|
# mail: nishiki@yaegashi.fr
|
||||||
|
# info: a simple script who manage your passwords
|
||||||
|
|
||||||
|
module MPW
|
||||||
|
|
||||||
|
module Sync
|
||||||
|
|
||||||
|
require 'rubygems'
|
||||||
|
require 'i18n'
|
||||||
|
require 'socket'
|
||||||
|
require 'json'
|
||||||
|
|
||||||
|
class MPW
|
||||||
|
|
||||||
|
attr_accessor :error_msg
|
||||||
|
attr_accessor :enable
|
||||||
|
|
||||||
|
# Constructor
|
||||||
|
def initialize()
|
||||||
|
@error_msg = nil
|
||||||
|
@enable = 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)
|
||||||
|
@enable = true
|
||||||
|
rescue Exception => e
|
||||||
|
@error_msg = "#{I18n.t('error.sync.connection')}\n#{e}"
|
||||||
|
@enable = false
|
||||||
|
end
|
||||||
|
|
||||||
|
return @enable
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get data on server
|
||||||
|
# @args: gpg_password -> the gpg password
|
||||||
|
# @rtrn: nil if nothing data or error
|
||||||
|
def get(gpg_password)
|
||||||
|
if !@enable
|
||||||
|
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)
|
||||||
|
|
||||||
|
if !defined?(msg['error'])
|
||||||
|
@error_msg = I18n.t('error.sync.communication')
|
||||||
|
return nil
|
||||||
|
elsif msg['error'].nil?
|
||||||
|
tmp_file = "/tmp/mpw-#{MPW.password()}.gpg"
|
||||||
|
File.open(tmp_file, 'w') do |file|
|
||||||
|
file << msg['data']
|
||||||
|
end
|
||||||
|
|
||||||
|
@mpw = MPW.new(tmp_file)
|
||||||
|
if !@mpw.decrypt(gpg_password)
|
||||||
|
puts @mpw.error_msg
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
File.unlink(tmp_file)
|
||||||
|
return @mpw.search()
|
||||||
|
else
|
||||||
|
@error_msg = I18n.t(msg['error'])
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
# Update the remote data
|
||||||
|
# @args: data -> the data to send on server
|
||||||
|
# @rtrn: false if there is a problem
|
||||||
|
def update(data)
|
||||||
|
if !@enable
|
||||||
|
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)
|
||||||
|
|
||||||
|
if !defined?(msg['error'])
|
||||||
|
@error_msg = I18n.t('error.sync.communication')
|
||||||
|
return false
|
||||||
|
elsif msg['error'].nil?
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
@error_msg = I18n.t(msg['error'])
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Close the connection
|
||||||
|
def close()
|
||||||
|
if !@enable
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
send_msg = {:action => 'close'}
|
||||||
|
@socket.puts send_msg.to_json
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -10,9 +10,8 @@ require 'readline'
|
||||||
require 'i18n'
|
require 'i18n'
|
||||||
require 'yaml'
|
require 'yaml'
|
||||||
|
|
||||||
require "#{APP_ROOT}/lib/MPW.rb"
|
require "#{APP_ROOT}/MPW/MPW"
|
||||||
require "#{APP_ROOT}/lib/MPWConfig.rb"
|
require "#{APP_ROOT}/MPW/Sync/MPW"
|
||||||
require "#{APP_ROOT}/lib/Sync.rb"
|
|
||||||
|
|
||||||
class Cli
|
class Cli
|
||||||
|
|
||||||
|
@ -32,7 +31,7 @@ class Cli
|
||||||
# @rtnr: true if the synchro is finish
|
# @rtnr: true if the synchro is finish
|
||||||
def sync()
|
def sync()
|
||||||
if !defined?(@sync)
|
if !defined?(@sync)
|
||||||
@sync = Sync.new()
|
@sync = MPW::Sync::MPW.new
|
||||||
|
|
||||||
if !@config.sync_host.nil? && !@config.sync_port.nil?
|
if !@config.sync_host.nil? && !@config.sync_port.nil?
|
||||||
if !@sync.connect(@config.sync_host, @config.sync_port, @config.key, @config.sync_pwd, @config.sync_suffix)
|
if !@sync.connect(@config.sync_host, @config.sync_port, @config.key, @config.sync_pwd, @config.sync_suffix)
|
||||||
|
@ -99,7 +98,7 @@ class Cli
|
||||||
# Request the GPG password and decrypt the file
|
# Request the GPG password and decrypt the file
|
||||||
def decrypt()
|
def decrypt()
|
||||||
if !defined?(@mpw)
|
if !defined?(@mpw)
|
||||||
@mpw = MPW.new(@config.file_gpg, @config.key)
|
@mpw = MPW::MPW.new(@config.file_gpg, @config.key)
|
||||||
end
|
end
|
||||||
|
|
||||||
@passwd = ask(I18n.t('display.gpg_password')) {|q| q.echo = false}
|
@passwd = ask(I18n.t('display.gpg_password')) {|q| q.echo = false}
|
||||||
|
@ -132,30 +131,30 @@ class Cli
|
||||||
# @args: item -> an array with the item information
|
# @args: item -> an array with the item information
|
||||||
def displayFormat(item)
|
def displayFormat(item)
|
||||||
puts '--------------------'
|
puts '--------------------'
|
||||||
puts "Id: #{item[MPW::ID]}"
|
puts "Id: #{item[MPW::MPW::ID]}"
|
||||||
puts "#{I18n.t('display.name')}: #{item[MPW::NAME]}"
|
puts "#{I18n.t('display.name')}: #{item[MPW::MPW::NAME]}"
|
||||||
puts "#{I18n.t('display.group')}: #{item[MPW::GROUP]}"
|
puts "#{I18n.t('display.group')}: #{item[MPW::MPW::GROUP]}"
|
||||||
puts "#{I18n.t('display.server')}: #{item[MPW::SERVER]}"
|
puts "#{I18n.t('display.server')}: #{item[MPW::MPW::SERVER]}"
|
||||||
puts "#{I18n.t('display.protocol')}: #{item[MPW::PROTOCOL]}"
|
puts "#{I18n.t('display.protocol')}: #{item[MPW::MPW::PROTOCOL]}"
|
||||||
puts "#{I18n.t('display.login')}: #{item[MPW::LOGIN]}"
|
puts "#{I18n.t('display.login')}: #{item[MPW::MPW::LOGIN]}"
|
||||||
puts "#{I18n.t('display.password')}: #{item[MPW::PASSWORD]}"
|
puts "#{I18n.t('display.password')}: #{item[MPW::MPW::PASSWORD]}"
|
||||||
puts "#{I18n.t('display.port')}: #{item[MPW::PORT]}"
|
puts "#{I18n.t('display.port')}: #{item[MPW::MPW::PORT]}"
|
||||||
puts "#{I18n.t('display.comment')}: #{item[MPW::COMMENT]}"
|
puts "#{I18n.t('display.comment')}: #{item[MPW::MPW::COMMENT]}"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Display an item in the alternative format
|
# Display an item in the alternative format
|
||||||
# @args: item -> an array with the item information
|
# @args: item -> an array with the item information
|
||||||
def displayFormatAlt(item)
|
def displayFormatAlt(item)
|
||||||
port = item[MPW::PORT].nil? ? '' : ":#{item[MPW::PORT]}"
|
port = item[MPW::MPW::PORT].nil? ? '' : ":#{item[MPW::MPW::PORT]}"
|
||||||
|
|
||||||
if item[MPW::PASSWORD].nil? || item[MPW::PASSWORD].empty?
|
if item[MPW::MPW::PASSWORD].nil? || item[MPW::MPW::PASSWORD].empty?
|
||||||
if item[MPW::LOGIN].include('@')
|
if item[MPW::MPW::LOGIN].include('@')
|
||||||
puts "# #{item[MPW::ID]} #{item[MPW::PROTOCOL]}://#{item[MPW::LOGIN]}@#{item[MPW::SERVER]}#{port}"
|
puts "# #{item[MPW::MPW::ID]} #{item[MPW::MPW::PROTOCOL]}://#{item[MPW::MPW::LOGIN]}@#{item[MPW::MPW::SERVER]}#{port}"
|
||||||
else
|
else
|
||||||
puts "# #{item[MPW::ID]} #{item[MPW::PROTOCOL]}://{#{item[MPW::LOGIN]}}@#{item[MPW::SERVER]}#{port}"
|
puts "# #{item[MPW::MPW::ID]} #{item[MPW::MPW::PROTOCOL]}://{#{item[MPW::MPW::LOGIN]}}@#{item[MPW::MPW::SERVER]}#{port}"
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
puts "# #{item[MPW::ID]} #{item[MPW::PROTOCOL]}://{#{item[MPW::LOGIN]}:#{item[MPW::PASSWORD]}}@#{item[MPW::SERVER]}#{port}"
|
puts "# #{item[MPW::MPW::ID]} #{item[MPW::MPW::PROTOCOL]}://{#{item[MPW::MPW::LOGIN]}:#{item[MPW::MPW::PASSWORD]}}@#{item[MPW::MPW::SERVER]}#{port}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -193,14 +192,14 @@ class Cli
|
||||||
if not row.empty?
|
if not row.empty?
|
||||||
puts I18n.t('form.update.title')
|
puts I18n.t('form.update.title')
|
||||||
puts '--------------------'
|
puts '--------------------'
|
||||||
name = ask(I18n.t('form.update.name' , :name => row[MPW::NAME])).to_s
|
name = ask(I18n.t('form.update.name' , :name => row[MPW::MPW::NAME])).to_s
|
||||||
group = ask(I18n.t('form.update.group' , :group => row[MPW::GROUP])).to_s
|
group = ask(I18n.t('form.update.group' , :group => row[MPW::MPW::GROUP])).to_s
|
||||||
server = ask(I18n.t('form.update.server' , :server => row[MPW::SERVER])).to_s
|
server = ask(I18n.t('form.update.server' , :server => row[MPW::MPW::SERVER])).to_s
|
||||||
protocol = ask(I18n.t('form.update.protocol', :protocol => row[MPW::PROTOCOL])).to_s
|
protocol = ask(I18n.t('form.update.protocol', :protocol => row[MPW::MPW::PROTOCOL])).to_s
|
||||||
login = ask(I18n.t('form.update.login' , :login => row[MPW::LOGIN])).to_s
|
login = ask(I18n.t('form.update.login' , :login => row[MPW::MPW::LOGIN])).to_s
|
||||||
passwd = ask(I18n.t('form.update.password')).to_s
|
passwd = ask(I18n.t('form.update.password')).to_s
|
||||||
port = ask(I18n.t('form.update.port' , :port => row[MPW::PORT])).to_s
|
port = ask(I18n.t('form.update.port' , :port => row[MPW::MPW::PORT])).to_s
|
||||||
comment = ask(I18n.t('form.update.comment' , :comment => row[MPW::COMMENT])).to_s
|
comment = ask(I18n.t('form.update.comment' , :comment => row[MPW::MPW::COMMENT])).to_s
|
||||||
|
|
||||||
if @mpw.update(name, group, server, protocol, login, passwd, port, comment, id)
|
if @mpw.update(name, group, server, protocol, login, passwd, port, comment, id)
|
||||||
if @mpw.encrypt()
|
if @mpw.encrypt()
|
|
@ -3,7 +3,7 @@
|
||||||
# mail: nishiki@yaegashi.fr
|
# mail: nishiki@yaegashi.fr
|
||||||
# info: a simple script who manage your passwords
|
# info: a simple script who manage your passwords
|
||||||
|
|
||||||
require "#{APP_ROOT}/lib/Cli.rb"
|
require "#{APP_ROOT}/MPW/UI/Cli"
|
||||||
|
|
||||||
class CliSSH < Cli
|
class CliSSH < Cli
|
||||||
|
|
||||||
|
@ -16,11 +16,11 @@ class CliSSH < Cli
|
||||||
|
|
||||||
if result.length > 0
|
if result.length > 0
|
||||||
result.each do |r|
|
result.each do |r|
|
||||||
server = @server.nil? ? r[MPW::SERVER] : @server
|
server = @server.nil? ? r[MPW::MPW::SERVER] : @server
|
||||||
port = @port.nil? ? r[MPW::PORT] : @port
|
port = @port.nil? ? r[MPW::MPW::PORT] : @port
|
||||||
login = @login.nil? ? r[MPW::LOGIN] : @login
|
login = @login.nil? ? r[MPW::MPW::LOGIN] : @login
|
||||||
|
|
||||||
passwd = r[MPW::PASSWORD]
|
passwd = r[MPW::MPW::PASSWORD]
|
||||||
|
|
||||||
if port.nil? || port.empty?
|
if port.nil? || port.empty?
|
||||||
port = 22
|
port = 22
|
331
lib/MPW.rb
331
lib/MPW.rb
|
@ -1,331 +0,0 @@
|
||||||
#!/usr/bin/ruby
|
|
||||||
# author: nishiki
|
|
||||||
# mail: nishiki@yaegashi.fr
|
|
||||||
# info: a simple script who manage your passwords
|
|
||||||
|
|
||||||
require 'rubygems'
|
|
||||||
require 'gpgme'
|
|
||||||
require 'csv'
|
|
||||||
require 'i18n'
|
|
||||||
|
|
||||||
class MPW
|
|
||||||
|
|
||||||
ID = 0
|
|
||||||
NAME = 1
|
|
||||||
GROUP = 2
|
|
||||||
PROTOCOL = 3
|
|
||||||
SERVER = 4
|
|
||||||
LOGIN = 5
|
|
||||||
PASSWORD = 6
|
|
||||||
PORT = 7
|
|
||||||
COMMENT = 8
|
|
||||||
DATE = 9
|
|
||||||
|
|
||||||
attr_accessor :error_msg
|
|
||||||
|
|
||||||
# Constructor
|
|
||||||
def initialize(file_gpg, key=nil)
|
|
||||||
@error_msg = nil
|
|
||||||
@file_gpg = file_gpg
|
|
||||||
@key = key
|
|
||||||
end
|
|
||||||
|
|
||||||
# Decrypt a gpg file
|
|
||||||
# @args: password -> the GPG key password
|
|
||||||
# @rtrn: true if data has been decrypted
|
|
||||||
def decrypt(passwd=nil)
|
|
||||||
@data = Array.new
|
|
||||||
|
|
||||||
begin
|
|
||||||
if File.exist?(@file_gpg)
|
|
||||||
crypto = GPGME::Crypto.new(:armor => true)
|
|
||||||
data_decrypt = crypto.decrypt(IO.read(@file_gpg), :password => passwd).read
|
|
||||||
|
|
||||||
data_decrypt.lines do |line|
|
|
||||||
@data.push(line.parse_csv)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return true
|
|
||||||
rescue Exception => e
|
|
||||||
@error_msg = "#{I18n.t('error.gpg_file.decrypt')}\n#{e}"
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Encrypt a file
|
|
||||||
# @rtrn: true if the file has been encrypted
|
|
||||||
def encrypt()
|
|
||||||
begin
|
|
||||||
crypto = GPGME::Crypto.new(:armor => true)
|
|
||||||
file_gpg = File.open(@file_gpg, 'w+')
|
|
||||||
|
|
||||||
data_to_encrypt = ''
|
|
||||||
@data.each do |row|
|
|
||||||
data_to_encrypt << row.to_csv
|
|
||||||
end
|
|
||||||
|
|
||||||
crypto.encrypt(data_to_encrypt, :recipients => @key, :output => file_gpg)
|
|
||||||
file_gpg.close
|
|
||||||
|
|
||||||
return true
|
|
||||||
rescue Exception => e
|
|
||||||
@error_msg = "#{I18n.t('error.gpg_file.encrypt')}\n#{e}"
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
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)
|
|
||||||
result = Array.new()
|
|
||||||
|
|
||||||
if !search.nil?
|
|
||||||
search = search.downcase
|
|
||||||
end
|
|
||||||
search = search.force_encoding('ASCII-8BIT')
|
|
||||||
|
|
||||||
@data.each do |row|
|
|
||||||
name = row[NAME].nil? ? nil : row[NAME].downcase
|
|
||||||
server = row[SERVER].nil? ? nil : row[SERVER].downcase
|
|
||||||
comment = row[COMMENT].nil? ? nil : row[COMMENT].downcase
|
|
||||||
|
|
||||||
if name =~ /^.*#{search}.*$/ || server =~ /^.*#{search}.*$/ || comment =~ /^.*#{search}.*$/
|
|
||||||
if (protocol.nil? || protocol.eql?(row[PROTOCOL])) && (group.nil? || group.eql?(row[GROUP]))
|
|
||||||
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|
|
|
||||||
if row[ID] == id
|
|
||||||
return row
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return Array.new()
|
|
||||||
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)
|
|
||||||
row = Array.new()
|
|
||||||
update = false
|
|
||||||
|
|
||||||
i = 0
|
|
||||||
@data.each do |r|
|
|
||||||
if r[ID] == id
|
|
||||||
row = r
|
|
||||||
update = true
|
|
||||||
break
|
|
||||||
end
|
|
||||||
i += 1
|
|
||||||
end
|
|
||||||
|
|
||||||
if port.to_i <= 0
|
|
||||||
port = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
row_update = Array.new()
|
|
||||||
row_update[DATE] = Time.now.to_i
|
|
||||||
|
|
||||||
row_update[ID] = id.nil? || id.empty? ? MPW.password(16) : id
|
|
||||||
row_update[NAME] = name.nil? || name.empty? ? row[NAME] : name
|
|
||||||
row_update[GROUP] = group.nil? || group.empty? ? row[GROUP] : group
|
|
||||||
row_update[SERVER] = server.nil? || server.empty? ? row[SERVER] : server
|
|
||||||
row_update[PROTOCOL] = protocol.nil? || protocol.empty? ? row[PROTOCOL] : protocol
|
|
||||||
row_update[LOGIN] = login.nil? || login.empty? ? row[LOGIN] : login
|
|
||||||
row_update[PASSWORD] = passwd.nil? || passwd.empty? ? row[PASSWORD] : passwd
|
|
||||||
row_update[PORT] = port.nil? || port.empty? ? row[PORT] : port
|
|
||||||
row_update[COMMENT] = comment.nil? || comment.empty? ? row[COMMENT] : comment
|
|
||||||
|
|
||||||
row_update[] = row_update[NAME].nil? ? nil : row_update[NAME].force_encoding('ASCII-8BIT')
|
|
||||||
row_update[] = row_update[GROUP].nil? ? nil : row_update[GROUP].force_encoding('ASCII-8BIT')
|
|
||||||
row_update[] = row_update[SERVER].nil? ? nil : row_update[SERVER].force_encoding('ASCII-8BIT')
|
|
||||||
row_update[] = row_update[PROTOCOL].nil? ? nil : row_update[PROTOCOL].force_encoding('ASCII-8BIT')
|
|
||||||
row_update[] = row_update[LOGIN].nil? ? nil : row_update[LOGIN].force_encoding('ASCII-8BIT')
|
|
||||||
row_update[] = row_update[PASSWORD].nil? ? nil : row_update[PASSWORD].force_encoding('ASCII-8BIT')
|
|
||||||
row_update[] = row_update[COMMENT].nil? ? nil : row_update[COMMENT].force_encoding('ASCII-8BIT')
|
|
||||||
|
|
||||||
if row_update[NAME].nil? || row_update[NAME].empty?
|
|
||||||
@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|
|
|
||||||
if row[ID] == id
|
|
||||||
@data.delete_at(i)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
i += 1
|
|
||||||
end
|
|
||||||
|
|
||||||
@error_msg = I18n.t('error.delete.id_no_exist', :id => id)
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
# Export to csv
|
|
||||||
# @args: file -> a string to match
|
|
||||||
# @rtrn: true if export work
|
|
||||||
def export(file)
|
|
||||||
begin
|
|
||||||
File.open(file, 'w+') do |file|
|
|
||||||
@data.each do |row|
|
|
||||||
row.delete_at(ID).delete_at(DATE)
|
|
||||||
file << row.to_csv
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return true
|
|
||||||
rescue Exception => e
|
|
||||||
@error_msg = "#{I18n.t('error.export.write', :file => file)}\n#{e}"
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Import to csv
|
|
||||||
# @args: file -> path to file import
|
|
||||||
# @rtrn: true if the import work
|
|
||||||
def import(file)
|
|
||||||
begin
|
|
||||||
data_new = IO.read(file)
|
|
||||||
data_new.lines do |line|
|
|
||||||
if not line =~ /(.*,){6}/
|
|
||||||
@error_msg = I18n.t('error.import.bad_format')
|
|
||||||
return false
|
|
||||||
else
|
|
||||||
row = line.parse_csv.unshift(0)
|
|
||||||
if not update(row[NAME], row[GROUP], row[SERVER], row[PROTOCOL], row[LOGIN], row[PASSWORD], row[PORT], row[COMMENT])
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return true
|
|
||||||
rescue Exception => e
|
|
||||||
@error_msg = "#{I18n.t('error.import.read', :file => file)}\n#{e}"
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
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)
|
|
||||||
begin
|
|
||||||
result = Array.new()
|
|
||||||
id = 0
|
|
||||||
|
|
||||||
data = IO.read(file)
|
|
||||||
data.lines do |line|
|
|
||||||
if not line =~ /(.*,){6}/
|
|
||||||
@error_msg = I18n.t('error.import.bad_format')
|
|
||||||
return false
|
|
||||||
else
|
|
||||||
result.push(line.parse_csv.unshift(id))
|
|
||||||
end
|
|
||||||
|
|
||||||
id += 1
|
|
||||||
end
|
|
||||||
|
|
||||||
return result
|
|
||||||
rescue Exception => e
|
|
||||||
@error_msg = "#{I18n.t('error.import.read', :file => file)}\n#{e}"
|
|
||||||
return false
|
|
||||||
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
|
|
||||||
|
|
||||||
return encrypt()
|
|
||||||
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
|
|
136
lib/MPWConfig.rb
136
lib/MPWConfig.rb
|
@ -1,136 +0,0 @@
|
||||||
#!/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
|
|
||||||
# sync_host -> the server host for synchronization
|
|
||||||
# sync_port -> the server port for synchronization
|
|
||||||
# sync_pwd -> the password for synchronization
|
|
||||||
# sync_suffix -> the suffix file (optionnal)
|
|
||||||
# @rtrn: true if le config file is create
|
|
||||||
def setup(key, lang, file_gpg, timeout_pwd, sync_host=nil, sync_port=nil, sync_pwd=nil, sync_suffix=nil)
|
|
||||||
|
|
||||||
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 = timeout_pwd.empty? ? 60 : timeout_pwd.to_i
|
|
||||||
|
|
||||||
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' => 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
|
|
||||||
|
|
||||||
# Set the last update when there is a sync
|
|
||||||
# @rtrn: true is the file has been updated
|
|
||||||
def set_last_update()
|
|
||||||
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
|
|
128
lib/Sync.rb
128
lib/Sync.rb
|
@ -1,128 +0,0 @@
|
||||||
#!/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
|
|
||||||
attr_accessor :enable
|
|
||||||
|
|
||||||
# Constructor
|
|
||||||
def initialize()
|
|
||||||
@error_msg = nil
|
|
||||||
@enable = 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)
|
|
||||||
@enable = true
|
|
||||||
rescue Exception => e
|
|
||||||
@error_msg = "#{I18n.t('error.sync.connection')}\n#{e}"
|
|
||||||
@enable = false
|
|
||||||
end
|
|
||||||
|
|
||||||
return @enable
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get data on server
|
|
||||||
# @args: gpg_password -> the gpg password
|
|
||||||
# @rtrn: nil if nothing data or error
|
|
||||||
def get(gpg_password)
|
|
||||||
if !@enable
|
|
||||||
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)
|
|
||||||
|
|
||||||
if !defined?(msg['error'])
|
|
||||||
@error_msg = I18n.t('error.sync.communication')
|
|
||||||
return nil
|
|
||||||
elsif msg['error'].nil?
|
|
||||||
tmp_file = "/tmp/mpw-#{MPW.password()}.gpg"
|
|
||||||
File.open(tmp_file, 'w') do |file|
|
|
||||||
file << msg['data']
|
|
||||||
end
|
|
||||||
|
|
||||||
@mpw = MPW.new(tmp_file)
|
|
||||||
if !@mpw.decrypt(gpg_password)
|
|
||||||
puts @mpw.error_msg
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
File.unlink(tmp_file)
|
|
||||||
return @mpw.search()
|
|
||||||
else
|
|
||||||
@error_msg = I18n.t(msg['error'])
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
# Update the remote data
|
|
||||||
# @args: data -> the data to send on server
|
|
||||||
# @rtrn: false if there is a problem
|
|
||||||
def update(data)
|
|
||||||
if !@enable
|
|
||||||
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)
|
|
||||||
|
|
||||||
if !defined?(msg['error'])
|
|
||||||
@error_msg = I18n.t('error.sync.communication')
|
|
||||||
return false
|
|
||||||
elsif msg['error'].nil?
|
|
||||||
return true
|
|
||||||
else
|
|
||||||
@error_msg = I18n.t(msg['error'])
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# TODO
|
|
||||||
def delete()
|
|
||||||
end
|
|
||||||
|
|
||||||
# Close the connection
|
|
||||||
def close()
|
|
||||||
if !@enable
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
send_msg = {:action => 'close'}
|
|
||||||
@socket.puts send_msg.to_json
|
|
||||||
end
|
|
||||||
end
|
|
6
mpw
6
mpw
|
@ -10,8 +10,8 @@ require 'locale'
|
||||||
require 'i18n'
|
require 'i18n'
|
||||||
|
|
||||||
APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)
|
APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)
|
||||||
require "#{APP_ROOT}/lib/Cli.rb"
|
require "#{APP_ROOT}/MPW/UI/Cli"
|
||||||
require "#{APP_ROOT}/lib/MPW.rb"
|
require "#{APP_ROOT}/MPW/Config"
|
||||||
|
|
||||||
lang = Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
|
lang = Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ OptionParser.new do |opts|
|
||||||
end
|
end
|
||||||
end.parse!
|
end.parse!
|
||||||
|
|
||||||
config = MPWConfig.new(options[:config])
|
config = MPW::Config.new(options[:config])
|
||||||
check_error = config.checkconfig()
|
check_error = config.checkconfig()
|
||||||
|
|
||||||
cli = Cli.new(lang, config)
|
cli = Cli.new(lang, config)
|
||||||
|
|
5
mpw-ssh
5
mpw-ssh
|
@ -10,7 +10,8 @@ require 'locale'
|
||||||
require 'i18n'
|
require 'i18n'
|
||||||
|
|
||||||
APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)
|
APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)
|
||||||
require "#{APP_ROOT}/lib/CliSSH.rb"
|
require "#{APP_ROOT}/MPW/UI/CliSSH"
|
||||||
|
require "#{APP_ROOT}/MPW/Config"
|
||||||
|
|
||||||
lang = Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
|
lang = Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
|
||||||
|
|
||||||
|
@ -45,7 +46,7 @@ OptionParser.new do |opts|
|
||||||
end
|
end
|
||||||
end.parse!
|
end.parse!
|
||||||
|
|
||||||
config = MPWConfig.new(options[:config])
|
config = MPW::Config.new(options[:config])
|
||||||
check_error = config.checkconfig()
|
check_error = config.checkconfig()
|
||||||
|
|
||||||
cli = CliSSH.new(lang, config)
|
cli = CliSSH.new(lang, config)
|
||||||
|
|
Loading…
Reference in a new issue