secret_store 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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