module RemoteDb module Concerns module RestrictedColumns extend ActiveSupport::Concern class ForbiddenColumnException < Exception; end included do def read_attribute_with_safety(name) original_db_column = self.class.original_columns.include?(name.to_sym) visible_db_column = self.class.visible_columns.include? (name.to_sym) if (original_db_column && visible_db_column) || !original_db_column read_attribute_without_safety(name) else raise ForbiddenColumnException, "Column #{name} is not allowed for access." end end alias_method_chain :read_attribute, :safety end module ClassMethods def visible_columns @visible_columns ||= [] end def original_columns @original_columns ||= [] end # Note: This is a hack that relies on ActiveRecord's internals. Most of the # logic is originally from: ActiveRecord::ModelSchema#reset_column_information` def table_columns=(visible_columns) unless abstract_class? @visible_columns = visible_columns @original_columns = columns.map(&:name).map(&:to_sym) @columns.reject! do |column| !visible_columns.include?(column.name.to_sym) end @column_names = @content_columns = @column_defaults = @columns_hash = nil @dynamic_methods_hash = nil @inheritance_column = nil unless defined?(@explicit_inheritance_column) && @explicit_inheritance_column @arel_engine = @relation = nil end end end end end end