Commit 1e087ef9 authored by lpsolit%gmail.com's avatar lpsolit%gmail.com

Bug 340278: Move CheckCanChangeField() from process_bug.cgi to Bug.pm - Patch by…

Bug 340278: Move CheckCanChangeField() from process_bug.cgi to Bug.pm - Patch by Frédéric Buclin <LpSolit@gmail.com> r=mkanat a=justdave
parent f301c924
...@@ -100,7 +100,7 @@ sub initBug { ...@@ -100,7 +100,7 @@ sub initBug {
my ($bug_id, $user_id) = (@_); my ($bug_id, $user_id) = (@_);
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
$bug_id = trim($bug_id); $bug_id = trim($bug_id || 0);
my $old_bug_id = $bug_id; my $old_bug_id = $bug_id;
...@@ -1166,6 +1166,138 @@ sub CheckIfVotedConfirmed { ...@@ -1166,6 +1166,138 @@ sub CheckIfVotedConfirmed {
return $ret; return $ret;
} }
################################################################################
# check_can_change_field() defines what users are allowed to change. You
# can add code here for site-specific policy changes, according to the
# instructions given in the Bugzilla Guide and below. Note that you may also
# have to update the Bugzilla::Bug::user() function to give people access to the
# options that they are permitted to change.
#
# check_can_change_field() returns true if the user is allowed to change this
# field, and false if they are not.
#
# The parameters to this method are as follows:
# $field - name of the field in the bugs table the user is trying to change
# $oldvalue - what they are changing it from
# $newvalue - what they are changing it to
# $PrivilegesRequired - return the reason of the failure, if any
# $data - hash containing relevant parameters, e.g. from the CGI object
################################################################################
sub check_can_change_field {
my $self = shift;
my ($field, $oldvalue, $newvalue, $PrivilegesRequired, $data) = (@_);
my $user = $self->{'who'};
$oldvalue = defined($oldvalue) ? $oldvalue : '';
$newvalue = defined($newvalue) ? $newvalue : '';
# Return true if they haven't changed this field at all.
if ($oldvalue eq $newvalue) {
return 1;
} elsif (trim($oldvalue) eq trim($newvalue)) {
return 1;
# numeric fields need to be compared using ==
} elsif (($field eq 'estimated_time' || $field eq 'remaining_time')
&& $newvalue ne $data->{'dontchange'}
&& $oldvalue == $newvalue)
{
return 1;
}
# Allow anyone to change comments.
if ($field =~ /^longdesc/) {
return 1;
}
# Ignore the assigned_to field if the bug is not being reassigned
if ($field eq 'assigned_to'
&& $data->{'knob'} ne 'reassignbycomponent'
&& $data->{'knob'} ne 'reassign')
{
return 1;
}
# If the user isn't allowed to change a field, we must tell him who can.
# We store the required permission set into the $PrivilegesRequired
# variable which gets passed to the error template.
#
# $PrivilegesRequired = 0 : no privileges required;
# $PrivilegesRequired = 1 : the reporter, assignee or an empowered user;
# $PrivilegesRequired = 2 : the assignee or an empowered user;
# $PrivilegesRequired = 3 : an empowered user.
# Allow anyone with "editbugs" privs to change anything.
if ($user->in_group('editbugs')) {
return 1;
}
# *Only* users with "canconfirm" privs can confirm bugs.
if ($field eq 'canconfirm'
|| ($field eq 'bug_status'
&& $oldvalue eq 'UNCONFIRMED'
&& is_open_state($newvalue)))
{
$PrivilegesRequired = 3;
return $user->in_group('canconfirm');
}
# Make sure that a valid bug ID has been given.
if (!$self->{'error'}) {
# Allow the assignee to change anything else.
if ($self->{'assigned_to_id'} == $user->id) {
return 1;
}
# Allow the QA contact to change anything else.
if (Bugzilla->params->{'useqacontact'}
&& $self->{'qa_contact_id'}
&& ($self->{'qa_contact_id'} == $user->id))
{
return 1;
}
}
# At this point, the user is either the reporter or an
# unprivileged user. We first check for fields the reporter
# is not allowed to change.
# The reporter may not:
# - reassign bugs, unless the bugs are assigned to him;
# in that case we will have already returned 1 above
# when checking for the assignee of the bug.
if ($field eq 'assigned_to') {
$PrivilegesRequired = 2;
return 0;
}
# - change the QA contact
if ($field eq 'qa_contact') {
$PrivilegesRequired = 2;
return 0;
}
# - change the target milestone
if ($field eq 'target_milestone') {
$PrivilegesRequired = 2;
return 0;
}
# - change the priority (unless he could have set it originally)
if ($field eq 'priority'
&& !Param('letsubmitterchoosepriority'))
{
$PrivilegesRequired = 2;
return 0;
}
# The reporter is allowed to change anything else.
if (!$self->{'error'} && $self->{'reporter_id'} == $user->id) {
return 1;
}
# If we haven't returned by this point, then the user doesn't
# have the necessary permissions to change this field.
$PrivilegesRequired = 1;
return 0;
}
# #
# Field Validation # Field Validation
# #
......
...@@ -673,11 +673,11 @@ ...@@ -673,11 +673,11 @@
<para> <para>
For maximum flexibility, customizing this means editing Bugzilla's Perl For maximum flexibility, customizing this means editing Bugzilla's Perl
code. This gives the administrator complete control over exactly who is code. This gives the administrator complete control over exactly who is
allowed to do what. The relevant function is called allowed to do what. The relevant method is called
<filename>CheckCanChangeField()</filename>, <filename>check_can_change_field()</filename>,
and is found in <filename>process_bug.cgi</filename> in your and is found in <filename>Bug.pm</filename> in your
Bugzilla directory. If you open that file and search for Bugzilla/ directory. If you open that file and search for
<quote>sub CheckCanChangeField</quote>, you'll find it. <quote>sub check_can_change_field</quote>, you'll find it.
</para> </para>
<para> <para>
......
...@@ -41,11 +41,6 @@ ...@@ -41,11 +41,6 @@
use strict; use strict;
my $UserInEditGroupSet = -1;
my $UserInCanConfirmGroupSet = -1;
my $PrivilegesRequired = 0;
my $lastbugid = 0;
use lib qw(.); use lib qw(.);
use Bugzilla; use Bugzilla;
...@@ -78,6 +73,7 @@ $vars->{'use_keywords'} = 1 if Bugzilla::Keyword::keyword_count(); ...@@ -78,6 +73,7 @@ $vars->{'use_keywords'} = 1 if Bugzilla::Keyword::keyword_count();
my @editable_bug_fields = editable_bug_fields(); my @editable_bug_fields = editable_bug_fields();
my $requiremilestone = 0; my $requiremilestone = 0;
my $PrivilegesRequired = 0;
###################################################################### ######################################################################
# Subroutines # Subroutines
...@@ -143,6 +139,11 @@ if (defined $cgi->param('id')) { ...@@ -143,6 +139,11 @@ if (defined $cgi->param('id')) {
# Make sure there are bugs to process. # Make sure there are bugs to process.
scalar(@idlist) || ThrowUserError("no_bugs_chosen"); scalar(@idlist) || ThrowUserError("no_bugs_chosen");
# Build a bug object using $cgi->param('id') as ID.
# If there are more than one bug changed at once, the bug object will be
# empty, which doesn't matter.
my $bug = new Bugzilla::Bug(scalar $cgi->param('id'), $whoid);
# Make sure form param 'dontchange' is defined so it can be compared to easily. # Make sure form param 'dontchange' is defined so it can be compared to easily.
$cgi->param('dontchange','') unless defined $cgi->param('dontchange'); $cgi->param('dontchange','') unless defined $cgi->param('dontchange');
...@@ -176,7 +177,6 @@ ValidateComment(scalar $cgi->param('comment')); ...@@ -176,7 +177,6 @@ ValidateComment(scalar $cgi->param('comment'));
# during validation. # during validation.
foreach my $field ("dependson", "blocked") { foreach my $field ("dependson", "blocked") {
if ($cgi->param('id')) { if ($cgi->param('id')) {
my $bug = new Bugzilla::Bug($cgi->param('id'), $user->id);
my @old = @{$bug->$field}; my @old = @{$bug->$field};
my @new; my @new;
foreach my $id (split(/[\s,]+/, $cgi->param($field))) { foreach my $id (split(/[\s,]+/, $cgi->param($field))) {
...@@ -199,7 +199,8 @@ foreach my $field ("dependson", "blocked") { ...@@ -199,7 +199,8 @@ foreach my $field ("dependson", "blocked") {
} }
} }
if ((@$added || @$removed) if ((@$added || @$removed)
&& (!CheckCanChangeField($field, $bug->bug_id, 0, 1))) { && !$bug->check_can_change_field($field, 0, 1, \$PrivilegesRequired))
{
$vars->{'privs'} = $PrivilegesRequired; $vars->{'privs'} = $PrivilegesRequired;
$vars->{'field'} = $field; $vars->{'field'} = $field;
ThrowUserError("illegal_change", $vars); ThrowUserError("illegal_change", $vars);
...@@ -307,8 +308,9 @@ if (((defined $cgi->param('id') && $cgi->param('product') ne $oldproduct) ...@@ -307,8 +308,9 @@ if (((defined $cgi->param('id') && $cgi->param('product') ne $oldproduct)
&& CheckonComment( "reassignbycomponent" )) && CheckonComment( "reassignbycomponent" ))
{ {
# Check to make sure they actually have the right to change the product # Check to make sure they actually have the right to change the product
if (!CheckCanChangeField('product', scalar $cgi->param('id'), $oldproduct, if (!$bug->check_can_change_field('product', $oldproduct, $cgi->param('product'),
$cgi->param('product'))) { \$PrivilegesRequired))
{
$vars->{'oldvalue'} = $oldproduct; $vars->{'oldvalue'} = $oldproduct;
$vars->{'newvalue'} = $cgi->param('product'); $vars->{'newvalue'} = $cgi->param('product');
$vars->{'field'} = 'product'; $vars->{'field'} = 'product';
...@@ -410,169 +412,6 @@ if (((defined $cgi->param('id') && $cgi->param('product') ne $oldproduct) ...@@ -410,169 +412,6 @@ if (((defined $cgi->param('id') && $cgi->param('product') ne $oldproduct)
} }
} }
# Checks that the user is allowed to change the given field. Actually, right
# now, the rules are pretty simple, and don't look at the field itself very
# much, but that could be enhanced.
my $ownerid;
my $reporterid;
my $qacontactid;
################################################################################
# CheckCanChangeField() defines what users are allowed to change what bugs. You
# can add code here for site-specific policy changes, according to the
# instructions given in the Bugzilla Guide and below. Note that you may also
# have to update the Bugzilla::Bug::user() function to give people access to the
# options that they are permitted to change.
#
# CheckCanChangeField() should return true if the user is allowed to change this
# field, and false if they are not.
#
# The parameters to this function are as follows:
# $field - name of the field in the bugs table the user is trying to change
# $bugid - the ID of the bug they are changing
# $oldvalue - what they are changing it from
# $newvalue - what they are changing it to
#
# Note that this function is currently not called for dependency changes
# (bug 141593) or CC changes, which means anyone can change those fields.
#
# Do not change the sections between START DO_NOT_CHANGE and END DO_NOT_CHANGE.
################################################################################
sub CheckCanChangeField {
# START DO_NOT_CHANGE
my ($field, $bugid, $oldvalue, $newvalue) = (@_);
$oldvalue = defined($oldvalue) ? $oldvalue : '';
$newvalue = defined($newvalue) ? $newvalue : '';
# Return true if they haven't changed this field at all.
if ($oldvalue eq $newvalue) {
return 1;
} elsif (trim($oldvalue) eq trim($newvalue)) {
return 1;
# numeric fields need to be compared using ==
} elsif (($field eq "estimated_time" || $field eq "remaining_time")
&& $newvalue ne $cgi->param('dontchange')
&& $oldvalue == $newvalue)
{
return 1;
}
# END DO_NOT_CHANGE
# Allow anyone to change comments.
if ($field =~ /^longdesc/) {
return 1;
}
# Ignore the assigned_to field if the bug is not being reassigned
if ($field eq "assigned_to"
&& $cgi->param('knob') ne "reassignbycomponent"
&& $cgi->param('knob') ne "reassign")
{
return 1;
}
# START DO_NOT_CHANGE
# Find out whether the user is a member of the "editbugs" and/or
# "canconfirm" groups. $UserIn*GroupSet are caches of the return value of
# the UserInGroup calls.
if ($UserInEditGroupSet < 0) {
$UserInEditGroupSet = UserInGroup("editbugs");
}
if ($UserInCanConfirmGroupSet < 0) {
$UserInCanConfirmGroupSet = UserInGroup("canconfirm");
}
# END DO_NOT_CHANGE
# If the user isn't allowed to change a field, we must tell him who can.
# We store the required permission set into the $PrivilegesRequired
# variable which gets passed to the error template.
#
# $PrivilegesRequired = 0 : no privileges required;
# $PrivilegesRequired = 1 : the reporter, assignee or an empowered user;
# $PrivilegesRequired = 2 : the assignee or an empowered user;
# $PrivilegesRequired = 3 : an empowered user.
# Allow anyone with "editbugs" privs to change anything.
if ($UserInEditGroupSet) {
return 1;
}
# *Only* users with "canconfirm" privs can confirm bugs.
if ($field eq "canconfirm"
|| ($field eq "bug_status"
&& $oldvalue eq 'UNCONFIRMED'
&& is_open_state($newvalue)))
{
$PrivilegesRequired = 3;
return $UserInCanConfirmGroupSet;
}
# START DO_NOT_CHANGE
# $reporterid, $ownerid and $qacontactid are caches of the results of
# the call to find out the assignee, reporter and qacontact of the current bug.
if ($lastbugid != $bugid) {
($reporterid, $ownerid, $qacontactid) = $dbh->selectrow_array(
q{SELECT reporter, assigned_to, qa_contact FROM bugs
WHERE bug_id = ? }, undef, $bugid);
$lastbugid = $bugid;
}
# END DO_NOT_CHANGE
# Allow the assignee to change anything else.
if ($ownerid == $whoid) {
return 1;
}
# Allow the QA contact to change anything else.
if (Param('useqacontact') && $qacontactid && ($qacontactid == $whoid)) {
return 1;
}
# At this point, the user is either the reporter or an
# unprivileged user. We first check for fields the reporter
# is not allowed to change.
# The reporter may not:
# - reassign bugs, unless the bugs are assigned to him;
# in that case we will have already returned 1 above
# when checking for the assignee of the bug.
if ($field eq "assigned_to") {
$PrivilegesRequired = 2;
return 0;
}
# - change the QA contact
if ($field eq "qa_contact") {
$PrivilegesRequired = 2;
return 0;
}
# - change the target milestone
if ($field eq "target_milestone") {
$PrivilegesRequired = 2;
return 0;
}
# - change the priority (unless he could have set it originally)
if ($field eq "priority"
&& !Param('letsubmitterchoosepriority'))
{
$PrivilegesRequired = 2;
return 0;
}
# The reporter is allowed to change anything else.
if ($reporterid == $whoid) {
return 1;
}
# If we haven't returned by this point, then the user doesn't
# have the necessary permissions to change this field.
$PrivilegesRequired = 1;
return 0;
}
# Confirm that the reporter of the current bug can access the bug we are duping to. # Confirm that the reporter of the current bug can access the bug we are duping to.
sub DuplicateUserConfirm { sub DuplicateUserConfirm {
# if we've already been through here, then exit # if we've already been through here, then exit
...@@ -775,7 +614,7 @@ sub DoComma { ...@@ -775,7 +614,7 @@ sub DoComma {
# confirming the bug or not. # confirming the bug or not.
my $everconfirmed; my $everconfirmed;
sub DoConfirm { sub DoConfirm {
if (CheckCanChangeField("canconfirm", scalar $cgi->param('id'), 0, 1)) { if ($bug->check_can_change_field("canconfirm", 0, 1, \$PrivilegesRequired)) {
DoComma(); DoComma();
$::query .= "everconfirmed = 1"; $::query .= "everconfirmed = 1";
$everconfirmed = 1; $everconfirmed = 1;
...@@ -860,7 +699,9 @@ sub ChangeResolution { ...@@ -860,7 +699,9 @@ sub ChangeResolution {
$dbh->selectrow_array('SELECT resolution FROM bugs WHERE bug_id = ?', $dbh->selectrow_array('SELECT resolution FROM bugs WHERE bug_id = ?',
undef, $bug_id); undef, $bug_id);
} }
unless (CheckCanChangeField('resolution', $bug_id, $old_resolution, $str)) { unless ($bug->check_can_change_field('resolution', $old_resolution, $str,
\$PrivilegesRequired))
{
$vars->{'oldvalue'} = $old_resolution; $vars->{'oldvalue'} = $old_resolution;
$vars->{'newvalue'} = $str; $vars->{'newvalue'} = $str;
$vars->{'field'} = 'resolution'; $vars->{'field'} = 'resolution';
...@@ -873,7 +714,7 @@ sub ChangeResolution { ...@@ -873,7 +714,7 @@ sub ChangeResolution {
trick_taint($str); trick_taint($str);
push(@values, $str); push(@values, $str);
# We define this variable here so that customized installations # We define this variable here so that customized installations
# may set rules based on the resolution in CheckCanChangeField. # may set rules based on the resolution in Bug::check_can_change_field().
$cgi->param('resolution', $str); $cgi->param('resolution', $str);
} }
} }
...@@ -1541,7 +1382,8 @@ foreach my $id (@idlist) { ...@@ -1541,7 +1382,8 @@ foreach my $id (@idlist) {
$oldvalues[$i] = defined($oldvalues[$i]) ? $oldvalues[$i] : ''; $oldvalues[$i] = defined($oldvalues[$i]) ? $oldvalues[$i] : '';
# Convert the deadline taken from the DB into the YYYY-MM-DD format # Convert the deadline taken from the DB into the YYYY-MM-DD format
# for consistency with the deadline provided by the user, if any. # for consistency with the deadline provided by the user, if any.
# Else CheckCanChangeField() would see them as different in all cases. # Else Bug::check_can_change_field() would see them as different
# in all cases.
if ($col eq 'deadline') { if ($col eq 'deadline') {
$oldvalues[$i] = format_time($oldvalues[$i], "%Y-%m-%d"); $oldvalues[$i] = format_time($oldvalues[$i], "%Y-%m-%d");
} }
...@@ -1562,12 +1404,18 @@ foreach my $id (@idlist) { ...@@ -1562,12 +1404,18 @@ foreach my $id (@idlist) {
$formhash{'bug_status'} = $oldhash{'bug_status'}; $formhash{'bug_status'} = $oldhash{'bug_status'};
} }
} }
# This hash is required by Bug::check_can_change_field().
my $cgi_hash = {
'dontchange' => scalar $cgi->param('dontchange'),
'knob' => scalar $cgi->param('knob')
};
foreach my $col (@editable_bug_fields) { foreach my $col (@editable_bug_fields) {
# The 'resolution' field is checked by ChangeResolution(), # The 'resolution' field is checked by ChangeResolution(),
# i.e. only if we effectively use it. # i.e. only if we effectively use it.
next if ($col eq 'resolution'); next if ($col eq 'resolution');
if (exists $formhash{$col} if (exists $formhash{$col}
&& !CheckCanChangeField($col, $id, $oldhash{$col}, $formhash{$col})) && !$old_bug_obj->check_can_change_field($col, $oldhash{$col}, $formhash{$col},
\$PrivilegesRequired, $cgi_hash))
{ {
my $vars; my $vars;
if ($col eq 'component_id') { if ($col eq 'component_id') {
...@@ -1592,14 +1440,15 @@ foreach my $id (@idlist) { ...@@ -1592,14 +1440,15 @@ foreach my $id (@idlist) {
# When editing multiple bugs, users can specify a list of keywords to delete # When editing multiple bugs, users can specify a list of keywords to delete
# from bugs. If the list matches the current set of keywords on those bugs, # from bugs. If the list matches the current set of keywords on those bugs,
# CheckCanChangeField above will fail to check permissions because it thinks # Bug::check_can_change_field will fail to check permissions because it thinks
# the list hasn't changed. To fix that, we have to call CheckCanChangeField # the list hasn't changed. To fix that, we have to call Bug::check_can_change_field
# again with old!=new if the keyword action is "delete" and old=new. # again with old!=new if the keyword action is "delete" and old=new.
if ($keywordaction eq "delete" if ($keywordaction eq "delete"
&& defined $cgi->param('keywords') && defined $cgi->param('keywords')
&& length(@keywordlist) > 0 && length(@keywordlist) > 0
&& $cgi->param('keywords') eq $oldhash{keywords} && $cgi->param('keywords') eq $oldhash{keywords}
&& !CheckCanChangeField("keywords", $id, "old is not", "equal to new")) && !$old_bug_obj->check_can_change_field("keywords", "old is not", "equal to new",
\$PrivilegesRequired))
{ {
$vars->{'oldvalue'} = $oldhash{keywords}; $vars->{'oldvalue'} = $oldhash{keywords};
$vars->{'newvalue'} = "no keywords"; $vars->{'newvalue'} = "no keywords";
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment