summaryrefslogtreecommitdiff
path: root/app/finders/concerns/finder_methods.rb
blob: 622cbcf4928ff142ea4ec2bf1f798c978e20139b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# 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

  def find(*args)
    raise_not_found_unless_authorized model.find(*args)
  end

  private

  def raise_not_found_unless_authorized(result)
    result = if_authorized(result)

    raise ActiveRecord::RecordNotFound.new("Couldn't find #{model}") unless result

    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)

    if can_read_object?(result)
      result
    else
      nil
    end
  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

  # This fetches the model from the `ActiveRecord::Relation` but does not
  # actually execute the query.
  def model
    execute.model
  end
end