summaryrefslogtreecommitdiff
path: root/app/finders/concerns/finder_methods.rb
blob: 8de3276184d19478ed8f17769f6b9efc3c1119dd (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
# 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)

    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)

    model_name = object&.model_name || model.model_name

    Ability.allowed?(current_user, :"read_#{model_name.singular}", object)
  end

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