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: 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