trocla 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,8 +1,9 @@
1
1
  require 'openssl'
2
+
3
+ # Trocla::Formats::X509
2
4
  class Trocla::Formats::X509 < Trocla::Formats::Base
3
5
  expensive true
4
6
  def format(plain_password,options={})
5
-
6
7
  if plain_password.match(/-----BEGIN RSA PRIVATE KEY-----.*-----END RSA PRIVATE KEY-----.*-----BEGIN CERTIFICATE-----.*-----END CERTIFICATE-----/m)
7
8
  # just an import, don't generate any new keys
8
9
  return plain_password
@@ -11,17 +12,17 @@ class Trocla::Formats::X509 < Trocla::Formats::Base
11
12
  cn = nil
12
13
  if options['subject']
13
14
  subject = options['subject']
14
- if cna = OpenSSL::X509::Name.parse(subject).to_a.find{|e| e[0] == 'CN' }
15
+ if cna = OpenSSL::X509::Name.parse(subject).to_a.find { |e| e[0] == 'CN' }
15
16
  cn = cna[1]
16
17
  end
17
18
  elsif options['CN']
18
19
  subject = ''
19
20
  cn = options['CN']
20
- ['C','ST','L','O','OU','CN','emailAddress'].each do |field|
21
+ ['C', 'ST', 'L', 'O', 'OU', 'CN', 'emailAddress'].each do |field|
21
22
  subject << "/#{field}=#{options[field]}" if options[field]
22
23
  end
23
24
  else
24
- raise "You need to pass \"subject\" or \"CN\" as an option to use this format"
25
+ raise 'You need to pass "subject" or "CN" as an option to use this format'
25
26
  end
26
27
  hash = options['hash'] || 'sha2'
27
28
  sign_with = options['ca']
@@ -33,17 +34,17 @@ class Trocla::Formats::X509 < Trocla::Formats::Base
33
34
  key_usages = Array(key_usages) if key_usages
34
35
 
35
36
  altnames = if become_ca || (an = options['altnames']) && Array(an).empty?
36
- []
37
- else
38
- # ensure that we have the CN with us, but only if it
39
- # it's like a hostname.
40
- # This might have to be improved.
41
- if cn.include?(' ')
42
- Array(an).collect { |v| "DNS:#{v}" }.join(', ')
43
- else
44
- (["DNS:#{cn}"] + Array(an).collect { |v| "DNS:#{v}" }).uniq.join(', ')
45
- end
46
- end
37
+ []
38
+ else
39
+ # ensure that we have the CN with us, but only if it
40
+ # it's like a hostname.
41
+ # This might have to be improved.
42
+ if cn.include?(' ')
43
+ Array(an).collect { |v| "DNS:#{v}" }.join(', ')
44
+ else
45
+ (["DNS:#{cn}"] + Array(an).collect { |v| "DNS:#{v}" }).uniq.join(', ')
46
+ end
47
+ end
47
48
 
48
49
  begin
49
50
  key = mkkey(keysize)
@@ -55,7 +56,7 @@ class Trocla::Formats::X509 < Trocla::Formats::Base
55
56
  cert = nil
56
57
  if sign_with # certificate signed with CA
57
58
  begin
58
- ca_str = trocla.get_password(sign_with,'x509')
59
+ ca_str = trocla.get_password(sign_with, 'x509')
59
60
  ca = OpenSSL::X509::Certificate.new(ca_str)
60
61
  cakey = OpenSSL::PKey::RSA.new(ca_str)
61
62
  caserial = getserial(sign_with)
@@ -72,8 +73,10 @@ class Trocla::Formats::X509 < Trocla::Formats::Base
72
73
  end
73
74
 
74
75
  begin
75
- cert = mkcert(caserial, request.subject, ca, request.public_key, days,
76
- altnames, key_usages, name_constraints, become_ca)
76
+ cert = mkcert(
77
+ caserial, request.subject, ca, request.public_key, days,
78
+ altnames, key_usages, name_constraints, become_ca
79
+ )
77
80
  cert.sign(cakey, signature(hash))
78
81
  addserial(sign_with, caserial)
79
82
  rescue Exception => e
@@ -82,8 +85,10 @@ class Trocla::Formats::X509 < Trocla::Formats::Base
82
85
  else # self-signed certificate
83
86
  begin
84
87
  subj = OpenSSL::X509::Name.parse(subject)
85
- cert = mkcert(getserial(subj), subj, nil, key.public_key, days,
86
- altnames, key_usages, name_constraints, become_ca)
88
+ cert = mkcert(
89
+ getserial(subj), subj, nil, key.public_key, days,
90
+ altnames, key_usages, name_constraints, become_ca
91
+ )
87
92
  cert.sign(key, signature(hash))
88
93
  rescue Exception => e
89
94
  raise "Self-signed certificate #{subject} creation failed: #{e.message}"
@@ -92,7 +97,7 @@ class Trocla::Formats::X509 < Trocla::Formats::Base
92
97
  key.to_pem + cert.to_pem
93
98
  end
94
99
 
95
- def render(output,render_options={})
100
+ def render(output, render_options = {})
96
101
  if render_options['keyonly']
97
102
  OpenSSL::PKey::RSA.new(output).to_pem
98
103
  elsif render_options['certonly']
@@ -100,26 +105,26 @@ class Trocla::Formats::X509 < Trocla::Formats::Base
100
105
  elsif render_options['publickeyonly']
101
106
  OpenSSL::PKey::RSA.new(output).public_key.to_pem
102
107
  else
103
- super(output,render_options)
108
+ super(output, render_options)
104
109
  end
105
110
  end
106
111
 
107
112
  private
108
- # nice help: https://gist.github.com/mitfik/1922961
109
113
 
114
+ # nice help: https://gist.github.com/mitfik/1922961
110
115
  def signature(hash = 'sha2')
111
116
  if hash == 'sha1'
112
- OpenSSL::Digest::SHA1.new
117
+ OpenSSL::Digest::SHA1.new
113
118
  elsif hash == 'sha224'
114
- OpenSSL::Digest::SHA224.new
119
+ OpenSSL::Digest::SHA224.new
115
120
  elsif hash == 'sha2' || hash == 'sha256'
116
- OpenSSL::Digest::SHA256.new
121
+ OpenSSL::Digest::SHA256.new
117
122
  elsif hash == 'sha384'
118
- OpenSSL::Digest::SHA384.new
123
+ OpenSSL::Digest::SHA384.new
119
124
  elsif hash == 'sha512'
120
- OpenSSL::Digest::SHA512.new
125
+ OpenSSL::Digest::SHA512.new
121
126
  else
122
- raise "Unrecognized hash: #{hash}"
127
+ raise "Unrecognized hash: #{hash}"
123
128
  end
124
129
  end
125
130
 
@@ -127,7 +132,7 @@ class Trocla::Formats::X509 < Trocla::Formats::Base
127
132
  OpenSSL::PKey::RSA.generate(len)
128
133
  end
129
134
 
130
- def mkreq(subject,public_key)
135
+ def mkreq(subject, public_key)
131
136
  request = OpenSSL::X509::Request.new
132
137
  request.subject = subject
133
138
  request.public_key = public_key
@@ -135,9 +140,9 @@ class Trocla::Formats::X509 < Trocla::Formats::Base
135
140
  request
136
141
  end
137
142
 
138
- def mkcert(serial,subject,issuer,public_key,days,altnames, key_usages = nil, name_constraints = [], become_ca = false)
143
+ def mkcert(serial, subject, issuer, public_key, days, altnames, key_usages = nil, name_constraints = [], become_ca = false)
139
144
  cert = OpenSSL::X509::Certificate.new
140
- issuer = cert if issuer == nil
145
+ issuer = cert if issuer.nil?
141
146
  cert.subject = subject
142
147
  cert.issuer = issuer.subject
143
148
  cert.not_before = Time.now
@@ -149,36 +154,36 @@ class Trocla::Formats::X509 < Trocla::Formats::Base
149
154
  ef = OpenSSL::X509::ExtensionFactory.new
150
155
  ef.subject_certificate = cert
151
156
  ef.issuer_certificate = issuer
152
- cert.extensions = [ ef.create_extension("subjectKeyIdentifier", "hash") ]
157
+ cert.extensions = [ef.create_extension('subjectKeyIdentifier', 'hash')]
153
158
 
154
159
  if become_ca
155
- cert.add_extension ef.create_extension("basicConstraints","CA:TRUE", true)
160
+ cert.add_extension ef.create_extension('basicConstraints', 'CA:TRUE', true)
156
161
  unless (ku = key_usages || ca_key_usages).empty?
157
- cert.add_extension ef.create_extension("keyUsage", ku.join(', '), true)
162
+ cert.add_extension ef.create_extension('keyUsage', ku.join(', '), true)
158
163
  end
159
164
  if name_constraints && !name_constraints.empty?
160
- cert.add_extension ef.create_extension("nameConstraints","permitted;DNS:#{name_constraints.join(',permitted;DNS:')}",true)
165
+ cert.add_extension ef.create_extension('nameConstraints', "permitted;DNS:#{name_constraints.join(',permitted;DNS:')}", true)
161
166
  end
162
167
  else
163
- cert.add_extension ef.create_extension("subjectAltName", altnames, true) unless altnames.empty?
164
- cert.add_extension ef.create_extension("basicConstraints","CA:FALSE", true)
168
+ cert.add_extension ef.create_extension('subjectAltName', altnames, true) unless altnames.empty?
169
+ cert.add_extension ef.create_extension('basicConstraints', 'CA:FALSE', true)
165
170
  unless (ku = key_usages || cert_key_usages).empty?
166
- cert.add_extension ef.create_extension("keyUsage", ku.join(', '), true)
171
+ cert.add_extension ef.create_extension('keyUsage', ku.join(', '), true)
167
172
  end
168
173
  end
169
- cert.add_extension ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always")
174
+ cert.add_extension ef.create_extension('authorityKeyIdentifier', 'keyid:always,issuer:always')
170
175
 
171
176
  cert
172
177
  end
173
178
 
174
179
  def getserial(ca)
175
- newser = Trocla::Util.random_str(20,'hexadecimal').to_i(16)
180
+ newser = Trocla::Util.random_str(20, 'hexadecimal').to_i(16)
176
181
  all_serials(ca).include?(newser) ? getserial(ca) : newser
177
182
  end
178
183
 
179
184
  def all_serials(ca)
180
- if allser = trocla.get_password("#{ca}_all_serials",'plain')
181
- YAML.load(allser)
185
+ if allser = trocla.get_password("#{ca}_all_serials", 'plain')
186
+ YAML.safe_load(allser)
182
187
  else
183
188
  []
184
189
  end
@@ -186,14 +191,17 @@ class Trocla::Formats::X509 < Trocla::Formats::Base
186
191
 
187
192
  def addserial(ca,serial)
188
193
  serials = all_serials(ca) << serial
189
- trocla.set_password("#{ca}_all_serials",'plain',YAML.dump(serials))
194
+ trocla.set_password("#{ca}_all_serials", 'plain', YAML.dump(serials))
190
195
  end
191
196
 
192
197
  def cert_key_usages
193
198
  ['nonRepudiation', 'digitalSignature', 'keyEncipherment']
194
199
  end
200
+
195
201
  def ca_key_usages
196
- ['keyCertSign', 'cRLSign', 'nonRepudiation',
197
- 'digitalSignature', 'keyEncipherment' ]
202
+ [
203
+ 'keyCertSign', 'cRLSign', 'nonRepudiation',
204
+ 'digitalSignature', 'keyEncipherment'
205
+ ]
198
206
  end
199
207
  end
@@ -1,13 +1,19 @@
1
- class Trocla::Formats
1
+ # frozen_string_literal: true
2
2
 
3
+ # Trocla::Formats
4
+ class Trocla::Formats
5
+ # Base
3
6
  class Base
4
7
  attr_reader :trocla
8
+
5
9
  def initialize(trocla)
6
10
  @trocla = trocla
7
11
  end
8
- def render(output,render_options={})
12
+
13
+ def render(output, render_options = {})
9
14
  output
10
15
  end
16
+
11
17
  def expensive?
12
18
  self.class.expensive?
13
19
  end
@@ -15,6 +21,7 @@ class Trocla::Formats
15
21
  def expensive(is_expensive)
16
22
  @expensive = is_expensive
17
23
  end
24
+
18
25
  def expensive?
19
26
  @expensive == true
20
27
  end
@@ -27,7 +34,9 @@ class Trocla::Formats
27
34
  end
28
35
 
29
36
  def all
30
- Dir[File.expand_path(File.join(File.dirname(__FILE__),'formats','*.rb'))].collect{|f| File.basename(f,'.rb').downcase }
37
+ Dir[File.expand_path(
38
+ File.join(File.dirname(__FILE__), 'formats', '*.rb')
39
+ )].collect { |f| File.basename(f, '.rb').downcase }
31
40
  end
32
41
 
33
42
  def available?(format)
@@ -35,10 +44,11 @@ class Trocla::Formats
35
44
  end
36
45
 
37
46
  private
47
+
38
48
  def formats
39
49
  @@formats ||= Hash.new do |hash, format|
40
50
  format = format.downcase
41
- if File.exists?(path(format))
51
+ if File.exist?(path(format))
42
52
  require "trocla/formats/#{format}"
43
53
  hash[format] = (eval "Trocla::Formats::#{format.capitalize}")
44
54
  else
@@ -48,7 +58,7 @@ class Trocla::Formats
48
58
  end
49
59
 
50
60
  def path(format)
51
- File.expand_path(File.join(File.dirname(__FILE__),'formats',"#{format}.rb"))
61
+ File.expand_path(File.join(File.dirname(__FILE__), 'formats', "#{format}.rb"))
52
62
  end
53
63
  end
54
64
  end
data/lib/trocla/store.rb CHANGED
@@ -1,7 +1,8 @@
1
1
  # implements the default store behavior
2
2
  class Trocla::Store
3
3
  attr_reader :store_config, :trocla
4
- def initialize(config,trocla)
4
+
5
+ def initialize(config, trocla)
5
6
  @store_config = config
6
7
  @trocla = trocla
7
8
  end
@@ -9,14 +10,13 @@ class Trocla::Store
9
10
  # closes the store
10
11
  # when called do whatever "closes" your
11
12
  # store, e.g. close database connections.
12
- def close
13
- end
13
+ def close; end
14
14
 
15
15
  # should return value for key & format
16
16
  # returns nil if nothing or a nil value
17
17
  # was found.
18
18
  # If a key is expired it must return nil.
19
- def get(key,format)
19
+ def get(key, format)
20
20
  raise 'not implemented'
21
21
  end
22
22
 
@@ -33,11 +33,11 @@ class Trocla::Store
33
33
  # amount of seconds a key can live with.
34
34
  # This mechanism is expected to be
35
35
  # be implemented by the backend.
36
- def set(key,format,value,options={})
36
+ def set(key, format, value, options = {})
37
37
  if format == 'plain'
38
- set_plain(key,value,options)
38
+ set_plain(key, value, options)
39
39
  else
40
- set_format(key,format,value,options)
40
+ set_format(key, format, value, options)
41
41
  end
42
42
  end
43
43
 
@@ -46,30 +46,31 @@ class Trocla::Store
46
46
  # returns value of format or hash of
47
47
  # format => value # if everything is
48
48
  # deleted.
49
- def delete(key,format=nil)
50
- format.nil? ? (delete_all(key)||{}) : delete_format(key,format)
49
+ def delete(key, format = nil)
50
+ format.nil? ? (delete_all(key) || {}) : delete_format(key, format)
51
51
  end
52
52
 
53
53
  # returns all formats for a key
54
- def formats(key)
54
+ def formats(_)
55
55
  raise 'not implemented'
56
56
  end
57
57
 
58
58
  # def searches for a key
59
- def search(key)
59
+ def search(_)
60
60
  raise 'not implemented'
61
61
  end
62
62
 
63
63
  private
64
+
64
65
  # sets a new plain value
65
66
  # *must* invalidate all
66
67
  # other formats
67
- def set_plain(key,value,options)
68
+ def set_plain(key, value, options)
68
69
  raise 'not implemented'
69
70
  end
70
71
 
71
72
  # sets a value of a format
72
- def set_format(key,format,value,options)
73
+ def set_format(key, format, value, options)
73
74
  raise 'not implemented'
74
75
  end
75
76
 
@@ -77,14 +78,14 @@ class Trocla::Store
77
78
  # and returns a hash with all
78
79
  # formats and values
79
80
  # or nil if nothing is found
80
- def delete_all(key)
81
+ def delete_all(_)
81
82
  raise 'not implemented'
82
83
  end
83
84
 
84
85
  # deletes the value of the passed
85
86
  # key & format and returns the
86
87
  # value.
87
- def delete_format(key,format)
88
+ def delete_format(key, format)
88
89
  raise 'not implemented'
89
90
  end
90
91
  end
@@ -1,12 +1,13 @@
1
1
  # a simple in memory store just as an example
2
2
  class Trocla::Stores::Memory < Trocla::Store
3
3
  attr_reader :memory
4
- def initialize(config,trocla)
5
- super(config,trocla)
4
+
5
+ def initialize(config, trocla)
6
+ super(config, trocla)
6
7
  @memory = Hash.new({})
7
8
  end
8
9
 
9
- def get(key,format)
10
+ def get(key, format)
10
11
  unless expired?(key)
11
12
  memory[key][format]
12
13
  else
@@ -14,9 +15,10 @@ class Trocla::Stores::Memory < Trocla::Store
14
15
  nil
15
16
  end
16
17
  end
17
- def set(key,format,value,options={})
18
- super(key,format,value,options)
19
- set_expires(key,options['expires'])
18
+
19
+ def set(key, format, value, options = {})
20
+ super(key, format, value, options)
21
+ set_expires(key, options['expires'])
20
22
  end
21
23
 
22
24
  def formats(key)
@@ -29,25 +31,29 @@ class Trocla::Stores::Memory < Trocla::Store
29
31
  end
30
32
 
31
33
  private
32
- def set_plain(key,value,options)
34
+
35
+ def set_plain(key, value, _)
33
36
  memory[key] = { 'plain' => value }
34
37
  end
35
38
 
36
- def set_format(key,format,value,options)
39
+ def set_format(key, format, value, _)
37
40
  memory[key].merge!({ format => value })
38
41
  end
39
42
 
40
43
  def delete_all(key)
41
44
  memory.delete(key)
42
45
  end
43
- def delete_format(key,format)
46
+
47
+ def delete_format(key, format)
44
48
  old_val = (h = memory[key]).delete(format)
45
49
  h.empty? ? memory.delete(key) : memory[key] = h
46
50
  set_expires(key,nil)
47
51
  old_val
48
52
  end
53
+
49
54
  private
50
- def set_expires(key,expires)
55
+
56
+ def set_expires(key, expires)
51
57
  expires = memory[key]['_expires'] if expires.nil?
52
58
  if expires && expires > 0
53
59
  memory[key]['_expires'] = expires
@@ -57,6 +63,7 @@ class Trocla::Stores::Memory < Trocla::Store
57
63
  memory[key].delete('_expires_at')
58
64
  end
59
65
  end
66
+
60
67
  def expired?(key)
61
68
  memory.key?(key) &&
62
69
  (a = memory[key]['_expires_at']).is_a?(Time) && \
@@ -1,20 +1,22 @@
1
1
  # the default moneta based store
2
2
  class Trocla::Stores::Moneta < Trocla::Store
3
3
  attr_reader :moneta
4
- def initialize(config,trocla)
5
- super(config,trocla)
4
+
5
+ def initialize(config, trocla)
6
+ super(config, trocla)
6
7
  require 'moneta'
7
8
  # load expire support by default
8
- adapter_options = { :expires => true }.merge(
9
- store_config['adapter_options']||{})
10
- @moneta = Moneta.new(store_config['adapter'],adapter_options)
9
+ adapter_options = {
10
+ :expires => true
11
+ }.merge(store_config['adapter_options'] || {})
12
+ @moneta = Moneta.new(store_config['adapter'], adapter_options)
11
13
  end
12
14
 
13
15
  def close
14
16
  moneta.close
15
17
  end
16
18
 
17
- def get(key,format)
19
+ def get(key, format)
18
20
  moneta.fetch(key, {})[format]
19
21
  end
20
22
 
@@ -25,14 +27,16 @@ class Trocla::Stores::Moneta < Trocla::Store
25
27
 
26
28
  def search(key)
27
29
  raise 'The search option is not available for any adapter other than Sequel or YAML' unless store_config['adapter'] == :Sequel || store_config['adapter'] == :YAML
30
+
28
31
  r = search_keys(key)
29
32
  r.empty? ? nil : r
30
33
  end
31
34
 
32
35
  private
33
- def set_plain(key,value,options)
36
+
37
+ def set_plain(key, value, options)
34
38
  h = { 'plain' => value }
35
- mo = moneta_options(key,options)
39
+ mo = moneta_options(key, options)
36
40
  if options['expires'] && options['expires'] > 0
37
41
  h['_expires'] = options['expires']
38
42
  else
@@ -40,24 +44,28 @@ class Trocla::Stores::Moneta < Trocla::Store
40
44
  # expires if nothing is set.
41
45
  mo[:expires] = false
42
46
  end
43
- moneta.store(key,h,mo)
47
+ moneta.store(key, h, mo)
44
48
  end
45
49
 
46
- def set_format(key,format,value,options)
47
- moneta.store(key,
48
- moneta.fetch(key,{}).merge({ format => value }),
49
- moneta_options(key,options))
50
+ def set_format(key, format, value, options)
51
+ moneta.store(
52
+ key,
53
+ moneta.fetch(key, {}).merge({ format => value }),
54
+ moneta_options(key, options)
55
+ )
50
56
  end
51
57
 
52
58
  def delete_all(key)
53
59
  moneta.delete(key)
54
60
  end
55
- def delete_format(key,format)
56
- old_val = (h = moneta.fetch(key,{})).delete(format)
57
- h.empty? ? moneta.delete(key) : moneta.store(key,h,moneta_options(key,{}))
61
+
62
+ def delete_format(key, format)
63
+ old_val = (h = moneta.fetch(key, {})).delete(format)
64
+ h.empty? ? moneta.delete(key) : moneta.store(key, h, moneta_options(key, {}))
58
65
  old_val
59
66
  end
60
- def moneta_options(key,options)
67
+
68
+ def moneta_options(key, options)
61
69
  res = {}
62
70
  if options.key?('expires')
63
71
  res[:expires] = options['expires']
@@ -66,11 +74,13 @@ class Trocla::Stores::Moneta < Trocla::Store
66
74
  end
67
75
  res
68
76
  end
77
+
69
78
  def search_keys(key)
70
- _moneta = Moneta.new(store_config['adapter'], (store_config['adapter_options']||{}).merge({ :expires => false }))
79
+ _moneta = Moneta.new(store_config['adapter'], (store_config['adapter_options'] || {}).merge({ :expires => false }))
71
80
  a = []
81
+
72
82
  if store_config['adapter'] == :Sequel
73
- keys = _moneta.adapter.backend[:trocla].select_order_map {from_base64(:k)}
83
+ keys = _moneta.adapter.backend[:trocla].select_order_map { from_base64(:k) }
74
84
  elsif store_config['adapter'] == :YAML
75
85
  keys = _moneta.adapter.backend.transaction(true) { _moneta.adapter.backend.roots }
76
86
  end
@@ -1,18 +1,19 @@
1
1
  # the default vault based store
2
2
  class Trocla::Stores::Vault < Trocla::Store
3
- attr_reader :vault, :mount
4
- def initialize(config,trocla)
5
- super(config,trocla)
3
+ attr_reader :vault, :mount, :destroy
4
+
5
+ def initialize(config, trocla)
6
+ super(config, trocla)
6
7
  require 'vault'
7
8
  @mount = (config.delete(:mount) || 'kv')
9
+ @destroy = (config.delete(:destroy) || false)
8
10
  # load expire support by default
9
11
  @vault = Vault::Client.new(config)
10
12
  end
11
13
 
12
- def close
13
- end
14
+ def close; end
14
15
 
15
- def get(key,format)
16
+ def get(key, format)
16
17
  read(key)[format.to_sym]
17
18
  end
18
19
 
@@ -20,31 +21,58 @@ class Trocla::Stores::Vault < Trocla::Store
20
21
  read(key).keys
21
22
  end
22
23
 
24
+ def search(key)
25
+ arr = key.split('/')
26
+ regexp = Regexp.new(arr.pop(1)[0].to_s)
27
+ path = arr.join('/')
28
+ list = vault.kv(mount).list(path)
29
+ list.map! do |l|
30
+ if regexp.match(l)
31
+ path.empty? ? l : [path, l].join('/')
32
+ end
33
+ end
34
+ list.compact
35
+ end
36
+
23
37
  private
38
+
24
39
  def read(key)
25
40
  k = vault.kv(mount).read(key)
26
41
  k.nil? ? {} : k.data
27
42
  end
28
43
 
29
- def write(key, value)
44
+ def write(key, value, options = {})
45
+ vault.kv(mount).write_metadata(key, convert_metadata(options)) unless options.empty?
30
46
  vault.kv(mount).write(key, value)
31
47
  end
32
48
 
33
- def set_plain(key,value,options)
34
- set_format(key,'plain',value,options)
49
+ def set_plain(key, value, options)
50
+ set_format(key, 'plain', value, options)
35
51
  end
36
52
 
37
- def set_format(key,format,value,options)
38
- write(key, read(key).merge({format.to_sym => value}))
53
+ def set_format(key, format, value, options)
54
+ write(
55
+ key,
56
+ read(key).merge({ format.to_sym => value }),
57
+ options
58
+ )
39
59
  end
40
60
 
41
61
  def delete_all(key)
42
- vault.kv(mount).delete(key)
62
+ destroy ? vault.kv(mount).destroy(key) : vault.kv(mount).delete(key)
43
63
  end
44
64
 
45
- def delete_format(key,format)
65
+ def delete_format(key, format)
46
66
  old = read(key)
47
- write(key, old.reject { |k,v| k == format.to_sym })
67
+ new = old.reject { |k, _| k == format.to_sym }
68
+ new.empty? ? delete_all(key) : write(key, new)
48
69
  old[format.to_sym]
49
70
  end
71
+
72
+ def convert_metadata(metadatas)
73
+ metadatas.transform_keys!(&:to_sym)
74
+ metadatas[:delete_version_after] = metadatas.delete(:expire) if metadatas[:expire]
75
+ %i[random profiles expires length].each { |k| metadatas.delete(k) }
76
+ metadatas
77
+ end
50
78
  end
data/lib/trocla/stores.rb CHANGED
@@ -7,7 +7,7 @@ class Trocla::Stores
7
7
  end
8
8
 
9
9
  def all
10
- @all ||= Dir[ path '*' ].collect do |store|
10
+ @all ||= Dir[path '*'].collect do |store|
11
11
  File.basename(store, '.rb').downcase
12
12
  end
13
13
  end
@@ -17,10 +17,11 @@ class Trocla::Stores
17
17
  end
18
18
 
19
19
  private
20
+
20
21
  def stores
21
22
  @@stores ||= Hash.new do |hash, store|
22
23
  store = store.to_s.downcase
23
- if File.exists?(path(store))
24
+ if File.exist?(path(store))
24
25
  require "trocla/stores/#{store}"
25
26
  class_name = "Trocla::Stores::#{store.capitalize}"
26
27
  hash[store] = (eval class_name)