secret_store 0.0.2 → 0.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.
@@ -1,3 +1,3 @@
1
1
  class SecretStore
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
data/lib/secret_store.rb CHANGED
@@ -3,33 +3,21 @@ require "yaml"
3
3
  require "secret_store/version"
4
4
 
5
5
  class SecretStore
6
- attr_reader :file_path
7
-
8
6
  def initialize(password, file_path)
9
7
  self.password = password
10
- self.file_path = file_path
8
+ @data = YamlBackend.new(file_path)
11
9
  end
12
10
 
13
- def store(key, secret, reload_data = true)
14
- load_data if reload_data
15
-
16
- if @data[key.to_s]
17
- raise "Key #{key} already stored"
18
- end
19
-
20
- @data.merge!(key.to_s => encrypt(secret))
21
- store_data
22
- load_data[key]
11
+ def store(key, secret)
12
+ @data.insert(key, encrypt(secret))
23
13
  end
24
14
 
25
15
  def store!(key, secret)
26
- load_data
27
- @data.delete(key.to_s)
28
- store(key, secret, false)
16
+ @data.overwrite(key, encrypt(secret))
29
17
  end
30
18
 
31
19
  def get(key)
32
- if ciphertext = @data[key.to_s]
20
+ if ciphertext = @data[key]
33
21
  cipher.decrypt(ciphertext)
34
22
  end
35
23
  end
@@ -46,17 +34,10 @@ class SecretStore
46
34
  decrypted = decrypted_data
47
35
  self.password = new_password
48
36
  replace_with_decrypted(decrypted)
49
- store_data
50
37
  end
51
38
 
52
39
  private
53
40
 
54
- def file_path=(file_path)
55
- @file_path = file_path
56
- load_data
57
- @file_path
58
- end
59
-
60
41
  def password=(password)
61
42
  @cipher = nil
62
43
  @password = password
@@ -66,22 +47,8 @@ class SecretStore
66
47
  @cipher ||= Gibberish::AES.new(@password)
67
48
  end
68
49
 
69
- def load_data
70
- begin
71
- @data = YAML.load_file(file_path) || {}
72
- rescue Errno::ENOENT
73
- @data = {}
74
- end
75
- end
76
-
77
- def store_data
78
- File.open(@file_path, File::RDWR|File::CREAT|File::LOCK_EX, 0640) do |f|
79
- f.puts YAML.dump @data
80
- end
81
- end
82
-
83
50
  def decrypted_data
84
- @data.each_key.inject({}) do |decrypted_data, key|
51
+ @data.keys.inject({}) do |decrypted_data, key|
85
52
  decrypted_data[key] = get(key)
86
53
  decrypted_data
87
54
  end
@@ -89,7 +56,72 @@ class SecretStore
89
56
 
90
57
  def replace_with_decrypted(decrypted)
91
58
  decrypted.each do |key, plaintext|
92
- @data[key] = encrypt(plaintext)
59
+ @data.overwrite(key, encrypt(plaintext))
60
+ end
61
+ end
62
+
63
+ class YamlBackend
64
+ SAVE_FLAGS = File::RDWR | File::CREAT | File::LOCK_EX
65
+ SAVE_PERMS = 0640
66
+
67
+ def initialize(file_path)
68
+ @file_path = file_path
69
+ end
70
+
71
+ def [](key)
72
+ data[key.to_s]
73
+ end
74
+
75
+ def keys
76
+ data.keys
77
+ end
78
+
79
+ def insert(key, value)
80
+ if self[key]
81
+ raise "Key #{key} already stored"
82
+ end
83
+
84
+ data[key.to_s] = value
85
+ save
86
+ value
87
+ end
88
+
89
+ def overwrite(key, value)
90
+ delete!(key.to_s)
91
+ insert(key, value)
92
+ end
93
+
94
+ def delete(key)
95
+ return unless self[key]
96
+ value = delete!(key)
97
+ save && value
98
+ end
99
+
100
+ def reload
101
+ @data = nil
102
+ data && true
103
+ end
104
+
105
+ private
106
+
107
+ def delete!(key)
108
+ data.delete(key.to_s)
109
+ end
110
+
111
+ def data
112
+ begin
113
+ @data ||= YAML.load_file(@file_path) || {}
114
+ rescue Errno::ENOENT
115
+ @data = {}
116
+ end
117
+ end
118
+
119
+ def save
120
+ File.open(@file_path, SAVE_FLAGS, SAVE_PERMS) do |f|
121
+ f.truncate(0)
122
+ f.puts YAML.dump(data)
123
+ end
124
+ true
93
125
  end
94
126
  end
95
127
  end
@@ -5,7 +5,6 @@ describe SecretStore, "initializing" do
5
5
 
6
6
  it "takes a password and file path" do
7
7
  subject = SecretStore.new("pass", tmpfile.path)
8
- subject.file_path.should == tmpfile.path
9
8
  end
10
9
  end
11
10
 
@@ -82,11 +81,15 @@ describe SecretStore, "changing the password" do
82
81
  subject { SecretStore.new("the_pass", tmpfile.path) }
83
82
 
84
83
  it "resets the value for each secret key" do
85
- tmpfile.puts YAML.dump("foo" => "bar", "fizz" => "buzz")
84
+ subject.store("foo", "bar")
85
+ subject.store("fizz", "buzz")
86
+ original_data = YAML.load_file(tmpfile.path)
87
+
86
88
  subject.change_password("new_password")
89
+
87
90
  data = YAML.load_file(tmpfile.path)
88
- data["foo"].should_not == "bar"
89
- data["fizz"].should_not == "buzz"
91
+ data["foo"].should_not == original_data["foo"]
92
+ data["fizz"].should_not == original_data["fizz"]
90
93
  end
91
94
 
92
95
  it "leaves you with the ability to get secrets using the new password" do
@@ -99,3 +102,62 @@ describe SecretStore, "changing the password" do
99
102
  subject.get("foo").should == "bar"
100
103
  end
101
104
  end
105
+
106
+ describe SecretStore::YamlBackend do
107
+ let(:tmpfile) { Tempfile.new("secret_store") }
108
+ subject { SecretStore::YamlBackend.new(tmpfile) }
109
+
110
+ before do
111
+ tmpfile.puts YAML.dump("foo" => "bar", "fizz" => "buzz")
112
+ tmpfile.flush
113
+ end
114
+
115
+ it "reads indifferently from its data using []" do
116
+ subject["foo"].should == "bar"
117
+ subject["fizz"].should == "buzz"
118
+ subject[:fizz].should == "buzz"
119
+ end
120
+
121
+ it "can return the keys on data" do
122
+ subject.keys.should =~ %w[foo fizz]
123
+ end
124
+
125
+ it "can reload data on demand" do
126
+ File.open(tmpfile.path, 'w') do |f|
127
+ f.puts YAML.dump("foo" => "reloaded")
128
+ end
129
+
130
+ subject.reload
131
+ subject["foo"].should == "reloaded"
132
+ end
133
+
134
+ it "can insert a value for a non-existant key" do
135
+ subject.insert("new", "value")
136
+ subject.reload
137
+ subject["new"].should == "value"
138
+ end
139
+
140
+ it "will raise an error trying to insert for an existing key" do
141
+ lambda {
142
+ subject.insert("foo", "123")
143
+ }.should raise_error
144
+ end
145
+
146
+ it "can overwrite an existing key" do
147
+ subject.overwrite("foo", "123")
148
+ subject.reload
149
+ subject["foo"].should == "123"
150
+ end
151
+
152
+ it "can delete a key" do
153
+ subject.delete("foo").should == "bar"
154
+ subject.reload
155
+ subject.keys.should == ["fizz"]
156
+ end
157
+
158
+ it "can save the data" do
159
+ subject.overwrite("foo", "123")
160
+ data = SecretStore::YamlBackend.new(tmpfile)
161
+ data["foo"].should == "123"
162
+ end
163
+ end
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.2
4
+ version: 0.0.3
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: &2152744720 !ruby/object:Gem::Requirement
16
+ requirement: &2153518960 !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: *2152744720
24
+ version_requirements: *2153518960
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rake
27
- requirement: &2152744200 !ruby/object:Gem::Requirement
27
+ requirement: &2153518540 !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: *2152744200
35
+ version_requirements: *2153518540
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rspec
38
- requirement: &2152743740 !ruby/object:Gem::Requirement
38
+ requirement: &2153518120 !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: *2152743740
46
+ version_requirements: *2153518120
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: 2805007848849734124
77
+ hash: -2358538595218873167
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: 2805007848849734124
86
+ hash: -2358538595218873167
87
87
  requirements: []
88
88
  rubyforge_project: secret_store
89
89
  rubygems_version: 1.8.15