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: 4c759197c7079350367b3669fefa272cf9eee74c46cb084e73be1412f0fcfd53
4
- data.tar.gz: ba5fa3e80f410e2d773217eb02985fa34157bf12b9dec933e77cb1ea2d9eaff0
3
+ metadata.gz: 7e2526ad20e1d86146a978c5de1c9171d772c323afa2b6d6dce9fdedfc972d45
4
+ data.tar.gz: 19ab66ae3e11671e774f00279cc9dd4273559b4efd88b7fd555b3f7af4ad882d
5
5
  SHA512:
6
- metadata.gz: b10c08c3ec291f278d667f9768d20b912f69bdcc0e9c37162350e2017cb5462608ec95ae63a6bdbefccfd169916a2d930bc0ba000a0d2327c2b3804300d6f236
7
- data.tar.gz: cd35806dfe6c323eec8c30c3e2ddef0146c694a620a4366f640aa0fa9a90f85f825f9cc8c9ed73f6e5bda6194339d254c197a1d1472be6442e018d8e82c55a36
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
@@ -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
@@ -1,7 +1,6 @@
1
1
  module RubyEventStore
2
2
  module Mappers
3
3
  class NullMapper
4
-
5
4
  def event_to_serialized_record(domain_event)
6
5
  domain_event
7
6
  end
@@ -1,3 +1,3 @@
1
1
  module RubyEventStore
2
- VERSION = "0.37.0"
2
+ VERSION = "0.38.0"
3
3
  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.37.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-05 00:00:00.000000000 Z
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
- rubyforge_project:
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