dotgpg 0.3

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/lib/dotgpg/key.rb ADDED
@@ -0,0 +1,105 @@
1
+ class Dotgpg
2
+ class Key
3
+
4
+ def self.read(file)
5
+ GPGME::Key.import(file).imports.map do |import|
6
+ GPGME::Key.find(:public, import.fingerprint)
7
+ end.flatten.first
8
+ end
9
+
10
+ def self.secret_key(email=nil, force_new=nil)
11
+ new.secret_key(email, force_new)
12
+ end
13
+
14
+ def secret_key(email=nil, force_new=nil)
15
+ existing = existing_key(email)
16
+ if existing && !force_new
17
+ existing
18
+ else
19
+ create_new_key email
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def existing_key(email=nil)
26
+ existing_private_keys.detect do |k|
27
+ email.nil? || k.email == email
28
+ end
29
+ end
30
+
31
+ def create_new_key(email=nil)
32
+ name = guess_name
33
+ email ||= guess_email
34
+
35
+ if email
36
+ puts "Creating a new GPG key: #{name} <#{email}>"
37
+ passphrase = get_passphrase
38
+ else
39
+ puts "Creating a new GPG key for #{name}"
40
+ email = get_email
41
+ passphrase = get_passphrase
42
+ end
43
+
44
+ puts "Generating large prime numbers, please wait..."
45
+ ctx = GPGME::Ctx.new
46
+ ctx.genkey(<<EOF, nil, nil)
47
+ <GnupgKeyParms format="internal">
48
+ Key-Type: RSA
49
+ Key-Length: 2048
50
+ Subkey-Type: RSA
51
+ Subkey-Length: 2048
52
+ Name-Real: #{name}
53
+ Name-Comment: dotgpg
54
+ Name-Email: #{email}
55
+ Expire-Date: 0
56
+ Passphrase: #{passphrase}
57
+ </GnupgKeyParms>
58
+ EOF
59
+
60
+ # return the most recently created key (race!)
61
+ GPGME::Key.find(:secret).sort_by{ |key|
62
+ key.primary_subkey.timestamp
63
+ }.last
64
+ end
65
+
66
+ def guess_name
67
+ name = `git config user.name 2>/dev/null`.strip
68
+ name = `whoami`.strip if name == ""
69
+ name
70
+ end
71
+
72
+ def guess_email
73
+ email = `git config user.email 2>/dev/null`.strip
74
+ email if email != ""
75
+ end
76
+
77
+ def get_email
78
+ email = ""
79
+ until email =~ /@/
80
+ email = Dotgpg.read_input "Email address: "
81
+ end
82
+ email
83
+ end
84
+
85
+ def get_passphrase
86
+ passphrase = confirmation = nil
87
+ until passphrase && passphrase == confirmation
88
+ times = 0
89
+ until passphrase && passphrase.length >= 10
90
+ times += 1
91
+ $stderr.puts "Passphrases should be secure! (>=10 chars)" if times >= 2
92
+ passphrase = Dotgpg.read_passphrase("Passphrase: ")
93
+ end
94
+ until confirmation && confirmation.length >= 10
95
+ confirmation = Dotgpg.read_passphrase("Passphrase confirmation: ")
96
+ end
97
+ end
98
+ passphrase
99
+ end
100
+
101
+ def existing_private_keys
102
+ GPGME::Key.find(:secret)
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,20 @@
1
+ This directory contains GPG-encrypted files managed by `dotgpg`.
2
+
3
+ Getting started
4
+ ---------------
5
+
6
+ To read files in this directory, send the output of running `dotgpg key` to someone
7
+ who has access already. They will be able to run `dotgpg add` on your behalf.
8
+
9
+ Usage
10
+ -----
11
+
12
+ You can edit any file with `dotgpg edit FILE`, and read any file with `dotgpg cat FILE`.
13
+
14
+ The edit command looks at the value of `$EDITOR`, the internet will have a tutorial on
15
+ how to set this up with your favourite editor.
16
+
17
+ Details
18
+ -------
19
+
20
+ For more information, please see `dotgpg --help`, or the [README](https://github.com/ConradIrwin/dotgpg).
data/lib/dotgpg.rb ADDED
@@ -0,0 +1,96 @@
1
+ require 'pathname'
2
+ require 'fileutils'
3
+ require 'tempfile'
4
+ require 'shellwords'
5
+
6
+ require 'gpgme'
7
+ require 'thor'
8
+
9
+ require "dotgpg/key.rb"
10
+ require "dotgpg/dir.rb"
11
+ require "dotgpg/cli.rb"
12
+
13
+ class Dotgpg
14
+
15
+ class Failure < RuntimeError; end
16
+ class InvalidSignature < RuntimeError; end
17
+
18
+ # This method copied directly from Pry and is
19
+ # Copyright (c) 2013 John Mair (banisterfiend)
20
+ # https://github.com/pry/pry/blob/master/LICENSE
21
+ def self.editor
22
+ configured = ENV["VISUAL"] || ENV["EDITOR"] || guess_editor
23
+ case configured
24
+ when /^mate/, /^subl/
25
+ configured << " -w"
26
+ when /^[gm]vim/
27
+ configured << " --nofork"
28
+ when /^jedit/
29
+ configured << " -wait"
30
+ end
31
+
32
+ configured
33
+ end
34
+
35
+ def self.guess_editor
36
+ %w(subl sublime-text sensible-editor editor mate nano vim vi open).detect do |editor|
37
+ system("which #{editor} > /dev/null 2>&1")
38
+ end
39
+ end
40
+
41
+ def self.read_input(prompt)
42
+ $stderr.print prompt
43
+ $stderr.flush
44
+ $stdin.readline.strip
45
+ end
46
+
47
+ def self.read_passphrase(prompt)
48
+ `stty -echo`
49
+ read_input prompt
50
+ ensure
51
+ $stderr.print "\n"
52
+ `stty echo`
53
+ end
54
+
55
+ def self.interactive=(bool)
56
+ @interactive = bool
57
+ if interactive?
58
+ # get rid of stack trace on <ctrl-c>
59
+ trap(:INT){ exit 2 }
60
+ else
61
+ trap(:INT, "DEFAULT")
62
+ end
63
+ end
64
+
65
+ def self.interactive?
66
+ !!@interactive
67
+ end
68
+
69
+ # TODO: it'd be nice not to store the passphrase in
70
+ # plaintext in RAM.
71
+ def self.passphrase=(passphrase)
72
+ @passphrase = passphrase
73
+ end
74
+
75
+ def self.warn(context, error)
76
+ if interactive?
77
+ $stderr.puts "#{context}: #{error.message}"
78
+ else
79
+ puts "raising warning"
80
+ raise error
81
+ end
82
+ end
83
+
84
+ def self.passfunc(hook, uid_hint, passphrase_info, prev_was_bad, fd)
85
+ if interactive? && (!@passphrase || prev_was_bad != 0)
86
+ uid_hint = $1 if uid_hint =~ /<(.*)>/
87
+ @passphrase = read_passphrase "GPG passphrase for #{uid_hint}: "
88
+ elsif !@passphrase
89
+ raise "You must set Dotgpg.password or Dotgpg.interactive"
90
+ end
91
+
92
+ io = IO.for_fd(fd, 'w')
93
+ io.puts(@passphrase)
94
+ io.flush
95
+ end
96
+ end
data/spec/cli_spec.rb ADDED
@@ -0,0 +1,255 @@
1
+ require "./spec/helper"
2
+
3
+ describe Dotgpg::Cli do
4
+ before do
5
+ @dotgpg = Dotgpg::Cli.new
6
+ end
7
+
8
+ describe "init" do
9
+ it "should default to the current directory" do
10
+ $fixture.join("create-0").mkdir
11
+ Dir.chdir $fixture.join("create-0") do
12
+ @dotgpg.invoke(:init, [])
13
+ assert $fixture.join("create-0", ".gpg", "test@example.com").exist?
14
+ end
15
+ end
16
+
17
+ it "should create a .gpg directory" do
18
+ refute $fixture.join("create-1", ".gpg").exist?
19
+ @dotgpg.invoke(:init, [($fixture + "create-1").to_s])
20
+ assert $fixture.join("create-1", ".gpg").exist?
21
+ end
22
+
23
+ it "should add the user's secret key to the .gpg directory" do
24
+ @dotgpg.invoke(:init, [($fixture + "create-2").to_s])
25
+ assert_equal $fixture.join("create-2", ".gpg", "test@example.com").read, GPGME::Key.find(:secret).first.export(armor: true).to_s
26
+ end
27
+
28
+ it "should add a README to the directory" do
29
+ @dotgpg.invoke(:init, [($fixture + "create-3").to_s])
30
+ assert_equal $fixture.join("create-3", ".gpg", "test@example.com").read, GPGME::Key.find(:secret).first.export(armor: true).to_s
31
+ end
32
+
33
+ it "should fail if the .gpg directory already exists" do
34
+ FileUtils.mkdir_p $fixture + "create-4" + ".gpg"
35
+ assert_fails(/\.gpg already exists/) do
36
+ @dotgpg.invoke(:init, [($fixture + "create-4").to_s])
37
+ end
38
+ end
39
+
40
+ it "can succeed if the directory itself already exists" do
41
+ FileUtils.mkdir_p $fixture + "create-5"
42
+ @dotgpg.invoke(:init, [($fixture + "create-5").to_s])
43
+ assert $fixture.join("create-5", ".gpg").exist?
44
+ end
45
+ end
46
+
47
+ describe "key" do
48
+ it "should output the secret key" do
49
+ assert_outputs GPGME::Key.find(:secret).first.export(armor: true).to_s do
50
+ @dotgpg.invoke(:key)
51
+ end
52
+ end
53
+ end
54
+
55
+ describe "add" do
56
+ before do
57
+ @path = $fixture + rand.to_s.gsub(".", "")
58
+ @path.mkdir
59
+ Dir.chdir @path do
60
+ Dotgpg::Cli.new.invoke(:init, [])
61
+ end
62
+ end
63
+
64
+ it "should add the specified key" do
65
+ key_path = ($fixture + "add1.key").to_s
66
+ Dir.chdir @path do
67
+ @dotgpg.invoke(:add, [key_path])
68
+ end
69
+ assert Dotgpg::Dir.new(@path).has_key? Dotgpg::Key.read(File.read(key_path))
70
+ end
71
+
72
+ it "should abort if the current working directory is not dotgpg" do
73
+ key_path = ($fixture + "add1.key").to_s
74
+ assert_fails(/not in a dotgpg directory/) do
75
+ @dotgpg.invoke(:add, [key_path])
76
+ end
77
+ end
78
+
79
+ it "should abort if the key cannot be read" do
80
+ key_path = ($fixture + "no-add1.key").to_s
81
+ Dir.chdir @path do
82
+ assert_fails(/no-add1.key: not a valid GPG key/) do
83
+ @dotgpg.invoke(:add, [key_path])
84
+ end
85
+ end
86
+ end
87
+
88
+ it "should abort if the key already exists" do
89
+ key_path = ($fixture + "add1.key").to_s
90
+ Dir.chdir @path do
91
+ Dotgpg::Cli.new.invoke(:add, [key_path])
92
+
93
+ assert_fails(/add1@example.com: already exists/) do
94
+ @dotgpg.invoke(:add, [key_path])
95
+ end
96
+ end
97
+ end
98
+
99
+ it "should do nothing if the key exists and --force is specified" do
100
+ key_path = ($fixture + "add1.key").to_s
101
+ Dir.chdir @path do
102
+ Dotgpg::Cli.new.invoke(:add, [key_path])
103
+
104
+ @dotgpg.invoke(:add, [key_path], force: true)
105
+ end
106
+ end
107
+ end
108
+
109
+ describe "rm" do
110
+ before do
111
+ @path = $fixture + rand.to_s.gsub(".", "")
112
+ @path.mkdir
113
+ Dir.chdir @path do
114
+ Dotgpg::Cli.new.invoke(:init, [])
115
+ Dotgpg::Cli.new.invoke(:add, [($fixture + "add1.key").to_s])
116
+ end
117
+ end
118
+
119
+ it "should remove the specified key" do
120
+ Dir.chdir @path do
121
+ @dotgpg.invoke :rm, [".gpg/add1@example.com"]
122
+ end
123
+ refute Dotgpg::Dir.new(@path).has_key? Dotgpg::Key.read(File.read($fixture + "add1.key"))
124
+ end
125
+
126
+ it "should find the key by email" do
127
+ Dir.chdir @path do
128
+ @dotgpg.invoke :rm, ["add1@example.com"]
129
+ end
130
+ refute Dotgpg::Dir.new(@path).has_key? Dotgpg::Key.read(File.read($fixture + "add1.key"))
131
+ end
132
+
133
+ it "should abort if the key doesn't exist" do
134
+ Dir.chdir @path do
135
+ assert_fails(/add2@example.com: not a valid GPG key/) do
136
+ @dotgpg.invoke :rm, ["add2@example.com"]
137
+ end
138
+ end
139
+ end
140
+
141
+ it "should abort if the key is the user's secret key" do
142
+ Dir.chdir @path do
143
+ assert_fails(/test@example.com: refusing to remove your own secret key/) do
144
+ @dotgpg.invoke :rm, ["test@example.com"]
145
+ end
146
+ end
147
+ end
148
+
149
+ it "should do nothing if they key doesn't exist and --force is specified" do
150
+ Dir.chdir @path do
151
+ @dotgpg.invoke :rm, ["add2@example.com"], force: true
152
+ end
153
+ end
154
+
155
+ it "should remove a secret key if --force is given" do
156
+ key = Dotgpg::Key.read(File.read(@path + ".gpg" + "test@example.com"))
157
+ Dir.chdir @path do
158
+ @dotgpg.invoke :rm, ["test@example.com"], force: true
159
+ end
160
+
161
+ refute Dotgpg::Dir.new(@path).has_key? key
162
+ end
163
+ end
164
+
165
+ describe "cat" do
166
+ before do
167
+ Dotgpg.passphrase = 'test'
168
+
169
+ @path = $fixture + rand.to_s.gsub(".", "")
170
+ Dotgpg::Cli.new.invoke(:init, [@path.to_s])
171
+ Dotgpg::Dir.new(@path).encrypt @path + "a", "Test\n"
172
+ end
173
+
174
+ it "should cat an existing encrypted file" do
175
+ assert_outputs "Test\n" do
176
+ @dotgpg.invoke :cat, [(@path + "a").to_s]
177
+ end
178
+ end
179
+
180
+ it "should warn if a file doesn't exist" do
181
+ assert_warns "#{@path + "b"}: No such file or directory" do
182
+ @dotgpg.invoke :cat, [(@path + "b").to_s]
183
+ end
184
+ end
185
+
186
+ it "should cat the existing files if a mixture is specified" do
187
+ assert_outputs "Test\n" do
188
+ assert_warns "#{@path + "b"}: No such file or directory" do
189
+ @dotgpg.invoke :cat, [(@path + "b").to_s, (@path + "a").to_s]
190
+ end
191
+ end
192
+ end
193
+
194
+ it "should fail if the file is not in a .gpg directory" do
195
+ assert_fails "not in a dotgpg directory" do
196
+ @dotgpg.invoke :cat, ["/tmp/b"]
197
+ end
198
+ end
199
+
200
+ it "should fail if the passphrase is wrong" do
201
+ Dotgpg.passphrase = 'wrong'
202
+ assert_fails "Bad passphrase" do
203
+ @dotgpg.invoke :cat, [(@path + "a").to_s]
204
+ end
205
+ end
206
+ end
207
+
208
+ describe "edit" do
209
+ before do
210
+ Dotgpg.passphrase = 'test'
211
+
212
+ @path = $fixture + rand.to_s.gsub(".", "")
213
+ Dotgpg::Cli.new.invoke(:init, [@path.to_s])
214
+ Dotgpg::Dir.new(@path).encrypt @path + "a", "Bad test\n"
215
+
216
+ end
217
+
218
+ it "should let you edit an existing file" do
219
+ ENV['EDITOR'] = "sed -i '' s/Bad/Good/"
220
+ path = (@path + "a").to_s
221
+ @dotgpg.invoke(:edit, [path])
222
+ assert_outputs "Good test\n" do
223
+ Dotgpg::Dir.new(@path).decrypt path, $stdout
224
+ end
225
+ end
226
+
227
+ it "should open a non-existing file as blank" do
228
+ ENV['EDITOR'] = "ruby -e 'File.write(ARGV[0], %(Good test\n)) if File.read(ARGV[0]) == %()'"
229
+ path = (@path + "b").to_s
230
+ @dotgpg.invoke(:edit, [path])
231
+ assert_outputs "Good test\n" do
232
+ Dotgpg::Dir.new(@path).decrypt path, $stdout
233
+ end
234
+ end
235
+
236
+ it "should warn if a file cannot be decrypted" do
237
+ File.write(@path + "d", "not encrypted...")
238
+ path = (@path + "d").to_s
239
+ assert_warns "#{@path + "d"}: No data" do
240
+ @dotgpg.invoke(:edit, [path])
241
+ end
242
+ end
243
+
244
+ it "should fail if invoking the editor doesn't work" do
245
+ ENV['EDITOR'] = 'not-an-editor'
246
+ assert_fails "Problem with editor. Not saving changes" do
247
+ @dotgpg.invoke :edit, [(@path + "a").to_s]
248
+ end
249
+ end
250
+
251
+ it "should edit the existing files if a mixture is specified" do
252
+
253
+ end
254
+ end
255
+ end
data/spec/dir_spec.rb ADDED
@@ -0,0 +1,214 @@
1
+ require "./spec/helper"
2
+
3
+ describe Dotgpg::Dir do
4
+ before do
5
+ @dir = Dotgpg::Dir.new($basic)
6
+ end
7
+
8
+ describe ".closest" do
9
+ it "should find the current directory" do
10
+ Dir.chdir $basic do
11
+ assert_equal @dir, Dotgpg::Dir.closest(".")
12
+ end
13
+ end
14
+
15
+ it "should find a specified directory" do
16
+ assert_equal @dir, Dotgpg::Dir.closest($basic)
17
+ end
18
+
19
+ it "should find the directory containing the given file" do
20
+ assert_equal @dir, Dotgpg::Dir.closest($basic + "a")
21
+ end
22
+
23
+ it "should find the ancestor of the directory containing the given file" do
24
+ assert_equal @dir, Dotgpg::Dir.closest($basic + "b" + "c")
25
+ end
26
+
27
+ it "should not find a directory that does not exist" do
28
+ assert_nil Dotgpg::Dir.closest("/tmp")
29
+ end
30
+
31
+ it "should not find a directory that only some of the files are in" do
32
+ assert_equal nil, Dotgpg::Dir.closest($basic, "/tmp")
33
+ end
34
+
35
+ it "should find a directory that all of the files are in" do
36
+ assert_equal @dir, Dotgpg::Dir.closest($basic, $basic + "a", $basic + "b" + "c")
37
+ end
38
+ end
39
+
40
+ describe "dotgpg?" do
41
+ it 'should be true in a directory managed by dotgpg' do
42
+ assert_equal true, @dir.dotgpg?
43
+ end
44
+
45
+ it 'should not be true in a random directory' do
46
+ assert_equal false, Dotgpg::Dir.new(".").dotgpg?
47
+ end
48
+
49
+ it "should not be true in a directory that doesn't exist" do
50
+ assert_equal false, Dotgpg::Dir.new(rand.to_s).dotgpg?
51
+ end
52
+ end
53
+
54
+ describe "known_keys" do
55
+ before do
56
+ @keys = @dir.known_keys
57
+ end
58
+
59
+ it "should return private keys in the truststore" do
60
+ assert_includes @keys, GPGME::Key.find(:secret, "test@example.com").first
61
+ end
62
+
63
+ it "should return public keys not yet in the truststore" do
64
+ assert_includes @keys.map(&:email), "test2@example.com"
65
+ end
66
+
67
+ it "should return public keys in the truststore" do
68
+ assert_includes @keys.map(&:email), "test2@example.com"
69
+ end
70
+ end
71
+
72
+ describe "all_encrypted_files" do
73
+ before do
74
+ @files = @dir.all_encrypted_files
75
+ end
76
+
77
+ it "should find files in the top-level" do
78
+ assert_includes @files, $basic + "a"
79
+ end
80
+
81
+ it "should find files in sub-directories" do
82
+ assert_includes @files, $basic + "b" + "c"
83
+ end
84
+
85
+ it "should not find unencrypted files" do
86
+ readme = $basic + "README.md"
87
+ assert readme.exist?
88
+ refute_includes @files, $basic + "README.md"
89
+ end
90
+
91
+ it "should not find files through symlinks" do
92
+ duplicate_a = $basic + "c" + "basic" + "a"
93
+ assert duplicate_a.exist?
94
+ refute_includes @files, duplicate_a
95
+ end
96
+ end
97
+
98
+ describe "decrypt" do
99
+ before do
100
+ Dotgpg.passphrase = "test"
101
+ end
102
+
103
+ it "should be able to decrypt files for which the secret is known" do
104
+ s = StringIO.new
105
+ @dir.decrypt $basic + "a", s
106
+ s.rewind
107
+ assert_equal "Test\n", s.read
108
+ end
109
+
110
+ it "should warn if the file cannot be read" do
111
+ assert_warns "#{$basic + "404"}: No such file or directory" do
112
+ @dir.decrypt $basic + "404", StringIO.new
113
+ end
114
+ end
115
+
116
+ it "should warn if the file cannot be decrypted" do
117
+ assert_warns "#{$basic + "README.md"}: No data" do
118
+ @dir.decrypt $basic + "README.md", StringIO.new
119
+ end
120
+ end
121
+
122
+ it "should raise on bad passphrase" do
123
+ Dotgpg.passphrase = 'wrong'
124
+ assert_raises GPGME::Error::BadPassphrase do
125
+ assert_warns nil do
126
+ @dir.decrypt $basic + "a", StringIO.new
127
+ end
128
+ end
129
+ end
130
+ end
131
+
132
+ describe "encrypt" do
133
+ before do
134
+ Dotgpg.passphrase = 'test'
135
+ end
136
+
137
+ after do
138
+ FileUtils.rm_f $basic + "test-armor"
139
+ FileUtils.rm_f $basic + "test-recipients"
140
+ end
141
+
142
+ it "should armor files" do
143
+ @dir.encrypt $basic + "test-armor", 'test'
144
+ assert_match(/-----BEGIN PGP MESSAGE-----/, File.read($basic + "test-armor"))
145
+ end
146
+
147
+ it "should encrypt files to all recipients" do
148
+ @dir.encrypt $basic + "test-recipients", 'test'
149
+
150
+ ["D1B8548C844F4881", "54907534D1B5A86B", "8490321363F14C03"].each do |keyid|
151
+ assert_contains_keyid keyid, File.read($basic + "test-recipients")
152
+ end
153
+ end
154
+ end
155
+
156
+ describe "add_key" do
157
+ before do
158
+ Dotgpg.passphrase = 'test'
159
+ end
160
+
161
+ it "should create the file in the .gpg directory" do
162
+ add1 = Dotgpg::Key.read $fixture + "add1.key"
163
+ refute $basic.join(".gpg", "add1@example.com").exist?
164
+ @dir.add_key add1
165
+ assert $basic.join(".gpg", "add1@example.com").exist?
166
+ end
167
+
168
+ it "should add the key as a recipient on all the files" do
169
+ add2 = Dotgpg::Key.read $fixture + "add2.key"
170
+ refute_contains_keyid add2.subkeys.last.keyid, File.read($basic + "a")
171
+ @dir.add_key add2
172
+ assert_contains_keyid add2.subkeys.last.keyid, File.read($basic + "a")
173
+ end
174
+
175
+ it "should not add the key if re-encryption fails" do
176
+ Dotgpg.passphrase = 'wrong'
177
+ add3 = Dotgpg::Key.read $fixture + "add3.key"
178
+ assert_raises GPGME::Error::BadPassphrase do
179
+ @dir.add_key add3
180
+ end
181
+ refute $basic.join(".gpg", "add3@example.com").exist?
182
+ refute_contains_keyid add3.subkeys.last.keyid, File.read($basic + "a")
183
+ end
184
+ end
185
+
186
+ describe "remove_key" do
187
+ before do
188
+ Dotgpg.passphrase = 'test'
189
+ end
190
+
191
+ it "should remove the file from the .gpg directory" do
192
+ removed1 = Dotgpg::Key.read $basic.join(".gpg", "removed1@example.com")
193
+ assert $basic.join(".gpg", "removed1@example.com").exist?
194
+ @dir.remove_key removed1
195
+ refute $basic.join(".gpg", "removed1@example.com").exist?
196
+ end
197
+
198
+ it "should remove the key as a recipient from all the files" do
199
+ removed2 = Dotgpg::Key.read $basic.join(".gpg", "removed2@example.com")
200
+ assert_contains_keyid removed2.subkeys.last.keyid, File.read($basic + "a")
201
+ @dir.remove_key removed2
202
+ refute_contains_keyid removed2.subkeys.last.keyid, File.read($basic + "a")
203
+ end
204
+
205
+ it "should not remove the key if re-encryption fails" do
206
+ Dotgpg.passphrase = 'wrong'
207
+ removed3 = Dotgpg::Key.read $basic.join(".gpg", "removed3@example.com")
208
+ assert_raises GPGME::Error::BadPassphrase do
209
+ @dir.add_key removed3
210
+ end
211
+ assert $basic.join(".gpg", "removed3@example.com").exist?
212
+ end
213
+ end
214
+ end