omg-activerecord 8.0.0.alpha1
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 +355 -0
- data/MIT-LICENSE +22 -0
- data/README.rdoc +219 -0
- data/examples/performance.rb +185 -0
- data/examples/simple.rb +15 -0
- data/lib/active_record/aggregations.rb +287 -0
- data/lib/active_record/association_relation.rb +50 -0
- data/lib/active_record/associations/alias_tracker.rb +90 -0
- data/lib/active_record/associations/association.rb +417 -0
- data/lib/active_record/associations/association_scope.rb +175 -0
- data/lib/active_record/associations/belongs_to_association.rb +163 -0
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +50 -0
- data/lib/active_record/associations/builder/association.rb +170 -0
- data/lib/active_record/associations/builder/belongs_to.rb +160 -0
- data/lib/active_record/associations/builder/collection_association.rb +80 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +107 -0
- data/lib/active_record/associations/builder/has_many.rb +23 -0
- data/lib/active_record/associations/builder/has_one.rb +61 -0
- data/lib/active_record/associations/builder/singular_association.rb +48 -0
- data/lib/active_record/associations/collection_association.rb +535 -0
- data/lib/active_record/associations/collection_proxy.rb +1163 -0
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/foreign_association.rb +40 -0
- data/lib/active_record/associations/has_many_association.rb +167 -0
- data/lib/active_record/associations/has_many_through_association.rb +232 -0
- data/lib/active_record/associations/has_one_association.rb +142 -0
- data/lib/active_record/associations/has_one_through_association.rb +45 -0
- data/lib/active_record/associations/join_dependency/join_association.rb +106 -0
- data/lib/active_record/associations/join_dependency/join_base.rb +23 -0
- data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
- data/lib/active_record/associations/join_dependency.rb +301 -0
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +316 -0
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +153 -0
- data/lib/active_record/associations/preloader/through_association.rb +150 -0
- data/lib/active_record/associations/preloader.rb +135 -0
- data/lib/active_record/associations/singular_association.rb +76 -0
- data/lib/active_record/associations/through_association.rb +132 -0
- data/lib/active_record/associations.rb +1897 -0
- data/lib/active_record/asynchronous_queries_tracker.rb +64 -0
- data/lib/active_record/attribute_assignment.rb +82 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +106 -0
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +262 -0
- data/lib/active_record/attribute_methods/primary_key.rb +158 -0
- data/lib/active_record/attribute_methods/query.rb +50 -0
- data/lib/active_record/attribute_methods/read.rb +46 -0
- data/lib/active_record/attribute_methods/serialization.rb +232 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +94 -0
- data/lib/active_record/attribute_methods/write.rb +49 -0
- data/lib/active_record/attribute_methods.rb +542 -0
- data/lib/active_record/attributes.rb +307 -0
- data/lib/active_record/autosave_association.rb +586 -0
- data/lib/active_record/base.rb +338 -0
- data/lib/active_record/callbacks.rb +452 -0
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +15 -0
- data/lib/active_record/coders/yaml_column.rb +95 -0
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +290 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +210 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +923 -0
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +31 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +747 -0
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +319 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +239 -0
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +24 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +190 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +961 -0
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +106 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1883 -0
- data/lib/active_record/connection_adapters/abstract/transaction.rb +676 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +1218 -0
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +1016 -0
- data/lib/active_record/connection_adapters/column.rb +122 -0
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +28 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +95 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +114 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +106 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +106 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +97 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +300 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +40 -0
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +96 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +196 -0
- data/lib/active_record/connection_adapters/pool_config.rb +83 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +57 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +82 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +231 -0
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +91 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +54 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +31 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +109 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +74 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +124 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +125 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +45 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +38 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +238 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +71 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +169 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +392 -0
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +127 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +1162 -0
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +79 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +1182 -0
- data/lib/active_record/connection_adapters/schema_cache.rb +478 -0
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +45 -0
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +145 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +116 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +37 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +39 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +47 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +221 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +843 -0
- data/lib/active_record/connection_adapters/statement_pool.rb +67 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +69 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +212 -0
- data/lib/active_record/connection_adapters.rb +176 -0
- data/lib/active_record/connection_handling.rb +413 -0
- data/lib/active_record/core.rb +836 -0
- data/lib/active_record/counter_cache.rb +230 -0
- data/lib/active_record/database_configurations/connection_url_resolver.rb +105 -0
- data/lib/active_record/database_configurations/database_config.rb +104 -0
- data/lib/active_record/database_configurations/hash_config.rb +172 -0
- data/lib/active_record/database_configurations/url_config.rb +78 -0
- data/lib/active_record/database_configurations.rb +309 -0
- data/lib/active_record/delegated_type.rb +289 -0
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +38 -0
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +121 -0
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +70 -0
- data/lib/active_record/encryption/configurable.rb +60 -0
- data/lib/active_record/encryption/context.rb +42 -0
- data/lib/active_record/encryption/contexts.rb +76 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +230 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +184 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +177 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +159 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +53 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +96 -0
- data/lib/active_record/encryption/null_encryptor.rb +25 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
- data/lib/active_record/encryption/scheme.rb +107 -0
- data/lib/active_record/encryption.rb +58 -0
- data/lib/active_record/enum.rb +424 -0
- data/lib/active_record/errors.rb +614 -0
- data/lib/active_record/explain.rb +63 -0
- data/lib/active_record/explain_registry.rb +37 -0
- data/lib/active_record/explain_subscriber.rb +34 -0
- data/lib/active_record/fixture_set/file.rb +89 -0
- data/lib/active_record/fixture_set/model_metadata.rb +42 -0
- data/lib/active_record/fixture_set/render_context.rb +19 -0
- data/lib/active_record/fixture_set/table_row.rb +208 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +850 -0
- data/lib/active_record/future_result.rb +182 -0
- data/lib/active_record/gem_version.rb +17 -0
- data/lib/active_record/inheritance.rb +366 -0
- data/lib/active_record/insert_all.rb +328 -0
- data/lib/active_record/integration.rb +209 -0
- data/lib/active_record/internal_metadata.rb +164 -0
- data/lib/active_record/legacy_yaml_adapter.rb +15 -0
- data/lib/active_record/locale/en.yml +48 -0
- data/lib/active_record/locking/optimistic.rb +228 -0
- data/lib/active_record/locking/pessimistic.rb +102 -0
- data/lib/active_record/log_subscriber.rb +149 -0
- 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/session.rb +48 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector.rb +87 -0
- data/lib/active_record/middleware/shard_selector.rb +62 -0
- data/lib/active_record/migration/command_recorder.rb +406 -0
- data/lib/active_record/migration/compatibility.rb +490 -0
- data/lib/active_record/migration/default_strategy.rb +22 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/join_table.rb +16 -0
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +1626 -0
- data/lib/active_record/model_schema.rb +635 -0
- data/lib/active_record/nested_attributes.rb +633 -0
- data/lib/active_record/no_touching.rb +65 -0
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +968 -0
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +56 -0
- data/lib/active_record/query_logs.rb +247 -0
- data/lib/active_record/query_logs_formatter.rb +30 -0
- data/lib/active_record/querying.rb +122 -0
- data/lib/active_record/railtie.rb +440 -0
- data/lib/active_record/railties/console_sandbox.rb +5 -0
- data/lib/active_record/railties/controller_runtime.rb +65 -0
- data/lib/active_record/railties/databases.rake +641 -0
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +66 -0
- data/lib/active_record/reflection.rb +1287 -0
- data/lib/active_record/relation/batches/batch_enumerator.rb +115 -0
- data/lib/active_record/relation/batches.rb +491 -0
- data/lib/active_record/relation/calculations.rb +679 -0
- data/lib/active_record/relation/delegation.rb +154 -0
- data/lib/active_record/relation/finder_methods.rb +661 -0
- data/lib/active_record/relation/from_clause.rb +30 -0
- data/lib/active_record/relation/merger.rb +192 -0
- data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +76 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +60 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +24 -0
- data/lib/active_record/relation/predicate_builder.rb +181 -0
- data/lib/active_record/relation/query_attribute.rb +68 -0
- data/lib/active_record/relation/query_methods.rb +2235 -0
- data/lib/active_record/relation/record_fetch_warning.rb +52 -0
- data/lib/active_record/relation/spawn_methods.rb +78 -0
- data/lib/active_record/relation/where_clause.rb +218 -0
- data/lib/active_record/relation.rb +1495 -0
- data/lib/active_record/result.rb +249 -0
- data/lib/active_record/runtime_registry.rb +82 -0
- data/lib/active_record/sanitization.rb +254 -0
- data/lib/active_record/schema.rb +77 -0
- data/lib/active_record/schema_dumper.rb +364 -0
- data/lib/active_record/schema_migration.rb +106 -0
- data/lib/active_record/scoping/default.rb +205 -0
- data/lib/active_record/scoping/named.rb +202 -0
- data/lib/active_record/scoping.rb +136 -0
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +66 -0
- data/lib/active_record/serialization.rb +29 -0
- data/lib/active_record/signed_id.rb +137 -0
- data/lib/active_record/statement_cache.rb +164 -0
- data/lib/active_record/store.rb +299 -0
- data/lib/active_record/suppressor.rb +59 -0
- data/lib/active_record/table_metadata.rb +85 -0
- data/lib/active_record/tasks/database_tasks.rb +681 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +120 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +147 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +89 -0
- data/lib/active_record/test_databases.rb +24 -0
- data/lib/active_record/test_fixtures.rb +321 -0
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +177 -0
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +70 -0
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +523 -0
- data/lib/active_record/translation.rb +22 -0
- data/lib/active_record/type/adapter_specific_registry.rb +144 -0
- data/lib/active_record/type/date.rb +9 -0
- data/lib/active_record/type/date_time.rb +9 -0
- data/lib/active_record/type/decimal_without_scale.rb +15 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +57 -0
- data/lib/active_record/type/internal/timezone.rb +22 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +76 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +35 -0
- data/lib/active_record/type/type_map.rb +58 -0
- data/lib/active_record/type/unsigned_integer.rb +16 -0
- data/lib/active_record/type.rb +83 -0
- data/lib/active_record/type_caster/connection.rb +33 -0
- data/lib/active_record/type_caster/map.rb +23 -0
- data/lib/active_record/type_caster.rb +9 -0
- data/lib/active_record/validations/absence.rb +25 -0
- data/lib/active_record/validations/associated.rb +65 -0
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/numericality.rb +36 -0
- data/lib/active_record/validations/presence.rb +45 -0
- data/lib/active_record/validations/uniqueness.rb +295 -0
- data/lib/active_record/validations.rb +101 -0
- data/lib/active_record/version.rb +10 -0
- data/lib/active_record.rb +616 -0
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +33 -0
- data/lib/arel/collectors/bind.rb +31 -0
- data/lib/arel/collectors/composite.rb +46 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +27 -0
- data/lib/arel/collectors/substitute_binds.rb +35 -0
- data/lib/arel/crud.rb +48 -0
- data/lib/arel/delete_manager.rb +32 -0
- data/lib/arel/errors.rb +19 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +53 -0
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +48 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +125 -0
- data/lib/arel/nodes/bind_param.rb +44 -0
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +62 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +44 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +15 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +45 -0
- data/lib/arel/nodes/grouping.rb +11 -0
- data/lib/arel/nodes/homogeneous_in.rb +68 -0
- data/lib/arel/nodes/in.rb +15 -0
- data/lib/arel/nodes/infix_operation.rb +92 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/nary.rb +39 -0
- data/lib/arel/nodes/node.rb +161 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +32 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +35 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +44 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +46 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/nodes.rb +75 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +260 -0
- data/lib/arel/select_manager.rb +276 -0
- data/lib/arel/table.rb +121 -0
- data/lib/arel/tree_manager.rb +65 -0
- data/lib/arel/update_manager.rb +49 -0
- data/lib/arel/visitors/dot.rb +299 -0
- data/lib/arel/visitors/mysql.rb +111 -0
- data/lib/arel/visitors/postgresql.rb +99 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +1033 -0
- data/lib/arel/visitors/visitor.rb +45 -0
- data/lib/arel/visitors.rb +13 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +73 -0
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +76 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +29 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +48 -0
- data/lib/rails/generators/active_record/migration.rb +54 -0
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +94 -0
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +7 -0
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- data/lib/rails/generators/active_record.rb +19 -0
- metadata +505 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module DynamicMatchers # :nodoc:
|
|
5
|
+
private
|
|
6
|
+
def respond_to_missing?(name, _)
|
|
7
|
+
if self == Base
|
|
8
|
+
super
|
|
9
|
+
else
|
|
10
|
+
match = Method.match(self, name)
|
|
11
|
+
match && match.valid? || super
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def method_missing(name, ...)
|
|
16
|
+
match = Method.match(self, name)
|
|
17
|
+
|
|
18
|
+
if match && match.valid?
|
|
19
|
+
match.define
|
|
20
|
+
send(name, ...)
|
|
21
|
+
else
|
|
22
|
+
super
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class Method
|
|
27
|
+
@matchers = []
|
|
28
|
+
|
|
29
|
+
class << self
|
|
30
|
+
attr_reader :matchers
|
|
31
|
+
|
|
32
|
+
def match(model, name)
|
|
33
|
+
klass = matchers.find { |k| k.pattern.match?(name) }
|
|
34
|
+
klass.new(model, name) if klass
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def pattern
|
|
38
|
+
@pattern ||= /\A#{prefix}_([_a-zA-Z]\w*)#{suffix}\Z/
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def prefix
|
|
42
|
+
raise NotImplementedError
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def suffix
|
|
46
|
+
""
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
attr_reader :model, :name, :attribute_names
|
|
51
|
+
|
|
52
|
+
def initialize(model, method_name)
|
|
53
|
+
@model = model
|
|
54
|
+
@name = method_name.to_s
|
|
55
|
+
@attribute_names = @name.match(self.class.pattern)[1].split("_and_")
|
|
56
|
+
@attribute_names.map! { |name| @model.attribute_aliases[name] || name }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def valid?
|
|
60
|
+
attribute_names.all? { |name| model.columns_hash[name] || model.reflect_on_aggregation(name.to_sym) }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def define
|
|
64
|
+
model.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
|
65
|
+
def self.#{name}(#{signature})
|
|
66
|
+
#{body}
|
|
67
|
+
end
|
|
68
|
+
CODE
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
def body
|
|
73
|
+
"#{finder}(#{attributes_hash})"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# The parameters in the signature may have reserved Ruby words, in order
|
|
77
|
+
# to prevent errors, we start each param name with `_`.
|
|
78
|
+
def signature
|
|
79
|
+
attribute_names.map { |name| "_#{name}" }.join(", ")
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Given that the parameters starts with `_`, the finder needs to use the
|
|
83
|
+
# same parameter name.
|
|
84
|
+
def attributes_hash
|
|
85
|
+
"{" + attribute_names.map { |name| ":#{name} => _#{name}" }.join(",") + "}"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def finder
|
|
89
|
+
raise NotImplementedError
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
class FindBy < Method
|
|
94
|
+
Method.matchers << self
|
|
95
|
+
|
|
96
|
+
def self.prefix
|
|
97
|
+
"find_by"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def finder
|
|
101
|
+
"find_by"
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
class FindByBang < Method
|
|
106
|
+
Method.matchers << self
|
|
107
|
+
|
|
108
|
+
def self.prefix
|
|
109
|
+
"find_by"
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def self.suffix
|
|
113
|
+
"!"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def finder
|
|
117
|
+
"find_by!"
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module Encryption
|
|
5
|
+
class AutoFilteredParameters
|
|
6
|
+
def initialize(app)
|
|
7
|
+
@app = app
|
|
8
|
+
@attributes_by_class = Concurrent::Map.new
|
|
9
|
+
@collecting = true
|
|
10
|
+
|
|
11
|
+
install_collecting_hook
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def enable
|
|
15
|
+
apply_collected_attributes
|
|
16
|
+
@collecting = false
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
attr_reader :app
|
|
21
|
+
|
|
22
|
+
def install_collecting_hook
|
|
23
|
+
ActiveRecord::Encryption.on_encrypted_attribute_declared do |klass, attribute|
|
|
24
|
+
attribute_was_declared(klass, attribute)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def attribute_was_declared(klass, attribute)
|
|
29
|
+
if collecting?
|
|
30
|
+
collect_for_later(klass, attribute)
|
|
31
|
+
else
|
|
32
|
+
apply_filter(klass, attribute)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def apply_collected_attributes
|
|
37
|
+
@attributes_by_class.each do |klass, attributes|
|
|
38
|
+
attributes.each do |attribute|
|
|
39
|
+
apply_filter(klass, attribute)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def collecting?
|
|
45
|
+
@collecting
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def collect_for_later(klass, attribute)
|
|
49
|
+
@attributes_by_class[klass] ||= Concurrent::Array.new
|
|
50
|
+
@attributes_by_class[klass] << attribute
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def apply_filter(klass, attribute)
|
|
54
|
+
filter = [("#{klass.model_name.element}" if klass.name), attribute.to_s].compact.join(".")
|
|
55
|
+
unless excluded_from_filter_parameters?(filter)
|
|
56
|
+
app.config.filter_parameters << filter unless app.config.filter_parameters.include?(filter)
|
|
57
|
+
klass.filter_attributes += [ attribute ]
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def excluded_from_filter_parameters?(filter_parameter)
|
|
62
|
+
ActiveRecord::Encryption.config.excluded_from_filter_parameters.find { |excluded_filter| excluded_filter.to_s == filter_parameter }
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "openssl"
|
|
4
|
+
|
|
5
|
+
module ActiveRecord
|
|
6
|
+
module Encryption
|
|
7
|
+
class Cipher
|
|
8
|
+
# A 256-GCM cipher.
|
|
9
|
+
#
|
|
10
|
+
# By default it will use random initialization vectors. For deterministic encryption, it will use a SHA-256 hash of
|
|
11
|
+
# the text to encrypt and the secret.
|
|
12
|
+
#
|
|
13
|
+
# See +Encryptor+
|
|
14
|
+
class Aes256Gcm
|
|
15
|
+
CIPHER_TYPE = "aes-256-gcm"
|
|
16
|
+
|
|
17
|
+
class << self
|
|
18
|
+
def key_length
|
|
19
|
+
OpenSSL::Cipher.new(CIPHER_TYPE).key_len
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def iv_length
|
|
23
|
+
OpenSSL::Cipher.new(CIPHER_TYPE).iv_len
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# When iv not provided, it will generate a random iv on each encryption operation (default and
|
|
28
|
+
# recommended operation)
|
|
29
|
+
def initialize(secret, deterministic: false)
|
|
30
|
+
@secret = secret
|
|
31
|
+
@deterministic = deterministic
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def encrypt(clear_text)
|
|
35
|
+
# This code is extracted from +ActiveSupport::MessageEncryptor+. Not using it directly because we want to control
|
|
36
|
+
# the message format and only serialize things once at the +ActiveRecord::Encryption::Message+ level. Also, this
|
|
37
|
+
# cipher is prepared to deal with deterministic/non deterministic encryption modes.
|
|
38
|
+
|
|
39
|
+
cipher = OpenSSL::Cipher.new(CIPHER_TYPE)
|
|
40
|
+
cipher.encrypt
|
|
41
|
+
cipher.key = @secret
|
|
42
|
+
|
|
43
|
+
iv = generate_iv(cipher, clear_text)
|
|
44
|
+
cipher.iv = iv
|
|
45
|
+
|
|
46
|
+
encrypted_data = clear_text.empty? ? clear_text.dup : cipher.update(clear_text)
|
|
47
|
+
encrypted_data << cipher.final
|
|
48
|
+
|
|
49
|
+
ActiveRecord::Encryption::Message.new(payload: encrypted_data).tap do |message|
|
|
50
|
+
message.headers.iv = iv
|
|
51
|
+
message.headers.auth_tag = cipher.auth_tag
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def decrypt(encrypted_message)
|
|
56
|
+
encrypted_data = encrypted_message.payload
|
|
57
|
+
iv = encrypted_message.headers.iv
|
|
58
|
+
auth_tag = encrypted_message.headers.auth_tag
|
|
59
|
+
|
|
60
|
+
# Currently the OpenSSL bindings do not raise an error if auth_tag is
|
|
61
|
+
# truncated, which would allow an attacker to easily forge it. See
|
|
62
|
+
# https://github.com/ruby/openssl/issues/63
|
|
63
|
+
raise ActiveRecord::Encryption::Errors::EncryptedContentIntegrity if auth_tag.nil? || auth_tag.bytes.length != 16
|
|
64
|
+
|
|
65
|
+
cipher = OpenSSL::Cipher.new(CIPHER_TYPE)
|
|
66
|
+
|
|
67
|
+
cipher.decrypt
|
|
68
|
+
cipher.key = @secret
|
|
69
|
+
cipher.iv = iv
|
|
70
|
+
|
|
71
|
+
cipher.auth_tag = auth_tag
|
|
72
|
+
cipher.auth_data = ""
|
|
73
|
+
|
|
74
|
+
decrypted_data = encrypted_data.empty? ? encrypted_data : cipher.update(encrypted_data)
|
|
75
|
+
decrypted_data << cipher.final
|
|
76
|
+
|
|
77
|
+
decrypted_data
|
|
78
|
+
rescue OpenSSL::Cipher::CipherError, TypeError, ArgumentError
|
|
79
|
+
raise ActiveRecord::Encryption::Errors::Decryption
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def inspect # :nodoc:
|
|
83
|
+
"#<#{self.class.name}:#{'%#016x' % (object_id << 1)}>"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
private
|
|
87
|
+
def generate_iv(cipher, clear_text)
|
|
88
|
+
if @deterministic
|
|
89
|
+
generate_deterministic_iv(clear_text)
|
|
90
|
+
else
|
|
91
|
+
cipher.random_iv
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def generate_deterministic_iv(clear_text)
|
|
96
|
+
OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, @secret, clear_text)[0, ActiveRecord::Encryption.cipher.iv_length]
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module Encryption
|
|
5
|
+
# The algorithm used for encrypting and decrypting +Message+ objects.
|
|
6
|
+
#
|
|
7
|
+
# It uses AES-256-GCM. It will generate a random IV for non deterministic encryption (default)
|
|
8
|
+
# or derive an initialization vector from the encrypted content for deterministic encryption.
|
|
9
|
+
#
|
|
10
|
+
# See +Cipher::Aes256Gcm+.
|
|
11
|
+
class Cipher
|
|
12
|
+
DEFAULT_ENCODING = Encoding::UTF_8
|
|
13
|
+
|
|
14
|
+
# Encrypts the provided text and return an encrypted +Message+.
|
|
15
|
+
def encrypt(clean_text, key:, deterministic: false)
|
|
16
|
+
cipher_for(key, deterministic: deterministic).encrypt(clean_text).tap do |message|
|
|
17
|
+
message.headers.encoding = clean_text.encoding.name unless clean_text.encoding == DEFAULT_ENCODING
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Decrypt the provided +Message+.
|
|
22
|
+
#
|
|
23
|
+
# When +key+ is an Array, it will try all the keys raising a
|
|
24
|
+
# +ActiveRecord::Encryption::Errors::Decryption+ if none works.
|
|
25
|
+
def decrypt(encrypted_message, key:)
|
|
26
|
+
try_to_decrypt_with_each(encrypted_message, keys: Array(key)).tap do |decrypted_text|
|
|
27
|
+
decrypted_text.force_encoding(encrypted_message.headers.encoding || DEFAULT_ENCODING)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def key_length
|
|
32
|
+
Aes256Gcm.key_length
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def iv_length
|
|
36
|
+
Aes256Gcm.iv_length
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
def try_to_decrypt_with_each(encrypted_text, keys:)
|
|
41
|
+
keys.each.with_index do |key, index|
|
|
42
|
+
return cipher_for(key).decrypt(encrypted_text)
|
|
43
|
+
rescue ActiveRecord::Encryption::Errors::Decryption
|
|
44
|
+
raise if index == keys.length - 1
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def cipher_for(secret, deterministic: false)
|
|
49
|
+
Aes256Gcm.new(secret, deterministic: deterministic)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "openssl"
|
|
4
|
+
|
|
5
|
+
module ActiveRecord
|
|
6
|
+
module Encryption
|
|
7
|
+
# Container of configuration options
|
|
8
|
+
class Config
|
|
9
|
+
attr_accessor :primary_key, :deterministic_key, :store_key_references, :key_derivation_salt, :hash_digest_class,
|
|
10
|
+
:support_unencrypted_data, :encrypt_fixtures, :validate_column_size, :add_to_filter_parameters,
|
|
11
|
+
:excluded_from_filter_parameters, :extend_queries, :previous_schemes, :forced_encoding_for_deterministic_encryption,
|
|
12
|
+
:compressor
|
|
13
|
+
|
|
14
|
+
def initialize
|
|
15
|
+
set_defaults
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Configure previous encryption schemes.
|
|
19
|
+
#
|
|
20
|
+
# config.active_record.encryption.previous = [ { key_provider: MyOldKeyProvider.new } ]
|
|
21
|
+
def previous=(previous_schemes_properties)
|
|
22
|
+
previous_schemes_properties.each do |properties|
|
|
23
|
+
add_previous_scheme(**properties)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def support_sha1_for_non_deterministic_encryption=(value)
|
|
28
|
+
if value && has_primary_key?
|
|
29
|
+
sha1_key_generator = ActiveRecord::Encryption::KeyGenerator.new(hash_digest_class: OpenSSL::Digest::SHA1)
|
|
30
|
+
sha1_key_provider = ActiveRecord::Encryption::DerivedSecretKeyProvider.new(primary_key, key_generator: sha1_key_generator)
|
|
31
|
+
add_previous_scheme key_provider: sha1_key_provider
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
%w(key_derivation_salt primary_key deterministic_key).each do |key|
|
|
36
|
+
silence_redefinition_of_method "has_#{key}?"
|
|
37
|
+
define_method("has_#{key}?") do
|
|
38
|
+
instance_variable_get(:"@#{key}").presence
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
silence_redefinition_of_method key
|
|
42
|
+
define_method(key) do
|
|
43
|
+
public_send("has_#{key}?") or
|
|
44
|
+
raise Errors::Configuration, "Missing Active Record encryption credential: active_record_encryption.#{key}"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
def set_defaults
|
|
50
|
+
self.store_key_references = false
|
|
51
|
+
self.support_unencrypted_data = false
|
|
52
|
+
self.encrypt_fixtures = false
|
|
53
|
+
self.validate_column_size = true
|
|
54
|
+
self.add_to_filter_parameters = true
|
|
55
|
+
self.excluded_from_filter_parameters = []
|
|
56
|
+
self.previous_schemes = []
|
|
57
|
+
self.forced_encoding_for_deterministic_encryption = Encoding::UTF_8
|
|
58
|
+
self.hash_digest_class = OpenSSL::Digest::SHA1
|
|
59
|
+
self.compressor = Zlib
|
|
60
|
+
|
|
61
|
+
# TODO: Setting to false for now as the implementation is a bit experimental
|
|
62
|
+
self.extend_queries = false
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def add_previous_scheme(**properties)
|
|
66
|
+
previous_schemes << ActiveRecord::Encryption::Scheme.new(**properties)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module Encryption
|
|
5
|
+
# Configuration API for ActiveRecord::Encryption
|
|
6
|
+
module Configurable
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
included do
|
|
10
|
+
mattr_reader :config, default: Config.new
|
|
11
|
+
mattr_accessor :encrypted_attribute_declaration_listeners
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class_methods do
|
|
15
|
+
# Expose getters for context properties
|
|
16
|
+
Context::PROPERTIES.each do |name|
|
|
17
|
+
delegate name, to: :context
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def configure(primary_key: nil, deterministic_key: nil, key_derivation_salt: nil, **properties) # :nodoc:
|
|
21
|
+
config.primary_key = primary_key
|
|
22
|
+
config.deterministic_key = deterministic_key
|
|
23
|
+
config.key_derivation_salt = key_derivation_salt
|
|
24
|
+
|
|
25
|
+
# Set the default for this property here instead of in +Config#set_defaults+ as this needs
|
|
26
|
+
# to happen *after* the keys have been set.
|
|
27
|
+
properties[:support_sha1_for_non_deterministic_encryption] = true if properties[:support_sha1_for_non_deterministic_encryption].nil?
|
|
28
|
+
|
|
29
|
+
properties.each do |name, value|
|
|
30
|
+
ActiveRecord::Encryption.config.send "#{name}=", value if ActiveRecord::Encryption.config.respond_to?("#{name}=")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
ActiveRecord::Encryption.reset_default_context
|
|
34
|
+
|
|
35
|
+
properties.each do |name, value|
|
|
36
|
+
ActiveRecord::Encryption.context.send "#{name}=", value if ActiveRecord::Encryption.context.respond_to?("#{name}=")
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Register callback to be invoked when an encrypted attribute is declared.
|
|
41
|
+
#
|
|
42
|
+
# === Example
|
|
43
|
+
#
|
|
44
|
+
# ActiveRecord::Encryption.on_encrypted_attribute_declared do |klass, attribute_name|
|
|
45
|
+
# ...
|
|
46
|
+
# end
|
|
47
|
+
def on_encrypted_attribute_declared(&block)
|
|
48
|
+
self.encrypted_attribute_declaration_listeners ||= Concurrent::Array.new
|
|
49
|
+
self.encrypted_attribute_declaration_listeners << block
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def encrypted_attribute_was_declared(klass, name) # :nodoc:
|
|
53
|
+
self.encrypted_attribute_declaration_listeners&.each do |block|
|
|
54
|
+
block.call(klass, name)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module Encryption
|
|
5
|
+
# An encryption context configures the different entities used to perform encryption:
|
|
6
|
+
#
|
|
7
|
+
# * A key provider
|
|
8
|
+
# * A key generator
|
|
9
|
+
# * An encryptor, the facade to encrypt data
|
|
10
|
+
# * A cipher, the encryption algorithm
|
|
11
|
+
# * A message serializer
|
|
12
|
+
class Context
|
|
13
|
+
PROPERTIES = %i[ key_provider key_generator cipher message_serializer encryptor frozen_encryption ]
|
|
14
|
+
|
|
15
|
+
attr_accessor(*PROPERTIES)
|
|
16
|
+
|
|
17
|
+
def initialize
|
|
18
|
+
set_defaults
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
alias frozen_encryption? frozen_encryption
|
|
22
|
+
|
|
23
|
+
silence_redefinition_of_method :key_provider
|
|
24
|
+
def key_provider
|
|
25
|
+
@key_provider ||= build_default_key_provider
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
def set_defaults
|
|
30
|
+
self.frozen_encryption = false
|
|
31
|
+
self.key_generator = ActiveRecord::Encryption::KeyGenerator.new
|
|
32
|
+
self.cipher = ActiveRecord::Encryption::Cipher.new
|
|
33
|
+
self.encryptor = ActiveRecord::Encryption::Encryptor.new
|
|
34
|
+
self.message_serializer = ActiveRecord::Encryption::MessageSerializer.new
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def build_default_key_provider
|
|
38
|
+
ActiveRecord::Encryption::DerivedSecretKeyProvider.new(ActiveRecord::Encryption.config.primary_key)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module Encryption
|
|
5
|
+
# ActiveRecord::Encryption uses encryption contexts to configure the different entities used to
|
|
6
|
+
# encrypt/decrypt at a given moment in time.
|
|
7
|
+
#
|
|
8
|
+
# By default, the library uses a default encryption context. This is the Context that gets configured
|
|
9
|
+
# initially via +config.active_record.encryption+ options. Library users can define nested encryption contexts
|
|
10
|
+
# when running blocks of code.
|
|
11
|
+
#
|
|
12
|
+
# See Context.
|
|
13
|
+
module Contexts
|
|
14
|
+
extend ActiveSupport::Concern
|
|
15
|
+
|
|
16
|
+
included do
|
|
17
|
+
mattr_accessor :default_context, default: Context.new
|
|
18
|
+
thread_mattr_accessor :custom_contexts
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class_methods do
|
|
22
|
+
# Configures a custom encryption context to use when running the provided block of code.
|
|
23
|
+
#
|
|
24
|
+
# It supports overriding all the properties defined in +Context+.
|
|
25
|
+
#
|
|
26
|
+
# Example:
|
|
27
|
+
#
|
|
28
|
+
# ActiveRecord::Encryption.with_encryption_context(encryptor: ActiveRecord::Encryption::NullEncryptor.new) do
|
|
29
|
+
# ...
|
|
30
|
+
# end
|
|
31
|
+
#
|
|
32
|
+
# Encryption contexts can be nested.
|
|
33
|
+
def with_encryption_context(properties)
|
|
34
|
+
self.custom_contexts ||= []
|
|
35
|
+
self.custom_contexts << default_context.dup
|
|
36
|
+
properties.each do |key, value|
|
|
37
|
+
self.current_custom_context.send("#{key}=", value)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
yield
|
|
41
|
+
ensure
|
|
42
|
+
self.custom_contexts.pop
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Runs the provided block in an encryption context where encryption is disabled:
|
|
46
|
+
#
|
|
47
|
+
# * Reading encrypted content will return its ciphertexts.
|
|
48
|
+
# * Writing encrypted content will write its clear text.
|
|
49
|
+
def without_encryption(&block)
|
|
50
|
+
with_encryption_context encryptor: ActiveRecord::Encryption::NullEncryptor.new, &block
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Runs the provided block in an encryption context where:
|
|
54
|
+
#
|
|
55
|
+
# * Reading encrypted content will return its ciphertext.
|
|
56
|
+
# * Writing encrypted content will fail.
|
|
57
|
+
def protecting_encrypted_data(&block)
|
|
58
|
+
with_encryption_context encryptor: ActiveRecord::Encryption::EncryptingOnlyEncryptor.new, frozen_encryption: true, &block
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Returns the current context. By default it will return the current context.
|
|
62
|
+
def context
|
|
63
|
+
self.current_custom_context || self.default_context
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def current_custom_context
|
|
67
|
+
self.custom_contexts&.last
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def reset_default_context
|
|
71
|
+
self.default_context = Context.new
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module Encryption
|
|
5
|
+
# A KeyProvider that derives keys from passwords.
|
|
6
|
+
class DerivedSecretKeyProvider < KeyProvider
|
|
7
|
+
def initialize(passwords, key_generator: ActiveRecord::Encryption.key_generator)
|
|
8
|
+
super(Array(passwords).collect { |password| derive_key_from(password, using: key_generator) })
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
def derive_key_from(password, using: key_generator)
|
|
13
|
+
secret = using.derive_key_from(password)
|
|
14
|
+
ActiveRecord::Encryption::Key.new(secret)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module Encryption
|
|
5
|
+
# A KeyProvider that derives keys from passwords.
|
|
6
|
+
class DeterministicKeyProvider < DerivedSecretKeyProvider
|
|
7
|
+
def initialize(password)
|
|
8
|
+
passwords = Array(password)
|
|
9
|
+
raise ActiveRecord::Encryption::Errors::Configuration, "Deterministic encryption keys can't be rotated" if passwords.length > 1
|
|
10
|
+
super(passwords)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|