shh 0.0.6 → 0.0.7

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/README.rdoc CHANGED
@@ -5,3 +5,92 @@ Secret Squirrel
5
5
  This is a command line utility for managing secure information such as accounts, passwords as individual files so that they can be easily managed in a version control repository.
6
6
 
7
7
  You won't be hiding anything from the NSA with this level of encryption so it isn't recommended that you make your repository publicly accessible.
8
+
9
+ Now with more tab completion!
10
+
11
+ = Usage
12
+
13
+ Here you sit expectantly in front of a computer at the command line.
14
+
15
+ == Install
16
+
17
+ gem install shh
18
+
19
+ (you may need to install gemcutter first)
20
+
21
+ == Launch
22
+
23
+ Open all 'secrets' stored in ~/.secret
24
+
25
+ > shh
26
+
27
+ Opens all 'secrets' stored in foo/.secret
28
+
29
+ > shh foo
30
+
31
+ == Authenticate
32
+
33
+ Enter your passphrase
34
+
35
+ This passphrase will be used to encrypt and decrypt all of your secrets so don't make it too obvious.
36
+
37
+ == Listing mode
38
+
39
+ This mode allows you to view and edit 'entries' (which are encrypted hashes stored in files)
40
+
41
+ > list
42
+
43
+ bitbucket (260f34de-6779-4367-af2a-44184dec1cc1)
44
+ amazon (291a3e6c-2c7b-4960-a146-1f6635d9a74e)
45
+ yahoo (2fe408e7-7f2b-4dc0-854a-c92c21f131ae)
46
+ evernote (41ad0a21-eafc-4f70-9b0d-0251be207b9e)
47
+ gmail (8b35c1f2-851d-40fc-bd12-c2aad12c370a)
48
+ rememberthemilk (a499f612-d034-4e49-82a8-6967d29653a1)
49
+
50
+ * list - show entries
51
+ * edit <name> - edit or create entry
52
+ * view <name> - view entry
53
+ * quit
54
+
55
+ == Viewing mode
56
+
57
+ This mode allows you to view (but not edit) an existing entry
58
+
59
+ > view bitbucket
60
+ (bitbucket) > list
61
+ id,name,password,username
62
+ (bitbucket) > show username
63
+ markryall
64
+
65
+ list keys, show key, copy key or quit? c password
66
+
67
+ * list - show entry keys
68
+ * show <key> - show entry value on screen
69
+ * copy <key> - copy entry value to clipboard
70
+ * quit
71
+
72
+ == Editing mode
73
+
74
+ This mode is viewing mode plus some editing commands
75
+
76
+ > edit bitbucket
77
+ (bitbucket) > edit foo
78
+ Enter new value for foo
79
+ bar
80
+ (bitbucket) > delete foo
81
+
82
+ * edit <key> - edit or create key
83
+ * delete <key> - remove a key
84
+
85
+ == Quitting mode
86
+
87
+ You don't want to be a quitter
88
+
89
+ = Future plans for world domination
90
+
91
+ * Add some color
92
+ * Add help
93
+ * Launch applications for urls
94
+ * Add multi line values (edit with default text editor)
95
+ * Include uuid as part of encryption key (and upgrade encryption on existing entries)
96
+ * add some source control (hg/git) commands
data/bin/shh CHANGED
@@ -2,4 +2,4 @@
2
2
 
3
3
  $:.unshift File.dirname(__FILE__)+'/../lib'
4
4
  require 'shh'
5
- Shh::Cli.new.execute(*ARGV)
5
+ Shh::Cli.execute(*ARGV)
data/lib/shh/cli.rb CHANGED
@@ -1,93 +1,16 @@
1
1
  require 'rubygems'
2
- require 'pathname2'
3
- require 'highline/import'
4
- require 'uuidtools'
5
- require 'yaml'
6
- require 'shh/crypt'
7
- require 'shh/clipboard'
2
+ require 'shh/repository'
3
+ require 'shh/prompt'
4
+ require 'shh/entries_menu'
8
5
 
9
6
  module Shh
10
7
  class Cli
11
- def execute *args
12
- passphrase = ask('Enter your passphrase') { |q| q.echo = false }
8
+ def self.execute *args
9
+ prompt = Prompt.new
10
+ passphrase = prompt.get('Enter your passphrase', :silent => true)
11
+ path = args.shift || ('~')
13
12
 
14
- path = args.shift
15
- @folder = path ? Pathname.new(path) : Pathname.new(File.expand_path('~'))+'.secret'
16
- @folder.mkdir_p
17
- @crypt = Crypt.new(passphrase)
18
- @clipboard = Shh.clipboard
19
-
20
- loop do
21
- choose do |menu|
22
- menu.layout = :menu_only
23
- menu.shell = true
24
- menu.choice('list entries') { list }
25
- menu.choice('edit entry') { |command, details| edit details }
26
- menu.choice('view entry') { |command, details| view details }
27
- menu.choice('quit') { exit }
28
- end
29
- end
30
- end
31
-
32
- def list
33
- each_entry {|entry| say "#{entry['name']} (#{entry['id']})" }
34
- end
35
-
36
- def edit name=''
37
- entry = find_entry(check_name(name))
38
- entry ||= {'name' => check_name(name), 'id' => UUIDTools::UUID.random_create.to_s}
39
- persist_entry prompt_loop(entry)
40
- end
41
-
42
- def view name=''
43
- prompt_loop find_entry(check_name(name)), true
44
- end
45
-
46
- private
47
-
48
- def each_entry
49
- @folder.children.each do |child|
50
- yield load_entry(child)
51
- end
52
- end
53
-
54
- def check_name name
55
- name = ask('Enter the entry name') unless name.size > 0
56
- name
57
- end
58
-
59
- def find_entry name
60
- each_entry {|e| return e if e['name'] == name}
61
- nil
62
- end
63
-
64
- def load_entry path
65
- entry = path.open('rb') {|io| @crypt.decrypt(io) }
66
- YAML::load(entry)
67
- end
68
-
69
- def persist_entry entry
70
- (@folder + entry['id']).open('wb') {|io| @crypt.encrypt(entry.to_yaml, io) }
71
- end
72
-
73
- def prompt_loop hash, read_only=false
74
- loop do
75
- choose do |menu|
76
- menu.layout = :menu_only
77
- menu.shell = true
78
- menu.choice('edit key') { |command, name| hash[name] = new_value(name) } unless read_only
79
- menu.choice('list keys') { say(hash.keys.sort.join(',')) }
80
- menu.choice('show key') { |command, name| say(hash[name]) if hash[name] }
81
- menu.choice('delete key') { |command, name| hash[name] = nil } unless read_only
82
- menu.choice('copy key') { |command, name| @clipboard.content = hash[name] } if @clipboard
83
- menu.choice('quit') { return hash }
84
- end
85
- end
86
- end
87
-
88
- def new_value name
89
- echo = (name =~ /pass/) ? false : true
90
- ask("Enter new value for #{name}") {|q| q.echo = echo }
13
+ EntriesMenu.new(prompt, Repository.new(passphrase, path)).main_loop
91
14
  end
92
15
  end
93
16
  end
@@ -0,0 +1,63 @@
1
+ require 'uuidtools'
2
+ require 'readline'
3
+ require 'shh/entry_menu'
4
+
5
+ module Shh
6
+ class EntriesMenu
7
+ def initialize prompt, repository
8
+ @prompt, @repository = prompt, repository
9
+ refresh
10
+ end
11
+
12
+ def main_loop
13
+ prompt_text = ' > '
14
+
15
+ begin
16
+ while line = Readline.readline(prompt_text, true).strip
17
+ case line
18
+ when 'quit'
19
+ return
20
+ when 'refresh'
21
+ refresh
22
+ when 'list'
23
+ list
24
+ when /^view (.*)/
25
+ view $1
26
+ when /^edit (.*)/
27
+ edit $1
28
+ end
29
+ end
30
+ rescue Interrupt => e
31
+ return
32
+ end
33
+ end
34
+
35
+ def refresh
36
+ commands = ['list', 'refresh', 'quit']
37
+ @repository.each_entry do |entry|
38
+ commands << "edit #{entry['name']}"
39
+ commands << "view #{entry['name']}"
40
+ end
41
+ Readline.completion_proc = lambda do |text|
42
+ commands.grep( /^#{Regexp.escape(text)}/ ).sort
43
+ end
44
+ Readline.completer_word_break_characters = ''
45
+ end
46
+
47
+ def list
48
+ @repository.each_entry {|entry| say "#{entry['name']} (#{entry['id']})" }
49
+ end
50
+
51
+ def edit name
52
+ entry = @repository.find_entry(name)
53
+ entry ||= {'name' => name, 'id' => UUIDTools::UUID.random_create.to_s}
54
+ @repository.persist_entry EntryMenu.new(@prompt, entry).main_loop
55
+ refresh
56
+ end
57
+
58
+ def view name
59
+ EntryMenu.new(@prompt, @repository.find_entry(name), true).main_loop
60
+ refresh
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,60 @@
1
+ require 'shh/clipboard'
2
+
3
+ module Shh
4
+ class EntryMenu
5
+ def initialize prompt, hash, read_only=false
6
+ @prompt, @hash, @read_only = prompt, hash, read_only
7
+ @clipboard = Shh.clipboard
8
+ refresh
9
+ end
10
+
11
+ def main_loop
12
+ prompt_text = "(#{@hash['name']}) > "
13
+
14
+ begin
15
+ while line = Readline.readline(prompt_text, true).strip
16
+ case line
17
+ when 'quit'
18
+ return @hash
19
+ when 'list'
20
+ say(@hash.keys.sort.join(','))
21
+ when /^edit (.*)/
22
+ name = $1
23
+ @hash[name] = new_value(name) unless @read_only
24
+ when /^copy (.*)/
25
+ name = $1
26
+ @clipboard.content = @hash[name] if @clipboard and @hash[name]
27
+ when /^delete (.*)/
28
+ name = $1
29
+ @hash.delete(name) unless @read_only
30
+ when /^show (.*)/
31
+ name = $1
32
+ say(@hash[name]) if @hash[name]
33
+ end
34
+ end
35
+ rescue Interrupt => e
36
+ return @hash
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def refresh
43
+ commands = ['list', 'quit']
44
+ @hash.keys.each do |key|
45
+ commands << "edit #{key}" unless @read_only
46
+ commands << "delete #{key}" unless @read_only
47
+ commands << "show #{key}"
48
+ commands << "copy #{key}"
49
+ end
50
+ Readline.completion_proc = lambda do |text|
51
+ commands.grep( /^#{Regexp.escape(text)}/ ).sort
52
+ end
53
+ Readline.completer_word_break_characters = ''
54
+ end
55
+
56
+ def new_value name
57
+ @prompt.get "Enter new value for #{name}", :silent => (name =~ /pass/)
58
+ end
59
+ end
60
+ end
data/lib/shh/prompt.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'highline/import'
2
+
3
+ module Shh
4
+ class Prompt
5
+ def get text, params={}
6
+ return params[:value] if params[:value] and params[:value].size > 0
7
+ echo = params[:silent] ? false : true
8
+ ask(text) { |q| q.echo = false }
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,39 @@
1
+ require 'pathname2'
2
+ require 'yaml'
3
+ require 'shh/crypt'
4
+
5
+ module Shh
6
+ class Repository
7
+ attr_reader :folder
8
+
9
+ def initialize passphrase, path
10
+ @folder = Pathname.new(File.expand_path(path)) + '.secret'
11
+ @folder.mkdir_p
12
+ @crypt = Crypt.new(passphrase)
13
+ end
14
+
15
+ def each_entry
16
+ @folder.children.each do |child|
17
+ entry = load_entry(child)
18
+ yield entry if entry
19
+ end
20
+ end
21
+
22
+ def find_entry name
23
+ each_entry {|e| return e if e['name'] == name}
24
+ nil
25
+ end
26
+
27
+ def load_entry path
28
+ return nil if path.directory?
29
+ yaml = path.open('rb') {|io| @crypt.decrypt(io) }
30
+ entry = YAML::load(yaml)
31
+ return nil unless entry
32
+ path.basename.to_s == entry['id'] ? entry : nil
33
+ end
34
+
35
+ def persist_entry entry
36
+ (@folder + entry['id']).open('wb') {|io| @crypt.encrypt(entry.to_yaml, io) }
37
+ end
38
+ end
39
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shh
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark Ryall
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-12-18 00:00:00 +11:00
12
+ date: 2009-12-26 00:00:00 +11:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -67,6 +67,10 @@ files:
67
67
  - lib/shh/clipboard.rb
68
68
  - lib/shh/crypt.rb
69
69
  - lib/shh/darwin10_clipboard.rb
70
+ - lib/shh/entries_menu.rb
71
+ - lib/shh/entry_menu.rb
72
+ - lib/shh/prompt.rb
73
+ - lib/shh/repository.rb
70
74
  - lib/shh/win32_clipboard.rb
71
75
  - lib/shh.rb
72
76
  - bin/shh