Class: Permittable::FilterParameterRegistry

Inherits:
Object
  • Object
show all
Defined in:
lib/permittable/filter_parameter_registry.rb

Overview

Registry of sensitive parameter names (populated by sensitive: true contract fields) surfaced to Rails' log filtering. Appending plain symbols to config.filter_parameters at class-load time misses every consumer that snapshots the list at boot (ActiveRecord's filter_attributes copy, lograge-style initializers, precompiled filters). A proc appended once at boot by Permittable::Railtie consults this live registry at filter time, so fields registered when a controller class loads later (lazy loading in development) are still redacted.

Matching mirrors Rails symbol-filter semantics: case-insensitive substring match on the parameter key. The whole object is duck-typed (#add, #include?, #to_proc, #reset!) so a host can swap in its own registry via Permittable.filter_parameter_registry= and pool registrations.

Constant Summary collapse

FILTERED =
"[FILTERED]".freeze

Instance Method Summary collapse

Constructor Details

#initializeFilterParameterRegistry

Returns a new instance of FilterParameterRegistry.



18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/permittable/filter_parameter_registry.rb', line 18

def initialize
  @fields = Set.new
  @mutex = Mutex.new
  @pattern = nil
  # Stable object so the Railtie's idempotence check (`include?` before
  # `<<`) holds across repeated initializer runs. ActiveSupport's
  # ParameterFilter dups values before invoking proc filters, so in-place
  # String#replace is the supported redaction mechanism.
  @proc = lambda do |key, value|
    value.replace(FILTERED) if value.is_a?(String) && include?(key)
  end
end

Instance Method Details

#add(field) ⇒ Object



31
32
33
34
35
36
37
38
39
# File 'lib/permittable/filter_parameter_registry.rb', line 31

def add(field)
  name = field.to_s.downcase
  return if name.empty?

  @mutex.synchronize do
    @pattern = nil if @fields.add?(name)
  end
  nil
end

#include?(key) ⇒ Boolean

Returns:

  • (Boolean)


41
42
43
44
# File 'lib/permittable/filter_parameter_registry.rb', line 41

def include?(key)
  regexp = pattern
  !regexp.nil? && regexp.match?(key.to_s)
end

#patternObject



46
47
48
49
50
51
52
# File 'lib/permittable/filter_parameter_registry.rb', line 46

def pattern
  @mutex.synchronize do
    next nil if @fields.empty?

    @pattern ||= Regexp.new(@fields.map { |f| Regexp.escape(f) }.join("|"), Regexp::IGNORECASE)
  end
end

#reset!Object

Spec hygiene — the registry is process-global.



59
60
61
62
63
64
# File 'lib/permittable/filter_parameter_registry.rb', line 59

def reset!
  @mutex.synchronize do
    @fields.clear
    @pattern = nil
  end
end

#to_procObject



54
55
56
# File 'lib/permittable/filter_parameter_registry.rb', line 54

def to_proc
  @proc
end