activerecord 7.0.0 → 7.1.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 +4 -4
- data/CHANGELOG.md +1607 -1040
- data/MIT-LICENSE +1 -1
- data/README.rdoc +17 -18
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/association.rb +18 -3
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +14 -6
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +21 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +17 -12
- data/lib/active_record/associations/collection_proxy.rb +22 -12
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +27 -17
- data/lib/active_record/associations/has_many_through_association.rb +10 -6
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency.rb +20 -14
- data/lib/active_record/associations/preloader/association.rb +27 -6
- data/lib/active_record/associations/preloader/through_association.rb +1 -1
- data/lib/active_record/associations/preloader.rb +13 -10
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +345 -219
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/dirty.rb +40 -26
- data/lib/active_record/attribute_methods/primary_key.rb +76 -24
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +18 -5
- data/lib/active_record/attribute_methods/serialization.rb +172 -69
- data/lib/active_record/attribute_methods/write.rb +3 -3
- data/lib/active_record/attribute_methods.rb +110 -28
- data/lib/active_record/attributes.rb +3 -3
- data/lib/active_record/autosave_association.rb +56 -10
- data/lib/active_record/base.rb +10 -5
- data/lib/active_record/callbacks.rb +16 -32
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -34
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +164 -89
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +63 -43
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +128 -32
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +60 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +52 -8
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +163 -29
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +302 -129
- data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
- data/lib/active_record/connection_adapters/abstract_adapter.rb +504 -106
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +217 -104
- data/lib/active_record/connection_adapters/column.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +23 -144
- data/lib/active_record/connection_adapters/mysql/quoting.rb +29 -12
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +38 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
- data/lib/active_record/connection_adapters/pool_config.rb +14 -5
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +72 -45
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +41 -8
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +6 -10
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +358 -57
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +343 -181
- data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
- data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +45 -39
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +22 -5
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +41 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +242 -81
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
- data/lib/active_record/connection_adapters.rb +3 -1
- data/lib/active_record/connection_handling.rb +73 -96
- data/lib/active_record/core.rb +136 -148
- data/lib/active_record/counter_cache.rb +46 -25
- data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -0
- data/lib/active_record/database_configurations/database_config.rb +9 -3
- data/lib/active_record/database_configurations/hash_config.rb +22 -12
- data/lib/active_record/database_configurations/url_config.rb +17 -11
- data/lib/active_record/database_configurations.rb +87 -34
- data/lib/active_record/delegated_type.rb +9 -4
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +2 -0
- data/lib/active_record/disable_joins_association_relation.rb +1 -1
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
- data/lib/active_record/encryption/config.rb +25 -1
- data/lib/active_record/encryption/configurable.rb +13 -14
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +8 -4
- data/lib/active_record/encryption/derived_secret_key_provider.rb +9 -3
- data/lib/active_record/encryption/deterministic_key_provider.rb +1 -1
- data/lib/active_record/encryption/encryptable_record.rb +38 -22
- data/lib/active_record/encryption/encrypted_attribute_type.rb +19 -8
- data/lib/active_record/encryption/encryptor.rb +7 -7
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +3 -3
- data/lib/active_record/encryption/extended_deterministic_queries.rb +83 -71
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
- data/lib/active_record/encryption/key_generator.rb +12 -1
- data/lib/active_record/encryption/message.rb +1 -1
- data/lib/active_record/encryption/message_serializer.rb +2 -0
- data/lib/active_record/encryption/properties.rb +4 -4
- data/lib/active_record/encryption/scheme.rb +20 -23
- data/lib/active_record/encryption.rb +1 -0
- data/lib/active_record/enum.rb +114 -27
- data/lib/active_record/errors.rb +108 -15
- data/lib/active_record/explain.rb +23 -3
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +29 -8
- data/lib/active_record/fixtures.rb +121 -73
- data/lib/active_record/future_result.rb +30 -5
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +30 -16
- data/lib/active_record/insert_all.rb +55 -8
- data/lib/active_record/integration.rb +10 -10
- data/lib/active_record/internal_metadata.rb +118 -30
- data/lib/active_record/locking/optimistic.rb +32 -18
- data/lib/active_record/locking/pessimistic.rb +8 -5
- data/lib/active_record/log_subscriber.rb +39 -17
- data/lib/active_record/marshalling.rb +56 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
- data/lib/active_record/middleware/database_selector.rb +18 -13
- data/lib/active_record/middleware/shard_selector.rb +7 -5
- data/lib/active_record/migration/command_recorder.rb +104 -9
- data/lib/active_record/migration/compatibility.rb +158 -64
- data/lib/active_record/migration/default_strategy.rb +23 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration.rb +271 -117
- data/lib/active_record/model_schema.rb +82 -50
- data/lib/active_record/nested_attributes.rb +23 -3
- data/lib/active_record/normalization.rb +159 -0
- data/lib/active_record/persistence.rb +200 -47
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +3 -21
- data/lib/active_record/query_logs.rb +87 -51
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +16 -3
- data/lib/active_record/railtie.rb +127 -61
- data/lib/active_record/railties/controller_runtime.rb +12 -8
- data/lib/active_record/railties/databases.rake +142 -143
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +32 -5
- data/lib/active_record/reflection.rb +177 -45
- data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
- data/lib/active_record/relation/batches.rb +190 -61
- data/lib/active_record/relation/calculations.rb +200 -83
- data/lib/active_record/relation/delegation.rb +23 -9
- data/lib/active_record/relation/finder_methods.rb +77 -16
- data/lib/active_record/relation/merger.rb +2 -0
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +26 -14
- data/lib/active_record/relation/query_attribute.rb +25 -1
- data/lib/active_record/relation/query_methods.rb +429 -76
- data/lib/active_record/relation/spawn_methods.rb +18 -1
- data/lib/active_record/relation.rb +98 -41
- data/lib/active_record/result.rb +25 -9
- data/lib/active_record/runtime_registry.rb +10 -1
- data/lib/active_record/sanitization.rb +57 -16
- data/lib/active_record/schema.rb +36 -22
- data/lib/active_record/schema_dumper.rb +65 -23
- data/lib/active_record/schema_migration.rb +68 -33
- data/lib/active_record/scoping/default.rb +20 -12
- data/lib/active_record/scoping/named.rb +2 -2
- data/lib/active_record/scoping.rb +2 -1
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/serialization.rb +5 -0
- data/lib/active_record/signed_id.rb +9 -7
- data/lib/active_record/store.rb +16 -11
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +16 -3
- data/lib/active_record/tasks/database_tasks.rb +138 -107
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +17 -15
- data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
- data/lib/active_record/test_fixtures.rb +123 -99
- data/lib/active_record/timestamp.rb +26 -14
- data/lib/active_record/token_for.rb +113 -0
- data/lib/active_record/touch_later.rb +11 -6
- data/lib/active_record/transactions.rb +39 -13
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +8 -4
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +3 -3
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +50 -5
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +143 -16
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +1 -1
- data/lib/arel/nodes/and.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -1
- data/lib/arel/nodes/bound_sql_literal.rb +61 -0
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/filter.rb +1 -1
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/homogeneous_in.rb +0 -8
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/node.rb +111 -2
- data/lib/arel/nodes/sql_literal.rb +6 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes.rb +4 -0
- data/lib/arel/predications.rb +2 -0
- data/lib/arel/table.rb +9 -5
- data/lib/arel/visitors/mysql.rb +8 -1
- data/lib/arel/visitors/to_sql.rb +81 -17
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +16 -2
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- metadata +50 -15
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
@@ -18,20 +18,22 @@ module ActiveRecord
|
|
18
18
|
#
|
19
19
|
# === Options
|
20
20
|
#
|
21
|
-
# * <tt>:key_provider</tt> -
|
22
|
-
#
|
21
|
+
# * <tt>:key_provider</tt> - A key provider to provide encryption and decryption keys. Defaults to
|
22
|
+
# +ActiveRecord::Encryption.key_provider+.
|
23
23
|
# * <tt>:key</tt> - A password to derive the key from. It's a shorthand for a +:key_provider+ that
|
24
24
|
# serves derivated keys. Both options can't be used at the same time.
|
25
|
-
# * <tt>:key_provider</tt> - Set a +:key_provider+ to provide encryption and decryption keys. If not
|
26
|
-
# provided, it will default to the key provider set with `config.key_provider`.
|
27
25
|
# * <tt>:deterministic</tt> - By default, encryption is not deterministic. It will use a random
|
28
26
|
# initialization vector for each encryption operation. This means that encrypting the same content
|
29
27
|
# with the same key twice will generate different ciphertexts. When set to +true+, it will generate the
|
30
28
|
# initialization vector based on the encrypted content. This means that the same content will generate
|
31
29
|
# the same ciphertexts. This enables querying encrypted text with Active Record. Deterministic encryption
|
32
30
|
# will use the oldest encryption scheme to encrypt new data by default. You can change this by setting
|
33
|
-
#
|
31
|
+
# <tt>deterministic: { fixed: false }</tt>. That will make it use the newest encryption scheme for encrypting new
|
34
32
|
# data.
|
33
|
+
# * <tt>:support_unencrypted_data</tt> - If `config.active_record.encryption.support_unencrypted_data` is +true+,
|
34
|
+
# you can set this to +false+ to opt out of unencrypted data support for this attribute. This is useful for
|
35
|
+
# scenarios where you encrypt one column, and want to disable support for unencrypted data without having to tweak
|
36
|
+
# the global setting.
|
35
37
|
# * <tt>:downcase</tt> - When true, it converts the encrypted content to downcase automatically. This allows to
|
36
38
|
# effectively ignore case when querying data. Notice that the case is lost. Use +:ignore_case+ if you are interested
|
37
39
|
# in preserving it.
|
@@ -44,13 +46,11 @@ module ActiveRecord
|
|
44
46
|
# * <tt>:previous</tt> - List of previous encryption schemes. When provided, they will be used in order when trying to read
|
45
47
|
# the attribute. Each entry of the list can contain the properties supported by #encrypts. Also, when deterministic
|
46
48
|
# encryption is used, they will be used to generate additional ciphertexts to check in the queries.
|
47
|
-
def encrypts(*names, key_provider: nil, key: nil, deterministic: false, downcase: false, ignore_case: false, previous: [], **context_properties)
|
49
|
+
def encrypts(*names, key_provider: nil, key: nil, deterministic: false, support_unencrypted_data: nil, downcase: false, ignore_case: false, previous: [], **context_properties)
|
48
50
|
self.encrypted_attributes ||= Set.new # not using :default because the instance would be shared across classes
|
49
|
-
scheme = scheme_for key_provider: key_provider, key: key, deterministic: deterministic, downcase: downcase, \
|
50
|
-
ignore_case: ignore_case, previous: previous, **context_properties
|
51
51
|
|
52
52
|
names.each do |name|
|
53
|
-
encrypt_attribute name,
|
53
|
+
encrypt_attribute name, key_provider: key_provider, key: key, deterministic: deterministic, support_unencrypted_data: support_unencrypted_data, downcase: downcase, ignore_case: ignore_case, previous: previous, **context_properties
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
@@ -63,32 +63,35 @@ module ActiveRecord
|
|
63
63
|
|
64
64
|
# Given a attribute name, it returns the name of the source attribute when it's a preserved one.
|
65
65
|
def source_attribute_from_preserved_attribute(attribute_name)
|
66
|
-
attribute_name.to_s.sub(ORIGINAL_ATTRIBUTE_PREFIX, "") if
|
66
|
+
attribute_name.to_s.sub(ORIGINAL_ATTRIBUTE_PREFIX, "") if attribute_name.start_with?(ORIGINAL_ATTRIBUTE_PREFIX)
|
67
67
|
end
|
68
68
|
|
69
69
|
private
|
70
|
-
def scheme_for(key_provider: nil, key: nil, deterministic: false, downcase: false, ignore_case: false, previous: [], **context_properties)
|
70
|
+
def scheme_for(key_provider: nil, key: nil, deterministic: false, support_unencrypted_data: nil, downcase: false, ignore_case: false, previous: [], **context_properties)
|
71
71
|
ActiveRecord::Encryption::Scheme.new(key_provider: key_provider, key: key, deterministic: deterministic,
|
72
|
-
|
72
|
+
support_unencrypted_data: support_unencrypted_data, downcase: downcase, ignore_case: ignore_case, **context_properties).tap do |scheme|
|
73
73
|
scheme.previous_schemes = global_previous_schemes_for(scheme) +
|
74
|
-
|
74
|
+
Array.wrap(previous).collect { |scheme_config| ActiveRecord::Encryption::Scheme.new(**scheme_config) }
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
78
78
|
def global_previous_schemes_for(scheme)
|
79
|
-
ActiveRecord::Encryption.config.previous_schemes.
|
80
|
-
scheme.merge(previous_scheme)
|
79
|
+
ActiveRecord::Encryption.config.previous_schemes.filter_map do |previous_scheme|
|
80
|
+
scheme.merge(previous_scheme) if scheme.compatible_with?(previous_scheme)
|
81
81
|
end
|
82
82
|
end
|
83
83
|
|
84
|
-
def encrypt_attribute(name,
|
84
|
+
def encrypt_attribute(name, key_provider: nil, key: nil, deterministic: false, support_unencrypted_data: nil, downcase: false, ignore_case: false, previous: [], **context_properties)
|
85
85
|
encrypted_attributes << name.to_sym
|
86
86
|
|
87
87
|
attribute name do |cast_type|
|
88
|
-
|
88
|
+
scheme = scheme_for key_provider: key_provider, key: key, deterministic: deterministic, support_unencrypted_data: support_unencrypted_data, \
|
89
|
+
downcase: downcase, ignore_case: ignore_case, previous: previous, **context_properties
|
90
|
+
|
91
|
+
ActiveRecord::Encryption::EncryptedAttributeType.new(scheme: scheme, cast_type: cast_type, default: columns_hash[name.to_s]&.default)
|
89
92
|
end
|
90
93
|
|
91
|
-
preserve_original_encrypted(name) if
|
94
|
+
preserve_original_encrypted(name) if ignore_case
|
92
95
|
ActiveRecord::Encryption.encrypted_attribute_was_declared(self, name)
|
93
96
|
end
|
94
97
|
|
@@ -141,12 +144,16 @@ module ActiveRecord
|
|
141
144
|
|
142
145
|
# Returns whether a given attribute is encrypted or not.
|
143
146
|
def encrypted_attribute?(attribute_name)
|
144
|
-
ActiveRecord::Encryption.encryptor.encrypted?
|
147
|
+
ActiveRecord::Encryption.encryptor.encrypted? read_attribute_before_type_cast(attribute_name)
|
145
148
|
end
|
146
149
|
|
147
150
|
# Returns the ciphertext for +attribute_name+.
|
148
151
|
def ciphertext_for(attribute_name)
|
149
|
-
|
152
|
+
if encrypted_attribute?(attribute_name)
|
153
|
+
read_attribute_before_type_cast(attribute_name)
|
154
|
+
else
|
155
|
+
read_attribute_for_database(attribute_name)
|
156
|
+
end
|
150
157
|
end
|
151
158
|
|
152
159
|
# Encrypts all the encryptable attributes and saves the changes.
|
@@ -162,6 +169,15 @@ module ActiveRecord
|
|
162
169
|
private
|
163
170
|
ORIGINAL_ATTRIBUTE_PREFIX = "original_"
|
164
171
|
|
172
|
+
def _create_record(attribute_names = self.attribute_names)
|
173
|
+
if has_encrypted_attributes?
|
174
|
+
# Always persist encrypted attributes, because an attribute might be
|
175
|
+
# encrypting a column default value.
|
176
|
+
attribute_names |= self.class.encrypted_attributes.map(&:to_s)
|
177
|
+
end
|
178
|
+
super
|
179
|
+
end
|
180
|
+
|
165
181
|
def encrypt_attributes
|
166
182
|
validate_encryption_allowed
|
167
183
|
|
@@ -190,12 +206,12 @@ module ActiveRecord
|
|
190
206
|
end
|
191
207
|
|
192
208
|
def build_decrypt_attribute_assignments
|
193
|
-
Array(self.class.encrypted_attributes).
|
209
|
+
Array(self.class.encrypted_attributes).to_h do |attribute_name|
|
194
210
|
type = type_for_attribute(attribute_name)
|
195
211
|
encrypted_value = ciphertext_for(attribute_name)
|
196
212
|
new_value = type.deserialize(encrypted_value)
|
197
213
|
[attribute_name, new_value]
|
198
|
-
end
|
214
|
+
end
|
199
215
|
end
|
200
216
|
|
201
217
|
def cant_modify_encrypted_attributes_when_frozen
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Encryption
|
5
|
-
# An
|
5
|
+
# An ActiveModel::Type::Value that encrypts/decrypts strings of text.
|
6
6
|
#
|
7
7
|
# This is the central piece that connects the encryption system with +encrypts+ declarations in the
|
8
8
|
# model classes. Whenever you declare an attribute as encrypted, it configures an +EncryptedAttributeType+
|
@@ -19,12 +19,17 @@ module ActiveRecord
|
|
19
19
|
#
|
20
20
|
# * <tt>:scheme</tt> - A +Scheme+ with the encryption properties for this attribute.
|
21
21
|
# * <tt>:cast_type</tt> - A type that will be used to serialize (before encrypting) and deserialize
|
22
|
-
# (after decrypting).
|
23
|
-
def initialize(scheme:, cast_type: ActiveModel::Type::String.new, previous_type: false)
|
22
|
+
# (after decrypting). ActiveModel::Type::String by default.
|
23
|
+
def initialize(scheme:, cast_type: ActiveModel::Type::String.new, previous_type: false, default: nil)
|
24
24
|
super()
|
25
25
|
@scheme = scheme
|
26
26
|
@cast_type = cast_type
|
27
27
|
@previous_type = previous_type
|
28
|
+
@default = default
|
29
|
+
end
|
30
|
+
|
31
|
+
def cast(value)
|
32
|
+
cast_type.cast(value)
|
28
33
|
end
|
29
34
|
|
30
35
|
def deserialize(value)
|
@@ -49,6 +54,10 @@ module ActiveRecord
|
|
49
54
|
@previous_types[support_unencrypted_data?] ||= build_previous_types_for(previous_schemes_including_clean_text)
|
50
55
|
end
|
51
56
|
|
57
|
+
def support_unencrypted_data?
|
58
|
+
ActiveRecord::Encryption.config.support_unencrypted_data && scheme.support_unencrypted_data? && !previous_type?
|
59
|
+
end
|
60
|
+
|
52
61
|
private
|
53
62
|
def previous_schemes_including_clean_text
|
54
63
|
previous_schemes.including((clean_text_scheme if support_unencrypted_data?)).compact
|
@@ -70,7 +79,13 @@ module ActiveRecord
|
|
70
79
|
|
71
80
|
def decrypt(value)
|
72
81
|
with_context do
|
73
|
-
|
82
|
+
unless value.nil?
|
83
|
+
if @default && @default == value
|
84
|
+
value
|
85
|
+
else
|
86
|
+
encryptor.decrypt(value, **decryption_options)
|
87
|
+
end
|
88
|
+
end
|
74
89
|
end
|
75
90
|
rescue ActiveRecord::Encryption::Errors::Base => error
|
76
91
|
if previous_types_without_clean_text.blank?
|
@@ -120,10 +135,6 @@ module ActiveRecord
|
|
120
135
|
ActiveRecord::Encryption.encryptor
|
121
136
|
end
|
122
137
|
|
123
|
-
def support_unencrypted_data?
|
124
|
-
ActiveRecord::Encryption.config.support_unencrypted_data && !previous_type?
|
125
|
-
end
|
126
|
-
|
127
138
|
def encryption_options
|
128
139
|
@encryption_options ||= { key_provider: key_provider, cipher_options: { deterministic: deterministic? } }.compact
|
129
140
|
end
|
@@ -6,17 +6,17 @@ require "active_support/core_ext/numeric"
|
|
6
6
|
|
7
7
|
module ActiveRecord
|
8
8
|
module Encryption
|
9
|
-
# An encryptor exposes the encryption API that
|
9
|
+
# An encryptor exposes the encryption API that ActiveRecord::Encryption::EncryptedAttributeType
|
10
10
|
# uses for encrypting and decrypting attribute values.
|
11
11
|
#
|
12
|
-
# It interacts with a
|
13
|
-
#
|
12
|
+
# It interacts with a KeyProvider for getting the keys, and delegate to
|
13
|
+
# ActiveRecord::Encryption::Cipher the actual encryption algorithm.
|
14
14
|
class Encryptor
|
15
15
|
# Encrypts +clean_text+ and returns the encrypted result
|
16
16
|
#
|
17
17
|
# Internally, it will:
|
18
18
|
#
|
19
|
-
# 1. Create a new
|
19
|
+
# 1. Create a new ActiveRecord::Encryption::Message
|
20
20
|
# 2. Compress and encrypt +clean_text+ as the message payload
|
21
21
|
# 3. Serialize it with +ActiveRecord::Encryption.message_serializer+ (+ActiveRecord::Encryption::SafeMarshal+
|
22
22
|
# by default)
|
@@ -26,10 +26,10 @@ module ActiveRecord
|
|
26
26
|
#
|
27
27
|
# [:key_provider]
|
28
28
|
# Key provider to use for the encryption operation. It will default to
|
29
|
-
# +ActiveRecord::Encryption.key_provider+ when not provided
|
29
|
+
# +ActiveRecord::Encryption.key_provider+ when not provided.
|
30
30
|
#
|
31
31
|
# [:cipher_options]
|
32
|
-
#
|
32
|
+
# Cipher-specific options that will be passed to the Cipher configured in
|
33
33
|
# +ActiveRecord::Encryption.cipher+
|
34
34
|
def encrypt(clear_text, key_provider: default_key_provider, cipher_options: {})
|
35
35
|
clear_text = force_encoding_if_needed(clear_text) if cipher_options[:deterministic]
|
@@ -47,7 +47,7 @@ module ActiveRecord
|
|
47
47
|
# +ActiveRecord::Encryption.key_provider+ when not provided
|
48
48
|
#
|
49
49
|
# [:cipher_options]
|
50
|
-
#
|
50
|
+
# Cipher-specific options that will be passed to the Cipher configured in
|
51
51
|
# +ActiveRecord::Encryption.cipher+
|
52
52
|
def decrypt(encrypted_text, key_provider: default_key_provider, cipher_options: {})
|
53
53
|
message = deserialize_message(encrypted_text)
|
@@ -4,13 +4,13 @@ module ActiveRecord
|
|
4
4
|
module Encryption
|
5
5
|
# Implements a simple envelope encryption approach where:
|
6
6
|
#
|
7
|
-
# * It generates a random data-encryption key for each encryption operation
|
7
|
+
# * It generates a random data-encryption key for each encryption operation.
|
8
8
|
# * It stores the generated key along with the encrypted payload. It encrypts this key
|
9
|
-
# with the master key provided in the
|
9
|
+
# with the master key provided in the +active_record_encryption.primary_key+ credential.
|
10
10
|
#
|
11
11
|
# This provider can work with multiple master keys. It will use the last one for encrypting.
|
12
12
|
#
|
13
|
-
# When
|
13
|
+
# When +config.active_record.encryption.store_key_references+ is true, it will also store a reference to
|
14
14
|
# the specific master key that was used to encrypt the data-encryption key. When not set,
|
15
15
|
# it will try all the configured master keys looking for the right one, in order to
|
16
16
|
# return the right decryption key.
|
@@ -1,119 +1,131 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Automatically expand encrypted arguments to support querying both encrypted and unencrypted data
|
4
|
-
#
|
5
|
-
# Active Record Encryption supports querying the db using deterministic attributes. For example:
|
6
|
-
#
|
7
|
-
# Contact.find_by(email_address: "jorge@hey.com")
|
8
|
-
#
|
9
|
-
# The value "jorge@hey.com" will get encrypted automatically to perform the query. But there is
|
10
|
-
# a problem while the data is being encrypted. This won't work. During that time, you need these
|
11
|
-
# queries to be:
|
12
|
-
#
|
13
|
-
# Contact.find_by(email_address: [ "jorge@hey.com", "<encrypted jorge@hey.com>" ])
|
14
|
-
#
|
15
|
-
# This patches ActiveRecord to support this automatically. It addresses both:
|
16
|
-
#
|
17
|
-
# * ActiveRecord::Base: Used in +Contact.find_by_email_address(...)+
|
18
|
-
# * ActiveRecord::Relation: Used in +Contact.internal.find_by_email_address(...)+
|
19
|
-
#
|
20
|
-
# +ActiveRecord::Base+ relies on +ActiveRecord::Relation+ (+ActiveRecord::QueryMethods+) but it does
|
21
|
-
# some prepared statements caching. That's why we need to intercept +ActiveRecord::Base+ as soon
|
22
|
-
# as it's invoked (so that the proper prepared statement is cached).
|
23
|
-
#
|
24
|
-
# When modifying this file run performance tests in +test/performance/extended_deterministic_queries_performance_test.rb+ to
|
25
|
-
# make sure performance overhead is acceptable.
|
26
|
-
#
|
27
|
-
# We will extend this to support previous "encryption context" versions in future iterations
|
28
|
-
#
|
29
|
-
# @TODO Experimental. Support for every kind of query is pending
|
30
|
-
# @TODO It should not patch anything if not needed (no previous schemes or no support for previous encryption schemes)
|
31
3
|
module ActiveRecord
|
32
4
|
module Encryption
|
5
|
+
# Automatically expand encrypted arguments to support querying both encrypted and unencrypted data
|
6
|
+
#
|
7
|
+
# Active Record \Encryption supports querying the db using deterministic attributes. For example:
|
8
|
+
#
|
9
|
+
# Contact.find_by(email_address: "jorge@hey.com")
|
10
|
+
#
|
11
|
+
# The value "jorge@hey.com" will get encrypted automatically to perform the query. But there is
|
12
|
+
# a problem while the data is being encrypted. This won't work. During that time, you need these
|
13
|
+
# queries to be:
|
14
|
+
#
|
15
|
+
# Contact.find_by(email_address: [ "jorge@hey.com", "<encrypted jorge@hey.com>" ])
|
16
|
+
#
|
17
|
+
# This patches ActiveRecord to support this automatically. It addresses both:
|
18
|
+
#
|
19
|
+
# * ActiveRecord::Base - Used in <tt>Contact.find_by_email_address(...)</tt>
|
20
|
+
# * ActiveRecord::Relation - Used in <tt>Contact.internal.find_by_email_address(...)</tt>
|
21
|
+
#
|
22
|
+
# This module is included if `config.active_record.encryption.extend_queries` is `true`.
|
33
23
|
module ExtendedDeterministicQueries
|
34
24
|
def self.install_support
|
25
|
+
# ActiveRecord::Base relies on ActiveRecord::Relation (ActiveRecord::QueryMethods) but it does
|
26
|
+
# some prepared statements caching. That's why we need to intercept +ActiveRecord::Base+ as soon
|
27
|
+
# as it's invoked (so that the proper prepared statement is cached).
|
35
28
|
ActiveRecord::Relation.prepend(RelationQueries)
|
36
29
|
ActiveRecord::Base.include(CoreQueries)
|
37
30
|
ActiveRecord::Encryption::EncryptedAttributeType.prepend(ExtendedEncryptableType)
|
38
31
|
Arel::Nodes::HomogeneousIn.prepend(InWithAdditionalValues)
|
39
32
|
end
|
40
33
|
|
41
|
-
|
42
|
-
|
34
|
+
# When modifying this file run performance tests in
|
35
|
+
# +activerecord/test/cases/encryption/performance/extended_deterministic_queries_performance_test.rb+
|
36
|
+
# to make sure performance overhead is acceptable.
|
37
|
+
#
|
38
|
+
# @TODO We will extend this to support previous "encryption context" versions in future iterations
|
39
|
+
# @TODO Experimental. Support for every kind of query is pending
|
40
|
+
# @TODO It should not patch anything if not needed (no previous schemes or no support for previous encryption schemes)
|
41
|
+
|
42
|
+
module EncryptedQuery # :nodoc:
|
43
|
+
class << self
|
44
|
+
def process_arguments(owner, args, check_for_additional_values)
|
45
|
+
return args if owner.deterministic_encrypted_attributes&.empty?
|
43
46
|
|
44
|
-
private
|
45
|
-
def process_encrypted_query_arguments(args, check_for_additional_values)
|
46
47
|
if args.is_a?(Array) && (options = args.first).is_a?(Hash)
|
47
|
-
|
48
|
-
|
48
|
+
options = options.transform_keys do |key|
|
49
|
+
if key.is_a?(Array)
|
50
|
+
key.map(&:to_s)
|
51
|
+
else
|
52
|
+
key.to_s
|
53
|
+
end
|
54
|
+
end
|
55
|
+
args[0] = options
|
56
|
+
|
57
|
+
owner.deterministic_encrypted_attributes&.each do |attribute_name|
|
58
|
+
attribute_name = attribute_name.to_s
|
59
|
+
type = owner.type_for_attribute(attribute_name)
|
49
60
|
if !type.previous_types.empty? && value = options[attribute_name]
|
50
61
|
options[attribute_name] = process_encrypted_query_argument(value, check_for_additional_values, type)
|
51
62
|
end
|
52
63
|
end
|
53
64
|
end
|
54
|
-
end
|
55
65
|
|
56
|
-
|
57
|
-
|
66
|
+
args
|
67
|
+
end
|
58
68
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
69
|
+
private
|
70
|
+
def process_encrypted_query_argument(value, check_for_additional_values, type)
|
71
|
+
return value if check_for_additional_values && value.is_a?(Array) && value.last.is_a?(AdditionalValue)
|
72
|
+
|
73
|
+
case value
|
74
|
+
when String, Array
|
75
|
+
list = Array(value)
|
76
|
+
list + list.flat_map do |each_value|
|
77
|
+
if check_for_additional_values && each_value.is_a?(AdditionalValue)
|
78
|
+
each_value
|
79
|
+
else
|
80
|
+
additional_values_for(each_value, type)
|
81
|
+
end
|
67
82
|
end
|
83
|
+
else
|
84
|
+
value
|
68
85
|
end
|
69
|
-
else
|
70
|
-
value
|
71
86
|
end
|
72
|
-
end
|
73
87
|
|
74
|
-
|
75
|
-
|
76
|
-
|
88
|
+
def additional_values_for(value, type)
|
89
|
+
type.previous_types.collect do |additional_type|
|
90
|
+
AdditionalValue.new(value, additional_type)
|
91
|
+
end
|
77
92
|
end
|
78
|
-
|
93
|
+
end
|
79
94
|
end
|
80
95
|
|
81
96
|
module RelationQueries
|
82
|
-
include EncryptedQueryArgumentProcessor
|
83
|
-
|
84
97
|
def where(*args)
|
85
|
-
|
86
|
-
super
|
98
|
+
super(*EncryptedQuery.process_arguments(self, args, true))
|
87
99
|
end
|
88
100
|
|
89
101
|
def exists?(*args)
|
90
|
-
|
91
|
-
super
|
102
|
+
super(*EncryptedQuery.process_arguments(self, args, true))
|
92
103
|
end
|
93
104
|
|
94
|
-
def
|
95
|
-
|
96
|
-
end
|
105
|
+
def scope_for_create
|
106
|
+
return super unless klass.deterministic_encrypted_attributes&.any?
|
97
107
|
|
98
|
-
|
99
|
-
|
100
|
-
end
|
108
|
+
scope_attributes = super
|
109
|
+
wheres = where_values_hash
|
101
110
|
|
102
|
-
|
103
|
-
|
104
|
-
|
111
|
+
klass.deterministic_encrypted_attributes.each do |attribute_name|
|
112
|
+
attribute_name = attribute_name.to_s
|
113
|
+
values = wheres[attribute_name]
|
114
|
+
if values.is_a?(Array) && values[1..].all?(AdditionalValue)
|
115
|
+
scope_attributes[attribute_name] = values.first
|
116
|
+
end
|
105
117
|
end
|
118
|
+
|
119
|
+
scope_attributes
|
120
|
+
end
|
106
121
|
end
|
107
122
|
|
108
123
|
module CoreQueries
|
109
124
|
extend ActiveSupport::Concern
|
110
125
|
|
111
126
|
class_methods do
|
112
|
-
include EncryptedQueryArgumentProcessor
|
113
|
-
|
114
127
|
def find_by(*args)
|
115
|
-
|
116
|
-
super
|
128
|
+
super(*EncryptedQuery.process_arguments(self, args, false))
|
117
129
|
end
|
118
130
|
end
|
119
131
|
end
|
@@ -12,9 +12,9 @@ module ActiveRecord
|
|
12
12
|
super(record, attribute, value)
|
13
13
|
|
14
14
|
klass = record.class
|
15
|
-
klass.deterministic_encrypted_attributes&.
|
16
|
-
encrypted_type = klass.type_for_attribute(
|
17
|
-
|
15
|
+
if klass.deterministic_encrypted_attributes&.include?(attribute)
|
16
|
+
encrypted_type = klass.type_for_attribute(attribute)
|
17
|
+
encrypted_type.previous_types.each do |type|
|
18
18
|
encrypted_value = type.serialize(value)
|
19
19
|
ActiveRecord::Encryption.without_encryption do
|
20
20
|
super(record, attribute, encrypted_value)
|
@@ -6,6 +6,12 @@ module ActiveRecord
|
|
6
6
|
module Encryption
|
7
7
|
# Utility for generating and deriving random keys.
|
8
8
|
class KeyGenerator
|
9
|
+
attr_reader :hash_digest_class
|
10
|
+
|
11
|
+
def initialize(hash_digest_class: ActiveRecord::Encryption.config.hash_digest_class)
|
12
|
+
@hash_digest_class = hash_digest_class
|
13
|
+
end
|
14
|
+
|
9
15
|
# Returns a random key. The key will have a size in bytes of +:length+ (configured +Cipher+'s length by default)
|
10
16
|
def generate_random_key(length: key_length)
|
11
17
|
SecureRandom.random_bytes(length)
|
@@ -30,10 +36,15 @@ module ActiveRecord
|
|
30
36
|
#
|
31
37
|
# The generated key will be salted with the value of +ActiveRecord::Encryption.key_derivation_salt+
|
32
38
|
def derive_key_from(password, length: key_length)
|
33
|
-
ActiveSupport::KeyGenerator.new(password
|
39
|
+
ActiveSupport::KeyGenerator.new(password, hash_digest_class: hash_digest_class)
|
40
|
+
.generate_key(key_derivation_salt, length)
|
34
41
|
end
|
35
42
|
|
36
43
|
private
|
44
|
+
def key_derivation_salt
|
45
|
+
@key_derivation_salt ||= ActiveRecord::Encryption.config.key_derivation_salt
|
46
|
+
end
|
47
|
+
|
37
48
|
def key_length
|
38
49
|
@key_length ||= ActiveRecord::Encryption.cipher.key_length
|
39
50
|
end
|
@@ -12,12 +12,12 @@ module ActiveRecord
|
|
12
12
|
#
|
13
13
|
# message.headers.encrypted_data_key # instead of message.headers[:k]
|
14
14
|
#
|
15
|
-
# See +Properties
|
15
|
+
# See +Properties::DEFAULT_PROPERTIES+, Key, Message
|
16
16
|
class Properties
|
17
|
-
ALLOWED_VALUE_CLASSES = [String, ActiveRecord::Encryption::Message, Numeric, TrueClass, FalseClass, Symbol, NilClass]
|
17
|
+
ALLOWED_VALUE_CLASSES = [String, ActiveRecord::Encryption::Message, Numeric, Integer, Float, BigDecimal, TrueClass, FalseClass, Symbol, NilClass]
|
18
18
|
|
19
19
|
delegate_missing_to :data
|
20
|
-
delegate :==, to: :data
|
20
|
+
delegate :==, :[], :each, :key?, to: :data
|
21
21
|
|
22
22
|
# For each entry it generates an accessor exposing the full name
|
23
23
|
DEFAULT_PROPERTIES = {
|
@@ -54,7 +54,7 @@ module ActiveRecord
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def validate_value_type(value)
|
57
|
-
unless ALLOWED_VALUE_CLASSES.
|
57
|
+
unless ALLOWED_VALUE_CLASSES.include?(value.class) || ALLOWED_VALUE_CLASSES.any? { |klass| value.is_a?(klass) }
|
58
58
|
raise ActiveRecord::Encryption::Errors::ForbiddenClass, "Can't store a #{value.class}, only properties of type #{ALLOWED_VALUE_CLASSES.inspect} are allowed"
|
59
59
|
end
|
60
60
|
end
|
@@ -6,11 +6,11 @@ module ActiveRecord
|
|
6
6
|
#
|
7
7
|
# It validates and serves attribute encryption options.
|
8
8
|
#
|
9
|
-
# See
|
9
|
+
# See EncryptedAttributeType, Context
|
10
10
|
class Scheme
|
11
11
|
attr_accessor :previous_schemes
|
12
12
|
|
13
|
-
def initialize(key_provider: nil, key: nil, deterministic: nil, downcase: nil, ignore_case: nil,
|
13
|
+
def initialize(key_provider: nil, key: nil, deterministic: nil, support_unencrypted_data: nil, downcase: nil, ignore_case: nil,
|
14
14
|
previous_schemes: nil, **context_properties)
|
15
15
|
# Initializing all attributes to +nil+ as we want to allow a "not set" semantics so that we
|
16
16
|
# can merge schemes without overriding values with defaults. See +#merge+
|
@@ -18,6 +18,7 @@ module ActiveRecord
|
|
18
18
|
@key_provider_param = key_provider
|
19
19
|
@key = key
|
20
20
|
@deterministic = deterministic
|
21
|
+
@support_unencrypted_data = support_unencrypted_data
|
21
22
|
@downcase = downcase || ignore_case
|
22
23
|
@ignore_case = ignore_case
|
23
24
|
@previous_schemes_param = previous_schemes
|
@@ -36,7 +37,11 @@ module ActiveRecord
|
|
36
37
|
end
|
37
38
|
|
38
39
|
def deterministic?
|
39
|
-
|
40
|
+
!!@deterministic
|
41
|
+
end
|
42
|
+
|
43
|
+
def support_unencrypted_data?
|
44
|
+
@support_unencrypted_data.nil? ? ActiveRecord::Encryption.config.support_unencrypted_data : @support_unencrypted_data
|
40
45
|
end
|
41
46
|
|
42
47
|
def fixed?
|
@@ -45,10 +50,7 @@ module ActiveRecord
|
|
45
50
|
end
|
46
51
|
|
47
52
|
def key_provider
|
48
|
-
@key_provider ||=
|
49
|
-
validate_keys!
|
50
|
-
@key_provider_param || build_key_provider
|
51
|
-
end
|
53
|
+
@key_provider ||= @key_provider_param || build_key_provider || default_key_provider
|
52
54
|
end
|
53
55
|
|
54
56
|
def merge(other_scheme)
|
@@ -56,7 +58,7 @@ module ActiveRecord
|
|
56
58
|
end
|
57
59
|
|
58
60
|
def to_h
|
59
|
-
{ key_provider: @key_provider_param,
|
61
|
+
{ key_provider: @key_provider_param, deterministic: @deterministic, downcase: @downcase, ignore_case: @ignore_case,
|
60
62
|
previous_schemes: @previous_schemes_param, **@context_properties }.compact
|
61
63
|
end
|
62
64
|
|
@@ -68,32 +70,27 @@ module ActiveRecord
|
|
68
70
|
end
|
69
71
|
end
|
70
72
|
|
73
|
+
def compatible_with?(other_scheme)
|
74
|
+
deterministic? == other_scheme.deterministic?
|
75
|
+
end
|
76
|
+
|
71
77
|
private
|
72
78
|
def validate_config!
|
73
79
|
raise Errors::Configuration, "ignore_case: can only be used with deterministic encryption" if @ignore_case && !@deterministic
|
74
80
|
raise Errors::Configuration, "key_provider: and key: can't be used simultaneously" if @key_provider_param && @key
|
75
81
|
end
|
76
82
|
|
77
|
-
def validate_keys!
|
78
|
-
validate_credential :key_derivation_salt
|
79
|
-
validate_credential :primary_key, "needs to be configured to use non-deterministic encryption" unless @deterministic
|
80
|
-
validate_credential :deterministic_key, "needs to be configured to use deterministic encryption" if @deterministic
|
81
|
-
end
|
82
|
-
|
83
|
-
def validate_credential(key, error_message = "is not configured")
|
84
|
-
unless ActiveRecord::Encryption.config.public_send(key).present?
|
85
|
-
raise Errors::Configuration, "#{key} #{error_message}. Please configure it via credential "\
|
86
|
-
"active_record_encryption.#{key} or by setting config.active_record.encryption.#{key}"
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
83
|
def build_key_provider
|
91
84
|
return DerivedSecretKeyProvider.new(@key) if @key.present?
|
92
85
|
|
93
|
-
if @deterministic
|
94
|
-
DeterministicKeyProvider.new(deterministic_key)
|
86
|
+
if @deterministic
|
87
|
+
DeterministicKeyProvider.new(ActiveRecord::Encryption.config.deterministic_key)
|
95
88
|
end
|
96
89
|
end
|
90
|
+
|
91
|
+
def default_key_provider
|
92
|
+
ActiveRecord::Encryption.key_provider
|
93
|
+
end
|
97
94
|
end
|
98
95
|
end
|
99
96
|
end
|