Skip to content

Commit 6ede60d

Browse files
committed
Fix #11481: Don't show bug group actions that can't be used
Currently the bug action group dropdown list on view_all_bug_page.php shows (almost) every available option, even if the current user isn't authorised to use those options. This patch implements checking to see which options are available to the user for the issues which are currently shown on view_all_bug_page.php. Options are only displayed in the dropdown list if the user is able to use the option on at least one of the bugs displayed. Additionally, the logic behind when selection checkboxes are shown alongside a bug has been improved. Checkboxes won't display next to bugs that the user cannot perform group actions on. Backported from master branch because it's needed to resolve an LFI/XSS issue in bug_actiongroup_ext.php.
1 parent 5b93161 commit 6ede60d

6 files changed

+173
-75
lines changed

Diff for: core/bug_group_action_api.php

+136
Original file line numberDiff line numberDiff line change
@@ -155,3 +155,139 @@ function bug_group_action_process( $p_action, $p_bug_id ) {
155155
$t_function_name = 'action_' . $p_action . '_process';
156156
return $t_function_name( $p_bug_id );
157157
}
158+
159+
/**
160+
* Get a list of bug group actions available to the current user for one or
161+
* more projects.
162+
* @param array $p_projects An array containing one or more project IDs
163+
* @return null
164+
*/
165+
function bug_group_action_get_commands( $p_project_ids = null ) {
166+
if ( $p_project_ids === null || count( $p_project_ids ) == 0 ) {
167+
$p_project_ids = array( ALL_PROJECTS );
168+
}
169+
170+
$t_commands = array();
171+
foreach( $p_project_ids as $t_project_id ) {
172+
173+
if( !isset( $t_commands['MOVE'] ) &&
174+
access_has_project_level( config_get( 'move_bug_threshold', null, null, $t_project_id ), $t_project_id ) ) {
175+
$t_commands['MOVE'] = lang_get( 'actiongroup_menu_move' );
176+
}
177+
178+
if( !isset( $t_commands['COPY'] ) &&
179+
access_has_any_project( config_get( 'report_bug_threshold', null, null, $t_project_id ) ) ) {
180+
$t_commands['COPY'] = lang_get( 'actiongroup_menu_copy' );
181+
}
182+
183+
if( !isset( $t_commands['ASSIGN'] ) &&
184+
access_has_project_level( config_get( 'update_bug_assign_threshold', null, null, $t_project_id ), $t_project_id ) ) {
185+
if( ON == config_get( 'auto_set_status_to_assigned', null, null, $t_project_id ) &&
186+
access_has_project_level( access_get_status_threshold( config_get( 'bug_assigned_status', null, null, $t_project_id ), $t_project_id ), $t_project_id ) ) {
187+
$t_commands['ASSIGN'] = lang_get( 'actiongroup_menu_assign' );
188+
} else {
189+
$t_commands['ASSIGN'] = lang_get( 'actiongroup_menu_assign' );
190+
}
191+
}
192+
193+
if( !isset( $t_commands['CLOSE'] ) &&
194+
access_has_project_level( config_get( 'update_bug_status_threshold', null, null, $t_project_id ), $t_project_id ) &&
195+
( access_has_project_level( access_get_status_threshold( config_get( 'bug_closed_status_threshold', null, null, $t_project_id ), $t_project_id ), $t_project_id ) ||
196+
access_has_project_level( config_get( 'allow_reporter_close', null, null, $t_project_id ), $t_project_id ) ) ) {
197+
$t_commands['CLOSE'] = lang_get( 'actiongroup_menu_close' );
198+
}
199+
200+
if( !isset( $t_commands['DELETE'] ) &&
201+
access_has_project_level( config_get( 'delete_bug_threshold', null, null, $t_project_id ), $t_project_id ) ) {
202+
$t_commands['DELETE'] = lang_get( 'actiongroup_menu_delete' );
203+
}
204+
205+
if( !isset( $t_commands['RESOLVE'] ) &&
206+
access_has_project_level( config_get( 'update_bug_status_threshold', null, null, $t_project_id ), $t_project_id ) &&
207+
access_has_project_level( access_get_status_threshold( config_get( 'bug_resolved_status_threshold', null, null, $t_project_id ), $t_project_id ), $t_project_id ) ) {
208+
$t_commands['RESOLVE'] = lang_get( 'actiongroup_menu_resolve' );
209+
}
210+
211+
if( !isset( $t_commands['SET_STICKY'] ) &&
212+
access_has_project_level( config_get( 'set_bug_sticky_threshold', null, null, $t_project_id ), $t_project_id ) ) {
213+
$t_commands['SET_STICKY'] = lang_get( 'actiongroup_menu_set_sticky' );
214+
}
215+
216+
if( !isset( $t_commands['UP_PRIOR'] ) &&
217+
access_has_project_level( config_get( 'update_bug_threshold', null, null, $t_project_id ), $t_project_id ) ) {
218+
$t_commands['UP_PRIOR'] = lang_get( 'actiongroup_menu_update_priority' );
219+
}
220+
221+
if( !isset( $t_commands['EXT_UPDATE_SEVERITY'] ) &&
222+
access_has_project_level( config_get( 'update_bug_threshold', null, null, $t_project_id ), $t_project_id ) ) {
223+
$t_commands['EXT_UPDATE_SEVERITY'] = lang_get( 'actiongroup_menu_update_severity' );
224+
}
225+
226+
if( !isset( $t_commands['UP_STATUS'] ) &&
227+
access_has_project_level( config_get( 'update_bug_status_threshold', null, null, $t_project_id ), $t_project_id ) ) {
228+
$t_commands['UP_STATUS'] = lang_get( 'actiongroup_menu_update_status' );
229+
}
230+
231+
if( !isset( $t_commands['UP_CATEGORY'] ) &&
232+
access_has_project_level( config_get( 'update_bug_threshold', null, null, $t_project_id ), $t_project_id ) ) {
233+
$t_commands['UP_CATEGORY'] = lang_get( 'actiongroup_menu_update_category' );
234+
}
235+
236+
if( !isset( $t_commands['VIEW_STATUS'] ) &&
237+
access_has_project_level( config_get( 'change_view_status_threshold', null, null, $t_project_id ), $t_project_id ) ) {
238+
$t_commands['VIEW_STATUS'] = lang_get( 'actiongroup_menu_update_view_status' );
239+
}
240+
241+
if( !isset( $t_commands['EXT_UPDATE_PRODUCT_BUILD'] ) &&
242+
config_get( 'enable_product_build', null, null, $t_project_id ) == ON &&
243+
access_has_project_level( config_get( 'update_bug_threshold', null, null, $t_project_id ), $t_project_id ) ) {
244+
$t_commands['EXT_UPDATE_PRODUCT_BUILD'] = lang_get( 'actiongroup_menu_update_product_build' );
245+
}
246+
247+
if( !isset( $t_commands['EXT_ADD_NOTE'] ) &&
248+
access_has_project_level( config_get( 'add_bugnote_threshold', null, null, $t_project_id ), $t_project_id ) ) {
249+
$t_commands['EXT_ADD_NOTE'] = lang_get( 'actiongroup_menu_add_note' );
250+
}
251+
252+
if( !isset( $t_commands['EXT_ATTACH_TAGS'] ) &&
253+
access_has_project_level( config_get( 'tag_attach_threshold', null, null, $t_project_id ), $t_project_id ) ) {
254+
$t_commands['EXT_ATTACH_TAGS'] = lang_get( 'actiongroup_menu_attach_tags' );
255+
}
256+
257+
if( !isset( $t_commands['UP_FIXED_IN_VERSION'] ) &&
258+
version_should_show_product_version( $t_project_id ) &&
259+
access_has_project_level( config_get( 'update_bug_threshold', null, null, $t_project_id ), $t_project_id ) ) {
260+
$t_commands['UP_FIXED_IN_VERSION'] = lang_get( 'actiongroup_menu_update_fixed_in_version' );
261+
}
262+
263+
if( !isset( $t_commands['UP_TARGET_VERSION'] ) &&
264+
version_should_show_product_version( $t_project_id ) &&
265+
access_has_project_level( config_get( 'roadmap_update_threshold', null, null, $t_project_id ), $t_project_id ) ) {
266+
$t_commands['UP_TARGET_VERSION'] = lang_get( 'actiongroup_menu_update_target_version' );
267+
}
268+
269+
$t_custom_field_ids = custom_field_get_linked_ids( $t_project_id );
270+
foreach( $t_custom_field_ids as $t_custom_field_id ) {
271+
if( !custom_field_has_write_access_to_project( $t_custom_field_id, $t_project_id ) ) {
272+
continue;
273+
}
274+
$t_custom_field_def = custom_field_get_definition( $t_custom_field_id );
275+
$t_command_id = 'custom_field_' . $t_custom_field_id;
276+
$t_command_caption = sprintf( lang_get( 'actiongroup_menu_update_field' ), lang_get_defaulted( $t_custom_field_def['name'] ) );
277+
$t_commands[$t_command_id] = string_display( $t_command_caption );
278+
}
279+
}
280+
281+
$t_custom_group_actions = config_get( 'custom_group_actions' );
282+
283+
foreach( $t_custom_group_actions as $t_custom_group_action ) {
284+
# use label if provided to get the localized text, otherwise fallback to action name.
285+
if( isset( $t_custom_group_action['label'] ) ) {
286+
$t_commands[$t_custom_group_action['action']] = lang_get_defaulted( $t_custom_group_action['label'] );
287+
} else {
288+
$t_commands[$t_custom_group_action['action']] = lang_get_defaulted( $t_custom_group_action['action'] );
289+
}
290+
}
291+
292+
return $t_commands;
293+
}

Diff for: core/columns_api.php

+16-3
Original file line numberDiff line numberDiff line change
@@ -836,11 +836,24 @@ function print_column_title_overdue( $p_sort, $p_dir, $p_columns_target = COLUMN
836836
* @access public
837837
*/
838838
function print_column_selection( $p_bug, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
839-
global $t_checkboxes_exist, $t_update_bug_threshold;
839+
global $g_checkboxes_exist;
840840

841841
echo '<td>';
842-
if( access_has_bug_level( $t_update_bug_threshold, $p_bug->id ) ) {
843-
$t_checkboxes_exist = true;
842+
if( access_has_any_project( config_get( 'report_bug_threshold', null, null, $p_bug->project_id ), $p_bug->project_id ) ||
843+
# !TODO: check if any other projects actually exist for the bug to be moved to
844+
access_has_project_level( config_get( 'move_bug_threshold', null, null, $p_bug->project_id ), $p_bug->project_id ) ||
845+
# !TODO: factor in $g_auto_set_status_to_assigned == ON
846+
access_has_project_level( config_get( 'update_bug_assign_threshold', null, null, $p_bug->project_id ), $p_bug->project_id ) ||
847+
access_has_project_level( config_get( 'update_bug_threshold', null, null, $p_bug->project_id ), $p_bug->project_id ) ||
848+
access_has_project_level( config_get( 'delete_bug_threshold', null, null, $p_bug->project_id ), $p_bug->project_id ) ||
849+
# !TODO: check to see if the bug actually has any different selectable workflow states
850+
access_has_project_level( config_get( 'update_bug_status_threshold', null, null, $p_bug->project_id ), $p_bug->project_id ) ||
851+
access_has_project_level( config_get( 'set_bug_sticky_threshold', null, null, $p_bug->project_id ), $p_bug->project_id ) ||
852+
access_has_project_level( config_get( 'change_view_status_threshold', null, null, $p_bug->project_id ), $p_bug->project_id ) ||
853+
access_has_project_level( config_get( 'add_bugnote_threshold', null, null, $p_bug->project_id ), $p_bug->project_id ) ||
854+
access_has_project_level( config_get( 'tag_attach_threshold', null, null, $p_bug->project_id ), $p_bug->project_id ) ||
855+
access_has_project_level( config_get( 'roadmap_update_threshold', null, null, $p_bug->project_id ), $p_bug->project_id ) ) {
856+
$g_checkboxes_exist = true;
844857
printf( "<input type=\"checkbox\" name=\"bug_arr[]\" value=\"%d\" />", $p_bug->id );
845858
} else {
846859
echo "&#160;";

Diff for: core/print_api.php

+12-62
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@
4848
*/
4949
require_once( 'file_api.php' );
5050

51+
require_once( 'bug_group_action_api.php' );
52+
5153
# --------------------
5254
# Print the headers to cause the page to redirect to $p_url
5355
# If $p_die is true (default), terminate the execution of the script
@@ -945,68 +947,16 @@ function print_language_option_list( $p_language ) {
945947
}
946948
}
947949

948-
# @@@ preliminary support for multiple bug actions.
949-
function print_all_bug_action_option_list() {
950-
$commands = array(
951-
'MOVE' => lang_get( 'actiongroup_menu_move' ),
952-
'COPY' => lang_get( 'actiongroup_menu_copy' ),
953-
'ASSIGN' => lang_get( 'actiongroup_menu_assign' ),
954-
'CLOSE' => lang_get( 'actiongroup_menu_close' ),
955-
'DELETE' => lang_get( 'actiongroup_menu_delete' ),
956-
'RESOLVE' => lang_get( 'actiongroup_menu_resolve' ),
957-
'SET_STICKY' => lang_get( 'actiongroup_menu_set_sticky' ),
958-
'UP_PRIOR' => lang_get( 'actiongroup_menu_update_priority' ),
959-
'EXT_UPDATE_SEVERITY' => lang_get( 'actiongroup_menu_update_severity' ),
960-
'UP_STATUS' => lang_get( 'actiongroup_menu_update_status' ),
961-
'UP_CATEGORY' => lang_get( 'actiongroup_menu_update_category' ),
962-
'VIEW_STATUS' => lang_get( 'actiongroup_menu_update_view_status' ),
963-
'EXT_UPDATE_PRODUCT_BUILD' => lang_get( 'actiongroup_menu_update_product_build' ),
964-
'EXT_ADD_NOTE' => lang_get( 'actiongroup_menu_add_note' ),
965-
'EXT_ATTACH_TAGS' => lang_get( 'actiongroup_menu_attach_tags' ),
966-
);
967-
968-
$t_project_id = helper_get_current_project();
969-
970-
if( ALL_PROJECTS != $t_project_id ) {
971-
$t_user_id = auth_get_current_user_id();
972-
973-
if( access_has_project_level( config_get( 'update_bug_threshold' ), $t_project_id ) ) {
974-
$commands['UP_FIXED_IN_VERSION'] = lang_get( 'actiongroup_menu_update_fixed_in_version' );
975-
}
976-
977-
if( access_has_project_level( config_get( 'roadmap_update_threshold' ), $t_project_id ) ) {
978-
$commands['UP_TARGET_VERSION'] = lang_get( 'actiongroup_menu_update_target_version' );
979-
}
980-
981-
$t_custom_field_ids = custom_field_get_linked_ids( $t_project_id );
982-
983-
foreach( $t_custom_field_ids as $t_custom_field_id ) {
984-
# if user has not access right to modify the field, then there is no
985-
# point in showing it.
986-
if( !custom_field_has_write_access_to_project( $t_custom_field_id, $t_project_id, $t_user_id ) ) {
987-
continue;
988-
}
989-
990-
$t_custom_field_def = custom_field_get_definition( $t_custom_field_id );
991-
$t_command_id = 'custom_field_' . $t_custom_field_id;
992-
$t_command_caption = sprintf( lang_get( 'actiongroup_menu_update_field' ), lang_get_defaulted( $t_custom_field_def['name'] ) );
993-
$commands[$t_command_id] = string_display( $t_command_caption );
994-
}
995-
}
996-
997-
$t_custom_group_actions = config_get( 'custom_group_actions' );
998-
999-
foreach( $t_custom_group_actions as $t_custom_group_action ) {
1000-
# use label if provided to get the localized text, otherwise fallback to action name.
1001-
if( isset( $t_custom_group_action['label'] ) ) {
1002-
$commands[$t_custom_group_action['action']] = lang_get_defaulted( $t_custom_group_action['label'] );
1003-
} else {
1004-
$commands[$t_custom_group_action['action']] = lang_get_defaulted( $t_custom_group_action['action'] );
1005-
}
1006-
}
1007-
1008-
while( list( $key, $val ) = each( $commands ) ) {
1009-
echo '<option value="' . $key . '">' . $val . '</option>';
950+
/**
951+
* Print a dropdown list of all bug actions available to a user for a specified
952+
* set of projects.
953+
* @param array $p_projects An array containing one or more project IDs
954+
* @return null
955+
*/
956+
function print_all_bug_action_option_list( $p_project_ids = null ) {
957+
$t_commands = bug_group_action_get_commands( $p_project_ids);
958+
while( list( $t_action_id, $t_action_label ) = each( $t_commands ) ) {
959+
echo '<option value="' . $t_action_id . '">' . $t_action_label . '</option>';
1010960
}
1011961
}
1012962

Diff for: my_view_inc.php

-2
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,6 @@
5050
$t_sort = $t_filter['sort'];
5151
$t_dir = $t_filter['dir'];
5252

53-
$t_checkboxes_exist = false;
54-
5553
$t_icon_path = config_get( 'icon_path' );
5654
$t_update_bug_threshold = config_get( 'update_bug_threshold' );
5755
$t_bug_resolved_status_threshold = config_get( 'bug_resolved_status_threshold' );

Diff for: view_all_bug_page.php

+5-3
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,11 @@
5151
$t_users_handlers[] = $rows[$i]->handler_id;
5252
$t_project_ids[] = $rows[$i]->project_id;
5353
}
54-
user_cache_array_rows( array_unique( $t_users_handlers ) );
55-
project_cache_array_rows( array_unique( $t_project_ids ) );
56-
54+
$t_unique_users_handlers = array_unique( $t_users_handlers );
55+
$t_unique_project_ids = array_unique( $t_project_ids );
56+
user_cache_array_rows( $t_unique_users_handlers );
57+
project_cache_array_rows( $t_unique_project_ids );
58+
5759
gpc_set_cookie( config_get( 'bug_list_cookie' ), implode( ',', $t_bugslist ) );
5860

5961
compress_enable();

Diff for: view_all_inc.php

+4-5
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,9 @@
5555
list( $t_sort, ) = explode( ',', $t_filter['sort'] );
5656
list( $t_dir, ) = explode( ',', $t_filter['dir'] );
5757

58-
$t_checkboxes_exist = false;
58+
$g_checkboxes_exist = false;
5959

6060
$t_icon_path = config_get( 'icon_path' );
61-
$t_update_bug_threshold = config_get( 'update_bug_threshold' );
6261

6362
# Improve performance by caching category data in one pass
6463
if ( helper_get_current_project() > 0 ) {
@@ -226,14 +225,14 @@ function write_bug_rows ( $p_rows )
226225
<td class="left" colspan="<?php echo $col_count; ?>">
227226
<span class="floatleft">
228227
<?php
229-
if ( $t_checkboxes_exist && ON == config_get( 'use_javascript' ) ) {
228+
if ( $g_checkboxes_exist && ON == config_get( 'use_javascript' ) ) {
230229
echo "<input type=\"checkbox\" name=\"all_bugs\" value=\"all\" onclick=\"checkall('bug_action', this.form.all_bugs.checked)\" /><span class=\"small\">" . lang_get( 'select_all' ) . '</span>';
231230
}
232231

233-
if ( $t_checkboxes_exist ) {
232+
if ( $g_checkboxes_exist ) {
234233
?>
235234
<select name="action">
236-
<?php print_all_bug_action_option_list() ?>
235+
<?php print_all_bug_action_option_list( $t_unique_project_ids ) ?>
237236
</select>
238237
<input type="submit" class="button" value="<?php echo lang_get( 'ok' ); ?>" />
239238
<?php

0 commit comments

Comments
 (0)