pwl 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +10 -12
- data/Gemfile.lock +19 -14
- data/README.md +1 -1
- data/VERSION +1 -1
- data/bin/pwl +51 -43
- data/lib/pwl.rb +6 -2
- data/lib/pwl/{store.rb → locker.rb} +19 -21
- data/lib/pwl/presenter/html.rb +19 -0
- data/lib/pwl/presenter/json.rb +22 -0
- data/lib/pwl/presenter/yaml.rb +23 -0
- data/pwl.gemspec +35 -23
- data/templates/export.html.erb +2 -2
- data/test/acceptance/test_add.rb +19 -0
- data/test/acceptance/test_delete.rb +1 -1
- data/test/acceptance/test_export.rb +4 -4
- data/test/acceptance/test_export_json.rb +59 -0
- data/test/acceptance/test_export_yaml.rb +59 -0
- data/test/acceptance/test_get.rb +1 -1
- data/test/acceptance/test_init.rb +6 -6
- data/test/acceptance/test_list.rb +3 -3
- data/test/acceptance/test_passwd.rb +2 -2
- data/test/fixtures/test_all.json +19 -0
- data/test/fixtures/test_all.yaml +11 -0
- data/test/fixtures/test_empty.json +7 -0
- data/test/fixtures/test_empty.yaml +5 -0
- data/test/helper.rb +8 -8
- data/test/unit/test_store_construction.rb +26 -26
- data/test/unit/test_store_crud.rb +35 -35
- data/test/unit/test_store_metadata.rb +16 -16
- data/test/unit/test_store_password_policy.rb +6 -6
- data/test/unit/test_store_security.rb +13 -13
- metadata +82 -27
- data/test/acceptance/test_put.rb +0 -19
@@ -7,9 +7,9 @@ class TestPasswd < Test::Pwl::AppTestCase
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def test_standard
|
10
|
-
assert_successful('', '
|
10
|
+
assert_successful('', 'add foo bar')
|
11
11
|
|
12
|
-
new_pwd =
|
12
|
+
new_pwd = locker_password.reverse
|
13
13
|
|
14
14
|
# If we are in a pipe (and we are in these tests), the new password is expected as first arg
|
15
15
|
assert_successful('^$', "passwd \"#{new_pwd}\"")
|
@@ -0,0 +1,19 @@
|
|
1
|
+
{
|
2
|
+
"created": "2012-03-28T21:54:21+02:00",
|
3
|
+
"last_accessed": "2012-03-28T22:01:49+02:00",
|
4
|
+
"last_modified": "2012-03-29T22:46:29+02:00",
|
5
|
+
"entries": [
|
6
|
+
{
|
7
|
+
"key": "foo",
|
8
|
+
"value": "one"
|
9
|
+
},
|
10
|
+
{
|
11
|
+
"key": "bar",
|
12
|
+
"value": "two"
|
13
|
+
},
|
14
|
+
{
|
15
|
+
"key": "Chuck Norris",
|
16
|
+
"value": "Roundhouse Kick"
|
17
|
+
}
|
18
|
+
]
|
19
|
+
}
|
data/test/helper.rb
CHANGED
@@ -24,18 +24,18 @@ end
|
|
24
24
|
module Test
|
25
25
|
module Pwl
|
26
26
|
class TestCase < Test::Unit::TestCase
|
27
|
-
attr_reader :
|
27
|
+
attr_reader :locker, :locker_file
|
28
28
|
|
29
29
|
def setup
|
30
|
-
@
|
31
|
-
@
|
30
|
+
@locker_file = temp_file_name
|
31
|
+
@locker = ::Pwl::Locker.new(@locker_file, locker_password)
|
32
32
|
end
|
33
33
|
|
34
34
|
def teardown
|
35
|
-
File.unlink(@
|
35
|
+
File.unlink(@locker_file)
|
36
36
|
end
|
37
37
|
|
38
|
-
def
|
38
|
+
def locker_password
|
39
39
|
's3cret passw0rd'
|
40
40
|
end
|
41
41
|
|
@@ -53,14 +53,14 @@ module Test
|
|
53
53
|
|
54
54
|
protected
|
55
55
|
|
56
|
-
def assert_successful(expected_out, cmd, password =
|
56
|
+
def assert_successful(expected_out, cmd, password = locker_password)
|
57
57
|
out, err, rc = execute(cmd, password)
|
58
58
|
assert_equal(0, rc.exitstatus, "Expected exit status 0, but it was #{rc.exitstatus}. STDERR was: #{err}")
|
59
59
|
assert(err.empty?, "Expected empty STDERR, but it yielded #{err}")
|
60
60
|
assert(out =~ /#{expected_out}/, "'#{out}' did not match expected response '#{expected_out}'")
|
61
61
|
end
|
62
62
|
|
63
|
-
def assert_error(expected_err, cmd, password =
|
63
|
+
def assert_error(expected_err, cmd, password = locker_password)
|
64
64
|
out, err, rc = execute(cmd, password)
|
65
65
|
assert_not_equal(0, rc.exitstatus, "Expected non-zero exit status, but it was #{rc.exitstatus}. STDOUT was: #{out}")
|
66
66
|
assert(out.empty?, "Expected empty STDOUT, but it yielded #{out}")
|
@@ -68,7 +68,7 @@ module Test
|
|
68
68
|
end
|
69
69
|
|
70
70
|
def execute(cmd, password)
|
71
|
-
Open3.capture3("echo \"#{password}\" | #{APP} #{cmd} --file \"#{
|
71
|
+
Open3.capture3("echo \"#{password}\" | #{APP} #{cmd} --file \"#{locker_file}\"")
|
72
72
|
end
|
73
73
|
|
74
74
|
def fixture(name)
|
@@ -1,16 +1,16 @@
|
|
1
1
|
require 'helper'
|
2
2
|
require 'tempfile'
|
3
3
|
|
4
|
-
class
|
5
|
-
def
|
6
|
-
assert_raise Pwl::
|
7
|
-
Pwl::
|
4
|
+
class TestLockerConstruction < Test::Pwl::TestCase
|
5
|
+
def test_existing_locker
|
6
|
+
assert_raise Pwl::Locker::FileAlreadyExistsError do
|
7
|
+
Pwl::Locker.new(locker_file, locker_password)
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
def
|
12
|
-
assert_raise Pwl::
|
13
|
-
Pwl::
|
11
|
+
def test_nonexisting_locker
|
12
|
+
assert_raise Pwl::Locker::FileNotFoundError do
|
13
|
+
Pwl::Locker.open(temp_file_name, locker_password)
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
@@ -20,11 +20,11 @@ class TestStoreConstruction < Test::Pwl::TestCase
|
|
20
20
|
CREATED = 3 # user and system root exists, system root contains created stamp
|
21
21
|
SALT = 4 # like above, plus salt is set to random value
|
22
22
|
|
23
|
-
def
|
24
|
-
{USER => Pwl::
|
25
|
-
SYSTEM => Pwl::
|
26
|
-
CREATED => Pwl::
|
27
|
-
SALT => Pwl::
|
23
|
+
def test_existing_uninitialized_locker
|
24
|
+
{USER => Pwl::Locker::NotInitializedError,
|
25
|
+
SYSTEM => Pwl::Locker::NotInitializedError,
|
26
|
+
CREATED => Pwl::Locker::NotInitializedError,
|
27
|
+
SALT => Pwl::Locker::WrongMasterPasswordError,
|
28
28
|
}.each{|fake_level, error| assert assert_uninitialized(fake_level, error)}
|
29
29
|
end
|
30
30
|
|
@@ -34,10 +34,10 @@ class TestStoreConstruction < Test::Pwl::TestCase
|
|
34
34
|
existing_file = Tempfile.new(self.class.name)
|
35
35
|
|
36
36
|
begin
|
37
|
-
|
37
|
+
fake_locker(existing_file, fake_level)
|
38
38
|
|
39
39
|
assert_raise(error) do
|
40
|
-
Pwl::
|
40
|
+
Pwl::Locker.open(existing_file, locker_password)
|
41
41
|
end
|
42
42
|
ensure
|
43
43
|
existing_file.close
|
@@ -45,18 +45,18 @@ class TestStoreConstruction < Test::Pwl::TestCase
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
-
# Pretend that we have a
|
49
|
-
def
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
48
|
+
# Pretend that we have a locker with correct layout
|
49
|
+
def fake_locker(existing_file, fake_level)
|
50
|
+
locker = PStore.new(existing_file)
|
51
|
+
locker.transaction{
|
52
|
+
locker.commit if fake_level < USER
|
53
|
+
locker[:user] = {}
|
54
|
+
locker.commit if fake_level < SYSTEM
|
55
|
+
locker[:system] = {}
|
56
|
+
locker.commit if fake_level < CREATED
|
57
|
+
locker[:system][:created] = DateTime.now
|
58
|
+
locker.commit if fake_level < SALT
|
59
|
+
locker[:system][:salt] = Random.rand.to_s
|
60
60
|
}
|
61
61
|
end
|
62
62
|
end
|
@@ -1,90 +1,90 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
|
-
class
|
4
|
-
def
|
5
|
-
|
6
|
-
assert_equal('bar',
|
3
|
+
class TestLockerCRUD < Test::Pwl::TestCase
|
4
|
+
def test_add_get
|
5
|
+
locker.add('foo', 'bar')
|
6
|
+
assert_equal('bar', locker.get('foo'))
|
7
7
|
end
|
8
8
|
|
9
9
|
def test_get_blank
|
10
|
-
assert_raise Pwl::
|
11
|
-
|
10
|
+
assert_raise Pwl::Locker::BlankKeyError do
|
11
|
+
locker.get('')
|
12
12
|
end
|
13
13
|
|
14
|
-
assert_raise Pwl::
|
15
|
-
|
14
|
+
assert_raise Pwl::Locker::BlankKeyError do
|
15
|
+
locker.get(nil)
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
def
|
20
|
-
assert_raise Pwl::
|
21
|
-
|
19
|
+
def test_add_get_blank
|
20
|
+
assert_raise Pwl::Locker::BlankValueError do
|
21
|
+
locker.add('empty', '')
|
22
22
|
end
|
23
23
|
|
24
|
-
assert_raise Pwl::
|
25
|
-
|
24
|
+
assert_raise Pwl::Locker::BlankValueError do
|
25
|
+
locker.add('nil', nil)
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
29
|
def test_unknown_key
|
30
|
-
assert_raise Pwl::
|
31
|
-
|
30
|
+
assert_raise Pwl::Locker::KeyNotFoundError do
|
31
|
+
locker.get('foo')
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
35
|
def test_list_empty
|
36
|
-
assert_empty(
|
36
|
+
assert_empty(locker.list)
|
37
37
|
end
|
38
38
|
|
39
39
|
def test_list
|
40
40
|
test_vector = Hash['foo', 'one', 'bar', 'two', 'Chuck Norris', 'Roundhouse Kick']
|
41
|
-
test_vector.each{|k,v|
|
42
|
-
assert_equal(test_vector.keys,
|
43
|
-
|
44
|
-
assert_equal(test_vector[key],
|
41
|
+
test_vector.each{|k,v| locker.add(k, v)}
|
42
|
+
assert_equal(test_vector.keys, locker.list)
|
43
|
+
locker.list.each{|key|
|
44
|
+
assert_equal(test_vector[key], locker.get(key))
|
45
45
|
}
|
46
46
|
end
|
47
47
|
|
48
48
|
def test_all
|
49
49
|
test_vector = Hash['foo', 'one', 'bar', 'two', 'Chuck Norris', 'Roundhouse Kick']
|
50
|
-
test_vector.each{|k,v|
|
51
|
-
assert_equal(test_vector,
|
52
|
-
|
50
|
+
test_vector.each{|k,v| locker.add(k, v)}
|
51
|
+
assert_equal(test_vector, locker.all)
|
52
|
+
locker.all.each{|k,v|
|
53
53
|
assert_equal(test_vector[k], v)
|
54
54
|
}
|
55
55
|
end
|
56
56
|
|
57
57
|
def test_list_filter
|
58
58
|
test_vector = Hash['foo', 'one', 'bar', 'two', 'Chuck Norris', 'Roundhouse Kick']
|
59
|
-
test_vector.each{|k,v|
|
59
|
+
test_vector.each{|k,v| locker.add(k, v)}
|
60
60
|
|
61
61
|
filter = 'foo bar'
|
62
62
|
expected = test_vector.keys.select{|k,v| k =~ /#{filter}/}
|
63
|
-
assert_equal(expected,
|
63
|
+
assert_equal(expected, locker.list(filter))
|
64
64
|
end
|
65
65
|
|
66
66
|
def test_delete
|
67
|
-
|
68
|
-
assert_equal('bar',
|
67
|
+
locker.add('foo', 'bar')
|
68
|
+
assert_equal('bar', locker.delete('foo'))
|
69
69
|
|
70
|
-
assert_raise Pwl::
|
71
|
-
|
70
|
+
assert_raise Pwl::Locker::KeyNotFoundError do
|
71
|
+
locker.get('foo')
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
75
75
|
def test_delete_blank
|
76
|
-
assert_raise Pwl::
|
77
|
-
|
76
|
+
assert_raise Pwl::Locker::BlankKeyError do
|
77
|
+
locker.delete('')
|
78
78
|
end
|
79
79
|
|
80
|
-
assert_raise Pwl::
|
81
|
-
|
80
|
+
assert_raise Pwl::Locker::BlankKeyError do
|
81
|
+
locker.delete(nil)
|
82
82
|
end
|
83
83
|
end
|
84
84
|
|
85
85
|
def test_delete_unknown_key
|
86
|
-
assert_raise Pwl::
|
87
|
-
|
86
|
+
assert_raise Pwl::Locker::KeyNotFoundError do
|
87
|
+
locker.delete('foo')
|
88
88
|
end
|
89
89
|
end
|
90
90
|
end
|
@@ -1,35 +1,35 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
|
-
class
|
3
|
+
class TestLockerMetaData < Test::Pwl::TestCase
|
4
4
|
# when comparing timestamps, allow not more than this difference in seconds
|
5
|
-
TIMESTAMP_PRECISION =
|
5
|
+
TIMESTAMP_PRECISION = 0.001
|
6
6
|
|
7
7
|
def test_created
|
8
|
-
assert_equal(nil,
|
9
|
-
assert_in_delta(DateTime.now
|
8
|
+
assert_equal(nil, locker.last_accessed)
|
9
|
+
assert_in_delta(DateTime.now, locker.created, TIMESTAMP_PRECISION)
|
10
10
|
end
|
11
11
|
|
12
12
|
def test_last_accessed
|
13
|
-
assert_equal(nil,
|
14
|
-
|
15
|
-
assert_equal(nil,
|
16
|
-
|
17
|
-
assert_in_delta(DateTime.now
|
13
|
+
assert_equal(nil, locker.last_accessed)
|
14
|
+
locker.add('foobar', 'barfoot')
|
15
|
+
assert_equal(nil, locker.last_accessed)
|
16
|
+
locker.get('foobar')
|
17
|
+
assert_in_delta(DateTime.now, locker.last_accessed, TIMESTAMP_PRECISION)
|
18
18
|
end
|
19
19
|
|
20
20
|
def test_last_accessed_nonexisting
|
21
|
-
assert_equal(nil,
|
22
|
-
assert_raise Pwl::
|
23
|
-
|
21
|
+
assert_equal(nil, locker.last_accessed)
|
22
|
+
assert_raise Pwl::Locker::KeyNotFoundError do
|
23
|
+
locker.get('foobar')
|
24
24
|
end
|
25
25
|
|
26
26
|
# Make sure a failed read is not counted as last_accessed
|
27
|
-
assert_equal(nil,
|
27
|
+
assert_equal(nil, locker.last_accessed)
|
28
28
|
end
|
29
29
|
|
30
30
|
def test_last_modified
|
31
|
-
assert_equal(nil,
|
32
|
-
|
33
|
-
assert_in_delta(DateTime.now
|
31
|
+
assert_equal(nil, locker.last_modified)
|
32
|
+
locker.add('foobar', 'barfoot')
|
33
|
+
assert_in_delta(DateTime.now, locker.last_modified, TIMESTAMP_PRECISION)
|
34
34
|
end
|
35
35
|
end
|
@@ -1,12 +1,12 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
|
-
class
|
3
|
+
class TestLockerPasswordPolicy < Test::Pwl::TestCase
|
4
4
|
def setup
|
5
|
-
@
|
5
|
+
@locker_file = temp_file_name
|
6
6
|
end
|
7
7
|
|
8
8
|
def teardown
|
9
|
-
File.unlink(@
|
9
|
+
File.unlink(@locker_file) if File.exist?(@locker_file)
|
10
10
|
end
|
11
11
|
|
12
12
|
def test_validations_ok_0
|
@@ -48,9 +48,9 @@ class TestStorePasswordPolicy < Test::Pwl::TestCase
|
|
48
48
|
private
|
49
49
|
|
50
50
|
def assert_valid(password)
|
51
|
-
|
52
|
-
|
53
|
-
assert_equal('bar',
|
51
|
+
locker = ::Pwl::Locker.new(@locker_file, password)
|
52
|
+
locker.add('foo', 'bar')
|
53
|
+
assert_equal('bar', locker.get('foo'))
|
54
54
|
end
|
55
55
|
|
56
56
|
def assert_invalid(password)
|
@@ -1,34 +1,34 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
|
-
class
|
3
|
+
class TestLockerSecurity < Test::Pwl::TestCase
|
4
4
|
|
5
|
-
# Read back raw PStore and ensure what we get is not clear text, even though we know the structure of the
|
5
|
+
# Read back raw PStore and ensure what we get is not clear text, even though we know the structure of the locker
|
6
6
|
def test_encryption
|
7
|
-
assert(!
|
8
|
-
|
9
|
-
raw = PStore.new(
|
7
|
+
assert(!locker.nil?, "Locker expected, but it is nil")
|
8
|
+
locker.add('foo', 'bar')
|
9
|
+
raw = PStore.new(locker_file)
|
10
10
|
assert_not_equal('bar', raw.transaction{raw[:user]['foo']})
|
11
11
|
assert_nil(raw.transaction{raw[:user]['foo']}) # must not find cleartext entry
|
12
12
|
end
|
13
13
|
|
14
14
|
def test_wrong_password
|
15
|
-
assert_raise Pwl::
|
16
|
-
Pwl::
|
15
|
+
assert_raise Pwl::Locker::WrongMasterPasswordError do
|
16
|
+
Pwl::Locker.open(locker_file, locker_password.reverse)
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
20
|
def test_change_password
|
21
|
-
assert(!
|
22
|
-
|
23
|
-
|
21
|
+
assert(!locker.nil?, "Locker expected, but it is nil")
|
22
|
+
locker.add('Homer', 'Simpson')
|
23
|
+
locker.change_password!(locker_password.reverse)
|
24
24
|
|
25
25
|
# the old password must not work anymore
|
26
|
-
assert_raise Pwl::
|
27
|
-
Pwl::
|
26
|
+
assert_raise Pwl::Locker::WrongMasterPasswordError do
|
27
|
+
Pwl::Locker.open(locker_file, locker_password)
|
28
28
|
end
|
29
29
|
|
30
30
|
# Read back with the changed password
|
31
|
-
reopened = Pwl::
|
31
|
+
reopened = Pwl::Locker.open(locker_file, locker_password.reverse)
|
32
32
|
assert_equal('Simpson', reopened.get('Homer'))
|
33
33
|
end
|
34
34
|
end
|