module Recurso module Queries class Relation def initialize(identity, resource, relation_name, all_columns: true, action: :view, include_actions: []) @identity = identity @resource = resource @relation = resource.send(relation_name) @all_columns = all_columns @include_actions = (include_actions + Array(action)).uniq @action = action end def resources @relation .select("#{@relation.table_name}.#{@all_columns ? :"*" : :id}") .select(*@include_actions.map { |action| "included.can_#{action}"}) .joins(" INNER JOIN (#{included_permissions}) included ON #{@relation.table_name}.id = included.id AND included.can_#{@action} = 1 ") end private def included_permissions " SELECT cascaded.id, #{included_permission_select} FROM (#{cascaded_permissions}) cascaded GROUP BY cascaded.id " end def cascaded_permissions @relation.relevant_associations.map do |assoc| @relation .select(:id, :level) .joins(through_join_for(assoc)) .joins(" INNER join #{permission_class.table_name} ON #{permission_class.table_name}.resource_type = '#{assoc.class_name}' AND #{permission_class.table_name}.resource_id = #{resource_id_for(assoc)} AND #{permission_class.table_name}.#{identity_foreign_key} = #{@identity.id.to_i} ").to_sql end.join(" UNION ") end def included_permission_select @include_actions.map do |action| level_values = @resource.relevant_levels_for(action).map { |level| permission_class.levels[level] }.join(',') "cascaded.level IN (#{level_values}) as can_#{action}" end.join(", ") end def resource_id_for(assoc) "#{(assoc.through_reflection || assoc.active_record).table_name}.#{assoc.foreign_key}" end def through_join_for(assoc) return unless through = assoc.through_reflection " LEFT OUTER JOIN #{through.table_name} ON #{through.table_name}.#{through.association_primary_key} = #{resource_id_for(through)} " end def permission_class @permission_class ||= Recurso::Config.instance.permission_class_name_for(@identity.class).constantize end def identity_foreign_key @identity_foreign_key ||= Recurso::Config.instance.identity_foreign_key_for(@identity.class) end end end end