clandestine 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+ require 'clandestine/safe_authentication'
3
+
4
+ module Clandestine
5
+ describe SafeAuthentication do
6
+ let(:safe) { double('safe') }
7
+
8
+ describe '#authenticate' do
9
+ context 'correct password' do
10
+ it 'yields safe' do
11
+ SafeAuthentication.stub(:authenticated?) { true }
12
+ SafeAuthentication.authenticate(safe, 'password').should eq true
13
+ end
14
+ end
15
+
16
+ context 'incorrect password' do
17
+ it 'raises error' do
18
+ SafeAuthentication.stub(:authenticated?) { false }
19
+ error = $stderr
20
+ $stderr = StringIO.new
21
+ expect {
22
+ SafeAuthentication.authenticate(safe, 'password')
23
+ }.to raise_error SystemExit
24
+ $stderr.string.should eq "Invalid password!\n"
25
+ $stderr = error
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,11 +1,3 @@
1
- # This file was generated by the `rspec --init` command. Conventionally, all
2
- # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
- # Require this file using `require "spec_helper.rb"` to ensure that it is only
4
- # loaded once.
5
- #
6
- # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
-
8
- require 'clandestine'
9
1
  require 'rspec'
10
2
 
11
3
  RSpec.configure do |config|
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: clandestine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alec Tower
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-04-13 00:00:00.000000000 Z
11
+ date: 2014-02-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: highline
@@ -24,20 +24,48 @@ dependencies:
24
24
  - - '>='
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: json
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bcrypt-ruby
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
27
55
  - !ruby/object:Gem::Dependency
28
56
  name: rspec
29
57
  requirement: !ruby/object:Gem::Requirement
30
58
  requirements:
31
59
  - - ~>
32
60
  - !ruby/object:Gem::Version
33
- version: '2.8'
61
+ version: '2'
34
62
  type: :development
35
63
  prerelease: false
36
64
  version_requirements: !ruby/object:Gem::Requirement
37
65
  requirements:
38
66
  - - ~>
39
67
  - !ruby/object:Gem::Version
40
- version: '2.8'
68
+ version: '2'
41
69
  description: A ruby command line tool for storing encrypted passwords on *nix systems
42
70
  email:
43
71
  - alectower@gmail.com
@@ -55,15 +83,25 @@ files:
55
83
  - bin/clandestine
56
84
  - clandestine.gemspec
57
85
  - lib/clandestine.rb
58
- - lib/clandestine/config.rb
86
+ - lib/clandestine/clandestine_error.rb
87
+ - lib/clandestine/command_line_options.rb
88
+ - lib/clandestine/command_line_runner.rb
89
+ - lib/clandestine/commands.rb
90
+ - lib/clandestine/commands/add.rb
91
+ - lib/clandestine/commands/delete.rb
92
+ - lib/clandestine/commands/get.rb
93
+ - lib/clandestine/commands/remove_safe.rb
94
+ - lib/clandestine/commands/update.rb
59
95
  - lib/clandestine/crypt.rb
60
- - lib/clandestine/guard.rb
96
+ - lib/clandestine/io.rb
97
+ - lib/clandestine/password_generator.rb
61
98
  - lib/clandestine/safe.rb
99
+ - lib/clandestine/safe_authentication.rb
100
+ - lib/clandestine/safe_location.rb
62
101
  - lib/clandestine/version.rb
63
- - spec/config_spec.rb
102
+ - spec/commands_spec.rb
64
103
  - spec/crypt_spec.rb
65
- - spec/guard_spec.rb
66
- - spec/safe_spec.rb
104
+ - spec/safe_authentication_spec.rb
67
105
  - spec/spec_helper.rb
68
106
  homepage: https://github.com/uniosx/clandestine
69
107
  licenses: []
@@ -90,8 +128,8 @@ specification_version: 4
90
128
  summary: A ruby command line tool that uses AES-256-CBC encryption to store passwords.
91
129
  It provides easy storage and retrieval on *nix systems
92
130
  test_files:
93
- - spec/config_spec.rb
131
+ - spec/commands_spec.rb
94
132
  - spec/crypt_spec.rb
95
- - spec/guard_spec.rb
96
- - spec/safe_spec.rb
133
+ - spec/safe_authentication_spec.rb
97
134
  - spec/spec_helper.rb
135
+ has_rdoc:
@@ -1,25 +0,0 @@
1
- module Clandestine
2
- module Config
3
- CONFIG_PATH = "#{ENV['HOME']}/.pswds"
4
-
5
- def change_safe_location(new_location)
6
- if new_location
7
- FileUtils.mkpath File.dirname new_location unless Dir.exists? File.dirname new_location
8
- old_file = File.readlink CONFIG_PATH if File.symlink? CONFIG_PATH
9
- if IO.readlines(CONFIG_PATH).empty?
10
- File.new new_location, 'w' unless File.exists? new_location
11
- else
12
- FileUtils.copy_file(CONFIG_PATH, new_location, true) unless File.exists? new_location
13
- end
14
- FileUtils.ln_s(new_location, CONFIG_PATH, :force => true)
15
- FileUtils.rm_f old_file if old_file
16
- else
17
- if File.symlink? CONFIG_PATH
18
- puts "Current safe location is #{File.readlink CONFIG_PATH}"
19
- else
20
- puts "Current safe location is #{CONFIG_PATH}"
21
- end
22
- end
23
- end
24
- end
25
- end
@@ -1,217 +0,0 @@
1
- require 'highline/import'
2
-
3
- module Clandestine
4
- # Handles user requests and interacts
5
- # with the Safe class.
6
- #
7
- # Main class to interface with user.
8
- # The Guard protects the safe by
9
- # authenticating the user, and making sure
10
- # data is in the correct format to be
11
- # held in the safe.
12
- class Guard
13
- include Clandestine::Config
14
-
15
- # Create new guard object with specified safe.
16
- # Default safe location is ENV['HOME']/.pswds
17
- def initialize(safe = Safe.new)
18
- @safe = safe
19
- end
20
-
21
- # Main method that validates arguments
22
- # and processes input
23
- def on_the_job
24
- validate_args ? process_input : print_options
25
- end
26
-
27
- # Checks for the correct switches to be
28
- # supplied by user
29
- def validate_args
30
- if ARGV.size > 0 && ARGV.size < 3
31
- @command = ARGV[0]
32
- @arg = ARGV[1]
33
- (@command =~ /[(-c)]/ && !@arg.nil?) || (@command =~ /[(-a)(-g)(-l)]/ && @arg.nil?)
34
- else
35
- false
36
- end
37
- end
38
-
39
- # Command line options
40
- def print_options
41
- puts "Clandestine v#{Clandestine::VERSION}\
42
- \nOptions: \
43
- \n\t-a <key> :: add password related to <key> \
44
- \n\t-g <key> :: get password related to <key> \
45
- \n\t-g :: get all keys \
46
- \n\t-d <key> :: delete password related to <key> \
47
- \n\t-d :: delete all passwords \
48
- \n\t-l :: print current location of safe \
49
- \n\t-l <path> :: move safe location to <path> \
50
- \n\t-c :: change password to safe \
51
- \n\t-r :: remove safe completely
52
- \n\n\tPasswords will be copied to the clipboard for 5 seconds \
53
- \n\n"
54
- end
55
-
56
- # Case statement for switches
57
- # Main logic method
58
- def process_input
59
- case @command
60
- when "-a"
61
- add_value
62
- when "-g"
63
- get_value
64
- when "-d"
65
- delete_value
66
- when "-l"
67
- change_safe_location(@arg)
68
- when "-c"
69
- change_safe_password
70
- when "-r"
71
- remove_safe
72
- else
73
- print_options
74
- end
75
- end
76
-
77
- # Adds a new value to the safe
78
- # and binds it to the key provided
79
- # by the user.
80
- def add_value
81
- if @arg
82
- safe_password = ask("Enter the password for the safe") { |q| q.echo = "*" }
83
- key_password = ask("Enter the password for #{@arg}") { |q| q.echo = "*" }
84
- add_data "#{@arg}:#{key_password}", safe_password
85
- else
86
- puts "A key is needed in order to store a password"
87
- end
88
- end
89
-
90
- # Retrieves value from safe
91
- # that is related to the key
92
- # entered by the user
93
- def get_value
94
- (puts "The safe is empty"; return) if @safe.empty?
95
- safe_password = ask("Enter the password for the safe") { |q| q.echo = "*" }
96
- case @arg
97
- when /^\S+$/
98
- data = retrieve_data(safe_password)
99
- value = data[@arg] if data
100
- if value
101
- IO.popen('pbcopy', 'w') {|clipboard| clipboard.print "#{value}"}
102
- say("Password on clipboard countdown: ")
103
- 10.downto(1) do |num|
104
- sleep(1)
105
- num == 1 ? say("#{num}") : say("#{num} ")
106
- end
107
- IO.popen('pbcopy', 'w') {|clipboard| clipboard.print ""}
108
- else
109
- puts "No value found for #{@arg}"
110
- end
111
- else
112
- data = retrieve_data safe_password
113
- if data
114
- data.each_key { |key| puts key }
115
- else
116
- puts "Safe is empty"
117
- end
118
- end
119
- end
120
-
121
- # Removes key and value from safe.
122
- def delete_value
123
- (puts " The safe is empty"; return) if @safe.empty?
124
- case @arg
125
- when /^\S+$/
126
- safe_password = ask("Enter the password for the safe") { |q| q.echo = "*" }
127
- remove_key_value safe_password
128
- when nil
129
- empty_safe
130
- end
131
- end
132
-
133
- # First retrieves old data to see if
134
- # the key already exists in the safe,
135
- # adds it if not.
136
- def add_data(key_value, safe_password)
137
- old_data = retrieve_data safe_password
138
- if old_data && old_data[@arg]
139
- puts "#@arg already exists"
140
- return
141
- end
142
- merge_data(old_data, key_value, safe_password)
143
- puts "Successfully added key value pair"
144
- end
145
-
146
- # Merges new key value pair with old data
147
- def merge_data(old_data, key_value, safe_password)
148
- new_data = ''
149
- old_data.each { |key, value| new_data << "#{key}:#{value}\n" } if old_data
150
- new_data << "#{key_value}\n" if key_value
151
- @safe.lock(new_data, safe_password)
152
- end
153
-
154
- # Retrieves data from safe and returns
155
- # it in a key value hash
156
- def retrieve_data(safe_password)
157
- begin
158
- return if !data = @safe.unlock(safe_password)
159
- data.split("\n").map {|l| l.split(":")}.map {|k, v| Hash[k => v]}.reduce({}) {|h, i| h.merge i}
160
- rescue OpenSSL::Cipher::CipherError
161
- puts "You entered an incorrect password"
162
- exit(0)
163
- end
164
- end
165
-
166
- # Prompts user for delete confirmation
167
- # and empties safe if user entered "Y" or "y".
168
- def empty_safe
169
- (puts "The safe is already empty"; return) if @safe.empty?
170
- puts "Are you sure? (y|Y)"
171
- if STDIN.gets =~ /^[yY]{1}$/
172
- @safe.empty_safe
173
- puts "Safe was emptied!"
174
- else
175
- puts "Not empyting safe"
176
- end
177
- end
178
-
179
- # Attempts to remove key and value
180
- # from safe if it exists.
181
- # If new_data is empty, it means that
182
- # there is only one key value in the safe,
183
- # So the safe needs to be emptied rather
184
- # re-storing the new information.
185
- def remove_key_value(safe_password)
186
- new_data = ''
187
- old_data = retrieve_data safe_password
188
- if old_data
189
- puts "Deleting #@arg and related password"
190
- old_data.each { |key, value| new_data << "#{key}:#{value}\n" if key !~ /^#@arg$/ }
191
- if new_data.empty?
192
- @safe.empty_safe
193
- else
194
- @safe.lock(new_data, safe_password)
195
- end
196
- else
197
- puts "Safe is empty"
198
- end
199
- end
200
-
201
- # Attempts to change the password for the safe.
202
- # If the safe is empty, there is no password to be changed.
203
- def change_safe_password
204
- (puts "The safe is empty, no password is currently active"; return) if @safe.empty?
205
- old_safe_password = ask("Enter the old password for the safe") { |q| q.echo = "*" }
206
- new_safe_password = ask("Enter the new password for the safe") { |q| q.echo = "*" }
207
- data = retrieve_data old_safe_password
208
- puts "Successfully changed password" if (merge_data(data, nil, new_safe_password) if data)
209
- end
210
-
211
- # Removes safe from file system
212
- def remove_safe
213
- @safe.self_destruct
214
- puts "The safe has been removed!"
215
- end
216
- end
217
- end
data/spec/config_spec.rb DELETED
@@ -1,47 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Clandestine::Config do
4
- include Clandestine::Config
5
- before :all do
6
- Clandestine::Config::CONFIG_PATH = "#{ENV['HOME']}/.tstpswds"
7
- File.new Clandestine::Config::CONFIG_PATH, 'w'
8
- self.instance_eval do
9
- @new_location = "#{ENV['HOME']}/Dropbox/.tstpswds"
10
- @newer_location = "#{ENV['HOME']}/Dropbox/.tst_pswds"
11
- end
12
- end
13
- after :each do
14
- File.delete Clandestine::Config::CONFIG_PATH if File.exists? Clandestine::Config::CONFIG_PATH
15
- File.delete @new_location if File.exists? @new_location
16
- File.delete @newer_location if File.exists? @newer_location
17
- end
18
- it "should change the location of the password file" do
19
- change_safe_location @new_location
20
- File.exists?(@new_location).should eql true
21
- File.symlink?(Clandestine::Config::CONFIG_PATH).should eql true
22
- end
23
- it "should move default safe location contents to a new safe" do
24
- File.open(Clandestine::Config::CONFIG_PATH, 'a') {|f| f << "stuff"}
25
- change_safe_location @new_location
26
- IO.readlines(@new_location).should eql ["stuff"]
27
- IO.readlines(Clandestine::Config::CONFIG_PATH).should eql ["stuff"]
28
- end
29
- it "should move sym linked safe location contents to a new safe" do
30
- File.open(@new_location, 'a') {|f| f << "stuff"}
31
- FileUtils.ln_s @new_location, Clandestine::Config::CONFIG_PATH, :force => true
32
- change_safe_location @newer_location
33
- IO.readlines(Clandestine::Config::CONFIG_PATH).should eql ["stuff"]
34
- IO.readlines(@newer_location).should eql ["stuff"]
35
- end
36
- it "should delete old safe when moving to a new location" do
37
- File.open(Clandestine::Config::CONFIG_PATH, 'a') {|f| f << "stuff"}
38
- change_safe_location @new_location
39
- change_safe_location @newer_location
40
- IO.readlines(@newer_location).should eql ["stuff"]
41
- File.exists?(@new_location).should eql false
42
- end
43
- it "should print the current safe location if no argument is supplied" do
44
- $stdout.should_receive(:puts).with("Current safe location is #{Clandestine::Config::CONFIG_PATH}")
45
- change_safe_location nil
46
- end
47
- end
data/spec/guard_spec.rb DELETED
@@ -1,127 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Clandestine::Guard do
4
- before :each do
5
- Clandestine::Guard.instance_eval do
6
- attr_accessor :safe, :command, :arg
7
- end
8
- @guard = Clandestine::Guard.new mock("safe")
9
- ARGV.clear
10
- end
11
- it "should validate 0 args" do
12
- @guard.validate_args.should eql false
13
- end
14
- it "should validate 1 arg" do
15
- ARGV << "-g"
16
- @guard.validate_args.should eql true
17
- end
18
- it "should validate > 1 and < 3 args" do
19
- ARGV << "-g" << "keys"
20
- @guard.validate_args.should eql true
21
- end
22
- it "should validate >= 3 args" do
23
- ARGV << "-g" << "all" << "too many"
24
- @guard.validate_args.should eql false
25
- end
26
- it "should process input after validating args" do
27
- @guard.stub(:validate_args).and_return true
28
- @guard.should_receive(:validate_args)
29
- @guard.should_receive(:process_input)
30
- @guard.on_the_job
31
- end
32
- it "should print options if no args are provided" do
33
- @guard.stub(:validate_args).and_return false
34
- @guard.should_receive(:print_options)
35
- @guard.on_the_job
36
- end
37
- it "should process the -a switch to add a key and value" do
38
- @guard.stub(:ask).and_return("safepassword", "gmailpassword")
39
- @guard.stub(:add_data).with("gmail:gmailpassword", "safepassword").and_return "Successfully added key value pair"
40
- @guard.command = "-a"
41
- @guard.arg = "gmail"
42
- @guard.process_input.should eql "Successfully added key value pair"
43
- end
44
- it "should process the -g switch with no argument" do
45
- @guard.stub(:ask).and_return "safepassword"
46
- @guard.stub(:retrieve_data).with("safepassword").and_return nil
47
- @guard.safe.should_receive(:empty?).and_return true
48
- $stdout.should_receive(:puts).with("The safe is empty")
49
- @guard.command = "-g"
50
- @guard.process_input
51
- end
52
- it "should process the -g switch with an argument" do
53
- @guard.safe.stub(:empty?).and_return false
54
- @guard.stub(:ask).and_return "safepassword"
55
- @guard.stub(:retrieve_data).with("safepassword").and_return Hash["gmail" => "gmailpassword"]
56
- IO.should_receive(:popen).with('pbcopy', 'w')
57
- @guard.should_receive(:sleep).with(1).exactly(10).times
58
- IO.should_receive(:popen).with('pbcopy', 'w')
59
- @guard.command = "-g"
60
- @guard.arg = "gmail"
61
- @guard.process_input
62
- end
63
- it "should process the -d switch with an argument" do
64
- @guard.safe.stub(:empty?).and_return false
65
- @guard.stub(:remove_key_value).with("safepassword")
66
- @guard.stub(:ask).and_return "safepassword"
67
- @guard.should_receive(:ask).with("Enter the password for the safe")
68
- @guard.should_receive(:remove_key_value).with("safepassword")
69
- @guard.command = "-d"
70
- @guard.arg = "gmail"
71
- @guard.process_input
72
- end
73
- it "should process the -d switch with no argument" do
74
- @guard.safe.stub(:empty?).and_return false
75
- @guard.should_receive(:empty_safe)
76
- @guard.command = "-d"
77
- @guard.arg = nil
78
- @guard.process_input
79
- end
80
- it "should process the -l switch with a path as an argument" do
81
- @guard.command = "-l"
82
- @guard.arg = "~/.new_safe"
83
- @guard.should_receive(:change_safe_location).with("~/.new_safe")
84
- @guard.process_input
85
- end
86
- it "should process the -c switch" do
87
- @guard.command = "-c"
88
- @guard.should_receive(:change_safe_password)
89
- @guard.process_input
90
- end
91
- it "should process the -r switch" do
92
- @guard.command = "-r"
93
- @guard.should_receive(:remove_safe)
94
- @guard.process_input
95
- end
96
- it "should add data to safe" do
97
- @guard.stub(:retrieve_data).with("fakepassword").and_return nil
98
- @guard.safe.stub(:lock).with("gmail:gmailpassword\n", "fakepassword")
99
- $stdout.should_receive(:puts).with("Successfully added key value pair")
100
- @guard.add_data "gmail:gmailpassword", "fakepassword"
101
- end
102
- it "should retreive data froms safe" do
103
- @guard.safe.stub(:unlock).with("safepassword").and_return("gmail:gmailpassword")
104
- @guard.retrieve_data("safepassword").should eql Hash["gmail" => "gmailpassword"]
105
- end
106
- it "should empty the safe" do
107
- STDIN.stub(:gets).and_return("Y")
108
- @guard.safe.stub(:empty?).and_return false
109
- $stdout.should_receive(:puts).with("Are you sure? (y|Y)")
110
- @guard.safe.should_receive(:empty_safe)
111
- $stdout.should_receive(:puts).with("Safe was emptied!")
112
- @guard.empty_safe
113
- end
114
- it "should remove key value from safe" do
115
- @guard.stub(:retrieve_data).with("safepassword").and_return Hash["gmail" => "gmailpassword", "facebook" => "facebookpassword"]
116
- $stdout.should_receive(:puts).with("Deleting gmail and related password")
117
- @guard.safe.should_receive(:lock).with("facebook:facebookpassword\n", "safepassword")
118
- @guard.arg = "gmail"
119
- @guard.remove_key_value "safepassword"
120
- end
121
- it "should remove the safe" do
122
- @guard.safe.stub(:self_destruct)
123
- @guard.safe.should_receive(:self_destruct)
124
- $stdout.should_receive(:puts).with("The safe has been removed!")
125
- @guard.remove_safe
126
- end
127
- end