secret_store 0.0.2 → 0.0.3

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