summaryrefslogtreecommitdiff
path: root/app/finders/concerns/finder_with_cross_project_access.rb
blob: 220f62bcc7f922c07ff926abf5dd6728a30e1459 (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
63
64
65
66
67
68
69
70
71
72
# frozen_string_literal: true

# Module to prepend into finders to specify wether or not the finder requires
# cross project access
#
# This module depends on the finder implementing the following methods:
#
# - `#execute` should return an `ActiveRecord::Relation`
# - `#current_user` the user that requires access (or nil)
module FinderWithCrossProjectAccess
  extend ActiveSupport::Concern
  extend ::Gitlab::Utils::Override

  prepended do
    extend Gitlab::CrossProjectAccess::ClassMethods
  end

  override :execute
  def execute(*args)
    check = Gitlab::CrossProjectAccess.find_check(self)
    original = super

    return original unless check
    return original if should_skip_cross_project_check || can_read_cross_project?

    if check.should_run?(self)
      original.model.none
    else
      original
    end
  end

  # We can skip the cross project check for finding indivitual records.
  # this would be handled by the `can?(:read_*, result)` call in `FinderMethods`
  # itself.
  override :find_by!
  def find_by!(*args)
    skip_cross_project_check { super }
  end

  override :find_by
  def find_by(*args)
    skip_cross_project_check { super }
  end

  override :find
  def find(*args)
    skip_cross_project_check { super }
  end

  private

  attr_accessor :should_skip_cross_project_check

  def skip_cross_project_check
    self.should_skip_cross_project_check = true

    yield
  ensure
    # The find could raise an `ActiveRecord::RecordNotFound`, after which we
    # still want to re-enable the check.
    self.should_skip_cross_project_check = false
  end

  def can_read_cross_project?
    Ability.allowed?(current_user, :read_cross_project)
  end

  def can_read_project?(project)
    Ability.allowed?(current_user, :read_project, project)
  end
end