clandestine 0.0.1 → 0.1.0

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.
@@ -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