]> source.dussan.org Git - redmine.git/commitdiff
Merged r21645-r21646 and r21657 to 4.2-stable (#37255).
authorMarius Balteanu <marius.balteanu@zitec.com>
Mon, 20 Jun 2022 05:46:15 +0000 (05:46 +0000)
committerMarius Balteanu <marius.balteanu@zitec.com>
Mon, 20 Jun 2022 05:46:15 +0000 (05:46 +0000)
git-svn-id: https://svn.redmine.org/redmine/branches/4.2-stable@21659 e93f8b46-1217-0410-a6f0-8f06a7374b81

app/helpers/application_helper.rb
app/models/issue_custom_field.rb
app/models/query.rb
app/models/time_entry.rb
test/functional/timelog_controller_test.rb
test/unit/query_test.rb

index dec2d494d1c7455a44956b21d35420a0ec326aea..2d3cfa3bbdac6c01fcfcf3f395c28947fdaf7003 100644 (file)
@@ -281,6 +281,7 @@ module ApplicationHelper
         object.filename
       end
     when 'CustomValue', 'CustomFieldValue'
+      return "" unless object.customized&.visible?
       if object.custom_field
         f = object.custom_field.format.formatted_custom_value(self, object, html)
         if f.nil? || f.is_a?(String)
index da725fafe1032fda51bb076ebb93f55840595deb..285a0d48c26f4fd14eb040d32aefba90194be64a 100644 (file)
@@ -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
index fad3930aa6690482cc9f22f2bdc0b76b283a90ec..3891cc456d6fcc126aaddf6e117ccda95ae9aac1 100644 (file)
@@ -115,7 +115,8 @@ class QueryAssociationColumn < QueryColumn
   end
 
   def value_object(object)
-    if assoc = object.send(@association)
+    assoc = object.send(@association)
+    if assoc && assoc.visible?
       assoc.send @attribute
     end
   end
@@ -186,7 +187,8 @@ class QueryAssociationCustomFieldColumn < QueryCustomFieldColumn
   end
 
   def value_object(object)
-    if assoc = object.send(@association)
+    assoc = object.send(@association)
+    if assoc && assoc.visible?
       super(assoc)
     end
   end
index b5926c6129107a00d01a1ba11f66be0f8a79ae31..e72c0511750dfc95d74d34d66eac1d2ddf7e13af 100644 (file)
@@ -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).
index 1f768273727526f507aa8595710a80c05e2cece1..ebb256cdf4536fb8746f35aa9deb783cfe99df7f 100644 (file)
@@ -1466,6 +1466,102 @@ class TimelogControllerTest < Redmine::ControllerTest
     assert_select 'td.issue_cf_2', :text => 'filter_on_issue_custom_field'
   end
 
+  def test_index_should_not_disclose_issue_data
+    category = IssueCategory.find 2
+    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, category_id: category.id
+    assert_not issue.visible?(User.find(3))
+    # since the issue is not visible, its custom fields and associated ojects should not be visible either
+
+    get :index, :params => {
+      :c => %w(issue issue.cf_2 issue.category)
+    }
+    assert_response :success
+    assert_select 'td.issue', :text => /#{issue.subject}/, :count => 0
+    assert_select 'td.issue-category', :text => /#{category.name}/, :count => 0
+    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'})
index 86f3a1e1456e14376d63a52018938cc7d6334e75..54d0c06d2c82db8cdad34e1eff7d2ed8b2ab4381 100644 (file)
@@ -1088,14 +1088,15 @@ class QueryTest < ActiveSupport::TestCase
   def test_filter_on_custom_field_should_ignore_projects_with_field_disabled
     field =
       IssueCustomField.generate!(
-        :trackers => Tracker.all, :project_ids => [1, 3, 4],
+        :trackers => Tracker.all, :project_ids => [1, 3, 5],
         :is_for_all => false, :is_filter => true
       )
     Issue.generate!(:project_id => 3, :tracker_id => 2,
                     :custom_field_values => {field.id.to_s => 'Foo'})
-    Issue.generate!(:project_id => 4, :tracker_id => 2,
+    Issue.generate!(:project_id => 5, :tracker_id => 2,
                     :custom_field_values => {field.id.to_s => 'Foo'})
 
+    User.current = User.find(1)
     query = IssueQuery.new(:name => '_', :project => Project.find(1))
     query.filters = {"cf_#{field.id}" => {:operator => '=', :values => ['Foo']}}
     assert_equal 2, find_issues_with_query(query).size