activesupport 6.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activesupport might be problematic. Click here for more details.

Files changed (250) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +572 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +40 -0
  5. data/lib/active_support.rb +96 -0
  6. data/lib/active_support/actionable_error.rb +48 -0
  7. data/lib/active_support/all.rb +5 -0
  8. data/lib/active_support/array_inquirer.rb +48 -0
  9. data/lib/active_support/backtrace_cleaner.rb +132 -0
  10. data/lib/active_support/benchmarkable.rb +51 -0
  11. data/lib/active_support/builder.rb +8 -0
  12. data/lib/active_support/cache.rb +830 -0
  13. data/lib/active_support/cache/file_store.rb +196 -0
  14. data/lib/active_support/cache/mem_cache_store.rb +212 -0
  15. data/lib/active_support/cache/memory_store.rb +174 -0
  16. data/lib/active_support/cache/null_store.rb +48 -0
  17. data/lib/active_support/cache/redis_cache_store.rb +488 -0
  18. data/lib/active_support/cache/strategy/local_cache.rb +194 -0
  19. data/lib/active_support/cache/strategy/local_cache_middleware.rb +45 -0
  20. data/lib/active_support/callbacks.rb +856 -0
  21. data/lib/active_support/concern.rb +171 -0
  22. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +17 -0
  23. data/lib/active_support/concurrency/share_lock.rb +227 -0
  24. data/lib/active_support/configurable.rb +146 -0
  25. data/lib/active_support/core_ext.rb +5 -0
  26. data/lib/active_support/core_ext/array.rb +9 -0
  27. data/lib/active_support/core_ext/array/access.rb +104 -0
  28. data/lib/active_support/core_ext/array/conversions.rb +213 -0
  29. data/lib/active_support/core_ext/array/extract.rb +21 -0
  30. data/lib/active_support/core_ext/array/extract_options.rb +31 -0
  31. data/lib/active_support/core_ext/array/grouping.rb +109 -0
  32. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  33. data/lib/active_support/core_ext/array/prepend_and_append.rb +5 -0
  34. data/lib/active_support/core_ext/array/wrap.rb +48 -0
  35. data/lib/active_support/core_ext/benchmark.rb +16 -0
  36. data/lib/active_support/core_ext/big_decimal.rb +3 -0
  37. data/lib/active_support/core_ext/big_decimal/conversions.rb +14 -0
  38. data/lib/active_support/core_ext/class.rb +4 -0
  39. data/lib/active_support/core_ext/class/attribute.rb +141 -0
  40. data/lib/active_support/core_ext/class/attribute_accessors.rb +6 -0
  41. data/lib/active_support/core_ext/class/subclasses.rb +54 -0
  42. data/lib/active_support/core_ext/date.rb +7 -0
  43. data/lib/active_support/core_ext/date/acts_like.rb +10 -0
  44. data/lib/active_support/core_ext/date/blank.rb +14 -0
  45. data/lib/active_support/core_ext/date/calculations.rb +146 -0
  46. data/lib/active_support/core_ext/date/conversions.rb +96 -0
  47. data/lib/active_support/core_ext/date/zones.rb +8 -0
  48. data/lib/active_support/core_ext/date_and_time/calculations.rb +351 -0
  49. data/lib/active_support/core_ext/date_and_time/compatibility.rb +16 -0
  50. data/lib/active_support/core_ext/date_and_time/zones.rb +41 -0
  51. data/lib/active_support/core_ext/date_time.rb +7 -0
  52. data/lib/active_support/core_ext/date_time/acts_like.rb +16 -0
  53. data/lib/active_support/core_ext/date_time/blank.rb +14 -0
  54. data/lib/active_support/core_ext/date_time/calculations.rb +211 -0
  55. data/lib/active_support/core_ext/date_time/compatibility.rb +18 -0
  56. data/lib/active_support/core_ext/date_time/conversions.rb +107 -0
  57. data/lib/active_support/core_ext/digest.rb +3 -0
  58. data/lib/active_support/core_ext/digest/uuid.rb +53 -0
  59. data/lib/active_support/core_ext/enumerable.rb +188 -0
  60. data/lib/active_support/core_ext/file.rb +3 -0
  61. data/lib/active_support/core_ext/file/atomic.rb +70 -0
  62. data/lib/active_support/core_ext/hash.rb +10 -0
  63. data/lib/active_support/core_ext/hash/compact.rb +5 -0
  64. data/lib/active_support/core_ext/hash/conversions.rb +263 -0
  65. data/lib/active_support/core_ext/hash/deep_merge.rb +34 -0
  66. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  67. data/lib/active_support/core_ext/hash/except.rb +24 -0
  68. data/lib/active_support/core_ext/hash/indifferent_access.rb +24 -0
  69. data/lib/active_support/core_ext/hash/keys.rb +143 -0
  70. data/lib/active_support/core_ext/hash/reverse_merge.rb +25 -0
  71. data/lib/active_support/core_ext/hash/slice.rb +26 -0
  72. data/lib/active_support/core_ext/hash/transform_values.rb +5 -0
  73. data/lib/active_support/core_ext/integer.rb +5 -0
  74. data/lib/active_support/core_ext/integer/inflections.rb +31 -0
  75. data/lib/active_support/core_ext/integer/multiple.rb +12 -0
  76. data/lib/active_support/core_ext/integer/time.rb +22 -0
  77. data/lib/active_support/core_ext/kernel.rb +5 -0
  78. data/lib/active_support/core_ext/kernel/concern.rb +14 -0
  79. data/lib/active_support/core_ext/kernel/reporting.rb +45 -0
  80. data/lib/active_support/core_ext/kernel/singleton_class.rb +8 -0
  81. data/lib/active_support/core_ext/load_error.rb +9 -0
  82. data/lib/active_support/core_ext/marshal.rb +24 -0
  83. data/lib/active_support/core_ext/module.rb +13 -0
  84. data/lib/active_support/core_ext/module/aliasing.rb +31 -0
  85. data/lib/active_support/core_ext/module/anonymous.rb +30 -0
  86. data/lib/active_support/core_ext/module/attr_internal.rb +38 -0
  87. data/lib/active_support/core_ext/module/attribute_accessors.rb +212 -0
  88. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +144 -0
  89. data/lib/active_support/core_ext/module/concerning.rb +134 -0
  90. data/lib/active_support/core_ext/module/delegation.rb +313 -0
  91. data/lib/active_support/core_ext/module/deprecation.rb +25 -0
  92. data/lib/active_support/core_ext/module/introspection.rb +86 -0
  93. data/lib/active_support/core_ext/module/reachable.rb +6 -0
  94. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  95. data/lib/active_support/core_ext/module/remove_method.rb +17 -0
  96. data/lib/active_support/core_ext/name_error.rb +38 -0
  97. data/lib/active_support/core_ext/numeric.rb +5 -0
  98. data/lib/active_support/core_ext/numeric/bytes.rb +66 -0
  99. data/lib/active_support/core_ext/numeric/conversions.rb +136 -0
  100. data/lib/active_support/core_ext/numeric/inquiry.rb +5 -0
  101. data/lib/active_support/core_ext/numeric/time.rb +66 -0
  102. data/lib/active_support/core_ext/object.rb +16 -0
  103. data/lib/active_support/core_ext/object/acts_like.rb +21 -0
  104. data/lib/active_support/core_ext/object/blank.rb +155 -0
  105. data/lib/active_support/core_ext/object/conversions.rb +6 -0
  106. data/lib/active_support/core_ext/object/deep_dup.rb +55 -0
  107. data/lib/active_support/core_ext/object/duplicable.rb +49 -0
  108. data/lib/active_support/core_ext/object/inclusion.rb +29 -0
  109. data/lib/active_support/core_ext/object/instance_variables.rb +30 -0
  110. data/lib/active_support/core_ext/object/json.rb +228 -0
  111. data/lib/active_support/core_ext/object/to_param.rb +3 -0
  112. data/lib/active_support/core_ext/object/to_query.rb +89 -0
  113. data/lib/active_support/core_ext/object/try.rb +156 -0
  114. data/lib/active_support/core_ext/object/with_options.rb +82 -0
  115. data/lib/active_support/core_ext/range.rb +7 -0
  116. data/lib/active_support/core_ext/range/compare_range.rb +70 -0
  117. data/lib/active_support/core_ext/range/conversions.rb +41 -0
  118. data/lib/active_support/core_ext/range/each.rb +25 -0
  119. data/lib/active_support/core_ext/range/include_range.rb +9 -0
  120. data/lib/active_support/core_ext/range/include_time_with_zone.rb +23 -0
  121. data/lib/active_support/core_ext/range/overlaps.rb +10 -0
  122. data/lib/active_support/core_ext/regexp.rb +7 -0
  123. data/lib/active_support/core_ext/securerandom.rb +45 -0
  124. data/lib/active_support/core_ext/string.rb +15 -0
  125. data/lib/active_support/core_ext/string/access.rb +114 -0
  126. data/lib/active_support/core_ext/string/behavior.rb +8 -0
  127. data/lib/active_support/core_ext/string/conversions.rb +59 -0
  128. data/lib/active_support/core_ext/string/exclude.rb +13 -0
  129. data/lib/active_support/core_ext/string/filters.rb +145 -0
  130. data/lib/active_support/core_ext/string/indent.rb +45 -0
  131. data/lib/active_support/core_ext/string/inflections.rb +259 -0
  132. data/lib/active_support/core_ext/string/inquiry.rb +15 -0
  133. data/lib/active_support/core_ext/string/multibyte.rb +58 -0
  134. data/lib/active_support/core_ext/string/output_safety.rb +314 -0
  135. data/lib/active_support/core_ext/string/starts_ends_with.rb +6 -0
  136. data/lib/active_support/core_ext/string/strip.rb +27 -0
  137. data/lib/active_support/core_ext/string/zones.rb +16 -0
  138. data/lib/active_support/core_ext/time.rb +7 -0
  139. data/lib/active_support/core_ext/time/acts_like.rb +10 -0
  140. data/lib/active_support/core_ext/time/calculations.rb +344 -0
  141. data/lib/active_support/core_ext/time/compatibility.rb +16 -0
  142. data/lib/active_support/core_ext/time/conversions.rb +72 -0
  143. data/lib/active_support/core_ext/time/zones.rb +113 -0
  144. data/lib/active_support/core_ext/uri.rb +25 -0
  145. data/lib/active_support/current_attributes.rb +203 -0
  146. data/lib/active_support/dependencies.rb +806 -0
  147. data/lib/active_support/dependencies/autoload.rb +79 -0
  148. data/lib/active_support/dependencies/interlock.rb +57 -0
  149. data/lib/active_support/dependencies/zeitwerk_integration.rb +110 -0
  150. data/lib/active_support/deprecation.rb +46 -0
  151. data/lib/active_support/deprecation/behaviors.rb +109 -0
  152. data/lib/active_support/deprecation/constant_accessor.rb +52 -0
  153. data/lib/active_support/deprecation/instance_delegator.rb +39 -0
  154. data/lib/active_support/deprecation/method_wrappers.rb +78 -0
  155. data/lib/active_support/deprecation/proxy_wrappers.rb +173 -0
  156. data/lib/active_support/deprecation/reporting.rb +114 -0
  157. data/lib/active_support/descendants_tracker.rb +109 -0
  158. data/lib/active_support/digest.rb +20 -0
  159. data/lib/active_support/duration.rb +433 -0
  160. data/lib/active_support/duration/iso8601_parser.rb +124 -0
  161. data/lib/active_support/duration/iso8601_serializer.rb +54 -0
  162. data/lib/active_support/encrypted_configuration.rb +45 -0
  163. data/lib/active_support/encrypted_file.rb +100 -0
  164. data/lib/active_support/evented_file_update_checker.rb +235 -0
  165. data/lib/active_support/execution_wrapper.rb +129 -0
  166. data/lib/active_support/executor.rb +8 -0
  167. data/lib/active_support/file_update_checker.rb +163 -0
  168. data/lib/active_support/gem_version.rb +17 -0
  169. data/lib/active_support/gzip.rb +38 -0
  170. data/lib/active_support/hash_with_indifferent_access.rb +399 -0
  171. data/lib/active_support/i18n.rb +16 -0
  172. data/lib/active_support/i18n_railtie.rb +126 -0
  173. data/lib/active_support/inflections.rb +72 -0
  174. data/lib/active_support/inflector.rb +9 -0
  175. data/lib/active_support/inflector/inflections.rb +257 -0
  176. data/lib/active_support/inflector/methods.rb +398 -0
  177. data/lib/active_support/inflector/transliterate.rb +147 -0
  178. data/lib/active_support/json.rb +4 -0
  179. data/lib/active_support/json/decoding.rb +76 -0
  180. data/lib/active_support/json/encoding.rb +134 -0
  181. data/lib/active_support/key_generator.rb +41 -0
  182. data/lib/active_support/lazy_load_hooks.rb +82 -0
  183. data/lib/active_support/locale/en.rb +31 -0
  184. data/lib/active_support/locale/en.yml +135 -0
  185. data/lib/active_support/log_subscriber.rb +135 -0
  186. data/lib/active_support/log_subscriber/test_helper.rb +106 -0
  187. data/lib/active_support/logger.rb +93 -0
  188. data/lib/active_support/logger_silence.rb +45 -0
  189. data/lib/active_support/logger_thread_safe_level.rb +56 -0
  190. data/lib/active_support/message_encryptor.rb +227 -0
  191. data/lib/active_support/message_verifier.rb +205 -0
  192. data/lib/active_support/messages/metadata.rb +71 -0
  193. data/lib/active_support/messages/rotation_configuration.rb +22 -0
  194. data/lib/active_support/messages/rotator.rb +56 -0
  195. data/lib/active_support/multibyte.rb +23 -0
  196. data/lib/active_support/multibyte/chars.rb +216 -0
  197. data/lib/active_support/multibyte/unicode.rb +157 -0
  198. data/lib/active_support/notifications.rb +253 -0
  199. data/lib/active_support/notifications/fanout.rb +244 -0
  200. data/lib/active_support/notifications/instrumenter.rb +164 -0
  201. data/lib/active_support/number_helper.rb +378 -0
  202. data/lib/active_support/number_helper/number_converter.rb +184 -0
  203. data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
  204. data/lib/active_support/number_helper/number_to_delimited_converter.rb +31 -0
  205. data/lib/active_support/number_helper/number_to_human_converter.rb +70 -0
  206. data/lib/active_support/number_helper/number_to_human_size_converter.rb +61 -0
  207. data/lib/active_support/number_helper/number_to_percentage_converter.rb +16 -0
  208. data/lib/active_support/number_helper/number_to_phone_converter.rb +60 -0
  209. data/lib/active_support/number_helper/number_to_rounded_converter.rb +56 -0
  210. data/lib/active_support/number_helper/rounding_helper.rb +66 -0
  211. data/lib/active_support/option_merger.rb +27 -0
  212. data/lib/active_support/ordered_hash.rb +50 -0
  213. data/lib/active_support/ordered_options.rb +85 -0
  214. data/lib/active_support/parameter_filter.rb +129 -0
  215. data/lib/active_support/per_thread_registry.rb +60 -0
  216. data/lib/active_support/proxy_object.rb +15 -0
  217. data/lib/active_support/rails.rb +29 -0
  218. data/lib/active_support/railtie.rb +80 -0
  219. data/lib/active_support/reloader.rb +130 -0
  220. data/lib/active_support/rescuable.rb +174 -0
  221. data/lib/active_support/security_utils.rb +31 -0
  222. data/lib/active_support/string_inquirer.rb +34 -0
  223. data/lib/active_support/subscriber.rb +169 -0
  224. data/lib/active_support/tagged_logging.rb +88 -0
  225. data/lib/active_support/test_case.rb +163 -0
  226. data/lib/active_support/testing/assertions.rb +228 -0
  227. data/lib/active_support/testing/autorun.rb +7 -0
  228. data/lib/active_support/testing/constant_lookup.rb +51 -0
  229. data/lib/active_support/testing/declarative.rb +28 -0
  230. data/lib/active_support/testing/deprecation.rb +38 -0
  231. data/lib/active_support/testing/file_fixtures.rb +38 -0
  232. data/lib/active_support/testing/isolation.rb +110 -0
  233. data/lib/active_support/testing/method_call_assertions.rb +70 -0
  234. data/lib/active_support/testing/parallelization.rb +128 -0
  235. data/lib/active_support/testing/setup_and_teardown.rb +55 -0
  236. data/lib/active_support/testing/stream.rb +44 -0
  237. data/lib/active_support/testing/tagged_logging.rb +27 -0
  238. data/lib/active_support/testing/time_helpers.rb +200 -0
  239. data/lib/active_support/time.rb +20 -0
  240. data/lib/active_support/time_with_zone.rb +561 -0
  241. data/lib/active_support/values/time_zone.rb +570 -0
  242. data/lib/active_support/version.rb +10 -0
  243. data/lib/active_support/xml_mini.rb +202 -0
  244. data/lib/active_support/xml_mini/jdom.rb +183 -0
  245. data/lib/active_support/xml_mini/libxml.rb +80 -0
  246. data/lib/active_support/xml_mini/libxmlsax.rb +83 -0
  247. data/lib/active_support/xml_mini/nokogiri.rb +83 -0
  248. data/lib/active_support/xml_mini/nokogirisax.rb +86 -0
  249. data/lib/active_support/xml_mini/rexml.rb +130 -0
  250. metadata +385 -0
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/module/attribute_accessors"
4
+ require "active_support/core_ext/class/attribute"
5
+ require "active_support/subscriber"
6
+
7
+ module ActiveSupport
8
+ # <tt>ActiveSupport::LogSubscriber</tt> is an object set to consume
9
+ # <tt>ActiveSupport::Notifications</tt> with the sole purpose of logging them.
10
+ # The log subscriber dispatches notifications to a registered object based
11
+ # on its given namespace.
12
+ #
13
+ # An example would be Active Record log subscriber responsible for logging
14
+ # queries:
15
+ #
16
+ # module ActiveRecord
17
+ # class LogSubscriber < ActiveSupport::LogSubscriber
18
+ # def sql(event)
19
+ # info "#{event.payload[:name]} (#{event.duration}) #{event.payload[:sql]}"
20
+ # end
21
+ # end
22
+ # end
23
+ #
24
+ # And it's finally registered as:
25
+ #
26
+ # ActiveRecord::LogSubscriber.attach_to :active_record
27
+ #
28
+ # Since we need to know all instance methods before attaching the log
29
+ # subscriber, the line above should be called after your
30
+ # <tt>ActiveRecord::LogSubscriber</tt> definition.
31
+ #
32
+ # After configured, whenever a <tt>"sql.active_record"</tt> notification is published,
33
+ # it will properly dispatch the event
34
+ # (<tt>ActiveSupport::Notifications::Event</tt>) to the sql method.
35
+ #
36
+ # Being an <tt>ActiveSupport::Notifications</tt> consumer,
37
+ # <tt>ActiveSupport::LogSubscriber</tt> exposes a simple interface to check if
38
+ # instrumented code raises an exception. It is common to log a different
39
+ # message in case of an error, and this can be achieved by extending
40
+ # the previous example:
41
+ #
42
+ # module ActiveRecord
43
+ # class LogSubscriber < ActiveSupport::LogSubscriber
44
+ # def sql(event)
45
+ # exception = event.payload[:exception]
46
+ #
47
+ # if exception
48
+ # exception_object = event.payload[:exception_object]
49
+ #
50
+ # error "[ERROR] #{event.payload[:name]}: #{exception.join(', ')} " \
51
+ # "(#{exception_object.backtrace.first})"
52
+ # else
53
+ # # standard logger code
54
+ # end
55
+ # end
56
+ # end
57
+ # end
58
+ #
59
+ # Log subscriber also has some helpers to deal with logging and automatically
60
+ # flushes all logs when the request finishes
61
+ # (via <tt>action_dispatch.callback</tt> notification) in a Rails environment.
62
+ class LogSubscriber < Subscriber
63
+ # Embed in a String to clear all previous ANSI sequences.
64
+ CLEAR = "\e[0m"
65
+ BOLD = "\e[1m"
66
+
67
+ # Colors
68
+ BLACK = "\e[30m"
69
+ RED = "\e[31m"
70
+ GREEN = "\e[32m"
71
+ YELLOW = "\e[33m"
72
+ BLUE = "\e[34m"
73
+ MAGENTA = "\e[35m"
74
+ CYAN = "\e[36m"
75
+ WHITE = "\e[37m"
76
+
77
+ mattr_accessor :colorize_logging, default: true
78
+
79
+ class << self
80
+ def logger
81
+ @logger ||= if defined?(Rails) && Rails.respond_to?(:logger)
82
+ Rails.logger
83
+ end
84
+ end
85
+
86
+ attr_writer :logger
87
+
88
+ def log_subscribers
89
+ subscribers
90
+ end
91
+
92
+ # Flush all log_subscribers' logger.
93
+ def flush_all!
94
+ logger.flush if logger.respond_to?(:flush)
95
+ end
96
+ end
97
+
98
+ def logger
99
+ LogSubscriber.logger
100
+ end
101
+
102
+ def start(name, id, payload)
103
+ super if logger
104
+ end
105
+
106
+ def finish(name, id, payload)
107
+ super if logger
108
+ rescue => e
109
+ if logger
110
+ logger.error "Could not log #{name.inspect} event. #{e.class}: #{e.message} #{e.backtrace}"
111
+ end
112
+ end
113
+
114
+ private
115
+
116
+ %w(info debug warn error fatal unknown).each do |level|
117
+ class_eval <<-METHOD, __FILE__, __LINE__ + 1
118
+ def #{level}(progname = nil, &block)
119
+ logger.#{level}(progname, &block) if logger
120
+ end
121
+ METHOD
122
+ end
123
+
124
+ # Set color by using a symbol or one of the defined constants. If a third
125
+ # option is set to +true+, it also adds bold to the string. This is based
126
+ # on the Highline implementation and will automatically append CLEAR to the
127
+ # end of the returned String.
128
+ def color(text, color, bold = false) # :doc:
129
+ return text unless colorize_logging
130
+ color = self.class.const_get(color.upcase) if color.is_a?(Symbol)
131
+ bold = bold ? BOLD : ""
132
+ "#{bold}#{color}#{text}#{CLEAR}"
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/log_subscriber"
4
+ require "active_support/logger"
5
+ require "active_support/notifications"
6
+
7
+ module ActiveSupport
8
+ class LogSubscriber
9
+ # Provides some helpers to deal with testing log subscribers by setting up
10
+ # notifications. Take for instance Active Record subscriber tests:
11
+ #
12
+ # class SyncLogSubscriberTest < ActiveSupport::TestCase
13
+ # include ActiveSupport::LogSubscriber::TestHelper
14
+ #
15
+ # setup do
16
+ # ActiveRecord::LogSubscriber.attach_to(:active_record)
17
+ # end
18
+ #
19
+ # def test_basic_query_logging
20
+ # Developer.all.to_a
21
+ # wait
22
+ # assert_equal 1, @logger.logged(:debug).size
23
+ # assert_match(/Developer Load/, @logger.logged(:debug).last)
24
+ # assert_match(/SELECT \* FROM "developers"/, @logger.logged(:debug).last)
25
+ # end
26
+ # end
27
+ #
28
+ # All you need to do is to ensure that your log subscriber is added to
29
+ # Rails::Subscriber, as in the second line of the code above. The test
30
+ # helpers are responsible for setting up the queue, subscriptions and
31
+ # turning colors in logs off.
32
+ #
33
+ # The messages are available in the @logger instance, which is a logger with
34
+ # limited powers (it actually does not send anything to your output), and
35
+ # you can collect them doing @logger.logged(level), where level is the level
36
+ # used in logging, like info, debug, warn and so on.
37
+ module TestHelper
38
+ def setup # :nodoc:
39
+ @logger = MockLogger.new
40
+ @notifier = ActiveSupport::Notifications::Fanout.new
41
+
42
+ ActiveSupport::LogSubscriber.colorize_logging = false
43
+
44
+ @old_notifier = ActiveSupport::Notifications.notifier
45
+ set_logger(@logger)
46
+ ActiveSupport::Notifications.notifier = @notifier
47
+ end
48
+
49
+ def teardown # :nodoc:
50
+ set_logger(nil)
51
+ ActiveSupport::Notifications.notifier = @old_notifier
52
+ end
53
+
54
+ class MockLogger
55
+ include ActiveSupport::Logger::Severity
56
+
57
+ attr_reader :flush_count
58
+ attr_accessor :level
59
+
60
+ def initialize(level = DEBUG)
61
+ @flush_count = 0
62
+ @level = level
63
+ @logged = Hash.new { |h, k| h[k] = [] }
64
+ end
65
+
66
+ def method_missing(level, message = nil)
67
+ if block_given?
68
+ @logged[level] << yield
69
+ else
70
+ @logged[level] << message
71
+ end
72
+ end
73
+
74
+ def logged(level)
75
+ @logged[level].compact.map { |l| l.to_s.strip }
76
+ end
77
+
78
+ def flush
79
+ @flush_count += 1
80
+ end
81
+
82
+ ActiveSupport::Logger::Severity.constants.each do |severity|
83
+ class_eval <<-EOT, __FILE__, __LINE__ + 1
84
+ def #{severity.downcase}?
85
+ #{severity} >= @level
86
+ end
87
+ EOT
88
+ end
89
+ end
90
+
91
+ # Wait notifications to be published.
92
+ def wait
93
+ @notifier.wait
94
+ end
95
+
96
+ # Overwrite if you use another logger in your log subscriber.
97
+ #
98
+ # def logger
99
+ # ActiveRecord::Base.logger = @logger
100
+ # end
101
+ def set_logger(logger)
102
+ ActiveSupport::LogSubscriber.logger = logger
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/logger_silence"
4
+ require "active_support/logger_thread_safe_level"
5
+ require "logger"
6
+
7
+ module ActiveSupport
8
+ class Logger < ::Logger
9
+ include LoggerSilence
10
+
11
+ # Returns true if the logger destination matches one of the sources
12
+ #
13
+ # logger = Logger.new(STDOUT)
14
+ # ActiveSupport::Logger.logger_outputs_to?(logger, STDOUT)
15
+ # # => true
16
+ def self.logger_outputs_to?(logger, *sources)
17
+ logdev = logger.instance_variable_get("@logdev")
18
+ logger_source = logdev.dev if logdev.respond_to?(:dev)
19
+ sources.any? { |source| source == logger_source }
20
+ end
21
+
22
+ # Broadcasts logs to multiple loggers.
23
+ def self.broadcast(logger) # :nodoc:
24
+ Module.new do
25
+ define_method(:add) do |*args, &block|
26
+ logger.add(*args, &block)
27
+ super(*args, &block)
28
+ end
29
+
30
+ define_method(:<<) do |x|
31
+ logger << x
32
+ super(x)
33
+ end
34
+
35
+ define_method(:close) do
36
+ logger.close
37
+ super()
38
+ end
39
+
40
+ define_method(:progname=) do |name|
41
+ logger.progname = name
42
+ super(name)
43
+ end
44
+
45
+ define_method(:formatter=) do |formatter|
46
+ logger.formatter = formatter
47
+ super(formatter)
48
+ end
49
+
50
+ define_method(:level=) do |level|
51
+ logger.level = level
52
+ super(level)
53
+ end
54
+
55
+ define_method(:local_level=) do |level|
56
+ logger.local_level = level if logger.respond_to?(:local_level=)
57
+ super(level) if respond_to?(:local_level=)
58
+ end
59
+
60
+ define_method(:silence) do |level = Logger::ERROR, &block|
61
+ if logger.respond_to?(:silence)
62
+ logger.silence(level) do
63
+ if defined?(super)
64
+ super(level, &block)
65
+ else
66
+ block.call(self)
67
+ end
68
+ end
69
+ else
70
+ if defined?(super)
71
+ super(level, &block)
72
+ else
73
+ block.call(self)
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+
80
+ def initialize(*args)
81
+ super
82
+ @formatter = SimpleFormatter.new
83
+ end
84
+
85
+ # Simple formatter which only displays the message.
86
+ class SimpleFormatter < ::Logger::Formatter
87
+ # This method is invoked when a log event occurs
88
+ def call(severity, timestamp, progname, msg)
89
+ "#{String === msg ? msg : msg.inspect}\n"
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+ require "active_support/core_ext/module/attribute_accessors"
5
+ require "active_support/logger_thread_safe_level"
6
+
7
+ module LoggerSilence
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ ActiveSupport::Deprecation.warn(
12
+ "Including LoggerSilence is deprecated and will be removed in Rails 6.1. " \
13
+ "Please use `ActiveSupport::LoggerSilence` instead"
14
+ )
15
+
16
+ include ActiveSupport::LoggerSilence
17
+ end
18
+ end
19
+
20
+ module ActiveSupport
21
+ module LoggerSilence
22
+ extend ActiveSupport::Concern
23
+
24
+ included do
25
+ cattr_accessor :silencer, default: true
26
+ include ActiveSupport::LoggerThreadSafeLevel
27
+ end
28
+
29
+ # Silences the logger for the duration of the block.
30
+ def silence(temporary_level = Logger::ERROR)
31
+ if silencer
32
+ begin
33
+ old_local_level = local_level
34
+ self.local_level = temporary_level
35
+
36
+ yield self
37
+ ensure
38
+ self.local_level = old_local_level
39
+ end
40
+ else
41
+ yield self
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+ require "active_support/core_ext/module/attribute_accessors"
5
+ require "concurrent"
6
+ require "fiber"
7
+
8
+ module ActiveSupport
9
+ module LoggerThreadSafeLevel # :nodoc:
10
+ extend ActiveSupport::Concern
11
+
12
+ included do
13
+ cattr_accessor :local_levels, default: Concurrent::Map.new(initial_capacity: 2), instance_accessor: false
14
+ end
15
+
16
+ Logger::Severity.constants.each do |severity|
17
+ class_eval(<<-EOT, __FILE__, __LINE__ + 1)
18
+ def #{severity.downcase}? # def debug?
19
+ Logger::#{severity} >= level # DEBUG >= level
20
+ end # end
21
+ EOT
22
+ end
23
+
24
+ def after_initialize
25
+ ActiveSupport::Deprecation.warn(
26
+ "Logger don't need to call #after_initialize directly anymore. It will be deprecated without replacement in " \
27
+ "Rails 6.1."
28
+ )
29
+ end
30
+
31
+ def local_log_id
32
+ Fiber.current.__id__
33
+ end
34
+
35
+ def local_level
36
+ self.class.local_levels[local_log_id]
37
+ end
38
+
39
+ def local_level=(level)
40
+ if level
41
+ self.class.local_levels[local_log_id] = level
42
+ else
43
+ self.class.local_levels.delete(local_log_id)
44
+ end
45
+ end
46
+
47
+ def level
48
+ local_level || super
49
+ end
50
+
51
+ def add(severity, message = nil, progname = nil, &block) # :nodoc:
52
+ return true if @logdev.nil? || (severity || UNKNOWN) < level
53
+ super
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,227 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openssl"
4
+ require "base64"
5
+ require "active_support/core_ext/array/extract_options"
6
+ require "active_support/core_ext/module/attribute_accessors"
7
+ require "active_support/message_verifier"
8
+ require "active_support/messages/metadata"
9
+
10
+ module ActiveSupport
11
+ # MessageEncryptor is a simple way to encrypt values which get stored
12
+ # somewhere you don't trust.
13
+ #
14
+ # The cipher text and initialization vector are base64 encoded and returned
15
+ # to you.
16
+ #
17
+ # This can be used in situations similar to the <tt>MessageVerifier</tt>, but
18
+ # where you don't want users to be able to determine the value of the payload.
19
+ #
20
+ # len = ActiveSupport::MessageEncryptor.key_len
21
+ # salt = SecureRandom.random_bytes(len)
22
+ # key = ActiveSupport::KeyGenerator.new('password').generate_key(salt, len) # => "\x89\xE0\x156\xAC..."
23
+ # crypt = ActiveSupport::MessageEncryptor.new(key) # => #<ActiveSupport::MessageEncryptor ...>
24
+ # encrypted_data = crypt.encrypt_and_sign('my secret data') # => "NlFBTTMwOUV5UlA1QlNEN2xkY2d6eThYWWh..."
25
+ # crypt.decrypt_and_verify(encrypted_data) # => "my secret data"
26
+ #
27
+ # === Confining messages to a specific purpose
28
+ #
29
+ # By default any message can be used throughout your app. But they can also be
30
+ # confined to a specific +:purpose+.
31
+ #
32
+ # token = crypt.encrypt_and_sign("this is the chair", purpose: :login)
33
+ #
34
+ # Then that same purpose must be passed when verifying to get the data back out:
35
+ #
36
+ # crypt.decrypt_and_verify(token, purpose: :login) # => "this is the chair"
37
+ # crypt.decrypt_and_verify(token, purpose: :shipping) # => nil
38
+ # crypt.decrypt_and_verify(token) # => nil
39
+ #
40
+ # Likewise, if a message has no purpose it won't be returned when verifying with
41
+ # a specific purpose.
42
+ #
43
+ # token = crypt.encrypt_and_sign("the conversation is lively")
44
+ # crypt.decrypt_and_verify(token, purpose: :scare_tactics) # => nil
45
+ # crypt.decrypt_and_verify(token) # => "the conversation is lively"
46
+ #
47
+ # === Making messages expire
48
+ #
49
+ # By default messages last forever and verifying one year from now will still
50
+ # return the original value. But messages can be set to expire at a given
51
+ # time with +:expires_in+ or +:expires_at+.
52
+ #
53
+ # crypt.encrypt_and_sign(parcel, expires_in: 1.month)
54
+ # crypt.encrypt_and_sign(doowad, expires_at: Time.now.end_of_year)
55
+ #
56
+ # Then the messages can be verified and returned up to the expire time.
57
+ # Thereafter, verifying returns +nil+.
58
+ #
59
+ # === Rotating keys
60
+ #
61
+ # MessageEncryptor also supports rotating out old configurations by falling
62
+ # back to a stack of encryptors. Call +rotate+ to build and add an encryptor
63
+ # so +decrypt_and_verify+ will also try the fallback.
64
+ #
65
+ # By default any rotated encryptors use the values of the primary
66
+ # encryptor unless specified otherwise.
67
+ #
68
+ # You'd give your encryptor the new defaults:
69
+ #
70
+ # crypt = ActiveSupport::MessageEncryptor.new(@secret, cipher: "aes-256-gcm")
71
+ #
72
+ # Then gradually rotate the old values out by adding them as fallbacks. Any message
73
+ # generated with the old values will then work until the rotation is removed.
74
+ #
75
+ # crypt.rotate old_secret # Fallback to an old secret instead of @secret.
76
+ # crypt.rotate cipher: "aes-256-cbc" # Fallback to an old cipher instead of aes-256-gcm.
77
+ #
78
+ # Though if both the secret and the cipher was changed at the same time,
79
+ # the above should be combined into:
80
+ #
81
+ # crypt.rotate old_secret, cipher: "aes-256-cbc"
82
+ class MessageEncryptor
83
+ prepend Messages::Rotator::Encryptor
84
+
85
+ cattr_accessor :use_authenticated_message_encryption, instance_accessor: false, default: false
86
+
87
+ class << self
88
+ def default_cipher #:nodoc:
89
+ if use_authenticated_message_encryption
90
+ "aes-256-gcm"
91
+ else
92
+ "aes-256-cbc"
93
+ end
94
+ end
95
+ end
96
+
97
+ module NullSerializer #:nodoc:
98
+ def self.load(value)
99
+ value
100
+ end
101
+
102
+ def self.dump(value)
103
+ value
104
+ end
105
+ end
106
+
107
+ module NullVerifier #:nodoc:
108
+ def self.verify(value)
109
+ value
110
+ end
111
+
112
+ def self.generate(value)
113
+ value
114
+ end
115
+ end
116
+
117
+ class InvalidMessage < StandardError; end
118
+ OpenSSLCipherError = OpenSSL::Cipher::CipherError
119
+
120
+ # Initialize a new MessageEncryptor. +secret+ must be at least as long as
121
+ # the cipher key size. For the default 'aes-256-gcm' cipher, this is 256
122
+ # bits. If you are using a user-entered secret, you can generate a suitable
123
+ # key by using <tt>ActiveSupport::KeyGenerator</tt> or a similar key
124
+ # derivation function.
125
+ #
126
+ # First additional parameter is used as the signature key for +MessageVerifier+.
127
+ # This allows you to specify keys to encrypt and sign data.
128
+ #
129
+ # ActiveSupport::MessageEncryptor.new('secret', 'signature_secret')
130
+ #
131
+ # Options:
132
+ # * <tt>:cipher</tt> - Cipher to use. Can be any cipher returned by
133
+ # <tt>OpenSSL::Cipher.ciphers</tt>. Default is 'aes-256-gcm'.
134
+ # * <tt>:digest</tt> - String of digest to use for signing. Default is
135
+ # +SHA1+. Ignored when using an AEAD cipher like 'aes-256-gcm'.
136
+ # * <tt>:serializer</tt> - Object serializer to use. Default is +Marshal+.
137
+ def initialize(secret, *signature_key_or_options)
138
+ options = signature_key_or_options.extract_options!
139
+ sign_secret = signature_key_or_options.first
140
+ @secret = secret
141
+ @sign_secret = sign_secret
142
+ @cipher = options[:cipher] || self.class.default_cipher
143
+ @digest = options[:digest] || "SHA1" unless aead_mode?
144
+ @verifier = resolve_verifier
145
+ @serializer = options[:serializer] || Marshal
146
+ end
147
+
148
+ # Encrypt and sign a message. We need to sign the message in order to avoid
149
+ # padding attacks. Reference: https://www.limited-entropy.com/padding-oracle-attacks/.
150
+ def encrypt_and_sign(value, expires_at: nil, expires_in: nil, purpose: nil)
151
+ verifier.generate(_encrypt(value, expires_at: expires_at, expires_in: expires_in, purpose: purpose))
152
+ end
153
+
154
+ # Decrypt and verify a message. We need to verify the message in order to
155
+ # avoid padding attacks. Reference: https://www.limited-entropy.com/padding-oracle-attacks/.
156
+ def decrypt_and_verify(data, purpose: nil, **)
157
+ _decrypt(verifier.verify(data), purpose)
158
+ end
159
+
160
+ # Given a cipher, returns the key length of the cipher to help generate the key of desired size
161
+ def self.key_len(cipher = default_cipher)
162
+ OpenSSL::Cipher.new(cipher).key_len
163
+ end
164
+
165
+ private
166
+ def _encrypt(value, **metadata_options)
167
+ cipher = new_cipher
168
+ cipher.encrypt
169
+ cipher.key = @secret
170
+
171
+ # Rely on OpenSSL for the initialization vector
172
+ iv = cipher.random_iv
173
+ cipher.auth_data = "" if aead_mode?
174
+
175
+ encrypted_data = cipher.update(Messages::Metadata.wrap(@serializer.dump(value), metadata_options))
176
+ encrypted_data << cipher.final
177
+
178
+ blob = "#{::Base64.strict_encode64 encrypted_data}--#{::Base64.strict_encode64 iv}"
179
+ blob = "#{blob}--#{::Base64.strict_encode64 cipher.auth_tag}" if aead_mode?
180
+ blob
181
+ end
182
+
183
+ def _decrypt(encrypted_message, purpose)
184
+ cipher = new_cipher
185
+ encrypted_data, iv, auth_tag = encrypted_message.split("--").map { |v| ::Base64.strict_decode64(v) }
186
+
187
+ # Currently the OpenSSL bindings do not raise an error if auth_tag is
188
+ # truncated, which would allow an attacker to easily forge it. See
189
+ # https://github.com/ruby/openssl/issues/63
190
+ raise InvalidMessage if aead_mode? && (auth_tag.nil? || auth_tag.bytes.length != 16)
191
+
192
+ cipher.decrypt
193
+ cipher.key = @secret
194
+ cipher.iv = iv
195
+ if aead_mode?
196
+ cipher.auth_tag = auth_tag
197
+ cipher.auth_data = ""
198
+ end
199
+
200
+ decrypted_data = cipher.update(encrypted_data)
201
+ decrypted_data << cipher.final
202
+
203
+ message = Messages::Metadata.verify(decrypted_data, purpose)
204
+ @serializer.load(message) if message
205
+ rescue OpenSSLCipherError, TypeError, ArgumentError
206
+ raise InvalidMessage
207
+ end
208
+
209
+ def new_cipher
210
+ OpenSSL::Cipher.new(@cipher)
211
+ end
212
+
213
+ attr_reader :verifier
214
+
215
+ def aead_mode?
216
+ @aead_mode ||= new_cipher.authenticated?
217
+ end
218
+
219
+ def resolve_verifier
220
+ if aead_mode?
221
+ NullVerifier
222
+ else
223
+ MessageVerifier.new(@sign_secret || @secret, digest: @digest, serializer: NullSerializer)
224
+ end
225
+ end
226
+ end
227
+ end