ruby_event_store 0.37.0 → 0.38.0
Sign up to get free protection for your applications and to get access to all the features.
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
|