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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +23 -0
- data/LICENSE +175 -0
- data/NOTICE +1 -0
- data/README.md +168 -0
- data/THIRD-PARTY-LICENSES +473 -0
- data/aws_advanced_ruby_driver_wrapper.gemspec +73 -0
- data/lib/aws_advanced_ruby_driver_wrapper/active_record/aws_mysql2_adapter.rb +73 -0
- data/lib/aws_advanced_ruby_driver_wrapper/active_record/aws_postgresql_adapter.rb +95 -0
- data/lib/aws_advanced_ruby_driver_wrapper/custom_configuration.rb +58 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/aurora_mysql_dialect.rb +103 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/aurora_pg_dialect.rb +124 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/dialect_codes.rb +38 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/global_mysql_dialect.rb +91 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/global_pg_dialect.rb +92 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/multi_az_cluster_mysql_dialect.rb +95 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/multi_az_cluster_pg_dialect.rb +86 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/mysql_dialect.rb +98 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/pg_dialect.rb +95 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/rds_mysql_dialect.rb +88 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/rds_pg_dialect.rb +86 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/unknown_dialect.rb +72 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/utils/dialect_utils.rb +71 -0
- data/lib/aws_advanced_ruby_driver_wrapper/driver_dialects/driver_dialect.rb +154 -0
- data/lib/aws_advanced_ruby_driver_wrapper/driver_dialects/driver_dialect_manager.rb +55 -0
- data/lib/aws_advanced_ruby_driver_wrapper/driver_dialects/mysql_driver_dialect.rb +165 -0
- data/lib/aws_advanced_ruby_driver_wrapper/driver_dialects/pg_driver_dialect.rb +201 -0
- data/lib/aws_advanced_ruby_driver_wrapper/errors/error_handler.rb +62 -0
- data/lib/aws_advanced_ruby_driver_wrapper/errors/mysql_error_handler.rb +80 -0
- data/lib/aws_advanced_ruby_driver_wrapper/errors/pg_error_handler.rb +126 -0
- data/lib/aws_advanced_ruby_driver_wrapper/errors.rb +59 -0
- data/lib/aws_advanced_ruby_driver_wrapper/host/connection_string_host_list_provider.rb +95 -0
- data/lib/aws_advanced_ruby_driver_wrapper/host/global_aurora_host_list_provider.rb +65 -0
- data/lib/aws_advanced_ruby_driver_wrapper/host/host_availability.rb +24 -0
- data/lib/aws_advanced_ruby_driver_wrapper/host/host_availability_strategy.rb +27 -0
- data/lib/aws_advanced_ruby_driver_wrapper/host/host_info.rb +137 -0
- data/lib/aws_advanced_ruby_driver_wrapper/host/host_role.rb +25 -0
- data/lib/aws_advanced_ruby_driver_wrapper/host/random_host_selector.rb +40 -0
- data/lib/aws_advanced_ruby_driver_wrapper/host/rds_host_list_provider.rb +206 -0
- data/lib/aws_advanced_ruby_driver_wrapper/logging.rb +110 -0
- data/lib/aws_advanced_ruby_driver_wrapper/monitoring/cluster_topology_monitor.rb +709 -0
- data/lib/aws_advanced_ruby_driver_wrapper/monitoring/global_cluster_topology_monitor.rb +72 -0
- data/lib/aws_advanced_ruby_driver_wrapper/monitoring/monitor.rb +99 -0
- data/lib/aws_advanced_ruby_driver_wrapper/monitoring/monitor_connection.rb +57 -0
- data/lib/aws_advanced_ruby_driver_wrapper/monitoring/monitor_state.rb +25 -0
- data/lib/aws_advanced_ruby_driver_wrapper/mysql.rb +429 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/blue_green_plugin.rb +205 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/host_mapper.rb +132 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/iam_host_tracker.rb +84 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/interim_status.rb +92 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/interval_rate.rb +27 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/phase.rb +69 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/phase_event_log.rb +85 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/phase_time_info.rb +25 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/role.rb +38 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/base_routing.rb +83 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/reject_connect_routing.rb +40 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/substitute_connect_routing.rb +136 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/suspend_connect_routing.rb +53 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/suspend_execute_routing.rb +52 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/suspend_until_corresponding_host_found_connect_routing.rb +83 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status.rb +68 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status_builder.rb +244 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status_info.rb +30 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status_monitor.rb +564 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status_provider.rb +414 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/switchover_state.rb +98 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/switchover_timer.rb +46 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/custom_endpoint_monitor.rb +266 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/custom_endpoint_plugin.rb +158 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/info.rb +111 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/member_list_type.rb +31 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/role.rb +45 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/default_plugin.rb +108 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/failover_mode.rb +43 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/failover_plugin.rb +467 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/gdb/gdb_failover_mode.rb +68 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/gdb/gdb_failover_plugin.rb +403 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/iam_auth_plugin.rb +159 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/initial_connection_strategy_plugin.rb +485 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/audit_logger.rb +157 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/column_cipher.rb +159 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/column_encryption_config.rb +61 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/connection_source.rb +91 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/data_key_cache.rb +220 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/encryption_algorithm.rb +75 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/encryption_config.rb +146 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/encryption_service.rb +391 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/error_context.rb +198 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/errors.rb +259 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/key_management_utility.rb +435 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/key_manager.rb +378 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/key_metadata.rb +86 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/kms_encryption_plugin.rb +890 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/kms_encryption_utility.rb +281 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/metadata_manager.rb +332 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/sanitizer.rb +147 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/schema_name.rb +70 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/schema_validator.rb +211 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/sql_runner.rb +147 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/type_marker.rb +109 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/secrets_manager_plugin.rb +358 -0
- data/lib/aws_advanced_ruby_driver_wrapper/postgresql.rb +659 -0
- data/lib/aws_advanced_ruby_driver_wrapper/property_definition.rb +409 -0
- data/lib/aws_advanced_ruby_driver_wrapper/ruby_method.rb +122 -0
- data/lib/aws_advanced_ruby_driver_wrapper/services/connection_service.rb +143 -0
- data/lib/aws_advanced_ruby_driver_wrapper/services/dialect_service.rb +267 -0
- data/lib/aws_advanced_ruby_driver_wrapper/services/host_service.rb +199 -0
- data/lib/aws_advanced_ruby_driver_wrapper/services/monitor_service.rb +186 -0
- data/lib/aws_advanced_ruby_driver_wrapper/services/plugin_call_context.rb +63 -0
- data/lib/aws_advanced_ruby_driver_wrapper/services/plugin_manager.rb +273 -0
- data/lib/aws_advanced_ruby_driver_wrapper/services/service_container.rb +30 -0
- data/lib/aws_advanced_ruby_driver_wrapper/services/service_utility.rb +78 -0
- data/lib/aws_advanced_ruby_driver_wrapper/services/session_state_service.rb +56 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/accessible_regions.rb +52 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/ar_constants.rb +25 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/aurora_topology_utils.rb +99 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/aws_credentials_utils.rb +62 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/connection_config.rb +91 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/connection_config_parser.rb +368 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/conversion_utils.rb +51 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/events/batching_event_publisher.rb +119 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/events/data_access_event.rb +26 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/events/monitor_reset_event.rb +26 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/global_aurora_topology_utils.rb +185 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/host_list_utils.rb +27 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/iam_auth_utils.rb +112 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/multi_az_topology_utils.rb +117 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/encryption_annotation_parser.rb +99 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/mysql_statement_analyzer.rb +641 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/pg_statement_analyzer.rb +502 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/query_analysis.rb +63 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/query_type.rb +35 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/routing_hint.rb +27 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/routing_hint_parser.rb +50 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/sql_parser.rb +139 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/rds_url_type.rb +71 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/rds_utils.rb +575 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/retry_util.rb +153 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/sql_encoding.rb +56 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/sql_method_analyzer.rb +195 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/storage/cache_entry.rb +56 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/storage/expiration_cache.rb +108 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/storage/sliding_expiration_cache.rb +137 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/storage/storage_service.rb +172 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/topology_utils.rb +127 -0
- data/lib/aws_advanced_ruby_driver_wrapper/version.rb +19 -0
- data/lib/aws_advanced_ruby_driver_wrapper/wrapper_property.rb +64 -0
- data/lib/aws_advanced_ruby_driver_wrapper.rb +116 -0
- 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
|