aws_advanced_ruby_driver_wrapper 1.0.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.
Files changed (150) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +23 -0
  3. data/LICENSE +175 -0
  4. data/NOTICE +1 -0
  5. data/README.md +168 -0
  6. data/THIRD-PARTY-LICENSES +473 -0
  7. data/aws_advanced_ruby_driver_wrapper.gemspec +73 -0
  8. data/lib/aws_advanced_ruby_driver_wrapper/active_record/aws_mysql2_adapter.rb +73 -0
  9. data/lib/aws_advanced_ruby_driver_wrapper/active_record/aws_postgresql_adapter.rb +95 -0
  10. data/lib/aws_advanced_ruby_driver_wrapper/custom_configuration.rb +58 -0
  11. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/aurora_mysql_dialect.rb +103 -0
  12. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/aurora_pg_dialect.rb +124 -0
  13. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/dialect_codes.rb +38 -0
  14. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/global_mysql_dialect.rb +91 -0
  15. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/global_pg_dialect.rb +92 -0
  16. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/multi_az_cluster_mysql_dialect.rb +95 -0
  17. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/multi_az_cluster_pg_dialect.rb +86 -0
  18. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/mysql_dialect.rb +98 -0
  19. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/pg_dialect.rb +95 -0
  20. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/rds_mysql_dialect.rb +88 -0
  21. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/rds_pg_dialect.rb +86 -0
  22. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/unknown_dialect.rb +72 -0
  23. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/utils/dialect_utils.rb +71 -0
  24. data/lib/aws_advanced_ruby_driver_wrapper/driver_dialects/driver_dialect.rb +154 -0
  25. data/lib/aws_advanced_ruby_driver_wrapper/driver_dialects/driver_dialect_manager.rb +55 -0
  26. data/lib/aws_advanced_ruby_driver_wrapper/driver_dialects/mysql_driver_dialect.rb +165 -0
  27. data/lib/aws_advanced_ruby_driver_wrapper/driver_dialects/pg_driver_dialect.rb +201 -0
  28. data/lib/aws_advanced_ruby_driver_wrapper/errors/error_handler.rb +62 -0
  29. data/lib/aws_advanced_ruby_driver_wrapper/errors/mysql_error_handler.rb +80 -0
  30. data/lib/aws_advanced_ruby_driver_wrapper/errors/pg_error_handler.rb +126 -0
  31. data/lib/aws_advanced_ruby_driver_wrapper/errors.rb +59 -0
  32. data/lib/aws_advanced_ruby_driver_wrapper/host/connection_string_host_list_provider.rb +95 -0
  33. data/lib/aws_advanced_ruby_driver_wrapper/host/global_aurora_host_list_provider.rb +65 -0
  34. data/lib/aws_advanced_ruby_driver_wrapper/host/host_availability.rb +24 -0
  35. data/lib/aws_advanced_ruby_driver_wrapper/host/host_availability_strategy.rb +27 -0
  36. data/lib/aws_advanced_ruby_driver_wrapper/host/host_info.rb +137 -0
  37. data/lib/aws_advanced_ruby_driver_wrapper/host/host_role.rb +25 -0
  38. data/lib/aws_advanced_ruby_driver_wrapper/host/random_host_selector.rb +40 -0
  39. data/lib/aws_advanced_ruby_driver_wrapper/host/rds_host_list_provider.rb +206 -0
  40. data/lib/aws_advanced_ruby_driver_wrapper/logging.rb +110 -0
  41. data/lib/aws_advanced_ruby_driver_wrapper/monitoring/cluster_topology_monitor.rb +709 -0
  42. data/lib/aws_advanced_ruby_driver_wrapper/monitoring/global_cluster_topology_monitor.rb +72 -0
  43. data/lib/aws_advanced_ruby_driver_wrapper/monitoring/monitor.rb +99 -0
  44. data/lib/aws_advanced_ruby_driver_wrapper/monitoring/monitor_connection.rb +57 -0
  45. data/lib/aws_advanced_ruby_driver_wrapper/monitoring/monitor_state.rb +25 -0
  46. data/lib/aws_advanced_ruby_driver_wrapper/mysql.rb +429 -0
  47. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/blue_green_plugin.rb +205 -0
  48. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/host_mapper.rb +132 -0
  49. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/iam_host_tracker.rb +84 -0
  50. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/interim_status.rb +92 -0
  51. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/interval_rate.rb +27 -0
  52. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/phase.rb +69 -0
  53. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/phase_event_log.rb +85 -0
  54. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/phase_time_info.rb +25 -0
  55. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/role.rb +38 -0
  56. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/base_routing.rb +83 -0
  57. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/reject_connect_routing.rb +40 -0
  58. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/substitute_connect_routing.rb +136 -0
  59. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/suspend_connect_routing.rb +53 -0
  60. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/suspend_execute_routing.rb +52 -0
  61. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/suspend_until_corresponding_host_found_connect_routing.rb +83 -0
  62. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status.rb +68 -0
  63. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status_builder.rb +244 -0
  64. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status_info.rb +30 -0
  65. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status_monitor.rb +564 -0
  66. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status_provider.rb +414 -0
  67. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/switchover_state.rb +98 -0
  68. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/switchover_timer.rb +46 -0
  69. data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/custom_endpoint_monitor.rb +266 -0
  70. data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/custom_endpoint_plugin.rb +158 -0
  71. data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/info.rb +111 -0
  72. data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/member_list_type.rb +31 -0
  73. data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/role.rb +45 -0
  74. data/lib/aws_advanced_ruby_driver_wrapper/plugins/default_plugin.rb +108 -0
  75. data/lib/aws_advanced_ruby_driver_wrapper/plugins/failover_mode.rb +43 -0
  76. data/lib/aws_advanced_ruby_driver_wrapper/plugins/failover_plugin.rb +467 -0
  77. data/lib/aws_advanced_ruby_driver_wrapper/plugins/gdb/gdb_failover_mode.rb +68 -0
  78. data/lib/aws_advanced_ruby_driver_wrapper/plugins/gdb/gdb_failover_plugin.rb +403 -0
  79. data/lib/aws_advanced_ruby_driver_wrapper/plugins/iam_auth_plugin.rb +159 -0
  80. data/lib/aws_advanced_ruby_driver_wrapper/plugins/initial_connection_strategy_plugin.rb +485 -0
  81. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/audit_logger.rb +157 -0
  82. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/column_cipher.rb +159 -0
  83. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/column_encryption_config.rb +61 -0
  84. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/connection_source.rb +91 -0
  85. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/data_key_cache.rb +220 -0
  86. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/encryption_algorithm.rb +75 -0
  87. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/encryption_config.rb +146 -0
  88. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/encryption_service.rb +391 -0
  89. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/error_context.rb +198 -0
  90. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/errors.rb +259 -0
  91. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/key_management_utility.rb +435 -0
  92. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/key_manager.rb +378 -0
  93. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/key_metadata.rb +86 -0
  94. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/kms_encryption_plugin.rb +890 -0
  95. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/kms_encryption_utility.rb +281 -0
  96. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/metadata_manager.rb +332 -0
  97. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/sanitizer.rb +147 -0
  98. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/schema_name.rb +70 -0
  99. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/schema_validator.rb +211 -0
  100. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/sql_runner.rb +147 -0
  101. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/type_marker.rb +109 -0
  102. data/lib/aws_advanced_ruby_driver_wrapper/plugins/secrets_manager_plugin.rb +358 -0
  103. data/lib/aws_advanced_ruby_driver_wrapper/postgresql.rb +659 -0
  104. data/lib/aws_advanced_ruby_driver_wrapper/property_definition.rb +409 -0
  105. data/lib/aws_advanced_ruby_driver_wrapper/ruby_method.rb +122 -0
  106. data/lib/aws_advanced_ruby_driver_wrapper/services/connection_service.rb +143 -0
  107. data/lib/aws_advanced_ruby_driver_wrapper/services/dialect_service.rb +267 -0
  108. data/lib/aws_advanced_ruby_driver_wrapper/services/host_service.rb +199 -0
  109. data/lib/aws_advanced_ruby_driver_wrapper/services/monitor_service.rb +186 -0
  110. data/lib/aws_advanced_ruby_driver_wrapper/services/plugin_call_context.rb +63 -0
  111. data/lib/aws_advanced_ruby_driver_wrapper/services/plugin_manager.rb +273 -0
  112. data/lib/aws_advanced_ruby_driver_wrapper/services/service_container.rb +30 -0
  113. data/lib/aws_advanced_ruby_driver_wrapper/services/service_utility.rb +78 -0
  114. data/lib/aws_advanced_ruby_driver_wrapper/services/session_state_service.rb +56 -0
  115. data/lib/aws_advanced_ruby_driver_wrapper/utils/accessible_regions.rb +52 -0
  116. data/lib/aws_advanced_ruby_driver_wrapper/utils/ar_constants.rb +25 -0
  117. data/lib/aws_advanced_ruby_driver_wrapper/utils/aurora_topology_utils.rb +99 -0
  118. data/lib/aws_advanced_ruby_driver_wrapper/utils/aws_credentials_utils.rb +62 -0
  119. data/lib/aws_advanced_ruby_driver_wrapper/utils/connection_config.rb +91 -0
  120. data/lib/aws_advanced_ruby_driver_wrapper/utils/connection_config_parser.rb +368 -0
  121. data/lib/aws_advanced_ruby_driver_wrapper/utils/conversion_utils.rb +51 -0
  122. data/lib/aws_advanced_ruby_driver_wrapper/utils/events/batching_event_publisher.rb +119 -0
  123. data/lib/aws_advanced_ruby_driver_wrapper/utils/events/data_access_event.rb +26 -0
  124. data/lib/aws_advanced_ruby_driver_wrapper/utils/events/monitor_reset_event.rb +26 -0
  125. data/lib/aws_advanced_ruby_driver_wrapper/utils/global_aurora_topology_utils.rb +185 -0
  126. data/lib/aws_advanced_ruby_driver_wrapper/utils/host_list_utils.rb +27 -0
  127. data/lib/aws_advanced_ruby_driver_wrapper/utils/iam_auth_utils.rb +112 -0
  128. data/lib/aws_advanced_ruby_driver_wrapper/utils/multi_az_topology_utils.rb +117 -0
  129. data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/encryption_annotation_parser.rb +99 -0
  130. data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/mysql_statement_analyzer.rb +641 -0
  131. data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/pg_statement_analyzer.rb +502 -0
  132. data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/query_analysis.rb +63 -0
  133. data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/query_type.rb +35 -0
  134. data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/routing_hint.rb +27 -0
  135. data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/routing_hint_parser.rb +50 -0
  136. data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/sql_parser.rb +139 -0
  137. data/lib/aws_advanced_ruby_driver_wrapper/utils/rds_url_type.rb +71 -0
  138. data/lib/aws_advanced_ruby_driver_wrapper/utils/rds_utils.rb +575 -0
  139. data/lib/aws_advanced_ruby_driver_wrapper/utils/retry_util.rb +153 -0
  140. data/lib/aws_advanced_ruby_driver_wrapper/utils/sql_encoding.rb +56 -0
  141. data/lib/aws_advanced_ruby_driver_wrapper/utils/sql_method_analyzer.rb +195 -0
  142. data/lib/aws_advanced_ruby_driver_wrapper/utils/storage/cache_entry.rb +56 -0
  143. data/lib/aws_advanced_ruby_driver_wrapper/utils/storage/expiration_cache.rb +108 -0
  144. data/lib/aws_advanced_ruby_driver_wrapper/utils/storage/sliding_expiration_cache.rb +137 -0
  145. data/lib/aws_advanced_ruby_driver_wrapper/utils/storage/storage_service.rb +172 -0
  146. data/lib/aws_advanced_ruby_driver_wrapper/utils/topology_utils.rb +127 -0
  147. data/lib/aws_advanced_ruby_driver_wrapper/version.rb +19 -0
  148. data/lib/aws_advanced_ruby_driver_wrapper/wrapper_property.rb +64 -0
  149. data/lib/aws_advanced_ruby_driver_wrapper.rb +116 -0
  150. metadata +227 -0
@@ -0,0 +1,198 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License").
6
+ # You may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require_relative 'sanitizer'
18
+
19
+ module AwsAdvancedRubyDriverWrapper
20
+ module Plugins
21
+ module Encryption
22
+ # Builds the human-readable, redacted message that goes with a kms_encryption failure.
23
+ #
24
+ # ErrorContext.builder
25
+ # .table('users').column('ssn').operation('ENCRYPT').parameter_index(2)
26
+ # .build_encryption_error_message('the data key was rejected')
27
+ # # => "Encryption failed: the data key was rejected for column users.ssn during ENCRYPT (parameter index: 2)"
28
+ #
29
+ # Sensitive details are redacted by {Sanitizer} as they are added, so a context can be
30
+ # logged or attached to an exception message as is.
31
+ class ErrorContext
32
+ # Keys rendered inline by the +build_*_error_message+ helpers rather than in the
33
+ # trailing bracketed list.
34
+ POSITIONAL_KEYS = %i[table column operation parameter_index column_index retry_attempt max_retry_attempts].freeze
35
+
36
+ # @return [ErrorContext] a new, empty context
37
+ def self.builder
38
+ new
39
+ end
40
+
41
+ def initialize
42
+ @context = {}
43
+ end
44
+
45
+ # @return [self]
46
+ def table(table_name)
47
+ put(:table, Sanitizer.table_name(table_name))
48
+ end
49
+
50
+ # @return [self]
51
+ def column(column_name)
52
+ put(:column, Sanitizer.column_name(column_name))
53
+ end
54
+
55
+ # @return [self]
56
+ def operation(operation)
57
+ put(:operation, operation)
58
+ end
59
+
60
+ # @return [self]
61
+ def key_id(key_id)
62
+ put(:key_id, Sanitizer.key_id(key_id))
63
+ end
64
+
65
+ # @return [self]
66
+ def master_key_arn(master_key_arn)
67
+ put(:master_key_arn, Sanitizer.arn(master_key_arn))
68
+ end
69
+
70
+ # @return [self]
71
+ def algorithm(algorithm)
72
+ put(:algorithm, algorithm)
73
+ end
74
+
75
+ # @return [self]
76
+ def parameter_index(index)
77
+ put(:parameter_index, index)
78
+ end
79
+
80
+ # @return [self]
81
+ def column_index(index)
82
+ put(:column_index, index)
83
+ end
84
+
85
+ # @return [self]
86
+ def sql(sql)
87
+ put(:sql, Sanitizer.sql(sql))
88
+ end
89
+
90
+ # @return [self]
91
+ def data_type(data_type)
92
+ put(:data_type, data_type)
93
+ end
94
+
95
+ # @param attempt [Integer] the attempt that failed, 1-based
96
+ # @param max_attempts [Integer]
97
+ # @return [self]
98
+ def retry_attempt(attempt, max_attempts)
99
+ put(:retry_attempt, attempt)
100
+ put(:max_retry_attempts, max_attempts)
101
+ end
102
+
103
+ # @param cache_type [String]
104
+ # @param cache_hit [Boolean]
105
+ # @return [self]
106
+ def cache_info(cache_type, cache_hit)
107
+ put(:cache_type, cache_type)
108
+ put(:cache_hit, cache_hit)
109
+ end
110
+
111
+ # @return [Hash] a copy of the accumulated context
112
+ def context
113
+ @context.dup
114
+ end
115
+
116
+ # @return [Boolean]
117
+ def empty?
118
+ @context.empty?
119
+ end
120
+
121
+ # @param base_message [String]
122
+ # @return [String] the message with the whole context appended as key=value pairs
123
+ def build_message(base_message)
124
+ return base_message.to_s if @context.empty?
125
+
126
+ "#{base_message} [Context: #{@context.map { |key, value| "#{key}=#{value}" }.join(', ')}]"
127
+ end
128
+
129
+ # @return [String]
130
+ def build_encryption_error_message(base_message = nil)
131
+ build_prefixed_message('Encryption failed', base_message)
132
+ end
133
+
134
+ # @return [String]
135
+ def build_decryption_error_message(base_message = nil)
136
+ build_prefixed_message('Decryption failed', base_message)
137
+ end
138
+
139
+ # @return [String]
140
+ def build_key_management_error_message(base_message = nil)
141
+ build_prefixed_message('Key management operation failed', base_message)
142
+ end
143
+
144
+ # @return [String]
145
+ def build_metadata_error_message(base_message = nil)
146
+ build_prefixed_message('Metadata operation failed', base_message)
147
+ end
148
+
149
+ private
150
+
151
+ def put(key, value)
152
+ @context[key] = value unless value.nil?
153
+ self
154
+ end
155
+
156
+ def build_prefixed_message(prefix, base_message)
157
+ message = +prefix
158
+ message << ": #{base_message}" unless base_message.nil? || base_message.to_s.strip.empty?
159
+ add_contextual_info(message)
160
+ end
161
+
162
+ def add_contextual_info(message)
163
+ message << target_description
164
+ message << " during #{@context[:operation]}" if @context[:operation]
165
+ message << index_description
166
+ message << " (retry #{@context[:retry_attempt]}/#{@context[:max_retry_attempts]})" if @context[:retry_attempt]
167
+ message << remaining_description
168
+ message
169
+ end
170
+
171
+ def target_description
172
+ table_name = @context[:table]
173
+ column_name = @context[:column]
174
+
175
+ if table_name && column_name then " for column #{table_name}.#{column_name}"
176
+ elsif table_name then " for table #{table_name}"
177
+ elsif column_name then " for column #{column_name}"
178
+ else ''
179
+ end
180
+ end
181
+
182
+ def index_description
183
+ if @context[:parameter_index] then " (parameter index: #{@context[:parameter_index]})"
184
+ elsif @context[:column_index] then " (column index: #{@context[:column_index]})"
185
+ else ''
186
+ end
187
+ end
188
+
189
+ def remaining_description
190
+ remaining = @context.except(*POSITIONAL_KEYS)
191
+ return '' if remaining.empty?
192
+
193
+ " [#{remaining.map { |key, value| "#{key}=#{value}" }.join(', ')}]"
194
+ end
195
+ end
196
+ end
197
+ end
198
+ end
@@ -0,0 +1,259 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License").
6
+ # You may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require_relative '../../errors'
18
+ require_relative 'sanitizer'
19
+
20
+ module AwsAdvancedRubyDriverWrapper
21
+ module Errors
22
+ # Base class for every failure raised by the kms_encryption plugin. Carries a stable
23
+ # error code and an ordered context hash that is appended to the message, so that a
24
+ # failure can be traced back to the table, column, key, and operation involved without
25
+ # leaking the encrypted values themselves.
26
+ #
27
+ # Context is populated with fluent +with_*+ helpers, each of which returns self:
28
+ #
29
+ # raise Errors::EncryptionError.encryption_failed('Cipher rejected the key')
30
+ # .with_table('users').with_column('ssn')
31
+ class EncryptionPluginError < AwsError
32
+ Sanitizer = Plugins::Encryption::Sanitizer
33
+
34
+ attr_reader :code, :base_message, :context
35
+
36
+ # @param message [String] the message without any context appended
37
+ # @param code [String, nil] a stable error code, e.g. 'ENC01'
38
+ # @param context [Hash] initial context entries; nil values are dropped
39
+ def initialize(message, code: nil, context: {})
40
+ super(message)
41
+ @base_message = message.to_s
42
+ @code = code || default_code
43
+ @context = {}
44
+ context.each { |key, value| with_context(key, value) }
45
+ end
46
+
47
+ # The error code used when one is not passed explicitly. Subclasses override this; the base
48
+ # class has no default code.
49
+ # @return [String, nil]
50
+ def default_code
51
+ nil
52
+ end
53
+
54
+ # Adds a context entry, ignoring nil values so that unknown details are simply absent.
55
+ # @return [self]
56
+ def with_context(key, value)
57
+ @context[key.to_sym] = value unless value.nil?
58
+ self
59
+ end
60
+
61
+ # @return [self]
62
+ def with_operation(operation)
63
+ with_context(:operation, operation)
64
+ end
65
+
66
+ # The message with the accumulated context appended.
67
+ # @return [String]
68
+ def to_s
69
+ return @base_message if @context.empty?
70
+
71
+ "#{@base_message} [Context: #{@context.map { |key, value| "#{key}=#{value}" }.join(', ')}]"
72
+ end
73
+ end
74
+
75
+ # Raised when a value cannot be encrypted or decrypted, or when the encrypted payload
76
+ # fails its integrity check.
77
+ class EncryptionError < EncryptionPluginError
78
+ ENCRYPTION_FAILED = 'ENC01'
79
+ DECRYPTION_FAILED = 'ENC02'
80
+ INVALID_ALGORITHM = 'ENC03'
81
+ INVALID_KEY = 'ENC04'
82
+ TYPE_CONVERSION_FAILED = 'ENC05'
83
+ # A value could not be confirmed to be a valid encrypted payload of this column: it is too
84
+ # short to be one, or its HMAC does not verify. Distinct from DECRYPTION_FAILED, which means a
85
+ # confirmed payload that would not decrypt (a wrong data key), so that a lenient read can
86
+ # return an unverifiable value untouched while still failing closed on a real decryption fault.
87
+ INTEGRITY_CHECK_FAILED = 'ENC06'
88
+
89
+ def default_code
90
+ ENCRYPTION_FAILED
91
+ end
92
+
93
+ class << self
94
+ def encryption_failed(message, context = {})
95
+ new(message, code: ENCRYPTION_FAILED, context: context)
96
+ end
97
+
98
+ def decryption_failed(message, context = {})
99
+ new(message, code: DECRYPTION_FAILED, context: context)
100
+ end
101
+
102
+ def integrity_check_failed(message, context = {})
103
+ new(message, code: INTEGRITY_CHECK_FAILED, context: context)
104
+ end
105
+
106
+ def invalid_algorithm(message, context = {})
107
+ new(message, code: INVALID_ALGORITHM, context: context)
108
+ end
109
+
110
+ def invalid_key(message, context = {})
111
+ new(message, code: INVALID_KEY, context: context)
112
+ end
113
+
114
+ def type_conversion_failed(message, context = {})
115
+ new(message, code: TYPE_CONVERSION_FAILED, context: context)
116
+ end
117
+ end
118
+
119
+ # @return [self]
120
+ def with_table(table_name)
121
+ with_context(:table, Sanitizer.table_name(table_name))
122
+ end
123
+
124
+ # @return [self]
125
+ def with_column(column_name)
126
+ with_context(:column, Sanitizer.column_name(column_name))
127
+ end
128
+
129
+ # @return [self]
130
+ def with_algorithm(algorithm)
131
+ with_context(:algorithm, algorithm)
132
+ end
133
+
134
+ # @return [self]
135
+ def with_data_type(data_type)
136
+ with_context(:data_type, data_type)
137
+ end
138
+ end
139
+
140
+ # Raised when a data key or master key cannot be created, retrieved, decrypted, or stored.
141
+ class KeyManagementError < EncryptionPluginError
142
+ KEY_CREATION_FAILED = 'KEY01'
143
+ KEY_RETRIEVAL_FAILED = 'KEY02'
144
+ KEY_DECRYPTION_FAILED = 'KEY03'
145
+ KEY_STORAGE_FAILED = 'KEY04'
146
+ KMS_CONNECTION_FAILED = 'KEY05'
147
+ INVALID_KEY_METADATA = 'KEY06'
148
+ UNAUTHORIZED_MASTER_KEY = 'KEY07'
149
+
150
+ def default_code
151
+ KEY_RETRIEVAL_FAILED
152
+ end
153
+
154
+ class << self
155
+ def key_creation_failed(message, context = {})
156
+ new(message, code: KEY_CREATION_FAILED, context: context)
157
+ end
158
+
159
+ def key_retrieval_failed(message, context = {})
160
+ new(message, code: KEY_RETRIEVAL_FAILED, context: context)
161
+ end
162
+
163
+ def key_decryption_failed(message, context = {})
164
+ new(message, code: KEY_DECRYPTION_FAILED, context: context)
165
+ end
166
+
167
+ def key_storage_failed(message, context = {})
168
+ new(message, code: KEY_STORAGE_FAILED, context: context)
169
+ end
170
+
171
+ def kms_connection_failed(message, context = {})
172
+ new(message, code: KMS_CONNECTION_FAILED, context: context)
173
+ end
174
+
175
+ def invalid_key_metadata(message, context = {})
176
+ new(message, code: INVALID_KEY_METADATA, context: context)
177
+ end
178
+
179
+ def unauthorized_master_key(message, context = {})
180
+ new(message, code: UNAUTHORIZED_MASTER_KEY, context: context)
181
+ end
182
+ end
183
+
184
+ # @return [self]
185
+ def with_key_id(key_id)
186
+ with_context(:key_id, Sanitizer.key_id(key_id))
187
+ end
188
+
189
+ # @return [self]
190
+ def with_master_key_arn(master_key_arn)
191
+ with_context(:master_key_arn, Sanitizer.arn(master_key_arn))
192
+ end
193
+
194
+ # @param attempt [Integer] the attempt that failed, 1-based
195
+ # @param max_attempts [Integer]
196
+ # @return [self]
197
+ def with_retry_info(attempt, max_attempts)
198
+ with_context(:retry_attempt, "#{attempt}/#{max_attempts}")
199
+ end
200
+ end
201
+
202
+ # Raised when the kms_encryption metadata tables cannot be read, refreshed, or validated.
203
+ class MetadataError < EncryptionPluginError
204
+ METADATA_LOAD_FAILED = 'META01'
205
+ METADATA_CACHE_FAILED = 'META02'
206
+ METADATA_REFRESH_FAILED = 'META03'
207
+ METADATA_LOOKUP_FAILED = 'META04'
208
+ METADATA_VALIDATION_FAILED = 'META05'
209
+
210
+ def default_code
211
+ METADATA_LOOKUP_FAILED
212
+ end
213
+
214
+ class << self
215
+ def load_failed(message, context = {})
216
+ new(message, code: METADATA_LOAD_FAILED, context: context)
217
+ end
218
+
219
+ def cache_failed(message, context = {})
220
+ new(message, code: METADATA_CACHE_FAILED, context: context)
221
+ end
222
+
223
+ def refresh_failed(message, context = {})
224
+ new(message, code: METADATA_REFRESH_FAILED, context: context)
225
+ end
226
+
227
+ def lookup_failed(message, context = {})
228
+ new(message, code: METADATA_LOOKUP_FAILED, context: context)
229
+ end
230
+
231
+ def validation_failed(message, context = {})
232
+ new(message, code: METADATA_VALIDATION_FAILED, context: context)
233
+ end
234
+ end
235
+
236
+ # @return [self]
237
+ def with_table(table_name)
238
+ with_context(:table, Sanitizer.table_name(table_name))
239
+ end
240
+
241
+ # @return [self]
242
+ def with_column(column_name)
243
+ with_context(:column, Sanitizer.column_name(column_name))
244
+ end
245
+
246
+ # @param cache_type [String]
247
+ # @param cache_hit [Boolean]
248
+ # @return [self]
249
+ def with_cache_info(cache_type, cache_hit)
250
+ with_context(:cache_type, cache_type).with_context(:cache_hit, cache_hit)
251
+ end
252
+
253
+ # @return [self]
254
+ def with_sql(sql)
255
+ with_context(:sql, Sanitizer.sql(sql))
256
+ end
257
+ end
258
+ end
259
+ end