From b2bf7c6d8c481a3c33a055c39a6810daa6c80a61 Mon Sep 17 00:00:00 2001 From: Marius Balteanu Date: Thu, 16 Jun 2022 21:44:46 +0000 Subject: [PATCH] Respect field visibility when filtering/sorting by associated issue fields (#37255). Patch by Holger Just. git-svn-id: https://svn.redmine.org/redmine/trunk@21646 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/models/issue_custom_field.rb | 2 +- app/models/time_entry.rb | 6 +- test/functional/timelog_controller_test.rb | 74 ++++++++++++++++++++++ 3 files changed, 80 insertions(+), 2 deletions(-) diff --git a/app/models/issue_custom_field.rb b/app/models/issue_custom_field.rb index 18ec6bb43..de5c9ceb2 100644 --- a/app/models/issue_custom_field.rb +++ b/app/models/issue_custom_field.rb @@ -39,7 +39,7 @@ class IssueCustomField < CustomField project_condition = "EXISTS (SELECT 1 FROM #{CustomField.table_name} ifa WHERE ifa.is_for_all = #{self.class.connection.quoted_true} AND ifa.id = #{id_column})" + " OR #{Issue.table_name}.project_id IN (SELECT project_id FROM #{table_name_prefix}custom_fields_projects#{table_name_suffix} WHERE custom_field_id = #{id_column})" - "((#{sql}) AND (#{tracker_condition}) AND (#{project_condition}))" + "((#{sql}) AND (#{tracker_condition}) AND (#{project_condition}) AND (#{Issue.visible_condition(user)}))" end def validate_custom_field diff --git a/app/models/time_entry.rb b/app/models/time_entry.rb index 88e1796f3..34e4aab7b 100644 --- a/app/models/time_entry.rb +++ b/app/models/time_entry.rb @@ -63,7 +63,11 @@ class TimeEntry < ActiveRecord::Base where(TimeEntry.visible_condition(args.shift || User.current, *args)) end) scope :left_join_issue, (lambda do - joins("LEFT OUTER JOIN #{Issue.table_name} ON #{Issue.table_name}.id = #{TimeEntry.table_name}.issue_id") + joins( + "LEFT OUTER JOIN #{Issue.table_name}" \ + " ON #{Issue.table_name}.id = #{TimeEntry.table_name}.issue_id" \ + " AND (#{Issue.visible_condition(User.current)})" + ) end) scope :on_issue, (lambda do |issue| joins(:issue). diff --git a/test/functional/timelog_controller_test.rb b/test/functional/timelog_controller_test.rb index 15c15e7c0..321ed4d21 100644 --- a/test/functional/timelog_controller_test.rb +++ b/test/functional/timelog_controller_test.rb @@ -1488,6 +1488,80 @@ class TimelogControllerTest < Redmine::ControllerTest assert_select 'td.issue_cf_2', :text => 'filter_on_issue_custom_field', :count => 0 end + def test_index_should_not_filter_by_invisible_issue_data + issue = + Issue.generate!( + :project_id => 1, :tracker_id => 1, + :custom_field_values => {2 => 'filter_on_issue_custom_field'} + ) + entry = TimeEntry.generate!(:issue => issue, :hours => 2.5) + session[:user_id] = 3 + issue.update_columns is_private: true + assert_not issue.visible?(User.find(3)) + # since the issue is not visible, its custom fields and associated ojects should not be filterable + + get :index, :params => { + :f => ['issue.tracker_id'], + :op => {'issue.tracker_id' => '='}, + :v => {'issue.tracker_id' => ["1"]}, + } + assert_response :success + assert_not_include entry.id.to_s, css_select('input[name="ids[]"]').map {|e| e.attr(:value)} + + get :index, :params => { + :f => ['issue.cf_2'], + :op => {'issue.cf_2' => '='}, + :v => {'issue.cf_2' => ['filter_on_issue_custom_field']}, + } + assert_response :success + assert_equal [], css_select('input[name="ids[]"]').map {|e| e.attr(:value)} + end + + def test_indext_should_not_sort_by_invisible_issue_data + category1 = IssueCategory.find 1 + category2 = IssueCategory.find 2 + issue1 = + Issue.generate!( + :project_id => 1, :tracker_id => 1, + :custom_field_values => {2 => 'filter_on_issue_custom_field'} + ) + issue2 = + Issue.generate!( + :project_id => 1, :tracker_id => 1, + :custom_field_values => {2 => 'xxx_this_will_be_last'} + ) + entry1 = TimeEntry.generate!(:issue => issue1, :hours => 2.5, :spent_on => '2022-06-13') + entry2 = TimeEntry.generate!(:issue => issue2, :hours => 2.5, :spent_on => '2022-06-12') + session[:user_id] = 3 + issue1.update_columns is_private: true, category_id: category1.id + issue2.update_columns is_private: true, category_id: category2.id + assert_not issue1.visible?(User.find(3)) + assert_not issue2.visible?(User.find(3)) + # since the issues are not visible, their custom fields and associated ojects should not be sortable + + # issue.cf_2:desc would be entry2, entry1 + # spent_on:desc is entry1, entry2 + get :index, :params => { + :sort => "issue.cf_2:desc,spent_on:desc", + :f => ['spent_on'], + :op => {'spent_on' => '><'}, + :v => {'spent_on' => ['2022-06-12', '2022-06-13']}, + } + assert_response :success + assert_equal [entry1.id.to_s, entry2.id.to_s], css_select('input[name="ids[]"]').map {|e| e.attr(:value)} + + # issue.category:desc would be entry2, entry1 + # spent_on:desc is entry1, entry2 + get :index, :params => { + :sort => "issue.category:desc,spent_on:desc", + :f => ['spent_on'], + :op => {'spent_on' => '><'}, + :v => {'spent_on' => ['2022-06-12', '2022-06-13']}, + } + assert_response :success + assert_equal [entry1.id.to_s, entry2.id.to_s], css_select('input[name="ids[]"]').map {|e| e.attr(:value)} + end + def test_index_with_time_entry_custom_field_column field = TimeEntryCustomField.generate!(:field_format => 'string') entry = TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value'}) -- 2.39.5