Class: Woods::Console::TableGate

Inherits:
Object
  • Object
show all
Defined in:
lib/woods/console/table_gate.rb

Overview

Layer 1 of the Console defense-in-depth stack: rejects requests touching blocked tables. SQL parsing is delegated to SqlTableScanner; this class handles permission enforcement only. Raises TableGateError on violations.

Instance Method Summary collapse

Constructor Details

#initialize(blocked_tables:, model_tables:, model_reflections: {}) ⇒ TableGate

Returns a new instance of TableGate.

Parameters:

  • blocked_tables (Array<String>)

    case-insensitive; bare names match every schema.

  • model_tables (Hash{String=>String})

    model => table.

  • model_reflections (Hash{String=>Hash{String=>String}}) (defaults to: {})

    model => assoc => table.



20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/woods/console/table_gate.rb', line 20

def initialize(blocked_tables:, model_tables:, model_reflections: {})
  @blocked_bare = Set.new
  @blocked_qualified = Set.new
  Array(blocked_tables).each do |entry|
    name = entry.to_s.downcase
    next if name.empty?

    name.include?('.') ? @blocked_qualified << name : @blocked_bare << name
  end
  @model_tables = model_tables || {}
  @model_reflections = model_reflections || {}
end

Instance Method Details

#active?Boolean

Returns:

  • (Boolean)


33
# File 'lib/woods/console/table_gate.rb', line 33

def active? = !(@blocked_bare.empty? && @blocked_qualified.empty?)

#check_association!(model_name, association) ⇒ Object

Raises:



68
69
70
71
72
73
74
75
76
# File 'lib/woods/console/table_gate.rb', line 68

def check_association!(model_name, association)
  return unless active? && association

  reflections = @model_reflections[model_name.to_s]
  return unless reflections

  table = reflections[association.to_s]
  raise TableGateError, reject_message(table) if table && blocked?(table)
end

#check_joins!(model_name, joins) ⇒ Object

rubocop:disable Metrics/CyclomaticComplexity



56
57
58
59
60
61
62
63
64
65
66
# File 'lib/woods/console/table_gate.rb', line 56

def check_joins!(model_name, joins) # rubocop:disable Metrics/CyclomaticComplexity
  return unless active? && joins && Array(joins).any?

  reflections = @model_reflections[model_name.to_s]
  return unless reflections

  Array(joins).each do |join|
    table = reflections[join.to_s]
    raise TableGateError, reject_message(table) if table && blocked?(table)
  end
end

#check_model!(model_name) ⇒ Object



43
44
45
46
47
48
# File 'lib/woods/console/table_gate.rb', line 43

def check_model!(model_name)
  return unless active?

  table = @model_tables[model_name.to_s]
  check_table!(table) unless table.nil?
end

#check_sql!(sql) ⇒ Object



35
36
37
38
39
40
41
# File 'lib/woods/console/table_gate.rb', line 35

def check_sql!(sql)
  return unless active? && sql&.length&.positive?

  SqlTableScanner.identifiers_in(sql).each do |raw|
    raise TableGateError, reject_message(raw) if blocked?(raw)
  end
end

#check_table!(table_name) ⇒ Object

Raises:



50
51
52
53
54
# File 'lib/woods/console/table_gate.rb', line 50

def check_table!(table_name)
  return unless active?
  return if table_name.nil? || table_name.to_s.empty?
  raise TableGateError, reject_message(table_name) if blocked?(table_name)
end