kstor 0.4.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -2
  3. data/bin/kstor +48 -40
  4. data/bin/kstor-srv +2 -2
  5. data/lib/kstor/config.rb +2 -1
  6. data/lib/kstor/controller/authentication.rb +25 -4
  7. data/lib/kstor/controller/base.rb +61 -0
  8. data/lib/kstor/controller/request_handler.rb +84 -16
  9. data/lib/kstor/controller/secret.rb +63 -64
  10. data/lib/kstor/controller/users.rb +11 -22
  11. data/lib/kstor/controller.rb +3 -3
  12. data/lib/kstor/crypto/armored_value.rb +82 -0
  13. data/lib/kstor/crypto/keys.rb +9 -41
  14. data/lib/kstor/crypto.rb +0 -1
  15. data/lib/kstor/error.rb +36 -3
  16. data/lib/kstor/log/simple_logger.rb +83 -0
  17. data/lib/kstor/log/systemd_logger.rb +22 -0
  18. data/lib/kstor/log.rb +53 -4
  19. data/lib/kstor/message/base.rb +223 -0
  20. data/lib/kstor/message/error.rb +15 -0
  21. data/lib/kstor/message/group_create.rb +14 -0
  22. data/lib/kstor/message/group_created.rb +16 -0
  23. data/lib/kstor/message/ping.rb +14 -0
  24. data/lib/kstor/message/pong.rb +14 -0
  25. data/lib/kstor/message/secret_create.rb +16 -0
  26. data/lib/kstor/message/secret_created.rb +14 -0
  27. data/lib/kstor/message/secret_delete.rb +14 -0
  28. data/lib/kstor/message/secret_deleted.rb +14 -0
  29. data/lib/kstor/message/secret_list.rb +14 -0
  30. data/lib/kstor/message/secret_search.rb +14 -0
  31. data/lib/kstor/message/secret_unlock.rb +14 -0
  32. data/lib/kstor/message/secret_update_meta.rb +15 -0
  33. data/lib/kstor/message/secret_update_value.rb +15 -0
  34. data/lib/kstor/message/secret_updated.rb +14 -0
  35. data/lib/kstor/message/secret_value.rb +35 -0
  36. data/lib/kstor/message.rb +26 -113
  37. data/lib/kstor/model.rb +42 -25
  38. data/lib/kstor/server.rb +15 -3
  39. data/lib/kstor/session.rb +34 -2
  40. data/lib/kstor/socket_server.rb +20 -1
  41. data/lib/kstor/sql_connection.rb +16 -1
  42. data/lib/kstor/store.rb +182 -72
  43. data/lib/kstor/systemd.rb +5 -0
  44. data/lib/kstor/version.rb +2 -1
  45. data/lib/kstor.rb +1 -1
  46. 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
- logger.min_priority = lvl
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 ||= Journald::Logger.new('kstor')
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,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kstor/message/base'
4
+
5
+ module KStor
6
+ module Message
7
+ # Error response.
8
+ class Error < Base
9
+ message_type :error, response: true
10
+
11
+ arg :code
12
+ arg :message
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kstor/message/base'
4
+
5
+ module KStor
6
+ module Message
7
+ # Request to create a group of users.
8
+ class GroupCreate < Base
9
+ message_type :group_create, request: true
10
+
11
+ arg :name
12
+ end
13
+ end
14
+ 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,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kstor/message/base'
4
+
5
+ module KStor
6
+ module Message
7
+ # Ping server.
8
+ class Ping < Base
9
+ message_type :ping, request: true
10
+
11
+ arg :payload
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kstor/message/base'
4
+
5
+ module KStor
6
+ module Message
7
+ # Ping response.
8
+ class Pong < Base
9
+ message_type :pong, response: true
10
+
11
+ arg :payload
12
+ end
13
+ end
14
+ 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,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kstor/message/base'
4
+
5
+ module KStor
6
+ module Message
7
+ # Response for secret created.
8
+ class SecretCreated < Base
9
+ message_type :secret_created, response: true
10
+
11
+ arg :secret_id
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kstor/message/base'
4
+
5
+ module KStor
6
+ module Message
7
+ # Request to delete a secret.
8
+ class SecretDelete < Base
9
+ message_type :secret_delete, request: true
10
+
11
+ arg :secret_id
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kstor/message/base'
4
+
5
+ module KStor
6
+ module Message
7
+ # Response for secret deleted.
8
+ class SecretDeleted < Base
9
+ message_type :secret_deleted, response: true
10
+
11
+ arg :secret_id
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kstor/message/base'
4
+
5
+ module KStor
6
+ module Message
7
+ # Response for secret search.
8
+ class SecretList < Base
9
+ message_type :secret_list, response: true
10
+
11
+ arg :secrets
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kstor/message/base'
4
+
5
+ module KStor
6
+ module Message
7
+ # Request to search secrets.
8
+ class SecretSearch < Base
9
+ message_type :secret_search, request: true
10
+
11
+ arg :meta
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kstor/message/base'
4
+
5
+ module KStor
6
+ module Message
7
+ # Request to decrypt a secret.
8
+ class SecretUnlock < Base
9
+ message_type :secret_unlock, request: true
10
+
11
+ arg :secret_id
12
+ end
13
+ end
14
+ 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,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kstor/message/base'
4
+
5
+ module KStor
6
+ module Message
7
+ # Response for secret updated.
8
+ class SecretUpdated < Base
9
+ message_type :secret_updated, response: true
10
+
11
+ arg :secret_id
12
+ end
13
+ end
14
+ 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