module ErpTechSvcs::Extensions::ActiveRecord::ProtectedByCapabilities::ClassMethods

Public Instance Methods

protected_with_capabilities(options = {}) click to toggle source
# File lib/erp_tech_svcs/extensions/active_record/protected_with_capabilities.rb, line 12
                      def protected_with_capabilities(options = {})
                        extend ProtectedByCapabilities::SingletonMethods
                    include ProtectedByCapabilities::InstanceMethods

has_many :capabilities, :as => :capability_resource
                    
# protect all instance of this class by default
class_attribute :protect_all_instances
self.protect_all_instances = (options[:protect_all_instances].nil? ? false : options[:protect_all_instances])

# Get records filtered via query scope capabilities
# By default Compass AE treats query scopes as restrictions
# A user will see all records unless the user has a capability accessor with a query scope
# If you set :protect_all_instances => true it is honored via with_user_security & with_instance_security but NOT with_query_security
# arguments: user, capability_type_iids
# capability_type_iids is optional and can be a single string or an array of strings
# Example: which files can this user download? FileAsset.with_query_security(user, 'download').all
# Example: which website sections can this user either view or edit? WebsiteSection.with_query_security(user, ['view','edit']).all
scope :with_query_security, lambda{|*args|
  raise ArgumentError if args.empty? || args.size > 2
  user = args.first
  capability_type_iids = args.second || []
  capability_type_iids = [capability_type_iids] if capability_type_iids.is_a?(String)

  scope_type = ScopeType.find_by_internal_identifier('query')
  granted_capabilities = user.all_capabilities.where(:scope_type_id => scope_type.id).where(:capability_resource_type => self.name)

  unless capability_type_iids.empty?
    capability_type_ids = capability_type_iids.collect{|type| convert_capability_type(type).id }
    granted_capabilities = granted_capabilities.where("capability_type_id IN (?)", capability_type_ids.join(','))
  end

  query = nil
  granted_capabilities.each do |scope_capability|
    query = query.nil? ? where(scope_capability.scope_query) : query.where(scope_capability.scope_query)
  end
  query
}

# Get records for this model permitted via instance capabilities
# If :protect_all_instances => true return only instances user has explicitly been granted access to
# If :protect_all_instances => false return instances without capabilities or that user is granted access to (default)
# arguments: user, capability_type_iids
# capability_type_iids is optional and can be a single string or an array of strings
# Example: which files can this user download? FileAsset.with_instance_security(user, 'download').all
# Example: which website sections can this user either view or edit? WebsiteSection.with_instance_security(user, ['view','edit']).all
scope :with_instance_security, lambda{|*args|
  raise ArgumentError if args.empty? || args.size > 2
  user = args.first
  capability_type_iids = args.second || []
  capability_type_iids = [capability_type_iids] if capability_type_iids.is_a?(String)

  scope_type = ScopeType.find_by_internal_identifier('instance')
  granted_capabilities = user.all_capabilities.where(:scope_type_id => scope_type.id).where(:capability_resource_type => self.name)

  unless capability_type_iids.empty?
    capability_type_ids = capability_type_iids.collect{|type| convert_capability_type(type).id }
    granted_capabilities = granted_capabilities.where("capability_type_id IN (#{capability_type_ids.join(',')})")
  end

  denied_capabilities = instance_capabilities.select('capabilities.id').where("capabilities.id NOT IN (#{granted_capabilities.select('capabilities.id').to_sql})")
  deny_count = denied_capabilities.count

  join_type = (self.protect_all_instances ? 'JOIN' : 'LEFT JOIN')
  query = joins("#{join_type} capabilities AS c ON c.capability_resource_id = #{self.table_name}.id AND c.capability_resource_type = '#{self.name}'").
          group(columns.collect{|c| "#{self.table_name}.#{c.name}" })
  query = (deny_count == 0 ? query.where("c.id NOT IN (SELECT id FROM capabilities) OR c.id = c.id") : query.where("c.id NOT IN (SELECT id FROM capabilities) OR c.id NOT IN (#{denied_capabilities.to_sql})"))
  query
}

# Get records for this model that the given user has access to
# arguments: user, capability_type_iids
# capability_type_iids is optional and can be a single string or an array of strings
# Example: which files can this user download? FileAsset.with_user_security(user, 'download').all
# Example: which website sections can this user either view or edit? WebsiteSection.with_user_security(user, ['view','edit']).all
scope :with_user_security, lambda{|*args|
  raise ArgumentError if args.empty? || args.size > 2              
  with_instance_security(*args).with_query_security(*args)
}
                      end