Skip to content
This repository has been archived by the owner on Dec 25, 2019. It is now read-only.

Commit

Permalink
Add simple HTML export using built-in ERB template
Browse files Browse the repository at this point in the history
  • Loading branch information
Nicholas E. Rabenau committed Mar 10, 2012
1 parent 7b96209 commit 91bafd1
Show file tree
Hide file tree
Showing 8 changed files with 190 additions and 7 deletions.
32 changes: 26 additions & 6 deletions bin/pwm
Expand Up @@ -27,14 +27,16 @@ program :help, 'Exit Status', "#{program(:name)} sets the following exit status
program :help, 'Author', 'Nicholas E. Rabenau <nerab@gmx.at>'

DEFAULT_STORE_FILE = File.expand_path("~/.#{program(:name)}.pstore")
DEFAULT_EXPORT_TEMPLATE = File.join(File.dirname(__FILE__), *%w[.. templates export.html.erb])

store_file = DEFAULT_STORE_FILE

global_option '-V', '--verbose', 'Enable verbose output'
global_option('-f', '--file FILE', 'Determine the file that holds the store'){|file| store_file = file}
global_option '-g', '--gui', 'Request the master password using an OS-specific GUI dialog. This option takes precedence over STDIN.'

command :init do |c|
c.syntax = "#{program(:name)} init"
c.syntax = "#{program(:name)} #{c.name}"
c.summary = 'Initializes a new store'
c.description = 'This command initializes a new password store. Password quality is enforced using validation rules.'
c.example "Initializes a new password store in #{DEFAULT_STORE_FILE}", "#{program(:name)} #{c.name}"
Expand Down Expand Up @@ -68,7 +70,7 @@ command :init do |c|
end

command :get do |c|
c.syntax = "#{program(:name)} get NAME"
c.syntax = "#{program(:name)} #{c.name} NAME"
c.summary = 'Retrieves the value for NAME and prints it to STDOUT.'
c.description = 'This command retrieves the value stored under NAME and prints it on STDOUT.'
c.example 'Reads the value stored under the name "foo" and prints it to STDOUT', "#{program(:name)} #{c.name} foo"
Expand All @@ -91,7 +93,7 @@ command :get do |c|
end

command :list do |c|
c.syntax = "#{program(:name)} list [FILTER]"
c.syntax = "#{program(:name)} #{c.name} [FILTER]"
c.summary = 'Lists all names with optional FILTER.'
c.description = 'This command prints all names to STDOUT. If present, only those names matching FILTER will be returned.'
c.example 'Prints all names', "#{program(:name)} #{c.name}"
Expand Down Expand Up @@ -122,7 +124,7 @@ command :list do |c|
end

command :put do |c|
c.syntax = "#{program(:name)} put NAME [VALUE]"
c.syntax = "#{program(:name)} #{c.name} NAME [VALUE]"
c.summary = 'Stores VALUE under NAME'
c.description = 'Adds or updates the entry stored under NAME. If NAME is already present in the store, it will be updated with VALUE. If NAME is not already present in the store, a new entry will be created. If VALUE is not given, it will be read from STDIN.'
c.example 'Stores the value "bar" under the name "foo"', "#{program(:name)} #{c.name} foo bar"
Expand Down Expand Up @@ -161,7 +163,7 @@ command :put do |c|
end

command :delete do |c|
c.syntax = "#{program(:name)} delete NAME"
c.syntax = "#{program(:name)} #{c.name} NAME"
c.summary = 'Deletes the entry stored under NAME'
c.description = 'Deletes the complete entry that is stored under NAME. If NAME is not present in the store, an error will thrown.'
c.example 'Deletes what was stored under the name "foo"', "#{program(:name)} #{c.name} foo"
Expand All @@ -181,7 +183,7 @@ command :delete do |c|
end

command :passwd do |c|
c.syntax = "#{program(:name)} passwd [NEW_MASTER_PASSWORD]"
c.syntax = "#{program(:name)} #{c.name} [NEW_MASTER_PASSWORD]"
c.summary = 'Changes the master password to NEW_MASTER_PASSWORD.'
c.description = 'This command changes the master password of the store. Password quality is enforced using validation rules.'
c.action do |args, options|
Expand Down Expand Up @@ -224,6 +226,24 @@ command :passwd do |c|
end
end

command :export do |c|
c.syntax = "#{program(:name)} #{c.name}"
c.summary = 'Exports all entries.'
c.description = 'This command prints all entries to STDOUT.'
c.example 'Prints all entries', "#{program(:name)} #{c.name}"
c.action do |args, options|
exit_with(:file_not_found, options.verbose, :file => store_file) unless File.exists?(store_file)

begin
template = ERB.new(File.read(DEFAULT_EXPORT_TEMPLATE))
store = Pwm::Store.open(store_file, get_password('Enter master password:', options.gui))
puts template.result(binding)
rescue Pwm::Dialog::Cancelled
exit_with(:aborted, options.verbose)
end
end
end

def exit_with(error_code, verbose, msg_args = {})
msg = EXIT_CODES[error_code]
raise "No message defined for error #{error_code}" if !msg
Expand Down
13 changes: 12 additions & 1 deletion lib/pwm/store.rb
Expand Up @@ -165,7 +165,7 @@ def delete(key)
end

#
# Return all keys, optionally filtered by filter.
# Return all keys, optionally filtered by filter
#
def list(filter = nil)
@backend.transaction(true){
Expand All @@ -179,6 +179,17 @@ def list(filter = nil)
}
end

#
# Return all entries
#
def all
result = {}
@backend.transaction(true){
@backend[:user].each{|k,v| result[decrypt(k)] = decrypt(v)}
}
result
end

#
# Change the master password to +new_master_password+. Note that we don't take a password confirmation here.
# This is up to a UI layer.
Expand Down
38 changes: 38 additions & 0 deletions templates/export.html.erb
@@ -0,0 +1,38 @@
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>pwd export</title>
<style type="text/css">
table { page-break-inside:auto }
tr { page-break-inside:avoid; page-break-after:auto }
thead { display:table-header-group }
tfoot { display:table-footer-group }
</style>
</head>
<body>
<table>
<thead>
<tr>
<th>Name</th>
<th>Value</th>
</tr>
</thead>
<tfoot>
<tr>
<td>Exported from pwm</td>
<td>Last modified: <%= store.last_modified.strftime('%F') rescue 'never' %></td>
<td></td>
</tr>
</tfoot>
<tbody>
<% store.all.each{|key, value|%>
<tr>
<td><%= key %></td>
<td><%= value %></td>
</tr>
<% } %>
</tbody>
</table>
</body>
</html>
19 changes: 19 additions & 0 deletions test/acceptance/test_export.rb
@@ -0,0 +1,19 @@
require 'helper'

# Tests `pwm export`
class TestExport < Test::Pwm::AppTestCase
def test_empty
fixture = fixture("test_empty.html").gsub('DATE_TIME_STAMP', 'never')
# assert_successful(fixture, 'export')
end

def test_all
test_vector = Hash['foo', 'one', 'bar', 'two', 'Chuck Norris', 'Roundhouse Kick']
test_vector.each{|k,v|
assert_successful('', "put '#{k}' '#{v}'")
}

fixture = fixture("test_all.html").gsub('DATE_TIME_STAMP', DateTime.now.strftime('%F'))
assert_successful(fixture, 'export')
end
end
48 changes: 48 additions & 0 deletions test/fixtures/test_all.html
@@ -0,0 +1,48 @@
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>pwd export</title>
<style type="text/css">
table { page-break-inside:auto }
tr { page-break-inside:avoid; page-break-after:auto }
thead { display:table-header-group }
tfoot { display:table-footer-group }
</style>
</head>
<body>
<table>
<thead>
<tr>
<th>Name</th>
<th>Value</th>
</tr>
</thead>
<tfoot>
<tr>
<td>Exported from pwm</td>
<td>Last modified: 2012-03-10</td>
<td></td>
</tr>
</tfoot>
<tbody>

<tr>
<td>foo</td>
<td>one</td>
</tr>

<tr>
<td>bar</td>
<td>two</td>
</tr>

<tr>
<td>Chuck Norris</td>
<td>Roundhouse Kick</td>
</tr>

</tbody>
</table>
</body>
</html>
33 changes: 33 additions & 0 deletions test/fixtures/test_empty.html
@@ -0,0 +1,33 @@
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>pwd export</title>
<style type="text/css">
table { page-break-inside:auto }
tr { page-break-inside:avoid; page-break-after:auto }
thead { display:table-header-group }
tfoot { display:table-footer-group }
</style>
</head>
<body>
<table>
<thead>
<tr>
<th>Name</th>
<th>Value</th>
</tr>
</thead>
<tfoot>
<tr>
<td>Exported from pwm</td>
<td>Last modified: DATE_TIME_STAMP</td>
<td></td>
</tr>
</tfoot>
<tbody>

</tbody>
</table>
</body>
</html>
5 changes: 5 additions & 0 deletions test/helper.rb
Expand Up @@ -18,6 +18,7 @@
require 'tmpdir'

class Test::Unit::TestCase
FIXTURES_DIR = File.join(File.dirname(__FILE__), 'fixtures')
end

module Test
Expand Down Expand Up @@ -69,6 +70,10 @@ def assert_error(expected_err, cmd, password = store_password)
def execute(cmd, password)
Open3.capture3("echo \"#{password}\" | #{APP} #{cmd} --file \"#{store_file}\"")
end

def fixture(name)
File.read(File.join(FIXTURES_DIR, name))
end
end
end
end
9 changes: 9 additions & 0 deletions test/unit/test_store_crud.rb
Expand Up @@ -45,6 +45,15 @@ def test_list
}
end

def test_all
test_vector = Hash['foo', 'one', 'bar', 'two', 'Chuck Norris', 'Roundhouse Kick']
test_vector.each{|k,v| store.put(k, v)}
assert_equal(test_vector, store.all)
store.all.each{|k,v|
assert_equal(test_vector[k], v)
}
end

def test_list_filter
test_vector = Hash['foo', 'one', 'bar', 'two', 'Chuck Norris', 'Roundhouse Kick']
test_vector.each{|k,v| store.put(k, v)}
Expand Down

0 comments on commit 91bafd1

Please sign in to comment.