lockr 0.3.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|