summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgerv%gerv.net <>2002-09-19 08:23:52 +0200
committergerv%gerv.net <>2002-09-19 08:23:52 +0200
commit6fa5c6eccf660b60ff65e5b46e0c66968b19351c (patch)
tree5fd8fb23d2a68f33dd7dc4f0d9ab7022f288eebd
parent02b923a6ecbe1ad51bde68cbaa8f0c869eda12e2 (diff)
downloadbugzilla-6fa5c6eccf660b60ff65e5b46e0c66968b19351c.tar.gz
bugzilla-6fa5c6eccf660b60ff65e5b46e0c66968b19351c.tar.xz
Bug 168804 - Document CheckCanChangeField so sites can modify it for local needs. Patch by gerv; r=bbaetz, joel.
-rw-r--r--docs/sgml/administration.sgml94
-rw-r--r--docs/xml/administration.xml94
-rwxr-xr-xprocess_bug.cgi152
3 files changed, 300 insertions, 40 deletions
diff --git a/docs/sgml/administration.sgml b/docs/sgml/administration.sgml
index f932beb25..a82a659bf 100644
--- a/docs/sgml/administration.sgml
+++ b/docs/sgml/administration.sgml
@@ -1176,6 +1176,100 @@
</section>
+ <section id="cust-change-permissions">
+ <title>Change Permission Customisation</title>
+
+ <warning>
+ <para>
+ This feature should be considered experimental; the Bugzilla code you
+ will be changing is not stable, and could change or move between
+ versions. Be aware that if you make modifications to it, you may have
+ to re-make them or port them if Bugzilla changes internally between
+ versions.
+ </para>
+ </warning>
+
+ <para>
+ Companies often have rules about which employees, or classes of employees,
+ are allowed to change certain things in the bug system. For example,
+ only the bug's designated QA Contact may be allowed to VERIFY the bug.
+ Bugzilla has been
+ designed to make it easy for you to write your own custom rules to define
+ who is allowed to make what sorts of value transition.
+ </para>
+
+ <para>
+ For maximum flexibility, customising this means editing Bugzilla's Perl
+ code. This gives the administrator complete control over exactly who is
+ allowed to do what. The relevant function is called
+ <filename>CheckCanChangeField()</filename>,
+ and is found in <filename>process_bug.cgi</filename> in your
+ Bugzilla directory. If you open that file and grep for
+ "sub CheckCanChangeField", you'll find it.
+ </para>
+
+ <para>
+ This function has been carefully commented to allow you to see exactly
+ how it works, and give you an idea of how to make changes to it. Certain
+ marked sections should not be changed - these are the "plumbing" which
+ makes the rest of the function work. In between those sections, you'll
+ find snippets of code like:
+ <programlisting> # Allow the owner to change anything.
+ if ($ownerid eq $whoid) {
+ return 1;
+ }</programlisting>
+ It's fairly obvious what this piece of code does.
+ </para>
+
+ <para>
+ So, how does one go about changing this function? Well, simple changes
+ can be made just be removing pieces - for example, if you wanted to
+ prevent any user adding a comment to a bug, just remove the lines marked
+ "Allow anyone to change comments." And if you want the reporter to have
+ no special rights on bugs they have filed, just remove the entire section
+ which refers to him.
+ </para>
+
+ <para>
+ More complex customisations are not much harder. Basically, you add
+ a check in the right place in the function, i.e. after all the variables
+ you are using have been set up. So, don't look at $ownerid before
+ $ownerid has been obtained from the database. You can either add a
+ positive check, which returns 1 (allow) if certain conditions are true,
+ or a negative check, which returns 0 (deny.) E.g.:
+ <programlisting> if ($field eq "qacontact") {
+ if (UserInGroup("quality_assurance")) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+ }</programlisting>
+ This says that only users in the group "quality_assurance" can change
+ the QA Contact field of a bug. Getting more weird:
+ <programlisting> if (($field eq "priority") &&
+ ($vars->{'user'}{'login'} =~ /.*\@example\.com$/))
+ {
+ if ($oldvalue eq "P1") {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+ }</programlisting>
+ This says that if the user is trying to change the priority field,
+ and their email address is @example.com, they can only do so if the
+ old value of the field was "P1". Not very useful, but illustrative.
+ </para>
+
+ <para>
+ For a list of possible field names, look in
+ <filename>data/versioncache</filename> for the list called
+ <filename>@::log_columns</filename>. If you need help writing custom
+ rules for your organisation, ask in the newsgroup.
+ </para>
+ </section>
+
<section id="upgrading">
<title>Upgrading to New Releases</title>
diff --git a/docs/xml/administration.xml b/docs/xml/administration.xml
index f932beb25..a82a659bf 100644
--- a/docs/xml/administration.xml
+++ b/docs/xml/administration.xml
@@ -1176,6 +1176,100 @@
</section>
+ <section id="cust-change-permissions">
+ <title>Change Permission Customisation</title>
+
+ <warning>
+ <para>
+ This feature should be considered experimental; the Bugzilla code you
+ will be changing is not stable, and could change or move between
+ versions. Be aware that if you make modifications to it, you may have
+ to re-make them or port them if Bugzilla changes internally between
+ versions.
+ </para>
+ </warning>
+
+ <para>
+ Companies often have rules about which employees, or classes of employees,
+ are allowed to change certain things in the bug system. For example,
+ only the bug's designated QA Contact may be allowed to VERIFY the bug.
+ Bugzilla has been
+ designed to make it easy for you to write your own custom rules to define
+ who is allowed to make what sorts of value transition.
+ </para>
+
+ <para>
+ For maximum flexibility, customising this means editing Bugzilla's Perl
+ code. This gives the administrator complete control over exactly who is
+ allowed to do what. The relevant function is called
+ <filename>CheckCanChangeField()</filename>,
+ and is found in <filename>process_bug.cgi</filename> in your
+ Bugzilla directory. If you open that file and grep for
+ "sub CheckCanChangeField", you'll find it.
+ </para>
+
+ <para>
+ This function has been carefully commented to allow you to see exactly
+ how it works, and give you an idea of how to make changes to it. Certain
+ marked sections should not be changed - these are the "plumbing" which
+ makes the rest of the function work. In between those sections, you'll
+ find snippets of code like:
+ <programlisting> # Allow the owner to change anything.
+ if ($ownerid eq $whoid) {
+ return 1;
+ }</programlisting>
+ It's fairly obvious what this piece of code does.
+ </para>
+
+ <para>
+ So, how does one go about changing this function? Well, simple changes
+ can be made just be removing pieces - for example, if you wanted to
+ prevent any user adding a comment to a bug, just remove the lines marked
+ "Allow anyone to change comments." And if you want the reporter to have
+ no special rights on bugs they have filed, just remove the entire section
+ which refers to him.
+ </para>
+
+ <para>
+ More complex customisations are not much harder. Basically, you add
+ a check in the right place in the function, i.e. after all the variables
+ you are using have been set up. So, don't look at $ownerid before
+ $ownerid has been obtained from the database. You can either add a
+ positive check, which returns 1 (allow) if certain conditions are true,
+ or a negative check, which returns 0 (deny.) E.g.:
+ <programlisting> if ($field eq "qacontact") {
+ if (UserInGroup("quality_assurance")) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+ }</programlisting>
+ This says that only users in the group "quality_assurance" can change
+ the QA Contact field of a bug. Getting more weird:
+ <programlisting> if (($field eq "priority") &&
+ ($vars->{'user'}{'login'} =~ /.*\@example\.com$/))
+ {
+ if ($oldvalue eq "P1") {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+ }</programlisting>
+ This says that if the user is trying to change the priority field,
+ and their email address is @example.com, they can only do so if the
+ old value of the field was "P1". Not very useful, but illustrative.
+ </para>
+
+ <para>
+ For a list of possible field names, look in
+ <filename>data/versioncache</filename> for the list called
+ <filename>@::log_columns</filename>. If you need help writing custom
+ rules for your organisation, ask in the newsgroup.
+ </para>
+ </section>
+
<section id="upgrading">
<title>Upgrading to New Releases</title>
diff --git a/process_bug.cgi b/process_bug.cgi
index ab65c0da5..be661c629 100755
--- a/process_bug.cgi
+++ b/process_bug.cgi
@@ -260,9 +260,34 @@ 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.
+#
+# 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 {
- my ($f, $bugid, $oldvalue, $newvalue) = (@_);
- if ($f eq "assigned_to" || $f eq "reporter" || $f eq "qa_contact") {
+ # START DO_NOT_CHANGE
+ my ($field, $bugid, $oldvalue, $newvalue) = (@_);
+
+ # Convert email IDs into addresses for $oldvalue
+ if (($field eq "assigned_to") ||
+ ($field eq "reporter") ||
+ ($field eq "qa_contact"))
+ {
if ($oldvalue =~ /^\d+$/) {
if ($oldvalue == 0) {
$oldvalue = "";
@@ -271,64 +296,106 @@ sub CheckCanChangeField {
}
}
}
+
+ # Return true if they haven't changed this field at all.
if ($oldvalue eq $newvalue) {
return 1;
- }
- if (trim($oldvalue) eq trim($newvalue)) {
+ }
+ elsif (trim($oldvalue) eq trim($newvalue)) {
return 1;
}
- if ($f =~ /^longdesc/) {
- return 1;
+
+ # A resolution change is always accompanied by a status change. So, we
+ # always OK resolution changes; if they really can't do this, we will
+ # notice it when status is checked.
+ if ($field eq "resolution") {
+ return 1;
}
- if ($f eq "resolution") { # always OK this. if they really can't,
- return 1; # it'll flag it when "status" is checked.
+ # END DO_NOT_CHANGE
+
+ # Allow anyone to change comments.
+ if ($field =~ /^longdesc/) {
+ 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
+
+ # Allow anyone with "editbugs" to change anything.
if ($UserInEditGroupSet) {
return 1;
}
+
+ # Allow anyone with "canconfirm" to confirm bugs.
+ if (($field eq "bug_status") &&
+ ($oldvalue eq $::unconfirmedstate) &&
+ IsOpenedState($newvalue) &&
+ $UserInCanConfirmGroupSet)
+ {
+ return 1;
+ }
+
+ # START DO_NOT_CHANGE
+ # $reporterid, $ownerid and $qacontactid are caches of the results of
+ # the call to find out the owner, reporter and qacontact of the current bug.
if ($lastbugid != $bugid) {
- SendSQL("SELECT reporter, assigned_to, qa_contact FROM bugs " .
- "WHERE bug_id = $bugid");
+ SendSQL("SELECT reporter, assigned_to, qa_contact FROM bugs
+ WHERE bug_id = $bugid");
($reporterid, $ownerid, $qacontactid) = (FetchSQLData());
+ }
+ # END DO_NOT_CHANGE
+
+ # Allow the owner to change anything.
+ if ($ownerid eq $whoid) {
+ return 1;
}
- # Let reporter change bug status, even if they can't edit bugs.
- # If reporter can't re-open their bug they will just file a duplicate.
- # While we're at it, let them close their own bugs as well.
- if ( ($f eq "bug_status") && ($whoid eq $reporterid) ) {
+
+ # Allow the QA contact to change anything.
+ if ($qacontactid eq $whoid) {
return 1;
}
- if ($f eq "bug_status" && $newvalue ne $::unconfirmedstate &&
- IsOpenedState($newvalue)) {
-
- # Hmm. They are trying to set this bug to some opened state
- # that isn't the UNCONFIRMED state. Are they in the right
- # group? Or, has it ever been confirmed? If not, then this
- # isn't legal.
-
- if ($UserInCanConfirmGroupSet < 0) {
- $UserInCanConfirmGroupSet = UserInGroup("canconfirm");
- }
- if ($UserInCanConfirmGroupSet) {
- return 1;
+
+ # The reporter's a more complicated case...
+ if ($reporterid eq $whoid) {
+ # Reporter may not:
+ # - confirm his own bugs (this assumes he doesn't have canconfirm, or we
+ # would have returned "1" earlier)
+ if (($field eq "bug_status") &&
+ ($oldvalue eq $::unconfirmedstate) &&
+ IsOpenedState($newvalue))
+ {
+ return 0;
}
- SendSQL("SELECT everconfirmed FROM bugs WHERE bug_id = $bugid");
- my $everconfirmed = FetchOneColumn();
- if ($everconfirmed) {
- return 1;
+
+ # - change the target milestone
+ if ($field eq "target_milestone") {
+ return 0;
+ }
+
+ # - change the priority (unless he could have set it originally)
+ if (($field eq "priority") &&
+ !Param('letsubmitterchoosepriority'))
+ {
+ return 0;
}
- } elsif ($reporterid eq $whoid || $ownerid eq $whoid ||
- $qacontactid eq $whoid) {
+
+ # Allow reporter to change anything else.
return 1;
}
-
- # The user doesn't have the necessary permissions to change this field.
- $vars->{'oldvalue'} = $oldvalue;
- $vars->{'newvalue'} = $newvalue;
- $vars->{'field'} = $f;
- ThrowUserError("illegal_change", "abort");
+
+ # If we haven't returned by this point, then the user doesn't have the
+ # necessary permissions to change this field.
+ return 0;
}
# Confirm that the reporter of the current bug can access the bug we are duping to.
@@ -989,7 +1056,12 @@ foreach my $id (@idlist) {
$oldvalues[$i] ||= '';
$oldhash{$col} = $oldvalues[$i];
if (exists $::FORM{$col}) {
- CheckCanChangeField($col, $id, $oldvalues[$i], $::FORM{$col});
+ if (!CheckCanChangeField($col, $id, $oldvalues[$i], $::FORM{$col})) {
+ $vars->{'oldvalue'} = $oldvalues[$i];
+ $vars->{'newvalue'} = $::FORM{$col};
+ $vars->{'field'} = $col;
+ ThrowUserError("illegal_change", "abort");
+ }
}
$i++;
}