2018-09-11 15:08:34 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2017-12-11 09:21:06 -05:00
|
|
|
# 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:
|
|
|
|
#
|
2018-12-07 12:09:00 -05:00
|
|
|
# - `#execute` should return an `ActiveRecord::Relation` or the `model` needs to
|
|
|
|
# be defined in the call to `requires_cross_project_access`.
|
2017-12-11 09:21:06 -05:00
|
|
|
# - `#current_user` the user that requires access (or nil)
|
|
|
|
module FinderWithCrossProjectAccess
|
|
|
|
extend ActiveSupport::Concern
|
|
|
|
extend ::Gitlab::Utils::Override
|
|
|
|
|
|
|
|
prepended do
|
|
|
|
extend Gitlab::CrossProjectAccess::ClassMethods
|
2018-12-07 12:09:00 -05:00
|
|
|
|
|
|
|
cattr_accessor :finder_model
|
|
|
|
|
|
|
|
def self.requires_cross_project_access(*args)
|
|
|
|
super
|
|
|
|
|
|
|
|
self.finder_model = extract_model_from_arguments(args)
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def self.extract_model_from_arguments(args)
|
|
|
|
args.detect { |argument| argument.is_a?(Hash) && argument[:model] }
|
|
|
|
&.fetch(:model)
|
|
|
|
end
|
2017-12-11 09:21:06 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
override :execute
|
|
|
|
def execute(*args)
|
|
|
|
check = Gitlab::CrossProjectAccess.find_check(self)
|
2018-12-07 12:09:00 -05:00
|
|
|
original = -> { super }
|
2017-12-11 09:21:06 -05:00
|
|
|
|
2018-12-07 12:09:00 -05:00
|
|
|
return original.call unless check
|
|
|
|
return original.call if should_skip_cross_project_check || can_read_cross_project?
|
2017-12-11 09:21:06 -05:00
|
|
|
|
|
|
|
if check.should_run?(self)
|
2018-12-07 12:09:00 -05:00
|
|
|
finder_model&.none || original.call.model.none
|
2017-12-11 09:21:06 -05:00
|
|
|
else
|
2018-12-07 12:09:00 -05:00
|
|
|
original.call
|
2017-12-11 09:21:06 -05:00
|
|
|
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
|
|
|
|
|
|
|
|
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
|