commit a0ad2a13fb1d3372885810185bc8b4b1b485eaae
parent c697fa517c2d1daf2752b3f369731ee67212cd0a
Author: Daniel Barlow <dan@telent.net>
Date: Fri, 13 Aug 2021 17:29:42 +0100
add "moss rebuild", re-encrypts all the secrets
Diffstat:
4 files changed, 48 insertions(+), 7 deletions(-)
diff --git a/README.md b/README.md
@@ -179,9 +179,7 @@ security requirements, look elsewhere.
users on your machine
-
## TO DO
* copy to clipboard
-* support re-encrypting a subtree when recipients change
* warn or fail if iffy store permissions?
diff --git a/features/moss.feature b/features/moss.feature
@@ -25,7 +25,7 @@ Feature: command-line password management
Scenario: it overwrites secrets when forced
Given I am using a temporary password store
- When I store a secret for "home/ebay" with content "first"
+ And there is a secret for "home/ebay" with content "first"
And I force store a secret for "home/ebay" with content "second"
Then "home/ebay.age" plaintext is "second"
@@ -143,3 +143,11 @@ Feature: command-line password management
When I run "moss cat"
Then it shows a usage message for "cat"
+
+ Scenario: I can re-encrypt files
+ Given I am using a temporary password store
+ And there is a secret for "home/ebay" with content "horsebattery"
+ And there is a secret for "home/catfish" with content "chocolate"
+ When I add a recipient for the identity "partner.key"
+ And I re-encrypt the store
+ Then I can decrypt "home/catfish" with key "partner.key" to "chocolate"
diff --git a/features/step_definitions/steps.rb b/features/step_definitions/steps.rb
@@ -61,6 +61,11 @@ Then('{string} does not exist') do |path|
expect(store_path(path)).not_to exist
end
+# yes, this step is a Given and a When with different text
+Given("there is a secret for {string} with content {string}") do |name, content|
+ @stored_secret_name = name
+ shell "echo -n #{content} | #{MOSS} insert #{name.inspect}"
+end
When("I store a secret for {string} with content {string}") do |name, content|
@stored_secret_name = name
@@ -94,12 +99,19 @@ Then("{string} plaintext is {string}") do |name, expected|
expect(plaintext).to eq expected
end
-Then("I can decrypt it with key {string} to {string}") do |keyfile, expected|
- path_name = store_path(@stored_secret_name + '.age')
+def decrypt(secret_name, keyfile)
+ path_name = store_path(secret_name + '.age')
contents = File.read(path_name)
expect(contents).to match /AGE ENCRYPTED FILE/
- plaintext = shell "age -i fixtures/keys/#{keyfile} -d #{path_name.to_s.inspect}"
- expect(plaintext).to eq expected
+ shell "age -i fixtures/keys/#{keyfile} -d #{path_name.to_s.inspect}"
+end
+
+Then("I can decrypt it with key {string} to {string}") do |keyfile, expected|
+ expect(decrypt(@stored_secret_name, keyfile)).to eq expected
+end
+
+Then("I can decrypt {string} with key {string} to {string}") do |secret_name, keyfile, expected|
+ expect(decrypt(secret_name, keyfile)).to eq expected
end
Then("I cannot decrypt it with key {string}") do |keyfile|
@@ -154,6 +166,14 @@ Given("there are recipient files in different subtrees") do |table|
end
end
+When('I add a recipient for the identity {string}') do |keyfile|
+ add_to_store(".recipients",
+ store_path(".recipients").read +
+ "\n" +
+ recipient_for_identity("fixtures/keys/#{keyfile}"))
+end
+
+
Given("the store is version-controlled") do
shell "cd #{ENV["MOSS_HOME"]}/store && git init --initial-branch=main"
end
@@ -226,3 +246,7 @@ Then("it shows a usage message for {string}") do |command|
expect(@exit_status).to be > 0
expect(@i_see).to match /usage: moss #{command} \<file\>/
end
+
+When('I re-encrypt the store') do
+ @i_see = shell "#{MOSS} rebuild"
+end
diff --git a/moss.rb b/moss.rb
@@ -127,6 +127,13 @@ class Moss
pathname.delete
end
+ def rebuild_store(verbose:)
+ secrets.each do |name|
+ warn name if verbose
+ write_secret(name, read_secret(name), overwrite: true)
+ end
+ end
+
def secrets
Dir[store.join("**/*.age")].map { |n|
Pathname.new(n).relative_path_from(store).sub_ext("").to_s
@@ -341,6 +348,10 @@ def cli
MOSS.remove_secret(secret)
end
+ command :rebuild, "re-encrypt all secrets in the store (for use e.g. after changing recipients)" do |quiet: false|
+ MOSS.rebuild_store(verbose: ! quiet)
+ end
+
command :git, "perform git operation in store" do |* git_command|
MOSS.git_operation(git_command)
end