Skip to content

Commit

Permalink
Fix #11481: Don't show bug group actions that can't be used
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
davidhicks committed Sep 4, 2011
1 parent 5b93161 commit 6ede60d
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 75 deletions.
136 changes: 136 additions & 0 deletions core/bug_group_action_api.php
Expand Up @@ -155,3 +155,139 @@ function bug_group_action_process( $p_action, $p_bug_id ) {
$t_function_name = 'action_' . $p_action . '_process';
return $t_function_name( $p_bug_id );
}

/**
* Get a list of bug group actions available to the current user for one or
* more projects.
* @param array $p_projects An array containing one or more project IDs
* @return null
*/
function bug_group_action_get_commands( $p_project_ids = null ) {
if ( $p_project_ids === null || count( $p_project_ids ) == 0 ) {
$p_project_ids = array( ALL_PROJECTS );
}

$t_commands = array();
foreach( $p_project_ids as $t_project_id ) {

if( !isset( $t_commands['MOVE'] ) &&
access_has_project_level( config_get( 'move_bug_threshold', null, null, $t_project_id ), $t_project_id ) ) {
$t_commands['MOVE'] = lang_get( 'actiongroup_menu_move' );
}

if( !isset( $t_commands['COPY'] ) &&
access_has_any_project( config_get( 'report_bug_threshold', null, null, $t_project_id ) ) ) {
$t_commands['COPY'] = lang_get( 'actiongroup_menu_copy' );
}

if( !isset( $t_commands['ASSIGN'] ) &&
access_has_project_level( config_get( 'update_bug_assign_threshold', null, null, $t_project_id ), $t_project_id ) ) {
if( ON == config_get( 'auto_set_status_to_assigned', null, null, $t_project_id ) &&
access_has_project_level( access_get_status_threshold( config_get( 'bug_assigned_status', null, null, $t_project_id ), $t_project_id ), $t_project_id ) ) {
$t_commands['ASSIGN'] = lang_get( 'actiongroup_menu_assign' );
} else {
$t_commands['ASSIGN'] = lang_get( 'actiongroup_menu_assign' );
}
}

if( !isset( $t_commands['CLOSE'] ) &&
access_has_project_level( config_get( 'update_bug_status_threshold', null, null, $t_project_id ), $t_project_id ) &&
( 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 ) ||
access_has_project_level( config_get( 'allow_reporter_close', null, null, $t_project_id ), $t_project_id ) ) ) {
$t_commands['CLOSE'] = lang_get( 'actiongroup_menu_close' );
}

if( !isset( $t_commands['DELETE'] ) &&
access_has_project_level( config_get( 'delete_bug_threshold', null, null, $t_project_id ), $t_project_id ) ) {
$t_commands['DELETE'] = lang_get( 'actiongroup_menu_delete' );
}

if( !isset( $t_commands['RESOLVE'] ) &&
access_has_project_level( config_get( 'update_bug_status_threshold', null, null, $t_project_id ), $t_project_id ) &&
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 ) ) {
$t_commands['RESOLVE'] = lang_get( 'actiongroup_menu_resolve' );
}

if( !isset( $t_commands['SET_STICKY'] ) &&
access_has_project_level( config_get( 'set_bug_sticky_threshold', null, null, $t_project_id ), $t_project_id ) ) {
$t_commands['SET_STICKY'] = lang_get( 'actiongroup_menu_set_sticky' );
}

if( !isset( $t_commands['UP_PRIOR'] ) &&
access_has_project_level( config_get( 'update_bug_threshold', null, null, $t_project_id ), $t_project_id ) ) {
$t_commands['UP_PRIOR'] = lang_get( 'actiongroup_menu_update_priority' );
}

if( !isset( $t_commands['EXT_UPDATE_SEVERITY'] ) &&
access_has_project_level( config_get( 'update_bug_threshold', null, null, $t_project_id ), $t_project_id ) ) {
$t_commands['EXT_UPDATE_SEVERITY'] = lang_get( 'actiongroup_menu_update_severity' );
}

if( !isset( $t_commands['UP_STATUS'] ) &&
access_has_project_level( config_get( 'update_bug_status_threshold', null, null, $t_project_id ), $t_project_id ) ) {
$t_commands['UP_STATUS'] = lang_get( 'actiongroup_menu_update_status' );
}

if( !isset( $t_commands['UP_CATEGORY'] ) &&
access_has_project_level( config_get( 'update_bug_threshold', null, null, $t_project_id ), $t_project_id ) ) {
$t_commands['UP_CATEGORY'] = lang_get( 'actiongroup_menu_update_category' );
}

if( !isset( $t_commands['VIEW_STATUS'] ) &&
access_has_project_level( config_get( 'change_view_status_threshold', null, null, $t_project_id ), $t_project_id ) ) {
$t_commands['VIEW_STATUS'] = lang_get( 'actiongroup_menu_update_view_status' );
}

if( !isset( $t_commands['EXT_UPDATE_PRODUCT_BUILD'] ) &&
config_get( 'enable_product_build', null, null, $t_project_id ) == ON &&
access_has_project_level( config_get( 'update_bug_threshold', null, null, $t_project_id ), $t_project_id ) ) {
$t_commands['EXT_UPDATE_PRODUCT_BUILD'] = lang_get( 'actiongroup_menu_update_product_build' );
}

if( !isset( $t_commands['EXT_ADD_NOTE'] ) &&
access_has_project_level( config_get( 'add_bugnote_threshold', null, null, $t_project_id ), $t_project_id ) ) {
$t_commands['EXT_ADD_NOTE'] = lang_get( 'actiongroup_menu_add_note' );
}

if( !isset( $t_commands['EXT_ATTACH_TAGS'] ) &&
access_has_project_level( config_get( 'tag_attach_threshold', null, null, $t_project_id ), $t_project_id ) ) {
$t_commands['EXT_ATTACH_TAGS'] = lang_get( 'actiongroup_menu_attach_tags' );
}

if( !isset( $t_commands['UP_FIXED_IN_VERSION'] ) &&
version_should_show_product_version( $t_project_id ) &&
access_has_project_level( config_get( 'update_bug_threshold', null, null, $t_project_id ), $t_project_id ) ) {
$t_commands['UP_FIXED_IN_VERSION'] = lang_get( 'actiongroup_menu_update_fixed_in_version' );
}

if( !isset( $t_commands['UP_TARGET_VERSION'] ) &&
version_should_show_product_version( $t_project_id ) &&
access_has_project_level( config_get( 'roadmap_update_threshold', null, null, $t_project_id ), $t_project_id ) ) {
$t_commands['UP_TARGET_VERSION'] = lang_get( 'actiongroup_menu_update_target_version' );
}

$t_custom_field_ids = custom_field_get_linked_ids( $t_project_id );
foreach( $t_custom_field_ids as $t_custom_field_id ) {
if( !custom_field_has_write_access_to_project( $t_custom_field_id, $t_project_id ) ) {
continue;
}
$t_custom_field_def = custom_field_get_definition( $t_custom_field_id );
$t_command_id = 'custom_field_' . $t_custom_field_id;
$t_command_caption = sprintf( lang_get( 'actiongroup_menu_update_field' ), lang_get_defaulted( $t_custom_field_def['name'] ) );
$t_commands[$t_command_id] = string_display( $t_command_caption );
}
}

$t_custom_group_actions = config_get( 'custom_group_actions' );

foreach( $t_custom_group_actions as $t_custom_group_action ) {
# use label if provided to get the localized text, otherwise fallback to action name.
if( isset( $t_custom_group_action['label'] ) ) {
$t_commands[$t_custom_group_action['action']] = lang_get_defaulted( $t_custom_group_action['label'] );
} else {
$t_commands[$t_custom_group_action['action']] = lang_get_defaulted( $t_custom_group_action['action'] );
}
}

return $t_commands;
}
19 changes: 16 additions & 3 deletions core/columns_api.php
Expand Up @@ -836,11 +836,24 @@ function print_column_title_overdue( $p_sort, $p_dir, $p_columns_target = COLUMN
* @access public
*/
function print_column_selection( $p_bug, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
global $t_checkboxes_exist, $t_update_bug_threshold;
global $g_checkboxes_exist;

echo '<td>';
if( access_has_bug_level( $t_update_bug_threshold, $p_bug->id ) ) {
$t_checkboxes_exist = true;
if( access_has_any_project( config_get( 'report_bug_threshold', null, null, $p_bug->project_id ), $p_bug->project_id ) ||
# !TODO: check if any other projects actually exist for the bug to be moved to
access_has_project_level( config_get( 'move_bug_threshold', null, null, $p_bug->project_id ), $p_bug->project_id ) ||
# !TODO: factor in $g_auto_set_status_to_assigned == ON
access_has_project_level( config_get( 'update_bug_assign_threshold', null, null, $p_bug->project_id ), $p_bug->project_id ) ||
access_has_project_level( config_get( 'update_bug_threshold', null, null, $p_bug->project_id ), $p_bug->project_id ) ||
access_has_project_level( config_get( 'delete_bug_threshold', null, null, $p_bug->project_id ), $p_bug->project_id ) ||
# !TODO: check to see if the bug actually has any different selectable workflow states
access_has_project_level( config_get( 'update_bug_status_threshold', null, null, $p_bug->project_id ), $p_bug->project_id ) ||
access_has_project_level( config_get( 'set_bug_sticky_threshold', null, null, $p_bug->project_id ), $p_bug->project_id ) ||
access_has_project_level( config_get( 'change_view_status_threshold', null, null, $p_bug->project_id ), $p_bug->project_id ) ||
access_has_project_level( config_get( 'add_bugnote_threshold', null, null, $p_bug->project_id ), $p_bug->project_id ) ||
access_has_project_level( config_get( 'tag_attach_threshold', null, null, $p_bug->project_id ), $p_bug->project_id ) ||
access_has_project_level( config_get( 'roadmap_update_threshold', null, null, $p_bug->project_id ), $p_bug->project_id ) ) {
$g_checkboxes_exist = true;
printf( "<input type=\"checkbox\" name=\"bug_arr[]\" value=\"%d\" />", $p_bug->id );
} else {
echo "&#160;";
Expand Down
74 changes: 12 additions & 62 deletions core/print_api.php
Expand Up @@ -48,6 +48,8 @@
*/
require_once( 'file_api.php' );

require_once( 'bug_group_action_api.php' );

# --------------------
# Print the headers to cause the page to redirect to $p_url
# If $p_die is true (default), terminate the execution of the script
Expand Down Expand Up @@ -945,68 +947,16 @@ function print_language_option_list( $p_language ) {
}
}

# @@@ preliminary support for multiple bug actions.
function print_all_bug_action_option_list() {
$commands = array(
'MOVE' => lang_get( 'actiongroup_menu_move' ),
'COPY' => lang_get( 'actiongroup_menu_copy' ),
'ASSIGN' => lang_get( 'actiongroup_menu_assign' ),
'CLOSE' => lang_get( 'actiongroup_menu_close' ),
'DELETE' => lang_get( 'actiongroup_menu_delete' ),
'RESOLVE' => lang_get( 'actiongroup_menu_resolve' ),
'SET_STICKY' => lang_get( 'actiongroup_menu_set_sticky' ),
'UP_PRIOR' => lang_get( 'actiongroup_menu_update_priority' ),
'EXT_UPDATE_SEVERITY' => lang_get( 'actiongroup_menu_update_severity' ),
'UP_STATUS' => lang_get( 'actiongroup_menu_update_status' ),
'UP_CATEGORY' => lang_get( 'actiongroup_menu_update_category' ),
'VIEW_STATUS' => lang_get( 'actiongroup_menu_update_view_status' ),
'EXT_UPDATE_PRODUCT_BUILD' => lang_get( 'actiongroup_menu_update_product_build' ),
'EXT_ADD_NOTE' => lang_get( 'actiongroup_menu_add_note' ),
'EXT_ATTACH_TAGS' => lang_get( 'actiongroup_menu_attach_tags' ),
);

$t_project_id = helper_get_current_project();

if( ALL_PROJECTS != $t_project_id ) {
$t_user_id = auth_get_current_user_id();

if( access_has_project_level( config_get( 'update_bug_threshold' ), $t_project_id ) ) {
$commands['UP_FIXED_IN_VERSION'] = lang_get( 'actiongroup_menu_update_fixed_in_version' );
}

if( access_has_project_level( config_get( 'roadmap_update_threshold' ), $t_project_id ) ) {
$commands['UP_TARGET_VERSION'] = lang_get( 'actiongroup_menu_update_target_version' );
}

$t_custom_field_ids = custom_field_get_linked_ids( $t_project_id );

foreach( $t_custom_field_ids as $t_custom_field_id ) {
# if user has not access right to modify the field, then there is no
# point in showing it.
if( !custom_field_has_write_access_to_project( $t_custom_field_id, $t_project_id, $t_user_id ) ) {
continue;
}

$t_custom_field_def = custom_field_get_definition( $t_custom_field_id );
$t_command_id = 'custom_field_' . $t_custom_field_id;
$t_command_caption = sprintf( lang_get( 'actiongroup_menu_update_field' ), lang_get_defaulted( $t_custom_field_def['name'] ) );
$commands[$t_command_id] = string_display( $t_command_caption );
}
}

$t_custom_group_actions = config_get( 'custom_group_actions' );

foreach( $t_custom_group_actions as $t_custom_group_action ) {
# use label if provided to get the localized text, otherwise fallback to action name.
if( isset( $t_custom_group_action['label'] ) ) {
$commands[$t_custom_group_action['action']] = lang_get_defaulted( $t_custom_group_action['label'] );
} else {
$commands[$t_custom_group_action['action']] = lang_get_defaulted( $t_custom_group_action['action'] );
}
}

while( list( $key, $val ) = each( $commands ) ) {
echo '<option value="' . $key . '">' . $val . '</option>';
/**
* Print a dropdown list of all bug actions available to a user for a specified
* set of projects.
* @param array $p_projects An array containing one or more project IDs
* @return null
*/
function print_all_bug_action_option_list( $p_project_ids = null ) {
$t_commands = bug_group_action_get_commands( $p_project_ids);
while( list( $t_action_id, $t_action_label ) = each( $t_commands ) ) {
echo '<option value="' . $t_action_id . '">' . $t_action_label . '</option>';
}
}

Expand Down
2 changes: 0 additions & 2 deletions my_view_inc.php
Expand Up @@ -50,8 +50,6 @@
$t_sort = $t_filter['sort'];
$t_dir = $t_filter['dir'];

$t_checkboxes_exist = false;

$t_icon_path = config_get( 'icon_path' );
$t_update_bug_threshold = config_get( 'update_bug_threshold' );
$t_bug_resolved_status_threshold = config_get( 'bug_resolved_status_threshold' );
Expand Down
8 changes: 5 additions & 3 deletions view_all_bug_page.php
Expand Up @@ -51,9 +51,11 @@
$t_users_handlers[] = $rows[$i]->handler_id;
$t_project_ids[] = $rows[$i]->project_id;
}
user_cache_array_rows( array_unique( $t_users_handlers ) );
project_cache_array_rows( array_unique( $t_project_ids ) );

$t_unique_users_handlers = array_unique( $t_users_handlers );
$t_unique_project_ids = array_unique( $t_project_ids );
user_cache_array_rows( $t_unique_users_handlers );
project_cache_array_rows( $t_unique_project_ids );

gpc_set_cookie( config_get( 'bug_list_cookie' ), implode( ',', $t_bugslist ) );

compress_enable();
Expand Down
9 changes: 4 additions & 5 deletions view_all_inc.php
Expand Up @@ -55,10 +55,9 @@
list( $t_sort, ) = explode( ',', $t_filter['sort'] );
list( $t_dir, ) = explode( ',', $t_filter['dir'] );

$t_checkboxes_exist = false;
$g_checkboxes_exist = false;

$t_icon_path = config_get( 'icon_path' );
$t_update_bug_threshold = config_get( 'update_bug_threshold' );

# Improve performance by caching category data in one pass
if ( helper_get_current_project() > 0 ) {
Expand Down Expand Up @@ -226,14 +225,14 @@ function write_bug_rows ( $p_rows )
<td class="left" colspan="<?php echo $col_count; ?>">
<span class="floatleft">
<?php
if ( $t_checkboxes_exist && ON == config_get( 'use_javascript' ) ) {
if ( $g_checkboxes_exist && ON == config_get( 'use_javascript' ) ) {
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>';
}

if ( $t_checkboxes_exist ) {
if ( $g_checkboxes_exist ) {
?>
<select name="action">
<?php print_all_bug_action_option_list() ?>
<?php print_all_bug_action_option_list( $t_unique_project_ids ) ?>
</select>
<input type="submit" class="button" value="<?php echo lang_get( 'ok' ); ?>" />
<?php
Expand Down

0 comments on commit 6ede60d

Please sign in to comment.