secret_store 0.0.1 → 0.0.2

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/README.md CHANGED
@@ -1,11 +1,14 @@
1
1
  SecretStore
2
2
  ===========
3
3
 
4
- Store secrets for your app in a encrypted in a yaml file.
4
+ Store secrets for your app AES encrypted in a yaml file.
5
5
 
6
6
  Often when working on a web application, you have the need for storing a
7
7
  variety of API keys or secrets to 3rd party services. However, it's not
8
8
  desireable to check those secrets in as plain text to the code respository.
9
+ This means that any 3rd party access to your repository provides that info.
10
+ With SecretStore, parties without knowledge of the master password (this can
11
+ include your own team members!) will not have direct access to such secrets.
9
12
 
10
13
  One common way around this is to set such keys in the environment. This is,
11
14
  for example, what [Heroku recommends](https://devcenter.heroku.com/articles/config-vars)
@@ -41,11 +44,12 @@ require "secret_store"
41
44
  secret_store = SecretStore.new("master_password", "/path/to/data.yml")
42
45
  secret_store.store("some_api_key", "c7dd199")
43
46
  secret_store.get("some_api_key") # => "c7dd199"
44
- secret_store.get("unknown_key") # => raises IndexError
47
+ secret_store.get("unknown_key") # => nil
48
+ secret_store.get!("unknown_key") # => raises IndexError
45
49
 
46
- secret_store.store("known_key", "b123fa") => stores
47
- secret_store.store("known_key", "new_val") => raises error
48
- secret_store.store("known_key", "new_val", :force) => overwrites stored
50
+ secret_store.store("secret", "b123fa") # => stores
51
+ secret_store.store("secret", "new_val") # => raises error
52
+ secret_store.store!("secret", "new_val") # => overwrites stored
49
53
  ```
50
54
 
51
55
  For a typical application, it could be desirable to define a
@@ -1,3 +1,3 @@
1
1
  class SecretStore
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/lib/secret_store.rb CHANGED
@@ -10,10 +10,10 @@ class SecretStore
10
10
  self.file_path = file_path
11
11
  end
12
12
 
13
- def store(key, secret, force = false)
14
- load_data
13
+ def store(key, secret, reload_data = true)
14
+ load_data if reload_data
15
15
 
16
- if ! force && @data[key.to_s]
16
+ if @data[key.to_s]
17
17
  raise "Key #{key} already stored"
18
18
  end
19
19
 
@@ -22,9 +22,20 @@ class SecretStore
22
22
  load_data[key]
23
23
  end
24
24
 
25
+ def store!(key, secret)
26
+ load_data
27
+ @data.delete(key.to_s)
28
+ store(key, secret, false)
29
+ end
30
+
25
31
  def get(key)
26
- ciphertext = @data.fetch(key.to_s)
27
- cipher.decrypt(ciphertext)
32
+ if ciphertext = @data[key.to_s]
33
+ cipher.decrypt(ciphertext)
34
+ end
35
+ end
36
+
37
+ def get!(key)
38
+ get(key) or raise IndexError.new(%Q[key not found: "#{key}"])
28
39
  end
29
40
 
30
41
  def encrypt(secret)
@@ -32,18 +43,9 @@ class SecretStore
32
43
  end
33
44
 
34
45
  def change_password(new_password)
35
- new_data = {}
36
-
37
- @data.each_key do |key|
38
- new_data[key] = get(key)
39
- end
40
-
46
+ decrypted = decrypted_data
41
47
  self.password = new_password
42
-
43
- new_data.each do |key, plaintext|
44
- @data[key] = encrypt(plaintext)
45
- end
46
-
48
+ replace_with_decrypted(decrypted)
47
49
  store_data
48
50
  end
49
51
 
@@ -77,4 +79,17 @@ class SecretStore
77
79
  f.puts YAML.dump @data
78
80
  end
79
81
  end
82
+
83
+ def decrypted_data
84
+ @data.each_key.inject({}) do |decrypted_data, key|
85
+ decrypted_data[key] = get(key)
86
+ decrypted_data
87
+ end
88
+ end
89
+
90
+ def replace_with_decrypted(decrypted)
91
+ decrypted.each do |key, plaintext|
92
+ @data[key] = encrypt(plaintext)
93
+ end
94
+ end
80
95
  end
@@ -36,18 +36,17 @@ describe SecretStore, "storing a secret" do
36
36
  end
37
37
 
38
38
  context "when the key is already stored" do
39
- it "raises if not forced" do
39
+ it "raises" do
40
40
  subject.store("foobar", "fizzbuzz")
41
41
  lambda {
42
42
  subject.store("foobar", "fizzbuzz")
43
43
  }.should raise_error
44
44
  end
45
45
 
46
- it "stores if forced" do
46
+ it "can be overwritten with #store!" do
47
47
  subject.store("foobar", "fizzbuzz")
48
- lambda {
49
- subject.store("foobar", "fizzbuzz", :force)
50
- }.should_not raise_error
48
+ subject.store!("foobar", "buzzfizz")
49
+ subject.get("foobar").should == "buzzfizz"
51
50
  end
52
51
  end
53
52
  end
@@ -61,12 +60,6 @@ describe SecretStore, "getting a secret" do
61
60
  subject.get("foobar").should == "fizzbuzz"
62
61
  end
63
62
 
64
- it "raises IndexError if the key is not found" do
65
- lambda{
66
- subject.get("not_found")
67
- }.should raise_error(IndexError)
68
- end
69
-
70
63
  it "raises OpenSSL::Cipher::CipherError if the password for the store is wrong" do
71
64
  subject.store("foobar", "fizzbuzz")
72
65
  with_wrong_pass = SecretStore.new("wrong_pass", tmpfile.path)
@@ -74,6 +67,14 @@ describe SecretStore, "getting a secret" do
74
67
  with_wrong_pass.get("foobar")
75
68
  }.should raise_error(OpenSSL::Cipher::CipherError)
76
69
  end
70
+
71
+ context "when called via #get!" do
72
+ it "raises IndexError if the key is not found" do
73
+ lambda{
74
+ subject.get!("not_found")
75
+ }.should raise_error(IndexError)
76
+ end
77
+ end
77
78
  end
78
79
 
79
80
  describe SecretStore, "changing the password" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: secret_store
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -13,7 +13,7 @@ date: 2012-08-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: gibberish
16
- requirement: &2160632160 !ruby/object:Gem::Requirement
16
+ requirement: &2152744720 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *2160632160
24
+ version_requirements: *2152744720
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rake
27
- requirement: &2160631720 !ruby/object:Gem::Requirement
27
+ requirement: &2152744200 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *2160631720
35
+ version_requirements: *2152744200
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rspec
38
- requirement: &2160631280 !ruby/object:Gem::Requirement
38
+ requirement: &2152743740 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,7 +43,7 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *2160631280
46
+ version_requirements: *2152743740
47
47
  description: Store secrets for your app in a encrypted in a yaml file.
48
48
  email:
49
49
  - xternal1+github@gmail.com
@@ -74,7 +74,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
74
74
  version: '0'
75
75
  segments:
76
76
  - 0
77
- hash: -1229492496997866402
77
+ hash: 2805007848849734124
78
78
  required_rubygems_version: !ruby/object:Gem::Requirement
79
79
  none: false
80
80
  requirements:
@@ -83,7 +83,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
83
83
  version: '0'
84
84
  segments:
85
85
  - 0
86
- hash: -1229492496997866402
86
+ hash: 2805007848849734124
87
87
  requirements: []
88
88
  rubyforge_project: secret_store
89
89
  rubygems_version: 1.8.15