trocla 0.4.0 → 0.5.0
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.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +24 -0
- data/.rspec +1 -1
- data/CHANGELOG.md +11 -0
- data/Gemfile +10 -9
- data/README.md +18 -2
- data/bin/trocla +42 -42
- data/lib/VERSION +1 -1
- data/lib/trocla/default_config.yaml +3 -4
- data/lib/trocla/encryptions/none.rb +0 -1
- data/lib/trocla/encryptions/ssl.rb +11 -11
- data/lib/trocla/encryptions.rb +12 -5
- data/lib/trocla/formats/bcrypt.rb +2 -2
- data/lib/trocla/formats/md5crypt.rb +2 -2
- data/lib/trocla/formats/mysql.rb +2 -2
- data/lib/trocla/formats/pgsql.rb +50 -3
- data/lib/trocla/formats/plain.rb +1 -3
- data/lib/trocla/formats/sha1.rb +1 -1
- data/lib/trocla/formats/sha256crypt.rb +2 -2
- data/lib/trocla/formats/sha512crypt.rb +2 -2
- data/lib/trocla/formats/ssha.rb +3 -3
- data/lib/trocla/formats/sshkey.rb +4 -8
- data/lib/trocla/formats/wireguard.rb +45 -0
- data/lib/trocla/formats/x509.rb +54 -46
- data/lib/trocla/formats.rb +15 -5
- data/lib/trocla/store.rb +16 -15
- data/lib/trocla/stores/memory.rb +17 -10
- data/lib/trocla/stores/moneta.rb +29 -19
- data/lib/trocla/stores/vault.rb +42 -14
- data/lib/trocla/stores.rb +3 -2
- data/lib/trocla/util.rb +28 -17
- data/lib/trocla/version.rb +7 -4
- data/lib/trocla.rb +47 -45
- data/spec/trocla/formats/pgsql_spec.rb +25 -0
- data/spec/trocla_spec.rb +3 -0
- data/trocla.gemspec +17 -13
- metadata +30 -14
- data/.travis.yml +0 -6
data/lib/trocla/formats/x509.rb
CHANGED
@@ -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
|
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
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
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(
|
76
|
-
|
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(
|
86
|
-
|
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
|
-
|
117
|
+
OpenSSL::Digest::SHA1.new
|
113
118
|
elsif hash == 'sha224'
|
114
|
-
|
119
|
+
OpenSSL::Digest::SHA224.new
|
115
120
|
elsif hash == 'sha2' || hash == 'sha256'
|
116
|
-
|
121
|
+
OpenSSL::Digest::SHA256.new
|
117
122
|
elsif hash == 'sha384'
|
118
|
-
|
123
|
+
OpenSSL::Digest::SHA384.new
|
119
124
|
elsif hash == 'sha512'
|
120
|
-
|
125
|
+
OpenSSL::Digest::SHA512.new
|
121
126
|
else
|
122
|
-
|
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
|
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 = [
|
157
|
+
cert.extensions = [ef.create_extension('subjectKeyIdentifier', 'hash')]
|
153
158
|
|
154
159
|
if become_ca
|
155
|
-
cert.add_extension ef.create_extension(
|
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(
|
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(
|
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(
|
164
|
-
cert.add_extension ef.create_extension(
|
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(
|
171
|
+
cert.add_extension ef.create_extension('keyUsage', ku.join(', '), true)
|
167
172
|
end
|
168
173
|
end
|
169
|
-
cert.add_extension ef.create_extension(
|
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.
|
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
|
-
[
|
197
|
-
'
|
202
|
+
[
|
203
|
+
'keyCertSign', 'cRLSign', 'nonRepudiation',
|
204
|
+
'digitalSignature', 'keyEncipherment'
|
205
|
+
]
|
198
206
|
end
|
199
207
|
end
|
data/lib/trocla/formats.rb
CHANGED
@@ -1,13 +1,19 @@
|
|
1
|
-
|
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
|
-
|
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(
|
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.
|
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
|
-
|
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(
|
54
|
+
def formats(_)
|
55
55
|
raise 'not implemented'
|
56
56
|
end
|
57
57
|
|
58
58
|
# def searches for a key
|
59
|
-
def search(
|
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(
|
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
|
data/lib/trocla/stores/memory.rb
CHANGED
@@ -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
|
-
|
5
|
-
|
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
|
-
|
18
|
-
|
19
|
-
|
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
|
-
|
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,
|
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
|
-
|
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
|
-
|
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) && \
|
data/lib/trocla/stores/moneta.rb
CHANGED
@@ -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
|
-
|
5
|
-
|
4
|
+
|
5
|
+
def initialize(config, trocla)
|
6
|
+
super(config, trocla)
|
6
7
|
require 'moneta'
|
7
8
|
# load expire support by default
|
8
|
-
adapter_options = {
|
9
|
-
|
10
|
-
|
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
|
-
|
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(
|
48
|
-
|
49
|
-
|
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
|
-
|
56
|
-
|
57
|
-
|
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
|
-
|
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
|
data/lib/trocla/stores/vault.rb
CHANGED
@@ -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
|
-
|
5
|
-
|
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(
|
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
|
-
|
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[
|
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.
|
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)
|