kstor 0.4.3 → 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/README.md +6 -2
- data/bin/kstor +48 -40
- data/bin/kstor-srv +2 -2
- data/lib/kstor/config.rb +2 -1
- data/lib/kstor/controller/authentication.rb +25 -4
- data/lib/kstor/controller/base.rb +61 -0
- data/lib/kstor/controller/request_handler.rb +84 -16
- data/lib/kstor/controller/secret.rb +63 -64
- data/lib/kstor/controller/users.rb +11 -22
- data/lib/kstor/controller.rb +3 -3
- data/lib/kstor/crypto/armored_value.rb +82 -0
- data/lib/kstor/crypto/keys.rb +9 -41
- data/lib/kstor/crypto.rb +0 -1
- data/lib/kstor/error.rb +36 -3
- data/lib/kstor/log/simple_logger.rb +83 -0
- data/lib/kstor/log/systemd_logger.rb +22 -0
- data/lib/kstor/log.rb +53 -4
- data/lib/kstor/message/base.rb +223 -0
- data/lib/kstor/message/error.rb +15 -0
- data/lib/kstor/message/group_create.rb +14 -0
- data/lib/kstor/message/group_created.rb +16 -0
- data/lib/kstor/message/ping.rb +14 -0
- data/lib/kstor/message/pong.rb +14 -0
- data/lib/kstor/message/secret_create.rb +16 -0
- data/lib/kstor/message/secret_created.rb +14 -0
- data/lib/kstor/message/secret_delete.rb +14 -0
- data/lib/kstor/message/secret_deleted.rb +14 -0
- data/lib/kstor/message/secret_list.rb +14 -0
- data/lib/kstor/message/secret_search.rb +14 -0
- data/lib/kstor/message/secret_unlock.rb +14 -0
- data/lib/kstor/message/secret_update_meta.rb +15 -0
- data/lib/kstor/message/secret_update_value.rb +15 -0
- data/lib/kstor/message/secret_updated.rb +14 -0
- data/lib/kstor/message/secret_value.rb +35 -0
- data/lib/kstor/message.rb +26 -113
- data/lib/kstor/model.rb +42 -25
- data/lib/kstor/server.rb +15 -3
- data/lib/kstor/session.rb +34 -2
- data/lib/kstor/socket_server.rb +20 -1
- data/lib/kstor/sql_connection.rb +16 -1
- data/lib/kstor/store.rb +182 -72
- data/lib/kstor/systemd.rb +5 -0
- data/lib/kstor/version.rb +2 -1
- data/lib/kstor.rb +1 -1
- metadata +25 -3
data/lib/kstor/log.rb
CHANGED
@@ -1,56 +1,105 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'journald/logger'
|
4
|
-
|
5
3
|
module KStor
|
6
4
|
# Central logging to systemd-journald.
|
7
5
|
module Log
|
8
6
|
class << self
|
7
|
+
# Log an exception.
|
8
|
+
#
|
9
|
+
# @param exc [Exception] exception to log.
|
9
10
|
def exception(exc)
|
10
11
|
logger.exception(exc)
|
11
12
|
end
|
12
13
|
|
14
|
+
# Log a debug message.
|
15
|
+
#
|
16
|
+
# @param msg [String] message
|
13
17
|
def debug(msg)
|
14
18
|
logger.debug(msg)
|
15
19
|
end
|
16
20
|
|
21
|
+
# Log an informative message.
|
22
|
+
#
|
23
|
+
# @param msg [String] message
|
17
24
|
def info(msg)
|
18
25
|
logger.info(msg)
|
19
26
|
end
|
20
27
|
|
28
|
+
# Log a notice message.
|
29
|
+
#
|
30
|
+
# @param msg [String] message
|
21
31
|
def notice(msg)
|
22
32
|
logger.notice(msg)
|
23
33
|
end
|
24
34
|
|
35
|
+
# Log a warning.
|
36
|
+
#
|
37
|
+
# @param msg [String] message
|
25
38
|
def warn(msg)
|
26
39
|
logger.warn(msg)
|
27
40
|
end
|
28
41
|
|
42
|
+
# Log an error message.
|
43
|
+
#
|
44
|
+
# @param msg [String] message
|
29
45
|
def error(msg)
|
30
46
|
logger.error(msg)
|
31
47
|
end
|
32
48
|
|
49
|
+
# Log a critical error message.
|
50
|
+
#
|
51
|
+
# @param msg [String] message
|
33
52
|
def critical(msg)
|
34
53
|
logger.critical(msg)
|
35
54
|
end
|
36
55
|
|
56
|
+
# Log an alert message.
|
57
|
+
#
|
58
|
+
# @param msg [String] message
|
37
59
|
def alert(msg)
|
38
60
|
logger.alert(msg)
|
39
61
|
end
|
40
62
|
|
63
|
+
# Log an emergency message.
|
64
|
+
#
|
65
|
+
# @param msg [String] message
|
41
66
|
def emergency(msg)
|
42
67
|
logger.emergency(msg)
|
43
68
|
end
|
44
69
|
|
70
|
+
# Set reporting level.
|
45
71
|
def reporting_level=(lvl)
|
46
|
-
|
72
|
+
lvl = level_str_to_int(lvl) if lvl.respond_to?(:to_str)
|
73
|
+
|
74
|
+
if logger.respond_to?(:min_priority)
|
75
|
+
logger.min_priority = lvl
|
76
|
+
else
|
77
|
+
logger.level = lvl
|
78
|
+
end
|
47
79
|
end
|
48
80
|
|
49
81
|
private
|
50
82
|
|
83
|
+
def level_str_to_int(value)
|
84
|
+
case value
|
85
|
+
when /^debug$/i then const_get(:DEBUG)
|
86
|
+
when /^info$/i then const_get(:INFO)
|
87
|
+
when /^warn$/i then const_get(:WARN)
|
88
|
+
when /^error$/i then const_get(:ERROR)
|
89
|
+
else
|
90
|
+
raise "Unknown log level #{value.inspect}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
51
94
|
def logger
|
52
|
-
@logger ||=
|
95
|
+
@logger ||= create_logger
|
53
96
|
end
|
54
97
|
end
|
55
98
|
end
|
56
99
|
end
|
100
|
+
|
101
|
+
if ENV['INIT_IS_SYSTEMD']
|
102
|
+
require 'kstor/log/systemd_logger'
|
103
|
+
else
|
104
|
+
require 'kstor/log/simple_logger'
|
105
|
+
end
|
@@ -0,0 +1,223 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module KStor
|
4
|
+
# Units of communication between server and clients.
|
5
|
+
module Message
|
6
|
+
# A user request or response.
|
7
|
+
class Base
|
8
|
+
attr_reader :args
|
9
|
+
|
10
|
+
# Create new message.
|
11
|
+
#
|
12
|
+
# @param args [Hash] message arguments
|
13
|
+
# @param auth [Hash] authentication data
|
14
|
+
# @return [KStor::Message::Base] new message
|
15
|
+
def initialize(args, auth = {})
|
16
|
+
@args = check_args!(args)
|
17
|
+
@auth = check_auth!(auth.compact)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Message type.
|
21
|
+
def type
|
22
|
+
self.class.type
|
23
|
+
end
|
24
|
+
|
25
|
+
# User login
|
26
|
+
def login
|
27
|
+
@auth[:login]
|
28
|
+
end
|
29
|
+
|
30
|
+
# User password
|
31
|
+
def password
|
32
|
+
@auth[:password]
|
33
|
+
end
|
34
|
+
|
35
|
+
# User session ID
|
36
|
+
def session_id
|
37
|
+
@auth[:session_id]
|
38
|
+
end
|
39
|
+
|
40
|
+
# True if this message is a request.
|
41
|
+
def request?
|
42
|
+
self.class.request
|
43
|
+
end
|
44
|
+
|
45
|
+
# True if this message is a response.
|
46
|
+
def response?
|
47
|
+
!request?
|
48
|
+
end
|
49
|
+
|
50
|
+
# True if this message is an error response.
|
51
|
+
def error?
|
52
|
+
response? && type == 'error'
|
53
|
+
end
|
54
|
+
|
55
|
+
# True if this message is a request and has login and password arguments.
|
56
|
+
def login_request?
|
57
|
+
request? && @auth.key?(:login) && @auth.key?(:password)
|
58
|
+
end
|
59
|
+
|
60
|
+
# True if this message is a request and has a session ID.
|
61
|
+
def session_request?
|
62
|
+
request? && @auth.key?(:session_id)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Convert this message to a Hash
|
66
|
+
#
|
67
|
+
# @return [Hash] this message as a Hash.
|
68
|
+
def to_h
|
69
|
+
h = { 'type' => type, 'args' => @args }
|
70
|
+
if login_request?
|
71
|
+
h['login'] = @auth[:login]
|
72
|
+
h['password'] = @auth[:password]
|
73
|
+
elsif session_request? || response?
|
74
|
+
h['session_id'] = @auth[:session_id]
|
75
|
+
end
|
76
|
+
|
77
|
+
h
|
78
|
+
end
|
79
|
+
|
80
|
+
# Hide sensitive information when debugging
|
81
|
+
def inspect
|
82
|
+
if login_request?
|
83
|
+
inspect_login_request
|
84
|
+
elsif session_request? || response?
|
85
|
+
inspect_session_request_or_response
|
86
|
+
else
|
87
|
+
raise 'WTFBBQ?!???1!11!'
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Serialize this message to JSON.
|
92
|
+
#
|
93
|
+
# @return [String] JSON data.
|
94
|
+
def serialize
|
95
|
+
to_h.to_json
|
96
|
+
end
|
97
|
+
|
98
|
+
class << self
|
99
|
+
attr_reader :type
|
100
|
+
attr_reader :request
|
101
|
+
attr_reader :registry
|
102
|
+
attr_reader :arg_names
|
103
|
+
|
104
|
+
# Declare message type and direction (request or response).
|
105
|
+
def message_type(name, request: nil, response: nil)
|
106
|
+
@type = name
|
107
|
+
if (request && response) || (!request && !response)
|
108
|
+
raise 'Is it a request or a response type?!?'
|
109
|
+
end
|
110
|
+
|
111
|
+
@request = !!request
|
112
|
+
end
|
113
|
+
|
114
|
+
# Declare an argument to this message type.
|
115
|
+
def arg(name)
|
116
|
+
@arg_names ||= []
|
117
|
+
@arg_names << name.to_s
|
118
|
+
define_method(name) do
|
119
|
+
@args[name.to_s]
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# Create a new message of the given type
|
124
|
+
def for_type(name, args, auth)
|
125
|
+
klass = @registry[name.to_sym]
|
126
|
+
raise "unknown message type #{name.inspect}" unless klass
|
127
|
+
|
128
|
+
klass.new(args, auth)
|
129
|
+
end
|
130
|
+
|
131
|
+
# True if message type "name" is known.
|
132
|
+
def type?(name)
|
133
|
+
@registry.key?(name.to_sym)
|
134
|
+
end
|
135
|
+
|
136
|
+
# List of known types.
|
137
|
+
def types
|
138
|
+
@registry.types
|
139
|
+
end
|
140
|
+
|
141
|
+
# Parse message.
|
142
|
+
def parse(str)
|
143
|
+
data = JSON.parse(str)
|
144
|
+
type = data.delete('type').to_sym
|
145
|
+
args = data.delete('args').transform_keys(&:to_s)
|
146
|
+
auth = data.transform_keys(&:to_sym)
|
147
|
+
for_type(type, args, auth)
|
148
|
+
rescue JSON::ParserError
|
149
|
+
raise UnparsableResponse
|
150
|
+
end
|
151
|
+
|
152
|
+
# Register new message type.
|
153
|
+
def register(klass)
|
154
|
+
@registry ||= {}
|
155
|
+
unless klass.respond_to?(:type) && klass.respond_to?(:request)
|
156
|
+
raise "#{klass} is not a subclass of #{self}"
|
157
|
+
end
|
158
|
+
|
159
|
+
if @registry.key?(klass.type)
|
160
|
+
message_type = subclass.type
|
161
|
+
old_klass = @registry[message_type]
|
162
|
+
raise "duplicate message type #{message_type} in #{klass}, " \
|
163
|
+
"already defined in #{old_klass}"
|
164
|
+
end
|
165
|
+
@registry[klass.type] = klass
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
private
|
170
|
+
|
171
|
+
def check_auth!(opts)
|
172
|
+
return opts if response?
|
173
|
+
return opts if opts.key?(:login) && opts.key?(:password)
|
174
|
+
return opts if opts.key?(:session_id)
|
175
|
+
|
176
|
+
raise RequestMissesAuthData
|
177
|
+
end
|
178
|
+
|
179
|
+
def check_args!(raw_args)
|
180
|
+
args = self.class.arg_names.to_h { |name| [name, raw_args[name]] }
|
181
|
+
missing = args.select { |_, v| v.nil? }.keys
|
182
|
+
unless missing.empty?
|
183
|
+
raise MissingMessageArgument.new(missing.inspect, type)
|
184
|
+
end
|
185
|
+
|
186
|
+
args
|
187
|
+
end
|
188
|
+
|
189
|
+
def inspect_login_request
|
190
|
+
base_inspect(
|
191
|
+
['@login=%<login>s', '@password="******"'],
|
192
|
+
login: @auth[:login].inspect
|
193
|
+
)
|
194
|
+
end
|
195
|
+
|
196
|
+
def inspect_session_request_or_response
|
197
|
+
base_inspect(['@session_id=******'])
|
198
|
+
end
|
199
|
+
|
200
|
+
def base_inspect(fmt_parts, **fmt_args)
|
201
|
+
begin_fmt = ["#<#{self.class}:%<id>x", '@type=%<type>s']
|
202
|
+
end_fmt = ['@args=%<args>s>']
|
203
|
+
fmt = (begin_fmt + fmt_parts + end_fmt).join(' ')
|
204
|
+
args = fmt_args.merge(
|
205
|
+
id: object_id, type: type.inspect, args: @args.inspect
|
206
|
+
)
|
207
|
+
format(fmt, **args)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
class << self
|
212
|
+
# Register new message type.
|
213
|
+
def register_all_message_types
|
214
|
+
constants(false).each do |const|
|
215
|
+
klass = const_get(const)
|
216
|
+
next unless klass.respond_to?(:superclass) && klass.superclass == Base
|
217
|
+
|
218
|
+
Base.register(klass)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'kstor/message/base'
|
4
|
+
|
5
|
+
module KStor
|
6
|
+
module Message
|
7
|
+
# Response for group created.
|
8
|
+
class GroupCreated < Base
|
9
|
+
message_type :group_created, response: true
|
10
|
+
|
11
|
+
arg :group_id
|
12
|
+
arg :group_name
|
13
|
+
arg :group_pubk
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'kstor/message/base'
|
4
|
+
|
5
|
+
module KStor
|
6
|
+
module Message
|
7
|
+
# Request to create a secret.
|
8
|
+
class SecretCreate < Base
|
9
|
+
message_type :secret_create, request: true
|
10
|
+
|
11
|
+
arg :meta
|
12
|
+
arg :group_ids
|
13
|
+
arg :plaintext
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'kstor/message/base'
|
4
|
+
|
5
|
+
module KStor
|
6
|
+
module Message
|
7
|
+
# Request to update secret metadata.
|
8
|
+
class SecretUpdateMeta < Base
|
9
|
+
message_type :secret_update_meta, request: true
|
10
|
+
|
11
|
+
arg :meta
|
12
|
+
arg :secret_id
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'kstor/message/base'
|
4
|
+
|
5
|
+
module KStor
|
6
|
+
module Message
|
7
|
+
# Request to update secret value.
|
8
|
+
class SecretUpdateValue < Base
|
9
|
+
message_type :secret_update_value, request: true
|
10
|
+
|
11
|
+
arg :plaintext
|
12
|
+
arg :secret_id
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'kstor/message/base'
|
4
|
+
|
5
|
+
module KStor
|
6
|
+
module Message
|
7
|
+
# Response for secret unlock.
|
8
|
+
class SecretValue < Base
|
9
|
+
message_type :secret_value, response: true
|
10
|
+
|
11
|
+
arg :id
|
12
|
+
arg :value_author
|
13
|
+
arg :metadata_author
|
14
|
+
arg :metadata
|
15
|
+
arg :plaintext
|
16
|
+
arg :groups
|
17
|
+
|
18
|
+
# Create a new secret-value response.
|
19
|
+
#
|
20
|
+
# @option opts [Integer] :id ID of secret
|
21
|
+
# @option opts [Hash] :value_author info on user that created secret value
|
22
|
+
# @option opts [Hash] :metadata_author info on user that created secret
|
23
|
+
# metadata
|
24
|
+
# @option opts [Hash] :metadata metadata of this secret
|
25
|
+
# @option opts [String] :plaintext decrypted value of secret
|
26
|
+
# @option opts [Array[Hash]] :groups list of groups that can decrypt this
|
27
|
+
# secret
|
28
|
+
# @param opts [Hash[Symbol, String]] common request options
|
29
|
+
def initialize(**opts)
|
30
|
+
super(**opts)
|
31
|
+
opts.delete(:group_id)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|