ruby_event_store 0.37.0 → 0.38.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
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7e2526ad20e1d86146a978c5de1c9171d772c323afa2b6d6dce9fdedfc972d45
|
4
|
+
data.tar.gz: 19ab66ae3e11671e774f00279cc9dd4273559b4efd88b7fd555b3f7af4ad882d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3773bb45339a106e8c40871ed22004b0c23fded77f3d621a6bf6ede2df7c5881d47aaf2d5bc705a6e794d7829f522184ef89a8258f7be2db40223b680e2ac2fd
|
7
|
+
data.tar.gz: 1c8372486ef7f311ead954b4ee20409cb10b258ec0b023d3bdbdb2fbd2acd754b339a2e5d7e029427560be59f73b975ce60a8f5d6cf4515c98475fea726f0751
|
data/Makefile
CHANGED
@@ -4,7 +4,11 @@ REQUIRE = $(GEM_NAME)
|
|
4
4
|
IGNORE = RubyEventStore::InMemoryRepository\#append_with_synchronize \
|
5
5
|
RubyEventStore::Client::Within\#add_thread_subscribers \
|
6
6
|
RubyEventStore::Client::Within\#add_thread_global_subscribers \
|
7
|
-
RubyEventStore::Client::Within\#call
|
7
|
+
RubyEventStore::Client::Within\#call \
|
8
|
+
RubyEventStore::Mappers::InMemoryEncryptionKeyRepository\#prepare_encrypt \
|
9
|
+
RubyEventStore::Mappers::EncryptionKey\#prepare_encrypt \
|
10
|
+
RubyEventStore::Mappers::EncryptionKey\#prepare_decrypt
|
11
|
+
|
8
12
|
SUBJECT ?= RubyEventStore*
|
9
13
|
|
10
14
|
include ../lib/install.mk
|
data/lib/ruby_event_store.rb
CHANGED
@@ -19,6 +19,7 @@ require 'ruby_event_store/mappers/default'
|
|
19
19
|
require 'ruby_event_store/mappers/protobuf'
|
20
20
|
require 'ruby_event_store/mappers/null_mapper'
|
21
21
|
require 'ruby_event_store/mappers/instrumented_mapper'
|
22
|
+
require 'ruby_event_store/mappers/encryption_mapper'
|
22
23
|
require 'ruby_event_store/batch_enumerator'
|
23
24
|
require 'ruby_event_store/correlated_commands'
|
24
25
|
require 'ruby_event_store/link_by_metadata'
|
@@ -0,0 +1,218 @@
|
|
1
|
+
module RubyEventStore
|
2
|
+
module Mappers
|
3
|
+
class ForgottenData
|
4
|
+
FORGOTTEN_DATA = 'FORGOTTEN_DATA'.freeze
|
5
|
+
|
6
|
+
def initialize(string = FORGOTTEN_DATA)
|
7
|
+
@string = string
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
@string
|
12
|
+
end
|
13
|
+
|
14
|
+
def ==(other)
|
15
|
+
@string == other
|
16
|
+
end
|
17
|
+
|
18
|
+
def method_missing(*)
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def respond_to_missing?(*)
|
23
|
+
true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class InMemoryEncryptionKeyRepository
|
28
|
+
DEFAULT_CIPHER = 'aes-256-cbc'.freeze
|
29
|
+
|
30
|
+
def initialize
|
31
|
+
@keys = {}
|
32
|
+
end
|
33
|
+
|
34
|
+
def key_of(identifier, cipher: DEFAULT_CIPHER)
|
35
|
+
@keys[[identifier, cipher]]
|
36
|
+
end
|
37
|
+
|
38
|
+
def create(identifier, cipher: DEFAULT_CIPHER)
|
39
|
+
crypto = prepare_encrypt(cipher)
|
40
|
+
@keys[[identifier, cipher]] = EncryptionKey.new(cipher: cipher, key: crypto.random_key)
|
41
|
+
end
|
42
|
+
|
43
|
+
def forget(identifier)
|
44
|
+
@keys = @keys.reject { |(id, _)| id.eql?(identifier) }
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def prepare_encrypt(cipher)
|
50
|
+
crypto = OpenSSL::Cipher.new(cipher)
|
51
|
+
crypto.encrypt
|
52
|
+
crypto
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class EncryptionKey
|
57
|
+
def initialize(cipher:, key:)
|
58
|
+
@cipher = cipher
|
59
|
+
@key = key
|
60
|
+
end
|
61
|
+
|
62
|
+
def encrypt(message, iv)
|
63
|
+
crypto = prepare_encrypt(cipher)
|
64
|
+
crypto.iv = iv
|
65
|
+
crypto.key = key
|
66
|
+
crypto.update(message) + crypto.final
|
67
|
+
end
|
68
|
+
|
69
|
+
def decrypt(message, iv)
|
70
|
+
crypto = prepare_decrypt(cipher)
|
71
|
+
crypto.iv = iv
|
72
|
+
crypto.key = key
|
73
|
+
crypto.update(message) + crypto.final
|
74
|
+
end
|
75
|
+
|
76
|
+
def random_iv
|
77
|
+
crypto = prepare_encrypt(cipher)
|
78
|
+
crypto.random_iv
|
79
|
+
end
|
80
|
+
|
81
|
+
attr_reader :cipher, :key
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def prepare_encrypt(cipher)
|
86
|
+
crypto = OpenSSL::Cipher.new(cipher)
|
87
|
+
crypto.encrypt
|
88
|
+
crypto
|
89
|
+
end
|
90
|
+
|
91
|
+
def prepare_decrypt(cipher)
|
92
|
+
crypto = OpenSSL::Cipher.new(cipher)
|
93
|
+
crypto.decrypt
|
94
|
+
crypto
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class MissingEncryptionKey < StandardError
|
99
|
+
def initialize(key_identifier)
|
100
|
+
super %Q|Could not find encryption key for '#{key_identifier}'|
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class EncryptionMapper
|
105
|
+
class Leaf
|
106
|
+
def self.===(hash)
|
107
|
+
hash.keys.sort.eql? %i(cipher identifier iv)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
private_constant :Leaf
|
111
|
+
|
112
|
+
def initialize(key_repository, serializer: YAML, forgotten_data: ForgottenData.new)
|
113
|
+
@key_repository = key_repository
|
114
|
+
@serializer = serializer
|
115
|
+
@forgotten_data = forgotten_data
|
116
|
+
end
|
117
|
+
|
118
|
+
def event_to_serialized_record(domain_event)
|
119
|
+
metadata = domain_event.metadata.to_h
|
120
|
+
crypto_description = encryption_metadata(domain_event.data, encryption_schema(domain_event))
|
121
|
+
metadata[:encryption] = crypto_description unless crypto_description.empty?
|
122
|
+
|
123
|
+
SerializedRecord.new(
|
124
|
+
event_id: domain_event.event_id,
|
125
|
+
metadata: serializer.dump(metadata),
|
126
|
+
data: serializer.dump(encrypt_data(deep_dup(domain_event.data), crypto_description)),
|
127
|
+
event_type: domain_event.class.to_s
|
128
|
+
)
|
129
|
+
end
|
130
|
+
|
131
|
+
def serialized_record_to_event(record)
|
132
|
+
metadata = serializer.load(record.metadata)
|
133
|
+
crypto_description = Hash(metadata.delete(:encryption))
|
134
|
+
|
135
|
+
Object.const_get(record.event_type).new(
|
136
|
+
event_id: record.event_id,
|
137
|
+
data: decrypt_data(serializer.load(record.data), crypto_description),
|
138
|
+
metadata: metadata,
|
139
|
+
)
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
143
|
+
attr_reader :key_repository, :serializer, :forgotten_data
|
144
|
+
|
145
|
+
def encryption_schema(event)
|
146
|
+
event.class.respond_to?(:encryption_schema) ? event.class.encryption_schema : {}
|
147
|
+
end
|
148
|
+
|
149
|
+
def deep_dup(hash)
|
150
|
+
duplicate = hash.dup
|
151
|
+
duplicate.each do |k, v|
|
152
|
+
duplicate[k] = v.instance_of?(Hash) ? deep_dup(v) : v
|
153
|
+
end
|
154
|
+
duplicate
|
155
|
+
end
|
156
|
+
|
157
|
+
def encryption_metadata(data, schema)
|
158
|
+
schema.inject({}) do |acc, (key, value)|
|
159
|
+
case value
|
160
|
+
when Hash
|
161
|
+
acc[key] = encryption_metadata(data, value)
|
162
|
+
when Proc
|
163
|
+
key_identifier = value.call(data)
|
164
|
+
encryption_key = key_repository.key_of(key_identifier) or raise MissingEncryptionKey.new(key_identifier)
|
165
|
+
acc[key] = {
|
166
|
+
cipher: encryption_key.cipher,
|
167
|
+
iv: encryption_key.random_iv,
|
168
|
+
identifier: key_identifier,
|
169
|
+
}
|
170
|
+
end
|
171
|
+
acc
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def encrypt_data(data, meta)
|
176
|
+
meta.reduce(data) do |acc, (key, value)|
|
177
|
+
acc[key] = encrypt_attribute(acc, key, value)
|
178
|
+
acc
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def decrypt_data(data, meta)
|
183
|
+
meta.reduce(data) do |acc, (key, value)|
|
184
|
+
acc[key] = decrypt_attribute(data, key, value)
|
185
|
+
acc
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def encrypt_attribute(data, attribute, meta)
|
190
|
+
case meta
|
191
|
+
when Leaf
|
192
|
+
value = data.fetch(attribute)
|
193
|
+
return unless value
|
194
|
+
|
195
|
+
encryption_key = key_repository.key_of(meta.fetch(:identifier))
|
196
|
+
encryption_key.encrypt(serializer.dump(value), meta.fetch(:iv))
|
197
|
+
when Hash
|
198
|
+
encrypt_data(data.fetch(attribute), meta)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def decrypt_attribute(data, attribute, meta)
|
203
|
+
case meta
|
204
|
+
when Leaf
|
205
|
+
cryptogram = data.fetch(attribute)
|
206
|
+
return unless cryptogram
|
207
|
+
|
208
|
+
encryption_key = key_repository.key_of(meta.fetch(:identifier), cipher: meta.fetch(:cipher)) or return forgotten_data
|
209
|
+
serializer.load(encryption_key.decrypt(cryptogram, meta.fetch(:iv)))
|
210
|
+
when Hash
|
211
|
+
decrypt_data(data.fetch(attribute), meta)
|
212
|
+
end
|
213
|
+
rescue OpenSSL::Cipher::CipherError
|
214
|
+
forgotten_data
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby_event_store
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.38.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Arkency
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-03-
|
11
|
+
date: 2019-03-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -50,6 +50,7 @@ files:
|
|
50
50
|
- lib/ruby_event_store/instrumented_repository.rb
|
51
51
|
- lib/ruby_event_store/link_by_metadata.rb
|
52
52
|
- lib/ruby_event_store/mappers/default.rb
|
53
|
+
- lib/ruby_event_store/mappers/encryption_mapper.rb
|
53
54
|
- lib/ruby_event_store/mappers/instrumented_mapper.rb
|
54
55
|
- lib/ruby_event_store/mappers/null_mapper.rb
|
55
56
|
- lib/ruby_event_store/mappers/protobuf.rb
|
@@ -95,8 +96,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
95
96
|
- !ruby/object:Gem::Version
|
96
97
|
version: '0'
|
97
98
|
requirements: []
|
98
|
-
|
99
|
-
rubygems_version: 2.7.6
|
99
|
+
rubygems_version: 3.0.2
|
100
100
|
signing_key:
|
101
101
|
specification_version: 4
|
102
102
|
summary: Event Store in Ruby
|