familia 2.0.0.pre14 → 2.0.0.pre16
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/.github/workflows/code-quality.yml +138 -0
- data/.github/workflows/code-smellage.yml +145 -0
- data/.github/workflows/docs.yml +31 -8
- data/.gitignore +1 -1
- data/.pre-commit-config.yaml +7 -1
- data/.reek.yml +98 -0
- data/.rubocop.yml +48 -10
- data/.talismanrc +9 -0
- data/.yardopts +18 -13
- data/CHANGELOG.rst +66 -6
- data/CLAUDE.md +1 -1
- data/Gemfile +6 -5
- data/Gemfile.lock +99 -23
- data/LICENSE.txt +1 -1
- data/README.md +285 -85
- data/changelog.d/README.md +2 -2
- data/docs/archive/FAMILIA_RELATIONSHIPS.md +22 -22
- data/docs/archive/FAMILIA_TECHNICAL.md +41 -41
- data/docs/archive/FAMILIA_UPDATE.md +3 -3
- data/docs/archive/README.md +3 -2
- data/docs/{guides/API-Reference.md → archive/api-reference.md} +87 -101
- data/docs/conf.py +29 -0
- data/docs/guides/{Field-System-Guide.md → core-field-system.md} +9 -9
- data/docs/guides/feature-encrypted-fields.md +785 -0
- data/docs/guides/{Expiration-Feature-Guide.md → feature-expiration.md} +11 -2
- data/docs/guides/feature-external-identifiers.md +637 -0
- data/docs/guides/feature-object-identifiers.md +435 -0
- data/docs/guides/{Quantization-Feature-Guide.md → feature-quantization.md} +94 -29
- data/docs/guides/feature-relationships-methods.md +684 -0
- data/docs/guides/feature-relationships.md +200 -0
- data/docs/guides/{Features-System-Developer-Guide.md → feature-system-devs.md} +4 -4
- data/docs/guides/{Feature-System-Guide.md → feature-system.md} +5 -5
- data/docs/guides/{Transient-Fields-Guide.md → feature-transient-fields.md} +2 -2
- data/docs/guides/{Implementation-Guide.md → implementation.md} +3 -3
- data/docs/guides/index.md +176 -0
- data/docs/guides/{Security-Model.md → security-model.md} +1 -1
- data/docs/migrating/v2.0.0-pre.md +1 -1
- data/docs/migrating/v2.0.0-pre11.md +4 -4
- data/docs/migrating/v2.0.0-pre12.md +2 -2
- data/docs/migrating/v2.0.0-pre13.md +1 -1
- data/docs/migrating/v2.0.0-pre5.md +33 -12
- data/docs/migrating/v2.0.0-pre6.md +2 -2
- data/docs/migrating/v2.0.0-pre7.md +8 -8
- data/docs/overview.md +623 -19
- data/docs/reference/api-technical.md +1365 -0
- data/examples/autoloader/mega_customer/features/deprecated_fields.rb +7 -0
- data/examples/autoloader/mega_customer/safe_dump_fields.rb +1 -1
- data/examples/autoloader/mega_customer.rb +3 -1
- data/examples/encrypted_fields.rb +378 -0
- data/examples/json_usage_patterns.rb +144 -0
- data/examples/relationships.rb +13 -13
- data/examples/safe_dump.rb +6 -6
- data/examples/single_connection_transaction_confusions.rb +379 -0
- data/lib/familia/base.rb +49 -10
- data/lib/familia/connection/handlers.rb +223 -0
- data/lib/familia/connection/individual_command_proxy.rb +64 -0
- data/lib/familia/connection/middleware.rb +75 -0
- data/lib/familia/connection/operation_core.rb +93 -0
- data/lib/familia/connection/operations.rb +277 -0
- data/lib/familia/connection/pipeline_core.rb +87 -0
- data/lib/familia/connection/transaction_core.rb +100 -0
- data/lib/familia/connection.rb +60 -186
- data/lib/familia/data_type/commands.rb +53 -51
- data/lib/familia/data_type/serialization.rb +108 -107
- data/lib/familia/data_type/types/counter.rb +1 -1
- data/lib/familia/data_type/types/hashkey.rb +13 -10
- data/lib/familia/data_type/types/{list.rb → listkey.rb} +13 -5
- data/lib/familia/data_type/types/lock.rb +3 -2
- data/lib/familia/data_type/types/sorted_set.rb +26 -15
- data/lib/familia/data_type/types/{string.rb → stringkey.rb} +7 -5
- data/lib/familia/data_type/types/unsorted_set.rb +20 -27
- data/lib/familia/data_type.rb +75 -47
- data/lib/familia/distinguisher.rb +85 -0
- data/lib/familia/encryption/encrypted_data.rb +15 -24
- data/lib/familia/encryption/manager.rb +6 -4
- data/lib/familia/encryption/providers/aes_gcm_provider.rb +1 -1
- data/lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb +7 -9
- data/lib/familia/encryption/providers/xchacha20_poly1305_provider.rb +4 -5
- data/lib/familia/encryption/request_cache.rb +7 -7
- data/lib/familia/encryption.rb +2 -3
- data/lib/familia/errors.rb +9 -3
- data/lib/familia/{autoloader.rb → features/autoloader.rb} +49 -23
- data/lib/familia/features/encrypted_fields/concealed_string.rb +3 -4
- data/lib/familia/features/encrypted_fields/encrypted_field_type.rb +13 -14
- data/lib/familia/features/encrypted_fields.rb +68 -66
- data/lib/familia/features/expiration/extensions.rb +61 -0
- data/lib/familia/features/expiration.rb +35 -87
- data/lib/familia/features/external_identifier.rb +11 -12
- data/lib/familia/features/object_identifier.rb +58 -20
- data/lib/familia/features/quantization.rb +17 -22
- data/lib/familia/features/relationships/README.md +97 -0
- data/lib/familia/features/relationships/collection_operations.rb +104 -0
- data/lib/familia/features/relationships/indexing/multi_index_generators.rb +202 -0
- data/lib/familia/features/relationships/indexing/unique_index_generators.rb +301 -0
- data/lib/familia/features/relationships/indexing.rb +176 -256
- data/lib/familia/features/relationships/indexing_relationship.rb +35 -0
- data/lib/familia/features/relationships/participation/participant_methods.rb +160 -0
- data/lib/familia/features/relationships/participation/target_methods.rb +225 -0
- data/lib/familia/features/relationships/participation.rb +656 -0
- data/lib/familia/features/relationships/participation_relationship.rb +31 -0
- data/lib/familia/features/relationships/score_encoding.rb +20 -20
- data/lib/familia/features/relationships.rb +69 -271
- data/lib/familia/features/safe_dump.rb +127 -132
- data/lib/familia/features/transient_fields/redacted_string.rb +6 -6
- data/lib/familia/features/transient_fields/transient_field_type.rb +5 -5
- data/lib/familia/features/transient_fields.rb +5 -5
- data/lib/familia/features.rb +21 -21
- data/lib/familia/field_type.rb +24 -4
- data/lib/familia/horreum/core/connection.rb +229 -26
- data/lib/familia/horreum/core/database_commands.rb +27 -17
- data/lib/familia/horreum/core/serialization.rb +40 -20
- data/lib/familia/horreum/core/utils.rb +2 -1
- data/lib/familia/horreum/shared/settings.rb +2 -1
- data/lib/familia/horreum/subclass/definition.rb +33 -45
- data/lib/familia/horreum/subclass/management.rb +72 -24
- data/lib/familia/horreum/subclass/related_fields_management.rb +82 -21
- data/lib/familia/horreum.rb +196 -114
- data/lib/familia/json_serializer.rb +0 -1
- data/lib/familia/logging.rb +11 -114
- data/lib/familia/refinements/dear_json.rb +122 -0
- data/lib/familia/refinements/logger_trace.rb +20 -17
- data/lib/familia/refinements/stylize_words.rb +65 -0
- data/lib/familia/refinements/time_literals.rb +60 -52
- data/lib/familia/refinements.rb +2 -1
- data/lib/familia/secure_identifier.rb +60 -28
- data/lib/familia/settings.rb +83 -7
- data/lib/familia/utils.rb +5 -87
- data/lib/familia/verifiable_identifier.rb +4 -4
- data/lib/familia/version.rb +1 -1
- data/lib/familia.rb +72 -15
- data/lib/middleware/database_middleware.rb +56 -14
- data/lib/{familia/multi_result.rb → multi_result.rb} +23 -16
- data/try/configuration/scenarios_try.rb +1 -1
- data/try/connection/fiber_context_preservation_try.rb +250 -0
- data/try/connection/handler_constraints_try.rb +59 -0
- data/try/connection/operation_mode_guards_try.rb +208 -0
- data/try/connection/pipeline_fallback_integration_try.rb +128 -0
- data/try/connection/responsibility_chain_tracking_try.rb +72 -0
- data/try/connection/transaction_fallback_integration_try.rb +288 -0
- data/try/connection/transaction_mode_permissive_try.rb +153 -0
- data/try/connection/transaction_mode_strict_try.rb +98 -0
- data/try/connection/transaction_mode_warn_try.rb +131 -0
- data/try/connection/transaction_modes_try.rb +249 -0
- data/try/core/autoloader_try.rb +129 -11
- data/try/core/connection_try.rb +7 -7
- data/try/core/conventional_inheritance_try.rb +130 -0
- data/try/core/create_method_try.rb +15 -23
- data/try/core/database_consistency_try.rb +10 -10
- data/try/core/errors_try.rb +8 -11
- data/try/core/familia_extended_try.rb +2 -2
- data/try/core/familia_members_methods_try.rb +76 -0
- data/try/core/isolated_dbclient_try.rb +165 -0
- data/try/core/middleware_try.rb +16 -16
- data/try/core/persistence_operations_try.rb +4 -4
- data/try/core/pools_try.rb +42 -26
- data/try/core/secure_identifier_try.rb +28 -24
- data/try/core/time_utils_try.rb +10 -10
- data/try/core/tools_try.rb +1 -1
- data/try/core/utils_try.rb +2 -2
- data/try/data_types/boolean_try.rb +4 -4
- data/try/data_types/datatype_base_try.rb +0 -2
- data/try/data_types/list_try.rb +10 -10
- data/try/data_types/sorted_set_try.rb +5 -5
- data/try/data_types/string_try.rb +12 -12
- data/try/data_types/unsortedset_try.rb +33 -0
- data/try/debugging/cache_behavior_tracer.rb +7 -7
- data/try/debugging/debug_aad_process.rb +1 -1
- data/try/debugging/debug_concealed_internal.rb +1 -1
- data/try/debugging/debug_cross_context.rb +1 -1
- data/try/debugging/debug_fresh_cross_context.rb +1 -1
- data/try/debugging/encryption_method_tracer.rb +10 -10
- data/try/edge_cases/hash_symbolization_try.rb +1 -1
- data/try/edge_cases/ttl_side_effects_try.rb +1 -1
- data/try/encryption/config_persistence_try.rb +2 -2
- data/try/encryption/encryption_core_try.rb +19 -19
- data/try/encryption/instance_variable_scope_try.rb +1 -1
- data/try/encryption/module_loading_try.rb +2 -2
- data/try/encryption/providers/aes_gcm_provider_try.rb +1 -1
- data/try/encryption/providers/xchacha20_poly1305_provider_try.rb +1 -1
- data/try/encryption/secure_memory_handling_try.rb +1 -1
- data/try/features/encrypted_fields/concealed_string_core_try.rb +11 -7
- data/try/features/encrypted_fields/encrypted_fields_core_try.rb +1 -1
- data/try/features/encrypted_fields/encrypted_fields_integration_try.rb +3 -3
- data/try/features/encrypted_fields/encrypted_fields_no_cache_security_try.rb +10 -10
- data/try/features/encrypted_fields/encrypted_fields_security_try.rb +14 -14
- data/try/features/encrypted_fields/error_conditions_try.rb +7 -7
- data/try/features/encrypted_fields/fresh_key_try.rb +1 -1
- data/try/features/encrypted_fields/nonce_uniqueness_try.rb +1 -1
- data/try/features/encrypted_fields/secure_by_default_behavior_try.rb +7 -7
- data/try/features/encrypted_fields/universal_serialization_safety_try.rb +13 -20
- data/try/features/external_identifier/external_identifier_try.rb +1 -1
- data/try/features/feature_dependencies_try.rb +3 -3
- data/try/features/object_identifier/object_identifier_integration_try.rb +28 -34
- data/try/features/object_identifier/object_identifier_try.rb +10 -0
- data/try/features/quantization/quantization_try.rb +1 -1
- data/try/features/relationships/indexing_commands_verification_try.rb +136 -0
- data/try/features/relationships/indexing_try.rb +433 -0
- data/try/features/relationships/participation_commands_verification_spec.rb +102 -0
- data/try/features/relationships/participation_commands_verification_try.rb +105 -0
- data/try/features/relationships/participation_performance_improvements_try.rb +124 -0
- data/try/features/relationships/participation_reverse_index_try.rb +196 -0
- data/try/features/relationships/relationships_api_changes_try.rb +72 -71
- data/try/features/relationships/relationships_edge_cases_try.rb +15 -18
- data/try/features/relationships/relationships_performance_minimal_try.rb +2 -2
- data/try/features/relationships/relationships_performance_simple_try.rb +8 -8
- data/try/features/relationships/relationships_performance_try.rb +20 -20
- data/try/features/relationships/relationships_try.rb +27 -38
- data/try/features/safe_dump/safe_dump_advanced_try.rb +2 -2
- data/try/features/transient_fields/refresh_reset_try.rb +1 -1
- data/try/features/transient_fields/simple_refresh_test.rb +1 -1
- data/try/helpers/test_cleanup.rb +86 -0
- data/try/helpers/test_helpers.rb +3 -3
- data/try/horreum/base_try.rb +3 -2
- data/try/horreum/commands_try.rb +1 -1
- data/try/horreum/destroy_related_fields_cleanup_try.rb +330 -0
- data/try/horreum/initialization_try.rb +11 -7
- data/try/horreum/relations_try.rb +21 -13
- data/try/horreum/serialization_try.rb +12 -11
- data/try/integration/cross_component_try.rb +3 -3
- data/try/memory/memory_basic_test.rb +1 -1
- data/try/memory/memory_docker_ruby_dump.sh +1 -1
- data/try/models/customer_safe_dump_try.rb +1 -1
- data/try/models/customer_try.rb +8 -10
- data/try/models/datatype_base_try.rb +3 -3
- data/try/models/familia_object_try.rb +9 -8
- data/try/performance/benchmarks_try.rb +2 -2
- data/try/prototypes/atomic_saves_v1_context_proxy.rb +2 -2
- data/try/prototypes/atomic_saves_v3_connection_pool.rb +3 -3
- data/try/prototypes/atomic_saves_v4.rb +1 -1
- data/try/prototypes/lib/atomic_saves_v2_connection_switching_helpers.rb +4 -4
- data/try/prototypes/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -4
- data/try/prototypes/pooling/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -4
- data/try/prototypes/pooling/lib/connection_pool_metrics.rb +5 -5
- data/try/prototypes/pooling/lib/connection_pool_stress_test.rb +26 -26
- data/try/prototypes/pooling/lib/connection_pool_threading_models.rb +7 -7
- data/try/prototypes/pooling/lib/visualize_stress_results.rb +1 -1
- data/try/prototypes/pooling/pool_siege.rb +11 -11
- data/try/prototypes/pooling/run_stress_tests.rb +7 -7
- data/try/refinements/dear_json_array_methods_try.rb +53 -0
- data/try/refinements/dear_json_hash_methods_try.rb +54 -0
- data/try/refinements/logger_trace_methods_try.rb +44 -0
- data/try/refinements/time_literals_numeric_methods_try.rb +141 -0
- data/try/refinements/time_literals_string_methods_try.rb +80 -0
- metadata +77 -45
- data/.rubocop_todo.yml +0 -208
- data/docs/connection_pooling.md +0 -192
- data/docs/guides/Connection-Pooling-Guide.md +0 -437
- data/docs/guides/Encrypted-Fields-Overview.md +0 -101
- data/docs/guides/Feature-System-Autoloading.md +0 -228
- data/docs/guides/Home.md +0 -116
- data/docs/guides/Relationships-Guide.md +0 -737
- data/docs/guides/relationships-methods.md +0 -266
- data/docs/reference/auditing_database_commands.rb +0 -228
- data/examples/permissions.rb +0 -240
- data/lib/familia/features/autoloadable.rb +0 -113
- data/lib/familia/features/relationships/cascading.rb +0 -437
- data/lib/familia/features/relationships/membership.rb +0 -497
- data/lib/familia/features/relationships/permission_management.rb +0 -264
- data/lib/familia/features/relationships/querying.rb +0 -615
- data/lib/familia/features/relationships/redis_operations.rb +0 -274
- data/lib/familia/features/relationships/tracking.rb +0 -418
- data/lib/familia/refinements/snake_case.rb +0 -40
- data/lib/familia/validation/command_recorder.rb +0 -336
- data/lib/familia/validation/expectations.rb +0 -519
- data/lib/familia/validation/validation_helpers.rb +0 -443
- data/lib/familia/validation/validator.rb +0 -412
- data/lib/familia/validation.rb +0 -140
- data/try/data_types/set_try.rb +0 -33
- data/try/features/autoloadable/autoloadable_try.rb +0 -61
- data/try/features/relationships/categorical_permissions_try.rb +0 -515
- data/try/features/safe_dump/safe_dump_autoloading_try.rb +0 -111
- data/try/validation/atomic_operations_try.rb.disabled +0 -320
- data/try/validation/command_validation_try.rb.disabled +0 -207
- data/try/validation/performance_validation_try.rb.disabled +0 -324
- data/try/validation/real_world_scenarios_try.rb.disabled +0 -390
@@ -22,9 +22,9 @@ module Familia
|
|
22
22
|
puts " Plaintext length: #{plaintext&.length} chars"
|
23
23
|
puts " AAD: #{additional_data ? 'present' : 'none'}"
|
24
24
|
|
25
|
-
start_time =
|
25
|
+
start_time = Familia.now
|
26
26
|
result = orig_encrypt(plaintext, context: context, additional_data: additional_data)
|
27
|
-
elapsed = ((
|
27
|
+
elapsed = ((Familia.now - start_time) * 1000).round(2)
|
28
28
|
|
29
29
|
puts " Result length: #{result ? result.length : 'nil'} chars"
|
30
30
|
puts " Elapsed: #{elapsed}ms"
|
@@ -38,17 +38,17 @@ module Familia
|
|
38
38
|
puts " Encrypted length: #{encrypted_json&.length} chars"
|
39
39
|
puts " AAD: #{additional_data ? 'present' : 'none'}"
|
40
40
|
|
41
|
-
start_time =
|
41
|
+
start_time = Familia.now
|
42
42
|
begin
|
43
43
|
result = orig_decrypt(encrypted_json, context: context, additional_data: additional_data)
|
44
|
-
elapsed = ((
|
44
|
+
elapsed = ((Familia.now - start_time) * 1000).round(2)
|
45
45
|
|
46
46
|
puts " Result length: #{result ? result.length : 'nil'} chars"
|
47
47
|
puts " Elapsed: #{elapsed}ms"
|
48
48
|
puts
|
49
49
|
result
|
50
50
|
rescue => e
|
51
|
-
elapsed = ((
|
51
|
+
elapsed = ((Familia.now - start_time) * 1000).round(2)
|
52
52
|
puts " ERROR: #{e.class}: #{e.message}"
|
53
53
|
puts " Elapsed: #{elapsed}ms"
|
54
54
|
puts
|
@@ -66,16 +66,16 @@ module Familia
|
|
66
66
|
puts " Context: #{context}"
|
67
67
|
puts " Version: #{version || 'current'}"
|
68
68
|
|
69
|
-
cache =
|
69
|
+
cache = Fiber[:familia_key_cache] ||= {}
|
70
70
|
cache_key = "#{version || current_key_version}:#{context}"
|
71
71
|
puts " Cache key: #{cache_key}"
|
72
72
|
puts " Cache before: #{cache.keys.inspect}"
|
73
73
|
|
74
|
-
start_time =
|
74
|
+
start_time = Familia.now
|
75
75
|
result = orig_derive_key_with_provider(provider, context, version: version)
|
76
|
-
elapsed = ((
|
76
|
+
elapsed = ((Familia.now - start_time) * 1000).round(2)
|
77
77
|
|
78
|
-
cache_after =
|
78
|
+
cache_after = Fiber[:familia_key_cache] || {}
|
79
79
|
puts " Cache after: #{cache_after.keys.inspect}"
|
80
80
|
puts " Derived key: [#{result.bytesize} bytes]"
|
81
81
|
puts " Elapsed: #{elapsed}ms"
|
@@ -92,7 +92,7 @@ Familia.config.encryption_keys = test_keys
|
|
92
92
|
Familia.config.current_key_version = :v1
|
93
93
|
|
94
94
|
# Clear cache
|
95
|
-
|
95
|
+
Fiber[:familia_key_cache] = nil
|
96
96
|
|
97
97
|
# Define test model
|
98
98
|
class TraceTestModel < Familia::Horreum
|
@@ -90,7 +90,7 @@ end
|
|
90
90
|
provider = @provider_class.new
|
91
91
|
master_key = 'a' * 32
|
92
92
|
context = 'TestModel:field:user123'
|
93
|
-
#
|
93
|
+
# UnsortedSet config with null byte
|
94
94
|
original_personal = Familia.config.encryption_personalization
|
95
95
|
Familia.config.encryption_personalization = "bad\0config"
|
96
96
|
begin
|
@@ -189,4 +189,4 @@ true
|
|
189
189
|
#=> true
|
190
190
|
|
191
191
|
# TEARDOWN
|
192
|
-
|
192
|
+
Fiber[:familia_key_cache]&.clear if Fiber[:familia_key_cache]
|
@@ -26,7 +26,7 @@ plaintext = "sensitive data here"
|
|
26
26
|
Familia.config.encryption_keys = test_keys
|
27
27
|
Familia.config.current_key_version = :v2
|
28
28
|
encrypted = Familia::Encryption.encrypt(plaintext, context: context)
|
29
|
-
encrypted_data =
|
29
|
+
encrypted_data = Familia::JsonSerializer.parse(encrypted, symbolize_names: true)
|
30
30
|
encrypted_data[:algorithm]
|
31
31
|
#=> "xchacha20poly1305"
|
32
32
|
|
@@ -38,7 +38,7 @@ plaintext = "sensitive data here"
|
|
38
38
|
Familia.config.encryption_keys = test_keys
|
39
39
|
Familia.config.current_key_version = :v2
|
40
40
|
encrypted = Familia::Encryption.encrypt(plaintext, context: context)
|
41
|
-
encrypted_data =
|
41
|
+
encrypted_data = Familia::JsonSerializer.parse(encrypted, symbolize_names: true)
|
42
42
|
encrypted_data[:key_version]
|
43
43
|
#=> "v2"
|
44
44
|
|
@@ -109,13 +109,13 @@ Familia.config.encryption_keys = test_keys
|
|
109
109
|
Familia.config.current_key_version = :v1
|
110
110
|
|
111
111
|
# Create encrypted data with unknown algorithm. Error should not leak algorithm details.
|
112
|
-
invalid_encrypted = {
|
112
|
+
invalid_encrypted = Familia::JsonSerializer.dump({
|
113
113
|
algorithm: "unknown-cipher",
|
114
114
|
key_version: "v1",
|
115
115
|
nonce: Base64.strict_encode64("x" * 12),
|
116
116
|
ciphertext: Base64.strict_encode64("encrypted_data"),
|
117
117
|
auth_tag: Base64.strict_encode64("y" * 16)
|
118
|
-
}
|
118
|
+
})
|
119
119
|
|
120
120
|
Familia::Encryption.decrypt(invalid_encrypted, context: context)
|
121
121
|
#=!> Familia::EncryptionError
|
@@ -141,13 +141,13 @@ Familia.config.encryption_keys = test_keys
|
|
141
141
|
Familia.config.current_key_version = :v1
|
142
142
|
|
143
143
|
# Create encrypted data with invalid base64 nonce
|
144
|
-
invalid_encrypted = {
|
144
|
+
invalid_encrypted = Familia::JsonSerializer.dump({
|
145
145
|
algorithm: "aes-256-gcm",
|
146
146
|
key_version: "v1",
|
147
147
|
nonce: "invalid_base64!@#",
|
148
148
|
ciphertext: Base64.strict_encode64("encrypted_data"),
|
149
149
|
auth_tag: Base64.strict_encode64("y" * 16)
|
150
|
-
}
|
150
|
+
})
|
151
151
|
|
152
152
|
begin
|
153
153
|
Familia::Encryption.decrypt(invalid_encrypted, context: context)
|
@@ -165,13 +165,13 @@ Familia.config.encryption_keys = test_keys
|
|
165
165
|
Familia.config.current_key_version = :v1
|
166
166
|
|
167
167
|
# Create encrypted data with invalid base64 auth_tag
|
168
|
-
invalid_encrypted = {
|
168
|
+
invalid_encrypted = Familia::JsonSerializer.dump({
|
169
169
|
algorithm: "aes-256-gcm",
|
170
170
|
key_version: "v1",
|
171
171
|
nonce: Base64.strict_encode64("x" * 12),
|
172
172
|
ciphertext: Base64.strict_encode64("encrypted_data"),
|
173
173
|
auth_tag: "invalid_base64!@#"
|
174
|
-
}
|
174
|
+
})
|
175
175
|
|
176
176
|
begin
|
177
177
|
Familia::Encryption.decrypt(invalid_encrypted, context: context)
|
@@ -189,13 +189,13 @@ Familia.config.encryption_keys = test_keys
|
|
189
189
|
Familia.config.current_key_version = :v1
|
190
190
|
|
191
191
|
# Create encrypted data with wrong nonce size (8 bytes instead of 12)
|
192
|
-
invalid_encrypted = {
|
192
|
+
invalid_encrypted = Familia::JsonSerializer.dump({
|
193
193
|
algorithm: "aes-256-gcm",
|
194
194
|
key_version: "v1",
|
195
195
|
nonce: Base64.strict_encode64("x" * 8),
|
196
196
|
ciphertext: Base64.strict_encode64("encrypted_data"),
|
197
197
|
auth_tag: Base64.strict_encode64("y" * 16)
|
198
|
-
}
|
198
|
+
})
|
199
199
|
|
200
200
|
begin
|
201
201
|
Familia::Encryption.decrypt(invalid_encrypted, context: context)
|
@@ -213,13 +213,13 @@ Familia.config.encryption_keys = test_keys
|
|
213
213
|
Familia.config.current_key_version = :v1
|
214
214
|
|
215
215
|
# Create encrypted data with wrong auth_tag size (8 bytes instead of 16)
|
216
|
-
invalid_encrypted = {
|
216
|
+
invalid_encrypted = Familia::JsonSerializer.dump({
|
217
217
|
algorithm: "aes-256-gcm",
|
218
218
|
key_version: "v1",
|
219
219
|
nonce: Base64.strict_encode64("x" * 12),
|
220
220
|
ciphertext: Base64.strict_encode64("encrypted_data"),
|
221
221
|
auth_tag: Base64.strict_encode64("y" * 8)
|
222
|
-
}
|
222
|
+
})
|
223
223
|
|
224
224
|
begin
|
225
225
|
Familia::Encryption.decrypt(invalid_encrypted, context: context)
|
@@ -237,12 +237,12 @@ Familia.config.encryption_keys = test_keys
|
|
237
237
|
Familia.config.current_key_version = :v1
|
238
238
|
|
239
239
|
# Create encrypted data missing nonce field
|
240
|
-
invalid_encrypted = {
|
240
|
+
invalid_encrypted = Familia::JsonSerializer.dump({
|
241
241
|
algorithm: "aes-256-gcm",
|
242
242
|
key_version: "v1",
|
243
243
|
ciphertext: Base64.strict_encode64("encrypted_data"),
|
244
244
|
auth_tag: Base64.strict_encode64("y" * 16)
|
245
|
-
}
|
245
|
+
})
|
246
246
|
|
247
247
|
begin
|
248
248
|
Familia::Encryption.decrypt(invalid_encrypted, context: context)
|
@@ -284,7 +284,7 @@ plaintext = "algorithm check"
|
|
284
284
|
Familia.config.encryption_keys = test_keys
|
285
285
|
Familia.config.current_key_version = :v1
|
286
286
|
encrypted_xchacha = Familia::Encryption.encrypt_with('xchacha20poly1305', plaintext, context: context)
|
287
|
-
encrypted_data_xchacha =
|
287
|
+
encrypted_data_xchacha = Familia::JsonSerializer.parse(encrypted_xchacha, symbolize_names: true)
|
288
288
|
encrypted_data_xchacha[:algorithm]
|
289
289
|
#=> "xchacha20poly1305"
|
290
290
|
|
@@ -296,7 +296,7 @@ plaintext = "algorithm check"
|
|
296
296
|
Familia.config.encryption_keys = test_keys
|
297
297
|
Familia.config.current_key_version = :v1
|
298
298
|
encrypted_aes = Familia::Encryption.encrypt_with('aes-256-gcm', plaintext, context: context)
|
299
|
-
encrypted_data_aes =
|
299
|
+
encrypted_data_aes = Familia::JsonSerializer.parse(encrypted_aes, symbolize_names: true)
|
300
300
|
encrypted_data_aes[:algorithm]
|
301
301
|
#=> "aes-256-gcm"
|
302
302
|
|
@@ -308,7 +308,7 @@ plaintext = "nonce size test"
|
|
308
308
|
Familia.config.encryption_keys = test_keys
|
309
309
|
Familia.config.current_key_version = :v1
|
310
310
|
encrypted_xchacha = Familia::Encryption.encrypt_with('xchacha20poly1305', plaintext, context: context)
|
311
|
-
encrypted_data_xchacha =
|
311
|
+
encrypted_data_xchacha = Familia::JsonSerializer.parse(encrypted_xchacha, symbolize_names: true)
|
312
312
|
nonce_bytes_xchacha = Base64.strict_decode64(encrypted_data_xchacha[:nonce])
|
313
313
|
nonce_bytes_xchacha.length
|
314
314
|
#=> 24
|
@@ -321,10 +321,10 @@ plaintext = "nonce size test"
|
|
321
321
|
Familia.config.encryption_keys = test_keys
|
322
322
|
Familia.config.current_key_version = :v1
|
323
323
|
encrypted_aes = Familia::Encryption.encrypt_with('aes-256-gcm', plaintext, context: context)
|
324
|
-
encrypted_data_aes =
|
324
|
+
encrypted_data_aes = Familia::JsonSerializer.parse(encrypted_aes, symbolize_names: true)
|
325
325
|
nonce_bytes_aes = Base64.strict_decode64(encrypted_data_aes[:nonce])
|
326
326
|
nonce_bytes_aes.length
|
327
327
|
#=> 12
|
328
328
|
|
329
329
|
# TEARDOWN
|
330
|
-
|
330
|
+
Fiber[:familia_key_cache]&.clear if Fiber[:familia_key_cache]
|
@@ -20,7 +20,7 @@ require_relative '../helpers/test_helpers'
|
|
20
20
|
@test_keys[:v1].nil?
|
21
21
|
#=> false
|
22
22
|
|
23
|
-
##
|
23
|
+
## UnsortedSet config and check immediately in same test
|
24
24
|
Familia.config.encryption_keys = @test_keys
|
25
25
|
Familia.config.current_key_version = :v1
|
26
26
|
result = Familia::Encryption.encrypt('test', context: 'test')
|
@@ -13,12 +13,12 @@ require_relative '../helpers/test_helpers'
|
|
13
13
|
defined?(Familia::Encryption)
|
14
14
|
#=> "constant"
|
15
15
|
|
16
|
-
##
|
16
|
+
## UnsortedSet and check configuration directly in test
|
17
17
|
Familia.encryption_keys = { v1: Base64.strict_encode64('a' * 32) }
|
18
18
|
Familia.encryption_keys.is_a?(Hash)
|
19
19
|
#=> true
|
20
20
|
|
21
|
-
##
|
21
|
+
## UnsortedSet and check current key version directly in test
|
22
22
|
Familia.current_key_version = :v1
|
23
23
|
Familia.current_key_version
|
24
24
|
#=> :v1
|
@@ -77,15 +77,19 @@ end
|
|
77
77
|
@doc.content.to_str
|
78
78
|
#=!> NoMethodError
|
79
79
|
|
80
|
-
## JSON serialization - to_json (fails for security)
|
80
|
+
## JSON serialization - to_json (fails for security) - Basic tryouts testcase
|
81
81
|
begin
|
82
|
-
@doc.content
|
83
|
-
raise "Should have raised SerializerError"
|
82
|
+
Familia::JsonSerializer.dump(@doc.content)
|
84
83
|
rescue Familia::SerializerError => e
|
85
84
|
e.class
|
86
85
|
end
|
87
86
|
#=> Familia::SerializerError
|
88
87
|
|
88
|
+
## JSON serialization - to_json (fails for security) - Robust tryout testcase
|
89
|
+
Familia::JsonSerializer.dump(@doc.content)
|
90
|
+
#=:> Familia::SerializerError
|
91
|
+
#=~> /Failed to dump ConcealedString/
|
92
|
+
|
89
93
|
## JSON serialization - as_json
|
90
94
|
@doc.content.as_json
|
91
95
|
#=> "[CONCEALED]"
|
@@ -147,7 +151,7 @@ end
|
|
147
151
|
|
148
152
|
## Encrypted data is valid JSON
|
149
153
|
begin
|
150
|
-
parsed =
|
154
|
+
parsed = Familia::JsonSerializer.parse(@encrypted_data)
|
151
155
|
parsed.key?('algorithm')
|
152
156
|
rescue
|
153
157
|
false
|
@@ -218,15 +222,15 @@ debug_array.map(&:to_s)
|
|
218
222
|
## Debug what's actually in the database
|
219
223
|
@all_keys = Familia.dbclient.keys("*")
|
220
224
|
@all_keys
|
221
|
-
#=> ["
|
225
|
+
#=> ["secret_document:test123:object", "secret_document:instances"]
|
222
226
|
|
223
227
|
## Check database storage - should be encrypted
|
224
|
-
@db_hash = Familia.dbclient.hgetall("
|
228
|
+
@db_hash = Familia.dbclient.hgetall("secret_document:test123:object")
|
225
229
|
@db_hash.keys
|
226
230
|
#=> ["id", "title", "content", "api_key"]
|
227
231
|
|
228
232
|
## Database storage contains encrypted string
|
229
|
-
db_content = Familia.dbclient.hget("
|
233
|
+
db_content = Familia.dbclient.hget("secret_document:test123:object", "content")
|
230
234
|
db_content&.class&.name || "nil"
|
231
235
|
#=> "String"
|
232
236
|
|
@@ -138,7 +138,7 @@ end
|
|
138
138
|
@model4 = FullSecureModel4.new(model_id: 'secure-126')
|
139
139
|
@model4.password = 'secure-pass'
|
140
140
|
@model4.activity_log << 'User logged in'
|
141
|
-
@model4.metadata['last_login'] =
|
141
|
+
@model4.metadata['last_login'] = Familia.now.to_i.to_s
|
142
142
|
|
143
143
|
[@model4.password.to_s, @model4.activity_log.size, @model4.metadata.has_key?('last_login')]
|
144
144
|
#=> ['[CONCEALED]', 1, true]
|
@@ -189,7 +189,7 @@ end
|
|
189
189
|
# Test shows that algorithm parameter is currently ignored - XChaCha20Poly1305 is used by default
|
190
190
|
concealed_data = @aes_model.secret_data
|
191
191
|
encrypted_json = concealed_data.encrypted_value
|
192
|
-
parsed_data =
|
192
|
+
parsed_data = Familia::JsonSerializer.parse(encrypted_json, symbolize_names: true)
|
193
193
|
[parsed_data[:algorithm], @aes_model.secret_data.reveal { |data| data }]
|
194
194
|
#=> ["xchacha20poly1305", "aes-gcm integration test"]
|
195
195
|
|
@@ -211,6 +211,6 @@ end
|
|
211
211
|
# Verify algorithm and decryption
|
212
212
|
concealed_data = @aes_model2.secret_data
|
213
213
|
encrypted_json = concealed_data.encrypted_value
|
214
|
-
parsed_data =
|
214
|
+
parsed_data = Familia::JsonSerializer.parse(encrypted_json, symbolize_names: true)
|
215
215
|
[parsed_data[:algorithm], @aes_model2.secret_data.reveal { |data| data }]
|
216
216
|
#=> ["xchacha20poly1305", "aes-gcm integration test"]
|
@@ -14,7 +14,7 @@ Familia.config.current_key_version = :v1
|
|
14
14
|
|
15
15
|
## No persistent key cache exists
|
16
16
|
## Verify that we don't maintain a key cache at all
|
17
|
-
|
17
|
+
Fiber[:familia_key_cache]
|
18
18
|
#=> nil
|
19
19
|
|
20
20
|
## Each encryption gets fresh key derivation
|
@@ -30,7 +30,7 @@ end
|
|
30
30
|
#=> 'secret-value'
|
31
31
|
|
32
32
|
## No cache should be created
|
33
|
-
|
33
|
+
Fiber[:familia_key_cache]
|
34
34
|
#=> nil
|
35
35
|
|
36
36
|
## Reading the value also doesn't create cache
|
@@ -41,7 +41,7 @@ end
|
|
41
41
|
#=> 'secret-value'
|
42
42
|
|
43
43
|
## repaired test
|
44
|
-
|
44
|
+
Fiber[:familia_key_cache]
|
45
45
|
#=> nil
|
46
46
|
|
47
47
|
## Multiple fields don't share state
|
@@ -61,7 +61,7 @@ end
|
|
61
61
|
#=> 'value-c'
|
62
62
|
|
63
63
|
## Still no cache after multiple operations
|
64
|
-
|
64
|
+
Fiber[:familia_key_cache]
|
65
65
|
#=> nil
|
66
66
|
|
67
67
|
## All values can be retrieved correctly
|
@@ -83,7 +83,7 @@ end
|
|
83
83
|
#=> 'value-c'
|
84
84
|
|
85
85
|
## Still no cache
|
86
|
-
|
86
|
+
Fiber[:familia_key_cache]
|
87
87
|
#=> nil
|
88
88
|
|
89
89
|
## Master keys are wiped after each operation
|
@@ -111,7 +111,7 @@ end.all?
|
|
111
111
|
#=> true
|
112
112
|
|
113
113
|
## Still no cache after multiple operations
|
114
|
-
|
114
|
+
Fiber[:familia_key_cache]
|
115
115
|
#=> nil
|
116
116
|
|
117
117
|
## Thread isolation (no shared state between threads)
|
@@ -132,7 +132,7 @@ end
|
|
132
132
|
model.thread_secret = "thread-secret-#{i}"
|
133
133
|
|
134
134
|
# Verify no cache in this thread
|
135
|
-
cache_state =
|
135
|
+
cache_state = Fiber[:familia_key_cache]
|
136
136
|
|
137
137
|
# Store results
|
138
138
|
@results << {
|
@@ -182,7 +182,7 @@ end
|
|
182
182
|
#=> true
|
183
183
|
|
184
184
|
## Still no cache after 100 operations
|
185
|
-
|
185
|
+
Fiber[:familia_key_cache]
|
186
186
|
#=> nil
|
187
187
|
|
188
188
|
## Key rotation works without cache complications
|
@@ -200,7 +200,7 @@ end
|
|
200
200
|
@model6.rotated_field = 'encrypted-with-v2'
|
201
201
|
|
202
202
|
# Still no cache with new key version
|
203
|
-
|
203
|
+
Fiber[:familia_key_cache]
|
204
204
|
#=> nil
|
205
205
|
|
206
206
|
## Value is correctly encrypted/decrypted with v2
|
@@ -214,6 +214,6 @@ Familia.config.current_key_version = :v1
|
|
214
214
|
#=> :v1
|
215
215
|
|
216
216
|
# Teardown
|
217
|
-
|
217
|
+
Fiber[:familia_key_cache] = nil
|
218
218
|
Familia.config.encryption_keys = nil
|
219
219
|
Familia.config.current_key_version = nil
|
@@ -140,12 +140,12 @@ user.password = 'test-password'
|
|
140
140
|
encrypted = user.instance_variable_get(:@password)
|
141
141
|
|
142
142
|
# Tamper with auth tag
|
143
|
-
parsed =
|
143
|
+
parsed = Familia::JsonSerializer.parse(encrypted.encrypted_value, symbolize_names: true)
|
144
144
|
original_auth_tag = parsed[:auth_tag]
|
145
145
|
tampered_auth_tag = original_auth_tag.dup
|
146
146
|
tampered_auth_tag[0] = tampered_auth_tag[0] == 'A' ? 'B' : 'A'
|
147
147
|
parsed[:auth_tag] = tampered_auth_tag
|
148
|
-
tampered_json = parsed
|
148
|
+
tampered_json = Familia::JsonSerializer.dump(parsed)
|
149
149
|
|
150
150
|
user.instance_variable_set(:@password, tampered_json)
|
151
151
|
begin
|
@@ -166,12 +166,12 @@ user.password = 'test-password'
|
|
166
166
|
encrypted = user.instance_variable_get(:@password)
|
167
167
|
|
168
168
|
# Tamper with ciphertext
|
169
|
-
parsed =
|
169
|
+
parsed = Familia::JsonSerializer.parse(encrypted.encrypted_value, symbolize_names: true)
|
170
170
|
original_ciphertext = parsed[:ciphertext]
|
171
171
|
tampered_ciphertext = original_ciphertext.dup
|
172
172
|
tampered_ciphertext[0] = tampered_ciphertext[0] == 'A' ? 'B' : 'A'
|
173
173
|
parsed[:ciphertext] = tampered_ciphertext
|
174
|
-
tampered_json = parsed
|
174
|
+
tampered_json = Familia::JsonSerializer.dump(parsed)
|
175
175
|
|
176
176
|
user.instance_variable_set(:@password, tampered_json)
|
177
177
|
begin
|
@@ -192,12 +192,12 @@ user.password = 'test-password'
|
|
192
192
|
encrypted = user.instance_variable_get(:@password)
|
193
193
|
|
194
194
|
# Tamper with nonce
|
195
|
-
parsed =
|
195
|
+
parsed = Familia::JsonSerializer.parse(encrypted.encrypted_value, symbolize_names: true)
|
196
196
|
original_nonce = parsed[:nonce]
|
197
197
|
tampered_nonce = original_nonce.dup
|
198
198
|
tampered_nonce[0] = tampered_nonce[0] == 'A' ? 'B' : 'A'
|
199
199
|
parsed[:nonce] = tampered_nonce
|
200
|
-
tampered_json = parsed
|
200
|
+
tampered_json = Familia::JsonSerializer.dump(parsed)
|
201
201
|
|
202
202
|
user.instance_variable_set(:@password, tampered_json)
|
203
203
|
begin
|
@@ -219,9 +219,9 @@ encrypted_with_v1 = user.instance_variable_get(:@password)
|
|
219
219
|
|
220
220
|
|
221
221
|
# Parse and change key version to non-existent version
|
222
|
-
parsed =
|
222
|
+
parsed = Familia::JsonSerializer.parse(encrypted_with_v1.encrypted_value, symbolize_names: true)
|
223
223
|
parsed[:key_version] = 'v999'
|
224
|
-
modified_json = parsed
|
224
|
+
modified_json = Familia::JsonSerializer.dump(parsed)
|
225
225
|
|
226
226
|
user.instance_variable_set(:@password, modified_json)
|
227
227
|
begin
|
@@ -239,12 +239,12 @@ user.password = 'nonce-test-xchacha'
|
|
239
239
|
encrypted_with_nonce = user.instance_variable_get(:@password)
|
240
240
|
|
241
241
|
# Parse and modify nonce (XChaCha20Poly1305 uses 24-byte nonces)
|
242
|
-
parsed =
|
242
|
+
parsed = Familia::JsonSerializer.parse(encrypted_with_nonce.encrypted_value, symbolize_names: true)
|
243
243
|
original_nonce = parsed[:nonce]
|
244
244
|
# Create a different valid base64 nonce for XChaCha20Poly1305 (24 bytes)
|
245
245
|
different_nonce = Base64.strict_encode64('x' * 24)
|
246
246
|
parsed[:nonce] = different_nonce
|
247
|
-
modified_json = parsed
|
247
|
+
modified_json = Familia::JsonSerializer.dump(parsed)
|
248
248
|
|
249
249
|
user.instance_variable_set(:@password, modified_json)
|
250
250
|
begin
|
@@ -264,12 +264,12 @@ encrypted_aes = Familia::Encryption.encrypt_with('aes-256-gcm', 'nonce-test-aes'
|
|
264
264
|
user_aes.instance_variable_set(:@password, encrypted_aes)
|
265
265
|
|
266
266
|
# Parse and modify nonce (AES-GCM uses 12-byte nonces)
|
267
|
-
parsed_aes =
|
267
|
+
parsed_aes = Familia::JsonSerializer.parse(encrypted_aes, symbolize_names: true)
|
268
268
|
original_nonce_aes = parsed_aes[:nonce]
|
269
269
|
# Create a different valid base64 nonce for AES-GCM (12 bytes)
|
270
270
|
different_nonce_aes = Base64.strict_encode64('y' * 12)
|
271
271
|
parsed_aes[:nonce] = different_nonce_aes
|
272
|
-
modified_json_aes = parsed_aes
|
272
|
+
modified_json_aes = Familia::JsonSerializer.dump(parsed_aes)
|
273
273
|
|
274
274
|
user_aes.instance_variable_set(:@password, modified_json_aes)
|
275
275
|
begin
|
@@ -367,9 +367,9 @@ user.password = 'algorithm-test'
|
|
367
367
|
encrypted = user.instance_variable_get(:@password)
|
368
368
|
|
369
369
|
# Tamper with algorithm field
|
370
|
-
parsed =
|
370
|
+
parsed = Familia::JsonSerializer.parse(encrypted.encrypted_value, symbolize_names: true)
|
371
371
|
parsed[:algorithm] = 'unsupported_algorithm'
|
372
|
-
tampered_json = parsed
|
372
|
+
tampered_json = Familia::JsonSerializer.dump(parsed)
|
373
373
|
|
374
374
|
user.instance_variable_set(:@password, tampered_json)
|
375
375
|
user.password
|
@@ -29,9 +29,9 @@ end
|
|
29
29
|
## Tampered auth tag fails decryption
|
30
30
|
@model.secret = 'valid-secret'
|
31
31
|
@valid_cipher = @model.secret.encrypted_value
|
32
|
-
@tampered =
|
32
|
+
@tampered = Familia::JsonSerializer.parse(@valid_cipher)
|
33
33
|
@tampered['auth_tag'] = Base64.strict_encode64('tampered' * 4)
|
34
|
-
@model.instance_variable_set(:@secret, @tampered
|
34
|
+
@model.instance_variable_set(:@secret, Familia::JsonSerializer.dump(@tampered))
|
35
35
|
|
36
36
|
@model.secret
|
37
37
|
#=!> Familia::EncryptionError
|
@@ -58,7 +58,7 @@ Familia::Encryption.reset_derivation_count!
|
|
58
58
|
# Ensure keys are available for this test
|
59
59
|
Familia.config.encryption_keys = @test_keys
|
60
60
|
@error_model = ErrorTest.new(id: 'err-counter')
|
61
|
-
#
|
61
|
+
# UnsortedSet valid JSON but with invalid base64 data to trigger decrypt failure after parsing
|
62
62
|
@error_model.instance_variable_set(:@secret, '{"algorithm":"aes-256-gcm","nonce":"dGVzdA==","ciphertext":"invalid-base64!!!","auth_tag":"dGVzdA==","key_version":"v1"}')
|
63
63
|
begin
|
64
64
|
@error_model.secret
|
@@ -72,9 +72,9 @@ Familia::Encryption.derivation_count.value
|
|
72
72
|
# Ensure keys are available for this test
|
73
73
|
Familia.config.encryption_keys = @test_keys
|
74
74
|
@model.secret = 'test-data'
|
75
|
-
@cipher_data =
|
75
|
+
@cipher_data = Familia::JsonSerializer.parse(@model.secret.encrypted_value)
|
76
76
|
@cipher_data['algorithm'] = 'unsupported-algorithm'
|
77
|
-
@model.instance_variable_set(:@secret, @cipher_data
|
77
|
+
@model.instance_variable_set(:@secret, Familia::JsonSerializer.dump(@cipher_data))
|
78
78
|
|
79
79
|
@model.secret
|
80
80
|
#=!> Familia::EncryptionError
|
@@ -93,9 +93,9 @@ Familia.config.current_key_version = @original_version
|
|
93
93
|
Familia.config.encryption_keys = @test_keys
|
94
94
|
Familia.config.current_key_version = :v1
|
95
95
|
@model.secret = 'test-data'
|
96
|
-
@cipher_with_bad_version =
|
96
|
+
@cipher_with_bad_version = Familia::JsonSerializer.parse(@model.secret.encrypted_value)
|
97
97
|
@cipher_with_bad_version['key_version'] = 'nonexistent'
|
98
|
-
@model.instance_variable_set(:@secret, @cipher_with_bad_version
|
98
|
+
@model.instance_variable_set(:@secret, Familia::JsonSerializer.dump(@cipher_with_bad_version))
|
99
99
|
@model.secret
|
100
100
|
#=!> Familia::EncryptionError
|
101
101
|
#==> error.message.include?('No key for version: nonexistent')
|
@@ -162,7 +162,7 @@ internal_empty.nil?
|
|
162
162
|
## Consistent behavior across Ruby restart simulation
|
163
163
|
model = PersistenceTestModel.new(user_id: 'persistence-test')
|
164
164
|
model.persistent_data = 'data-to-persist'
|
165
|
-
|
165
|
+
Fiber[:familia_request_cache] = nil if Fiber[:familia_request_cache]
|
166
166
|
# With secure-by-default, field access returns ConcealedString
|
167
167
|
model.persistent_data.to_s
|
168
168
|
#=> '[CONCEALED]'
|