kstor 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +7 -0
- data/bin/kstor +286 -0
- data/bin/kstor-srv +26 -0
- data/lib/kstor/config.rb +66 -0
- data/lib/kstor/controller/authentication.rb +79 -0
- data/lib/kstor/controller/secret.rb +201 -0
- data/lib/kstor/controller/users.rb +62 -0
- data/lib/kstor/controller.rb +80 -0
- data/lib/kstor/crypto/ascii_armor.rb +27 -0
- data/lib/kstor/crypto/keys.rb +116 -0
- data/lib/kstor/crypto.rb +240 -0
- data/lib/kstor/error.rb +85 -0
- data/lib/kstor/log.rb +56 -0
- data/lib/kstor/message.rb +132 -0
- data/lib/kstor/model.rb +437 -0
- data/lib/kstor/server.rb +51 -0
- data/lib/kstor/session.rb +80 -0
- data/lib/kstor/socket_server.rb +113 -0
- data/lib/kstor/sql_connection.rb +74 -0
- data/lib/kstor/store.rb +383 -0
- data/lib/kstor/systemd.rb +25 -0
- data/lib/kstor/version.rb +5 -0
- data/lib/kstor.rb +10 -0
- metadata +141 -0
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'kstor/log'
|
4
|
+
require 'kstor/systemd'
|
5
|
+
|
6
|
+
require 'socket'
|
7
|
+
require 'timeout'
|
8
|
+
|
9
|
+
module KStor
|
10
|
+
# Serve clients on UNIX sockets.
|
11
|
+
class SocketServer
|
12
|
+
GRACEFUL_TIMEOUT = 10
|
13
|
+
|
14
|
+
def initialize(socket_path:, nworkers:)
|
15
|
+
@path = socket_path
|
16
|
+
@nworkers = nworkers
|
17
|
+
@client_queue = Queue.new
|
18
|
+
@workers = []
|
19
|
+
end
|
20
|
+
|
21
|
+
def start
|
22
|
+
start_workers
|
23
|
+
server = Systemd.socket
|
24
|
+
Systemd.service_ready
|
25
|
+
loop do
|
26
|
+
maintain_workers
|
27
|
+
@client_queue.enq(server.accept.first)
|
28
|
+
end
|
29
|
+
rescue Interrupt
|
30
|
+
stop(server)
|
31
|
+
Log.info('socket_server: stopped.')
|
32
|
+
end
|
33
|
+
|
34
|
+
def work(client)
|
35
|
+
# Abstract method.
|
36
|
+
client.close
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def worker_run
|
42
|
+
while (client = @client_queue.deq)
|
43
|
+
Log.debug("socket_server: #{Thread.current.name} serving one client")
|
44
|
+
work(client)
|
45
|
+
Log.debug("socket_server: #{Thread.current.name} done serving client")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def stop(server)
|
50
|
+
Systemd.service_stopping
|
51
|
+
Log.debug('socket_server: closing UNIXServer')
|
52
|
+
server.close
|
53
|
+
Log.debug('socket_server: closing client queue')
|
54
|
+
@client_queue.close
|
55
|
+
Log.debug("socket_server: waiting #{GRACEFUL_TIMEOUT} seconds " \
|
56
|
+
'for workers to finish')
|
57
|
+
Timeout.timeout(GRACEFUL_TIMEOUT) { @workers.each(&:join) }
|
58
|
+
rescue Timeout::Error
|
59
|
+
Log.warn('socket_server: graceful timeout reached, killing workers')
|
60
|
+
immediate_stop(server)
|
61
|
+
end
|
62
|
+
|
63
|
+
def immediate_stop
|
64
|
+
@workers.each { |w| w.raise(Interrupt.new('abort')) }
|
65
|
+
@workers.each(&:join)
|
66
|
+
end
|
67
|
+
|
68
|
+
def start_workers
|
69
|
+
@nworkers.times do |i|
|
70
|
+
@workers << start_worker("worker-#{i}")
|
71
|
+
Log.debug("socket_server: started #{@workers.last.name}")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def start_worker(name)
|
76
|
+
thr = Thread.new { worker_run }
|
77
|
+
thr.name = name
|
78
|
+
|
79
|
+
thr
|
80
|
+
end
|
81
|
+
|
82
|
+
def maintain_workers
|
83
|
+
collect_dead_workers.each do |i, w|
|
84
|
+
name = w.name
|
85
|
+
Log.error("socket_server: #{name} died!")
|
86
|
+
rescue_worker_exception(w)
|
87
|
+
Log.info("socket_server: performing resurrection on #{name}")
|
88
|
+
@workers[i] = start_worker(name)
|
89
|
+
Log.debug("socket_server: welcome back, comrade #{name}")
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def collect_dead_workers
|
94
|
+
deads = {}
|
95
|
+
@workers.each_with_index do |w, i|
|
96
|
+
next if %w[sleep run].include?(w.status)
|
97
|
+
|
98
|
+
Log.debug("socket_server: #{w.name} status is #{w.status.inspect}")
|
99
|
+
deads[i] = w
|
100
|
+
end
|
101
|
+
|
102
|
+
deads
|
103
|
+
end
|
104
|
+
|
105
|
+
# rubocop:disable Lint/RescueException
|
106
|
+
def rescue_worker_exception(worker)
|
107
|
+
worker.join
|
108
|
+
rescue Exception => e
|
109
|
+
Log.exception(e)
|
110
|
+
end
|
111
|
+
# rubocop:enable Lint/RescueException
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'kstor/log'
|
4
|
+
require 'kstor/error'
|
5
|
+
|
6
|
+
require 'sqlite3'
|
7
|
+
|
8
|
+
module KStor
|
9
|
+
# Error: can't open database file.
|
10
|
+
class CantOpenDatabase < Error
|
11
|
+
error_code 'SQL/CANTOPEN'
|
12
|
+
error_message "Can't open database file at %s"
|
13
|
+
end
|
14
|
+
|
15
|
+
# Execute SQL commands in a per-thread SQLite connection.
|
16
|
+
class SQLConnection
|
17
|
+
def initialize(file_path)
|
18
|
+
@file_path = file_path
|
19
|
+
end
|
20
|
+
|
21
|
+
def execute(sql, *params, &)
|
22
|
+
database.execute(sql, *params, &)
|
23
|
+
end
|
24
|
+
|
25
|
+
def last_insert_row_id
|
26
|
+
database.last_insert_row_id
|
27
|
+
end
|
28
|
+
|
29
|
+
def transaction(&block)
|
30
|
+
result = nil
|
31
|
+
database.transaction do |db|
|
32
|
+
result = block.call(db)
|
33
|
+
end
|
34
|
+
|
35
|
+
result
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def database
|
41
|
+
key = :kstor_db_connection
|
42
|
+
setup_thread_connection(key)
|
43
|
+
db = Thread.current[key]
|
44
|
+
return db unless db.closed?
|
45
|
+
|
46
|
+
Log.warn('sqlite: bad connection, will re-connect')
|
47
|
+
db.close
|
48
|
+
Thread.current[k] = nil
|
49
|
+
setup_thread_connection(key)
|
50
|
+
end
|
51
|
+
|
52
|
+
def setup_thread_connection(key)
|
53
|
+
return if Thread.current[key]
|
54
|
+
|
55
|
+
Log.info(
|
56
|
+
"sqlite: initializing connection in thread #{Thread.current.name}"
|
57
|
+
)
|
58
|
+
Thread.current[key] = connect(@file_path)
|
59
|
+
Log.debug("sqlite: opened #{@file_path}")
|
60
|
+
|
61
|
+
Thread.current[key]
|
62
|
+
end
|
63
|
+
|
64
|
+
def connect(file_path)
|
65
|
+
db = SQLite3::Database.new(file_path)
|
66
|
+
db.results_as_hash = true
|
67
|
+
db.type_translation = SQLite3::Translator.new
|
68
|
+
|
69
|
+
db
|
70
|
+
rescue SQLite3::CantOpenException
|
71
|
+
raise Error.for_code('SQL/CANTOPEN', file_path)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/lib/kstor/store.rb
ADDED
@@ -0,0 +1,383 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'kstor/sql_connection'
|
4
|
+
require 'kstor/model'
|
5
|
+
require 'kstor/log'
|
6
|
+
|
7
|
+
module KStor
|
8
|
+
# Store and fetch objects in an SQLite database.
|
9
|
+
# rubocop:disable Metrics/MethodLength
|
10
|
+
class Store
|
11
|
+
def initialize(file_path)
|
12
|
+
@file_path = file_path
|
13
|
+
@db = SQLConnection.new(file_path)
|
14
|
+
@cache = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def transaction(&)
|
18
|
+
@db.transaction(&)
|
19
|
+
end
|
20
|
+
|
21
|
+
def users?
|
22
|
+
rows = @db.execute('SELECT count(*) AS n FROM users')
|
23
|
+
count = Integer(rows.first['n'])
|
24
|
+
Log.debug("store: count of users is #{count}")
|
25
|
+
|
26
|
+
count.positive?
|
27
|
+
end
|
28
|
+
|
29
|
+
def user_create(user)
|
30
|
+
@db.execute(<<-EOSQL, user.login, user.name, 'new')
|
31
|
+
INSERT INTO users (login, name, status)
|
32
|
+
VALUES (?, ?, ?)
|
33
|
+
EOSQL
|
34
|
+
user.id = @db.last_insert_row_id
|
35
|
+
Log.debug("store: stored new user #{user.login}")
|
36
|
+
params = [user.kdf_params, user.pubk, user.encrypted_privk].map(&:to_s)
|
37
|
+
return user if params.any?(&:nil?)
|
38
|
+
|
39
|
+
@db.execute(<<-EOSQL, user.id, *params)
|
40
|
+
INSERT INTO users_crypto_data (user_id, kdf_params, pubk, encrypted_privk)
|
41
|
+
VALUES (?, ?, ?, ?)
|
42
|
+
EOSQL
|
43
|
+
Log.debug("store: stored user crypto data for #{user.login}")
|
44
|
+
|
45
|
+
user
|
46
|
+
end
|
47
|
+
|
48
|
+
def user_update(user)
|
49
|
+
@db.execute(<<-EOSQL, user.name, user.status, user.id)
|
50
|
+
UPDATE users SET name = ?, status = ?
|
51
|
+
WHERE id = ?
|
52
|
+
EOSQL
|
53
|
+
params = [user.kdf_params, user.pubk, user.encrypted_privk, user.id]
|
54
|
+
@db.execute(<<-EOSQL, *params)
|
55
|
+
UPDATE users_crypto_data SET
|
56
|
+
kdf_params = ?,
|
57
|
+
pubk = ?
|
58
|
+
encrypted_params = ?
|
59
|
+
WHERE user_id = ?
|
60
|
+
EOSQL
|
61
|
+
end
|
62
|
+
|
63
|
+
def keychain_item_create(user_id, group_id, encrypted_privk)
|
64
|
+
@db.execute(<<-EOSQL, user_id, group_id, encrypted_privk.to_s)
|
65
|
+
INSERT INTO group_members (user_id, group_id, encrypted_privk)
|
66
|
+
VALUES (?, ?, ?)
|
67
|
+
EOSQL
|
68
|
+
end
|
69
|
+
|
70
|
+
def group_create(name, pubk)
|
71
|
+
@db.execute(<<-EOSQL, name, pubk.to_s)
|
72
|
+
INSERT INTO groups (name, pubk)
|
73
|
+
VALUES (?, ?)
|
74
|
+
EOSQL
|
75
|
+
@db.last_insert_row_id
|
76
|
+
end
|
77
|
+
|
78
|
+
def groups
|
79
|
+
return @cache[:groups] if @cache.key?(:groups)
|
80
|
+
|
81
|
+
Log.debug('store: loading groups')
|
82
|
+
rows = @db.execute(<<-EOSQL)
|
83
|
+
SELECT id,
|
84
|
+
name,
|
85
|
+
pubk
|
86
|
+
FROM groups
|
87
|
+
ORDER BY name
|
88
|
+
EOSQL
|
89
|
+
@cache[:groups] = rows.to_h do |r|
|
90
|
+
a = []
|
91
|
+
a << r['id']
|
92
|
+
a << Model::Group.new(
|
93
|
+
id: r['id'], name: r['name'], pubk: Crypto::PublicKey.new(r['pubk'])
|
94
|
+
)
|
95
|
+
a
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def users
|
100
|
+
return @cache[:users] if @cache.key?(:users)
|
101
|
+
|
102
|
+
Log.debug('store: loading users')
|
103
|
+
rows = @db.execute(<<-EOSQL)
|
104
|
+
SELECT u.id,
|
105
|
+
u.login,
|
106
|
+
u.name,
|
107
|
+
u.status,
|
108
|
+
c.pubk
|
109
|
+
FROM users u
|
110
|
+
LEFT JOIN users_crypto_data c ON (c.user_id = u.id)
|
111
|
+
ORDER BY u.login
|
112
|
+
EOSQL
|
113
|
+
|
114
|
+
@cache[:users] = users_from_resultset(rows)
|
115
|
+
end
|
116
|
+
|
117
|
+
# in: login
|
118
|
+
# out:
|
119
|
+
# - ID
|
120
|
+
# - name
|
121
|
+
# - status
|
122
|
+
# - public key
|
123
|
+
# - key derivation function parameters
|
124
|
+
# - encrypted private key
|
125
|
+
# - keychain: hash of:
|
126
|
+
# - group ID
|
127
|
+
# - encrypted group private key
|
128
|
+
def user_by_login(login)
|
129
|
+
Log.debug("store: loading user by login #{login.inspect}")
|
130
|
+
rows = @db.execute(<<-EOSQL, login)
|
131
|
+
SELECT u.id,
|
132
|
+
u.login,
|
133
|
+
u.name,
|
134
|
+
u.status,
|
135
|
+
c.kdf_params,
|
136
|
+
c.pubk,
|
137
|
+
c.encrypted_privk
|
138
|
+
FROM users u
|
139
|
+
LEFT JOIN users_crypto_data c ON (c.user_id = u.id)
|
140
|
+
WHERE u.login = ?
|
141
|
+
EOSQL
|
142
|
+
user_from_resultset(rows, include_crypto_data: true)
|
143
|
+
end
|
144
|
+
|
145
|
+
# in: user ID
|
146
|
+
# out:
|
147
|
+
# - ID
|
148
|
+
# - name
|
149
|
+
# - status
|
150
|
+
# - public key
|
151
|
+
# - key derivation function parameters
|
152
|
+
# - encrypted private key
|
153
|
+
def user_by_id(user_id)
|
154
|
+
Log.debug("store: loading user by ID ##{user_id}")
|
155
|
+
rows = @db.execute(<<-EOSQL, user_id)
|
156
|
+
SELECT u.id,
|
157
|
+
u.login,
|
158
|
+
u.name,
|
159
|
+
u.status,
|
160
|
+
c.kdf_params,
|
161
|
+
c.pubk,
|
162
|
+
c.encrypted_privk,
|
163
|
+
FROM users u
|
164
|
+
LEFT JOIN users_crypto_data c ON (c.user_id = u.id)
|
165
|
+
WHERE u.id = ?
|
166
|
+
EOSQL
|
167
|
+
user_from_resultset(rows, include_crypto_data: true)
|
168
|
+
end
|
169
|
+
|
170
|
+
# in: user ID
|
171
|
+
# out: array of:
|
172
|
+
# - secret ID
|
173
|
+
# - group ID common between user and secret
|
174
|
+
# - secret encrypted metadata
|
175
|
+
# - secret value and metadata author IDs
|
176
|
+
def secrets_for_user(user_id)
|
177
|
+
Log.debug("store: loading secrets for user ##{user_id}")
|
178
|
+
rows = @db.execute(<<-EOSQL, user_id)
|
179
|
+
SELECT s.id,
|
180
|
+
s.value_author_id,
|
181
|
+
s.meta_author_id,
|
182
|
+
sv.group_id,
|
183
|
+
sv.ciphertext,
|
184
|
+
sv.encrypted_metadata
|
185
|
+
FROM secrets s,
|
186
|
+
secret_values sv,
|
187
|
+
group_members gm
|
188
|
+
WHERE gm.user_id = ?
|
189
|
+
AND gm.group_id = sv.group_id
|
190
|
+
AND sv.secret_id = s.id
|
191
|
+
GROUP BY s.id
|
192
|
+
ORDER BY s.id, sv.group_id
|
193
|
+
EOSQL
|
194
|
+
|
195
|
+
rows.map { |r| secret_from_row(r) }
|
196
|
+
end
|
197
|
+
|
198
|
+
# in: secret ID, user ID
|
199
|
+
# out: encrypted value
|
200
|
+
def secret_fetch(secret_id, user_id)
|
201
|
+
Log.debug(
|
202
|
+
"store: loading secret value ##{secret_id} for user ##{user_id}"
|
203
|
+
)
|
204
|
+
rows = @db.execute(<<-EOSQL, user_id, secret_id)
|
205
|
+
SELECT s.id,
|
206
|
+
s.value_author_id,
|
207
|
+
s.meta_author_id,
|
208
|
+
sv.group_id,
|
209
|
+
sv.ciphertext,
|
210
|
+
sv.encrypted_metadata
|
211
|
+
FROM secrets s,
|
212
|
+
secret_values sv,
|
213
|
+
group_members gm
|
214
|
+
WHERE gm.user_id = ?
|
215
|
+
AND gm.group_id = sv.group_id
|
216
|
+
AND sv.secret_id = ?
|
217
|
+
AND s.id = sv.secret_id
|
218
|
+
EOSQL
|
219
|
+
return nil if rows.empty?
|
220
|
+
|
221
|
+
secret_from_row(rows.first)
|
222
|
+
end
|
223
|
+
|
224
|
+
# in:
|
225
|
+
# - user ID
|
226
|
+
# - hash of:
|
227
|
+
# - group ID
|
228
|
+
# - array of:
|
229
|
+
# - ciphertext
|
230
|
+
# - encrypted metadata
|
231
|
+
# out: secret ID
|
232
|
+
def secret_create(author_id, encrypted_data)
|
233
|
+
Log.debug("store: creating secret for user #{author_id}")
|
234
|
+
@db.execute(<<-EOSQL, author_id, author_id)
|
235
|
+
INSERT INTO secrets (value_author_id, meta_author_id) VALUES (?, ?)
|
236
|
+
EOSQL
|
237
|
+
secret_id = @db.last_insert_row_id
|
238
|
+
encrypted_data.each do |group_id, (ciphertext, encrypted_metadata)|
|
239
|
+
secret_value_create(secret_id, group_id, ciphertext, encrypted_metadata)
|
240
|
+
end
|
241
|
+
|
242
|
+
secret_id
|
243
|
+
end
|
244
|
+
|
245
|
+
def groups_for_secret(secret_id)
|
246
|
+
Log.debug("store: loading group IDs for secret #{secret_id}")
|
247
|
+
rows = @db.execute(<<-EOSQL, secret_id)
|
248
|
+
SELECT group_id
|
249
|
+
FROM secret_values
|
250
|
+
WHERE secret_id = ?
|
251
|
+
EOSQL
|
252
|
+
rows.map { |r| r['group_id'] }
|
253
|
+
end
|
254
|
+
|
255
|
+
# in: secret ID, author ID, array of [group ID, encrypted_metadata]
|
256
|
+
# out: nil
|
257
|
+
def secret_setmeta(secret_id, user_id, group_encrypted_metadata)
|
258
|
+
Log.debug("store: set metadata for secret ##{secret_id}")
|
259
|
+
@db.execute(<<-EOSQL, user_id, secret_id)
|
260
|
+
UPDATE secrets SET meta_author_id = ? WHERE id = ?
|
261
|
+
EOSQL
|
262
|
+
group_encrypted_metadata.each do |group_id, encrypted_metadata|
|
263
|
+
@db.execute(<<-EOSQL, encrypted_metadata.to_s, secret_id, group_id)
|
264
|
+
UPDATE secret_values
|
265
|
+
SET encrypted_metadata = ?
|
266
|
+
WHERE secret_id = ?
|
267
|
+
AND group_id = ?
|
268
|
+
EOSQL
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
def secret_setvalue(secret_id, user_id, group_ciphertexts)
|
273
|
+
Log.debug("store: set value for secret ##{secret_id}")
|
274
|
+
@db.execute(<<-EOSQL, user_id, secret_id)
|
275
|
+
UPDATE secrets SET value_author_id = ? WHERE id = ?
|
276
|
+
EOSQL
|
277
|
+
group_ciphertexts.each do |group_id, ciphertext|
|
278
|
+
@db.execute(<<-EOSQL, ciphertext.to_s, secret_id, group_id)
|
279
|
+
UPDATE secret_values
|
280
|
+
SET ciphertext = ?
|
281
|
+
WHERE secret_id = ?
|
282
|
+
AND group_id = ?
|
283
|
+
EOSQL
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
def secret_delete(secret_id)
|
288
|
+
Log.debug("store: delete secret ##{secret_id}")
|
289
|
+
# Will cascade to secret_values:
|
290
|
+
@db.execute(<<-EOSQL, secret_id)
|
291
|
+
DELETE FROM secrets WHERE id = ?
|
292
|
+
EOSQL
|
293
|
+
end
|
294
|
+
|
295
|
+
private
|
296
|
+
|
297
|
+
# in: secret ID, group ID, encrypted metadata and value
|
298
|
+
# out: nil
|
299
|
+
def secret_value_create(secret_id, group_id, ciphertext, encrypted_metadata)
|
300
|
+
params = [ciphertext.to_s, encrypted_metadata.to_s]
|
301
|
+
@db.execute(<<-EOSQL, secret_id, group_id, *params)
|
302
|
+
INSERT INTO secret_values (
|
303
|
+
secret_id, group_id,
|
304
|
+
ciphertext, encrypted_metadata
|
305
|
+
) VALUES (
|
306
|
+
?, ?,
|
307
|
+
?, ?
|
308
|
+
)
|
309
|
+
EOSQL
|
310
|
+
end
|
311
|
+
|
312
|
+
def users_from_resultset(rows)
|
313
|
+
users = []
|
314
|
+
while (u = user_from_resultset(rows, include_crypto_data: false))
|
315
|
+
users << u
|
316
|
+
end
|
317
|
+
|
318
|
+
users.to_h { |uu| [uu.id, uu] }
|
319
|
+
end
|
320
|
+
|
321
|
+
def user_from_resultset(rows, include_crypto_data: true)
|
322
|
+
return nil if rows.empty?
|
323
|
+
|
324
|
+
row = rows.shift
|
325
|
+
user_data = {
|
326
|
+
id: row['id'],
|
327
|
+
login: row['login'],
|
328
|
+
name: row['name'],
|
329
|
+
status: row['status'],
|
330
|
+
pubk: Crypto::PublicKey.new(row['pubk'])
|
331
|
+
}
|
332
|
+
include_crypto_data && user_crypto_data_from_resultset(user_data, row)
|
333
|
+
Model::User.new(**user_data)
|
334
|
+
end
|
335
|
+
|
336
|
+
def user_crypto_data_from_resultset(user_data, row)
|
337
|
+
user_data.merge!(
|
338
|
+
kdf_params: Crypto::KDFParams.new(row['kdf_params']),
|
339
|
+
encrypted_privk: Crypto::ArmoredValue.new(row['encrypted_privk']),
|
340
|
+
keychain: keychain_fetch(row['id'])
|
341
|
+
)
|
342
|
+
end
|
343
|
+
|
344
|
+
# in: user ID
|
345
|
+
# out: hash of:
|
346
|
+
# - group ID
|
347
|
+
# - encrypted_private key
|
348
|
+
def keychain_fetch(user_id)
|
349
|
+
rows = @db.execute(<<-EOSQL, user_id)
|
350
|
+
SELECT g.id,
|
351
|
+
g.pubk,
|
352
|
+
gm.encrypted_privk
|
353
|
+
FROM groups g,
|
354
|
+
group_members gm
|
355
|
+
WHERE gm.user_id = ?
|
356
|
+
AND gm.group_id = g.id
|
357
|
+
ORDER BY g.name
|
358
|
+
EOSQL
|
359
|
+
rows.to_h do |r|
|
360
|
+
[
|
361
|
+
r['id'],
|
362
|
+
Model::KeychainItem.new(
|
363
|
+
group_id: r['id'],
|
364
|
+
group_pubk: Crypto::PublicKey.new(r['pubk']),
|
365
|
+
encrypted_privk: Crypto::ArmoredValue.new(r['encrypted_privk'])
|
366
|
+
)
|
367
|
+
]
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
def secret_from_row(row)
|
372
|
+
Model::Secret.new(
|
373
|
+
id: row['id'],
|
374
|
+
value_author_id: row['value_author_id'],
|
375
|
+
meta_author_id: row['meta_author_id'],
|
376
|
+
group_id: row['group_id'],
|
377
|
+
ciphertext: Crypto::ArmoredValue.new(row['ciphertext']),
|
378
|
+
encrypted_metadata: Crypto::ArmoredValue.new(row['encrypted_metadata'])
|
379
|
+
)
|
380
|
+
end
|
381
|
+
end
|
382
|
+
# rubocop:enable Metrics/MethodLength
|
383
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'sd_notify'
|
4
|
+
|
5
|
+
module KStor
|
6
|
+
# Collection of methods for systemd integration.
|
7
|
+
module Systemd
|
8
|
+
class << self
|
9
|
+
def socket
|
10
|
+
listen_pid = ENV['LISTEN_PID'].to_i
|
11
|
+
return nil unless Process.pid == listen_pid
|
12
|
+
|
13
|
+
Socket.for_fd(3)
|
14
|
+
end
|
15
|
+
|
16
|
+
def service_ready
|
17
|
+
SdNotify.ready
|
18
|
+
end
|
19
|
+
|
20
|
+
def service_stopping
|
21
|
+
SdNotify.stopping
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|