activesupport 1.2.4 → 8.1.2
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 +505 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +40 -0
- data/lib/active_support/actionable_error.rb +50 -0
- data/lib/active_support/all.rb +5 -0
- data/lib/active_support/array_inquirer.rb +50 -0
- data/lib/active_support/backtrace_cleaner.rb +234 -0
- data/lib/active_support/benchmark.rb +21 -0
- data/lib/active_support/benchmarkable.rb +53 -0
- data/lib/active_support/broadcast_logger.rb +238 -0
- data/lib/active_support/builder.rb +8 -0
- data/lib/active_support/cache/coder.rb +153 -0
- data/lib/active_support/cache/entry.rb +134 -0
- data/lib/active_support/cache/file_store.rb +244 -0
- data/lib/active_support/cache/mem_cache_store.rb +288 -0
- data/lib/active_support/cache/memory_store.rb +264 -0
- data/lib/active_support/cache/null_store.rb +62 -0
- data/lib/active_support/cache/redis_cache_store.rb +498 -0
- data/lib/active_support/cache/serializer_with_fallback.rb +152 -0
- data/lib/active_support/cache/strategy/local_cache.rb +246 -0
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +45 -0
- data/lib/active_support/cache.rb +1170 -0
- data/lib/active_support/callbacks.rb +960 -0
- data/lib/active_support/class_attribute.rb +33 -0
- data/lib/active_support/code_generator.rb +79 -0
- data/lib/active_support/concern.rb +217 -0
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +18 -0
- data/lib/active_support/concurrency/null_lock.rb +13 -0
- data/lib/active_support/concurrency/share_lock.rb +225 -0
- data/lib/active_support/concurrency/thread_monitor.rb +55 -0
- data/lib/active_support/configurable.rb +193 -0
- data/lib/active_support/configuration_file.rb +60 -0
- data/lib/active_support/continuous_integration.rb +145 -0
- data/lib/active_support/core_ext/array/access.rb +100 -0
- data/lib/active_support/core_ext/array/conversions.rb +209 -26
- data/lib/active_support/core_ext/array/extract.rb +21 -0
- data/lib/active_support/core_ext/array/extract_options.rb +31 -0
- data/lib/active_support/core_ext/array/grouping.rb +109 -0
- data/lib/active_support/core_ext/array/inquiry.rb +19 -0
- data/lib/active_support/core_ext/array/wrap.rb +48 -0
- data/lib/active_support/core_ext/array.rb +8 -4
- data/lib/active_support/core_ext/benchmark.rb +6 -0
- data/lib/active_support/core_ext/big_decimal/conversions.rb +14 -0
- data/lib/active_support/core_ext/big_decimal.rb +3 -0
- data/lib/active_support/core_ext/class/attribute.rb +137 -0
- data/lib/active_support/core_ext/class/attribute_accessors.rb +6 -0
- data/lib/active_support/core_ext/class/subclasses.rb +24 -0
- data/lib/active_support/core_ext/class.rb +4 -0
- data/lib/active_support/core_ext/date/acts_like.rb +10 -0
- data/lib/active_support/core_ext/date/blank.rb +18 -0
- data/lib/active_support/core_ext/date/calculations.rb +161 -0
- data/lib/active_support/core_ext/date/conversions.rb +95 -28
- data/lib/active_support/core_ext/date/zones.rb +8 -0
- data/lib/active_support/core_ext/date.rb +6 -5
- data/lib/active_support/core_ext/date_and_time/calculations.rb +374 -0
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +23 -0
- data/lib/active_support/core_ext/date_and_time/zones.rb +40 -0
- data/lib/active_support/core_ext/date_time/acts_like.rb +16 -0
- data/lib/active_support/core_ext/date_time/blank.rb +18 -0
- data/lib/active_support/core_ext/date_time/calculations.rb +215 -0
- data/lib/active_support/core_ext/date_time/compatibility.rb +16 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +108 -0
- data/lib/active_support/core_ext/date_time.rb +7 -0
- data/lib/active_support/core_ext/digest/uuid.rb +76 -0
- data/lib/active_support/core_ext/digest.rb +3 -0
- data/lib/active_support/core_ext/enumerable.rb +277 -7
- data/lib/active_support/core_ext/erb/util.rb +201 -0
- data/lib/active_support/core_ext/file/atomic.rb +72 -0
- data/lib/active_support/core_ext/file.rb +3 -0
- data/lib/active_support/core_ext/hash/conversions.rb +262 -0
- data/lib/active_support/core_ext/hash/deep_merge.rb +43 -0
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
- data/lib/active_support/core_ext/hash/except.rb +12 -0
- data/lib/active_support/core_ext/hash/indifferent_access.rb +19 -55
- data/lib/active_support/core_ext/hash/keys.rb +134 -44
- data/lib/active_support/core_ext/hash/reverse_merge.rb +22 -22
- data/lib/active_support/core_ext/hash/slice.rb +27 -0
- data/lib/active_support/core_ext/hash.rb +9 -8
- data/lib/active_support/core_ext/integer/inflections.rb +29 -13
- data/lib/active_support/core_ext/integer/multiple.rb +12 -0
- data/lib/active_support/core_ext/integer/time.rb +22 -0
- data/lib/active_support/core_ext/integer.rb +4 -6
- data/lib/active_support/core_ext/kernel/concern.rb +14 -0
- data/lib/active_support/core_ext/kernel/reporting.rb +45 -0
- data/lib/active_support/core_ext/kernel/singleton_class.rb +8 -0
- data/lib/active_support/core_ext/kernel.rb +4 -78
- data/lib/active_support/core_ext/load_error.rb +6 -35
- data/lib/active_support/core_ext/module/aliasing.rb +31 -0
- data/lib/active_support/core_ext/module/anonymous.rb +30 -0
- data/lib/active_support/core_ext/module/attr_internal.rb +48 -0
- data/lib/active_support/core_ext/module/attribute_accessors.rb +214 -0
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +175 -0
- data/lib/active_support/core_ext/module/concerning.rb +140 -0
- data/lib/active_support/core_ext/module/delegation.rb +225 -0
- data/lib/active_support/core_ext/module/deprecation.rb +25 -0
- data/lib/active_support/core_ext/module/introspection.rb +65 -0
- data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
- data/lib/active_support/core_ext/module/remove_method.rb +17 -0
- data/lib/active_support/core_ext/module.rb +13 -0
- data/lib/active_support/core_ext/name_error.rb +59 -0
- data/lib/active_support/core_ext/numeric/bytes.rb +73 -42
- data/lib/active_support/core_ext/numeric/conversions.rb +145 -0
- data/lib/active_support/core_ext/numeric/time.rb +64 -57
- data/lib/active_support/core_ext/numeric.rb +4 -6
- data/lib/active_support/core_ext/object/acts_like.rb +45 -0
- data/lib/active_support/core_ext/object/blank.rb +199 -0
- data/lib/active_support/core_ext/object/conversions.rb +6 -0
- data/lib/active_support/core_ext/object/deep_dup.rb +71 -0
- data/lib/active_support/core_ext/object/duplicable.rb +69 -0
- data/lib/active_support/core_ext/object/inclusion.rb +37 -0
- data/lib/active_support/core_ext/object/instance_variables.rb +32 -0
- data/lib/active_support/core_ext/object/json.rb +267 -0
- data/lib/active_support/core_ext/object/to_param.rb +3 -0
- data/lib/active_support/core_ext/object/to_query.rb +93 -0
- data/lib/active_support/core_ext/object/try.rb +158 -0
- data/lib/active_support/core_ext/object/with.rb +46 -0
- data/lib/active_support/core_ext/object/with_options.rb +101 -0
- data/lib/active_support/core_ext/object.rb +17 -0
- data/lib/active_support/core_ext/pathname/blank.rb +20 -0
- data/lib/active_support/core_ext/pathname/existence.rb +23 -0
- data/lib/active_support/core_ext/pathname.rb +4 -0
- data/lib/active_support/core_ext/range/compare_range.rb +57 -0
- data/lib/active_support/core_ext/range/conversions.rb +58 -17
- data/lib/active_support/core_ext/range/overlap.rb +40 -0
- data/lib/active_support/core_ext/range/sole.rb +17 -0
- data/lib/active_support/core_ext/range.rb +5 -4
- data/lib/active_support/core_ext/regexp.rb +14 -0
- data/lib/active_support/core_ext/securerandom.rb +57 -0
- data/lib/active_support/core_ext/string/access.rb +93 -56
- data/lib/active_support/core_ext/string/behavior.rb +8 -0
- data/lib/active_support/core_ext/string/conversions.rb +57 -16
- data/lib/active_support/core_ext/string/exclude.rb +13 -0
- data/lib/active_support/core_ext/string/filters.rb +151 -0
- data/lib/active_support/core_ext/string/indent.rb +45 -0
- data/lib/active_support/core_ext/string/inflections.rb +297 -54
- data/lib/active_support/core_ext/string/inquiry.rb +16 -0
- data/lib/active_support/core_ext/string/multibyte.rb +67 -0
- data/lib/active_support/core_ext/string/output_safety.rb +235 -0
- data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -18
- data/lib/active_support/core_ext/string/strip.rb +27 -0
- data/lib/active_support/core_ext/string/zones.rb +16 -0
- data/lib/active_support/core_ext/string.rb +14 -10
- data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
- data/lib/active_support/core_ext/symbol.rb +3 -0
- data/lib/active_support/core_ext/thread/backtrace/location.rb +7 -0
- data/lib/active_support/core_ext/time/acts_like.rb +10 -0
- data/lib/active_support/core_ext/time/calculations.rb +358 -153
- data/lib/active_support/core_ext/time/compatibility.rb +15 -0
- data/lib/active_support/core_ext/time/conversions.rb +69 -30
- data/lib/active_support/core_ext/time/zones.rb +97 -0
- data/lib/active_support/core_ext/time.rb +6 -6
- data/lib/active_support/core_ext.rb +5 -1
- data/lib/active_support/current_attributes/test_helper.rb +13 -0
- data/lib/active_support/current_attributes.rb +243 -0
- data/lib/active_support/deep_mergeable.rb +53 -0
- data/lib/active_support/delegation.rb +183 -0
- data/lib/active_support/dependencies/autoload.rb +72 -0
- data/lib/active_support/dependencies/interlock.rb +55 -0
- data/lib/active_support/dependencies/require_dependency.rb +28 -0
- data/lib/active_support/dependencies.rb +84 -222
- data/lib/active_support/deprecation/behaviors.rb +148 -0
- data/lib/active_support/deprecation/constant_accessor.rb +74 -0
- data/lib/active_support/deprecation/deprecators.rb +104 -0
- data/lib/active_support/deprecation/disallowed.rb +54 -0
- data/lib/active_support/deprecation/method_wrappers.rb +68 -0
- data/lib/active_support/deprecation/proxy_wrappers.rb +189 -0
- data/lib/active_support/deprecation/reporting.rb +162 -0
- data/lib/active_support/deprecation.rb +81 -0
- data/lib/active_support/deprecator.rb +7 -0
- data/lib/active_support/descendants_tracker.rb +112 -0
- data/lib/active_support/digest.rb +22 -0
- data/lib/active_support/duration/iso8601_parser.rb +123 -0
- data/lib/active_support/duration/iso8601_serializer.rb +64 -0
- data/lib/active_support/duration.rb +524 -0
- data/lib/active_support/editor.rb +70 -0
- data/lib/active_support/encrypted_configuration.rb +126 -0
- data/lib/active_support/encrypted_file.rb +133 -0
- data/lib/active_support/environment_inquirer.rb +40 -0
- data/lib/active_support/error_reporter/test_helper.rb +15 -0
- data/lib/active_support/error_reporter.rb +318 -0
- data/lib/active_support/event_reporter/test_helper.rb +32 -0
- data/lib/active_support/event_reporter.rb +592 -0
- data/lib/active_support/evented_file_update_checker.rb +185 -0
- data/lib/active_support/execution_context/test_helper.rb +13 -0
- data/lib/active_support/execution_context.rb +110 -0
- data/lib/active_support/execution_wrapper.rb +150 -0
- data/lib/active_support/executor/test_helper.rb +7 -0
- data/lib/active_support/executor.rb +8 -0
- data/lib/active_support/file_update_checker.rb +166 -0
- data/lib/active_support/fork_tracker.rb +43 -0
- data/lib/active_support/gem_version.rb +17 -0
- data/lib/active_support/gzip.rb +41 -0
- data/lib/active_support/hash_with_indifferent_access.rb +464 -0
- data/lib/active_support/html_safe_translation.rb +56 -0
- data/lib/active_support/i18n.rb +17 -0
- data/lib/active_support/i18n_railtie.rb +140 -0
- data/lib/active_support/inflections.rb +68 -49
- data/lib/active_support/inflector/inflections.rb +290 -0
- data/lib/active_support/inflector/methods.rb +387 -0
- data/lib/active_support/inflector/transliterate.rb +147 -0
- data/lib/active_support/inflector.rb +7 -164
- data/lib/active_support/isolated_execution_state.rb +76 -0
- data/lib/active_support/json/decoding.rb +78 -0
- data/lib/active_support/json/encoding.rb +256 -0
- data/lib/active_support/json.rb +4 -0
- data/lib/active_support/key_generator.rb +66 -0
- data/lib/active_support/lazy_load_hooks.rb +107 -0
- data/lib/active_support/locale/en.rb +33 -0
- data/lib/active_support/locale/en.yml +141 -0
- data/lib/active_support/log_subscriber/test_helper.rb +106 -0
- data/lib/active_support/log_subscriber.rb +188 -0
- data/lib/active_support/logger.rb +55 -0
- data/lib/active_support/logger_silence.rb +21 -0
- data/lib/active_support/logger_thread_safe_level.rb +50 -0
- data/lib/active_support/message_encryptor.rb +374 -0
- data/lib/active_support/message_encryptors.rb +193 -0
- data/lib/active_support/message_pack/cache_serializer.rb +23 -0
- data/lib/active_support/message_pack/extensions.rb +310 -0
- data/lib/active_support/message_pack/serializer.rb +63 -0
- data/lib/active_support/message_pack.rb +50 -0
- data/lib/active_support/message_verifier.rb +377 -0
- data/lib/active_support/message_verifiers.rb +189 -0
- data/lib/active_support/messages/codec.rb +65 -0
- data/lib/active_support/messages/metadata.rb +146 -0
- data/lib/active_support/messages/rotation_configuration.rb +23 -0
- data/lib/active_support/messages/rotation_coordinator.rb +102 -0
- data/lib/active_support/messages/rotator.rb +69 -0
- data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
- data/lib/active_support/multibyte/chars.rb +188 -0
- data/lib/active_support/multibyte/unicode.rb +42 -0
- data/lib/active_support/multibyte.rb +27 -0
- data/lib/active_support/notifications/fanout.rb +467 -0
- data/lib/active_support/notifications/instrumenter.rb +240 -0
- data/lib/active_support/notifications.rb +281 -0
- data/lib/active_support/number_helper/number_converter.rb +190 -0
- data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +30 -0
- data/lib/active_support/number_helper/number_to_human_converter.rb +69 -0
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +60 -0
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +16 -0
- data/lib/active_support/number_helper/number_to_phone_converter.rb +60 -0
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +59 -0
- data/lib/active_support/number_helper/rounding_helper.rb +46 -0
- data/lib/active_support/number_helper.rb +479 -0
- data/lib/active_support/option_merger.rb +38 -0
- data/lib/active_support/ordered_hash.rb +50 -0
- data/lib/active_support/ordered_options.rb +141 -25
- data/lib/active_support/parameter_filter.rb +157 -0
- data/lib/active_support/rails.rb +26 -0
- data/lib/active_support/railtie.rb +180 -0
- data/lib/active_support/reloader.rb +138 -0
- data/lib/active_support/rescuable.rb +176 -0
- data/lib/active_support/secure_compare_rotator.rb +58 -0
- data/lib/active_support/security_utils.rb +38 -0
- data/lib/active_support/string_inquirer.rb +35 -0
- data/lib/active_support/structured_event_subscriber.rb +99 -0
- data/lib/active_support/subscriber.rb +141 -0
- data/lib/active_support/syntax_error_proxy.rb +67 -0
- data/lib/active_support/tagged_logging.rb +157 -0
- data/lib/active_support/test_case.rb +365 -0
- data/lib/active_support/testing/assertions.rb +369 -0
- data/lib/active_support/testing/autorun.rb +10 -0
- data/lib/active_support/testing/constant_lookup.rb +51 -0
- data/lib/active_support/testing/constant_stubbing.rb +54 -0
- data/lib/active_support/testing/declarative.rb +28 -0
- data/lib/active_support/testing/deprecation.rb +82 -0
- data/lib/active_support/testing/error_reporter_assertions.rb +124 -0
- data/lib/active_support/testing/event_reporter_assertions.rb +227 -0
- data/lib/active_support/testing/file_fixtures.rb +38 -0
- data/lib/active_support/testing/isolation.rb +121 -0
- data/lib/active_support/testing/method_call_assertions.rb +69 -0
- data/lib/active_support/testing/notification_assertions.rb +92 -0
- data/lib/active_support/testing/parallelization/server.rb +98 -0
- data/lib/active_support/testing/parallelization/worker.rb +107 -0
- data/lib/active_support/testing/parallelization.rb +79 -0
- data/lib/active_support/testing/parallelize_executor.rb +81 -0
- data/lib/active_support/testing/setup_and_teardown.rb +57 -0
- data/lib/active_support/testing/stream.rb +41 -0
- data/lib/active_support/testing/tagged_logging.rb +27 -0
- data/lib/active_support/testing/tests_without_assertions.rb +19 -0
- data/lib/active_support/testing/time_helpers.rb +273 -0
- data/lib/active_support/time.rb +20 -0
- data/lib/active_support/time_with_zone.rb +613 -0
- data/lib/active_support/values/time_zone.rb +599 -158
- data/lib/active_support/version.rb +7 -6
- data/lib/active_support/xml_mini/jdom.rb +175 -0
- data/lib/active_support/xml_mini/libxml.rb +80 -0
- data/lib/active_support/xml_mini/libxmlsax.rb +83 -0
- data/lib/active_support/xml_mini/nokogiri.rb +83 -0
- data/lib/active_support/xml_mini/nokogirisax.rb +86 -0
- data/lib/active_support/xml_mini/rexml.rb +137 -0
- data/lib/active_support/xml_mini.rb +212 -0
- data/lib/active_support.rb +122 -10
- metadata +524 -93
- data/CHANGELOG +0 -283
- data/lib/active_support/binding_of_caller.rb +0 -84
- data/lib/active_support/breakpoint.rb +0 -523
- data/lib/active_support/class_attribute_accessors.rb +0 -57
- data/lib/active_support/class_inheritable_attributes.rb +0 -117
- data/lib/active_support/clean_logger.rb +0 -36
- data/lib/active_support/core_ext/blank.rb +0 -38
- data/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb +0 -14
- data/lib/active_support/core_ext/cgi.rb +0 -5
- data/lib/active_support/core_ext/exception.rb +0 -29
- data/lib/active_support/core_ext/integer/even_odd.rb +0 -24
- data/lib/active_support/core_ext/object_and_class.rb +0 -44
- data/lib/active_support/module_attribute_accessors.rb +0 -57
- data/lib/active_support/whiny_nil.rb +0 -38
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "openssl"
|
|
4
|
+
require "base64"
|
|
5
|
+
require "active_support/core_ext/module/attribute_accessors"
|
|
6
|
+
require "active_support/messages/codec"
|
|
7
|
+
require "active_support/messages/rotator"
|
|
8
|
+
require "active_support/message_verifier"
|
|
9
|
+
|
|
10
|
+
module ActiveSupport
|
|
11
|
+
# = Active Support Message Encryptor
|
|
12
|
+
#
|
|
13
|
+
# MessageEncryptor is a simple way to encrypt values which get stored
|
|
14
|
+
# somewhere you don't trust.
|
|
15
|
+
#
|
|
16
|
+
# The cipher text and initialization vector are base64 encoded and returned
|
|
17
|
+
# to you.
|
|
18
|
+
#
|
|
19
|
+
# This can be used in situations similar to the MessageVerifier, but
|
|
20
|
+
# where you don't want users to be able to determine the value of the payload.
|
|
21
|
+
#
|
|
22
|
+
# len = ActiveSupport::MessageEncryptor.key_len
|
|
23
|
+
# salt = SecureRandom.random_bytes(len)
|
|
24
|
+
# key = ActiveSupport::KeyGenerator.new('password').generate_key(salt, len) # => "\x89\xE0\x156\xAC..."
|
|
25
|
+
# crypt = ActiveSupport::MessageEncryptor.new(key) # => #<ActiveSupport::MessageEncryptor ...>
|
|
26
|
+
# encrypted_data = crypt.encrypt_and_sign('my secret data') # => "NlFBTTMwOUV5UlA1QlNEN2xkY2d6eThYWWh..."
|
|
27
|
+
# crypt.decrypt_and_verify(encrypted_data) # => "my secret data"
|
|
28
|
+
#
|
|
29
|
+
# The +decrypt_and_verify+ method will raise an
|
|
30
|
+
# +ActiveSupport::MessageEncryptor::InvalidMessage+ exception if the data
|
|
31
|
+
# provided cannot be decrypted or verified.
|
|
32
|
+
#
|
|
33
|
+
# crypt.decrypt_and_verify('not encrypted data') # => ActiveSupport::MessageEncryptor::InvalidMessage
|
|
34
|
+
#
|
|
35
|
+
# === Confining messages to a specific purpose
|
|
36
|
+
#
|
|
37
|
+
# By default any message can be used throughout your app. But they can also be
|
|
38
|
+
# confined to a specific +:purpose+.
|
|
39
|
+
#
|
|
40
|
+
# token = crypt.encrypt_and_sign("this is the chair", purpose: :login)
|
|
41
|
+
#
|
|
42
|
+
# Then that same purpose must be passed when verifying to get the data back out:
|
|
43
|
+
#
|
|
44
|
+
# crypt.decrypt_and_verify(token, purpose: :login) # => "this is the chair"
|
|
45
|
+
# crypt.decrypt_and_verify(token, purpose: :shipping) # => nil
|
|
46
|
+
# crypt.decrypt_and_verify(token) # => nil
|
|
47
|
+
#
|
|
48
|
+
# Likewise, if a message has no purpose it won't be returned when verifying with
|
|
49
|
+
# a specific purpose.
|
|
50
|
+
#
|
|
51
|
+
# token = crypt.encrypt_and_sign("the conversation is lively")
|
|
52
|
+
# crypt.decrypt_and_verify(token, purpose: :scare_tactics) # => nil
|
|
53
|
+
# crypt.decrypt_and_verify(token) # => "the conversation is lively"
|
|
54
|
+
#
|
|
55
|
+
# === Making messages expire
|
|
56
|
+
#
|
|
57
|
+
# By default messages last forever and verifying one year from now will still
|
|
58
|
+
# return the original value. But messages can be set to expire at a given
|
|
59
|
+
# time with +:expires_in+ or +:expires_at+.
|
|
60
|
+
#
|
|
61
|
+
# crypt.encrypt_and_sign(parcel, expires_in: 1.month)
|
|
62
|
+
# crypt.encrypt_and_sign(doowad, expires_at: Time.now.end_of_year)
|
|
63
|
+
#
|
|
64
|
+
# Then the messages can be verified and returned up to the expire time.
|
|
65
|
+
# Thereafter, verifying returns +nil+.
|
|
66
|
+
#
|
|
67
|
+
# === Rotating keys
|
|
68
|
+
#
|
|
69
|
+
# MessageEncryptor also supports rotating out old configurations by falling
|
|
70
|
+
# back to a stack of encryptors. Call +rotate+ to build and add an encryptor
|
|
71
|
+
# so +decrypt_and_verify+ will also try the fallback.
|
|
72
|
+
#
|
|
73
|
+
# By default any rotated encryptors use the values of the primary
|
|
74
|
+
# encryptor unless specified otherwise.
|
|
75
|
+
#
|
|
76
|
+
# You'd give your encryptor the new defaults:
|
|
77
|
+
#
|
|
78
|
+
# crypt = ActiveSupport::MessageEncryptor.new(@secret, cipher: "aes-256-gcm")
|
|
79
|
+
#
|
|
80
|
+
# Then gradually rotate the old values out by adding them as fallbacks. Any message
|
|
81
|
+
# generated with the old values will then work until the rotation is removed.
|
|
82
|
+
#
|
|
83
|
+
# crypt.rotate old_secret # Fallback to an old secret instead of @secret.
|
|
84
|
+
# crypt.rotate cipher: "aes-256-cbc" # Fallback to an old cipher instead of aes-256-gcm.
|
|
85
|
+
#
|
|
86
|
+
# Though if both the secret and the cipher was changed at the same time,
|
|
87
|
+
# the above should be combined into:
|
|
88
|
+
#
|
|
89
|
+
# crypt.rotate old_secret, cipher: "aes-256-cbc"
|
|
90
|
+
class MessageEncryptor < Messages::Codec
|
|
91
|
+
prepend Messages::Rotator
|
|
92
|
+
|
|
93
|
+
cattr_accessor :use_authenticated_message_encryption, instance_accessor: false, default: false
|
|
94
|
+
|
|
95
|
+
class << self
|
|
96
|
+
def default_cipher # :nodoc:
|
|
97
|
+
if use_authenticated_message_encryption
|
|
98
|
+
"aes-256-gcm"
|
|
99
|
+
else
|
|
100
|
+
"aes-256-cbc"
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
module NullSerializer # :nodoc:
|
|
106
|
+
def self.load(value)
|
|
107
|
+
value
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def self.dump(value)
|
|
111
|
+
value
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
class InvalidMessage < StandardError; end
|
|
116
|
+
OpenSSLCipherError = OpenSSL::Cipher::CipherError
|
|
117
|
+
|
|
118
|
+
AUTH_TAG_LENGTH = 16 # :nodoc:
|
|
119
|
+
SEPARATOR = "--" # :nodoc:
|
|
120
|
+
|
|
121
|
+
# Initialize a new MessageEncryptor. +secret+ must be at least as long as
|
|
122
|
+
# the cipher key size. For the default 'aes-256-gcm' cipher, this is 256
|
|
123
|
+
# bits. If you are using a user-entered secret, you can generate a suitable
|
|
124
|
+
# key by using ActiveSupport::KeyGenerator or a similar key
|
|
125
|
+
# derivation function.
|
|
126
|
+
#
|
|
127
|
+
# The first additional parameter is used as the signature key for
|
|
128
|
+
# MessageVerifier. This allows you to specify keys to encrypt and sign
|
|
129
|
+
# data. Ignored when using an AEAD cipher like 'aes-256-gcm'.
|
|
130
|
+
#
|
|
131
|
+
# ActiveSupport::MessageEncryptor.new('secret', 'signature_secret')
|
|
132
|
+
#
|
|
133
|
+
# ==== Options
|
|
134
|
+
#
|
|
135
|
+
# [+:cipher+]
|
|
136
|
+
# Cipher to use. Can be any cipher returned by +OpenSSL::Cipher.ciphers+.
|
|
137
|
+
# Default is 'aes-256-gcm'.
|
|
138
|
+
#
|
|
139
|
+
# [+:digest+]
|
|
140
|
+
# Digest used for signing. Ignored when using an AEAD cipher like
|
|
141
|
+
# 'aes-256-gcm'.
|
|
142
|
+
#
|
|
143
|
+
# [+:serializer+]
|
|
144
|
+
# The serializer used to serialize message data. You can specify any
|
|
145
|
+
# object that responds to +dump+ and +load+, or you can choose from
|
|
146
|
+
# several preconfigured serializers: +:marshal+, +:json_allow_marshal+,
|
|
147
|
+
# +:json+, +:message_pack_allow_marshal+, +:message_pack+.
|
|
148
|
+
#
|
|
149
|
+
# The preconfigured serializers include a fallback mechanism to support
|
|
150
|
+
# multiple deserialization formats. For example, the +:marshal+ serializer
|
|
151
|
+
# will serialize using +Marshal+, but can deserialize using +Marshal+,
|
|
152
|
+
# ActiveSupport::JSON, or ActiveSupport::MessagePack. This makes it easy
|
|
153
|
+
# to migrate between serializers.
|
|
154
|
+
#
|
|
155
|
+
# The +:marshal+, +:json_allow_marshal+, and +:message_pack_allow_marshal+
|
|
156
|
+
# serializers support deserializing using +Marshal+, but the others do
|
|
157
|
+
# not. Beware that +Marshal+ is a potential vector for deserialization
|
|
158
|
+
# attacks in cases where a message signing secret has been leaked. <em>If
|
|
159
|
+
# possible, choose a serializer that does not support +Marshal+.</em>
|
|
160
|
+
#
|
|
161
|
+
# The +:message_pack+ and +:message_pack_allow_marshal+ serializers use
|
|
162
|
+
# ActiveSupport::MessagePack, which can roundtrip some Ruby types that are
|
|
163
|
+
# not supported by JSON, and may provide improved performance. However,
|
|
164
|
+
# these require the +msgpack+ gem.
|
|
165
|
+
#
|
|
166
|
+
# When using \Rails, the default depends on +config.active_support.message_serializer+.
|
|
167
|
+
# Otherwise, the default is +:marshal+.
|
|
168
|
+
#
|
|
169
|
+
# [+:url_safe+]
|
|
170
|
+
# By default, MessageEncryptor generates RFC 4648 compliant strings
|
|
171
|
+
# which are not URL-safe. In other words, they can contain "+" and "/".
|
|
172
|
+
# If you want to generate URL-safe strings (in compliance with "Base 64
|
|
173
|
+
# Encoding with URL and Filename Safe Alphabet" in RFC 4648), you can
|
|
174
|
+
# pass +true+.
|
|
175
|
+
#
|
|
176
|
+
# [+:force_legacy_metadata_serializer+]
|
|
177
|
+
# Whether to use the legacy metadata serializer, which serializes the
|
|
178
|
+
# message first, then wraps it in an envelope which is also serialized. This
|
|
179
|
+
# was the default in \Rails 7.0 and below.
|
|
180
|
+
#
|
|
181
|
+
# If you don't pass a truthy value, the default is set using
|
|
182
|
+
# +config.active_support.use_message_serializer_for_metadata+.
|
|
183
|
+
def initialize(secret, sign_secret = nil, **options)
|
|
184
|
+
super(**options)
|
|
185
|
+
@secret = secret
|
|
186
|
+
@cipher = options[:cipher] || self.class.default_cipher
|
|
187
|
+
@aead_mode = new_cipher.authenticated?
|
|
188
|
+
@verifier = if !@aead_mode
|
|
189
|
+
MessageVerifier.new(sign_secret || secret, **options, serializer: NullSerializer)
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Encrypt and sign a message. We need to sign the message in order to avoid
|
|
194
|
+
# padding attacks. Reference: https://www.limited-entropy.com/padding-oracle-attacks/.
|
|
195
|
+
#
|
|
196
|
+
# ==== Options
|
|
197
|
+
#
|
|
198
|
+
# [+:expires_at+]
|
|
199
|
+
# The datetime at which the message expires. After this datetime,
|
|
200
|
+
# verification of the message will fail.
|
|
201
|
+
#
|
|
202
|
+
# message = encryptor.encrypt_and_sign("hello", expires_at: Time.now.tomorrow)
|
|
203
|
+
# encryptor.decrypt_and_verify(message) # => "hello"
|
|
204
|
+
# # 24 hours later...
|
|
205
|
+
# encryptor.decrypt_and_verify(message) # => nil
|
|
206
|
+
#
|
|
207
|
+
# [+:expires_in+]
|
|
208
|
+
# The duration for which the message is valid. After this duration has
|
|
209
|
+
# elapsed, verification of the message will fail.
|
|
210
|
+
#
|
|
211
|
+
# message = encryptor.encrypt_and_sign("hello", expires_in: 24.hours)
|
|
212
|
+
# encryptor.decrypt_and_verify(message) # => "hello"
|
|
213
|
+
# # 24 hours later...
|
|
214
|
+
# encryptor.decrypt_and_verify(message) # => nil
|
|
215
|
+
#
|
|
216
|
+
# [+:purpose+]
|
|
217
|
+
# The purpose of the message. If specified, the same purpose must be
|
|
218
|
+
# specified when verifying the message; otherwise, verification will fail.
|
|
219
|
+
# (See #decrypt_and_verify.)
|
|
220
|
+
def encrypt_and_sign(value, **options)
|
|
221
|
+
create_message(value, **options)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# Decrypt and verify a message. We need to verify the message in order to
|
|
225
|
+
# avoid padding attacks. Reference: https://www.limited-entropy.com/padding-oracle-attacks/.
|
|
226
|
+
#
|
|
227
|
+
# ==== Options
|
|
228
|
+
#
|
|
229
|
+
# [+:purpose+]
|
|
230
|
+
# The purpose that the message was generated with. If the purpose does not
|
|
231
|
+
# match, +decrypt_and_verify+ will return +nil+.
|
|
232
|
+
#
|
|
233
|
+
# message = encryptor.encrypt_and_sign("hello", purpose: "greeting")
|
|
234
|
+
# encryptor.decrypt_and_verify(message, purpose: "greeting") # => "hello"
|
|
235
|
+
# encryptor.decrypt_and_verify(message) # => nil
|
|
236
|
+
#
|
|
237
|
+
# message = encryptor.encrypt_and_sign("bye")
|
|
238
|
+
# encryptor.decrypt_and_verify(message) # => "bye"
|
|
239
|
+
# encryptor.decrypt_and_verify(message, purpose: "greeting") # => nil
|
|
240
|
+
#
|
|
241
|
+
def decrypt_and_verify(message, **options)
|
|
242
|
+
catch_and_raise :invalid_message_format, as: InvalidMessage do
|
|
243
|
+
catch_and_raise :invalid_message_serialization, as: InvalidMessage do
|
|
244
|
+
catch_and_ignore :invalid_message_content do
|
|
245
|
+
read_message(message, **options)
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
# Given a cipher, returns the key length of the cipher to help generate the key of desired size
|
|
252
|
+
def self.key_len(cipher = default_cipher)
|
|
253
|
+
OpenSSL::Cipher.new(cipher).key_len
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def create_message(value, **options) # :nodoc:
|
|
257
|
+
sign(encrypt(serialize_with_metadata(value, **options)))
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def read_message(message, **options) # :nodoc:
|
|
261
|
+
deserialize_with_metadata(decrypt(verify(message)), **options)
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def inspect # :nodoc:
|
|
265
|
+
"#<#{self.class.name}:#{'%#016x' % (object_id << 1)}>"
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
private
|
|
269
|
+
def sign(data)
|
|
270
|
+
@verifier ? @verifier.create_message(data) : data
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def verify(data)
|
|
274
|
+
@verifier ? @verifier.read_message(data) : data
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def encrypt(data)
|
|
278
|
+
cipher = new_cipher
|
|
279
|
+
cipher.encrypt
|
|
280
|
+
cipher.key = @secret
|
|
281
|
+
|
|
282
|
+
# Rely on OpenSSL for the initialization vector
|
|
283
|
+
iv = cipher.random_iv
|
|
284
|
+
cipher.auth_data = "" if aead_mode?
|
|
285
|
+
|
|
286
|
+
encrypted_data = cipher.update(data)
|
|
287
|
+
encrypted_data << cipher.final
|
|
288
|
+
|
|
289
|
+
parts = [encrypted_data, iv]
|
|
290
|
+
parts << cipher.auth_tag(AUTH_TAG_LENGTH) if aead_mode?
|
|
291
|
+
|
|
292
|
+
join_parts(parts)
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def decrypt(encrypted_message)
|
|
296
|
+
cipher = new_cipher
|
|
297
|
+
encrypted_data, iv, auth_tag = extract_parts(encrypted_message)
|
|
298
|
+
|
|
299
|
+
# Currently the OpenSSL bindings do not raise an error if auth_tag is
|
|
300
|
+
# truncated, which would allow an attacker to easily forge it. See
|
|
301
|
+
# https://github.com/ruby/openssl/issues/63
|
|
302
|
+
if aead_mode? && auth_tag.bytesize != AUTH_TAG_LENGTH
|
|
303
|
+
throw :invalid_message_format, "truncated auth_tag"
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
cipher.decrypt
|
|
307
|
+
cipher.key = @secret
|
|
308
|
+
cipher.iv = iv
|
|
309
|
+
if aead_mode?
|
|
310
|
+
cipher.auth_tag = auth_tag
|
|
311
|
+
cipher.auth_data = ""
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
decrypted_data = cipher.update(encrypted_data)
|
|
315
|
+
decrypted_data << cipher.final
|
|
316
|
+
rescue OpenSSLCipherError => error
|
|
317
|
+
throw :invalid_message_format, error
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
def length_after_encode(length_before_encode)
|
|
321
|
+
if @url_safe
|
|
322
|
+
(4 * length_before_encode / 3.0).ceil # length without padding
|
|
323
|
+
else
|
|
324
|
+
4 * (length_before_encode / 3.0).ceil # length with padding
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
def length_of_encoded_iv
|
|
329
|
+
@length_of_encoded_iv ||= length_after_encode(new_cipher.iv_len)
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
def length_of_encoded_auth_tag
|
|
333
|
+
@length_of_encoded_auth_tag ||= length_after_encode(AUTH_TAG_LENGTH)
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
def join_parts(parts)
|
|
337
|
+
parts.map! { |part| encode(part) }.join(SEPARATOR)
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
def extract_part(encrypted_message, rindex, length)
|
|
341
|
+
index = rindex - length
|
|
342
|
+
|
|
343
|
+
if encrypted_message[index - SEPARATOR.length, SEPARATOR.length] == SEPARATOR
|
|
344
|
+
encrypted_message[index, length]
|
|
345
|
+
else
|
|
346
|
+
throw :invalid_message_format, "missing separator"
|
|
347
|
+
end
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
def extract_parts(encrypted_message)
|
|
351
|
+
parts = []
|
|
352
|
+
rindex = encrypted_message.length
|
|
353
|
+
|
|
354
|
+
if aead_mode?
|
|
355
|
+
parts << extract_part(encrypted_message, rindex, length_of_encoded_auth_tag)
|
|
356
|
+
rindex -= SEPARATOR.length + length_of_encoded_auth_tag
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
parts << extract_part(encrypted_message, rindex, length_of_encoded_iv)
|
|
360
|
+
rindex -= SEPARATOR.length + length_of_encoded_iv
|
|
361
|
+
|
|
362
|
+
parts << encrypted_message[0, rindex]
|
|
363
|
+
|
|
364
|
+
parts.reverse!.map! { |part| decode(part) }
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
def new_cipher
|
|
368
|
+
OpenSSL::Cipher.new(@cipher)
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
attr_reader :aead_mode
|
|
372
|
+
alias :aead_mode? :aead_mode
|
|
373
|
+
end
|
|
374
|
+
end
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/messages/rotation_coordinator"
|
|
4
|
+
|
|
5
|
+
module ActiveSupport
|
|
6
|
+
class MessageEncryptors < Messages::RotationCoordinator
|
|
7
|
+
##
|
|
8
|
+
# :attr_accessor: transitional
|
|
9
|
+
#
|
|
10
|
+
# If true, the first two rotation option sets are swapped when building
|
|
11
|
+
# message encryptors. For example, with the following configuration, message
|
|
12
|
+
# encryptors will encrypt messages using <tt>serializer: Marshal, url_safe: true</tt>,
|
|
13
|
+
# and will able to decrypt messages that were encrypted using any of the
|
|
14
|
+
# three option sets:
|
|
15
|
+
#
|
|
16
|
+
# encryptors = ActiveSupport::MessageEncryptors.new { ... }
|
|
17
|
+
# encryptors.rotate(serializer: JSON, url_safe: true)
|
|
18
|
+
# encryptors.rotate(serializer: Marshal, url_safe: true)
|
|
19
|
+
# encryptors.rotate(serializer: Marshal, url_safe: false)
|
|
20
|
+
# encryptors.transitional = true
|
|
21
|
+
#
|
|
22
|
+
# This can be useful when performing a rolling deploy of an application,
|
|
23
|
+
# wherein servers that have not yet been updated must still be able to
|
|
24
|
+
# decrypt messages from updated servers. In such a scenario, first perform a
|
|
25
|
+
# rolling deploy with the new rotation (e.g. <tt>serializer: JSON, url_safe: true</tt>)
|
|
26
|
+
# as the first rotation and <tt>transitional = true</tt>. Then, after all
|
|
27
|
+
# servers have been updated, perform a second rolling deploy with
|
|
28
|
+
# <tt>transitional = false</tt>.
|
|
29
|
+
#
|
|
30
|
+
#--
|
|
31
|
+
# Implemented by ActiveSupport::Messages::RotationCoordinator#transitional
|
|
32
|
+
|
|
33
|
+
##
|
|
34
|
+
# :singleton-method: new
|
|
35
|
+
# :call-seq: new(&secret_generator)
|
|
36
|
+
#
|
|
37
|
+
# Initializes a new instance. +secret_generator+ must accept a salt and a
|
|
38
|
+
# +secret_length+ kwarg, and return a suitable secret (string) or secrets
|
|
39
|
+
# (array of strings). +secret_generator+ may also accept other arbitrary
|
|
40
|
+
# kwargs. If #rotate is called with any options matching those kwargs, those
|
|
41
|
+
# options will be passed to +secret_generator+ instead of to the message
|
|
42
|
+
# encryptor.
|
|
43
|
+
#
|
|
44
|
+
# encryptors = ActiveSupport::MessageEncryptors.new do |salt, secret_length:, base:|
|
|
45
|
+
# MySecretGenerator.new(base).generate(salt, secret_length)
|
|
46
|
+
# end
|
|
47
|
+
#
|
|
48
|
+
# encryptors.rotate(base: "...")
|
|
49
|
+
#
|
|
50
|
+
#--
|
|
51
|
+
# Implemented by ActiveSupport::Messages::RotationCoordinator#initialize
|
|
52
|
+
|
|
53
|
+
##
|
|
54
|
+
# :method: []
|
|
55
|
+
# :call-seq: [](salt)
|
|
56
|
+
#
|
|
57
|
+
# Returns a MessageEncryptor configured with a secret derived from the
|
|
58
|
+
# given +salt+, and options from #rotate. MessageEncryptor instances will
|
|
59
|
+
# be memoized, so the same +salt+ will return the same instance.
|
|
60
|
+
#
|
|
61
|
+
#--
|
|
62
|
+
# Implemented by ActiveSupport::Messages::RotationCoordinator#[]
|
|
63
|
+
|
|
64
|
+
##
|
|
65
|
+
# :method: []=
|
|
66
|
+
# :call-seq: []=(salt, encryptor)
|
|
67
|
+
#
|
|
68
|
+
# Overrides a MessageEncryptor instance associated with a given +salt+.
|
|
69
|
+
#
|
|
70
|
+
#--
|
|
71
|
+
# Implemented by ActiveSupport::Messages::RotationCoordinator#[]=
|
|
72
|
+
|
|
73
|
+
##
|
|
74
|
+
# :method: rotate
|
|
75
|
+
# :call-seq:
|
|
76
|
+
# rotate(**options)
|
|
77
|
+
# rotate(&block)
|
|
78
|
+
#
|
|
79
|
+
# Adds +options+ to the list of option sets. Messages will be encrypted
|
|
80
|
+
# using the first set in the list. When decrypting, however, each set will
|
|
81
|
+
# be tried, in order, until one succeeds.
|
|
82
|
+
#
|
|
83
|
+
# Notably, the +:secret_generator+ option can specify a different secret
|
|
84
|
+
# generator than the one initially specified. The secret generator must
|
|
85
|
+
# respond to +call+, accept a salt and a +secret_length+ kwarg, and return
|
|
86
|
+
# a suitable secret (string) or secrets (array of strings). The secret
|
|
87
|
+
# generator may also accept other arbitrary kwargs.
|
|
88
|
+
#
|
|
89
|
+
# If any options match the kwargs of the operative secret generator, those
|
|
90
|
+
# options will be passed to the secret generator instead of to the message
|
|
91
|
+
# encryptor.
|
|
92
|
+
#
|
|
93
|
+
# For fine-grained per-salt rotations, a block form is supported. The block
|
|
94
|
+
# will receive the salt, and should return an appropriate options Hash. The
|
|
95
|
+
# block may also return +nil+ to indicate that the rotation does not apply
|
|
96
|
+
# to the given salt. For example:
|
|
97
|
+
#
|
|
98
|
+
# encryptors = ActiveSupport::MessageEncryptors.new { ... }
|
|
99
|
+
#
|
|
100
|
+
# encryptors.rotate do |salt|
|
|
101
|
+
# case salt
|
|
102
|
+
# when :foo
|
|
103
|
+
# { serializer: JSON, url_safe: true }
|
|
104
|
+
# when :bar
|
|
105
|
+
# { serializer: Marshal, url_safe: true }
|
|
106
|
+
# end
|
|
107
|
+
# end
|
|
108
|
+
#
|
|
109
|
+
# encryptors.rotate(serializer: Marshal, url_safe: false)
|
|
110
|
+
#
|
|
111
|
+
# # Uses `serializer: JSON, url_safe: true`.
|
|
112
|
+
# # Falls back to `serializer: Marshal, url_safe: false`.
|
|
113
|
+
# encryptors[:foo]
|
|
114
|
+
#
|
|
115
|
+
# # Uses `serializer: Marshal, url_safe: true`.
|
|
116
|
+
# # Falls back to `serializer: Marshal, url_safe: false`.
|
|
117
|
+
# encryptors[:bar]
|
|
118
|
+
#
|
|
119
|
+
# # Uses `serializer: Marshal, url_safe: false`.
|
|
120
|
+
# encryptors[:baz]
|
|
121
|
+
#
|
|
122
|
+
#--
|
|
123
|
+
# Implemented by ActiveSupport::Messages::RotationCoordinator#rotate
|
|
124
|
+
|
|
125
|
+
##
|
|
126
|
+
# :method: prepend
|
|
127
|
+
# :call-seq:
|
|
128
|
+
# prepend(**options)
|
|
129
|
+
# prepend(&block)
|
|
130
|
+
#
|
|
131
|
+
# Just like #rotate, but prepends the given options or block to the list of
|
|
132
|
+
# option sets.
|
|
133
|
+
#
|
|
134
|
+
# This can be useful when you have an already-configured +MessageEncryptors+
|
|
135
|
+
# instance, but you want to override the way messages are encrypted.
|
|
136
|
+
#
|
|
137
|
+
# module ThirdParty
|
|
138
|
+
# ENCRYPTORS = ActiveSupport::MessageEncryptors.new { ... }.
|
|
139
|
+
# rotate(serializer: Marshal, url_safe: true).
|
|
140
|
+
# rotate(serializer: Marshal, url_safe: false)
|
|
141
|
+
# end
|
|
142
|
+
#
|
|
143
|
+
# ThirdParty.ENCRYPTORS.prepend(serializer: JSON, url_safe: true)
|
|
144
|
+
#
|
|
145
|
+
# # Uses `serializer: JSON, url_safe: true`.
|
|
146
|
+
# # Falls back to `serializer: Marshal, url_safe: true` or
|
|
147
|
+
# # `serializer: Marshal, url_safe: false`.
|
|
148
|
+
# ThirdParty.ENCRYPTORS[:foo]
|
|
149
|
+
#
|
|
150
|
+
#--
|
|
151
|
+
# Implemented by ActiveSupport::Messages::RotationCoordinator#prepend
|
|
152
|
+
|
|
153
|
+
##
|
|
154
|
+
# :method: rotate_defaults
|
|
155
|
+
# :call-seq: rotate_defaults
|
|
156
|
+
#
|
|
157
|
+
# Invokes #rotate with the default options.
|
|
158
|
+
#
|
|
159
|
+
#--
|
|
160
|
+
# Implemented by ActiveSupport::Messages::RotationCoordinator#rotate_defaults
|
|
161
|
+
|
|
162
|
+
##
|
|
163
|
+
# :method: clear_rotations
|
|
164
|
+
# :call-seq: clear_rotations
|
|
165
|
+
#
|
|
166
|
+
# Clears the list of option sets.
|
|
167
|
+
#
|
|
168
|
+
#--
|
|
169
|
+
# Implemented by ActiveSupport::Messages::RotationCoordinator#clear_rotations
|
|
170
|
+
|
|
171
|
+
##
|
|
172
|
+
# :method: on_rotation
|
|
173
|
+
# :call-seq: on_rotation(&callback)
|
|
174
|
+
#
|
|
175
|
+
# Sets a callback to invoke when a message is decrypted using an option set
|
|
176
|
+
# other than the first.
|
|
177
|
+
#
|
|
178
|
+
# For example, this callback could log each time it is called, and thus
|
|
179
|
+
# indicate whether old option sets are still in use or can be removed from
|
|
180
|
+
# rotation.
|
|
181
|
+
#
|
|
182
|
+
#--
|
|
183
|
+
# Implemented by ActiveSupport::Messages::RotationCoordinator#on_rotation
|
|
184
|
+
|
|
185
|
+
##
|
|
186
|
+
private
|
|
187
|
+
def build(salt, secret_generator:, secret_generator_options:, **options)
|
|
188
|
+
secret_length = MessageEncryptor.key_len(*options[:cipher])
|
|
189
|
+
secret = secret_generator.call(salt, secret_length: secret_length, **secret_generator_options)
|
|
190
|
+
MessageEncryptor.new(*Array(secret), **options)
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "serializer"
|
|
4
|
+
|
|
5
|
+
module ActiveSupport
|
|
6
|
+
module MessagePack
|
|
7
|
+
module CacheSerializer
|
|
8
|
+
include Serializer
|
|
9
|
+
extend self
|
|
10
|
+
|
|
11
|
+
def load(dumped)
|
|
12
|
+
super
|
|
13
|
+
rescue ActiveSupport::MessagePack::MissingClassError
|
|
14
|
+
# Treat missing class as cache miss => return nil
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
def install_unregistered_type_handler
|
|
19
|
+
Extensions.install_unregistered_type_fallback(message_pack_factory)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|