module Heimdallr # Heimdallr is attached to your models by including the module and defining the # restrictions in your classes. # # class Article < ActiveRecord::Base # include Heimdallr::Model # # restrict do |context| # # ... # end # end # # {Heimdallr::Model} should be included prior to any other modules, as it may omit # some named scopes defined by those if it is not. # # @todo Improve description module Model extend ActiveSupport::Concern included do class_attribute :heimdallr_restrictions, :instance_writer => false end # Class methods for {Heimdallr::Model}. See also +ActiveSupport::Concern+. module ClassMethods # @overload restrict # Define restrictions for a model with a DSL. See {Model} overview # for DSL documentation. # # @yield A passed block is executed in the context of a new {Evaluator}. # # @overload restrict(context, action=:view) # Return a secure collection object for the current scope. # # @param [Object] context security context # @param [Symbol] action kind of actions which will be performed # @return [Proxy::Collection] def restrict(context=nil, options={}, &block) if block self.heimdallr_restrictions = Evaluator.new(self, block) else Proxy::Collection.new(context, restrictions(context).request_scope(:fetch, self), options) end end # Evaluate the restrictions for a given +context+ and +record+. # # @return [Evaluator] def restrictions(context, record=nil) heimdallr_restrictions.evaluate(context, record) end # @api private # # An internal attribute to store the list of user-defined name scopes. # It is required because ActiveRecord does not provide any introspection for # named scopes. attr_accessor :heimdallr_scopes # An interceptor for named scopes which adds them to {#heimdallr_scopes} list. def scope(name, *args) self.heimdallr_scopes ||= [] self.heimdallr_scopes.push name super end # @api private # # An internal attribute to store the list of user-defined relation-like methods # which return ActiveRecord family objects and can be automatically restricted. attr_accessor :heimdallr_relations # A DSL method for defining relation-like methods. def heimdallr_relation(*methods) self.heimdallr_relations ||= [] self.heimdallr_relations += methods.map(&:to_sym) end # Builds the Proxy class that should be used to wrap this model def heimdallr_proxy_class unless @heimdallr_proxy_class record = self @heimdallr_proxy_class = Class.new(Proxy::Record) do define_singleton_method :model_name do record.model_name end end end @heimdallr_proxy_class end end # Return a secure proxy object for this record. # # @return [Record::Proxy] def restrict(context, options={}) self.class.heimdallr_proxy_class.new(context, self, options) end # @api private # # An internal attribute to store the Heimdallr security validators for # the context in which this object is currently being saved. attr_accessor :heimdallr_validators # @api private # # An internal method to run Heimdallr security validators, when applicable. def heimdallr_validations validates_with Heimdallr::Validator end def self.included(klass) klass.class_eval do validate :heimdallr_validations end end end end