activesupport 4.2.0 → 5.2.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 (254) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +366 -232
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +4 -5
  5. data/lib/active_support.rb +17 -7
  6. data/lib/active_support/all.rb +5 -3
  7. data/lib/active_support/array_inquirer.rb +48 -0
  8. data/lib/active_support/backtrace_cleaner.rb +7 -5
  9. data/lib/active_support/benchmarkable.rb +6 -4
  10. data/lib/active_support/builder.rb +3 -1
  11. data/lib/active_support/cache.rb +271 -177
  12. data/lib/active_support/cache/file_store.rb +41 -35
  13. data/lib/active_support/cache/mem_cache_store.rb +97 -88
  14. data/lib/active_support/cache/memory_store.rb +27 -30
  15. data/lib/active_support/cache/null_store.rb +7 -8
  16. data/lib/active_support/cache/redis_cache_store.rb +454 -0
  17. data/lib/active_support/cache/strategy/local_cache.rb +67 -34
  18. data/lib/active_support/cache/strategy/local_cache_middleware.rb +10 -9
  19. data/lib/active_support/callbacks.rb +654 -560
  20. data/lib/active_support/concern.rb +5 -3
  21. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +17 -0
  22. data/lib/active_support/concurrency/share_lock.rb +227 -0
  23. data/lib/active_support/configurable.rb +8 -5
  24. data/lib/active_support/core_ext.rb +3 -1
  25. data/lib/active_support/core_ext/array.rb +9 -6
  26. data/lib/active_support/core_ext/array/access.rb +29 -1
  27. data/lib/active_support/core_ext/array/conversions.rb +22 -18
  28. data/lib/active_support/core_ext/array/extract_options.rb +2 -0
  29. data/lib/active_support/core_ext/array/grouping.rb +11 -18
  30. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  31. data/lib/active_support/core_ext/array/prepend_and_append.rb +5 -3
  32. data/lib/active_support/core_ext/array/wrap.rb +7 -4
  33. data/lib/active_support/core_ext/benchmark.rb +3 -1
  34. data/lib/active_support/core_ext/big_decimal.rb +3 -1
  35. data/lib/active_support/core_ext/big_decimal/conversions.rb +10 -12
  36. data/lib/active_support/core_ext/class.rb +4 -3
  37. data/lib/active_support/core_ext/class/attribute.rb +41 -22
  38. data/lib/active_support/core_ext/class/attribute_accessors.rb +3 -1
  39. data/lib/active_support/core_ext/class/subclasses.rb +20 -8
  40. data/lib/active_support/core_ext/date.rb +6 -4
  41. data/lib/active_support/core_ext/date/acts_like.rb +3 -1
  42. data/lib/active_support/core_ext/date/blank.rb +14 -0
  43. data/lib/active_support/core_ext/date/calculations.rb +11 -9
  44. data/lib/active_support/core_ext/date/conversions.rb +31 -23
  45. data/lib/active_support/core_ext/date/zones.rb +4 -2
  46. data/lib/active_support/core_ext/date_and_time/calculations.rb +179 -56
  47. data/lib/active_support/core_ext/date_and_time/compatibility.rb +16 -0
  48. data/lib/active_support/core_ext/date_and_time/zones.rb +12 -12
  49. data/lib/active_support/core_ext/date_time.rb +7 -4
  50. data/lib/active_support/core_ext/date_time/acts_like.rb +4 -2
  51. data/lib/active_support/core_ext/date_time/blank.rb +14 -0
  52. data/lib/active_support/core_ext/date_time/calculations.rb +58 -20
  53. data/lib/active_support/core_ext/date_time/compatibility.rb +18 -0
  54. data/lib/active_support/core_ext/date_time/conversions.rb +16 -12
  55. data/lib/active_support/core_ext/digest/uuid.rb +7 -5
  56. data/lib/active_support/core_ext/enumerable.rb +107 -28
  57. data/lib/active_support/core_ext/file.rb +3 -1
  58. data/lib/active_support/core_ext/file/atomic.rb +38 -31
  59. data/lib/active_support/core_ext/hash.rb +11 -9
  60. data/lib/active_support/core_ext/hash/compact.rb +24 -15
  61. data/lib/active_support/core_ext/hash/conversions.rb +63 -43
  62. data/lib/active_support/core_ext/hash/deep_merge.rb +9 -13
  63. data/lib/active_support/core_ext/hash/except.rb +11 -8
  64. data/lib/active_support/core_ext/hash/indifferent_access.rb +4 -3
  65. data/lib/active_support/core_ext/hash/keys.rb +33 -27
  66. data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
  67. data/lib/active_support/core_ext/hash/slice.rb +8 -8
  68. data/lib/active_support/core_ext/hash/transform_values.rb +16 -7
  69. data/lib/active_support/core_ext/integer.rb +5 -3
  70. data/lib/active_support/core_ext/integer/inflections.rb +3 -1
  71. data/lib/active_support/core_ext/integer/multiple.rb +2 -0
  72. data/lib/active_support/core_ext/integer/time.rb +11 -33
  73. data/lib/active_support/core_ext/kernel.rb +6 -5
  74. data/lib/active_support/core_ext/kernel/agnostics.rb +2 -0
  75. data/lib/active_support/core_ext/kernel/concern.rb +5 -1
  76. data/lib/active_support/core_ext/kernel/reporting.rb +4 -83
  77. data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
  78. data/lib/active_support/core_ext/load_error.rb +3 -22
  79. data/lib/active_support/core_ext/marshal.rb +13 -10
  80. data/lib/active_support/core_ext/module.rb +14 -11
  81. data/lib/active_support/core_ext/module/aliasing.rb +6 -44
  82. data/lib/active_support/core_ext/module/anonymous.rb +12 -1
  83. data/lib/active_support/core_ext/module/attr_internal.rb +8 -9
  84. data/lib/active_support/core_ext/module/attribute_accessors.rb +43 -40
  85. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +150 -0
  86. data/lib/active_support/core_ext/module/concerning.rb +11 -12
  87. data/lib/active_support/core_ext/module/delegation.rb +121 -39
  88. data/lib/active_support/core_ext/module/deprecation.rb +4 -2
  89. data/lib/active_support/core_ext/module/introspection.rb +9 -9
  90. data/lib/active_support/core_ext/module/reachable.rb +5 -2
  91. data/lib/active_support/core_ext/module/redefine_method.rb +49 -0
  92. data/lib/active_support/core_ext/module/remove_method.rb +8 -3
  93. data/lib/active_support/core_ext/name_error.rb +22 -2
  94. data/lib/active_support/core_ext/numeric.rb +6 -3
  95. data/lib/active_support/core_ext/numeric/bytes.rb +22 -0
  96. data/lib/active_support/core_ext/numeric/conversions.rb +79 -74
  97. data/lib/active_support/core_ext/numeric/inquiry.rb +28 -0
  98. data/lib/active_support/core_ext/numeric/time.rb +35 -38
  99. data/lib/active_support/core_ext/object.rb +14 -13
  100. data/lib/active_support/core_ext/object/acts_like.rb +12 -1
  101. data/lib/active_support/core_ext/object/blank.rb +29 -4
  102. data/lib/active_support/core_ext/object/conversions.rb +6 -4
  103. data/lib/active_support/core_ext/object/deep_dup.rb +13 -4
  104. data/lib/active_support/core_ext/object/duplicable.rb +98 -45
  105. data/lib/active_support/core_ext/object/inclusion.rb +5 -3
  106. data/lib/active_support/core_ext/object/instance_variables.rb +3 -1
  107. data/lib/active_support/core_ext/object/json.rb +49 -19
  108. data/lib/active_support/core_ext/object/to_param.rb +3 -1
  109. data/lib/active_support/core_ext/object/to_query.rb +6 -4
  110. data/lib/active_support/core_ext/object/try.rb +70 -22
  111. data/lib/active_support/core_ext/object/with_options.rb +16 -3
  112. data/lib/active_support/core_ext/range.rb +7 -4
  113. data/lib/active_support/core_ext/range/conversions.rb +27 -7
  114. data/lib/active_support/core_ext/range/each.rb +19 -17
  115. data/lib/active_support/core_ext/range/include_range.rb +21 -19
  116. data/lib/active_support/core_ext/range/include_time_with_zone.rb +23 -0
  117. data/lib/active_support/core_ext/range/overlaps.rb +2 -0
  118. data/lib/active_support/core_ext/regexp.rb +6 -0
  119. data/lib/active_support/core_ext/securerandom.rb +25 -0
  120. data/lib/active_support/core_ext/string.rb +15 -13
  121. data/lib/active_support/core_ext/string/access.rb +9 -7
  122. data/lib/active_support/core_ext/string/behavior.rb +3 -1
  123. data/lib/active_support/core_ext/string/conversions.rb +8 -5
  124. data/lib/active_support/core_ext/string/exclude.rb +2 -0
  125. data/lib/active_support/core_ext/string/filters.rb +10 -8
  126. data/lib/active_support/core_ext/string/indent.rb +6 -4
  127. data/lib/active_support/core_ext/string/inflections.rb +61 -24
  128. data/lib/active_support/core_ext/string/inquiry.rb +3 -1
  129. data/lib/active_support/core_ext/string/multibyte.rb +15 -7
  130. data/lib/active_support/core_ext/string/output_safety.rb +35 -35
  131. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -0
  132. data/lib/active_support/core_ext/string/strip.rb +4 -5
  133. data/lib/active_support/core_ext/string/zones.rb +4 -2
  134. data/lib/active_support/core_ext/time.rb +7 -5
  135. data/lib/active_support/core_ext/time/acts_like.rb +3 -1
  136. data/lib/active_support/core_ext/time/calculations.rb +101 -51
  137. data/lib/active_support/core_ext/time/compatibility.rb +16 -0
  138. data/lib/active_support/core_ext/time/conversions.rb +20 -13
  139. data/lib/active_support/core_ext/time/zones.rb +41 -7
  140. data/lib/active_support/core_ext/uri.rb +5 -4
  141. data/lib/active_support/current_attributes.rb +195 -0
  142. data/lib/active_support/dependencies.rb +143 -160
  143. data/lib/active_support/dependencies/autoload.rb +2 -0
  144. data/lib/active_support/dependencies/interlock.rb +57 -0
  145. data/lib/active_support/deprecation.rb +12 -9
  146. data/lib/active_support/deprecation/behaviors.rb +41 -12
  147. data/lib/active_support/deprecation/constant_accessor.rb +52 -0
  148. data/lib/active_support/deprecation/instance_delegator.rb +17 -2
  149. data/lib/active_support/deprecation/method_wrappers.rb +54 -21
  150. data/lib/active_support/deprecation/proxy_wrappers.rb +56 -28
  151. data/lib/active_support/deprecation/reporting.rb +32 -12
  152. data/lib/active_support/descendants_tracker.rb +2 -0
  153. data/lib/active_support/digest.rb +20 -0
  154. data/lib/active_support/duration.rb +326 -30
  155. data/lib/active_support/duration/iso8601_parser.rb +125 -0
  156. data/lib/active_support/duration/iso8601_serializer.rb +55 -0
  157. data/lib/active_support/encrypted_configuration.rb +49 -0
  158. data/lib/active_support/encrypted_file.rb +99 -0
  159. data/lib/active_support/evented_file_update_checker.rb +205 -0
  160. data/lib/active_support/execution_wrapper.rb +128 -0
  161. data/lib/active_support/executor.rb +8 -0
  162. data/lib/active_support/file_update_checker.rb +63 -37
  163. data/lib/active_support/gem_version.rb +4 -2
  164. data/lib/active_support/gzip.rb +7 -5
  165. data/lib/active_support/hash_with_indifferent_access.rb +130 -30
  166. data/lib/active_support/i18n.rb +8 -6
  167. data/lib/active_support/i18n_railtie.rb +34 -14
  168. data/lib/active_support/inflections.rb +13 -11
  169. data/lib/active_support/inflector.rb +7 -5
  170. data/lib/active_support/inflector/inflections.rb +61 -12
  171. data/lib/active_support/inflector/methods.rb +161 -136
  172. data/lib/active_support/inflector/transliterate.rb +48 -27
  173. data/lib/active_support/json.rb +4 -2
  174. data/lib/active_support/json/decoding.rb +16 -13
  175. data/lib/active_support/json/encoding.rb +15 -57
  176. data/lib/active_support/key_generator.rb +25 -25
  177. data/lib/active_support/lazy_load_hooks.rb +50 -20
  178. data/lib/active_support/locale/en.yml +2 -0
  179. data/lib/active_support/log_subscriber.rb +13 -10
  180. data/lib/active_support/log_subscriber/test_helper.rb +14 -12
  181. data/lib/active_support/logger.rb +54 -3
  182. data/lib/active_support/logger_silence.rb +12 -7
  183. data/lib/active_support/logger_thread_safe_level.rb +33 -0
  184. data/lib/active_support/message_encryptor.rb +173 -51
  185. data/lib/active_support/message_verifier.rb +150 -17
  186. data/lib/active_support/messages/metadata.rb +71 -0
  187. data/lib/active_support/messages/rotation_configuration.rb +22 -0
  188. data/lib/active_support/messages/rotator.rb +56 -0
  189. data/lib/active_support/multibyte.rb +4 -2
  190. data/lib/active_support/multibyte/chars.rb +37 -24
  191. data/lib/active_support/multibyte/unicode.rb +100 -96
  192. data/lib/active_support/notifications.rb +11 -7
  193. data/lib/active_support/notifications/fanout.rb +10 -8
  194. data/lib/active_support/notifications/instrumenter.rb +27 -7
  195. data/lib/active_support/number_helper.rb +94 -68
  196. data/lib/active_support/number_helper/number_converter.rb +13 -11
  197. data/lib/active_support/number_helper/number_to_currency_converter.rb +9 -9
  198. data/lib/active_support/number_helper/number_to_delimited_converter.rb +9 -3
  199. data/lib/active_support/number_helper/number_to_human_converter.rb +11 -9
  200. data/lib/active_support/number_helper/number_to_human_size_converter.rb +9 -8
  201. data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
  202. data/lib/active_support/number_helper/number_to_phone_converter.rb +13 -4
  203. data/lib/active_support/number_helper/number_to_rounded_converter.rb +23 -56
  204. data/lib/active_support/number_helper/rounding_helper.rb +66 -0
  205. data/lib/active_support/option_merger.rb +3 -1
  206. data/lib/active_support/ordered_hash.rb +6 -4
  207. data/lib/active_support/ordered_options.rb +22 -4
  208. data/lib/active_support/per_thread_registry.rb +13 -6
  209. data/lib/active_support/proxy_object.rb +2 -0
  210. data/lib/active_support/rails.rb +16 -8
  211. data/lib/active_support/railtie.rb +43 -9
  212. data/lib/active_support/reloader.rb +131 -0
  213. data/lib/active_support/rescuable.rb +108 -53
  214. data/lib/active_support/security_utils.rb +17 -6
  215. data/lib/active_support/string_inquirer.rb +11 -3
  216. data/lib/active_support/subscriber.rb +15 -14
  217. data/lib/active_support/tagged_logging.rb +14 -11
  218. data/lib/active_support/test_case.rb +18 -46
  219. data/lib/active_support/testing/assertions.rb +137 -20
  220. data/lib/active_support/testing/autorun.rb +4 -2
  221. data/lib/active_support/testing/constant_lookup.rb +2 -1
  222. data/lib/active_support/testing/declarative.rb +3 -1
  223. data/lib/active_support/testing/deprecation.rb +14 -10
  224. data/lib/active_support/testing/file_fixtures.rb +36 -0
  225. data/lib/active_support/testing/isolation.rb +34 -25
  226. data/lib/active_support/testing/method_call_assertions.rb +43 -0
  227. data/lib/active_support/testing/setup_and_teardown.rb +12 -3
  228. data/lib/active_support/testing/stream.rb +44 -0
  229. data/lib/active_support/testing/tagged_logging.rb +3 -1
  230. data/lib/active_support/testing/time_helpers.rb +96 -27
  231. data/lib/active_support/time.rb +14 -12
  232. data/lib/active_support/time_with_zone.rb +195 -53
  233. data/lib/active_support/values/time_zone.rb +200 -61
  234. data/lib/active_support/values/unicode_tables.dat +0 -0
  235. data/lib/active_support/version.rb +3 -1
  236. data/lib/active_support/xml_mini.rb +69 -51
  237. data/lib/active_support/xml_mini/jdom.rb +116 -113
  238. data/lib/active_support/xml_mini/libxml.rb +17 -16
  239. data/lib/active_support/xml_mini/libxmlsax.rb +16 -18
  240. data/lib/active_support/xml_mini/nokogiri.rb +15 -15
  241. data/lib/active_support/xml_mini/nokogirisax.rb +15 -16
  242. data/lib/active_support/xml_mini/rexml.rb +17 -16
  243. metadata +55 -43
  244. data/lib/active_support/concurrency/latch.rb +0 -27
  245. data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +0 -14
  246. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -45
  247. data/lib/active_support/core_ext/date_time/zones.rb +0 -6
  248. data/lib/active_support/core_ext/kernel/debugger.rb +0 -10
  249. data/lib/active_support/core_ext/module/method_transplanting.rb +0 -11
  250. data/lib/active_support/core_ext/module/qualified_const.rb +0 -52
  251. data/lib/active_support/core_ext/object/itself.rb +0 -15
  252. data/lib/active_support/core_ext/struct.rb +0 -6
  253. data/lib/active_support/core_ext/thread.rb +0 -86
  254. data/lib/active_support/core_ext/time/marshal.rb +0 -30
@@ -1,6 +1,8 @@
1
- require 'active_support/core_ext/module/attribute_accessors'
2
- require 'active_support/core_ext/class/attribute'
3
- require 'active_support/subscriber'
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"
4
6
 
5
7
  module ActiveSupport
6
8
  # ActiveSupport::LogSubscriber is an object set to consume
@@ -49,8 +51,7 @@ module ActiveSupport
49
51
  CYAN = "\e[36m"
50
52
  WHITE = "\e[37m"
51
53
 
52
- mattr_accessor :colorize_logging
53
- self.colorize_logging = true
54
+ mattr_accessor :colorize_logging, default: true
54
55
 
55
56
  class << self
56
57
  def logger
@@ -81,11 +82,13 @@ module ActiveSupport
81
82
 
82
83
  def finish(name, id, payload)
83
84
  super if logger
84
- rescue Exception => e
85
- logger.error "Could not log #{name.inspect} event. #{e.class}: #{e.message} #{e.backtrace}"
85
+ rescue => e
86
+ if logger
87
+ logger.error "Could not log #{name.inspect} event. #{e.class}: #{e.message} #{e.backtrace}"
88
+ end
86
89
  end
87
90
 
88
- protected
91
+ private
89
92
 
90
93
  %w(info debug warn error fatal unknown).each do |level|
91
94
  class_eval <<-METHOD, __FILE__, __LINE__ + 1
@@ -95,11 +98,11 @@ module ActiveSupport
95
98
  METHOD
96
99
  end
97
100
 
98
- # Set color by using a string or one of the defined constants. If a third
101
+ # Set color by using a symbol or one of the defined constants. If a third
99
102
  # option is set to +true+, it also adds bold to the string. This is based
100
103
  # on the Highline implementation and will automatically append CLEAR to the
101
104
  # end of the returned String.
102
- def color(text, color, bold=false)
105
+ def color(text, color, bold = false) # :doc:
103
106
  return text unless colorize_logging
104
107
  color = self.class.const_get(color.upcase) if color.is_a?(Symbol)
105
108
  bold = bold ? BOLD : ""
@@ -1,6 +1,8 @@
1
- require 'active_support/log_subscriber'
2
- require 'active_support/logger'
3
- require 'active_support/notifications'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/log_subscriber"
4
+ require "active_support/logger"
5
+ require "active_support/notifications"
4
6
 
5
7
  module ActiveSupport
6
8
  class LogSubscriber
@@ -10,7 +12,7 @@ module ActiveSupport
10
12
  # class SyncLogSubscriberTest < ActiveSupport::TestCase
11
13
  # include ActiveSupport::LogSubscriber::TestHelper
12
14
  #
13
- # def setup
15
+ # setup do
14
16
  # ActiveRecord::LogSubscriber.attach_to(:active_record)
15
17
  # end
16
18
  #
@@ -33,7 +35,7 @@ module ActiveSupport
33
35
  # you can collect them doing @logger.logged(level), where level is the level
34
36
  # used in logging, like info, debug, warn and so on.
35
37
  module TestHelper
36
- def setup
38
+ def setup # :nodoc:
37
39
  @logger = MockLogger.new
38
40
  @notifier = ActiveSupport::Notifications::Fanout.new
39
41
 
@@ -44,7 +46,7 @@ module ActiveSupport
44
46
  ActiveSupport::Notifications.notifier = @notifier
45
47
  end
46
48
 
47
- def teardown
49
+ def teardown # :nodoc:
48
50
  set_logger(nil)
49
51
  ActiveSupport::Notifications.notifier = @old_notifier
50
52
  end
@@ -58,15 +60,15 @@ module ActiveSupport
58
60
  def initialize(level = DEBUG)
59
61
  @flush_count = 0
60
62
  @level = level
61
- @logged = Hash.new { |h,k| h[k] = [] }
63
+ @logged = Hash.new { |h, k| h[k] = [] }
62
64
  end
63
65
 
64
66
  def method_missing(level, message = nil)
65
- if block_given?
66
- @logged[level] << yield
67
- else
68
- @logged[level] << message
69
- end
67
+ if block_given?
68
+ @logged[level] << yield
69
+ else
70
+ @logged[level] << message
71
+ end
70
72
  end
71
73
 
72
74
  def logged(level)
@@ -1,11 +1,25 @@
1
- require 'active_support/core_ext/module/attribute_accessors'
2
- require 'active_support/logger_silence'
3
- require 'logger'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/logger_silence"
4
+ require "active_support/logger_thread_safe_level"
5
+ require "logger"
4
6
 
5
7
  module ActiveSupport
6
8
  class Logger < ::Logger
9
+ include ActiveSupport::LoggerThreadSafeLevel
7
10
  include LoggerSilence
8
11
 
12
+ # Returns true if the logger destination matches one of the sources
13
+ #
14
+ # logger = Logger.new(STDOUT)
15
+ # ActiveSupport::Logger.logger_outputs_to?(logger, STDOUT)
16
+ # # => true
17
+ def self.logger_outputs_to?(logger, *sources)
18
+ logdev = logger.instance_variable_get("@logdev")
19
+ logger_source = logdev.dev if logdev.respond_to?(:dev)
20
+ sources.any? { |source| source == logger_source }
21
+ end
22
+
9
23
  # Broadcasts logs to multiple loggers.
10
24
  def self.broadcast(logger) # :nodoc:
11
25
  Module.new do
@@ -38,12 +52,49 @@ module ActiveSupport
38
52
  logger.level = level
39
53
  super(level)
40
54
  end
55
+
56
+ define_method(:local_level=) do |level|
57
+ logger.local_level = level if logger.respond_to?(:local_level=)
58
+ super(level) if respond_to?(:local_level=)
59
+ end
60
+
61
+ define_method(:silence) do |level = Logger::ERROR, &block|
62
+ if logger.respond_to?(:silence)
63
+ logger.silence(level) do
64
+ if defined?(super)
65
+ super(level, &block)
66
+ else
67
+ block.call(self)
68
+ end
69
+ end
70
+ else
71
+ if defined?(super)
72
+ super(level, &block)
73
+ else
74
+ block.call(self)
75
+ end
76
+ end
77
+ end
41
78
  end
42
79
  end
43
80
 
44
81
  def initialize(*args)
45
82
  super
46
83
  @formatter = SimpleFormatter.new
84
+ after_initialize if respond_to? :after_initialize
85
+ end
86
+
87
+ def add(severity, message = nil, progname = nil, &block)
88
+ return true if @logdev.nil? || (severity || UNKNOWN) < level
89
+ super
90
+ end
91
+
92
+ Logger::Severity.constants.each do |severity|
93
+ class_eval(<<-EOT, __FILE__, __LINE__ + 1)
94
+ def #{severity.downcase}? # def debug?
95
+ Logger::#{severity} >= level # DEBUG >= level
96
+ end # end
97
+ EOT
47
98
  end
48
99
 
49
100
  # Simple formatter which only displays the message.
@@ -1,24 +1,29 @@
1
- require 'active_support/concern'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+ require "active_support/core_ext/module/attribute_accessors"
5
+ require "concurrent"
2
6
 
3
7
  module LoggerSilence
4
8
  extend ActiveSupport::Concern
5
-
9
+
6
10
  included do
7
- cattr_accessor :silencer
8
- self.silencer = true
11
+ cattr_accessor :silencer, default: true
9
12
  end
10
13
 
11
14
  # Silences the logger for the duration of the block.
12
15
  def silence(temporary_level = Logger::ERROR)
13
16
  if silencer
14
17
  begin
15
- old_logger_level, self.level = level, temporary_level
18
+ old_local_level = local_level
19
+ self.local_level = temporary_level
20
+
16
21
  yield self
17
22
  ensure
18
- self.level = old_logger_level
23
+ self.local_level = old_local_level
19
24
  end
20
25
  else
21
26
  yield self
22
27
  end
23
28
  end
24
- end
29
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module ActiveSupport
6
+ module LoggerThreadSafeLevel # :nodoc:
7
+ extend ActiveSupport::Concern
8
+
9
+ def after_initialize
10
+ @local_levels = Concurrent::Map.new(initial_capacity: 2)
11
+ end
12
+
13
+ def local_log_id
14
+ Thread.current.__id__
15
+ end
16
+
17
+ def local_level
18
+ @local_levels[local_log_id]
19
+ end
20
+
21
+ def local_level=(level)
22
+ if level
23
+ @local_levels[local_log_id] = level
24
+ else
25
+ @local_levels.delete(local_log_id)
26
+ end
27
+ end
28
+
29
+ def level
30
+ local_level || super
31
+ end
32
+ end
33
+ end
@@ -1,6 +1,11 @@
1
- require 'openssl'
2
- require 'base64'
3
- require 'active_support/core_ext/array/extract_options'
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"
4
9
 
5
10
  module ActiveSupport
6
11
  # MessageEncryptor is a simple way to encrypt values which get stored
@@ -12,12 +17,83 @@ module ActiveSupport
12
17
  # This can be used in situations similar to the <tt>MessageVerifier</tt>, but
13
18
  # where you don't want users to be able to determine the value of the payload.
14
19
  #
15
- # salt = SecureRandom.random_bytes(64)
16
- # key = ActiveSupport::KeyGenerator.new('password').generate_key(salt) # => "\x89\xE0\x156\xAC..."
17
- # crypt = ActiveSupport::MessageEncryptor.new(key) # => #<ActiveSupport::MessageEncryptor ...>
18
- # encrypted_data = crypt.encrypt_and_sign('my secret data') # => "NlFBTTMwOUV5UlA1QlNEN2xkY2d6eThYWWh..."
19
- # crypt.decrypt_and_verify(encrypted_data) # => "my secret data"
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 upto 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"
20
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
+
21
97
  module NullSerializer #:nodoc:
22
98
  def self.load(value)
23
99
  value
@@ -28,80 +104,126 @@ module ActiveSupport
28
104
  end
29
105
  end
30
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
+
31
117
  class InvalidMessage < StandardError; end
32
118
  OpenSSLCipherError = OpenSSL::Cipher::CipherError
33
119
 
34
120
  # Initialize a new MessageEncryptor. +secret+ must be at least as long as
35
- # the cipher key size. For the default 'aes-256-cbc' cipher, this is 256
121
+ # the cipher key size. For the default 'aes-256-gcm' cipher, this is 256
36
122
  # bits. If you are using a user-entered secret, you can generate a suitable
37
- # key with <tt>OpenSSL::Digest::SHA256.new(user_secret).digest</tt> or
38
- # similar.
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')
39
130
  #
40
131
  # Options:
41
132
  # * <tt>:cipher</tt> - Cipher to use. Can be any cipher returned by
42
- # <tt>OpenSSL::Cipher.ciphers</tt>. Default is 'aes-256-cbc'.
43
- # * <tt>:digest</tt> - String of digest to use for signing. Default is +SHA1+.
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'.
44
136
  # * <tt>:serializer</tt> - Object serializer to use. Default is +Marshal+.
45
137
  def initialize(secret, *signature_key_or_options)
46
138
  options = signature_key_or_options.extract_options!
47
139
  sign_secret = signature_key_or_options.first
48
140
  @secret = secret
49
141
  @sign_secret = sign_secret
50
- @cipher = options[:cipher] || 'aes-256-cbc'
51
- @verifier = MessageVerifier.new(@sign_secret || @secret, digest: options[:digest] || 'SHA1', serializer: NullSerializer)
142
+ @cipher = options[:cipher] || self.class.default_cipher
143
+ @digest = options[:digest] || "SHA1" unless aead_mode?
144
+ @verifier = resolve_verifier
52
145
  @serializer = options[:serializer] || Marshal
53
146
  end
54
147
 
55
148
  # Encrypt and sign a message. We need to sign the message in order to avoid
56
- # padding attacks. Reference: http://www.limited-entropy.com/padding-oracle-attacks.
57
- def encrypt_and_sign(value)
58
- verifier.generate(_encrypt(value))
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))
59
152
  end
60
153
 
61
154
  # Decrypt and verify a message. We need to verify the message in order to
62
- # avoid padding attacks. Reference: http://www.limited-entropy.com/padding-oracle-attacks.
63
- def decrypt_and_verify(value)
64
- _decrypt(verifier.verify(value))
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)
65
158
  end
66
159
 
67
- private
68
-
69
- def _encrypt(value)
70
- cipher = new_cipher
71
- cipher.encrypt
72
- cipher.key = @secret
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
73
164
 
74
- # Rely on OpenSSL for the initialization vector
75
- iv = cipher.random_iv
165
+ private
166
+ def _encrypt(value, **metadata_options)
167
+ cipher = new_cipher
168
+ cipher.encrypt
169
+ cipher.key = @secret
76
170
 
77
- encrypted_data = cipher.update(@serializer.dump(value))
78
- encrypted_data << cipher.final
171
+ # Rely on OpenSSL for the initialization vector
172
+ iv = cipher.random_iv
173
+ cipher.auth_data = "" if aead_mode?
79
174
 
80
- "#{::Base64.strict_encode64 encrypted_data}--#{::Base64.strict_encode64 iv}"
81
- end
175
+ encrypted_data = cipher.update(Messages::Metadata.wrap(@serializer.dump(value), metadata_options))
176
+ encrypted_data << cipher.final
82
177
 
83
- def _decrypt(encrypted_message)
84
- cipher = new_cipher
85
- encrypted_data, iv = encrypted_message.split("--").map {|v| ::Base64.strict_decode64(v)}
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
86
182
 
87
- cipher.decrypt
88
- cipher.key = @secret
89
- cipher.iv = iv
183
+ def _decrypt(encrypted_message, purpose)
184
+ cipher = new_cipher
185
+ encrypted_data, iv, auth_tag = encrypted_message.split("--".freeze).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
90
208
 
91
- decrypted_data = cipher.update(encrypted_data)
92
- decrypted_data << cipher.final
209
+ def new_cipher
210
+ OpenSSL::Cipher.new(@cipher)
211
+ end
93
212
 
94
- @serializer.load(decrypted_data)
95
- rescue OpenSSLCipherError, TypeError, ArgumentError
96
- raise InvalidMessage
97
- end
213
+ def verifier
214
+ @verifier
215
+ end
98
216
 
99
- def new_cipher
100
- OpenSSL::Cipher::Cipher.new(@cipher)
101
- end
217
+ def aead_mode?
218
+ @aead_mode ||= new_cipher.authenticated?
219
+ end
102
220
 
103
- def verifier
104
- @verifier
105
- end
221
+ def resolve_verifier
222
+ if aead_mode?
223
+ NullVerifier
224
+ else
225
+ MessageVerifier.new(@sign_secret || @secret, digest: @digest, serializer: NullSerializer)
226
+ end
227
+ end
106
228
  end
107
229
  end