secret_store 0.0.3 → 0.0.4
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/lib/secret_store.rb +57 -6
- data/lib/secret_store/version.rb +1 -1
- data/spec/secret_store_spec.rb +46 -1
- metadata +10 -10
data/lib/secret_store.rb
CHANGED
@@ -1,11 +1,20 @@
|
|
1
|
-
require "gibberish"
|
2
1
|
require "yaml"
|
2
|
+
require "timeout"
|
3
|
+
require "gibberish"
|
3
4
|
require "secret_store/version"
|
4
5
|
|
5
6
|
class SecretStore
|
6
|
-
|
7
|
+
class ReadOnly < RuntimeError; end
|
8
|
+
|
9
|
+
def initialize(password, file_path, options = {})
|
7
10
|
self.password = password
|
8
|
-
|
11
|
+
backend_class = options.fetch(:backend_class, YamlBackend)
|
12
|
+
@data = backend_class.new(file_path)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.new_read_only(password, file_path)
|
16
|
+
store = new(password, file_path, :backend_class => ReadOnlyYamlBackend)
|
17
|
+
store
|
9
18
|
end
|
10
19
|
|
11
20
|
def store(key, secret)
|
@@ -31,6 +40,10 @@ class SecretStore
|
|
31
40
|
end
|
32
41
|
|
33
42
|
def change_password(new_password)
|
43
|
+
unless @data.permits_writes?
|
44
|
+
raise ReadOnly
|
45
|
+
end
|
46
|
+
|
34
47
|
decrypted = decrypted_data
|
35
48
|
self.password = new_password
|
36
49
|
replace_with_decrypted(decrypted)
|
@@ -61,7 +74,7 @@ class SecretStore
|
|
61
74
|
end
|
62
75
|
|
63
76
|
class YamlBackend
|
64
|
-
SAVE_FLAGS = File::RDWR | File::CREAT | File::LOCK_EX
|
77
|
+
SAVE_FLAGS = File::RDWR | File::CREAT | File::LOCK_EX | File::LOCK_NB
|
65
78
|
SAVE_PERMS = 0640
|
66
79
|
|
67
80
|
def initialize(file_path)
|
@@ -69,10 +82,12 @@ class SecretStore
|
|
69
82
|
end
|
70
83
|
|
71
84
|
def [](key)
|
85
|
+
reload_if_updated
|
72
86
|
data[key.to_s]
|
73
87
|
end
|
74
88
|
|
75
89
|
def keys
|
90
|
+
reload_if_updated
|
76
91
|
data.keys
|
77
92
|
end
|
78
93
|
|
@@ -102,12 +117,23 @@ class SecretStore
|
|
102
117
|
data && true
|
103
118
|
end
|
104
119
|
|
120
|
+
def permits_writes?
|
121
|
+
true
|
122
|
+
end
|
123
|
+
|
105
124
|
private
|
106
125
|
|
107
126
|
def delete!(key)
|
127
|
+
reset_mtime_tracker
|
108
128
|
data.delete(key.to_s)
|
109
129
|
end
|
110
130
|
|
131
|
+
def reload_if_updated
|
132
|
+
mtime = File.exists?(@file_path) && File.mtime(@file_path)
|
133
|
+
@mtime_tracker ||= mtime
|
134
|
+
@mtime_tracker != mtime && reload
|
135
|
+
end
|
136
|
+
|
111
137
|
def data
|
112
138
|
begin
|
113
139
|
@data ||= YAML.load_file(@file_path) || {}
|
@@ -117,11 +143,36 @@ class SecretStore
|
|
117
143
|
end
|
118
144
|
|
119
145
|
def save
|
146
|
+
#TODO figure out the WRONLY flag so we don't
|
147
|
+
# have to truncate
|
120
148
|
File.open(@file_path, SAVE_FLAGS, SAVE_PERMS) do |f|
|
121
149
|
f.truncate(0)
|
122
|
-
f.
|
150
|
+
f.write YAML.dump(data)
|
151
|
+
reset_mtime_tracker
|
152
|
+
true
|
123
153
|
end
|
124
|
-
|
154
|
+
end
|
155
|
+
|
156
|
+
def reset_mtime_tracker
|
157
|
+
@mtime_tracker = nil
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
class ReadOnlyYamlBackend < YamlBackend
|
162
|
+
[:insert, :overwrite, :delete, :save].each do |meth|
|
163
|
+
define_method meth do |*|
|
164
|
+
raise ReadOnly
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def permits_writes?
|
169
|
+
false
|
170
|
+
end
|
171
|
+
|
172
|
+
private
|
173
|
+
|
174
|
+
def reload_if_updated
|
175
|
+
#no-op
|
125
176
|
end
|
126
177
|
end
|
127
178
|
end
|
data/lib/secret_store/version.rb
CHANGED
data/spec/secret_store_spec.rb
CHANGED
@@ -6,6 +6,12 @@ describe SecretStore, "initializing" do
|
|
6
6
|
it "takes a password and file path" do
|
7
7
|
subject = SecretStore.new("pass", tmpfile.path)
|
8
8
|
end
|
9
|
+
|
10
|
+
it "can be optionally injected with a backend class" do
|
11
|
+
klass = Class.new
|
12
|
+
klass.should_receive(:new)
|
13
|
+
subject = SecretStore.new("pass", tmpfile.path, :backend_class => klass)
|
14
|
+
end
|
9
15
|
end
|
10
16
|
|
11
17
|
describe SecretStore, "storing a secret" do
|
@@ -157,7 +163,46 @@ describe SecretStore::YamlBackend do
|
|
157
163
|
|
158
164
|
it "can save the data" do
|
159
165
|
subject.overwrite("foo", "123")
|
160
|
-
data = SecretStore::YamlBackend.new(tmpfile)
|
166
|
+
data = SecretStore::YamlBackend.new(tmpfile.path)
|
161
167
|
data["foo"].should == "123"
|
162
168
|
end
|
169
|
+
|
170
|
+
it "will auto reload the data if the file mtime changes" do
|
171
|
+
subject["foo"].should == "bar"
|
172
|
+
SecretStore::YamlBackend.new(tmpfile.path).overwrite("foo", "changed")
|
173
|
+
FileUtils.touch(tmpfile.path, :mtime => Time.now + 5)
|
174
|
+
subject["foo"].should == "changed"
|
175
|
+
end
|
176
|
+
|
177
|
+
it "can create a file that doesn't exist" do
|
178
|
+
path = tmpfile.path
|
179
|
+
tmpfile.unlink
|
180
|
+
store = SecretStore::YamlBackend.new(path)
|
181
|
+
store["foo"].should be_nil
|
182
|
+
store.insert("foo", "bar")
|
183
|
+
store["foo"].should == "bar"
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
describe SecretStore::ReadOnlyYamlBackend do
|
188
|
+
let(:tmpfile) { Tempfile.new("secret_store") }
|
189
|
+
subject { SecretStore::ReadOnlyYamlBackend.new(tmpfile) }
|
190
|
+
|
191
|
+
it "doesn't allow inserts" do
|
192
|
+
lambda {
|
193
|
+
subject.insert("foo", "bar")
|
194
|
+
}.should raise_error(SecretStore::ReadOnly)
|
195
|
+
end
|
196
|
+
|
197
|
+
it "doesn't allow overwrites" do
|
198
|
+
lambda {
|
199
|
+
subject.overwrite("foo", "bar")
|
200
|
+
}.should raise_error(SecretStore::ReadOnly)
|
201
|
+
end
|
202
|
+
|
203
|
+
it "doesn't allow deletes" do
|
204
|
+
lambda {
|
205
|
+
subject.delete("foo")
|
206
|
+
}.should raise_error(SecretStore::ReadOnly)
|
207
|
+
end
|
163
208
|
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.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-08-
|
12
|
+
date: 2012-08-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: gibberish
|
16
|
-
requirement: &
|
16
|
+
requirement: &2161587900 !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: *
|
24
|
+
version_requirements: *2161587900
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rake
|
27
|
-
requirement: &
|
27
|
+
requirement: &2161587480 !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: *
|
35
|
+
version_requirements: *2161587480
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rspec
|
38
|
-
requirement: &
|
38
|
+
requirement: &2161587060 !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: *
|
46
|
+
version_requirements: *2161587060
|
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: -
|
77
|
+
hash: -2410818951335814899
|
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: -
|
86
|
+
hash: -2410818951335814899
|
87
87
|
requirements: []
|
88
88
|
rubyforge_project: secret_store
|
89
89
|
rubygems_version: 1.8.15
|