pwl 0.0.1 → 0.0.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/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
|