diff --git a/Bugzilla/Bug.pm b/Bugzilla/Bug.pm index 2cac77ed34e7a11bb4c8149e76990e53a2c76316..01d2321c40e6ca389f806ea475fecda5e915bca4 100755 --- a/Bugzilla/Bug.pm +++ b/Bugzilla/Bug.pm @@ -133,7 +133,7 @@ sub initBug { } } - $self->{'whoid'} = $user_id; + $self->{'who'} = new Bugzilla::User($user_id); my $query = " SELECT @@ -156,7 +156,7 @@ sub initBug { &::SendSQL($query); my @row = (); - if ((@row = &::FetchSQLData()) && &::CanSeeBug($bug_id, $self->{'whoid'})) { + if ((@row = &::FetchSQLData()) && $self->{'who'}->can_see_bug($bug_id)) { my $count = 0; my %fields; foreach my $field ("bug_id", "alias", "product_id", "product", "version", diff --git a/Bugzilla/BugMail.pm b/Bugzilla/BugMail.pm index 40a40dc2beb49a8488930932b0c3b157c5c7f361..8731a9f7243aea21b7311cf326c5412c2387ea86 100644 --- a/Bugzilla/BugMail.pm +++ b/Bugzilla/BugMail.pm @@ -720,7 +720,7 @@ sub NewProcessOnePerson ($$$$$$$$$$$$$) { # see the action of restricting the bug itself; the bug will just # quietly disappear from their radar. # - return unless CanSeeBug($id, $userid); + return unless $user->can_see_bug($id); # Drop any non-insiders if the comment is private return if (Param("insidergroup") && @@ -733,7 +733,7 @@ sub NewProcessOnePerson ($$$$$$$$$$$$$) { my $save_id = $dep_id; detaint_natural($dep_id) || warn("Unexpected Error: \@depbugs contains a non-numeric value: '$save_id'") && return; - return unless CanSeeBug($dep_id, $userid); + return unless $user->can_see_bug($dep_id); } my %mailhead = %defmailhead; diff --git a/Bugzilla/Flag.pm b/Bugzilla/Flag.pm index d4dac90532b08513c2b28b6871f61e46b0b0a187..3b2ae36c483056195c5d8b499503da952f96e8cc 100644 --- a/Bugzilla/Flag.pm +++ b/Bugzilla/Flag.pm @@ -185,7 +185,7 @@ sub validate { my $requestee = Bugzilla::User->new_from_login($requestee_email); # Throw an error if the user can't see the bug. - if (!&::CanSeeBug($bug_id, $requestee->id)) + if (!$requestee->can_see_bug($bug_id)) { ThrowUserError("flag_requestee_unauthorized", { flag_type => $flag->{'type'}, @@ -592,7 +592,7 @@ sub notify { || next; next if $flag->{'target'}->{'bug'}->{'restricted'} - && !&::CanSeeBug($flag->{'target'}->{'bug'}->{'id'}, $ccuser->id); + && !$ccuser->can_see_bug($flag->{'target'}->{'bug'}->{'id'}); next if $flag->{'target'}->{'attachment'}->{'isprivate'} && Param("insidergroup") && !$ccuser->in_group(Param("insidergroup")); diff --git a/Bugzilla/FlagType.pm b/Bugzilla/FlagType.pm index f1cb00c5da098576a59742583bfc7ef03b5cb5c2..687a01768eea4bd5702bc0ed1665b7dc7fa8cd00 100644 --- a/Bugzilla/FlagType.pm +++ b/Bugzilla/FlagType.pm @@ -226,7 +226,7 @@ sub validate { my $requestee = Bugzilla::User->new_from_login($requestee_email); # Throw an error if the user can't see the bug. - if (!&::CanSeeBug($bug_id, $requestee->id)) + if (!$requestee->can_see_bug($bug_id)) { ThrowUserError("flag_requestee_unauthorized", { flag_type => $flag_type, diff --git a/Bugzilla/User.pm b/Bugzilla/User.pm index 8396d183f28a95b2c9e54fd565b1ee94ce62306e..66087b81c941362a44b5f925d15ec9c066e4a042 100644 --- a/Bugzilla/User.pm +++ b/Bugzilla/User.pm @@ -244,6 +244,75 @@ sub in_group { return defined($res); } +sub can_see_bug { + my ($self, $bugid) = @_; + my $dbh = Bugzilla->dbh; + my $sth = $self->{sthCanSeeBug}; + my $userid = $self->{id}; + # Get fields from bug, presence of user on cclist, and determine if + # the user is missing any groups required by the bug. The prepared query + # is cached because this may be called for every row in buglists or + # every bug in a dependency list. + unless ($sth) { + $sth = $dbh->prepare("SELECT reporter, assigned_to, qa_contact, + reporter_accessible, cclist_accessible, + COUNT(cc.who), COUNT(bug_group_map.bug_id) + FROM bugs + LEFT JOIN cc + ON cc.bug_id = bugs.bug_id + AND cc.who = $userid + LEFT JOIN bug_group_map + ON bugs.bug_id = bug_group_map.bug_id + AND bug_group_map.group_ID NOT IN(" . + join(',',(-1, values(%{$self->groups}))) . + ") WHERE bugs.bug_id = ? GROUP BY bugs.bug_id"); + } + $sth->execute($bugid); + my ($reporter, $owner, $qacontact, $reporter_access, $cclist_access, + $isoncclist, $missinggroup) = $sth->fetchrow_array(); + $self->{sthCanSeeBug} = $sth; + return ( (($reporter == $userid) && $reporter_access) + || (Param('qacontact') && ($qacontact == $userid) && $userid) + || ($owner == $userid) + || ($isoncclist && $cclist_access) + || (!$missinggroup) ); +} + +sub get_selectable_products { + my ($self, $by_id) = @_; + + if (defined $self->{SelectableProducts}) { + my %list = @{$self->{SelectableProducts}}; + return \%list if $by_id; + return values(%list); + } + + my $query = "SELECT id, name " . + "FROM products " . + "LEFT JOIN group_control_map " . + "ON group_control_map.product_id = products.id "; + if (Param('useentrygroupdefault')) { + $query .= "AND group_control_map.entry != 0 "; + } else { + $query .= "AND group_control_map.membercontrol = " . + CONTROLMAPMANDATORY . " "; + } + $query .= "AND group_id NOT IN(" . + join(',', (-1,values(%{Bugzilla->user->groups}))) . ") " . + "WHERE group_id IS NULL ORDER BY name"; + my $dbh = Bugzilla->dbh; + my $sth = $dbh->prepare($query); + $sth->execute(); + my @products = (); + while (my @row = $sth->fetchrow_array) { + push(@products, @row); + } + $self->{SelectableProducts} = \@products; + my %list = @products; + return \%list if $by_id; + return values(%list); +} + # visible_groups_inherited returns a reference to a list of all the groups # whose members are visible to this user. sub visible_groups_inherited { @@ -939,6 +1008,10 @@ intended for cases where we are not looking at the currently logged in user, and only need to make a quick check for the group, where calling C<groups> and getting all of the groups would be overkill. +=item C<can_see_bug(bug_id)> + +Determines if the user can see the specified bug. + =item C<derive_groups> Bugzilla allows for group inheritance. When data about the user (or any of the @@ -947,6 +1020,13 @@ care of by the constructor. However, when updating the email address, the user may be placed into different groups, based on a new email regexp. This method should be called in such a case to force reresolution of these groups. +=item C<get_selectable_products(by_id)> + +Returns an alphabetical list of product names from which +the user can select bugs. If the $by_id parameter is true, it returns +a hash where the keys are the product ids and the values are the +product names. + =item C<visible_groups_inherited> Returns a list of all groups whose members should be visible to this user. diff --git a/CGI.pl b/CGI.pl index 5be0261d06d2d77a032348667760c088c2ac0fb1..4f5b79f72fa9c4400e0aa302c56a541268517756 100644 --- a/CGI.pl +++ b/CGI.pl @@ -172,7 +172,7 @@ sub ValidateBugID { return if $skip_authorization; - return if CanSeeBug($id, $::userid); + return if Bugzilla->user->can_see_bug($id); # The user did not pass any of the authorization tests, which means they # are not authorized to see the bug. Display an error and stop execution. diff --git a/globals.pl b/globals.pl index d1680959e84f28252c6efdcb6adb43776d85e5eb..9872dff709ede959fb0f2ba9d6c189a755ecd004 100644 --- a/globals.pl +++ b/globals.pl @@ -630,48 +630,6 @@ sub GetFieldDefs { } -sub CanSeeBug { - - my ($id, $userid) = @_; - - # Query the database for the bug, retrieving a boolean value that - # represents whether or not the user is authorized to access the bug. - - # if no groups are found --> user is permitted to access - # if no user is found for any group --> user is not permitted to access - my $query = "SELECT bugs.bug_id, reporter, assigned_to, qa_contact," . - " reporter_accessible, cclist_accessible," . - " cc.who IS NOT NULL," . - " COUNT(DISTINCT(bug_group_map.group_id)) as cntbugingroups," . - " COUNT(DISTINCT(user_group_map.group_id)) as cntuseringroups" . - " FROM bugs" . - " LEFT JOIN cc ON bugs.bug_id = cc.bug_id" . - " AND cc.who = $userid" . - " LEFT JOIN bug_group_map ON bugs.bug_id = bug_group_map.bug_id" . - " LEFT JOIN user_group_map ON" . - " user_group_map.group_id = bug_group_map.group_id" . - " AND user_group_map.isbless = 0" . - " AND user_group_map.user_id = $userid" . - " WHERE bugs.bug_id = $id GROUP BY bugs.bug_id"; - PushGlobalSQLState(); - SendSQL($query); - my ($found_id, $reporter, $assigned_to, $qa_contact, - $rep_access, $cc_access, - $found_cc, $found_groups, $found_members) - = FetchSQLData(); - PopGlobalSQLState(); - return ( - ($found_groups == 0) - || (($userid > 0) && - ( - ($assigned_to == $userid) - || (Param('useqacontact') && $qa_contact == $userid) - || (($reporter == $userid) && $rep_access) - || ($found_cc && $cc_access) - || ($found_groups == $found_members) - )) - ); -} sub ValidatePassword { # Determines whether or not a password is valid (i.e. meets Bugzilla's @@ -947,7 +905,7 @@ sub GetAttachmentLink { my ($bugid, $isobsolete, $desc) = FetchSQLData(); my $title = ""; my $className = ""; - if (CanSeeBug($bugid, $::userid)) { + if (Bugzilla->user->can_see_bug($bugid)) { $title = $desc; } if ($isobsolete) { @@ -1018,7 +976,7 @@ sub GetBugLink { $title .= " $bug_res"; $post = '</span>'; } - if (CanSeeBug($bug_num, $::userid)) { + if (Bugzilla->user->can_see_bug($bug_num)) { $title .= " - $bug_desc"; } $::buglink{$bug_num} = [$pre, value_quote($title), $post]; diff --git a/long_list.cgi b/long_list.cgi index 5644a53232d054c2cc2a6874864ba5fed1060609..757d002396cd97716027e81c7164796d0b0d3682 100755 --- a/long_list.cgi +++ b/long_list.cgi @@ -75,7 +75,7 @@ my @bugs; foreach my $bug_id (split(/[:,]/, $buglist)) { detaint_natural($bug_id) || next; - CanSeeBug($bug_id, $::userid) || next; + Bugzilla->user->can_see_bug($bug_id) || next; SendSQL("$generic_query AND bugs.bug_id = $bug_id"); my %bug; diff --git a/process_bug.cgi b/process_bug.cgi index b5d641f7740dcb5b17284d194ac276fb0cca68cf..2810d3b395bce55ed98a77e2792c5af97db3aeb8 100755 --- a/process_bug.cgi +++ b/process_bug.cgi @@ -493,8 +493,9 @@ sub DuplicateUserConfirm { SendSQL("SELECT reporter FROM bugs WHERE bug_id = " . SqlQuote($dupe)); my $reporter = FetchOneColumn(); + my $rep_user = Bugzilla::User->new($reporter); - if (CanSeeBug($original, $reporter)) { + if ($rep_user->can_see_bug($original)) { $::FORM{'confirm_add_duplicate'} = "1"; return; } @@ -1773,7 +1774,7 @@ foreach my $id (@idlist) { # now show the next bug if ($next_bug) { - if (detaint_natural($next_bug) && CanSeeBug($next_bug, $::userid)) { + if (detaint_natural($next_bug) && Bugzilla->user->can_see_bug($next_bug)) { my $bug = new Bugzilla::Bug($next_bug, $::userid); ThrowCodeError("bug_error", { bug => $bug }) if $bug->error; diff --git a/showdependencygraph.cgi b/showdependencygraph.cgi index b11562e1e421dd234b915218a8806eabcfae3c46..0d33d316d6275a76518a8f2d9c326f92040472cc 100755 --- a/showdependencygraph.cgi +++ b/showdependencygraph.cgi @@ -170,7 +170,7 @@ foreach my $k (keys(%seen)) { $summary ||= ''; # Resolution and summary are shown only if user can see the bug - if (!CanSeeBug($k, $::userid)) { + if (!Bugzilla->user->can_see_bug($k)) { $resolution = $summary = ''; } diff --git a/showdependencytree.cgi b/showdependencytree.cgi index 202043acdc538140cc4bb21f5609a3cc98cb7e63..f1a495a6d434a99f73bcab14f93772937fcd973c 100755 --- a/showdependencytree.cgi +++ b/showdependencytree.cgi @@ -146,7 +146,7 @@ sub GetBug { my ($id) = @_; my $bug = {}; - if (CanSeeBug($id, $::userid)) { + if (Bugzilla->user->can_see_bug($id)) { SendSQL("SELECT 1, bug_status, short_desc, diff --git a/votes.cgi b/votes.cgi index 8bd007d602ac89c62d8870ab9b5e713df9e101c4..f2590b3244a681cbfdbf03e8b69ab03b794ca048 100755 --- a/votes.cgi +++ b/votes.cgi @@ -185,7 +185,7 @@ sub show_user { # and they can see there are votes 'missing', but not on what bug # they are. This seems a reasonable compromise; the alternative is # to lie in the totals. - next if !CanSeeBug($id, $userid); + next if !Bugzilla->user->can_see_bug($id); push (@bugs, { id => $id, summary => $summary,