diff options
Diffstat (limited to 'app/finders/concerns')
-rw-r--r-- | app/finders/concerns/finder_with_group_hierarchy.rb | 74 | ||||
-rw-r--r-- | app/finders/concerns/merged_at_filter.rb | 20 | ||||
-rw-r--r-- | app/finders/concerns/packages/finder_helper.rb | 18 |
3 files changed, 104 insertions, 8 deletions
diff --git a/app/finders/concerns/finder_with_group_hierarchy.rb b/app/finders/concerns/finder_with_group_hierarchy.rb new file mode 100644 index 00000000000..86ccac19b63 --- /dev/null +++ b/app/finders/concerns/finder_with_group_hierarchy.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +# Module to include into finders to provide support for querying for +# objects up and down the group hierarchy. Extracted from LabelsFinder +# +# Supports params: +# :group +# :group_id +# :include_ancestor_groups +# :include_descendant_groups +module FinderWithGroupHierarchy + extend ActiveSupport::Concern + + private + + def item_ids + raise NotImplementedError + end + + # Gets redacted array of group ids + # which can include the ancestors and descendants of the requested group. + def group_ids_for(group) + strong_memoize(:group_ids) do + groups = groups_to_include(group) + + # Because we are sure that all groups are in the same hierarchy tree + # we can preset root group for all of them to optimize permission checks + Group.preset_root_ancestor_for(groups) + + groups_user_can_read_items(groups).map(&:id) + end + end + + def groups_to_include(group) + groups = [group] + + groups += group.ancestors if include_ancestor_groups? + groups += group.descendants if include_descendant_groups? + + groups + end + + def include_ancestor_groups? + params[:include_ancestor_groups] + end + + def include_descendant_groups? + params[:include_descendant_groups] + end + + def group? + params[:group].present? || params[:group_id].present? + end + + def group + strong_memoize(:group) { params[:group].presence || Group.find(params[:group_id]) } + end + + def read_permission + raise NotImplementedError + end + + def authorized_to_read_item?(item_parent) + return true if skip_authorization + + Ability.allowed?(current_user, read_permission, item_parent) + end + + def groups_user_can_read_items(groups) + DeclarativePolicy.user_scope do + groups.select { |group| authorized_to_read_item?(group) } + end + end +end diff --git a/app/finders/concerns/merged_at_filter.rb b/app/finders/concerns/merged_at_filter.rb index 581bcca3c25..e44354f36d1 100644 --- a/app/finders/concerns/merged_at_filter.rb +++ b/app/finders/concerns/merged_at_filter.rb @@ -10,7 +10,7 @@ module MergedAtFilter mr_metrics_scope = mr_metrics_scope.merged_after(merged_after) if merged_after.present? mr_metrics_scope = mr_metrics_scope.merged_before(merged_before) if merged_before.present? - items.join_metrics.merge(mr_metrics_scope) + join_metrics(items, mr_metrics_scope) end def merged_after @@ -20,4 +20,22 @@ module MergedAtFilter def merged_before params[:merged_before] end + + # rubocop: disable CodeReuse/ActiveRecord + # + # This join optimizes merged_at queries when the finder is invoked for a project by moving + # the target_project_id condition from merge_requests table to merge_request_metrics table. + def join_metrics(items, mr_metrics_scope) + scope = if project_id = items.where_values_hash["target_project_id"] + # removing the original merge_requests.target_project_id condition + items = items.unscope(where: :target_project_id) + # adding the target_project_id condition to merge_request_metrics + items.join_metrics(project_id) + else + items.join_metrics + end + + scope.merge(mr_metrics_scope) + end + # rubocop: enable CodeReuse/ActiveRecord end diff --git a/app/finders/concerns/packages/finder_helper.rb b/app/finders/concerns/packages/finder_helper.rb index 30bc0ff7909..39c018818d1 100644 --- a/app/finders/concerns/packages/finder_helper.rb +++ b/app/finders/concerns/packages/finder_helper.rb @@ -11,22 +11,26 @@ module Packages def packages_visible_to_user(user, within_group:) return ::Packages::Package.none unless within_group - return ::Packages::Package.none unless Ability.allowed?(user, :read_package, within_group) + return ::Packages::Package.none unless Ability.allowed?(user, :read_group, within_group) - projects = projects_visible_to_reporters(user, within_group.self_and_descendants.select(:id)) + projects = projects_visible_to_reporters(user, within_group: within_group) ::Packages::Package.for_projects(projects.select(:id)) end def projects_visible_to_user(user, within_group:) return ::Project.none unless within_group - return ::Project.none unless Ability.allowed?(user, :read_package, within_group) + return ::Project.none unless Ability.allowed?(user, :read_group, within_group) - projects_visible_to_reporters(user, within_group.self_and_descendants.select(:id)) + projects_visible_to_reporters(user, within_group: within_group) end - def projects_visible_to_reporters(user, namespace_ids) - ::Project.in_namespace(namespace_ids) - .public_or_visible_to_user(user, ::Gitlab::Access::REPORTER) + def projects_visible_to_reporters(user, within_group:) + if user.is_a?(DeployToken) && Feature.enabled?(:packages_finder_helper_deploy_token) + user.accessible_projects + else + within_group.all_projects + .public_or_visible_to_user(user, ::Gitlab::Access::REPORTER) + end end def package_type |