# frozen_string_literal: true module FinderMethods # rubocop: disable CodeReuse/ActiveRecord def find_by!(*args) raise_not_found_unless_authorized execute.reorder(nil).find_by!(*args) end # rubocop: enable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord def find_by(*args) if_authorized execute.reorder(nil).find_by(*args) end # rubocop: enable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord def find(*args) raise_not_found_unless_authorized execute.reorder(nil).find(*args) end # rubocop: enable CodeReuse/ActiveRecord private def raise_not_found_unless_authorized(result) result = if_authorized(result) unless result # This fetches the model from the `ActiveRecord::Relation` but does not # actually execute the query. model = execute.model raise ActiveRecord::RecordNotFound, "Couldn't find #{model}" end result end def if_authorized(result) # Return the result if the finder does not perform authorization checks. # this is currently the case in the `MilestoneFinder` return result unless respond_to?(:current_user, true) result if can_read_object?(result) end def can_read_object?(object) # When there's no policy, we'll allow the read, this is for example the case # for Todos return true unless DeclarativePolicy.has_policy?(object) Ability.allowed?(current_user, :"read_#{to_ability_name(object)}", object) end def to_ability_name(object) return object.to_ability_name if object.respond_to?(:to_ability_name) # Not all objects define `#to_ability_name`, so attempt to derive it: object.model_name.singular end end