lockr 0.3.0 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/lockr/action/add.rb +1 -1
- data/lib/lockr/action/base.rb +4 -47
- data/lib/lockr/action/list.rb +1 -1
- data/lib/lockr/action/remove.rb +1 -1
- data/lib/lockr/action/show.rb +1 -1
- data/lib/lockr/fileutils.rb +74 -0
- data/lib/lockr/sftp.rb +100 -0
- data/lib/lockr/version.rb +2 -2
- data/lib/lockr.rb +30 -6
- metadata +24 -10
data/lib/lockr/action/add.rb
CHANGED
data/lib/lockr/action/base.rb
CHANGED
@@ -1,47 +1,12 @@
|
|
1
1
|
require 'openssl'
|
2
2
|
require 'lockr/encryption/aes'
|
3
|
+
require 'lockr/fileutils'
|
3
4
|
|
4
5
|
class BaseAction
|
5
|
-
|
6
|
-
sha1 = OpenSSL::Digest::SHA512.new
|
7
|
-
|
8
|
-
File.open( filename) do |file|
|
9
|
-
buffer = ''
|
10
|
-
|
11
|
-
# Read the file 512 bytes at a time
|
12
|
-
while not file.eof
|
13
|
-
file.read(512, buffer)
|
14
|
-
sha1.update(buffer)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
sha1.to_s
|
19
|
-
end
|
6
|
+
include FileUtils
|
20
7
|
|
21
8
|
def save_to_vault( storelist, vault)
|
22
|
-
|
23
|
-
|
24
|
-
File.open( vault, 'w') do |f|
|
25
|
-
f.write( storelist.to_yaml)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def rotate_vault( vault)
|
30
|
-
return unless File.exists?(vault)
|
31
|
-
|
32
|
-
# move old files first
|
33
|
-
max_files = 2 # = 3 - 1
|
34
|
-
max_files.downto( 0) { |i|
|
35
|
-
|
36
|
-
if i == 0
|
37
|
-
File.rename( vault, "#{vault}_#{i}")
|
38
|
-
else
|
39
|
-
j = i - 1
|
40
|
-
if File.exists?("#{vault}_#{j}")
|
41
|
-
File.rename( "#{vault}_#{j}", "#{vault}_#{i}")
|
42
|
-
end
|
43
|
-
end
|
44
|
-
}
|
9
|
+
FileUtils.store_obj_yaml( vault, storelist)
|
45
10
|
end
|
46
11
|
|
47
12
|
# loads the datastructure for the password sets from the file
|
@@ -58,14 +23,6 @@ class BaseAction
|
|
58
23
|
# :username => PasswordStore
|
59
24
|
# }
|
60
25
|
def load_from_vault( vault)
|
61
|
-
|
62
|
-
|
63
|
-
if File.exist?( vault)
|
64
|
-
File.open( vault, 'r') do |f|
|
65
|
-
storelist = YAML::load(f)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
storelist
|
26
|
+
FileUtils.load_obj_yaml( vault)
|
70
27
|
end
|
71
28
|
end
|
data/lib/lockr/action/list.rb
CHANGED
@@ -12,7 +12,7 @@ class ListAction < AesAction
|
|
12
12
|
out << "Id: #{id}"
|
13
13
|
}
|
14
14
|
else
|
15
|
-
keyfilehash =
|
15
|
+
keyfilehash = FileUtils.calculate_sha512_hash( keyfile)
|
16
16
|
pwd_directory.each { |oid,value|
|
17
17
|
pwd_directory_id = YAML::load(decrypt( value[:enc], keyfilehash, value[:salt]))
|
18
18
|
pwd_directory_id.each { |username, pwdstore|
|
data/lib/lockr/action/remove.rb
CHANGED
@@ -4,7 +4,7 @@ require 'lockr/pwdstore'
|
|
4
4
|
class RemoveAction < AesAction
|
5
5
|
|
6
6
|
def initialize(id,username,keyfile,vault)
|
7
|
-
keyfilehash =
|
7
|
+
keyfilehash = FileUtils.calculate_sha512_hash( keyfile)
|
8
8
|
pwd_directory = load_from_vault( vault)
|
9
9
|
|
10
10
|
unless pwd_directory.has_key?( id)
|
data/lib/lockr/action/show.rb
CHANGED
@@ -0,0 +1,74 @@
|
|
1
|
+
module FileUtils
|
2
|
+
|
3
|
+
# rotate the provided file with a maximum of 'limit' backups
|
4
|
+
# renamed filed will be named file_0, file_1, ...
|
5
|
+
def FileUtils.rotate_file( file, limit)
|
6
|
+
return unless File.exists?(file)
|
7
|
+
|
8
|
+
# move old files first
|
9
|
+
max_files = limit - 1
|
10
|
+
max_files.downto( 0) { |i|
|
11
|
+
|
12
|
+
if i == 0
|
13
|
+
FileUtils.copy( file, "#{file}_#{i}")
|
14
|
+
else
|
15
|
+
j = i - 1
|
16
|
+
if File.exists?("#{file}_#{j}")
|
17
|
+
FileUtils.copy( "#{file}_#{j}", "#{file}_#{i}")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
}
|
21
|
+
|
22
|
+
puts "Rotated local vault file(s)"
|
23
|
+
end
|
24
|
+
|
25
|
+
# copy file_src to file_target
|
26
|
+
def FileUtils.copy( file_src, file_target)
|
27
|
+
return unless File.exists?( file_src)
|
28
|
+
|
29
|
+
dst = File.new( file_target, 'w')
|
30
|
+
File.open( file_src, 'r') do |src|
|
31
|
+
dst.write( src.read)
|
32
|
+
end
|
33
|
+
dst.close
|
34
|
+
end
|
35
|
+
|
36
|
+
# store an object as yaml to file
|
37
|
+
def FileUtils.store_obj_yaml( file, object)
|
38
|
+
File.open( file, 'w') do |f|
|
39
|
+
f.write( object.to_yaml)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# load an yaml object from file
|
44
|
+
def FileUtils.load_obj_yaml( file)
|
45
|
+
object = {}
|
46
|
+
|
47
|
+
unless File.exist?( file)
|
48
|
+
return object
|
49
|
+
end
|
50
|
+
|
51
|
+
File.open( file, 'r') do |f|
|
52
|
+
object = YAML::load(f)
|
53
|
+
end
|
54
|
+
|
55
|
+
object
|
56
|
+
end
|
57
|
+
|
58
|
+
# calculate the sha512 hash of a file
|
59
|
+
def FileUtils.calculate_sha512_hash( filename)
|
60
|
+
sha512 = OpenSSL::Digest::SHA512.new
|
61
|
+
|
62
|
+
File.open( filename) do |file|
|
63
|
+
buffer = ''
|
64
|
+
|
65
|
+
# Read the file 512 bytes at a time
|
66
|
+
while not file.eof
|
67
|
+
file.read(512, buffer)
|
68
|
+
sha512.update(buffer)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
sha512.to_s
|
73
|
+
end
|
74
|
+
end
|
data/lib/lockr/sftp.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'lockr/config'
|
2
|
+
require 'net/sftp'
|
3
|
+
require 'set'
|
4
|
+
require 'lockr/fileutils'
|
5
|
+
|
6
|
+
class SFTP
|
7
|
+
include FileUtils
|
8
|
+
|
9
|
+
# upload the vault via sftp to the location specified in the configuration
|
10
|
+
def upload( config, vault)
|
11
|
+
cfg_sftp = get_sftp_config( config)
|
12
|
+
remote_file = File.join( cfg_sftp[:directory], File.basename(vault))
|
13
|
+
Net::SFTP.start( cfg_sftp[:hostname], cfg_sftp[:username]) do |sftp|
|
14
|
+
|
15
|
+
# TODO check remote checksum to make sure upload is necessary
|
16
|
+
rotate_sftp_file( sftp, remote_file, 3)
|
17
|
+
|
18
|
+
# upload a file or directory to the remote host
|
19
|
+
sftp.upload!( vault, remote_file)
|
20
|
+
puts "Uploaded vault to host '#{cfg_sftp[:hostname]}' by SFTP"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# rotate the provided file with a maximum of 'limit' backups
|
25
|
+
# renamed filed will be named file_0, file_1, ...
|
26
|
+
def rotate_sftp_file( sftp, file, limit)
|
27
|
+
return unless file_exists( sftp, file)
|
28
|
+
|
29
|
+
# move old files first
|
30
|
+
max_files = limit - 1
|
31
|
+
max_files.downto( 0) { |i|
|
32
|
+
|
33
|
+
if i == 0
|
34
|
+
sftp.rename( file, "#{file}_#{i}")
|
35
|
+
else
|
36
|
+
j = i - 1
|
37
|
+
next unless file_exists( sftp, "#{file}_#{j}")
|
38
|
+
sftp.rename( "#{file}_#{j}", "#{file}_#{i}")
|
39
|
+
end
|
40
|
+
}
|
41
|
+
|
42
|
+
puts "Rotated remote vault file(s)"
|
43
|
+
end
|
44
|
+
|
45
|
+
# check if the file exists on the given sftp connection
|
46
|
+
def file_exists( sftp, file)
|
47
|
+
files = get_dir_listing( sftp, File.dirname(file))
|
48
|
+
files.include?( File.basename(file))
|
49
|
+
end
|
50
|
+
|
51
|
+
# get the file listing of a remote directory
|
52
|
+
def get_dir_listing( sftp, dir)
|
53
|
+
list = []
|
54
|
+
|
55
|
+
sftp.dir.foreach(dir) do |entry|
|
56
|
+
list << entry.name
|
57
|
+
end
|
58
|
+
|
59
|
+
Set.new(list)
|
60
|
+
end
|
61
|
+
|
62
|
+
# download the vault via sftp to the location specified in the configuration
|
63
|
+
def download( config, vault)
|
64
|
+
cfg_sftp = get_sftp_config( config)
|
65
|
+
|
66
|
+
Net::SFTP.start( cfg_sftp[:hostname], cfg_sftp[:username]) do |sftp|
|
67
|
+
|
68
|
+
# TODO check if remote file is same as local (checksum?)
|
69
|
+
rotate_file( vault, 3)
|
70
|
+
|
71
|
+
# upload a file or directory to the remote host
|
72
|
+
sftp.download!( File.join( cfg_sftp[:directory], File.basename(vault)), vault)
|
73
|
+
puts "Downloaded vault from host '#{cfg_sftp[:hostname]}' by SFTP"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# check config for section lockr and subsection sftp. then check for keys
|
78
|
+
# :hostname, :username and :directory. if anything is missing, raise ArgumentError
|
79
|
+
# returns sftp configuration hash
|
80
|
+
def get_sftp_config( config)
|
81
|
+
unless config.config.has_key?( :lockr)
|
82
|
+
raise ArgumentError, 'config has no "lockr" section'
|
83
|
+
end
|
84
|
+
|
85
|
+
cfg = config.config[:lockr]
|
86
|
+
|
87
|
+
unless cfg.has_key?( :sftp)
|
88
|
+
raise ArgumentError, 'config has no "sftp" section'
|
89
|
+
end
|
90
|
+
|
91
|
+
cfg_sftp = cfg[:sftp]
|
92
|
+
|
93
|
+
unless cfg_sftp.has_key?( :hostname) and cfg_sftp.has_key?( :username) and cfg_sftp.has_key?( :directory)
|
94
|
+
raise ArgumentError, 'config "sftp" section must have keys :host, :username and :directory'
|
95
|
+
end
|
96
|
+
|
97
|
+
cfg_sftp
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
data/lib/lockr/version.rb
CHANGED
data/lib/lockr.rb
CHANGED
@@ -10,16 +10,19 @@ require 'lockr/action/remove'
|
|
10
10
|
require 'lockr/action/show'
|
11
11
|
require 'lockr/config'
|
12
12
|
require 'lockr/pwdgen'
|
13
|
+
require 'lockr/sftp'
|
13
14
|
require 'lockr/version'
|
15
|
+
require 'lockr/fileutils'
|
14
16
|
|
15
17
|
class Lockr
|
16
18
|
|
17
19
|
def run()
|
18
20
|
options = parse_options()
|
19
|
-
|
21
|
+
configfile = Configuration.new()
|
22
|
+
merge_config( configfile, options)
|
20
23
|
validate_options( options)
|
21
24
|
acquire_additional_input( options)
|
22
|
-
process_actions( options)
|
25
|
+
process_actions( configfile, options)
|
23
26
|
end
|
24
27
|
|
25
28
|
def parse_options()
|
@@ -55,7 +58,17 @@ class Lockr
|
|
55
58
|
opts.on( '-g', '--genpwd PARAMS', 'generate a random password (based on the optional PARAMS)') do |params|
|
56
59
|
options[:generatepwd] = params
|
57
60
|
end
|
61
|
+
|
62
|
+
options[:download] = nil
|
63
|
+
opts.on( '-d', '--download', 'download latest vault from configured sftp location before executing action') do |d|
|
64
|
+
options[:download] = true
|
65
|
+
end
|
58
66
|
|
67
|
+
options[:upload] = nil
|
68
|
+
opts.on( '-u', '--upload', 'upload vault to configured sftp location after executing action') do |d|
|
69
|
+
options[:upload] = true
|
70
|
+
end
|
71
|
+
|
59
72
|
# This displays the help screen, all programs are
|
60
73
|
# assumed to have this option.
|
61
74
|
opts.on( '-h', '--help', 'Display this screen' ) do
|
@@ -82,9 +95,7 @@ class Lockr
|
|
82
95
|
options
|
83
96
|
end
|
84
97
|
|
85
|
-
def merge_config( options)
|
86
|
-
configfile = Configuration.new()
|
87
|
-
|
98
|
+
def merge_config( configfile, options)
|
88
99
|
if configfile.config.nil?
|
89
100
|
return
|
90
101
|
end
|
@@ -138,7 +149,15 @@ class Lockr
|
|
138
149
|
end
|
139
150
|
end
|
140
151
|
|
141
|
-
def process_actions( options)
|
152
|
+
def process_actions( configfile, options)
|
153
|
+
rotate_required = ( ! options[:download].nil? ) || ( ! %w{a add r remove}.index( options[:action]).nil? )
|
154
|
+
FileUtils.rotate_file( options[:vault], 3) if rotate_required
|
155
|
+
|
156
|
+
unless options[:download].nil?
|
157
|
+
sftp = SFTP.new
|
158
|
+
sftp.download( configfile, options[:vault])
|
159
|
+
end
|
160
|
+
|
142
161
|
begin
|
143
162
|
case options[:action]
|
144
163
|
when 'a', 'add'
|
@@ -162,5 +181,10 @@ class Lockr
|
|
162
181
|
say( "<%= color('Invalid keyfile', :red) %>")
|
163
182
|
exit 42
|
164
183
|
end
|
184
|
+
|
185
|
+
unless options[:upload].nil?
|
186
|
+
sftp = SFTP.new
|
187
|
+
sftp.upload( configfile, options[:vault])
|
188
|
+
end
|
165
189
|
end
|
166
190
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lockr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,31 +9,43 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-08-
|
12
|
+
date: 2012-08-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: highline
|
16
|
-
requirement: &
|
16
|
+
requirement: &70292937547480 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version:
|
21
|
+
version: 1.6.13
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70292937547480
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: bundler
|
27
|
-
requirement: &
|
27
|
+
requirement: &70292937546980 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version:
|
32
|
+
version: 1.1.4
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
36
|
-
|
35
|
+
version_requirements: *70292937546980
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: net-sftp
|
38
|
+
requirement: &70292937546520 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 2.0.5
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70292937546520
|
47
|
+
description: Lockr is a command line based password manager. Passwords are stored
|
48
|
+
AES-encrypted in a file on your own system.
|
37
49
|
email: info@byteblues.com
|
38
50
|
executables:
|
39
51
|
- lockr
|
@@ -49,8 +61,10 @@ files:
|
|
49
61
|
- lib/lockr/action/show.rb
|
50
62
|
- lib/lockr/encryption/aes.rb
|
51
63
|
- lib/lockr/config.rb
|
64
|
+
- lib/lockr/fileutils.rb
|
52
65
|
- lib/lockr/pwdgen.rb
|
53
66
|
- lib/lockr/pwdstore.rb
|
67
|
+
- lib/lockr/sftp.rb
|
54
68
|
- lib/lockr/version.rb
|
55
69
|
- !binary |-
|
56
70
|
YmluL2xvY2ty
|
@@ -77,5 +91,5 @@ rubyforge_project:
|
|
77
91
|
rubygems_version: 1.8.10
|
78
92
|
signing_key:
|
79
93
|
specification_version: 3
|
80
|
-
summary:
|
94
|
+
summary: Command line based password manager
|
81
95
|
test_files: []
|