keyrack 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm 1.9.2
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source :rubygems
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+ gem 'net-scp', :require => 'net/scp'
6
+ gem 'highline'
7
+ gem 'clipboard'
8
+
9
+ # Add dependencies to develop your gem here.
10
+ # Include everything needed to run rake, tests, features, etc.
11
+ group :development do
12
+ gem "bundler", "~> 1.0.0"
13
+ gem "jeweler", "~> 1.5.1"
14
+ gem "rcov", ">= 0"
15
+ gem "mocha", :require => false
16
+ end
@@ -0,0 +1,34 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ clipboard (0.9.2)
5
+ ffi
6
+ zucker (>= 8)
7
+ ffi (1.0.1)
8
+ rake (>= 0.8.7)
9
+ git (1.2.5)
10
+ highline (1.6.1)
11
+ jeweler (1.5.1)
12
+ bundler (~> 1.0.0)
13
+ git (>= 1.2.5)
14
+ rake
15
+ mocha (0.9.10)
16
+ rake
17
+ net-scp (1.0.4)
18
+ net-ssh (>= 1.99.1)
19
+ net-ssh (2.0.23)
20
+ rake (0.8.7)
21
+ rcov (0.9.9)
22
+ zucker (8)
23
+
24
+ PLATFORMS
25
+ ruby
26
+
27
+ DEPENDENCIES
28
+ bundler (~> 1.0.0)
29
+ clipboard
30
+ highline
31
+ jeweler (~> 1.5.1)
32
+ mocha
33
+ net-scp
34
+ rcov
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Jeremy Stephens
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,20 @@
1
+ = keyrack
2
+
3
+ Keyrack is a simple password manager with local or remote storage and RSA
4
+ encryption.
5
+
6
+ == Contributing to keyrack
7
+
8
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
9
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
10
+ * Fork the project
11
+ * Start a feature/bugfix branch
12
+ * Commit and push until you are happy with your contribution
13
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
14
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
15
+
16
+ == Copyright
17
+
18
+ Copyright (c) 2010 Jeremy Stephens. See LICENSE.txt for
19
+ further details.
20
+
@@ -0,0 +1,61 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'rake'
11
+
12
+ require 'jeweler'
13
+ Jeweler::Tasks.new do |gem|
14
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
+ gem.name = "keyrack"
16
+ gem.homepage = "http://github.com/viking/keyrack"
17
+ gem.license = "MIT"
18
+ gem.summary = %Q{Simple password manager}
19
+ gem.description = %Q{Simple password manager with local/remote database storage and RSA encryption.}
20
+ gem.email = "viking@pillageandplunder.net"
21
+ gem.authors = ["Jeremy Stephens"]
22
+ # Include your dependencies below. Runtime dependencies are required when using your gem,
23
+ # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
24
+ # gem.add_runtime_dependency 'jabber4r', '> 0.1'
25
+ # gem.add_development_dependency 'rspec', '> 1.2.3'
26
+ gem.add_runtime_dependency 'net-scp'
27
+ gem.add_runtime_dependency 'highline'
28
+ gem.add_runtime_dependency 'clipboard'
29
+ gem.add_development_dependency 'rspec', '> 1.2.3'
30
+ gem.add_development_dependency "bundler", "~> 1.0.0"
31
+ gem.add_development_dependency "jeweler", "~> 1.5.1"
32
+ gem.add_development_dependency "rcov", ">= 0"
33
+ gem.add_development_dependency "mocha"
34
+ end
35
+ Jeweler::RubygemsDotOrgTasks.new
36
+
37
+ require 'rake/testtask'
38
+ Rake::TestTask.new(:test) do |test|
39
+ test.libs << 'lib' << 'test'
40
+ test.pattern = 'test/**/test_*.rb'
41
+ test.verbose = true
42
+ end
43
+
44
+ require 'rcov/rcovtask'
45
+ Rcov::RcovTask.new do |test|
46
+ test.libs << 'test'
47
+ test.pattern = 'test/**/test_*.rb'
48
+ test.verbose = true
49
+ end
50
+
51
+ task :default => :test
52
+
53
+ require 'rake/rdoctask'
54
+ Rake::RDocTask.new do |rdoc|
55
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
56
+
57
+ rdoc.rdoc_dir = 'rdoc'
58
+ rdoc.title = "keyrack #{version}"
59
+ rdoc.rdoc_files.include('README*')
60
+ rdoc.rdoc_files.include('lib/**/*.rb')
61
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require 'keyrack'
3
+
4
+ Keyrack::Runner.new(ARGV)
@@ -0,0 +1,14 @@
1
+ require 'openssl'
2
+ require 'yaml'
3
+ require 'optparse'
4
+ require 'net/scp'
5
+ require 'highline'
6
+ require 'clipboard'
7
+
8
+ module Keyrack
9
+ end
10
+
11
+ require File.dirname(__FILE__) + '/keyrack/database'
12
+ require File.dirname(__FILE__) + '/keyrack/store'
13
+ require File.dirname(__FILE__) + '/keyrack/ui'
14
+ require File.dirname(__FILE__) + '/keyrack/runner'
@@ -0,0 +1,33 @@
1
+ module Keyrack
2
+ class Database
3
+ def initialize(config)
4
+ store_config = config[:store].dup
5
+ @store = Store[store_config.delete(:type)].new(store_config)
6
+ key_path = File.expand_path(config[:key])
7
+ @key = OpenSSL::PKey::RSA.new(File.read(key_path), config[:password])
8
+ @data = decrypt
9
+ end
10
+
11
+ def add(site, username, password)
12
+ @data[site] = { :username => username, :password => password }
13
+ end
14
+
15
+ def get(site)
16
+ @data[site]
17
+ end
18
+
19
+ def sites
20
+ @data.keys
21
+ end
22
+
23
+ def save
24
+ @store.write(@key.public_encrypt(Marshal.dump(@data)))
25
+ end
26
+
27
+ private
28
+ def decrypt
29
+ data = @store.read
30
+ data ? Marshal.load(@key.private_decrypt(data)) : {}
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,32 @@
1
+ module Keyrack
2
+ class Runner
3
+ def initialize(argv)
4
+ OptionParser.new do |opts|
5
+ opts.on("-c", "--config FILE", "Specify configuration file") do |f|
6
+ @options = YAML.load_file(f)
7
+ end
8
+ end.parse(argv)
9
+
10
+ @ui = UI::Console.new
11
+ password = @ui.get_password
12
+ @database = Database.new(@options.merge(:password => password))
13
+ @ui.database = @database
14
+
15
+ main_loop
16
+ end
17
+
18
+ def main_loop
19
+ loop do
20
+ case @ui.menu
21
+ when :new
22
+ result = @ui.get_new_entry
23
+ @database.add(result[:site], result[:username], result[:password])
24
+ when :save
25
+ @database.save
26
+ when :quit
27
+ break
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,13 @@
1
+ module Keyrack
2
+ module Store
3
+ def self.[](name)
4
+ case name
5
+ when :filesystem then Filesystem
6
+ when :ssh then SSH
7
+ end
8
+ end
9
+ end
10
+ end
11
+
12
+ require File.dirname(__FILE__) + '/store/filesystem'
13
+ require File.dirname(__FILE__) + '/store/ssh'
@@ -0,0 +1,17 @@
1
+ module Keyrack
2
+ module Store
3
+ class Filesystem
4
+ def initialize(options)
5
+ @path = File.expand_path(options[:path])
6
+ end
7
+
8
+ def read
9
+ File.exist?(@path) ? File.read(@path) : nil
10
+ end
11
+
12
+ def write(data)
13
+ File.open(@path, 'w') { |f| f.write(data) }
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,23 @@
1
+ module Keyrack
2
+ module Store
3
+ class SSH
4
+ def initialize(options)
5
+ @host = options[:host]
6
+ @user = options[:user]
7
+ @path = options[:path]
8
+ end
9
+
10
+ def read
11
+ begin
12
+ Net::SCP.download!(@host, @user, @path)
13
+ rescue Net::SCP::Error
14
+ nil
15
+ end
16
+ end
17
+
18
+ def write(data)
19
+ Net::SCP.upload!(@host, @user, StringIO.new(data), @path)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,6 @@
1
+ module Keyrack
2
+ module UI
3
+ end
4
+ end
5
+
6
+ require File.dirname(__FILE__) + '/ui/console'
@@ -0,0 +1,49 @@
1
+ module Keyrack
2
+ module UI
3
+ class Console
4
+ attr_accessor :database
5
+ def initialize
6
+ @highline = HighLine.new
7
+ end
8
+
9
+ def get_password
10
+ @highline.ask("Keyrack password: ") { |q| q.echo = false }
11
+ end
12
+
13
+ def menu
14
+ entries = []
15
+ choices = %w{n s q}
16
+ @database.sites.each_with_index do |site, i|
17
+ entry = @database.get(site)
18
+ entries << entry
19
+ choices << "#{i+1}"
20
+ @highline.say("% 2d. %s [%s]" % [i+1, site, entry[:username]])
21
+ end
22
+ @highline.say(" n. Add new")
23
+ @highline.say(" s. Save")
24
+ @highline.say(" q. Quit")
25
+ result = @highline.ask(" ? ") { |q| q.in = choices }
26
+ case result
27
+ when "n"
28
+ :new
29
+ when "s"
30
+ :save
31
+ when "q"
32
+ :quit
33
+ else
34
+ Clipboard.copy(entries[result.to_i - 1][:password])
35
+ @highline.say("The password has been copied to your clipboard.")
36
+ nil
37
+ end
38
+ end
39
+
40
+ def get_new_entry
41
+ result = {}
42
+ result[:site] = @highline.ask("Site: ")
43
+ result[:username] = @highline.ask("Username: ")
44
+ result[:password] = @highline.ask("Password: ")
45
+ result
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,4 @@
1
+ :store:
2
+ :type: :filesystem
3
+ :path: /tmp/keyrack.dat
4
+ :key: test/fixtures/id_rsa
@@ -0,0 +1 @@
1
+ foo
@@ -0,0 +1,30 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ Proc-Type: 4,ENCRYPTED
3
+ DEK-Info: AES-128-CBC,F09A7B088DE6A0D4DAED7A4E6EE0E655
4
+
5
+ oIv2SUdgq9Zgg94N57hhjDD6spYkQEejLP8k+hw2Qrfzze4BmnSYCt6RNPcIxnMy
6
+ kCS8abNxjewuzpzMtdzvcEW9RoIYO0VekH4vMc7oZsZ0HHn14avXGk6Z36deYYDm
7
+ pUmKJMYFi0jkeLZThwTZkTvISR3DgHznHGhTkqvCqW8zp6NksYyNxJSnbxAspAGh
8
+ KDL8EsiU7RruirswF3+lHaEftNH/HgYcfVNNeLSbN9GuvTZzwlsFmJCGzigL7LuH
9
+ 29bR+Q5dd+sx+lt7GzPb5tetYdvux25e0CXWbzwmsKNNOsZohJCMhwCkpzlb1Qrl
10
+ ttXQC7lm1VvebvwkC6ozXkdOhdS8nIc9HdukZ4EujgSHfvR8TYLcIH5YvP+3nKCX
11
+ VXQOjgEP9w+xADM9aPJJDHYCkSWYxwKgVYffv7YZwRQGBmShMAAuYyjlAUjzd6cG
12
+ lvZ6v+ScMY2nQtPvLbMfFilevzaOHSFT8WhVSmutHoBCVNvzbWWiKqTtEqw3XjgZ
13
+ TrrPHb738tNTIz0HrztPpgn+CksRrgSYoUZknPfnF40r+UfaouQvvGaKhDi50gh3
14
+ 8CvQQ5PZ7ngKKsp4YdJyK6b8fW4oIBok1vHIPvneRwRGZqtzT0u0jVBaoCs1IrTn
15
+ Lx1r9VKx4nqGm2SSM5SxbTJL+iHa7u7SFgb5zJUUGOO2iMMorpVfV4zpAd6TKsex
16
+ h8cAYBcROS98Ixh/SPl/+IKzyaNEBc+jqban3on9gSmX/pHRRvcAtrKX8194svc6
17
+ ZCSAcaaMk127AmxkFInUdZk9aKKZzjx0sQw6qd4c75Yl2Kwrt09umSOPnv0oRrI6
18
+ p+ZHRc8XRxlBYnz8v11hC7VP0zG94p5mNH2WaAsPuifZaIzTx4+HqhK4tzL6sYuv
19
+ obaPwzyYJvT7fAhAJ/MuC57AAygpTflSTv9cEbO4ufEUaNlvBFroiE5Di+9/sgEn
20
+ OnB33quVBih21d+1NUu2H8bxHQfNPm343o1y/pYvoTCZFlrLnmpnVK7QM+VTB9G4
21
+ X61z7agN5wKm8c/nssLplkvKVeZkuZzHqPDAfTM8OXNsc4DZfxvaEKhLNq9kmhB9
22
+ burDVGTeN/qsWBDJl9flerBmEHdF8yGKppJSygiqRGTstjX6/pUBnUPv9mY6yhu5
23
+ 9sEYfrOT1IuuZVNZygjA2WIhRxmDuSbyiVdlwtSfx0AbqeMVIem5BGnpEbV3RiHD
24
+ DelvO0fz77RF/4+w8Fms/7YO+4fs22AsOzozOSaK1nmMRaOBO+rFQAlfhvOibqn9
25
+ vENbwDbJsdG8lFwV6RGQtz5r8MgOKyx5v3VOqVti2ET8OBUhPPbuj0Cw32F1xMRq
26
+ pKFy5Jygh+x7WpnDqhyRqRCaTgky2cEfmxRE3DQzNvMlZbApKEbtarwFYJpOs4k8
27
+ FL87isDMBxw/KCCe4/EBpsGXcOCge3uNnA198rnpyaNwcmqL5AKHyQH/i3r5SZOT
28
+ sdYvWyugDbjIciQ+OEpqgwIdf0Rh89HWfZG7JHJY3OKifrnHI9srJ2KBsmv8wvQ6
29
+ dY/0GBaam/PX8OKEhWrpojGAPG+4uUVpy4AZ2H0DZC44sxJu+Xw/OcbenVJuUMN2
30
+ -----END RSA PRIVATE KEY-----
@@ -0,0 +1 @@
1
+ ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC5BJJ44xstXL/qsJzIDTr7NTX64g/Nvi2z5fDM05mYQ1+cbAm+s+xI0vBrzY7nU4eVK62h5HOVOh3BB9vGfF3rfycEwj1ZpcDuMAlwQnmnmwLXXwQJV0VQtUgvukBKVX/RwSPJPkGk0nEGvpBmtn8IjtlR+Ido2jeKyKHsUCygvExoVTvJprCGLYxAH7Wo6wtF2idWwmBY6ApFY8cdf0VVBwSsIGjRmnMXf6ggsP/eoYBmR7aD3nGvb4uE1P1XX5XgRAcd7gHbHFe9LVErVoAT734MGMaSQJ6vfBdjBe/BfoAaquGJcr/b+DLc++JpPbaRbDX7IUIlB1vqnkOcLOi5 viking@ip-10-195-17-174
@@ -0,0 +1,35 @@
1
+ require 'tempfile'
2
+ require 'rubygems'
3
+ require 'bundler'
4
+ begin
5
+ Bundler.setup(:default, :development)
6
+ rescue Bundler::BundlerError => e
7
+ $stderr.puts e.message
8
+ $stderr.puts "Run `bundle install` to install missing gems"
9
+ exit e.status_code
10
+ end
11
+ require 'test/unit'
12
+ require 'mocha'
13
+
14
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
15
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
16
+ require 'keyrack'
17
+
18
+ class Test::Unit::TestCase
19
+ def fixture_path(name)
20
+ File.dirname(__FILE__) + '/fixtures/' + name
21
+ end
22
+
23
+ def get_tmpname
24
+ tmpname = Dir::Tmpname.create('keyrack') { }
25
+ @tmpnames ||= []
26
+ @tmpnames << tmpname
27
+ tmpname
28
+ end
29
+
30
+ def teardown
31
+ if @tmpnames
32
+ @tmpnames.each { |t| File.unlink(t) if File.exist?(t) }
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,25 @@
1
+ require 'helper'
2
+
3
+ module Keyrack
4
+ module Store
5
+ class TestFilesystem < Test::Unit::TestCase
6
+ def test_read
7
+ path = fixture_path('foo.txt')
8
+ store = Filesystem.new(:path => path)
9
+ assert_equal File.read(path), store.read
10
+ end
11
+
12
+ def test_write
13
+ path = get_tmpname
14
+ store = Filesystem.new(:path => path)
15
+ store.write("foobar")
16
+ assert_equal "foobar", File.read(path)
17
+ end
18
+
19
+ def test_read_returns_nil_for_non_existant_file
20
+ store = Filesystem.new(:path => 'blargityblargh')
21
+ assert_nil store.read
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,28 @@
1
+ require 'helper'
2
+
3
+ module Keyrack
4
+ module Store
5
+ class TestSSH < Test::Unit::TestCase
6
+ def test_read
7
+ store = SSH.new(:host => 'example.com', :user => 'dude', :path => 'foo.txt')
8
+ Net::SCP.expects(:download!).with("example.com", "dude", "foo.txt").returns("foo")
9
+ assert_equal "foo", store.read
10
+ end
11
+
12
+ def test_write
13
+ store = SSH.new(:host => 'example.com', :user => 'dude', :path => 'foo.txt')
14
+ Net::SCP.expects(:upload!).with do |host, user, local, remote|
15
+ host == 'example.com' && user == 'dude' && local.is_a?(StringIO) &&
16
+ local.read == "foo" && remote == "foo.txt"
17
+ end
18
+ store.write("foo")
19
+ end
20
+
21
+ def test_read_returns_nil_for_non_existant_file
22
+ store = SSH.new(:host => 'example.com', :user => 'dude', :path => 'foo.txt')
23
+ Net::SCP.expects(:download!).with("example.com", "dude", "foo.txt").raises(Net::SCP::Error)
24
+ assert_nil store.read
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,38 @@
1
+ require 'helper'
2
+
3
+ module Keyrack
4
+ class TestDatabase < Test::Unit::TestCase
5
+ def setup
6
+ @path = get_tmpname
7
+ @database = Keyrack::Database.new({
8
+ :store => { :type => :filesystem, :path => @path },
9
+ :key => fixture_path('id_rsa'),
10
+ :password => 'secret'
11
+ })
12
+ @database.add('Twitter', 'username', 'password')
13
+ @database.save
14
+ end
15
+
16
+ def test_encrypts_database
17
+ key = OpenSSL::PKey::RSA.new(File.read(fixture_path('id_rsa')), 'secret')
18
+ encrypted_data = File.read(@path)
19
+ marshalled_data = key.private_decrypt(encrypted_data)
20
+ data = Marshal.load(marshalled_data)
21
+ assert_equal({'Twitter'=>{:username=>'username',:password=>'password'}}, data)
22
+ end
23
+
24
+ def test_reading_existing_database
25
+ database = Keyrack::Database.new({
26
+ :store => { :type => :filesystem, :path => @path },
27
+ :key => fixture_path('id_rsa'),
28
+ :password => 'secret'
29
+ })
30
+ expected = {:username => 'username', :password => 'password'}
31
+ assert_equal(expected, database.get('Twitter'))
32
+ end
33
+
34
+ def test_sites
35
+ assert_equal(%w{Twitter}, @database.sites)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,32 @@
1
+ require 'helper'
2
+
3
+ module Keyrack
4
+ class TestRunner < Test::Unit::TestCase
5
+ def test_console
6
+ config = {
7
+ :store => { :type => :filesystem, :path => 'foobar' },
8
+ :key => fixture_path('id_rsa')
9
+ }
10
+ config_path = get_tmpname
11
+ File.open(config_path, 'w') { |f| f.print(config.to_yaml) }
12
+
13
+ console = mock('console')
14
+ UI::Console.expects(:new).returns(console)
15
+ database = mock('database')
16
+
17
+ seq = sequence('ui sequence')
18
+ console.expects(:get_password).returns('secret').in_sequence(seq)
19
+ Database.expects(:new).with(config.merge(:password => 'secret')).returns(database).in_sequence(seq)
20
+ console.expects(:database=).with(database).in_sequence(seq)
21
+ console.expects(:menu).returns(:new).in_sequence(seq)
22
+ console.expects(:get_new_entry).returns({:site => "Foo", :username => "bar", :password => "baz"}).in_sequence(seq)
23
+ database.expects(:add).with("Foo", "bar", "baz")
24
+ console.expects(:menu).returns(nil).in_sequence(seq)
25
+ console.expects(:menu).returns(:save).in_sequence(seq)
26
+ database.expects(:save).in_sequence(seq)
27
+ console.expects(:menu).returns(:quit).in_sequence(seq)
28
+
29
+ runner = Runner.new(["-c", config_path])
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,13 @@
1
+ require 'helper'
2
+
3
+ module Keyrack
4
+ class TestStore < Test::Unit::TestCase
5
+ def test_get_filesystem
6
+ assert_equal Store::Filesystem, Store[:filesystem]
7
+ end
8
+
9
+ def test_get_ssh
10
+ assert_equal Store::SSH, Store[:ssh]
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,110 @@
1
+ require 'helper'
2
+
3
+ module Keyrack
4
+ module UI
5
+ class TestConsole < Test::Unit::TestCase
6
+ def setup
7
+ @path = get_tmpname
8
+ @database = Database.new({
9
+ :store => { :type => :filesystem, :path => @path },
10
+ :key => fixture_path('id_rsa'),
11
+ :password => 'secret'
12
+ })
13
+ @database.add('Twitter', 'username', 'password')
14
+ @database.save
15
+ end
16
+
17
+ def test_select_entry_from_menu
18
+ highline = mock('highline')
19
+ HighLine.expects(:new).returns(highline)
20
+ console = Console.new
21
+ console.database = @database
22
+
23
+ highline.expects(:say).with(" 1. Twitter [username]")
24
+ highline.expects(:say).with(" n. Add new")
25
+ highline.expects(:say).with(" s. Save")
26
+ highline.expects(:say).with(" q. Quit")
27
+
28
+ question = mock('question')
29
+ question.expects(:in=).with(%w{n s q 1})
30
+ highline.expects(:ask).yields(question).returns('1')
31
+ Clipboard.expects(:copy).with('password')
32
+ highline.expects(:say).with("The password has been copied to your clipboard.")
33
+ assert_nil console.menu
34
+ end
35
+
36
+ def test_select_new_from_menu
37
+ highline = mock('highline')
38
+ HighLine.expects(:new).returns(highline)
39
+ console = Console.new
40
+ console.database = @database
41
+
42
+ highline.expects(:say).with(" 1. Twitter [username]")
43
+ highline.expects(:say).with(" n. Add new")
44
+ highline.expects(:say).with(" s. Save")
45
+ highline.expects(:say).with(" q. Quit")
46
+
47
+ question = mock('question')
48
+ question.expects(:in=).with(%w{n s q 1})
49
+ highline.expects(:ask).yields(question).returns('n')
50
+ assert_equal :new, console.menu
51
+ end
52
+
53
+ def test_select_quit_from_menu
54
+ highline = mock('highline')
55
+ HighLine.expects(:new).returns(highline)
56
+ console = Console.new
57
+ console.database = @database
58
+
59
+ highline.expects(:say).with(" 1. Twitter [username]")
60
+ highline.expects(:say).with(" n. Add new")
61
+ highline.expects(:say).with(" s. Save")
62
+ highline.expects(:say).with(" q. Quit")
63
+
64
+ question = mock('question')
65
+ question.expects(:in=).with(%w{n s q 1})
66
+ highline.expects(:ask).yields(question).returns('q')
67
+ assert_equal :quit, console.menu
68
+ end
69
+
70
+ def test_select_save_from_menu
71
+ highline = mock('highline')
72
+ HighLine.expects(:new).returns(highline)
73
+ console = Console.new
74
+ console.database = @database
75
+
76
+ highline.expects(:say).with(" 1. Twitter [username]")
77
+ highline.expects(:say).with(" n. Add new")
78
+ highline.expects(:say).with(" s. Save")
79
+ highline.expects(:say).with(" q. Quit")
80
+
81
+ question = mock('question')
82
+ question.expects(:in=).with(%w{n s q 1})
83
+ highline.expects(:ask).yields(question).returns('s')
84
+ assert_equal :save, console.menu
85
+ end
86
+
87
+ def test_get_password
88
+ highline = mock('highline')
89
+ HighLine.expects(:new).returns(highline)
90
+ console = Console.new
91
+
92
+ question = mock('question')
93
+ question.expects(:echo=).with(false)
94
+ highline.expects(:ask).with("Keyrack password: ").yields(question).returns("foobar")
95
+ assert_equal "foobar", console.get_password
96
+ end
97
+
98
+ def test_get_new_entry
99
+ highline = mock('highline')
100
+ HighLine.expects(:new).returns(highline)
101
+ console = Console.new
102
+
103
+ highline.expects(:ask).with("Site: ").returns("Foo")
104
+ highline.expects(:ask).with("Username: ").returns("bar")
105
+ highline.expects(:ask).with("Password: ").returns("baz")
106
+ assert_equal({:site => "Foo", :username => "bar", :password => "baz"}, console.get_new_entry)
107
+ end
108
+ end
109
+ end
110
+ end
metadata ADDED
@@ -0,0 +1,303 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: keyrack
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Jeremy Stephens
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-12-12 00:00:00 -06:00
18
+ default_executable: keyrack
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: net-scp
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :runtime
31
+ prerelease: false
32
+ version_requirements: *id001
33
+ - !ruby/object:Gem::Dependency
34
+ name: highline
35
+ requirement: &id002 !ruby/object:Gem::Requirement
36
+ none: false
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ segments:
41
+ - 0
42
+ version: "0"
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: *id002
46
+ - !ruby/object:Gem::Dependency
47
+ name: clipboard
48
+ requirement: &id003 !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ segments:
54
+ - 0
55
+ version: "0"
56
+ type: :runtime
57
+ prerelease: false
58
+ version_requirements: *id003
59
+ - !ruby/object:Gem::Dependency
60
+ name: bundler
61
+ requirement: &id004 !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ~>
65
+ - !ruby/object:Gem::Version
66
+ segments:
67
+ - 1
68
+ - 0
69
+ - 0
70
+ version: 1.0.0
71
+ type: :development
72
+ prerelease: false
73
+ version_requirements: *id004
74
+ - !ruby/object:Gem::Dependency
75
+ name: jeweler
76
+ requirement: &id005 !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ~>
80
+ - !ruby/object:Gem::Version
81
+ segments:
82
+ - 1
83
+ - 5
84
+ - 1
85
+ version: 1.5.1
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: *id005
89
+ - !ruby/object:Gem::Dependency
90
+ name: rcov
91
+ requirement: &id006 !ruby/object:Gem::Requirement
92
+ none: false
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ segments:
97
+ - 0
98
+ version: "0"
99
+ type: :development
100
+ prerelease: false
101
+ version_requirements: *id006
102
+ - !ruby/object:Gem::Dependency
103
+ name: mocha
104
+ requirement: &id007 !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ segments:
110
+ - 0
111
+ version: "0"
112
+ type: :development
113
+ prerelease: false
114
+ version_requirements: *id007
115
+ - !ruby/object:Gem::Dependency
116
+ name: net-scp
117
+ requirement: &id008 !ruby/object:Gem::Requirement
118
+ none: false
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ segments:
123
+ - 0
124
+ version: "0"
125
+ type: :runtime
126
+ prerelease: false
127
+ version_requirements: *id008
128
+ - !ruby/object:Gem::Dependency
129
+ name: highline
130
+ requirement: &id009 !ruby/object:Gem::Requirement
131
+ none: false
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ segments:
136
+ - 0
137
+ version: "0"
138
+ type: :runtime
139
+ prerelease: false
140
+ version_requirements: *id009
141
+ - !ruby/object:Gem::Dependency
142
+ name: clipboard
143
+ requirement: &id010 !ruby/object:Gem::Requirement
144
+ none: false
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ segments:
149
+ - 0
150
+ version: "0"
151
+ type: :runtime
152
+ prerelease: false
153
+ version_requirements: *id010
154
+ - !ruby/object:Gem::Dependency
155
+ name: rspec
156
+ requirement: &id011 !ruby/object:Gem::Requirement
157
+ none: false
158
+ requirements:
159
+ - - ">"
160
+ - !ruby/object:Gem::Version
161
+ segments:
162
+ - 1
163
+ - 2
164
+ - 3
165
+ version: 1.2.3
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: *id011
169
+ - !ruby/object:Gem::Dependency
170
+ name: bundler
171
+ requirement: &id012 !ruby/object:Gem::Requirement
172
+ none: false
173
+ requirements:
174
+ - - ~>
175
+ - !ruby/object:Gem::Version
176
+ segments:
177
+ - 1
178
+ - 0
179
+ - 0
180
+ version: 1.0.0
181
+ type: :development
182
+ prerelease: false
183
+ version_requirements: *id012
184
+ - !ruby/object:Gem::Dependency
185
+ name: jeweler
186
+ requirement: &id013 !ruby/object:Gem::Requirement
187
+ none: false
188
+ requirements:
189
+ - - ~>
190
+ - !ruby/object:Gem::Version
191
+ segments:
192
+ - 1
193
+ - 5
194
+ - 1
195
+ version: 1.5.1
196
+ type: :development
197
+ prerelease: false
198
+ version_requirements: *id013
199
+ - !ruby/object:Gem::Dependency
200
+ name: rcov
201
+ requirement: &id014 !ruby/object:Gem::Requirement
202
+ none: false
203
+ requirements:
204
+ - - ">="
205
+ - !ruby/object:Gem::Version
206
+ segments:
207
+ - 0
208
+ version: "0"
209
+ type: :development
210
+ prerelease: false
211
+ version_requirements: *id014
212
+ - !ruby/object:Gem::Dependency
213
+ name: mocha
214
+ requirement: &id015 !ruby/object:Gem::Requirement
215
+ none: false
216
+ requirements:
217
+ - - ">="
218
+ - !ruby/object:Gem::Version
219
+ segments:
220
+ - 0
221
+ version: "0"
222
+ type: :development
223
+ prerelease: false
224
+ version_requirements: *id015
225
+ description: Simple password manager with local/remote database storage and RSA encryption.
226
+ email: viking@pillageandplunder.net
227
+ executables:
228
+ - keyrack
229
+ extensions: []
230
+
231
+ extra_rdoc_files:
232
+ - LICENSE.txt
233
+ - README.rdoc
234
+ files:
235
+ - .document
236
+ - .rvmrc
237
+ - Gemfile
238
+ - Gemfile.lock
239
+ - LICENSE.txt
240
+ - README.rdoc
241
+ - Rakefile
242
+ - VERSION
243
+ - bin/keyrack
244
+ - lib/keyrack.rb
245
+ - lib/keyrack/database.rb
246
+ - lib/keyrack/runner.rb
247
+ - lib/keyrack/store.rb
248
+ - lib/keyrack/store/filesystem.rb
249
+ - lib/keyrack/store/ssh.rb
250
+ - lib/keyrack/ui.rb
251
+ - lib/keyrack/ui/console.rb
252
+ - test/fixtures/config.yml
253
+ - test/fixtures/foo.txt
254
+ - test/fixtures/id_rsa
255
+ - test/fixtures/id_rsa.pub
256
+ - test/helper.rb
257
+ - test/keyrack/store/test_filesystem.rb
258
+ - test/keyrack/store/test_ssh.rb
259
+ - test/keyrack/test_database.rb
260
+ - test/keyrack/test_runner.rb
261
+ - test/keyrack/test_store.rb
262
+ - test/keyrack/ui/test_console.rb
263
+ has_rdoc: true
264
+ homepage: http://github.com/viking/keyrack
265
+ licenses:
266
+ - MIT
267
+ post_install_message:
268
+ rdoc_options: []
269
+
270
+ require_paths:
271
+ - lib
272
+ required_ruby_version: !ruby/object:Gem::Requirement
273
+ none: false
274
+ requirements:
275
+ - - ">="
276
+ - !ruby/object:Gem::Version
277
+ hash: -93442259
278
+ segments:
279
+ - 0
280
+ version: "0"
281
+ required_rubygems_version: !ruby/object:Gem::Requirement
282
+ none: false
283
+ requirements:
284
+ - - ">="
285
+ - !ruby/object:Gem::Version
286
+ segments:
287
+ - 0
288
+ version: "0"
289
+ requirements: []
290
+
291
+ rubyforge_project:
292
+ rubygems_version: 1.3.7
293
+ signing_key:
294
+ specification_version: 3
295
+ summary: Simple password manager
296
+ test_files:
297
+ - test/helper.rb
298
+ - test/keyrack/store/test_filesystem.rb
299
+ - test/keyrack/store/test_ssh.rb
300
+ - test/keyrack/test_database.rb
301
+ - test/keyrack/test_runner.rb
302
+ - test/keyrack/test_store.rb
303
+ - test/keyrack/ui/test_console.rb