From a68b0d94e176d37e1381f38f235f760ca0a1001d Mon Sep 17 00:00:00 2001 From: dklawren Date: Wed, 29 Aug 2018 10:40:06 -0400 Subject: Bug 1486369 - please enable "due date" for Firefox -> Security: Review Request compoent --- extensions/BMO/lib/Data.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/BMO/lib/Data.pm b/extensions/BMO/lib/Data.pm index 30a32c95e..b444d6a24 100644 --- a/extensions/BMO/lib/Data.pm +++ b/extensions/BMO/lib/Data.pm @@ -140,6 +140,7 @@ tie(%$cf_visible_in_products, "Tie::IxHash", "Data & BI Services Team" => [], "Data Compliance" => [], "Developer Engagement" => [], + "Firefox" => ["Security: Review Requests"], "Infrastructure & Operations" => [], "Marketing" => [], "mozilla.org" => ["Security Assurance: Review Request"], -- cgit v1.2.3-24-g4f1b From 2e6e3e13587ee526ba94faabd5551e67508518f5 Mon Sep 17 00:00:00 2001 From: dklawren Date: Fri, 7 Sep 2018 06:04:50 -0400 Subject: Bug 1488292 - Remove MozReview extension from BMO code tree as MozReview is being decommissioned --- Bugzilla/CGI.pm | 6 - Makefile.PL | 2 +- extensions/BMO/lib/Data.pm | 21 -- extensions/MozReview/Config.pm | 16 -- extensions/MozReview/Extension.pm | 132 ----------- extensions/MozReview/bin/add-mozreview-children.pl | 257 --------------------- extensions/MozReview/lib/Config.pm | 55 ----- extensions/MozReview/lib/WebService.pm | 209 ----------------- .../en/default/admin/params/mozreview.html.tmpl | 20 -- .../hook/attachment/create-before_form.html.tmpl | 17 -- .../en/default/hook/attachment/edit-view.html.tmpl | 16 -- .../default/hook/bug/edit-after_bug_data.html.tmpl | 35 --- .../en/default/hook/bug/show-header-end.html.tmpl | 13 -- .../default/hook/bug_modal/edit-module.html.tmpl | 21 -- .../en/default/hook/bug_modal/header-end.html.tmpl | 13 -- .../en/default/hook/global/header-start.html.tmpl | 11 - .../hook/global/user-error-errors.html.tmpl | 15 -- .../en/default/moz_review/header.html.tmpl | 10 - .../template/en/default/moz_review/table.html.tmpl | 28 --- extensions/MozReview/web/js/mozreview.js | 134 ----------- extensions/MozReview/web/style/attachment.css | 13 -- extensions/MozReview/web/style/mozreview.css | 79 ------- extensions/Push/lib/Connector/ReviewBoard.pm | 178 -------------- extensions/Push/t/ReviewBoard.t | 225 ------------------ scripts/issue-api-key.pl | 4 - template/en/default/account/prefs/apikey.html.tmpl | 18 +- template/en/default/attachment/create.html.tmpl | 1 - template/en/default/bug/edit.html.tmpl | 195 ++++++++-------- template/en/default/email/new-api-key.txt.tmpl | 5 +- 29 files changed, 103 insertions(+), 1646 deletions(-) delete mode 100644 extensions/MozReview/Config.pm delete mode 100644 extensions/MozReview/Extension.pm delete mode 100755 extensions/MozReview/bin/add-mozreview-children.pl delete mode 100644 extensions/MozReview/lib/Config.pm delete mode 100644 extensions/MozReview/lib/WebService.pm delete mode 100644 extensions/MozReview/template/en/default/admin/params/mozreview.html.tmpl delete mode 100644 extensions/MozReview/template/en/default/hook/attachment/create-before_form.html.tmpl delete mode 100644 extensions/MozReview/template/en/default/hook/attachment/edit-view.html.tmpl delete mode 100644 extensions/MozReview/template/en/default/hook/bug/edit-after_bug_data.html.tmpl delete mode 100644 extensions/MozReview/template/en/default/hook/bug/show-header-end.html.tmpl delete mode 100644 extensions/MozReview/template/en/default/hook/bug_modal/edit-module.html.tmpl delete mode 100644 extensions/MozReview/template/en/default/hook/bug_modal/header-end.html.tmpl delete mode 100644 extensions/MozReview/template/en/default/hook/global/header-start.html.tmpl delete mode 100644 extensions/MozReview/template/en/default/hook/global/user-error-errors.html.tmpl delete mode 100644 extensions/MozReview/template/en/default/moz_review/header.html.tmpl delete mode 100644 extensions/MozReview/template/en/default/moz_review/table.html.tmpl delete mode 100644 extensions/MozReview/web/js/mozreview.js delete mode 100644 extensions/MozReview/web/style/attachment.css delete mode 100644 extensions/MozReview/web/style/mozreview.css delete mode 100644 extensions/Push/lib/Connector/ReviewBoard.pm delete mode 100644 extensions/Push/t/ReviewBoard.t diff --git a/Bugzilla/CGI.pm b/Bugzilla/CGI.pm index 6236b015a..985e504f7 100644 --- a/Bugzilla/CGI.pm +++ b/Bugzilla/CGI.pm @@ -89,12 +89,6 @@ sub SHOW_BUG_MODAL_CSP { push @{ $policy{img_src} }, $attach_base; } - # MozReview API calls - my $mozreview_url = Bugzilla->params->{mozreview_base_url}; - if ($mozreview_url) { - push @{ $policy{connect_src} }, $mozreview_url . 'api/extensions/mozreview.extension.MozReviewExtension/summary/'; - } - return %policy; } diff --git a/Makefile.PL b/Makefile.PL index 3bf6926a5..3c600bef6 100755 --- a/Makefile.PL +++ b/Makefile.PL @@ -323,7 +323,7 @@ my %optional_features = ( } }, linux_pid => { - description => 'Linux::PID for MozReview', + description => 'Linux::PID', prereqs => { runtime => { requires => { 'Linux::Pid' => 0 }, diff --git a/extensions/BMO/lib/Data.pm b/extensions/BMO/lib/Data.pm index b444d6a24..dc6a3894f 100644 --- a/extensions/BMO/lib/Data.pm +++ b/extensions/BMO/lib/Data.pm @@ -27,21 +27,6 @@ our @EXPORT = qw( $cf_visible_in_products # will result in the user being redirected to that URL when viewing the # attachment. -my $mozreview_url_re = qr{ - # begins with mozreview hostname - ^ - https?://reviewboard(?:-dev)?\.(?:allizom|mozilla)\.org - - # followed by a review path - /r/\d+ - - # ends with optional suffix - (?: / - | /diff/\#index_header - )? - $ -}ix; - sub phabricator_url_re { my $phab_uri = Bugzilla->params->{phabricator_base_uri} || 'https://example.com'; return qr/^\Q${phab_uri}\ED\d+$/i; @@ -54,12 +39,6 @@ our %autodetect_attach_urls = ( content_type => 'text/x-github-pull-request', can_review => 1, }, - reviewboard => { - title => 'MozReview', - regex => $mozreview_url_re, - content_type => 'text/x-review-board-request', - can_review => 1, - }, Phabricator => { title => 'Phabricator', regex => \&phabricator_url_re, diff --git a/extensions/MozReview/Config.pm b/extensions/MozReview/Config.pm deleted file mode 100644 index 34d98907c..000000000 --- a/extensions/MozReview/Config.pm +++ /dev/null @@ -1,16 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. - -package Bugzilla::Extension::MozReview; - -use 5.10.1; -use strict; -use warnings; - -use constant NAME => 'MozReview'; - -__PACKAGE__->NAME; diff --git a/extensions/MozReview/Extension.pm b/extensions/MozReview/Extension.pm deleted file mode 100644 index 907f12e56..000000000 --- a/extensions/MozReview/Extension.pm +++ /dev/null @@ -1,132 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. - -package Bugzilla::Extension::MozReview; - -use 5.10.1; -use strict; -use warnings; -use parent qw(Bugzilla::Extension); - -use Bugzilla::Attachment; -use Bugzilla::Error; -use Bugzilla::Extension::MozReview::WebService; -use List::MoreUtils qw( any ); - -our $VERSION = '0.01'; - -my @METHOD_WHITELIST = ( - 'User.get', - 'User.login', - 'User.valid_login', - 'Bug.add_comment', - 'Bug.add_attachment', - 'Bug.attachments', - 'Bug.get', - 'Bug.update_attachment', -); - -BEGIN { - package Bugzilla::User::APIKey; - - sub is_mozreview { - my ($self) = @_; - my $mozreview_app_id = Bugzilla->params->{mozreview_app_id}; - return 0 unless $mozreview_app_id; - - return 1 if $self->app_id && $self->app_id eq $mozreview_app_id; - } -} - -sub template_before_process { - my ($self, $args) = @_; - my $file = $args->{'file'}; - my $vars = $args->{'vars'}; - - return unless (($file =~ /bug\/(show-header|edit).html.tmpl$/ || - $file =~ /bug_modal\/(header|edit).html.tmpl$/ || - $file eq 'attachment/create.html.tmpl') && - Bugzilla->params->{mozreview_base_url}); - - my $bug = exists $vars->{'bugs'} ? $vars->{'bugs'}[0] : $vars->{'bug'}; - - if ($bug) { - if ($file eq 'attachment/create.html.tmpl') { - if ($bug->product eq 'Core' || $bug->product eq 'Firefox' || - $bug->product eq 'Firefox for Android') { - $vars->{'mozreview_enabled'} = 1; - } - } else { - my $has_mozreview = 0; - my $attachments = Bugzilla::Attachment->get_attachments_by_bug($bug); - - foreach my $attachment (@$attachments) { - if ($attachment->contenttype eq 'text/x-review-board-request' - && !$attachment->isobsolete) { - $has_mozreview = 1; - last; - } - } - - if ($has_mozreview) { - $vars->{'mozreview'} = 1; - } - } - } -} - -sub auth_delegation_confirm { - my ($self, $args) = @_; - my $mozreview_callback_url = Bugzilla->params->{mozreview_auth_callback_url}; - my $mozreview_app_id = Bugzilla->params->{mozreview_app_id}; - - return unless $mozreview_callback_url; - return unless $mozreview_app_id; - - if (index($args->{callback}, $mozreview_callback_url) == 0 && $args->{app_id} eq $mozreview_app_id) { - ${$args->{skip_confirmation}} = 1; - } -} - -sub config_add_panels { - my ($self, $args) = @_; - my $modules = $args->{panel_modules}; - $modules->{MozReview} = "Bugzilla::Extension::MozReview::Config"; -} - -sub webservice { - my ($self, $args) = @_; - my $dispatch = $args->{dispatch}; - $dispatch->{MozReview} = "Bugzilla::Extension::MozReview::WebService"; -} - -sub webservice_before_call { - my ($self, $args) = @_; - my ($method, $full_method) = ($args->{method}, $args->{full_method}); - my $mozreview_app_id = Bugzilla->params->{mozreview_app_id} // ''; - my $user = Bugzilla->user; - my $getter = eval { $user->authorizer->successful_info_getter() } or return; - my $app_id = $getter->can("app_id") ? $getter->app_id // '' : ''; - - $full_method =~ s/^Bugzilla::Extension::(\w+)::WebService\./$1./; - - my $is_mozreview_method = $full_method =~ /^MozReview\./; - - if ($is_mozreview_method && (!$mozreview_app_id || !$app_id || $mozreview_app_id ne $app_id)) { - ThrowUserError('forbidden_method', { method => $full_method }); - } - - return unless $mozreview_app_id && $app_id; - - if ($app_id eq $mozreview_app_id && !$is_mozreview_method) { - unless (any { $full_method eq $_ } @METHOD_WHITELIST) { - ThrowUserError('forbidden_method', { method => $full_method }); - } - } -} - -__PACKAGE__->NAME; diff --git a/extensions/MozReview/bin/add-mozreview-children.pl b/extensions/MozReview/bin/add-mozreview-children.pl deleted file mode 100755 index 9faa923fa..000000000 --- a/extensions/MozReview/bin/add-mozreview-children.pl +++ /dev/null @@ -1,257 +0,0 @@ -#!/usr/bin/perl - -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. - -# This script obsoletes attachments containing URLs to MozReview parent -# review requests and adds attachments, with review flags, for MozReview -# child (commit) review requests to match the new scheme. - -use strict; -use warnings; -use 5.10.1; - -use lib qw(. lib local/lib/perl5); - -BEGIN { - use Bugzilla; - Bugzilla->extensions; -} -use Bugzilla::Constants qw( USAGE_MODE_CMDLINE ); -Bugzilla->usage_mode(USAGE_MODE_CMDLINE); - -use Bugzilla::Attachment; -use Bugzilla::Bug; -use Bugzilla::Constants; -use Bugzilla::Flag; -use Bugzilla::FlagType; -use JSON; -use LWP::Simple qw( get $ua ); - -$Bugzilla::Flag::disable_flagmail = 1; - -if (my $proxy = Bugzilla->params->{proxy_url}) { - $ua->proxy('https', $proxy); -} - -my $MOZREVIEW_MIMETYPE = 'text/x-review-board-request'; - -# Disable the "cannot ask for review" so we can reassign their flags to -# the new attachments. -Bugzilla->params->{max_reviewer_last_seen} = 0; - -my $rb_host = shift or die "syntax: $0 review-board-url\n"; -$rb_host =~ s#/$##; - -sub rr_url { - my ($rrid) = @_; - return $rb_host . "/r/" . $rrid . "/"; -} - -sub set_review_flag { - my ($child_attach, $flag_type, $flag_status, $reviewer, $setter) = @_; - - my %params = ( - type_id => $flag_type->id, - status => $flag_status - ); - - if ($flag_status eq "?") { - $params{'requestee'} = $reviewer->login; - $params{'setter'} = $setter; - } else { - $params{'setter'} = $reviewer; - } - - return Bugzilla::Flag->set_flag($child_attach, \%params); -} - -my $dbh = Bugzilla->dbh; - -my $bugs_query = "SELECT distinct bug_id FROM attachments WHERE mimetype='text/x-review-board-request' AND isobsolete=0"; -my $bug_ids = $dbh->selectcol_arrayref($bugs_query); -my $total_bugs = scalar @$bug_ids; -$total_bugs or die "No bugs were found.\n"; -my $bug_count = 0; - -print < to stop or to continue... -EOF -getc(); - -foreach my $bug_id (@$bug_ids) { - $dbh->bz_start_transaction(); - my $timestamp = $dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)'); - my $bug_changed = 0; - my $bug = Bugzilla::Bug->new($bug_id); - print "Bug " . $bug->id . " (" . ++$bug_count . "/" . $total_bugs . ")\n"; - - my $url = $rb_host . "/api/extensions/mozreview.extension.MozReviewExtension/summary/?bug=" . $bug->id; - print " Fetching reviews from $url...\n"; - my $body = get($url); - die "Error fetching review requests for bug " . $bug->id - unless defined $body; - - my $data = from_json($body); - my $summaries = $data->{"review_request_summaries"}; - my $attachments = Bugzilla::Attachment->get_attachments_by_bug($bug); - my %attach_map; - - my $flag_types = Bugzilla::FlagType::match({ - 'target_type' => 'attachment', - 'product_id' => $bug->product_id, - 'component_id' => $bug->component_id, - 'is_active' => 1}); - my $flag_type; - - foreach my $ft (@$flag_types) { - if ($ft->is_active && $ft->name eq "review") { - $flag_type = $ft; - last; - } - } - - if (!defined($flag_type)) { - print " Couldn't find flag type for attachments on this bug!\n"; - $dbh->bz_rollback_transaction(); - next; - } - - foreach my $attachment (@$attachments) { - next if ($attachment->isobsolete - || $attachment->contenttype ne $MOZREVIEW_MIMETYPE); - - print " Attachment " . $attachment->id . ": " . $attachment->data . "\n"; - my ($rrid) = $attachment->data =~ m#/r/(\d+)/?$#; - if (!defined($rrid)) { - print " Malformed or missing reviewboard URL\n"; - next; - } - - $attach_map{$attachment->data} = $attachment; - } - - foreach my $summary (@$summaries) { - my $parent = $summary->{"parent"}; - my $attacher = Bugzilla::User->new({ id => $parent->{"submitter_bmo_id"}, - cache => 1 }); - Bugzilla->set_user($attacher); - print " Parent review request " . $parent->{"id"} . "\n"; - - # %parent_flags is used to keep track of review flags related to - # reviewers. It maps requestee => status if status is "?" or - # setter => status otherwise. - my %parent_flags; - - my $parent_url = rr_url($parent->{"id"}); - my $parent_attach = $attach_map{$parent_url}; - if (defined($parent_attach)) { - print " Parent attachment has ID " . $parent_attach->id . ". Obsoleting it.\n"; - foreach my $flag (@{ $parent_attach->flags }) { - if ($flag->type->name eq "review") { - if ($flag->status eq "?") { - $parent_flags{$flag->requestee->id} = $flag; - } else { - $parent_flags{$flag->setter->id} = $flag; - } - } - } - $parent_attach->set_is_obsolete(1); - $parent_attach->update($timestamp); - print " Posting comment.\n"; - $bug->add_comment('', - { isprivate => 0, - type => CMT_ATTACHMENT_UPDATED, - extra_data => $parent_attach->id }); - $bug_changed = 1; - } else { - print " Parent attachment not found.\n"; - } - - my @children = @{ $summary->{"children"} }; - foreach my $child (@children) { - print " Child review request " . $child->{"id"} . "\n"; - my $child_url = rr_url($child->{"id"}); - my $child_attach = $attach_map{$child_url}; - if (defined($child_attach)) { - print " Found attachment.\n"; - next; - } - - print " No attachment found for child " . $child_url . "\n"; - my %child_attach_params = ( - bug => $bug, - data => $rb_host . "/r/" . $child->{"id"} . "/", - description => "MozReview Request: " . $child->{"summary"}, - filename => "reviewboard-" . $child->{"id"} . "-url.txt", - mimetype => $MOZREVIEW_MIMETYPE, - ); - $child_attach = Bugzilla::Attachment->create(\%child_attach_params); - print " New attachment id: " . $child_attach->id . "\n"; - $bug_changed = 1; - - # Set flags. If there was a parent, check it for flags by the - # requestee. Otherwise, set an r? flag. - - # Preserve the original flag hash since we need to modify it for - # every child to find extra reviewers (see below the 'foreach'). - my %tmp_parent_flags = %parent_flags; - - foreach my $reviewer_id (@{ $child->{"reviewers_bmo_ids"} }) { - my $reviewer = Bugzilla::User->new({ id => $reviewer_id, - cache => 1 }); - print " Reviewer " . $reviewer->login . " (" . $reviewer->id . ")\n"; - $reviewer->settings->{block_reviews}->{value} = 'off'; - my $flag = $tmp_parent_flags{$reviewer->id}; - if (defined($flag)) { - print " Flag for reviewer " . $reviewer->id . ": " . $flag->status . "\n"; - - set_review_flag($child_attach, $flag_type, $flag->status, - $reviewer, $attacher); - delete $tmp_parent_flags{$reviewer->id}; - } else { - # No flag on the parent; this probably means the reviewer - # canceled the review, so don't set r?. - print " No review flag for reviewer " . $reviewer->id . "\n"; - } - } - - # Preserve flags that were set directly on the attachment - # from reviewers not listed in the review request. - foreach my $extra_reviewer_id (keys %tmp_parent_flags) { - my $extra_reviewer = Bugzilla::User->new({ - id => $extra_reviewer_id, - cache => 1 - }); - my $flag = $tmp_parent_flags{$extra_reviewer_id}; - print " Extra flag set for reviewer " . $extra_reviewer->login . "\n"; - set_review_flag($child_attach, $flag->type, $flag->status, - $extra_reviewer, $flag->setter); - } - - $child_attach->update($timestamp); - print " Posting comment.\n"; - $bug->add_comment('', - { isprivate => 0, - type => CMT_ATTACHMENT_CREATED, - extra_data => $child_attach->id }); - } - } - - if ($bug_changed) { - print " Updating bug.\n"; - $bug->update($timestamp); - $dbh->do("UPDATE bugs SET lastdiffed = ?, delta_ts = ? WHERE bug_id = ?", - undef, $timestamp, $timestamp, $bug_id); - } - $dbh->bz_commit_transaction(); - Bugzilla->memcached->clear_all(); -} - -print "Done.\n"; diff --git a/extensions/MozReview/lib/Config.pm b/extensions/MozReview/lib/Config.pm deleted file mode 100644 index e0d6377d8..000000000 --- a/extensions/MozReview/lib/Config.pm +++ /dev/null @@ -1,55 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. - -package Bugzilla::Extension::MozReview::Config; - -use 5.10.1; -use strict; -use warnings; - -use Bugzilla::Config::Common; - -our $sortkey = 1300; - -sub get_param_list { - my ($class) = @_; - - my @params = ( - { - name => 'mozreview_base_url', - type => 't', - default => '', - checker => \&check_urlbase - }, - { - name => 'mozreview_auth_callback_url', - type => 't', - default => '', - checker => sub { - my ($url) = (@_); - - return 'must be an HTTP/HTTPS absolute URL' unless $url =~ m{^https?://}; - return ''; - } - }, - { - name => 'mozreview_app_id', - type => 't', - default => '', - checker => sub { - my ($app_id) = (@_); - - return 'must be a hex number' unless $app_id =~ /^[[:xdigit:]]+$/; - return ''; - }, - }, - ); - - return @params; -} - -1; diff --git a/extensions/MozReview/lib/WebService.pm b/extensions/MozReview/lib/WebService.pm deleted file mode 100644 index c6916d1fa..000000000 --- a/extensions/MozReview/lib/WebService.pm +++ /dev/null @@ -1,209 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. - -package Bugzilla::Extension::MozReview::WebService; - -use 5.10.1; -use strict; -use warnings; - -use base qw(Bugzilla::WebService); - -use Bugzilla::Attachment; -use Bugzilla::Bug; -use Bugzilla::Comment; -use Bugzilla::Constants; -use Bugzilla::Error; -use Bugzilla::WebService::Constants; -use Bugzilla::WebService::Util qw(extract_flags validate translate); -use Bugzilla::Util qw(trim); - -use List::MoreUtils qw(uniq all); -use List::Util qw(max); -use Storable qw(dclone); - -use constant PUBLIC_METHODS => qw( attachments ); - -BEGIN { - *_attachment_to_hash = \&Bugzilla::WebService::Bug::_attachment_to_hash; - *_flag_to_hash = \&Bugzilla::WebService::Bug::_flag_to_hash; -} - -sub attachments { - my ($self, $params) = validate(@_, 'attachments'); - my $dbh = Bugzilla->dbh; - - # BMO: Don't allow updating of bugs if disabled - if (Bugzilla->params->{disable_bug_updates}) { - ThrowErrorPage('bug/process/updates-disabled.html.tmpl', - 'Bug updates are currently disabled.'); - } - - my $user = Bugzilla->login(LOGIN_REQUIRED); - - ThrowCodeError('param_required', { param => 'attachments' }) - unless defined $params->{attachments}; - - my $bug = Bugzilla::Bug->check($params->{bug_id}); - - ThrowUserError("product_edit_denied", { product => $bug->product }) - unless $user->can_edit_product($bug->product_id); - - my (@modified, @created); - $dbh->bz_start_transaction(); - my $timestamp = $dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)'); - - my $comment_tags = $params->{comment_tags}; - my $attachments = $params->{attachments}; - - if ($comment_tags) { - ThrowUserError('comment_tag_disabled') - unless Bugzilla->params->{comment_taggers_group}; - - my $all_mozreview_tags = all { /^mozreview-?/i } @$comment_tags; - if ($all_mozreview_tags || $user->can_tag_comments) { - # there should be a method of User that does this. - local $user->{can_tag_comments} = 1; - $bug->set_all({ comment_tags => $comment_tags }); - } - else { - ThrowUserError('auth_failure', - { group => Bugzilla->params->{comment_taggers_group}, - action => 'update', - object => 'comment_tags' }) - } - } - - foreach my $attachment (@$attachments) { - my $flags = delete $attachment->{flags}; - my $attachment_id = delete $attachment->{attachment_id}; - my $comment = delete $attachment->{comment}; - my $attachment_obj; - - if ($attachment_id) { - $attachment_obj = Bugzilla::Attachment->check({ id => $attachment_id }); - ThrowUserError("mozreview_attachment_bug_mismatch", { bug => $bug, attachment => $attachment_obj }) - if $attachment_obj->bug_id != $bug->id; - - # HACK: preload same bug object. - $attachment_obj->{bug} = $bug; - - $attachment = translate($attachment, Bugzilla::WebService::Bug::ATTACHMENT_MAPPED_SETTERS); - - my ($update_flags, $new_flags) = $flags - ? extract_flags($flags, $bug, $attachment_obj) - : ([], []); - if ($attachment_obj->validate_can_edit) { - $attachment_obj->set_all($attachment); - $attachment_obj->set_flags($update_flags, $new_flags) if $flags; - } - elsif (scalar @$update_flags && !scalar(@$new_flags) && !scalar keys %$attachment) { - # Requestees can set flags targetted to them, even if they cannot - # edit the attachment. Flag setters can edit their own flags too. - my %flag_list = map { $_->{id} => $_ } @$update_flags; - my $flag_objs = Bugzilla::Flag->new_from_list([ keys %flag_list ]); - my @editable_flags; - foreach my $flag_obj (@$flag_objs) { - if ($flag_obj->setter_id == $user->id - || ($flag_obj->requestee_id && $flag_obj->requestee_id == $user->id)) - { - push(@editable_flags, $flag_list{$flag_obj->id}); - } - } - if (!scalar @editable_flags) { - ThrowUserError('illegal_attachment_edit', { attach_id => $attachment_obj->id }); - } - $attachment_obj->set_flags(\@editable_flags, []); - } - else { - ThrowUserError('illegal_attachment_edit', { attach_id => $attachment_obj->id }); - } - - my $changes = $attachment_obj->update($timestamp); - - if (my $comment_text = trim($comment)) { - $attachment_obj->bug->add_comment($comment_text, - { isprivate => $attachment_obj->isprivate, - type => CMT_ATTACHMENT_UPDATED, - extra_data => $attachment_obj->id }); - } - - $changes = translate($changes, Bugzilla::WebService::Bug::ATTACHMENT_MAPPED_RETURNS); - - my %hash = ( - id => $self->type('int', $attachment_obj->id), - last_change_time => $self->type('dateTime', $attachment_obj->modification_time), - changes => {}, - ); - - foreach my $field (keys %$changes) { - my $change = $changes->{$field}; - - # We normalize undef to an empty string, so that the API - # stays consistent for things like Deadline that can become - # empty. - $hash{changes}->{$field} = { - removed => $self->type('string', $change->[0] // ''), - added => $self->type('string', $change->[1] // '') - }; - } - - push(@modified, \%hash); - } - else { - $attachment_obj = Bugzilla::Attachment->create({ - bug => $bug, - creation_ts => $timestamp, - data => $attachment->{data}, - description => $attachment->{summary}, - filename => $attachment->{file_name}, - mimetype => $attachment->{content_type}, - ispatch => $attachment->{is_patch}, - isprivate => $attachment->{is_private}, - }); - - if ($flags) { - my ($old_flags, $new_flags) = extract_flags($flags, $bug, $attachment_obj); - $attachment_obj->set_flags($old_flags, $new_flags); - } - - push(@created, $attachment_obj); - - $attachment_obj->update($timestamp); - $bug->add_comment($comment, - { isprivate => $attachment_obj->isprivate, - type => CMT_ATTACHMENT_CREATED, - extra_data => $attachment_obj->id }); - - } - } - - $bug->update($timestamp); - - $dbh->bz_commit_transaction(); - $bug->send_changes(); - - my %attachments_created = map { $_->id => $self->_attachment_to_hash($_, $params) } @created; - my %attachments_modified = map { (ref $_->{id} ? $_->{id}->value : $_->{id}) => $_ } @modified; - - return { attachments_created => \%attachments_created, attachments_modified => \%attachments_modified }; -} - -sub rest_resources { - return [ - qr{^/mozreview/(\d+)/attachments$}, { - POST => { - method => 'attachments', - params => sub { - return { bug_id => $1 }; - }, - }, - }, - ]; -} - -1; diff --git a/extensions/MozReview/template/en/default/admin/params/mozreview.html.tmpl b/extensions/MozReview/template/en/default/admin/params/mozreview.html.tmpl deleted file mode 100644 index 4a35555a4..000000000 --- a/extensions/MozReview/template/en/default/admin/params/mozreview.html.tmpl +++ /dev/null @@ -1,20 +0,0 @@ -[%# This Source Code Form is subject to the terms of the Mozilla Public - # License, v. 2.0. If a copy of the MPL was not distributed with this - # file, You can obtain one at http://mozilla.org/MPL/2.0/. - # - # This Source Code Form is "Incompatible With Secondary Licenses", as - # defined by the Mozilla Public License, v. 2.0. - #%] - -[% - title = "MozReview" - desc = "Configure MozReview" -%] - -[% - param_descs = { - mozreview_base_url => 'MozReview Base URL', - mozreview_auth_callback_url => 'MozReview Auth Delegation URL', - mozreview_app_id => 'app_id for API Keys delegated to MozReview', - } -%] diff --git a/extensions/MozReview/template/en/default/hook/attachment/create-before_form.html.tmpl b/extensions/MozReview/template/en/default/hook/attachment/create-before_form.html.tmpl deleted file mode 100644 index bfa842c89..000000000 --- a/extensions/MozReview/template/en/default/hook/attachment/create-before_form.html.tmpl +++ /dev/null @@ -1,17 +0,0 @@ -[%# This Source Code Form is subject to the terms of the Mozilla Public - # License, v. 2.0. If a copy of the MPL was not distributed with this - # file, You can obtain one at http://mozilla.org/MPL/2.0/. - # - # This Source Code Form is "Incompatible With Secondary Licenses", as - # defined by the Mozilla Public License, v. 2.0. - #%] - -[% RETURN UNLESS mozreview_enabled %] - -
- Are you attaching a patch? Consider trying out - MozReview, Mozilla's - new repository-based code-review tool. - Read - the docs to get started. -
diff --git a/extensions/MozReview/template/en/default/hook/attachment/edit-view.html.tmpl b/extensions/MozReview/template/en/default/hook/attachment/edit-view.html.tmpl deleted file mode 100644 index 044c36ae9..000000000 --- a/extensions/MozReview/template/en/default/hook/attachment/edit-view.html.tmpl +++ /dev/null @@ -1,16 +0,0 @@ -[%# This Source Code Form is subject to the terms of the Mozilla Public - # License, v. 2.0. If a copy of the MPL was not distributed with this - # file, You can obtain one at http://mozilla.org/MPL/2.0/. - # - # This Source Code Form is "Incompatible With Secondary Licenses", as - # defined by the Mozilla Public License, v. 2.0. - #%] - -[% - RETURN UNLESS attachment.mimetype == "text/x-review-board-request" && attachment.external_redirect; - custom_attachment_viewer = 1; - url = attachment.data; -%] -

- Show review on MozReview
-

diff --git a/extensions/MozReview/template/en/default/hook/bug/edit-after_bug_data.html.tmpl b/extensions/MozReview/template/en/default/hook/bug/edit-after_bug_data.html.tmpl deleted file mode 100644 index b0e4ce600..000000000 --- a/extensions/MozReview/template/en/default/hook/bug/edit-after_bug_data.html.tmpl +++ /dev/null @@ -1,35 +0,0 @@ -[%# This Source Code Form is subject to the terms of the Mozilla Public - # License, v. 2.0. If a copy of the MPL was not distributed with this - # file, You can obtain one at http://mozilla.org/MPL/2.0/. - # - # This Source Code Form is "Incompatible With Secondary Licenses", as - # defined by the Mozilla Public License, v. 2.0. - #%] - -[% RETURN UNLESS mozreview %] - - - - - - - - - - - - - - - - - - - -
MozReview Requests
- [% INCLUDE moz_review/table.html.tmpl %] -
- Show - discarded requests -
diff --git a/extensions/MozReview/template/en/default/hook/bug/show-header-end.html.tmpl b/extensions/MozReview/template/en/default/hook/bug/show-header-end.html.tmpl deleted file mode 100644 index d70e36b57..000000000 --- a/extensions/MozReview/template/en/default/hook/bug/show-header-end.html.tmpl +++ /dev/null @@ -1,13 +0,0 @@ -[%# This Source Code Form is subject to the terms of the Mozilla Public - # License, v. 2.0. If a copy of the MPL was not distributed with this - # file, You can obtain one at http://mozilla.org/MPL/2.0/. - # - # This Source Code Form is "Incompatible With Secondary Licenses", as - # defined by the Mozilla Public License, v. 2.0. - #%] - -[% - IF mozreview; - PROCESS moz_review/header.html.tmpl; - END; -%] diff --git a/extensions/MozReview/template/en/default/hook/bug_modal/edit-module.html.tmpl b/extensions/MozReview/template/en/default/hook/bug_modal/edit-module.html.tmpl deleted file mode 100644 index 9785fa0bd..000000000 --- a/extensions/MozReview/template/en/default/hook/bug_modal/edit-module.html.tmpl +++ /dev/null @@ -1,21 +0,0 @@ -[%# This Source Code Form is subject to the terms of the Mozilla Public - # License, v. 2.0. If a copy of the MPL was not distributed with this - # file, You can obtain one at http://mozilla.org/MPL/2.0/. - # - # This Source Code Form is "Incompatible With Secondary Licenses", as - # defined by the Mozilla Public License, v. 2.0. - #%] - -[% RETURN UNLESS mozreview %] - -[% WRAPPER bug_modal/module.html.tmpl - title = "MozReview Requests" - collapsed = 0 -%] - [% INCLUDE moz_review/table.html.tmpl %] -
- -
-[% END %] diff --git a/extensions/MozReview/template/en/default/hook/bug_modal/header-end.html.tmpl b/extensions/MozReview/template/en/default/hook/bug_modal/header-end.html.tmpl deleted file mode 100644 index d70e36b57..000000000 --- a/extensions/MozReview/template/en/default/hook/bug_modal/header-end.html.tmpl +++ /dev/null @@ -1,13 +0,0 @@ -[%# This Source Code Form is subject to the terms of the Mozilla Public - # License, v. 2.0. If a copy of the MPL was not distributed with this - # file, You can obtain one at http://mozilla.org/MPL/2.0/. - # - # This Source Code Form is "Incompatible With Secondary Licenses", as - # defined by the Mozilla Public License, v. 2.0. - #%] - -[% - IF mozreview; - PROCESS moz_review/header.html.tmpl; - END; -%] diff --git a/extensions/MozReview/template/en/default/hook/global/header-start.html.tmpl b/extensions/MozReview/template/en/default/hook/global/header-start.html.tmpl deleted file mode 100644 index 6ad026de6..000000000 --- a/extensions/MozReview/template/en/default/hook/global/header-start.html.tmpl +++ /dev/null @@ -1,11 +0,0 @@ -[%# This Source Code Form is subject to the terms of the Mozilla Public - # License, v. 2.0. If a copy of the MPL was not distributed with this - # file, You can obtain one at http://mozilla.org/MPL/2.0/. - # - # This Source Code Form is "Incompatible With Secondary Licenses", as - # defined by the Mozilla Public License, v. 2.0. - #%] - -[% IF template.name == 'attachment/create.html.tmpl' %] - [% style_urls.push('extensions/MozReview/web/style/attachment.css') %] -[% END %] diff --git a/extensions/MozReview/template/en/default/hook/global/user-error-errors.html.tmpl b/extensions/MozReview/template/en/default/hook/global/user-error-errors.html.tmpl deleted file mode 100644 index 151e63b21..000000000 --- a/extensions/MozReview/template/en/default/hook/global/user-error-errors.html.tmpl +++ /dev/null @@ -1,15 +0,0 @@ -[%# This Source Code Form is subject to the terms of the Mozilla Public - # License, v. 2.0. If a copy of the MPL was not distributed with this - # file, You can obtain one at http://mozilla.org/MPL/2.0/. - # - # This Source Code Form is "Incompatible With Secondary Licenses", as - # defined by the Mozilla Public License, v. 2.0. - #%] - -[% IF error == "forbidden_method" %] - The requested method '[% method FILTER html %]' is not allowed to be called using the current API Key. -[% ELSIF error == "mozreview_attachment_bug_mismatch" %] - You tried to update attachment [% attachment.id FILTER html %] - as part of adding or updating attachments on [% bug.id FILTER html %]. - That attachment actually belongs to [% terms.bug %] [% attachment.bug_id FILTER html %]. -[% END %] diff --git a/extensions/MozReview/template/en/default/moz_review/header.html.tmpl b/extensions/MozReview/template/en/default/moz_review/header.html.tmpl deleted file mode 100644 index 99fb81ba4..000000000 --- a/extensions/MozReview/template/en/default/moz_review/header.html.tmpl +++ /dev/null @@ -1,10 +0,0 @@ -[%# This Source Code Form is subject to the terms of the Mozilla Public - # License, v. 2.0. If a copy of the MPL was not distributed with this - # file, You can obtain one at http://mozilla.org/MPL/2.0/. - # - # This Source Code Form is "Incompatible With Secondary Licenses", as - # defined by the Mozilla Public License, v. 2.0. - #%] - -[% style_urls.push('extensions/MozReview/web/style/mozreview.css') %] -[% javascript_urls.push('extensions/MozReview/web/js/mozreview.js') %] diff --git a/extensions/MozReview/template/en/default/moz_review/table.html.tmpl b/extensions/MozReview/template/en/default/moz_review/table.html.tmpl deleted file mode 100644 index 84b7add47..000000000 --- a/extensions/MozReview/template/en/default/moz_review/table.html.tmpl +++ /dev/null @@ -1,28 +0,0 @@ -[%# This Source Code Form is subject to the terms of the Mozilla Public - # License, v. 2.0. If a copy of the MPL was not distributed with this - # file, You can obtain one at http://mozilla.org/MPL/2.0/. - # - # This Source Code Form is "Incompatible With Secondary Licenses", as - # defined by the Mozilla Public License, v. 2.0. - #%] - - - - - - - - - - - - - - - - - - - -
SubmitterDiffChangesOpen IssuesLast Updated
Loading...
Error loading review requests: -
diff --git a/extensions/MozReview/web/js/mozreview.js b/extensions/MozReview/web/js/mozreview.js deleted file mode 100644 index 6fe51f53b..000000000 --- a/extensions/MozReview/web/js/mozreview.js +++ /dev/null @@ -1,134 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. - * - * This Source Code Form is "Incompatible With Secondary Licenses", as - * defined by the Mozilla Public License, v. 2.0. - */ - -var MozReview = {}; - -MozReview.getReviewRequest = function() { - var hostUrl = $('.mozreview-requests').data('mozreviewUrl'); - var tr = $(''); - var td = $(''); - var link = $(''); - - var rrSummaryApiUrl = hostUrl + - 'api/extensions/mozreview.extension.MozReviewExtension/summary/?bug=' + - BUGZILLA.bug_id; - var rrUiBaseUrl = hostUrl + 'r/'; - - function rrUrl(rrId) { - return rrUiBaseUrl + rrId + '/'; - } - - function rrDiffUrl(rrId) { - return rrUrl(rrId) + 'diff/#index_header'; - } - - function humanizedInt(i) { - if (i > 1000) { - return (i / 1000).toFixed(1) + 'k'; - } else { - return '' + i; - } - } - - function rrCommitRow(rr, firstCommit) { - var trCommit = tr.clone(); - var tdSubmitter = td.clone(); - var tdSummary = td.clone(); - var diffLink = link.clone(); - var diffStat = ''; - - if (firstCommit) { - tdSubmitter.text(rr.submitter); - } - - tdSummary.addClass('mozreview-summary'); - diffLink.attr('href', rrDiffUrl(rr.id)); - diffLink.text(rr.summary); - tdSummary.append(diffLink); - - if (rr.diff.insert > 0) { - diffStat = '+' + humanizedInt(rr.diff.insert); - } - - if (rr.diff.delete > 0) { - if (diffStat.length > 0) { - diffStat += ' / '; - } - diffStat += '-' + humanizedInt(rr.diff.delete); - } - - trCommit.append( - tdSubmitter, - tdSummary, - td.clone().text(diffStat) - .addClass('mozreview-diffstat'), - td.clone().text(rr.issue_open_count) - .addClass('mozreview-open-issues'), - td.clone().text(timeAgo(new Date(rr.last_updated))) - ); - - if (rr.status == "discarded") { - $('.mozreview-hide-discarded-row').removeClass('bz_default_hidden'); - trCommit.addClass('bz_default_hidden mozreview-discarded-request'); - } - - return trCommit; - } - - $('.mozreview-hide-discarded-link').click(function(event) { - event.preventDefault(); - if ($('.bz_default_hidden.mozreview-discarded-request').length) { - $('.mozreview-discarded-request').removeClass('bz_default_hidden'); - $('.mozreview-discarded-action').text('Hide'); - } else { - $('.mozreview-discarded-request').addClass('bz_default_hidden'); - $('.mozreview-discarded-action').text('Show'); - } - }); - - var tbody = $('tbody.mozreview-request'); - - function displayLoadError(errStr) { - var errRow = tbody.find('.mozreview-loading-error-row'); - errRow.find('.mozreview-load-error-string').text(errStr); - errRow.removeClass('bz_default_hidden'); - } - - $.getJSON(rrSummaryApiUrl, function(data) { - var family, parent, i, j; - - if (data.review_request_summaries.length === 0) { - displayLoadError('none returned from server'); - } else { - for (i = 0; i < data.review_request_summaries.length; i++) { - family = data.review_request_summaries[i]; - for (j = 0; j < family.children.length; j++) { - tbody.append(rrCommitRow(family.children[j], j == 0)); - } - } - } - - tbody.find('.mozreview-loading-row').addClass('bz_default_hidden'); - }).fail(function(jqXHR, textStatus, errorThrown) { - var errStr; - if (jqXHR.responseJSON && jqXHR.responseJSON.err && - jqXHR.responseJSON.err.msg) { - errStr = jqXHR.responseJSON.err.msg; - } else if (errorThrown) { - errStr = errorThrown; - } else { - errStr = 'unknown'; - } - displayLoadError(errStr); - tbody.find('.mozreview-loading-row').addClass('bz_default_hidden'); - }); -}; - -$().ready(function() { - MozReview.getReviewRequest(); -}); diff --git a/extensions/MozReview/web/style/attachment.css b/extensions/MozReview/web/style/attachment.css deleted file mode 100644 index 474bf466e..000000000 --- a/extensions/MozReview/web/style/attachment.css +++ /dev/null @@ -1,13 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. - * - * This Source Code Form is "Incompatible With Secondary Licenses", as - * defined by the Mozilla Public License, v. 2.0. */ - -.mozreview-ad { - background-color: #fff9db; - color: #666458; - padding: 5px; - margin-bottom: 10px; -} diff --git a/extensions/MozReview/web/style/mozreview.css b/extensions/MozReview/web/style/mozreview.css deleted file mode 100644 index a2263fc11..000000000 --- a/extensions/MozReview/web/style/mozreview.css +++ /dev/null @@ -1,79 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. - * - * This Source Code Form is "Incompatible With Secondary Licenses", as - * defined by the Mozilla Public License, v. 2.0. */ - -.mozreview-table { - background: #fff; - border: none; - border-collapse: collapse; - border-bottom: 1px solid rgba(0, 0, 0, 0.2); - box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); -} - -.mozreview-table th { - text-align: left; - padding: 4px; -} - -.mozreview-table td { - vertical-align: middle !important; - padding: 4px !important; -} - -.mozreview-table thead, .mozreview-table tfoot { - background-color: #eee; - color: #404040; -} - -.mozreview-requests { - background: #fff; - border: none; - border-collapse: collapse; -} - -.mozreview-requests th { - padding: 2px; -} - -.mozreview-requests td { - padding: 2px; -} - -.mozreview-requests .mozreview-summary { - text-align: left; -} - -.mozreview-requests .mozreview-open-issues { - text-align: center; -} - -.mozreview-requests .mozreview-diffstat { - text-align: center; - white-space: nowrap; -} - -/* bug-modal specific */ - -#module-mozreview-requests .module-content { - padding: 0; -} - -.bug_modal .mozreview-table { - width: 100%; -} - -.bug_modal .mozreview-request td { - padding-left: 8px; -} - -.bug_modal .mozreview-requests th { - text-align: left; - padding-left: 8px; -} - -.bug_modal .mozreview-hide-discarded-row { - padding: 4px; -} diff --git a/extensions/Push/lib/Connector/ReviewBoard.pm b/extensions/Push/lib/Connector/ReviewBoard.pm deleted file mode 100644 index 1c657a728..000000000 --- a/extensions/Push/lib/Connector/ReviewBoard.pm +++ /dev/null @@ -1,178 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. - -package Bugzilla::Extension::Push::Connector::ReviewBoard; - -use 5.10.1; -use strict; -use warnings; - -use base 'Bugzilla::Extension::Push::Connector::Base'; - -use Bugzilla::Bug; -use Bugzilla::BugMail; -use Bugzilla::Component; -use Bugzilla::Constants; -use Bugzilla::Extension::Push::Constants; -use Bugzilla::Extension::Push::Util; -use Bugzilla::Group; -use Bugzilla::Product; -use Bugzilla::User; -use Bugzilla::Util qw( trim ); - -use constant RB_CONTENT_TYPE => 'text/x-review-board-request'; -use constant AUTOMATION_USER => 'automation@bmo.tld'; - -sub options { - return ( - { - name => 'product', - label => 'Product to create bugs in', - type => 'string', - default => 'Developer Services', - required => 1, - validate => sub { - Bugzilla::Product->new({ name => $_[0] }) - || die "Invalid Product ($_[0])\n"; - }, - }, - { - name => 'component', - label => 'Component to create bugs in', - type => 'string', - default => 'MozReview', - required => 1, - validate => sub { - my ($component, $config) = @_; - my $product = Bugzilla::Product->new({ name => $config->{product} }) - || die "Invalid Product (" . $config->{product} . ")\n"; - Bugzilla::Component->new({ product => $product, name => $component }) - || die "Invalid Component ($component)\n"; - }, - }, - { - name => 'version', - label => "The bug's version", - type => 'string', - default => 'Production', - required => 1, - validate => sub { - my ($version, $config) = @_; - my $product = Bugzilla::Product->new({ name => $config->{product} }) - || die "Invalid Product (" . $config->{product} . ")\n"; - Bugzilla::Version->new({ product => $product, name => $version }) - || die "Invalid Version ($version)\n"; - }, - }, - { - name => 'group', - label => 'Security group', - type => 'string', - default => 'mozilla-employee-confidential', - required => 1, - validate => sub { - Bugzilla::Group->new({ name => $_[0] }) - || die "Invalid Group ($_[0])\n"; - }, - }, - { - name => 'cc', - label => 'Comma separated list of users to CC', - type => 'string', - default => '', - required => 1, - validate => sub { - foreach my $login (map { trim($_) } split(',', $_[0])) { - Bugzilla::User->new({ name => $login }) - || die "Invalid User ($login)\n"; - } - }, - }, - ); -} - -sub should_send { - my ($self, $message) = @_; - - if ($message->routing_key =~ /^(?:attachment|bug)\.modify:.*\bis_private\b/) { - my $payload = $message->payload_decoded(); - my $target = $payload->{event}->{target}; - - if ($target ne 'bug' && exists $payload->{$target}->{bug}) { - return 0 if $payload->{$target}->{bug}->{is_private}; - return 0 if $payload->{$target}->{content_type} ne RB_CONTENT_TYPE; - } - - return $payload->{$target}->{is_private} ? 1 : 0; - } - else { - # We're not interested in the message. - return 0; - } -} - -sub send { - my ($self, $message) = @_; - my $logger = Bugzilla->push_ext->logger; - my $config = $self->config; - - eval { - my $payload = $message->payload_decoded(); - my $target = $payload->{event}->{target}; - - # load attachments - my $bug_id = $target eq 'bug' ? $payload->{bug}->{id} : $payload->{attachment}->{bug}->{id}; - my $attach_id = $target eq 'attachment' ? $payload->{attachment}->{id} : undef; - Bugzilla->set_user(Bugzilla::User->super_user); - my $bug = Bugzilla::Bug->new({ id => $bug_id, cache => 1 }); - Bugzilla->logout; - - # create a bug if there are any mozreview attachments - my @reviews = grep { $_->contenttype eq RB_CONTENT_TYPE } @{ $bug->attachments }; - if (@reviews) { - - # build comment - my $comment = $target eq 'bug' - ? "Bug $bug_id has MozReview reviews and is no longer public." - : "MozReview attachment $attach_id on Bug $bug_id is no longer public."; - $comment .= "\n\n"; - foreach my $attachment (@reviews) { - $comment .= $attachment->data . "\n"; - } - - # create bug - my $user = Bugzilla::User->new({ name => AUTOMATION_USER, cache => 1 }); - die "Invalid User: " . AUTOMATION_USER . "\n" unless $user; - Bugzilla->set_user($user); - my $new_bug = Bugzilla::Bug->create({ - short_desc => "[SECURITY] Bug $bug_id is no longer public", - product => $config->{product}, - component => $config->{component}, - bug_severity => 'normal', - groups => [ map { trim($_) } split(',', $config->{group}) ], - op_sys => 'Unspecified', - rep_platform => 'Unspecified', - version => $config->{version}, - cc => [ map { trim($_) } split(',', $config->{cc}) ], - comment => $comment, - }); - Bugzilla::BugMail::Send($new_bug->id, { changer => Bugzilla->user }); - Bugzilla->logout; - - $logger->info("Created bug " . $new_bug->id); - } - }; - my $error = $@; - Bugzilla->logout; - if ($error) { - return (PUSH_RESULT_TRANSIENT, clean_error($error)); - } - - return PUSH_RESULT_OK; -} - -1; diff --git a/extensions/Push/t/ReviewBoard.t b/extensions/Push/t/ReviewBoard.t deleted file mode 100644 index c752e34ef..000000000 --- a/extensions/Push/t/ReviewBoard.t +++ /dev/null @@ -1,225 +0,0 @@ -#!/usr/bin/perl -T -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. -use strict; -use warnings; -use lib qw( . lib local/lib/perl5 ); - -use Test::More; -use Bugzilla; -use Bugzilla::Extension; -use Bugzilla::Attachment; -use Scalar::Util 'blessed'; - -BEGIN { - eval { - require Test::LWP::UserAgent; - require Test::MockObject; - }; - if ($@) { - plan skip_all => - 'Tests require Test::LWP::UserAgent and Test::MockObject'; - exit; - } -} - -BEGIN { - Bugzilla->extensions; # load all of them - use_ok 'Bugzilla::Extension::Push::Connector::ReviewBoard::Client'; - use_ok 'Bugzilla::Extension::Push::Constants'; -} - -my ($push) = grep { blessed($_) eq 'Bugzilla::Extension::Push' } @{Bugzilla->extensions }; -my $connectors = $push->_get_instance->connectors; -my $con = $connectors->by_name('ReviewBoard'); - -my $ua_204 = Test::LWP::UserAgent->new; -$ua_204->map_response( - qr{https://reviewboard-dev\.allizom\.org/api/review-requests/\d+}, - HTTP::Response->new('204')); - -my $ua_404 = Test::LWP::UserAgent->new; -$ua_404->map_response( - qr{https://reviewboard-dev\.allizom\.org/api/review-requests/\d+}, - HTTP::Response->new('404', undef, undef, q[{ "err": { "code": 100, "msg": "Object does not exist" }, "stat": "fail" }])); - -# forbidden -my $ua_403 = Test::LWP::UserAgent->new; -$ua_403->map_response( - qr{https://reviewboard-dev\.allizom\.org/api/review-requests/\d+}, - HTTP::Response->new('403', undef, undef, q[ {"err":{"code":101,"msg":"You don't have permission for this"},"stat":"fail"}])); - -# not logged in -my $ua_401 = Test::LWP::UserAgent->new; -$ua_401->map_response( - qr{https://reviewboard-dev\.allizom\.org/api/review-requests/\d+}, - HTTP::Response->new('401', undef, undef, q[ { "err": { "code": 103, "msg": "You are not logged in" }, "stat": "fail" } ])); - -# not logged in -my $ua_500 = Test::LWP::UserAgent->new; -$ua_500->map_response( - qr{https://reviewboard-dev\.allizom\.org/api/review-requests/\d+}, - HTTP::Response->new('500')); - -$con->client->{useragent} = $ua_204; -$con->config->{base_uri} = 'https://reviewboard-dev.allizom.org'; -$con->client->{base_uri} = 'https://reviewboard-dev.allizom.org'; - -{ - my $msg = message( - event => { - routing_key => 'attachment.modify:is_private', - target => 'attachment', - }, - attachment => { - is_private => 1, - content_type => 'text/plain', - bug => { id => 1, is_private => 0 }, - }, - ); - - ok(not($con->should_send($msg)), "text/plain message should not be sent"); -} - -my $data = slurp("extensions/Push/t/rblink.txt"); -Bugzilla::User::DEFAULT_USER->{userid} = 42; -Bugzilla->set_user(Bugzilla::User->super_user); -diag " " . Bugzilla::User->super_user->id; - -my $dbh = Bugzilla->dbh; -$dbh->bz_start_transaction; -my $timestamp = $dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)'); -my $bug = Bugzilla::Bug->new({id => 9000}); -my $attachment = Bugzilla::Attachment->create({ - bug => $bug, - creation_ts => $timestamp, - data => $data, - attach_size => length($data), - description => "rblink.txt", - filename => "rblink.txt", - isprivate => 1, - ispatch => 0, - mimetype => 'text/x-review-board-request' -}); -diag "".$attachment->id; -$dbh->bz_commit_transaction; - -{ - my $msg = message( - event => { - routing_key => 'attachment.modify:cc,is_private', - target => 'attachment', - }, - attachment => { - id => $attachment->id, - is_private => 1, - content_type => 'text/x-review-board-request', - bug => { id => $bug->id, is_private => 0 }, - }, - ); - ok($con->should_send($msg), "rb attachment should be sent"); - - { - my ($rv, $err) = $con->send($msg); - is($rv, PUSH_RESULT_OK, "good push result"); - diag $err if $err; - } - - { - local $con->client->{useragent} = $ua_404; - my ($rv, $err) = $con->send($msg); - is($rv, PUSH_RESULT_OK, "good push result for 404"); - diag $err if $err; - } - - - { - local $con->client->{useragent} = $ua_403; - my ($rv, $err) = $con->send($msg); - is($rv, PUSH_RESULT_TRANSIENT, "transient error on 403"); - diag $err if $err; - } - - - { - local $con->client->{useragent} = $ua_401; - my ($rv, $err) = $con->send($msg); - is($rv, PUSH_RESULT_TRANSIENT, "transient error on 401"); - diag $err if $err; - } - - { - local $con->client->{useragent} = $ua_500; - my ($rv, $err) = $con->send($msg); - is($rv, PUSH_RESULT_TRANSIENT, "transient error on 500"); - diag $err if $err; - } -} - -{ - my $msg = message( - event => { - routing_key => 'bug.modify:is_private', - target => 'bug', - }, - bug => { - is_private => 1, - id => $bug->id, - }, - ); - - ok($con->should_send($msg), "rb attachment should be sent"); - my ($rv, $err) = $con->send($msg); - is($rv, PUSH_RESULT_OK, "good push result"); - - { - local $con->client->{useragent} = $ua_404; - my ($rv, $err) = $con->send($msg); - is($rv, PUSH_RESULT_OK, "good push result for 404"); - } - - { - local $con->client->{useragent} = $ua_403; - my ($rv, $err) = $con->send($msg); - is($rv, PUSH_RESULT_TRANSIENT, "transient error on 404"); - diag $err if $err; - } - - - { - local $con->client->{useragent} = $ua_401; - my ($rv, $err) = $con->send($msg); - is($rv, PUSH_RESULT_TRANSIENT, "transient error on 401"); - diag $err if $err; - } - - { - local $con->client->{useragent} = $ua_401; - my ($rv, $err) = $con->send($msg); - is($rv, PUSH_RESULT_TRANSIENT, "transient error on 401"); - diag $err if $err; - } -} - -sub message { - my $msg_data = { @_ }; - - return Test::MockObject->new - ->set_always( routing_key => $msg_data->{event}{routing_key} ) - ->set_always( payload_decoded => $msg_data ); -} - -sub slurp { - my $file = shift; - local $/ = undef; - open my $fh, '<', $file or die "unable to open $file"; - my $s = readline $fh; - close $fh; - return $s; -} - -done_testing; diff --git a/scripts/issue-api-key.pl b/scripts/issue-api-key.pl index 810b7e17e..e4cc1cdd0 100755 --- a/scripts/issue-api-key.pl +++ b/scripts/issue-api-key.pl @@ -33,10 +33,6 @@ my $params = { api_key => $given_api_key, }; -if ($description && $description eq 'mozreview') { - $params->{app_id} = Bugzilla->params->{mozreview_app_id} // ''; -} - if ($given_api_key) { $api_key = Bugzilla::User::APIKey->create_special($params); } else { diff --git a/template/en/default/account/prefs/apikey.html.tmpl b/template/en/default/account/prefs/apikey.html.tmpl index cdbee848b..c4b4074c1 100644 --- a/template/en/default/account/prefs/apikey.html.tmpl +++ b/template/en/default/account/prefs/apikey.html.tmpl @@ -34,25 +34,15 @@ here.

Revoked - [%# because mozreview api-keys only work for the mozreview api, we hide the - # key and prevent renaming #%] [% FOREACH api_key IN api_keys %]
diff --git a/template/en/default/bug/edit.html.tmpl b/template/en/default/bug/edit.html.tmpl index 445e5fe0d..6a1edc163 100644 --- a/template/en/default/bug/edit.html.tmpl +++ b/template/en/default/bug/edit.html.tmpl @@ -110,27 +110,27 @@ [%# 1st Column %] - [% END %] [% END %] @@ -1315,7 +1314,7 @@ [% BLOCK commit_button %] [% IF user.id %]
-
[% END %] diff --git a/template/en/default/email/new-api-key.txt.tmpl b/template/en/default/email/new-api-key.txt.tmpl index aed904def..9ab13d90b 100644 --- a/template/en/default/email/new-api-key.txt.tmpl +++ b/template/en/default/email/new-api-key.txt.tmpl @@ -26,10 +26,7 @@ or update the key at the following URL: [%+ urlbase %]userprefs.cgi?tab=apikey -[% IF new_key.app_id == Param('mozreview_app_id') %] -This API key was automatically created by MozReview. If you did not recently log in to -MozReview, please disable the key at the above URL, and change your password immediately. -[% ELSIF new_key.app_id == Param('phabricator_app_id') %] +[% IF new_key.app_id == Param('phabricator_app_id') %] This API key was automatically created by Mozilla's Phabricator instance. If you did not recently log in to Phabricator, please disable the key at the above URL, and change your password immediately. [% ELSE %] -- cgit v1.2.3-24-g4f1b From a91453b19c462929b3ab77927b0d0a6807558b92 Mon Sep 17 00:00:00 2001 From: Israel Madueme Date: Mon, 10 Sep 2018 12:34:56 -0400 Subject: Bug 1479466 - Add Security Bugs Report Adds the security bugs report with open count and median age open of sec-critical and sec-high bugs. --- Bugzilla/Config/Reports.pm | 37 +++ Bugzilla/Report/SecurityRisk.pm | 318 +++++++++++++++++++++ scripts/secbugsreport.pl | 83 ++++++ t/security-risk.t | 156 ++++++++++ template/en/default/admin/params/reports.html.tmpl | 20 ++ .../default/reports/email/security-risk.html.tmpl | 95 ++++++ 6 files changed, 709 insertions(+) create mode 100644 Bugzilla/Config/Reports.pm create mode 100644 Bugzilla/Report/SecurityRisk.pm create mode 100644 scripts/secbugsreport.pl create mode 100644 t/security-risk.t create mode 100644 template/en/default/admin/params/reports.html.tmpl create mode 100644 template/en/default/reports/email/security-risk.html.tmpl diff --git a/Bugzilla/Config/Reports.pm b/Bugzilla/Config/Reports.pm new file mode 100644 index 000000000..26c5aad57 --- /dev/null +++ b/Bugzilla/Config/Reports.pm @@ -0,0 +1,37 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. + +package Bugzilla::Config::Reports; + +use 5.10.1; +use strict; +use warnings; + +use Bugzilla::Config::Common; + +our $sortkey = 1100; + +sub get_param_list { + my $class = shift; + my @param_list = ( + { + name => 'report_secbugs_active', + type => 'b', + default => 1, + }, + { + name => 'report_secbugs_emails', + type => 't', + default => 'bugzilla-admin@mozilla.org' + }, + { + name => 'report_secbugs_products', + type => 'l', + default => '[]' + }, + ); +} diff --git a/Bugzilla/Report/SecurityRisk.pm b/Bugzilla/Report/SecurityRisk.pm new file mode 100644 index 000000000..1b62d476c --- /dev/null +++ b/Bugzilla/Report/SecurityRisk.pm @@ -0,0 +1,318 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. + +package Bugzilla::Report::SecurityRisk; + +use 5.10.1; + +use Bugzilla; +use Bugzilla::Error; +use Bugzilla::Status qw(is_open_state); +use Bugzilla::Util qw(datetime_from); + +use DateTime; +use List::Util qw(any first sum); +use Moo; +use MooX::StrictConstructor; +use POSIX qw(ceil); +use Types::Standard qw(Num Int Bool Str HashRef ArrayRef CodeRef Map Dict Enum); +use Type::Utils; + +my $DateTime = class_type { class => 'DateTime' }; + +has 'start_date' => ( + is => 'ro', + required => 1, + isa => $DateTime, +); + +has 'end_date' => ( + is => 'ro', + required => 1, + isa => $DateTime, +); + +has 'products' => ( + is => 'ro', + required => 1, + isa => ArrayRef [Str], +); + +has 'sec_keywords' => ( + is => 'ro', + required => 1, + isa => ArrayRef [Str], +); + +has 'initial_bug_ids' => ( + is => 'lazy', + isa => ArrayRef [Int], +); + +has 'initial_bugs' => ( + is => 'lazy', + isa => HashRef [ + Dict [ + id => Int, + product => Str, + sec_level => Str, + is_open => Bool, + created_at => $DateTime, + ], + ], +); + +has 'check_open_state' => ( + is => 'ro', + isa => CodeRef, + default => sub { return \&is_open_state; }, +); + +has 'events' => ( + is => 'lazy', + isa => ArrayRef [ + Dict [ + bug_id => Int, + bug_when => $DateTime, + field_name => Enum [qw(bug_status keywords)], + removed => Str, + added => Str, + ], + ], +); + +has 'results' => ( + is => 'lazy', + isa => ArrayRef [ + Dict [ + date => $DateTime, + bugs_by_product => HashRef [ + Dict [ + open => ArrayRef [Int], + closed => ArrayRef [Int], + median_age_open => Num + ] + ], + bugs_by_sec_keyword => HashRef [ + Dict [ + open => ArrayRef [Int], + closed => ArrayRef [Int], + median_age_open => Num + ] + ], + ], + ], +); + +sub _build_initial_bug_ids { + # TODO: Handle changes in product (e.g. gravyarding) by searching the events table + # for changes to the 'product' field where one of $self->products is found in + # the 'removed' field, add the related bug id to the list of initial bugs. + my ($self) = @_; + my $dbh = Bugzilla->dbh; + my $products = join ', ', map { $dbh->quote($_) } @{ $self->products }; + my $sec_keywords = join ', ', map { $dbh->quote($_) } @{ $self->sec_keywords }; + my $query = qq{ + SELECT + bug_id + FROM + bugs AS bug + JOIN products AS product ON bug.product_id = product.id + JOIN components AS component ON bug.component_id = component.id + JOIN keywords USING (bug_id) + JOIN keyworddefs AS keyword ON keyword.id = keywords.keywordid + WHERE + keyword.name IN ($sec_keywords) + AND product.name IN ($products) + }; + return Bugzilla->dbh->selectcol_arrayref($query); +} + +sub _build_initial_bugs { + my ($self) = @_; + my $bugs = {}; + my $bugs_list = Bugzilla::Bug->new_from_list( $self->initial_bug_ids ); + for my $bug (@$bugs_list) { + $bugs->{ $bug->id } = { + id => $bug->id, + product => $bug->product, + sec_level => ( + # Select the first keyword matching one of the target keywords + # (of which there _should_ only be one found anyway). + first { + my $x = $_; + grep { lc($_) eq lc( $x->name ) } @{ $self->sec_keywords } + } + @{ $bug->keyword_objects } + )->name, + is_open => $self->check_open_state->( $bug->status->name ), + created_at => datetime_from( $bug->creation_ts ), + }; + } + return $bugs; +} + +sub _build_events { + my ($self) = @_; + return [] if !(@{$self->initial_bug_ids}); + my $bug_ids = join ', ', @{ $self->initial_bug_ids }; + my $start_date = $self->start_date->ymd('-'); + my $query = qq{ + SELECT + bug_id, + bug_when, + field.name AS field_name, + CONCAT(removed) AS removed, + CONCAT(added) AS added + FROM + bugs_activity + JOIN fielddefs AS field ON fieldid = field.id + JOIN bugs AS bug USING (bug_id) + WHERE + bug_id IN ($bug_ids) + AND field.name IN ('keywords' , 'bug_status') + AND bug_when >= '$start_date 00:00:00' + GROUP BY bug_id , bug_when , field.name + }; + my $result = Bugzilla->dbh->selectall_hashref( $query, 'bug_id' ); + my @events = values %$result; + foreach my $event (@events) { + $event->{bug_when} = datetime_from( $event->{bug_when} ); + } + + # We sort by reverse chronological order instead of ORDER BY + # since values %hash doesn't guareentee any order. + @events = sort { $b->{bug_when} cmp $a->{bug_when} } @events; + return \@events; +} + +sub _build_results { + my ($self) = @_; + my $e = 0; + my $bugs = $self->initial_bugs; + my @results = (); + + # We must generate a report for each week in the target time interval, regardless of + # whether anything changed. The for loop here ensures that we do so. + for ( my $report_date = $self->end_date; $report_date >= $self->start_date; $report_date->subtract( weeks => 1 ) ) { + # We rewind events while there are still events existing which occured after the start + # of the report week. The bugs will reflect a snapshot of how they were at the start of the week. + # $self->events is ordered reverse chronologically, so the end of the array is the earliest event. + while ( $e < scalar @{ $self->events } + && ( @{ $self->events }[$e] )->{bug_when} > $report_date ) + { + my $event = @{ $self->events }[$e]; + my $bug = $bugs->{ $event->{bug_id} }; + + # Undo bug status changes + if ( $event->{field_name} eq 'bug_status' ) { + $bug->{is_open} = $self->check_open_state->( $event->{removed} ); + } + + # Undo keyword changes + if ( $event->{field_name} eq 'keywords' ) { + my $bug_sec_level = $bug->{sec_level}; + if ( $event->{added} =~ /\b\Q$bug_sec_level\E\b/ ) { + # If the currently set sec level was added in this event, remove it. + $bug->{sec_level} = undef; + } + if ( $event->{removed} ) { + # If a target sec keyword was removed, add the first one back. + my $removed_sec = first { + $event->{removed} =~ /\b\Q$_\E\b/ + } + @{ $self->sec_keywords }; + $bug->{sec_level} = $removed_sec if ($removed_sec); + } + } + + $e++; + } + + # Remove uncreated bugs + foreach my $bug_key ( keys %$bugs ) { + if ( $bugs->{$bug_key}->{created_at} > $report_date ) { + delete $bugs->{$bug_key}; + } + } + + # Report! + my $date_snapshot = $report_date->clone(); + my @bugs_snapshot = values %$bugs; + unshift @results, + { + date => $date_snapshot, + bugs_by_product => $self->_bugs_by_product( $date_snapshot, @bugs_snapshot ), + bugs_by_sec_keyword => $self->_bugs_by_sec_keyword( $date_snapshot, @bugs_snapshot ), + }; + } + + return \@results; +} + +sub _bugs_by_product { + my ( $self, $report_date, @bugs ) = @_; + my $result = {}; + my $groups = {}; + foreach my $product ( @{ $self->products } ) { + $groups->{$product} = []; + } + foreach my $bug (@bugs) { + # We skip over bugs with no sec level which can happen during event rewinding. + if ( $bug->{sec_level} ) { + push @{ $groups->{ $bug->{product} } }, $bug; + } + } + foreach my $product ( @{ $self->products } ) { + my @open = map { $_->{id} } grep { ( $_->{is_open} ) } @{ $groups->{$product} }; + my @closed = map { $_->{id} } grep { !( $_->{is_open} ) } @{ $groups->{$product} }; + my @ages = map { $_->{created_at}->subtract_datetime_absolute($report_date)->seconds / 86_400; } + grep { ( $_->{is_open} ) } @{ $groups->{$product} }; + $result->{$product} = { + open => \@open, + closed => \@closed, + median_age_open => @ages ? _median(@ages) : 0, + }; + } + + return $result; +} + +sub _bugs_by_sec_keyword { + my ( $self, $report_date, @bugs ) = @_; + my $result = {}; + my $groups = {}; + foreach my $sec_keyword ( @{ $self->sec_keywords } ) { + $groups->{$sec_keyword} = []; + } + foreach my $bug (@bugs) { + # We skip over bugs with no sec level which can happen during event rewinding. + if ( $bug->{sec_level} ) { + push @{ $groups->{ $bug->{sec_level} } }, $bug; + } + } + foreach my $sec_keyword ( @{ $self->sec_keywords } ) { + my @open = map { $_->{id} } grep { ( $_->{is_open} ) } @{ $groups->{$sec_keyword} }; + my @closed = map { $_->{id} } grep { !( $_->{is_open} ) } @{ $groups->{$sec_keyword} }; + my @ages = map { $_->{created_at}->subtract_datetime_absolute($report_date)->seconds / 86_400 } + grep { ( $_->{is_open} ) } @{ $groups->{$sec_keyword} }; + $result->{$sec_keyword} = { + open => \@open, + closed => \@closed, + median_age_open => @ages ? _median(@ages) : 0, + }; + } + + return $result; +} + +sub _median { + # From tlm @ https://www.perlmonks.org/?node_id=474564. Jul 14, 2005 + return sum( ( sort { $a <=> $b } @_ )[ int( $#_ / 2 ), ceil( $#_ / 2 ) ] ) / 2; +} + +1; diff --git a/scripts/secbugsreport.pl b/scripts/secbugsreport.pl new file mode 100644 index 000000000..ae0639e20 --- /dev/null +++ b/scripts/secbugsreport.pl @@ -0,0 +1,83 @@ +#!/usr/bin/perl + +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. +# +# Usage secbugsreport.pl YYYY MM DD, e.g. secbugsreport.pl $(date +'%Y %m %d') + +use 5.10.1; +use strict; +use warnings; + +use lib qw(. lib local/lib/perl5); + +use Bugzilla; +use Bugzilla::Component; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Mailer; +use Bugzilla::Report::SecurityRisk; + +use DateTime; +use URI; +use JSON::MaybeXS; + +BEGIN { Bugzilla->extensions } +Bugzilla->usage_mode(USAGE_MODE_CMDLINE); + +exit 0 unless Bugzilla->params->{report_secbugs_active}; +exit 0 unless defined $ARGV[0] && defined $ARGV[1] && defined $ARGV[2]; + +my $html; +my $template = Bugzilla->template(); +my $end_date = DateTime->new( year => $ARGV[0], month => $ARGV[1], day => $ARGV[2] ); +my $start_date = $end_date->clone()->subtract( months => 6 ); +my $report_week = $end_date->ymd('-'); +my $products = decode_json( Bugzilla->params->{report_secbugs_products} ); +my $sec_keywords = [ 'sec-critical', 'sec-high' ]; +my $report = Bugzilla::Report::SecurityRisk->new( + start_date => $start_date, + end_date => $end_date, + products => $products, + sec_keywords => $sec_keywords +); +my $vars = { + urlbase => Bugzilla->localconfig->{urlbase}, + report_week => $report_week, + products => $products, + sec_keywords => $sec_keywords, + results => $report->results, + build_bugs_link => \&build_bugs_link, +}; + +$template->process( 'reports/email/security-risk.html.tmpl', $vars, \$html ) + or ThrowTemplateError( $template->error() ); + +# For now, only send HTML email. +my $email = Email::MIME->create( + header_str => [ + From => Bugzilla->params->{'mailfrom'}, + To => Bugzilla->params->{report_secbugs_emails}, + Subject => "Security Bugs Report for $report_week" + ], + attributes => { + content_type => 'text/html', + charset => 'UTF-8', + encoding => 'quoted-printable', + }, + body_str => $html, +); + +MessageToMTA($email); + +sub build_bugs_link { + my ( $arr, $product ) = @_; + my $uri = URI->new( Bugzilla->localconfig->{urlbase} . 'buglist.cgi' ); + $uri->query_param( bug_id => ( join ',', @$arr ) ); + $uri->query_param( product => $product ) if $product; + return $uri->as_string; +} diff --git a/t/security-risk.t b/t/security-risk.t new file mode 100644 index 000000000..520953bc0 --- /dev/null +++ b/t/security-risk.t @@ -0,0 +1,156 @@ +#!/usr/bin/perl +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. +use strict; +use warnings; +use 5.10.1; +use lib qw( . lib local/lib/perl5 ); +use Bugzilla; + +BEGIN { Bugzilla->extensions }; + +use Test::More; +use Test2::Tools::Mock; +use Try::Tiny; + +use ok 'Bugzilla::Report::SecurityRisk'; +can_ok('Bugzilla::Report::SecurityRisk', qw(new results)); + +sub check_open_state_mock { + my ($state) = @_; + return grep { /^$state$/ } qw(UNCOMFIRMED NEW ASSIGNED REOPENED); +} + +try { + use Bugzilla::Report::SecurityRisk; + my $report = Bugzilla::Report::SecurityRisk->new( + start_date => DateTime->new( year => 2000, month => 1, day => 9 ), + end_date => DateTime->new( year => 2000, month => 1, day => 16 ), + products => [ 'Firefox', 'Core' ], + sec_keywords => [ 'sec-critical', 'sec-high' ], + check_open_state => \&check_open_state_mock, + initial_bug_ids => [ 1, 2, 3, 4 ], + initial_bugs => { + 1 => { + id => 1, + product => 'Firefox', + sec_level => 'sec-high', + is_open => 0, + created_at => DateTime->new( year => 2000, month => 1, day => 1 ), + }, + 2 => { + id => 2, + product => 'Core', + sec_level => 'sec-critical', + is_open => 0, + created_at => DateTime->new( year => 2000, month => 1, day => 1 ), + }, + 3 => { + id => 3, + product => 'Core', + sec_level => 'sec-high', + is_open => 1, + created_at => DateTime->new( year => 2000, month => 1, day => 5 ), + }, + 4 => { + id => 4, + product => 'Firefox', + sec_level => 'sec-critical', + is_open => 1, + created_at => DateTime->new( year => 2000, month => 1, day => 10 ), + }, + }, + events => [ + # Canned event's should be in reverse chronological order. + { + bug_id => 2, + bug_when => DateTime->new( year => 2000, month => 1, day => 14 ), + field_name => 'keywords', + removed => '', + added => 'sec-critical', + + }, + { + bug_id => 1, + bug_when => DateTime->new( year => 2000, month => 1, day => 12 ), + field_name => 'bug_status', + removed => 'ASSIGNED', + added => 'RESOLVED', + }, + ], + ); + my $actual_results = $report->results; + my $expected_results = [ + { + date => DateTime->new( year => 2000, month => 1, day => 9 ), + bugs_by_product => { + 'Firefox' => { + # Rewind the event that caused 1 to close. + open => [1], + closed => [], + median_age_open => 8 + }, + 'Core' => { + # 2 wasn't a sec-critical bug on the report date. + open => [3], + closed => [], + median_age_open => 4 + } + }, + bugs_by_sec_keyword => { + 'sec-critical' => { + # 2 wasn't a sec-crtical bug and 4 wasn't created yet on the report date. + open => [], + closed => [], + median_age_open => 0 + }, + 'sec-high' => { + # Rewind the event that caused 1 to close. + open => [ 1, 3 ], + closed => [], + median_age_open => 6 + } + }, + }, + { # The report on 2000-01-16 matches the state of initial_bugs. + date => DateTime->new( year => 2000, month => 1, day => 16 ), + bugs_by_product => { + 'Firefox' => { + open => [4], + closed => [1], + median_age_open => 6 + }, + 'Core' => { + open => [3], + closed => [2], + median_age_open => 11 + } + }, + bugs_by_sec_keyword => { + 'sec-critical' => { + open => [4], + closed => [2], + median_age_open => 6 + }, + 'sec-high' => { + open => [3], + closed => [1], + median_age_open => 11 + } + }, + }, + ]; + + is_deeply($actual_results, $expected_results, 'Report results are accurate'); + +} +catch { + fail('got an exception during main part of test'); + diag($_); +}; + +done_testing; diff --git a/template/en/default/admin/params/reports.html.tmpl b/template/en/default/admin/params/reports.html.tmpl new file mode 100644 index 000000000..79b6af35d --- /dev/null +++ b/template/en/default/admin/params/reports.html.tmpl @@ -0,0 +1,20 @@ +[%# This Source Code Form is subject to the terms of the Mozilla Public + # License, v. 2.0. If a copy of the MPL was not distributed with this + # file, You can obtain one at http://mozilla.org/MPL/2.0/. + # + # This Source Code Form is "Incompatible With Secondary Licenses", as + # defined by the Mozilla Public License, v. 2.0. + #%] +[% + title = "Reports" + desc = "Configure reporting parameters" +%] + +[% param_descs = { + report_secbugs_active => "Enable or disable the security $terms.bugs report feature." + report_secbugs_emails => + "Comma delimited list of the email addresses that the security $terms.bugs report will be sent to.", + report_secbugs_products => + "JSON array of the products the security $terms.bugs report will report on. e.g [\"Prod1\", \"Prod2\"]", + } +%] diff --git a/template/en/default/reports/email/security-risk.html.tmpl b/template/en/default/reports/email/security-risk.html.tmpl new file mode 100644 index 000000000..0fca42e05 --- /dev/null +++ b/template/en/default/reports/email/security-risk.html.tmpl @@ -0,0 +1,95 @@ +[%# This Source Code Form is subject to the terms of the Mozilla Public + # License, v. 2.0. If a copy of the MPL was not distributed with this + # file, You can obtain one at http://mozilla.org/MPL/2.0/. + # + # This Source Code Form is "Incompatible With Secondary Licenses", as + # defined by the Mozilla Public License, v. 2.0. + #%] + +[% PROCESS global/variables.none.tmpl %] + + + + + Security [% terms.Bugs %] Report for the week of [% report_week FILTER html %] + + + +

Security [% terms.Bugs %] Report for the week of [% report_week FILTER html %]

+

To narrow down open [% terms.bugs %] click on the link and at the bottom of the search results use the 'Edit Search' functionality to filter by component and so on. +This will filter only the open [% terms.bugs %] counted in the report (as long as you do not modify the '[% terms.Bugs %] numbered' section of the search). +

+ +

[% terms.Bugs %] By Severity

+
+ [%# *** ID, product, component, status, resolution, Hardware, and OS *** %] [% PROCESS section_status %] - + [% PROCESS section_spacer %] - + [% PROCESS section_details1 %] - + [% PROCESS section_spacer %] - + [%# *** severity, priority, version and milestone *** %] - [% PROCESS section_details2 %] - + [% PROCESS section_details2 %] + [%# *** assigned to and qa contact *** %] [% PROCESS section_people %] - + [% PROCESS section_spacer %] - + [% PROCESS section_url_keyword_whiteboard %] - + [% PROCESS section_spacer %] [%# *** Dependencies and duplicates *** %] @@ -138,7 +138,7 @@ [% PROCESS section_dependson_blocks %] - [% IF user.id %] + [% IF user.id %] - +
@@ -187,7 +187,6 @@
- [%# BMO hook for adding MozReview table %] [% Hook.process("after_bug_data") %] [% IF user.is_timetracker %] @@ -230,7 +229,7 @@ [% ELSE %] [% PROCESS summon_comment_box %] [% END %] - + @@ -242,35 +241,35 @@ [%# That's the main table, which contains all editable fields. %]
- Last Comment - + [%-# %][% terms.Bug %] [% bug.bug_id FILTER html %] - [%-# %] - + [%-# %] - [% IF Param("usebugaliases") %] [% IF bug.alias != "" %] - ([% bug.alias FILTER html %]) + ([% bug.alias FILTER html %]) [% END %] [% END %] [% bug.short_desc FILTER quoteUrls(bug) FILTER wbr %] - [% IF bug.check_can_change_field('short_desc', 0, 1) || + [% IF bug.check_can_change_field('short_desc', 0, 1) || bug.check_can_change_field('alias', 0, 1) %] (edit) [% END %] - - + +
- +
[% IF Param("usebugaliases") %] - [% IF bug.check_can_change_field('alias', 0, 1) %] + [% IF bug.check_can_change_field('alias', 0, 1) %] - [% END %] + [% END %] [%# *** Summary *** %] - [%###############%] + [%###############%] [%# Component #%] [%###############%] @@ -352,9 +351,9 @@ [% PROCESS select selname => "version" %] - [%############%] + [%############%] [%# PLATFORM #%] - [%############%] + [%############%] [% END %] - + [% IF use_keywords %] [% END %] - + [% END %] [%############################################################################%] @@ -488,7 +487,7 @@ [% INCLUDE global/user.html.tmpl who = bug.assigned_to %] (edit) [% IF bug.assigned_to.id != user.id %] - (take) [% END %] @@ -506,10 +505,10 @@ [% ELSE %] [% INCLUDE global/user.html.tmpl who = bug.assigned_to %] @@ -537,7 +536,7 @@ [% INCLUDE global/user.html.tmpl who = bug.qa_contact %] (edit) [% IF bug.qa_contact.id != user.id %] - (take) [% END %] @@ -556,15 +555,15 @@ [% END %] @@ -678,20 +677,20 @@ [% BLOCK section_dependson_blocks %] - [% INCLUDE dependencies + [% INCLUDE dependencies field = bug_fields.dependson deps = bug.depends_on_obj %] - + - [% INCLUDE dependencies + [% INCLUDE dependencies field = bug_fields.blocked deps = bug.blocks_obj %] - + - + - + [% END %] @@ -844,10 +843,10 @@ || (!has_role && user.settings.state_addselfcc.value == 'cc_unless_role') %]> -
+
[% END %] [% END %] - [% bug.cc.size || 0 FILTER html %] + [% bug.cc.size || 0 FILTER html %] [% IF bug.cc.size == 1 %] user [% ELSE %] @@ -863,7 +862,7 @@ ([% IF user.id %]edit[% ELSE %]show[% END %]) [% IF user.id && bug.cc.size %]
-
    +
      [% FOREACH c = bug.cc %]
    • [% c FILTER email FILTER html %]
    • [% END %] @@ -912,11 +911,11 @@ [% IF user.id || bug.cc.size %] [% END %] @@ -952,7 +951,7 @@ [% BLOCK section_see_also %] [% IF Param('use_see_also') || bug.see_also.size %]
- [% INCLUDE bug/field.html.tmpl + [% INCLUDE bug/field.html.tmpl field = bug_fields.see_also value = bug.see_also editable = bug.check_can_change_field('see_also', 0, 1) @@ -1011,7 +1010,7 @@ YAHOO.util.Dom.removeClass(rows[i], 'bz_default_hidden'); } YAHOO.util.Event.preventDefault(e); - }); + }); [% END %] @@ -1070,31 +1069,31 @@ - + [% END %] [%############################################################################%] @@ -1160,7 +1159,7 @@ [% INCLUDE bug/field.html.tmpl field = bug_fields.deadline, value = bug.deadline, no_tds = 1 editable = bug.check_can_change_field('deadline', 0, 1) %] - + -
- : [% ELSIF bug.alias %] @@ -281,12 +280,12 @@ [% INCLUDE input inputname => "alias" size => "40" maxlength => "40" - no_td => 1 - %][% ")" IF NOT bug.check_can_change_field('alias', 0, 1) + no_td => 1 + %][% ")" IF NOT bug.check_can_change_field('alias', 0, 1) && bug.alias %]
@@ -334,7 +333,7 @@ value = bug.classification editable = bug.check_can_change_field('product', 0, 1) %]
: @@ -364,8 +363,8 @@ bug = bug, field = bug_fields.rep_platform, no_tds = 1, value = bug.rep_platform editable = bug.check_can_change_field('rep_platform', 0, 1) %] - [%+ INCLUDE bug/field.html.tmpl - bug = bug, field = bug_fields.op_sys, + [%+ INCLUDE bug/field.html.tmpl + bug = bug, field = bug_fields.op_sys, no_tds = 1, value = bug.op_sys editable = bug.check_can_change_field('op_sys', 0, 1) %] [%# BMO - hook for hw/os detection from reporter %] @@ -396,7 +395,7 @@ [% END %] [% END %] [% IF bug.user.canedit || bug.user.isreporter %] - (edit) [% END %] @@ -413,7 +412,7 @@ [% INCLUDE input inputname => "status_whiteboard" size => "40" colspan => 2 %]
@@ -468,7 +467,7 @@ [% PROCESS select selname = "target_milestone" %]
  Show dependency tree - + [% IF Param('webdotbase') %] / graph [% END %] @@ -721,7 +720,7 @@ [% IF NOT emitted_description %] [% emitted_description = 1 %]
- Only users in all of the selected groups can view this + Only users in all of the selected groups can view this [%+ terms.bug %]:

Unchecking all boxes makes this a more public [% terms.bug %]. @@ -730,7 +729,7 @@ [% END %] [% IF group.ingroup %] - [% END %] @@ -745,20 +744,20 @@ [% IF emitted_description %] [% IF NOT inallgroups %] -

Only members of a group can change the +

Only members of a group can change the visibility of [% terms.abug %] for that group.

[% END %] [% END %] [% IF inagroup %]
- Users in the roles selected below can always view + Users in the roles selected below can always view this [% terms.bug %]:
- [% user_can_edit_accessible = - bug.check_can_change_field("reporter_accessible", 0, 1) + [% user_can_edit_accessible = + bug.check_can_change_field("reporter_accessible", 0, 1) %] [% IF user_can_edit_accessible %] @@ -770,8 +769,8 @@
- [% user_can_edit_accessible = - bug.check_can_change_field("cclist_accessible", 0, 1) + [% user_can_edit_accessible = + bug.check_can_change_field("cclist_accessible", 0, 1) %] [% IF user_can_edit_accessible %] @@ -809,7 +808,7 @@ [% bug.creation_ts FILTER time("%Y-%m-%d %H:%M %Z") %] by [% INCLUDE global/user.html.tmpl who = bug.reporter %]
Modified: @@ -818,7 +817,7 @@ [% bug.delta_ts FILTER time("%Y-%m-%d %H:%M %Z") %] ([%# terms.Bug %]History) - +
[% IF bug.check_can_change_field(field.name, 0, 1) %] - [% END %] - + [% FOREACH dep_bug = deps %] [% dep_bug.id FILTER bug_link(dep_bug, use_alias => 1) FILTER none %][% " " %] [% END %] [% IF bug.check_can_change_field(field.name, 0, 1) %] - (edit) [% END %]
@@ -1169,7 +1168,7 @@ blocking this [% terms.bug %])
+
[% END %] [%############################################################################%] @@ -1179,7 +1178,7 @@ [% BLOCK comment_box %]
[% IF user.id %] -
[% END %] @@ -1267,7 +1266,7 @@ [% BLOCK select %]
- [% IF bug.check_can_change_field(selname, 0, 1) + [% IF bug.check_can_change_field(selname, 0, 1) AND bug.choices.${selname}.size > 1 %]
+ + + [% FOREACH keyword IN sec_keywords %] + + [% END %] + + + + [% FOREACH keyword IN sec_keywords %] + + + [% END %] + + [% FOREACH result IN results.reverse %] + + + [% FOREACH keyword IN sec_keywords %] + + + [% END %] + + [% END %] +
[% keyword FILTER html %]
Open CountMedian Days Open
[% result.date.ymd('-') FILTER html %] + [% IF result.bugs_by_sec_keyword.$keyword.open.size %] + + [% result.bugs_by_sec_keyword.$keyword.open.size FILTER html %] + + [% ELSE %] + [% result.bugs_by_sec_keyword.$keyword.open.size FILTER html %] + [% END %] + + [% result.bugs_by_sec_keyword.$keyword.median_age_open FILTER format("%.2f") FILTER html %] +
+ +

Sec-Critical + Sec-High [% terms.Bugs %] by Product

+ + + + [% FOREACH product IN products %] + + [% END %] + + + + [% FOREACH product IN products %] + + + [% END %] + + [% FOREACH result IN results.reverse %] + + + [% FOREACH product IN products %] + + + [% END %] + + [% END %] +
[% product FILTER html %]
Open CountMedian Days Open
[% result.date.ymd('-') FILTER html %] + [% IF result.bugs_by_product.$product.open.size %] + + [% result.bugs_by_product.$product.open.size FILTER html %] + + [% ELSE %] + [% result.bugs_by_product.$product.open.size FILTER html %] + [% END %] + + [% result.bugs_by_product.$product.median_age_open FILTER format("%.2f") FILTER html %] +
+ + -- cgit v1.2.3-24-g4f1b From c8b47ef3d5b39ab99065fe387c9ae93301b3aab0 Mon Sep 17 00:00:00 2001 From: Israel Madueme Date: Mon, 10 Sep 2018 13:30:05 -0400 Subject: no bug - add X-Bugzilla-Type to secbugsreport email --- scripts/secbugsreport.pl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/secbugsreport.pl b/scripts/secbugsreport.pl index ae0639e20..81041b222 100644 --- a/scripts/secbugsreport.pl +++ b/scripts/secbugsreport.pl @@ -62,7 +62,8 @@ my $email = Email::MIME->create( header_str => [ From => Bugzilla->params->{'mailfrom'}, To => Bugzilla->params->{report_secbugs_emails}, - Subject => "Security Bugs Report for $report_week" + Subject => "Security Bugs Report for $report_week", + 'X-Bugzilla-Type' => 'admin' ], attributes => { content_type => 'text/html', -- cgit v1.2.3-24-g4f1b From 8a73abb9a369e840708dcb6a48be2e19a8e6a284 Mon Sep 17 00:00:00 2001 From: Israel Madueme Date: Mon, 10 Sep 2018 13:43:48 -0400 Subject: bump to version 20180910.1 --- Bugzilla.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Bugzilla.pm b/Bugzilla.pm index 44286c75b..c31e2088b 100644 --- a/Bugzilla.pm +++ b/Bugzilla.pm @@ -13,7 +13,7 @@ use warnings; use Bugzilla::Logging; -our $VERSION = '20180820.1'; +our $VERSION = '20180910.1'; use Bugzilla::Auth; use Bugzilla::Auth::Persist::Cookie; -- cgit v1.2.3-24-g4f1b From 4945aec87af9752cc7a5d3ec178b4377bae644a3 Mon Sep 17 00:00:00 2001 From: dklawren Date: Wed, 12 Sep 2018 11:16:30 -0400 Subject: Bug 1487422 - Remove Phabricator section from MyDashboard and related WebService API --- .../en/default/pages/mydashboard.html.tmpl | 37 ++++---- extensions/MyDashboard/web/js/flags.js | 101 ++------------------ extensions/MyDashboard/web/js/query.js | 2 +- extensions/PhabBugz/lib/Util.pm | 45 --------- extensions/PhabBugz/lib/WebService.pm | 105 +-------------------- 5 files changed, 28 insertions(+), 262 deletions(-) diff --git a/extensions/MyDashboard/template/en/default/pages/mydashboard.html.tmpl b/extensions/MyDashboard/template/en/default/pages/mydashboard.html.tmpl index 5c372db3c..734be28df 100644 --- a/extensions/MyDashboard/template/en/default/pages/mydashboard.html.tmpl +++ b/extensions/MyDashboard/template/en/default/pages/mydashboard.html.tmpl @@ -45,7 +45,7 @@ {{#if removed}} {{#unless added}} - Removed: + Removed: {{/unless}} {{removed}} {{/if}} @@ -127,30 +127,29 @@ %] + [% IF Param('phabricator_enabled') %] +
+ [% END %] + [% BLOCK requests_table %]
-
[% title FILTER html_light %]
- Loading... - - 0 reviews found - | Refresh - | Buglist - -
+
[% title FILTER html_light %]
+ Loading... + + 0 reviews found + | Refresh + | Buglist + +
[% END %] - [% ## no-008filter - # requires PhabBugz extension - IF Param('phabricator_enabled'); - title = 'Phabricator Reviews Requested of You'; - PROCESS requests_table name='reviews' title=title; - END; - - PROCESS requests_table name='requestee' title='Flags Requested of You'; - PROCESS requests_table name='requester' title='Flags You Have Requested'; - %] + [% PROCESS requests_table name='requestee' title='Flags Requested of You' %] + [% PROCESS requests_table name='requester' title='Flags You Have Requested' %] +
[% IF user.showmybugslink OR user.queries.size OR user.queries_subscribed.size %]
diff --git a/extensions/MyDashboard/web/js/flags.js b/extensions/MyDashboard/web/js/flags.js index 8931e277a..b340b4ee1 100644 --- a/extensions/MyDashboard/web/js/flags.js +++ b/extensions/MyDashboard/web/js/flags.js @@ -16,18 +16,15 @@ $(function () { // Common var counter = 0; var dataSource = { - reviews: null, requestee: null, requester: null }; var dataTable = { - reviews: null, requestee: null, requester: null }; - var hasReviews = !!document.getElementById('reviews_container'); - var updateRequestsTable = function(type) { + var updateFlagTable = function(type) { if (!type) return; counter = counter + 1; @@ -50,15 +47,14 @@ $(function () { if (o.error && o.error.message) { alert("Failed to load requests:\n\n" + o.error.message); } else { - alert("Failed to load requests."); + alert("Failed to load requests"); } } }; - var method = type === 'reviews' ? 'PhabBugz.needs_review' : 'MyDashboard.run_flag_query'; var json_object = { version: "1.1", - method: method, + method: "MyDashboard.run_flag_query", id: counter, params: { type : type, @@ -138,84 +134,6 @@ $(function () { } }; - var phabAuthorFormatter = function(o) { - return '' + - Y.Escape.html(o.data.author_name) + ''; - }; - - var phabRowFormatter = function(o) { - var row = o.cell.ancestor(); - - // space in the 'flags' tables is tight - // render requests as two rows - diff title on first row, columns - // on second - - row.insert( - '' + - '' + - '' + - Y.Escape.html(o.data.title) + '', - 'after'); - - o.cell.setHTML('D' + o.data.id + ''); - - return false; - }; - - // Reviews - if (hasReviews) { - dataSource.reviews = new Y.DataSource.IO({ source: 'jsonrpc.cgi' }); - dataSource.reviews.on('error', function(e) { - console.log(e); - try { - var response = Y.JSON.parse(e.data.responseText); - if (response.error) - e.error.message = response.error.message; - } catch(ex) { - // ignore - } - }); - dataTable.reviews = new Y.DataTable({ - columns: [ - { key: 'author_email', label: 'Requester', sortable: true, - formatter: phabAuthorFormatter, allowHTML: true }, - { key: 'id', label: 'Revision', sortable: true, - nodeFormatter: phabRowFormatter, allowHTML: true }, - { key: 'bug_id', label: 'Bug', sortable: true, - formatter: bugLinkFormatter, allowHTML: true }, - { key: 'updated', label: 'Updated', sortable: true, - formatter: updatedFormatter, allowHTML: true } - ], - strings: { - emptyMessage: 'No review requests.', - } - }); - - dataTable.reviews.plug(Y.Plugin.DataTableSort); - - dataTable.reviews.plug(Y.Plugin.DataTableDataSource, { - datasource: dataSource.reviews - }); - - dataSource.reviews.plug(Y.Plugin.DataSourceJSONSchema, { - schema: { - resultListLocator: 'result.result', - resultFields: [ 'author_email', 'author_name', 'bug_id', - 'bug_status', 'bug_summary', 'id', 'status', 'title', - 'updated', 'updated_fancy', 'url' ] - } - }); - - dataTable.reviews.render("#reviews_table"); - - Y.one('#reviews_refresh').on('click', function(e) { - updateRequestsTable('reviews'); - }); - Y.one('#reviews_buglist').on('click', function(e) { - loadBugList('reviews'); - }); - } - // Requestee dataSource.requestee = new Y.DataSource.IO({ source: 'jsonrpc.cgi' }); dataSource.requestee.on('error', function(e) { @@ -259,7 +177,7 @@ $(function () { dataTable.requestee.render("#requestee_table"); Y.one('#requestee_refresh').on('click', function(e) { - updateRequestsTable('requestee'); + updateFlagTable('requestee'); }); Y.one('#requestee_buglist').on('click', function(e) { loadBugList('requestee'); @@ -307,23 +225,18 @@ $(function () { }); Y.one('#requester_refresh').on('click', function(e) { - updateRequestsTable('requester'); + updateFlagTable('requester'); }); Y.one('#requester_buglist').on('click', function(e) { loadBugList('requester'); }); // Initial load - if (hasReviews) { - Y.on("contentready", function (e) { - updateRequestsTable('reviews'); - }, "#reviews_table"); - } Y.on("contentready", function (e) { - updateRequestsTable("requestee"); + updateFlagTable("requestee"); }, "#requestee_table"); Y.on("contentready", function (e) { - updateRequestsTable("requester"); + updateFlagTable("requester"); }, "#requester_table"); }); }); diff --git a/extensions/MyDashboard/web/js/query.js b/extensions/MyDashboard/web/js/query.js index e5e0979a1..53139d27f 100644 --- a/extensions/MyDashboard/web/js/query.js +++ b/extensions/MyDashboard/web/js/query.js @@ -14,7 +14,7 @@ if (typeof(MyDashboard) == 'undefined') { $(function() { YUI({ base: 'js/yui3/', - combine: false, + combine: false, groups: { gallery: { combine: false, diff --git a/extensions/PhabBugz/lib/Util.pm b/extensions/PhabBugz/lib/Util.pm index a7ae98744..f2876366f 100644 --- a/extensions/PhabBugz/lib/Util.pm +++ b/extensions/PhabBugz/lib/Util.pm @@ -35,7 +35,6 @@ our @EXPORT = qw( create_revision_attachment get_attachment_revisions get_bug_role_phids - get_needs_review intersect is_attachment_phab_revision request @@ -206,48 +205,4 @@ sub set_phab_user { return Bugzilla->set_user($user, scope_guard => 1); } -sub get_needs_review { - my ($user) = @_; - $user //= Bugzilla->user; - return unless $user->id; - - my $phab_user = Bugzilla::Extension::PhabBugz::User->new_from_query( - { - ids => [ $user->id ] - } - ); - - return [] unless $phab_user; - - my $diffs = request( - 'differential.revision.search', - { - attachments => { - reviewers => 1, - }, - constraints => { - reviewerPHIDs => [$phab_user->phid], - statuses => ["open()"], - }, - order => 'newest', - } - ); - ThrowCodeError('phabricator_api_error', { reason => 'Malformed Response' }) - unless exists $diffs->{result}{data}; - - my @revisions; - foreach my $revision ( @{ $diffs->{result}{data} } ) { - foreach my $reviewer ( @{ $revision->{attachments}->{reviewers}->{reviewers} } ) { - if ( $reviewer->{reviewerPHID} eq $phab_user->phid - && $reviewer->{status} =~ /^(?:added|blocking)$/ ) - { - push @revisions, $revision; - last; - } - } - } - - return \@revisions; -} - 1; diff --git a/extensions/PhabBugz/lib/WebService.pm b/extensions/PhabBugz/lib/WebService.pm index fa9306667..19a758a70 100644 --- a/extensions/PhabBugz/lib/WebService.pm +++ b/extensions/PhabBugz/lib/WebService.pm @@ -16,29 +16,23 @@ use base qw(Bugzilla::WebService); use Bugzilla::Constants; use Bugzilla::Error; use Bugzilla::User; -use Bugzilla::Util qw(detaint_natural datetime_from time_ago trick_taint); +use Bugzilla::Util qw(detaint_natural trick_taint); use Bugzilla::WebService::Constants; use Bugzilla::Extension::PhabBugz::Constants; -use Bugzilla::Extension::PhabBugz::Util qw( - get_needs_review -); -use DateTime (); -use List::Util qw(first uniq); +use List::Util qw(first); use List::MoreUtils qw(any); use MIME::Base64 qw(decode_base64); use constant READ_ONLY => qw( check_user_enter_bug_permission check_user_permission_for_bug - needs_review ); use constant PUBLIC_METHODS => qw( check_user_enter_bug_permission check_user_permission_for_bug - needs_review set_build_target ); @@ -99,95 +93,6 @@ sub check_user_enter_bug_permission { }; } -sub needs_review { - my ($self, $params) = @_; - - $self->_check_phabricator(); - - my $user = Bugzilla->login(LOGIN_REQUIRED); - my $dbh = Bugzilla->dbh; - - my $reviews = get_needs_review(); - - my $authors = Bugzilla::Extension::PhabBugz::User->match({ - phids => [ - uniq - grep { defined } - map { $_->{fields}{authorPHID} } - @$reviews - ] - }); - - my %author_phab_to_id = map { $_->phid => $_->bugzilla_user->id } @$authors; - my %author_id_to_user = map { $_->bugzilla_user->id => $_->bugzilla_user } @$authors; - - # bug data - my $visible_bugs = $user->visible_bugs([ - uniq - grep { $_ } - map { $_->{fields}{'bugzilla.bug-id'} } - @$reviews - ]); - - # get all bug statuses and summaries in a single query to avoid creation of - # many bug objects - my %bugs; - if (@$visible_bugs) { - #<<< - my $bug_rows =$dbh->selectall_arrayref( - 'SELECT bug_id, bug_status, short_desc ' . - ' FROM bugs ' . - ' WHERE bug_id IN (' . join(',', ('?') x @$visible_bugs) . ')', - { Slice => {} }, - @$visible_bugs - ); - #>>> - %bugs = map { $_->{bug_id} => $_ } @$bug_rows; - } - - # build result - my $datetime_now = DateTime->now(time_zone => $user->timezone); - my @result; - foreach my $review (@$reviews) { - my $review_flat = { - id => $review->{id}, - title => $review->{fields}{title}, - url => Bugzilla->params->{phabricator_base_uri} . 'D' . $review->{id}, - }; - - # show date in user's timezone - my $datetime = DateTime->from_epoch( - epoch => $review->{fields}{dateModified}, - time_zone => 'UTC' - ); - $datetime->set_time_zone($user->timezone); - $review_flat->{updated} = $datetime->strftime('%Y-%m-%d %T %Z'); - $review_flat->{updated_fancy} = time_ago($datetime, $datetime_now); - - # review requester - if (my $author = $author_id_to_user{$author_phab_to_id{ $review->{fields}{authorPHID} }}) { - $review_flat->{author_name} = $author->name; - $review_flat->{author_email} = $author->email; - } - else { - $review_flat->{author_name} = 'anonymous'; - $review_flat->{author_email} = 'anonymous'; - } - - # referenced bug - if (my $bug_id = $review->{fields}{'bugzilla.bug-id'}) { - my $bug = $bugs{$bug_id}; - $review_flat->{bug_id} = $bug_id; - $review_flat->{bug_status} = $bug->{bug_status}; - $review_flat->{bug_summary} = $bug->{short_desc}; - } - - push @result, $review_flat; - } - - return { result => \@result }; -} - sub set_build_target { my ( $self, $params ) = @_; @@ -257,12 +162,6 @@ sub rest_resources { }, }, }, - # Review requests - qw{^/phabbugz/needs_review$}, { - GET => { - method => 'needs_review', - }, - } ]; } -- cgit v1.2.3-24-g4f1b From 5ab05f16aaa44042666c69065dc1c7576eb0de51 Mon Sep 17 00:00:00 2001 From: dklawren Date: Wed, 12 Sep 2018 19:56:20 -0400 Subject: Bug 1490687 - Stop setting r+s on Phabricator attachments --- extensions/PhabBugz/lib/Feed.pm | 134 ---------------------------------------- 1 file changed, 134 deletions(-) diff --git a/extensions/PhabBugz/lib/Feed.pm b/extensions/PhabBugz/lib/Feed.pm index f2a440bb1..2dbeef6a7 100644 --- a/extensions/PhabBugz/lib/Feed.pm +++ b/extensions/PhabBugz/lib/Feed.pm @@ -14,7 +14,6 @@ use IO::Async::Loop; use List::Util qw(first); use List::MoreUtils qw(any uniq); use Moo; -use Scalar::Util qw(blessed); use Try::Tiny; use Type::Params qw( compile ); use Type::Utils; @@ -501,105 +500,6 @@ sub process_revision_change { $attachment->update($timestamp); } - # REVIEWER STATUSES - - my (@accepted, @denied); - foreach my $review (@{ $revision->reviews }) { - push @accepted, $review->{user} if $review->{status} eq 'accepted'; - push @denied, $review->{user} if $review->{status} eq 'rejected'; - } - - my @accepted_user_ids = map { $_->bugzilla_user->id } grep { defined $_->bugzilla_user } @accepted; - my @denied_user_ids = map { $_->bugzilla_user->id } grep { defined $_->bugzilla_user } @denied; - my %reviewers_hash = map { $_->{user}->name => 1 } @{ $revision->reviews }; - - foreach my $attachment (@attachments) { - my ($attach_revision_id) = ($attachment->filename =~ PHAB_ATTACHMENT_PATTERN); - next if $revision->id != $attach_revision_id; - - # Clear old accepted review flags if no longer accepted - my (@denied_flags, @new_flags, @removed_flags, %accepted_done, $flag_type); - foreach my $flag (@{ $attachment->flags }) { - next if $flag->type->name ne 'review'; - $flag_type = $flag->type if $flag->type->is_active; - next if $flag->status ne '+'; - if (any { $flag->setter->id == $_ } @denied_user_ids) { - INFO('Denying review flag set by ' . $flag->setter->name); - push(@denied_flags, { id => $flag->id, setter => $flag->setter, status => 'X' }); - } - if (any { $flag->setter->id == $_ } @accepted_user_ids) { - INFO('Skipping as review+ already set by ' . $flag->setter->name); - $accepted_done{$flag->setter->id}++; - } - if (!any { $flag->setter->id == $_ } (@accepted_user_ids, @denied_user_ids)) { - INFO('Clearing review+ flag set by ' . $flag->setter->name); - push(@removed_flags, { id => $flag->id, setter => $flag->setter, status => 'X' }); - } - } - - $flag_type ||= first { $_->name eq 'review' && $_->is_active } @{ $attachment->flag_types }; - - die "Unable to find review flag!" unless $flag_type; - - # Create new flags - foreach my $user_id (@accepted_user_ids) { - next if $accepted_done{$user_id}; - my $user = Bugzilla::User->check({ id => $user_id, cache => 1 }); - INFO('Setting new review+ flag for ' . $user->name); - push(@new_flags, { type_id => $flag_type->id, setter => $user, status => '+' }); - } - - # Process each flag change by updating the flag and adding a comment - foreach my $flag_data (@new_flags) { - my $comment = $flag_data->{setter}->name . " has approved the revision."; - $self->add_flag_comment( - { - bug => $bug, - attachment => $attachment, - comment => $comment, - user => $flag_data->{setter}, - old_flags => [], - new_flags => [$flag_data], - timestamp => $timestamp - } - ); - } - foreach my $flag_data (@denied_flags) { - my $comment = $flag_data->{setter}->name . " has requested changes to the revision.\n"; - $self->add_flag_comment( - { - bug => $bug, - attachment => $attachment, - comment => $comment, - user => $flag_data->{setter}, - old_flags => [$flag_data], - new_flags => [], - timestamp => $timestamp - } - ); - } - foreach my $flag_data (@removed_flags) { - my $comment; - if ( exists $reviewers_hash{ $flag_data->{setter}->name } ) { - $comment = "Flag set by " . $flag_data->{setter}->name . " is no longer active.\n"; - } - else { - $comment = $flag_data->{setter}->name . " has been removed from the revision.\n"; - } - $self->add_flag_comment( - { - bug => $bug, - attachment => $attachment, - comment => $comment, - user => $flag_data->{setter}, - old_flags => [$flag_data], - new_flags => [], - timestamp => $timestamp - } - ); - } - } - # FINISH UP $bug->update($timestamp); @@ -839,38 +739,4 @@ sub get_group_members { ); } -sub add_flag_comment { - state $check = compile( - $Invocant, - Dict [ - bug => Bug, - attachment => Attachment, - comment => Str, - user => User, - old_flags => ArrayRef, - new_flags => ArrayRef, - timestamp => Str, - ], - ); - my ( $self, $params ) = $check->(@_); - my ( $bug, $attachment, $comment, $user, $old_flags, $new_flags, $timestamp ) - = @$params{qw(bug attachment comment user old_flags new_flags timestamp)}; - - # when this function returns, Bugzilla->user will return to its previous value. - my $restore_prev_user = Bugzilla->set_user($user, scope_guard => 1); - - INFO("Flag comment: $comment"); - $bug->add_comment( - $comment, - { - isprivate => $attachment->isprivate, - type => CMT_ATTACHMENT_UPDATED, - extra_data => $attachment->id - } - ); - - $attachment->set_flags( $old_flags, $new_flags ); - $attachment->update($timestamp); -} - 1; -- cgit v1.2.3-24-g4f1b From d3e00529333fb2eadac9c22a45f20c1050401952 Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Thu, 13 Sep 2018 22:37:55 +0200 Subject: Bug 1490708 - Ensure we always call DBIx::Connector->dbh before any DBI method (#744) The code didn't allow a way of doing this without a lot of work. So I had to take the following approach: The 'dbh' attribute is now a method that delegates to DBIx::Connector's dbh method. Per the docs, ->dbh() "Returns the connection's database handle. It will use a an existing handle if there is one, if the process has not been forked or a new thread spawned, and if the database is pingable. Otherwise, it will instantiate, cache, and return a new handle." Then there is the matter of the 'handles' on dbh. I've used Package::Stash to insert proxy methods into the class when it is loaded. --- Bugzilla/DB.pm | 43 ++++++++++++++++++++++++++++--------------- Makefile.PL | 1 + 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/Bugzilla/DB.pm b/Bugzilla/DB.pm index 80404131a..cd5954219 100644 --- a/Bugzilla/DB.pm +++ b/Bugzilla/DB.pm @@ -14,15 +14,9 @@ use DBI; use DBIx::Connector; our %Connector; -has 'dbh' => ( +has 'connector' => ( is => 'lazy', - handles => [ - qw[ - begin_work column_info commit disconnect do errstr get_info last_insert_id ping prepare - primary_key quote_identifier rollback selectall_arrayref selectall_hashref - selectcol_arrayref selectrow_array selectrow_arrayref selectrow_hashref table_info - ] - ], + handles => [ qw( dbh ) ], ); use Bugzilla::Constants; @@ -44,6 +38,29 @@ has [qw(dsn user pass attrs)] => ( required => 1, ); + +# Install proxy methods to the DBI object. +# We can't use handles() as DBIx::Connector->dbh has to be called each +# time we need a DBI handle to ensure the connection is alive. +{ + my @DBI_METHODS = qw( + begin_work column_info commit disconnect do errstr get_info last_insert_id ping prepare + primary_key quote_identifier rollback selectall_arrayref selectall_hashref + selectcol_arrayref selectrow_array selectrow_arrayref selectrow_hashref table_info + ); + my $stash = Package::Stash->new(__PACKAGE__); + + foreach my $method (@DBI_METHODS) { + my $symbol = '&' . $method; + $stash->add_symbol( + $symbol => sub { + my $self = shift; + return $self->dbh->$method(@_); + } + ); + } +} + ##################################################################### # Constants ##################################################################### @@ -152,9 +169,7 @@ sub _connect { # instantiate the correct DB specific module - my $dbh = $pkg_module->new($params); - - return $dbh; + return $pkg_module->new($params); } sub _handle_error { @@ -1263,7 +1278,7 @@ sub bz_rollback_transaction { # Subclass Helpers ##################################################################### -sub _build_dbh { +sub _build_connector { my ($self) = @_; my ($dsn, $user, $pass, $override_attrs) = map { $self->$_ } qw(dsn user pass attrs); @@ -1295,9 +1310,7 @@ sub _build_dbh { } } - my $connector = $Connector{"$user.$dsn"} //= DBIx::Connector->new($dsn, $user, $pass, $attributes); - - return $connector->dbh; + return $Connector{"$user.$dsn"} //= DBIx::Connector->new($dsn, $user, $pass, $attributes); } ##################################################################### diff --git a/Makefile.PL b/Makefile.PL index 3c600bef6..6d4225e9a 100755 --- a/Makefile.PL +++ b/Makefile.PL @@ -72,6 +72,7 @@ my %requires = ( 'Moo' => '2.002004', 'MooX::StrictConstructor' => '0.008', 'Mozilla::CA' => '20160104', + 'Package::Stash' => '0.37', 'Parse::CPAN::Meta' => '1.44', 'Role::Tiny' => '2.000003', 'Scope::Guard' => '0.21', -- cgit v1.2.3-24-g4f1b From 54ed8cf5c8a97f9aeccbac30870acebb0059a964 Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Thu, 13 Sep 2018 21:23:30 -0400 Subject: no bug - cleanup a few nits in the SecurityRiskReport (#746) - sorted imports, with Moo and MooX::StrictConstructor at the top because they change the behavior of the code. - removed 'scalar' when comparing an array to an integer as it isn't required. - adjusted multi-line first { } to single line since it still fits and perltidy makes it look ugly. - store each 'result' hash in a $result variable, again to make perltidy format better. - change use of 'unshift ARRAY' to 'push ARRAY' and reverse(). The later performs fewer mallocs (push is much more effficient than unshift). Please check if this logic is right. --- Bugzilla/Report/SecurityRisk.pm | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/Bugzilla/Report/SecurityRisk.pm b/Bugzilla/Report/SecurityRisk.pm index 1b62d476c..5eb98fd7f 100644 --- a/Bugzilla/Report/SecurityRisk.pm +++ b/Bugzilla/Report/SecurityRisk.pm @@ -8,19 +8,18 @@ package Bugzilla::Report::SecurityRisk; use 5.10.1; +use Moo; +use MooX::StrictConstructor; -use Bugzilla; use Bugzilla::Error; use Bugzilla::Status qw(is_open_state); use Bugzilla::Util qw(datetime_from); - +use Bugzilla; use DateTime; use List::Util qw(any first sum); -use Moo; -use MooX::StrictConstructor; use POSIX qw(ceil); -use Types::Standard qw(Num Int Bool Str HashRef ArrayRef CodeRef Map Dict Enum); use Type::Utils; +use Types::Standard qw(Num Int Bool Str HashRef ArrayRef CodeRef Map Dict Enum); my $DateTime = class_type { class => 'DateTime' }; @@ -202,7 +201,7 @@ sub _build_results { # We rewind events while there are still events existing which occured after the start # of the report week. The bugs will reflect a snapshot of how they were at the start of the week. # $self->events is ordered reverse chronologically, so the end of the array is the earliest event. - while ( $e < scalar @{ $self->events } + while ( $e < @{ $self->events } && ( @{ $self->events }[$e] )->{bug_when} > $report_date ) { my $event = @{ $self->events }[$e]; @@ -222,10 +221,7 @@ sub _build_results { } if ( $event->{removed} ) { # If a target sec keyword was removed, add the first one back. - my $removed_sec = first { - $event->{removed} =~ /\b\Q$_\E\b/ - } - @{ $self->sec_keywords }; + my $removed_sec = first { $event->{removed} =~ /\b\Q$_\E\b/ } @{ $self->sec_keywords }; $bug->{sec_level} = $removed_sec if ($removed_sec); } } @@ -243,15 +239,15 @@ sub _build_results { # Report! my $date_snapshot = $report_date->clone(); my @bugs_snapshot = values %$bugs; - unshift @results, - { + my $result = { date => $date_snapshot, bugs_by_product => $self->_bugs_by_product( $date_snapshot, @bugs_snapshot ), bugs_by_sec_keyword => $self->_bugs_by_sec_keyword( $date_snapshot, @bugs_snapshot ), - }; + }; + push @results, $result; } - return \@results; + return [reverse @results]; } sub _bugs_by_product { -- cgit v1.2.3-24-g4f1b From 78a2dcba195b6ee3e44e4cd7df031d79bee2b491 Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Mon, 17 Sep 2018 17:49:41 -0400 Subject: Bug 1491973 - Add GeckoView to BMO::Data special casing --- extensions/BMO/lib/Data.pm | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/extensions/BMO/lib/Data.pm b/extensions/BMO/lib/Data.pm index dc6a3894f..ed576f1a5 100644 --- a/extensions/BMO/lib/Data.pm +++ b/extensions/BMO/lib/Data.pm @@ -93,6 +93,7 @@ tie(%$cf_visible_in_products, "Tie::IxHash", "External Software Affecting Firefox" => [], "Firefox" => [], "Firefox for Android" => [], + "GeckoView" => [], "JSS" => [], "MailNews Core" => [], "Mozilla Labs" => [], @@ -142,24 +143,27 @@ tie(%$cf_visible_in_products, "Tie::IxHash", "Firefox for Android" => [], "Firefox for iOS" => [], "Firefox" => [], + "GeckoView" => [], "Hello (Loop)" => [], "Cloud Services" => [], "Tech Evangelism" => [], "Toolkit" => [], }, qr/^cf_has_regression_range$/ => { - "Core" => [], + "Core" => [], "Firefox for Android" => [], "Firefox for iOS" => [], - "Firefox" => [], - "Toolkit" => [], + "Firefox" => [], + "GeckoView" => [], + "Toolkit" => [], }, qr/^cf_has_str$/ => { "Core" => [], "Firefox for Android" => [], "Firefox for iOS" => [], - "Firefox" => [], - "Toolkit" => [], + "Firefox" => [], + "GeckoView" => [], + "Toolkit" => [], }, qr/^cf_cab_review$/ => { "Infrastructure & Operations Graveyard" => [], -- cgit v1.2.3-24-g4f1b From 078cd25cf89223457f6d79040dc701f179887b30 Mon Sep 17 00:00:00 2001 From: Kohei Yoshino Date: Mon, 17 Sep 2018 17:50:21 -0400 Subject: Bug 1345673 - Open Bugzilla History in a New Window or Tab --- extensions/BugModal/web/bug_modal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/BugModal/web/bug_modal.js b/extensions/BugModal/web/bug_modal.js index a4ae83d72..55451d3bf 100644 --- a/extensions/BugModal/web/bug_modal.js +++ b/extensions/BugModal/web/bug_modal.js @@ -448,7 +448,7 @@ $(function() { $('#action-history') .click(function(event) { event.preventDefault(); - document.location.href = 'show_activity.cgi?id=' + BUGZILLA.bug_id; + window.open(`show_activity.cgi?id=${BUGZILLA.bug_id}`, '_blank'); }); // use scrollTo for in-page activity links -- cgit v1.2.3-24-g4f1b From 8c35832dd5d1de52b3dd5b4843be627b826a570d Mon Sep 17 00:00:00 2001 From: dklawren Date: Mon, 17 Sep 2018 17:51:18 -0400 Subject: Bug 1490901 - ReviewBoard stub attachments no longer make a redirect, download a text file instead --- extensions/BMO/lib/Data.pm | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/extensions/BMO/lib/Data.pm b/extensions/BMO/lib/Data.pm index ed576f1a5..349f88093 100644 --- a/extensions/BMO/lib/Data.pm +++ b/extensions/BMO/lib/Data.pm @@ -27,6 +27,21 @@ our @EXPORT = qw( $cf_visible_in_products # will result in the user being redirected to that URL when viewing the # attachment. +my $mozreview_url_re = qr{ + # begins with mozreview hostname + ^ + https?://reviewboard(?:-dev)?\.(?:allizom|mozilla)\.org + + # followed by a review path + /r/\d+ + + # ends with optional suffix + (?: / + | /diff/\#index_header + )? + $ +}ix; + sub phabricator_url_re { my $phab_uri = Bugzilla->params->{phabricator_base_uri} || 'https://example.com'; return qr/^\Q${phab_uri}\ED\d+$/i; @@ -39,6 +54,12 @@ our %autodetect_attach_urls = ( content_type => 'text/x-github-pull-request', can_review => 1, }, + reviewboard => { + title => 'MozReview', + regex => $mozreview_url_re, + content_type => 'text/x-review-board-request', + can_review => 0, + }, Phabricator => { title => 'Phabricator', regex => \&phabricator_url_re, -- cgit v1.2.3-24-g4f1b From 2f3ca2822570ae271ca4603b5d3de119b40d9eb8 Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Tue, 18 Sep 2018 09:48:09 -0400 Subject: bump version to 20180918.1 --- Bugzilla.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Bugzilla.pm b/Bugzilla.pm index c31e2088b..69b255d17 100644 --- a/Bugzilla.pm +++ b/Bugzilla.pm @@ -13,7 +13,7 @@ use warnings; use Bugzilla::Logging; -our $VERSION = '20180910.1'; +our $VERSION = '20180918.1'; use Bugzilla::Auth; use Bugzilla::Auth::Persist::Cookie; -- cgit v1.2.3-24-g4f1b From b8b2a943056adbb112474df7bdf766970a56b2dc Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Tue, 18 Sep 2018 18:19:03 -0400 Subject: Bug 1455495 - Replace apache with Mojolicious --- .circleci/config.yml | 8 +- .perlcriticrc | 2 + Bugzilla.pm | 3 +- Bugzilla/Attachment/PatchReader.pm | 15 +- Bugzilla/CGI.pm | 36 ++- Bugzilla/Config/General.pm | 6 - Bugzilla/DaemonControl.pm | 26 ++- Bugzilla/Error.pm | 4 +- Bugzilla/Install/Filesystem.pm | 135 +---------- Bugzilla/Memcached.pm | 2 + Bugzilla/ModPerl.pm | 118 ---------- Bugzilla/ModPerl/BasicAuth.pm | 65 ------ Bugzilla/ModPerl/BlockIP.pm | 65 ------ Bugzilla/ModPerl/Hostage.pm | 71 ------ Bugzilla/ModPerl/StartupFix.pm | 51 ----- Bugzilla/Quantum.pm | 118 ++++++++++ Bugzilla/Quantum/CGI.pm | 160 +++++++++++++ Bugzilla/Quantum/Plugin/BasicAuth.pm | 40 ++++ Bugzilla/Quantum/Plugin/BlockIP.pm | 43 ++++ Bugzilla/Quantum/Plugin/Glue.pm | 101 ++++++++ Bugzilla/Quantum/Plugin/Hostage.pm | 86 +++++++ Bugzilla/Quantum/SES.pm | 254 +++++++++++++++++++++ Bugzilla/Quantum/Static.pm | 30 +++ Bugzilla/Quantum/Stdout.pm | 60 +++++ Bugzilla/Template.pm | 1 + Bugzilla/Util.pm | 3 +- Bugzilla/WebService/Server/XMLRPC.pm | 6 +- Bugzilla/WebService/Util.pm | 8 +- Dockerfile | 11 +- Log/Log4perl/Layout/Mozilla.pm | 3 + Makefile.PL | 20 +- README.rst | 35 --- buglist.cgi | 50 +--- bugzilla.pl | 20 ++ checksetup.pl | 3 +- colchange.cgi | 16 +- conf/httpd.conf | 103 --------- conf/log4perl-test.conf | 4 +- docker-compose.yml | 1 + docs/en/rst/administering/parameters.rst | 6 - docs/en/rst/style.rst | 5 +- editparams.cgi | 3 - extensions/BMO/Extension.pm | 101 ++++++++ heartbeat.cgi | 10 +- jobqueue-worker.pl | 0 mod_perl.pl | 204 ----------------- qa/t/lib/QA/Util.pm | 28 +++ qa/t/test_bug_edit.t | 117 +++++----- qa/t/test_shutdown.t | 72 ------ scripts/block-ip.pl | 10 +- scripts/entrypoint.pl | 14 +- scripts/undo.pl | 0 ses/index.cgi | 214 ----------------- t/001compile.t | 14 +- t/002goodperl.t | 7 +- .../en/default/admin/params/editparams.html.tmpl | 1 - template/en/default/admin/params/general.html.tmpl | 5 - template/en/default/global/field-descs.none.tmpl | 10 +- template/en/default/global/header.html.tmpl | 2 + template/en/default/global/messages.html.tmpl | 62 ++--- vagrant_support/apache.yml | 8 +- vagrant_support/hypnotoad | 122 ++++++++++ vagrant_support/hypnotoad.yml | 27 +++ vagrant_support/playbook.yml | 6 +- vagrant_support/update.yml | 5 +- 65 files changed, 1396 insertions(+), 1440 deletions(-) delete mode 100644 Bugzilla/ModPerl.pm delete mode 100644 Bugzilla/ModPerl/BasicAuth.pm delete mode 100644 Bugzilla/ModPerl/BlockIP.pm delete mode 100644 Bugzilla/ModPerl/Hostage.pm delete mode 100644 Bugzilla/ModPerl/StartupFix.pm create mode 100644 Bugzilla/Quantum.pm create mode 100644 Bugzilla/Quantum/CGI.pm create mode 100644 Bugzilla/Quantum/Plugin/BasicAuth.pm create mode 100644 Bugzilla/Quantum/Plugin/BlockIP.pm create mode 100644 Bugzilla/Quantum/Plugin/Glue.pm create mode 100644 Bugzilla/Quantum/Plugin/Hostage.pm create mode 100644 Bugzilla/Quantum/SES.pm create mode 100644 Bugzilla/Quantum/Static.pm create mode 100644 Bugzilla/Quantum/Stdout.pm create mode 100755 bugzilla.pl delete mode 100644 conf/httpd.conf mode change 100644 => 100755 jobqueue-worker.pl delete mode 100644 mod_perl.pl delete mode 100644 qa/t/test_shutdown.t mode change 100644 => 100755 scripts/undo.pl delete mode 100755 ses/index.cgi create mode 100755 vagrant_support/hypnotoad create mode 100644 vagrant_support/hypnotoad.yml diff --git a/.circleci/config.yml b/.circleci/config.yml index fe1263ee9..f64524c80 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,7 +7,7 @@ version: 2 defaults: bmo_slim_image: &bmo_slim_image - image: mozillabteam/bmo-slim:20180801.2 + image: mozillabteam/bmo-slim:20180809.1 user: app mysql_image: &mysql_image @@ -37,12 +37,6 @@ defaults: BMO_memcached_servers: localhost:11211 BMO_memcached_namespace: "bugzilla:" BMO_urlbase: AUTOMATIC - HTTPD_StartServers: 1 - HTTPD_MinSpareServers: 1 - HTTPD_MaxSpareServers: 1 - HTTPD_ServerLimit: 1 - HTTPD_MaxClients: 1 - HTTPD_MaxRequestsPerChild: 4000 mysql_env: &mysql_env MYSQL_DATABASE: bugs diff --git a/.perlcriticrc b/.perlcriticrc index 0b8e4c862..d5fc03fa0 100644 --- a/.perlcriticrc +++ b/.perlcriticrc @@ -5,6 +5,8 @@ severity = 1 #perltidyrc = .perltidyrc #severity = 2 +[-CodeLayout::ProhibitParensWithBuiltins] + [InputOutput::RequireCheckedSyscalls] severity = 2 functions = :builtins diff --git a/Bugzilla.pm b/Bugzilla.pm index 69b255d17..bb036fb06 100644 --- a/Bugzilla.pm +++ b/Bugzilla.pm @@ -838,7 +838,7 @@ sub check_rate_limit { my $limit = join("/", @$limit); Bugzilla->audit("[rate_limit] action=$action, ip=$ip, limit=$limit, name=$name"); if ($action eq 'block') { - Bugzilla::ModPerl::BlockIP->block_ip($ip); + $Bugzilla::Quantum::CGI::C->block_ip($ip); ThrowUserError("rate_limit"); } } @@ -854,6 +854,7 @@ sub markdown_parser { # Per-process cleanup. Note that this is a plain subroutine, not a method, # so we don't have $class available. +*cleanup = \&_cleanup; sub _cleanup { return if $^C; diff --git a/Bugzilla/Attachment/PatchReader.pm b/Bugzilla/Attachment/PatchReader.pm index 2cfbf2c6b..8025f5b82 100644 --- a/Bugzilla/Attachment/PatchReader.pm +++ b/Bugzilla/Attachment/PatchReader.pm @@ -116,18 +116,9 @@ sub process_interdiff { $ENV{'PATH'} = $lc->{diffpath}; my ($pid, $interdiff_stdout, $interdiff_stderr); - if ($ENV{MOD_PERL}) { - require Apache2::RequestUtil; - require Apache2::SubProcess; - my $request = Apache2::RequestUtil->request; - (undef, $interdiff_stdout, $interdiff_stderr) = $request->spawn_proc_prog( - $lc->{interdiffbin}, [$old_filename, $new_filename] - ); - } else { - $interdiff_stderr = gensym; - my $pid = open3(gensym, $interdiff_stdout, $interdiff_stderr, - $lc->{interdiffbin}, $old_filename, $new_filename); - } + $interdiff_stderr = gensym; + $pid = open3(gensym, $interdiff_stdout, $interdiff_stderr, + $lc->{interdiffbin}, $old_filename, $new_filename); binmode $interdiff_stdout; # Check for errors diff --git a/Bugzilla/CGI.pm b/Bugzilla/CGI.pm index 985e504f7..e76b1c609 100644 --- a/Bugzilla/CGI.pm +++ b/Bugzilla/CGI.pm @@ -117,7 +117,7 @@ sub new { # Under mod_perl, CGI's global variables get reset on each request, # so we need to set them up again every time. - $class->_init_bz_cgi_globals() if $ENV{MOD_PERL}; + $class->_init_bz_cgi_globals(); my $self = $class->SUPER::new(@args); @@ -135,6 +135,7 @@ sub new { # apache collapses // to / in $ENV{PATH_INFO} but not in $self->path_info. # url() requires the full path in ENV in order to generate the correct url. $ENV{PATH_INFO} = $path; + DEBUG("redirecting because we see PATH_INFO and don't like it"); print $self->redirect($self->url(-path => 0, -query => 1)); exit; } @@ -145,6 +146,7 @@ sub new { # Redirect to urlbase if we are not viewing an attachment. if ($self->url_is_attachment_base and $script ne 'attachment.cgi') { + DEBUG("Redirecting to urlbase because the url is in the attachment base and not attachment.cgi"); $self->redirect_to_urlbase(); } @@ -475,11 +477,6 @@ sub _prevent_unsafe_response { print $self->SUPER::header(-type => 'text/html', -status => '403 Forbidden'); if ($content_type ne 'text/html') { print "Untrusted Referer Header\n"; - if ($ENV{MOD_PERL}) { - my $r = $self->r; - $r->rflush; - $r->status(200); - } } exit; } @@ -597,8 +594,25 @@ sub header { $headers{'-link'} .= ', ; rel="preconnect"; crossorigin'; } } - - return $self->SUPER::header(%headers) || ""; + my $headers = $self->SUPER::header(%headers) || ''; + if ($self->server_software eq 'Bugzilla::Quantum::CGI') { + my $c = $Bugzilla::Quantum::CGI::C; + $c->res->headers->parse($headers); + my $status = $c->res->headers->status; + if ($status && $status =~ /^([0-9]+)/) { + $c->res->code($1); + } + elsif ($c->res->headers->location) { + $c->res->code(302); + } + else { + $c->res->code(200); + } + return ''; + } + else { + LOGDIE("Bugzilla::CGI->header() should only be called from inside Bugzilla::Quantum::CGI!"); + } } sub param { @@ -715,6 +729,7 @@ sub redirect { return $self->SUPER::redirect(@_); } +use Bugzilla::Logging; # This helps implement Bugzilla::Search::Recent, and also shortens search # URLs that get POSTed to buglist.cgi. sub redirect_search_url { @@ -763,6 +778,7 @@ sub redirect_search_url { # are only redirected if they're under the CGI_URI_LIMIT though. my $self_url = $self->self_url(); if ($self->request_method() ne 'POST' or length($self_url) < CGI_URI_LIMIT) { + DEBUG("Redirecting search url"); print $self->redirect(-url => $self_url); exit; } @@ -784,10 +800,8 @@ sub redirect_to_https { # XML-RPC clients (SOAP::Lite at least) require a 301 to redirect properly # and do not work with 302. Our redirect really is permanent anyhow, so # it doesn't hurt to make it a 301. + DEBUG("Redirecting to https"); print $self->redirect(-location => $url, -status => 301); - - # When using XML-RPC with mod_perl, we need the headers sent immediately. - $self->r->rflush if $ENV{MOD_PERL}; exit; } diff --git a/Bugzilla/Config/General.pm b/Bugzilla/Config/General.pm index 15688dfd3..c870c7376 100644 --- a/Bugzilla/Config/General.pm +++ b/Bugzilla/Config/General.pm @@ -39,12 +39,6 @@ use constant get_param_list => ( checker => \&check_utf8 }, - { - name => 'shutdownhtml', - type => 'l', - default => '' - }, - { name => 'announcehtml', type => 'l', diff --git a/Bugzilla/DaemonControl.pm b/Bugzilla/DaemonControl.pm index 6ff883af0..5cb32973f 100644 --- a/Bugzilla/DaemonControl.pm +++ b/Bugzilla/DaemonControl.pm @@ -23,7 +23,8 @@ use IO::Async::Protocol::LineStream; use IO::Async::Signal; use IO::Socket; use LWP::Simple qw(get); -use POSIX qw(setsid WEXITSTATUS); +use JSON::MaybeXS qw(encode_json); +use POSIX qw(WEXITSTATUS); use base qw(Exporter); @@ -43,8 +44,14 @@ our %EXPORT_TAGS = ( my $BUGZILLA_DIR = bz_locations->{cgi_path}; my $JOBQUEUE_BIN = catfile( $BUGZILLA_DIR, 'jobqueue.pl' ); my $CEREAL_BIN = catfile( $BUGZILLA_DIR, 'scripts', 'cereal.pl' ); -my $HTTPD_BIN = '/usr/sbin/httpd'; -my $HTTPD_CONFIG = catfile( bz_locations->{confdir}, 'httpd.conf' ); +my $BUGZILLA_BIN = catfile( $BUGZILLA_DIR, 'bugzilla.pl' ); +my $HYPNOTOAD_BIN = catfile( $BUGZILLA_DIR, 'local', 'bin', 'hypnotoad' ); +my @PERL5LIB = ( $BUGZILLA_DIR, catdir($BUGZILLA_DIR, 'lib'), catdir($BUGZILLA_DIR, 'local', 'lib', 'perl5') ); + +my %HTTP_BACKENDS = ( + hypnotoad => [ $HYPNOTOAD_BIN, $BUGZILLA_BIN, '-f' ], + simple => [ $BUGZILLA_BIN, 'daemon' ], +); sub catch_signal { my ($name, @done) = @_; @@ -98,13 +105,12 @@ sub run_httpd { my $exit_f = $loop->new_future; my $httpd = IO::Async::Process->new( code => sub { - - # we have to setsid() to make a new process group - # or else apache will kill its parent. - setsid(); - my @command = ( $HTTPD_BIN, '-DFOREGROUND', '-f' => $HTTPD_CONFIG, @args ); - exec @command - or die "failed to exec $command[0] $!"; + $ENV{BUGZILLA_HTTPD_ARGS} = encode_json(\@args); + $ENV{PERL5LIB} = join(':', @PERL5LIB); + my $backend = $ENV{HTTP_BACKEND} // 'hypnotoad'; + my $command = $HTTP_BACKENDS{ $backend }; + exec @$command + or die "failed to exec $command->[0] $!"; }, on_finish => on_finish($exit_f), on_exception => on_exception( 'httpd', $exit_f ), diff --git a/Bugzilla/Error.pm b/Bugzilla/Error.pm index 9fcd16386..f932294b0 100644 --- a/Bugzilla/Error.pm +++ b/Bugzilla/Error.pm @@ -31,7 +31,7 @@ use Scalar::Util qw(blessed); sub _in_eval { my $in_eval = 0; for (my $stack = 1; my $sub = (caller($stack))[3]; $stack++) { - last if $sub =~ /^ModPerl/; + last if $sub =~ /^Bugzilla::Quantum::CGI::try/; $in_eval = 1 if $sub =~ /^\(eval\)/; } return $in_eval; @@ -196,7 +196,7 @@ sub ThrowTemplateError { # mod_perl overrides exit to call die with this string # we never want to display this to the user - exit if $template_err =~ /\bModPerl::Util::exit\b/; + die $template_err if ref($template_err) eq 'ARRAY' && $template_err->[0] eq "EXIT\n"; state $logger = Log::Log4perl->get_logger('Bugzilla.Error.Template'); $logger->error($template_err); diff --git a/Bugzilla/Install/Filesystem.pm b/Bugzilla/Install/Filesystem.pm index 003be22e4..317152962 100644 --- a/Bugzilla/Install/Filesystem.pm +++ b/Bugzilla/Install/Filesystem.pm @@ -41,56 +41,11 @@ use English qw(-no_match_vars $OSNAME); use base qw(Exporter); our @EXPORT = qw( update_filesystem - create_htaccess fix_all_file_permissions fix_dir_permissions fix_file_permissions ); -use constant HT_DEFAULT_DENY => <<'EOT'; -# nothing in this directory is retrievable unless overridden by an .htaccess -# in a subdirectory -deny from all -EOT - -use constant HT_GRAPHS_DIR => <<'EOT'; -# Allow access to .png and .gif files. - - Allow from all - - -# And no directory listings, either. -Deny from all -EOT - -use constant HT_WEBDOT_DIR => <<'EOT'; -# Restrict access to .dot files to the public webdot server at research.att.com -# if research.att.com ever changes their IP, or if you use a different -# webdot server, you'll need to edit this - - Allow from 192.20.225.0/24 - Deny from all - - -# Allow access to .png files created by a local copy of 'dot' - - Allow from all - - -# And no directory listings, either. -Deny from all -EOT - -use constant HT_ASSETS_DIR => <<'EOT'; -# Allow access to .css and js files - - Allow from all - - -# And no directory listings, either. -Deny from all -EOT - use constant INDEX_HTML => <<'EOT'; @@ -112,11 +67,6 @@ use constant HTTPD_ENV => qw( NYTPROF_DIR ); -sub HTTPD_ENV_CONF { - my @env = (ENV_KEYS, HTTPD_ENV); - return join( "\n", map { "PerlPassEnv " . $_ } @env ) . "\n"; -} - ############### # Permissions # ############### @@ -230,13 +180,13 @@ sub FILESYSTEM { 'jobqueue-worker.pl' => { perms => OWNER_EXECUTE }, 'clean-bug-user-last-visit.pl' => { perms => WS_EXECUTE }, + 'bugzilla.pl' => { perms => OWNER_EXECUTE }, 'Bugzilla.pm' => { perms => CGI_READ }, "$localconfig*" => { perms => CGI_READ }, 'META.*' => { perms => CGI_READ }, 'MYMETA.*' => { perms => CGI_READ }, 'bugzilla.dtd' => { perms => WS_SERVE }, 'mod_perl.pl' => { perms => WS_SERVE }, - '.htaccess' => { perms => WS_SERVE }, 'cvs-update.log' => { perms => WS_SERVE }, 'scripts/sendunsentbugmail.pl' => { perms => WS_EXECUTE }, 'docs/bugzilla.ent' => { perms => OWNER_WRITE }, @@ -427,9 +377,6 @@ sub FILESYSTEM { "skins/yui3.css" => { perms => CGI_READ, overwrite => 1, contents => $yui3_all_css }, - "$confdir/env.conf" => { perms => CGI_READ, - overwrite => 1, - contents => \&HTTPD_ENV_CONF }, ); # Because checksetup controls the creation of index.html separately @@ -438,54 +385,15 @@ sub FILESYSTEM { 'index.html' => { perms => WS_SERVE, contents => INDEX_HTML } ); - # Because checksetup controls the .htaccess creation separately - # by a localconfig variable, these go in a separate variable from - # %create_files. - # - # Note that these get WS_SERVE as their permission - # because they're *read* by the webserver, even though they're not - # actually, themselves, served. - my %htaccess = ( - "$attachdir/.htaccess" => { perms => WS_SERVE, - contents => HT_DEFAULT_DENY }, - "$libdir/Bugzilla/.htaccess" => { perms => WS_SERVE, - contents => HT_DEFAULT_DENY }, - "$extlib/.htaccess" => { perms => WS_SERVE, - contents => HT_DEFAULT_DENY }, - "$templatedir/.htaccess" => { perms => WS_SERVE, - contents => HT_DEFAULT_DENY }, - 'contrib/.htaccess' => { perms => WS_SERVE, - contents => HT_DEFAULT_DENY }, - 'scripts/.htaccess' => { perms => WS_SERVE, - contents => HT_DEFAULT_DENY }, - 't/.htaccess' => { perms => WS_SERVE, - contents => HT_DEFAULT_DENY }, - 'xt/.htaccess' => { perms => WS_SERVE, - contents => HT_DEFAULT_DENY }, - '.circleci/.htaccess' => { perms => WS_SERVE, - contents => HT_DEFAULT_DENY }, - "$confdir/.htaccess" => { perms => WS_SERVE, - contents => HT_DEFAULT_DENY }, - "$datadir/.htaccess" => { perms => WS_SERVE, - contents => HT_DEFAULT_DENY }, - "$graphsdir/.htaccess" => { perms => WS_SERVE, - contents => HT_GRAPHS_DIR }, - "$webdotdir/.htaccess" => { perms => WS_SERVE, - contents => HT_WEBDOT_DIR }, - "$assetsdir/.htaccess" => { perms => WS_SERVE, - contents => HT_ASSETS_DIR }, - ); - Bugzilla::Hook::process('install_filesystem', { files => \%files, create_dirs => \%create_dirs, non_recurse_dirs => \%non_recurse_dirs, recurse_dirs => \%recurse_dirs, create_files => \%create_files, - htaccess => \%htaccess, }); - my %all_files = (%create_files, %htaccess, %index_html, %files); + my %all_files = (%create_files, %index_html, %files); my %all_dirs = (%create_dirs, %non_recurse_dirs); return { @@ -494,7 +402,6 @@ sub FILESYSTEM { all_dirs => \%all_dirs, create_files => \%create_files, - htaccess => \%htaccess, index_html => \%index_html, all_files => \%all_files, }; @@ -542,13 +449,6 @@ sub update_filesystem { _rename_file($oldparamsfile, "$datadir/$oldparamsfile"); } - # Remove old assets htaccess file to force recreation with correct values. - if (-e "$assetsdir/.htaccess") { - if (read_file("$assetsdir/.htaccess") =~ //) { - unlink("$assetsdir/.htaccess"); - } - } - _create_files(%files); if ($params->{index_html}) { _create_files(%{$fs->{index_html}}); @@ -653,27 +553,6 @@ sub _convert_single_file_skins { } } -sub create_htaccess { - _create_files(%{FILESYSTEM()->{htaccess}}); - - # Repair old .htaccess files - - my $webdot_dir = bz_locations()->{'webdotdir'}; - # The public webdot IP address changed. - my $webdot = new IO::File("$webdot_dir/.htaccess", 'r') - || die "$webdot_dir/.htaccess: $!"; - my $webdot_data; - { local $/; $webdot_data = <$webdot>; } - $webdot->close; - if ($webdot_data =~ /192\.20\.225\.10/) { - print "Repairing $webdot_dir/.htaccess...\n"; - $webdot_data =~ s/192\.20\.225\.10/192.20.225.0\/24/g; - $webdot = new IO::File("$webdot_dir/.htaccess", 'w') || die $!; - print $webdot $webdot_data; - $webdot->close; - } -} - sub _rename_file { my ($from, $to) = @_; print install_string('file_rename', { from => $from, to => $to }), "\n"; @@ -984,16 +863,6 @@ Params: C - Whether or not we should create Returns: nothing -=item C - -Description: Creates all of the .htaccess files for Apache, - in the various Bugzilla directories. Also updates - the .htaccess files if they need updating. - -Params: none - -Returns: nothing - =item C Description: Sets all the file permissions on all of Bugzilla's files diff --git a/Bugzilla/Memcached.pm b/Bugzilla/Memcached.pm index 40755aa29..6bbef080a 100644 --- a/Bugzilla/Memcached.pm +++ b/Bugzilla/Memcached.pm @@ -25,6 +25,8 @@ use Sys::Syslog qw(:DEFAULT); use constant MAX_KEY_LENGTH => 250; use constant RATE_LIMIT_PREFIX => "rate:"; +*new = \&_new; + sub _new { my $invocant = shift; my $class = ref($invocant) || $invocant; diff --git a/Bugzilla/ModPerl.pm b/Bugzilla/ModPerl.pm deleted file mode 100644 index 19cd1128f..000000000 --- a/Bugzilla/ModPerl.pm +++ /dev/null @@ -1,118 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. -package Bugzilla::ModPerl; - -use 5.10.1; -use strict; -use warnings; - -use File::Find (); -use Cwd (); -use Carp (); - -# We don't need (or want) to use Bugzilla's template subclass. -# it is easier to reason with the code without all the extra things Bugzilla::Template adds -# (and there might be side-effects, since this code is loaded very early in the httpd startup) -use Template (); - -use Bugzilla::ModPerl::BlockIP; -use Bugzilla::ModPerl::Hostage; - -sub apache_config { - my ($class, $cgi_path) = @_; - - Carp::croak "\$cgi_path is required" unless $cgi_path; - - my %htaccess; - $cgi_path = Cwd::realpath($cgi_path); - my $wanted = sub { - package File::Find; - our ($name, $dir); - - if ($name =~ m#/\.htaccess$#) { - open my $fh, '<', $name or die "cannot open $name $!"; - my $contents = do { - local $/ = undef; - <$fh>; - }; - close $fh; - $htaccess{$dir} = { file => $name, contents => $contents, dir => $dir }; - } - }; - - File::Find::find( { wanted => $wanted, no_chdir => 1 }, $cgi_path ); - my $template = Template->new; - my $conf; - my %vars = ( - root_htaccess => delete $htaccess{$cgi_path}, - htaccess_files => [ map { $htaccess{$_} } sort { length $a <=> length $b } keys %htaccess ], - cgi_path => $cgi_path, - ); - $template->process(\*DATA, \%vars, \$conf); - my $apache_version = Apache2::ServerUtil::get_server_version(); - if ($apache_version =~ m!Apache/(\d+)\.(\d+)\.(\d+)!) { - my ($major, $minor, $patch) = ($1, $2, $3); - if ($major > 2 || $major == 2 && $minor >= 4) { - $conf =~ s{^\s+deny\s+from\s+all.*$}{Require all denied}gmi; - $conf =~ s{^\s+allow\s+from\s+all.*$}{Require all granted}gmi; - $conf =~ s{^\s+allow\s+from\s+(\S+).*$}{Require host $1}gmi; - } - } - - return $conf; -} - -1; - -__DATA__ -# Make sure each httpd child receives a different random seed (bug 476622). -# Bugzilla::RNG has one srand that needs to be called for -# every process, and Perl has another. (Various Perl modules still use -# the built-in rand(), even though we never use it in Bugzilla itself, -# so we need to srand() both of them.) -PerlChildInitHandler "sub { Bugzilla::RNG::srand(); srand(); eval { Bugzilla->dbh->ping } }" -PerlInitHandler Bugzilla::ModPerl::Hostage -PerlAccessHandler Bugzilla::ModPerl::BlockIP - -# It is important to specify ErrorDocuments outside of all directories. -# These used to be in .htaccess, but then things like "AllowEncodedSlashes no" -# mean that urls containing %2f are unstyled. -ErrorDocument 401 /errors/401.html -ErrorDocument 403 /errors/403.html -ErrorDocument 404 /errors/404.html -ErrorDocument 500 /errors/500.html - - - AddHandler perl-script .cgi - # No need to PerlModule these because they're already defined in mod_perl.pl - PerlResponseHandler Bugzilla::ModPerl::ResponseHandler - PerlCleanupHandler Bugzilla::ModPerl::CleanupHandler Apache2::SizeLimit - PerlOptions +ParseHeaders - Options +ExecCGI +FollowSymLinks - DirectoryIndex index.cgi index.html - AllowOverride none - # from [% root_htaccess.file %] - [% root_htaccess.contents FILTER indent %] - - -# AWS SES endpoint for handling mail bounces/complaints - - PerlSetEnv AUTH_VAR_NAME ses_username - PerlSetEnv AUTH_VAR_PASS ses_password - PerlAuthenHandler Bugzilla::ModPerl::BasicAuth - AuthName SES - AuthType Basic - require valid-user - - -# directory rules for all the other places we have .htaccess files -[% FOREACH htaccess IN htaccess_files %] -# from [% htaccess.file %] - - [% htaccess.contents FILTER indent %] - -[% END %] diff --git a/Bugzilla/ModPerl/BasicAuth.pm b/Bugzilla/ModPerl/BasicAuth.pm deleted file mode 100644 index 7248a19f3..000000000 --- a/Bugzilla/ModPerl/BasicAuth.pm +++ /dev/null @@ -1,65 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. -package Bugzilla::ModPerl::BasicAuth; -use 5.10.1; -use strict; -use warnings; - -# Protects a mod_perl with Basic HTTP authentication. -# -# Example use: -# -# -# PerlAuthenHandler Bugzilla::ModPerl::BasicAuth -# PerlSetEnv AUTH_VAR_NAME ses_username -# PerlSetEnv AUTH_VAR_PASS ses_password -# AuthName SES -# AuthType Basic -# require valid-user -# -# -# AUTH_VAR_NAME and AUTH_VAR_PASS are the names of variables defined in -# `localconfig` which hold the authentication credentials. - -use Apache2::Const -compile => qw(OK HTTP_UNAUTHORIZED); ## no critic (Freenode::ModPerl) -use Bugzilla::Logging; -use Bugzilla (); - -sub handler { - my $r = shift; - my ($status, $password) = $r->get_basic_auth_pw; - if ($status != Apache2::Const::OK) { - WARN("Got non-OK status: $status when trying to get password"); - return $status - } - - my $auth_var_name = $ENV{AUTH_VAR_NAME}; - my $auth_var_pass = $ENV{AUTH_VAR_PASS}; - unless ($auth_var_name && $auth_var_pass) { - ERROR('AUTH_VAR_NAME and AUTH_VAR_PASS environmental vars not set'); - $r->note_basic_auth_failure; - return Apache2::Const::HTTP_UNAUTHORIZED; - } - - my $auth_user = Bugzilla->localconfig->{$auth_var_name}; - my $auth_pass = Bugzilla->localconfig->{$auth_var_pass}; - unless ($auth_user && $auth_pass) { - ERROR("$auth_var_name and $auth_var_pass not configured"); - $r->note_basic_auth_failure; - return Apache2::Const::HTTP_UNAUTHORIZED; - } - - unless ($r->user eq $auth_user && $password eq $auth_pass) { - $r->note_basic_auth_failure; - WARN('username and password do not match'); - return Apache2::Const::HTTP_UNAUTHORIZED; - } - - return Apache2::Const::OK; -} - -1; diff --git a/Bugzilla/ModPerl/BlockIP.pm b/Bugzilla/ModPerl/BlockIP.pm deleted file mode 100644 index 4e9a4be5c..000000000 --- a/Bugzilla/ModPerl/BlockIP.pm +++ /dev/null @@ -1,65 +0,0 @@ -package Bugzilla::ModPerl::BlockIP; -use 5.10.1; -use strict; -use warnings; - -use Apache2::RequestRec (); -use Apache2::Connection (); - -use Apache2::Const -compile => qw(OK); -use Cache::Memcached::Fast; - -use constant BLOCK_TIMEOUT => 60*60; - -my $MEMCACHED = Bugzilla::Memcached->_new()->{memcached}; -my $STATIC_URI = qr{ - ^/ - (?: extensions/[^/]+/web - | robots\.txt - | __heartbeat__ - | __lbheartbeat__ - | __version__ - | images - | skins - | js - | errors - ) -}xms; - -sub block_ip { - my ($class, $ip) = @_; - $MEMCACHED->set("block_ip:$ip" => 1, BLOCK_TIMEOUT) if $MEMCACHED; -} - -sub unblock_ip { - my ($class, $ip) = @_; - $MEMCACHED->delete("block_ip:$ip") if $MEMCACHED; -} - -sub handler { - my $r = shift; - return Apache2::Const::OK if $r->uri =~ $STATIC_URI; - - my $ip = $r->headers_in->{'X-Forwarded-For'}; - if ($ip) { - $ip = (split(/\s*,\s*/ms, $ip))[-1]; - } - else { - $ip = $r->connection->remote_ip; - } - - if ($MEMCACHED && $MEMCACHED->get("block_ip:$ip")) { - __PACKAGE__->block_ip($ip); - $r->status_line("429 Too Many Requests"); - # 500 is used here because apache 2.2 doesn't understand 429. - # the above line and the return value together mean we produce 429. - # Any other variation doesn't work. - $r->custom_response(500, "Too Many Requests"); - return 429; - } - else { - return Apache2::Const::OK; - } -} - -1; diff --git a/Bugzilla/ModPerl/Hostage.pm b/Bugzilla/ModPerl/Hostage.pm deleted file mode 100644 index a3bdfac58..000000000 --- a/Bugzilla/ModPerl/Hostage.pm +++ /dev/null @@ -1,71 +0,0 @@ -package Bugzilla::ModPerl::Hostage; -use 5.10.1; -use strict; -use warnings; - -use Apache2::Const qw(:common); ## no critic (Freenode::ModPerl) - -sub _attachment_root { - my ($base) = @_; - return undef unless $base; - return $base =~ m{^https?://(?:bug)?\%bugid\%\.([a-zA-Z\.-]+)} - ? $1 - : undef; -} - -sub _attachment_host_regex { - my ($base) = @_; - return undef unless $base; - my $val = $base; - $val =~ s{^https?://}{}s; - $val =~ s{/$}{}s; - my $regex = quotemeta $val; - $regex =~ s/\\\%bugid\\\%/\\d+/g; - return qr/^$regex$/s; -} - -sub handler { - my $r = shift; - state $urlbase = Bugzilla->localconfig->{urlbase}; - state $urlbase_uri = URI->new($urlbase); - state $urlbase_host = $urlbase_uri->host; - state $urlbase_host_regex = qr/^bug(\d+)\.\Q$urlbase_host\E$/; - state $attachment_base = Bugzilla->localconfig->{attachment_base}; - state $attachment_root = _attachment_root($attachment_base); - state $attachment_host_regex = _attachment_host_regex($attachment_base); - - my $hostname = $r->hostname; - return OK if $hostname eq $urlbase_host; - - my $path = $r->uri; - return OK if $path eq '/__lbheartbeat__'; - - if ($attachment_base && $hostname eq $attachment_root) { - $r->headers_out->set(Location => $urlbase); - return REDIRECT; - } - elsif ($attachment_base && $hostname =~ $attachment_host_regex) { - if ($path =~ m{^/attachment\.cgi}s) { - return OK; - } else { - my $new_uri = URI->new($r->unparsed_uri); - $new_uri->scheme($urlbase_uri->scheme); - $new_uri->host($urlbase_host); - $r->headers_out->set(Location => $new_uri); - return REDIRECT; - } - } - elsif (my ($id) = $hostname =~ $urlbase_host_regex) { - my $new_uri = $urlbase_uri->clone; - $new_uri->path('/show_bug.cgi'); - $new_uri->query_form(id => $id); - $r->headers_out->set(Location => $new_uri); - return REDIRECT; - } - else { - $r->headers_out->set(Location => $urlbase); - return REDIRECT; - } -} - -1; \ No newline at end of file diff --git a/Bugzilla/ModPerl/StartupFix.pm b/Bugzilla/ModPerl/StartupFix.pm deleted file mode 100644 index bcc467e9f..000000000 --- a/Bugzilla/ModPerl/StartupFix.pm +++ /dev/null @@ -1,51 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. -package Bugzilla::ModPerl::StartupFix; -use 5.10.1; -use strict; -use warnings; - -use Filter::Util::Call; -use Apache2::ServerUtil (); - -# This module is a source filter that removes every subsequent line -# if this is the first time apache has started, -# as reported by Apache2::ServerUtil::restart_count(), which is 1 -# on the first start. - -my $FIRST_STARTUP = <<'CODE'; -warn "Bugzilla::ModPerl::StartupFix: Skipping first startup using source filter\n"; -1; -CODE - -sub import { - my ($class) = @_; - my ($ref) = {}; - filter_add( bless $ref, $class ); -} - -# this will be called for each line. -# For the first line replaced, we insert $FIRST_STARTUP. -# Every subsequent line is replaced with an empty string. -sub filter { - my ($self) = @_; - my ($status); - if ($status = filter_read() > 0) { - if (Apache2::ServerUtil::restart_count() < 2) { - if (!$self->{did_it}) { - $self->{did_it} = 1; - $_ = $FIRST_STARTUP; - } - else { - $_ = ""; - } - } - } - return $status; -} - -1; \ No newline at end of file diff --git a/Bugzilla/Quantum.pm b/Bugzilla/Quantum.pm new file mode 100644 index 000000000..d2352f6d8 --- /dev/null +++ b/Bugzilla/Quantum.pm @@ -0,0 +1,118 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. + +package Bugzilla::Quantum; +use Mojo::Base 'Mojolicious'; + +# Needed for its exit() overload, must happen early in execution. +use CGI::Compile; + +use Bugzilla (); +use Bugzilla::BugMail (); +use Bugzilla::CGI (); +use Bugzilla::Constants qw(bz_locations); +use Bugzilla::Extension (); +use Bugzilla::Install::Requirements (); +use Bugzilla::Logging; +use Bugzilla::Quantum::CGI; +use Bugzilla::Quantum::SES; +use Bugzilla::Quantum::Static; +use Mojo::Loader qw( find_modules ); +use Module::Runtime qw( require_module ); +use Bugzilla::Util (); +use Cwd qw(realpath); +use MojoX::Log::Log4perl::Tiny; + +has 'static' => sub { Bugzilla::Quantum::Static->new }; + +sub startup { + my ($self) = @_; + + DEBUG('Starting up'); + $self->plugin('Bugzilla::Quantum::Plugin::Glue'); + $self->plugin('Bugzilla::Quantum::Plugin::Hostage'); + $self->plugin('Bugzilla::Quantum::Plugin::BlockIP'); + $self->plugin('Bugzilla::Quantum::Plugin::BasicAuth'); + + Bugzilla::Extension->load_all(); + if ( $self->mode ne 'development' ) { + Bugzilla->preload_features(); + DEBUG('preloading templates'); + Bugzilla->preload_templates(); + DEBUG('done preloading templates'); + require_module($_) for find_modules('Bugzilla::User::Setting'); + + $self->hook( + after_static => sub { + my ($c) = @_; + $c->res->headers->cache_control('public, max-age=31536000'); + } + ); + } + + my $r = $self->routes; + Bugzilla::Quantum::CGI->load_all($r); + Bugzilla::Quantum::CGI->load_one( 'bzapi_cgi', 'extensions/BzAPI/bin/rest.cgi' ); + + Bugzilla::WebService::Server::REST->preload; + + $r->any('/')->to('CGI#index_cgi'); + $r->any('/bug/')->to('CGI#show_bug_cgi'); + $r->any('/')->to('CGI#show_bug_cgi'); + + $r->any('/rest')->to('CGI#rest_cgi'); + $r->any('/rest.cgi/*PATH_INFO')->to( 'CGI#rest_cgi' => { PATH_INFO => '' } ); + $r->any('/rest/*PATH_INFO')->to( 'CGI#rest_cgi' => { PATH_INFO => '' } ); + $r->any('/extensions/BzAPI/bin/rest.cgi/*PATH_INFO')->to('CGI#bzapi_cgi'); + $r->any('/bzapi/*PATH_INFO')->to('CGI#bzapi_cgi'); + + $r->get( + '/__lbheartbeat__' => sub { + my $c = shift; + $c->reply->file( $c->app->home->child('__lbheartbeat__') ); + }, + ); + + $r->get( + '/__version__' => sub { + my $c = shift; + $c->reply->file( $c->app->home->child('version.json') ); + }, + ); + + $r->get( + '/version.json' => sub { + my $c = shift; + $c->reply->file( $c->app->home->child('version.json') ); + }, + ); + + $r->get('/__heartbeat__')->to('CGI#heartbeat_cgi'); + $r->get('/robots.txt')->to('CGI#robots_cgi'); + + $r->any('/review')->to( 'CGI#page_cgi' => { 'id' => 'splinter.html' } ); + $r->any('/user_profile')->to( 'CGI#page_cgi' => { 'id' => 'user_profile.html' } ); + $r->any('/userprofile')->to( 'CGI#page_cgi' => { 'id' => 'user_profile.html' } ); + $r->any('/request_defer')->to( 'CGI#page_cgi' => { 'id' => 'request_defer.html' } ); + $r->any('/login')->to( 'CGI#index_cgi' => { 'GoAheadAndLogIn' => '1' } ); + + $r->any( '/:new_bug' => [ new_bug => qr{new[-_]bug} ] )->to('CGI#new_bug_cgi'); + + my $ses_auth = $r->under( + '/ses' => sub { + my ($c) = @_; + my $lc = Bugzilla->localconfig; + + return $c->basic_auth( 'SES', $lc->{ses_username}, $lc->{ses_password} ); + } + ); + $ses_auth->any('/index.cgi')->to('SES#main'); + + Bugzilla::Hook::process( 'app_startup', { app => $self } ); +} + +1; diff --git a/Bugzilla/Quantum/CGI.pm b/Bugzilla/Quantum/CGI.pm new file mode 100644 index 000000000..0a74f1ee5 --- /dev/null +++ b/Bugzilla/Quantum/CGI.pm @@ -0,0 +1,160 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. + +package Bugzilla::Quantum::CGI; +use Mojo::Base 'Mojolicious::Controller'; + +use CGI::Compile; +use Try::Tiny; +use Taint::Util qw(untaint); +use Sys::Hostname; +use Sub::Quote 2.005000; +use Sub::Name; +use Socket qw(AF_INET inet_aton); +use File::Spec::Functions qw(catfile); +use File::Slurper qw(read_text); +use English qw(-no_match_vars); +use Bugzilla::Quantum::Stdout; +use Bugzilla::Constants qw(bz_locations); + +our $C; +my %SEEN; + +sub load_all { + my ( $class, $r ) = @_; + + foreach my $file ( glob '*.cgi' ) { + my $name = _file_to_method($file); + $class->load_one( $name, $file ); + $r->any("/$file")->to("CGI#$name"); + } +} + +sub load_one { + my ( $class, $name, $file ) = @_; + my $package = __PACKAGE__ . "::$name", my $inner_name = "_$name"; + my $content = read_text( catfile( bz_locations->{cgi_path}, $file ) ); + $content = "package $package; $content"; + untaint($content); + my %options = ( + package => $package, + file => $file, + line => 1, + no_defer => 1, + ); + die "Tried to load $file more than once" if $SEEN{$file}++; + my $inner = quote_sub $inner_name, $content, {}, \%options; + my $wrapper = sub { + my ($c) = @_; + my $stdin = $c->_STDIN; + local $C = $c; + local %ENV = $c->_ENV($file); + local $CGI::Compile::USE_REAL_EXIT = 0; + local $PROGRAM_NAME = $file; + local *STDIN; ## no critic (local) + open STDIN, '<', $stdin->path or die "STDIN @{[$stdin->path]}: $!" if -s $stdin->path; + tie *STDOUT, 'Bugzilla::Quantum::Stdout', controller => $c; ## no critic (tie) + try { + Bugzilla->init_page(); + $inner->(); + } + catch { + die $_ unless ref $_ eq 'ARRAY' && $_->[0] eq "EXIT\n"; + } + finally { + untie *STDOUT; + $c->finish; + Bugzilla->cleanup; + CGI::initialize_globals(); + }; + }; + + no strict 'refs'; ## no critic (strict) + *{$name} = subname( $name, $wrapper ); + return 1; +} + + +sub _ENV { + my ( $c, $script_name ) = @_; + my $tx = $c->tx; + my $req = $tx->req; + my $headers = $req->headers; + my $content_length = $req->content->is_multipart ? $req->body_size : $headers->content_length; + my %env_headers = ( HTTP_COOKIE => '', HTTP_REFERER => '' ); + + for my $name ( @{ $headers->names } ) { + my $key = uc "http_$name"; + $key =~ s/\W/_/g; + $env_headers{$key} = $headers->header($name); + } + + my $remote_user; + if ( my $userinfo = $req->url->to_abs->userinfo ) { + $remote_user = $userinfo =~ /([^:]+)/ ? $1 : ''; + } + elsif ( my $authenticate = $headers->authorization ) { + $remote_user = $authenticate =~ /Basic\s+(.*)/ ? b64_decode $1 : ''; + $remote_user = $remote_user =~ /([^:]+)/ ? $1 : ''; + } + my $path_info = $c->stash->{'mojo.captures'}{'PATH_INFO'}; + my %captures = %{ $c->stash->{'mojo.captures'} // {} }; + foreach my $key ( keys %captures ) { + if ( $key eq 'controller' || $key eq 'action' || $key eq 'PATH_INFO' || $key =~ /^REWRITE_/ ) { + delete $captures{$key}; + } + } + my $cgi_query = Mojo::Parameters->new(%captures); + $cgi_query->append( $req->url->query ); + my $prefix = $c->stash->{bmo_prefix} ? '/bmo/' : '/'; + + return ( + %ENV, + CONTENT_LENGTH => $content_length || 0, + CONTENT_TYPE => $headers->content_type || '', + GATEWAY_INTERFACE => 'CGI/1.1', + HTTPS => $req->is_secure ? 'on' : 'off', + %env_headers, + QUERY_STRING => $cgi_query->to_string, + PATH_INFO => $path_info ? "/$path_info" : '', + REMOTE_ADDR => $tx->original_remote_address, + REMOTE_HOST => $tx->original_remote_address, + REMOTE_PORT => $tx->remote_port, + REMOTE_USER => $remote_user || '', + REQUEST_METHOD => $req->method, + SCRIPT_NAME => "$prefix$script_name", + SERVER_NAME => hostname, + SERVER_PORT => $tx->local_port, + SERVER_PROTOCOL => $req->is_secure ? 'HTTPS' : 'HTTP', # TODO: Version is missing + SERVER_SOFTWARE => __PACKAGE__, + ); +} + +sub _STDIN { + my $c = shift; + my $stdin; + + if ( $c->req->content->is_multipart ) { + $stdin = Mojo::Asset::File->new; + $stdin->add_chunk( $c->req->build_body ); + } + else { + $stdin = $c->req->content->asset; + } + + return $stdin if $stdin->isa('Mojo::Asset::File'); + return Mojo::Asset::File->new->add_chunk( $stdin->slurp ); +} + +sub _file_to_method { + my ($name) = @_; + $name =~ s/\./_/s; + $name =~ s/\W+/_/gs; + return $name; +} + +1; diff --git a/Bugzilla/Quantum/Plugin/BasicAuth.pm b/Bugzilla/Quantum/Plugin/BasicAuth.pm new file mode 100644 index 000000000..e17273404 --- /dev/null +++ b/Bugzilla/Quantum/Plugin/BasicAuth.pm @@ -0,0 +1,40 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. +package Bugzilla::Quantum::Plugin::BasicAuth; +use 5.10.1; +use Mojo::Base qw(Mojolicious::Plugin); + +use Bugzilla::Logging; +use Carp; + +sub register { + my ( $self, $app, $conf ) = @_; + + $app->renderer->add_helper( + basic_auth => sub { + my ( $c, $realm, $auth_user, $auth_pass ) = @_; + my $req = $c->req; + my ( $user, $password ) = $req->url->to_abs->userinfo =~ /^([^:]+):(.*)/; + + unless ( $realm && $auth_user && $auth_pass ) { + croak 'basic_auth() called with missing parameters.'; + } + + unless ( $user eq $auth_user && $password eq $auth_pass ) { + WARN('username and password do not match'); + $c->res->headers->www_authenticate("Basic realm=\"$realm\""); + $c->res->code(401); + $c->rendered; + return 0; + } + + return 1; + } + ); +} + +1; \ No newline at end of file diff --git a/Bugzilla/Quantum/Plugin/BlockIP.pm b/Bugzilla/Quantum/Plugin/BlockIP.pm new file mode 100644 index 000000000..058ecbf64 --- /dev/null +++ b/Bugzilla/Quantum/Plugin/BlockIP.pm @@ -0,0 +1,43 @@ +package Bugzilla::Quantum::Plugin::BlockIP; +use 5.10.1; +use Mojo::Base 'Mojolicious::Plugin'; + +use Bugzilla::Memcached; + +use constant BLOCK_TIMEOUT => 60 * 60; + +my $MEMCACHED = Bugzilla::Memcached->new()->{memcached}; + +sub register { + my ( $self, $app, $conf ) = @_; + + $app->hook( before_routes => \&_before_routes ); + $app->helper( block_ip => \&_block_ip ); + $app->helper( unblock_ip => \&_unblock_ip ); +} + +sub _block_ip { + my ( $class, $ip ) = @_; + $MEMCACHED->set( "block_ip:$ip" => 1, BLOCK_TIMEOUT ) if $MEMCACHED; +} + +sub _unblock_ip { + my ( $class, $ip ) = @_; + $MEMCACHED->delete("block_ip:$ip") if $MEMCACHED; +} + +sub _before_routes { + my ($c) = @_; + return if $c->stash->{'mojo.static'}; + + my $ip = $c->tx->remote_address; + if ( $MEMCACHED && $MEMCACHED->get("block_ip:$ip") ) { + $c->block_ip($ip); + $c->res->code(429); + $c->res->message('Too Many Requests'); + $c->res->body('Too Many Requests'); + $c->finish; + } +} + +1; diff --git a/Bugzilla/Quantum/Plugin/Glue.pm b/Bugzilla/Quantum/Plugin/Glue.pm new file mode 100644 index 000000000..ded4daf15 --- /dev/null +++ b/Bugzilla/Quantum/Plugin/Glue.pm @@ -0,0 +1,101 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. + +package Bugzilla::Quantum::Plugin::Glue; +use 5.10.1; +use Mojo::Base 'Mojolicious::Plugin'; + +use Try::Tiny; +use Bugzilla::Constants; +use Bugzilla::Logging; +use Bugzilla::RNG (); +use JSON::MaybeXS qw(decode_json); + +sub register { + my ( $self, $app, $conf ) = @_; + + my %D; + if ( $ENV{BUGZILLA_HTTPD_ARGS} ) { + my $args = decode_json( $ENV{BUGZILLA_HTTPD_ARGS} ); + foreach my $arg (@$args) { + if ( $arg =~ /^-D(\w+)$/ ) { + $D{$1} = 1; + } + else { + die "Unknown httpd arg: $arg"; + } + } + } + + # hypnotoad is weird and doesn't look for MOJO_LISTEN itself. + $app->config( + hypnotoad => { + proxy => 1, + listen => [ $ENV{MOJO_LISTEN} ], + }, + ); + + # Make sure each httpd child receives a different random seed (bug 476622). + # Bugzilla::RNG has one srand that needs to be called for + # every process, and Perl has another. (Various Perl modules still use + # the built-in rand(), even though we never use it in Bugzilla itself, + # so we need to srand() both of them.) + # Also, ping the dbh to force a reconnection. + Mojo::IOLoop->next_tick( + sub { + Bugzilla::RNG::srand(); + srand(); + try { Bugzilla->dbh->ping }; + } + ); + + $app->hook( + before_dispatch => sub { + my ($c) = @_; + if ( $D{HTTPD_IN_SUBDIR} ) { + my $path = $c->req->url->path; + if ( $path =~ s{^/bmo}{}s ) { + $c->stash->{bmo_prefix} = 1; + $c->req->url->path($path); + } + } + Log::Log4perl::MDC->put( request_id => $c->req->request_id ); + } + ); + + + $app->secrets( [ Bugzilla->localconfig->{side_wide_secret} ] ); + + $app->renderer->add_handler( + 'bugzilla' => sub { + my ( $renderer, $c, $output, $options ) = @_; + my $vars = delete $c->stash->{vars}; + + # Helpers + my %helper; + foreach my $method ( grep {m/^\w+\z/} keys %{ $renderer->helpers } ) { + my $sub = $renderer->helpers->{$method}; + $helper{$method} = sub { $c->$sub(@_) }; + } + $vars->{helper} = \%helper; + + # The controller + $vars->{c} = $c; + my $name = $options->{template}; + unless ( $name =~ /\./ ) { + $name = sprintf '%s.%s.tmpl', $options->{template}, $options->{format}; + } + my $template = Bugzilla->template; + $template->process( $name, $vars, $output ) + or die $template->error; + } + ); + + $app->log( MojoX::Log::Log4perl::Tiny->new( logger => Log::Log4perl->get_logger( ref $app ) ) ); +} + +1; diff --git a/Bugzilla/Quantum/Plugin/Hostage.pm b/Bugzilla/Quantum/Plugin/Hostage.pm new file mode 100644 index 000000000..cbde7b5ee --- /dev/null +++ b/Bugzilla/Quantum/Plugin/Hostage.pm @@ -0,0 +1,86 @@ +package Bugzilla::Quantum::Plugin::Hostage; +use 5.10.1; +use Mojo::Base 'Mojolicious::Plugin'; +use Bugzilla::Logging; + +sub _attachment_root { + my ($base) = @_; + return undef unless $base; + return $base =~ m{^https?://(?:bug)?\%bugid\%\.([a-zA-Z\.-]+)} + ? $1 + : undef; +} + +sub _attachment_host_regex { + my ($base) = @_; + return undef unless $base; + my $val = $base; + $val =~ s{^https?://}{}s; + $val =~ s{/$}{}s; + my $regex = quotemeta $val; + $regex =~ s/\\\%bugid\\\%/\\d+/g; + return qr/^$regex$/s; +} + +sub register { + my ( $self, $app, $conf ) = @_; + + $app->hook( before_routes => \&_before_routes ); +} + +sub _before_routes { + my ($c) = @_; + state $urlbase = Bugzilla->localconfig->{urlbase}; + state $urlbase_uri = URI->new($urlbase); + state $urlbase_host = $urlbase_uri->host; + state $urlbase_host_regex = qr/^bug(\d+)\.\Q$urlbase_host\E$/; + state $attachment_base = Bugzilla->localconfig->{attachment_base}; + state $attachment_root = _attachment_root($attachment_base); + state $attachment_host_regex = _attachment_host_regex($attachment_base); + + my $stash = $c->stash; + my $req = $c->req; + my $url = $req->url->to_abs; + + return if $stash->{'mojo.static'}; + + my $hostname = $url->host; + return if $hostname eq $urlbase_host; + + my $path = $url->path; + return if $path eq '/__lbheartbeat__'; + + if ( $attachment_base && $hostname eq $attachment_root ) { + DEBUG("redirecting to $urlbase because $hostname is $attachment_root"); + $c->redirect_to($urlbase); + return; + } + elsif ( $attachment_base && $hostname =~ $attachment_host_regex ) { + if ( $path =~ m{^/attachment\.cgi}s ) { + return; + } + else { + my $new_uri = $url->clone; + $new_uri->scheme( $urlbase_uri->scheme ); + $new_uri->host($urlbase_host); + DEBUG("redirecting to $new_uri because $hostname matches attachment regex"); + $c->redirect_to($new_uri); + return; + } + } + elsif ( my ($id) = $hostname =~ $urlbase_host_regex ) { + my $new_uri = $urlbase_uri->clone; + $new_uri->path('/show_bug.cgi'); + $new_uri->query_form( id => $id ); + DEBUG("redirecting to $new_uri because $hostname includes bug id"); + $c->redirect_to($new_uri); + return; + } + else { + DEBUG("redirecting to $urlbase because $hostname doesn't make sense"); + $c->redirect_to($urlbase); + return; + } +} + +1; diff --git a/Bugzilla/Quantum/SES.pm b/Bugzilla/Quantum/SES.pm new file mode 100644 index 000000000..03916075d --- /dev/null +++ b/Bugzilla/Quantum/SES.pm @@ -0,0 +1,254 @@ +package Bugzilla::Quantum::SES; +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. + +use 5.10.1; +use Mojo::Base qw( Mojolicious::Controller ); + +use Bugzilla::Constants qw(ERROR_MODE_DIE); +use Bugzilla::Logging; +use Bugzilla::Mailer qw(MessageToMTA); +use Bugzilla::User (); +use Bugzilla::Util qw(html_quote remote_ip); +use JSON::MaybeXS qw(decode_json); +use LWP::UserAgent (); +use Try::Tiny qw(catch try); + +use Types::Standard qw( :all ); +use Type::Utils; +use Type::Params qw( compile ); + +my $Invocant = class_type { class => __PACKAGE__ }; + +sub main { + my ($self) = @_; + try { + $self->_main; + } + catch { + FATAL("Error in SES Handler: ", $_); + $self->_respond( 400 => 'Bad Request' ); + }; +} + +sub _main { + my ($self) = @_; + Bugzilla->error_mode(ERROR_MODE_DIE); + my $message = $self->_decode_json_wrapper( $self->req->body ) // return; + my $message_type = $self->req->headers->header('X-Amz-SNS-Message-Type') // '(missing)'; + + if ( $message_type eq 'SubscriptionConfirmation' ) { + $self->_confirm_subscription($message); + } + + elsif ( $message_type eq 'Notification' ) { + my $notification = $self->_decode_json_wrapper( $message->{Message} ) // return; + unless ( + # https://docs.aws.amazon.com/ses/latest/DeveloperGuide/event-publishing-retrieving-sns-contents.html + $self->_handle_notification( $notification, 'eventType' ) + + # https://docs.aws.amazon.com/ses/latest/DeveloperGuide/notification-contents.html + || $self->_handle_notification( $notification, 'notificationType' ) + ) + { + WARN('Failed to find notification type'); + $self->_respond( 400 => 'Bad Request' ); + } + } + + else { + WARN("Unsupported message-type: $message_type"); + $self->_respond( 200 => 'OK' ); + } +} + +sub _confirm_subscription { + state $check = compile($Invocant, Dict[SubscribeURL => Str, slurpy Any]); + my ($self, $message) = $check->(@_); + + my $subscribe_url = $message->{SubscribeURL}; + if ( !$subscribe_url ) { + WARN('Bad SubscriptionConfirmation request: missing SubscribeURL'); + $self->_respond( 400 => 'Bad Request' ); + return; + } + + my $ua = ua(); + my $res = $ua->get( $message->{SubscribeURL} ); + if ( !$res->is_success ) { + WARN( 'Bad response from SubscribeURL: ' . $res->status_line ); + $self->_respond( 400 => 'Bad Request' ); + return; + } + + $self->_respond( 200 => 'OK' ); +} + +my $NotificationType = Enum [qw( Bounce Complaint )]; +my $TypeField = Enum [qw(eventType notificationType)]; +my $Notification = Dict [ + eventType => Optional [$NotificationType], + notificationType => Optional [$NotificationType], + slurpy Any, +]; + +sub _handle_notification { + state $check = compile($Invocant, $Notification, $TypeField ); + my ( $self, $notification, $type_field ) = $check->(@_); + + if ( !exists $notification->{$type_field} ) { + return 0; + } + my $type = $notification->{$type_field}; + + if ( $type eq 'Bounce' ) { + $self->_process_bounce($notification); + } + elsif ( $type eq 'Complaint' ) { + $self->_process_complaint($notification); + } + else { + WARN("Unsupported notification-type: $type"); + $self->_respond( 200 => 'OK' ); + } + return 1; +} + +my $BouncedRecipients = ArrayRef[ + Dict[ + emailAddress => Str, + action => Str, + diagnosticCode => Str, + slurpy Any, + ], +]; +my $BounceNotification = Dict [ + bounce => Dict [ + bouncedRecipients => $BouncedRecipients, + reportingMTA => Str, + bounceSubType => Str, + bounceType => Str, + slurpy Any, + ], + slurpy Any, +]; + +sub _process_bounce { + state $check = compile($Invocant, $BounceNotification); + my ($self, $notification) = $check->(@_); + + # disable each account that is bouncing + foreach my $recipient ( @{ $notification->{bounce}->{bouncedRecipients} } ) { + my $address = $recipient->{emailAddress}; + my $reason = sprintf '(%s) %s', $recipient->{action} // 'error', $recipient->{diagnosticCode} // 'unknown'; + + my $user = Bugzilla::User->new( { name => $address, cache => 1 } ); + if ($user) { + + # never auto-disable admin accounts + if ( $user->in_group('admin') ) { + Bugzilla->audit("ignoring bounce for admin <$address>: $reason"); + } + + else { + my $template = Bugzilla->template_inner(); + my $vars = { + mta => $notification->{bounce}->{reportingMTA} // 'unknown', + reason => $reason, + }; + my $disable_text; + $template->process( 'admin/users/bounce-disabled.txt.tmpl', $vars, \$disable_text ) + || die $template->error(); + + $user->set_disabledtext($disable_text); + $user->set_disable_mail(1); + $user->update(); + Bugzilla->audit( "bounce for <$address> disabled userid-" . $user->id . ": $reason" ); + } + } + + else { + Bugzilla->audit("bounce for <$address> has no user: $reason"); + } + } + + $self->_respond( 200 => 'OK' ); +} + +my $ComplainedRecipients = ArrayRef[Dict[ emailAddress => Str, slurpy Any ]]; +my $ComplaintNotification = Dict[ + complaint => Dict [ + complainedRecipients => $ComplainedRecipients, + complaintFeedbackType => Str, + slurpy Any, + ], + slurpy Any, +]; + +sub _process_complaint { + state $check = compile($Invocant, $ComplaintNotification); + my ($self, $notification) = $check->(@_); + my $template = Bugzilla->template_inner(); + my $json = JSON::MaybeXS->new( + pretty => 1, + utf8 => 1, + canonical => 1, + ); + + foreach my $recipient ( @{ $notification->{complaint}->{complainedRecipients} } ) { + my $reason = $notification->{complaint}->{complaintFeedbackType} // 'unknown'; + my $address = $recipient->{emailAddress}; + Bugzilla->audit("complaint for <$address> for '$reason'"); + my $vars = { + email => $address, + user => Bugzilla::User->new( { name => $address, cache => 1 } ), + reason => $reason, + notification => $json->encode($notification), + }; + my $message; + $template->process( 'email/ses-complaint.txt.tmpl', $vars, \$message ) + || die $template->error(); + MessageToMTA($message); + } + + $self->_respond( 200 => 'OK' ); +} + +sub _respond { + my ( $self, $code, $message ) = @_; + $self->render(text => "$message\n", status => $code); +} + +sub _decode_json_wrapper { + state $check = compile($Invocant, Str); + my ($self, $json) = $check->(@_); + my $result; + my $ok = try { + $result = decode_json($json); + } + catch { + WARN( 'Malformed JSON from ' . $self->tx->remote_address ); + $self->_respond( 400 => 'Bad Request' ); + return undef; + }; + return $ok ? $result : undef; +} + +sub ua { + my $ua = LWP::UserAgent->new(); + $ua->timeout(10); + $ua->protocols_allowed( [ 'http', 'https' ] ); + if ( my $proxy_url = Bugzilla->params->{'proxy_url'} ) { + $ua->proxy( [ 'http', 'https' ], $proxy_url ); + } + else { + $ua->env_proxy; + } + return $ua; +} + +1; diff --git a/Bugzilla/Quantum/Static.pm b/Bugzilla/Quantum/Static.pm new file mode 100644 index 000000000..4543d1b84 --- /dev/null +++ b/Bugzilla/Quantum/Static.pm @@ -0,0 +1,30 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. + +package Bugzilla::Quantum::Static; +use Mojo::Base 'Mojolicious::Static'; +use Bugzilla::Constants qw(bz_locations); + +my $LEGACY_RE = qr{ + ^ (?:static/v[0-9]+\.[0-9]+/) ? + ( (?:extensions/[^/]+/web|(?:image|skin|j|graph)s)/.+) + $ +}xs; + +sub file { + my ( $self, $rel ) = @_; + + if ( my ($legacy_rel) = $rel =~ $LEGACY_RE ) { + local $self->{paths} = [ bz_locations->{cgi_path} ]; + return $self->SUPER::file($legacy_rel); + } + else { + return $self->SUPER::file($rel); + } +} + +1; diff --git a/Bugzilla/Quantum/Stdout.pm b/Bugzilla/Quantum/Stdout.pm new file mode 100644 index 000000000..9cf19992c --- /dev/null +++ b/Bugzilla/Quantum/Stdout.pm @@ -0,0 +1,60 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. + +package Bugzilla::Quantum::Stdout; +use 5.10.1; +use Moo; + +use Bugzilla::Logging; +use Encode; +use English qw(-no_match_vars); + +has 'controller' => ( + is => 'ro', + required => 1, +); + +has '_encoding' => ( + is => 'rw', + default => '', +); + +sub TIEHANDLE { ## no critic (unpack) + my $class = shift; + + return $class->new(@_); +} + +sub PRINTF { ## no critic (unpack) + my $self = shift; + $self->PRINT( sprintf @_ ); +} + +sub PRINT { ## no critic (unpack) + my $self = shift; + my $c = $self->controller; + my $bytes = join '', @_; + return unless $bytes; + if ( $self->_encoding ) { + $bytes = encode( $self->_encoding, $bytes ); + } + $c->write($bytes . ( $OUTPUT_RECORD_SEPARATOR // '' ) ); +} + +sub BINMODE { + my ( $self, $mode ) = @_; + if ($mode) { + if ( $mode eq ':bytes' or $mode eq ':raw' ) { + $self->_encoding(''); + } + elsif ( $mode eq ':utf8' ) { + $self->_encoding('utf8'); + } + } +} + +1; diff --git a/Bugzilla/Template.pm b/Bugzilla/Template.pm index 299734d64..39272a538 100644 --- a/Bugzilla/Template.pm +++ b/Bugzilla/Template.pm @@ -12,6 +12,7 @@ use 5.10.1; use strict; use warnings; +use Bugzilla::Logging; use Bugzilla::Template::PreloadProvider; use Bugzilla::Bug; use Bugzilla::Constants; diff --git a/Bugzilla/Util.pm b/Bugzilla/Util.pm index a8477a62d..aa524b263 100644 --- a/Bugzilla/Util.pm +++ b/Bugzilla/Util.pm @@ -29,7 +29,7 @@ use base qw(Exporter); get_text template_var disable_utf8 enable_utf8 detect_encoding email_filter round extract_nicks); - +use Bugzilla::Logging; use Bugzilla::Constants; use Bugzilla::RNG qw(irand); @@ -317,6 +317,7 @@ sub do_ssl_redirect_if_required { # If we're already running under SSL, never redirect. return if $ENV{HTTPS} && $ENV{HTTPS} eq 'on'; + DEBUG("Redirect to HTTPS because \$ENV{HTTPS}=$ENV{HTTPS}"); Bugzilla->cgi->redirect_to_https(); } diff --git a/Bugzilla/WebService/Server/XMLRPC.pm b/Bugzilla/WebService/Server/XMLRPC.pm index 6bb73af01..5ad50e91c 100644 --- a/Bugzilla/WebService/Server/XMLRPC.pm +++ b/Bugzilla/WebService/Server/XMLRPC.pm @@ -14,11 +14,7 @@ use warnings; use Bugzilla::Logging; use XMLRPC::Transport::HTTP; use Bugzilla::WebService::Server; -if ($ENV{MOD_PERL}) { - our @ISA = qw(XMLRPC::Transport::HTTP::Apache Bugzilla::WebService::Server); -} else { - our @ISA = qw(XMLRPC::Transport::HTTP::CGI Bugzilla::WebService::Server); -} +our @ISA = qw(XMLRPC::Transport::HTTP::CGI Bugzilla::WebService::Server); use Bugzilla::WebService::Constants; use Bugzilla::Error; diff --git a/Bugzilla/WebService/Util.pm b/Bugzilla/WebService/Util.pm index 29ff05448..d462c884a 100644 --- a/Bugzilla/WebService/Util.pm +++ b/Bugzilla/WebService/Util.pm @@ -23,7 +23,7 @@ use base qw(Exporter); # We have to "require", not "use" this, because otherwise it tries to # use features of Test::More during import(). -require Test::Taint; +require Test::Taint if ${^TAINT}; our @EXPORT_OK = qw( extract_flags @@ -193,8 +193,10 @@ sub taint_data { # Though this is a private function, it hasn't changed since 2004 and # should be safe to use, and prevents us from having to write it ourselves # or require another module to do it. - Test::Taint::_deeply_traverse(\&_delete_bad_keys, \@params); - Test::Taint::taint_deeply(\@params); + if (${^TAINT}) { + Test::Taint::_deeply_traverse(\&_delete_bad_keys, \@params); + Test::Taint::taint_deeply(\@params); + } } sub _delete_bad_keys { diff --git a/Dockerfile b/Dockerfile index 6426f1ad8..fd02f222d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM mozillabteam/bmo-slim:20180801.2 +FROM mozillabteam/bmo-slim:20180809.1 ARG CI ARG CIRCLE_SHA1 @@ -9,12 +9,6 @@ ENV CIRCLE_BUILD_URL=${CIRCLE_BUILD_URL} ENV CIRCLE_SHA1=${CIRCLE_SHA1} ENV LOG4PERL_CONFIG_FILE=log4perl-json.conf -ENV HTTPD_StartServers=8 -ENV HTTPD_MinSpareServers=5 -ENV HTTPD_MaxSpareServers=20 -ENV HTTPD_ServerLimit=256 -ENV HTTPD_MaxClients=256 -ENV HTTPD_MaxRequestsPerChild=4000 ENV PORT=8000 @@ -28,7 +22,8 @@ RUN mv /opt/bmo/local /app && \ chown -R app:app /app && \ perl -I/app -I/app/local/lib/perl5 -c -E 'use Bugzilla; BEGIN { Bugzilla->extensions }' && \ perl -c /app/scripts/entrypoint.pl && \ - setcap 'cap_net_bind_service=+ep' /usr/sbin/httpd + setcap 'cap_net_bind_service=+ep' /usr/sbin/httpd && \ + setcap 'cap_net_bind_service=+ep' /usr/bin/perl USER app diff --git a/Log/Log4perl/Layout/Mozilla.pm b/Log/Log4perl/Layout/Mozilla.pm index b625c54f4..4aedd9843 100644 --- a/Log/Log4perl/Layout/Mozilla.pm +++ b/Log/Log4perl/Layout/Mozilla.pm @@ -53,6 +53,9 @@ sub render { my $mdc = Log::Log4perl::MDC->get_context; my $fields = $mdc->{fields} // {}; + if ($mdc->{request_id}) { + $fields->{request_id} = $mdc->{request_id} + } my %out = ( EnvVersion => LOGGING_FORMAT_VERSION, Hostname => $HOSTNAME, diff --git a/Makefile.PL b/Makefile.PL index 6d4225e9a..7e3095867 100755 --- a/Makefile.PL +++ b/Makefile.PL @@ -69,6 +69,7 @@ my %requires = ( 'Module::Metadata' => '1.000033', 'Module::Runtime' => '0.014', 'Mojolicious' => '7.71', + 'MojoX::Log::Log4perl::Tiny' => '0.01', 'Moo' => '2.002004', 'MooX::StrictConstructor' => '0.008', 'Mozilla::CA' => '20160104', @@ -81,10 +82,12 @@ my %requires = ( 'Template' => '2.24', 'Text::CSV_XS' => '1.26', 'Throwable' => '0.200013', - 'Type::Tiny' => '1.000005', + 'Sub::Quote' => '2.005000', + 'Type::Tiny' => '1.004002', 'URI' => '1.55', 'URI::Escape::XS' => '0.14', 'version' => '0.87', + 'EV' => '4.0', ); my %build_requires = ( 'ExtUtils::MakeMaker' => '7.22', ); @@ -97,7 +100,7 @@ my %test_requires = ( 'Perl::Critic::Freenode' => 0, 'Capture::Tiny' => 0, ); -my %recommends = ( Safe => '2.30' ); +my %recommends = ( Safe => '2.30',); # Windows requires some additional modules. if ( $OSNAME eq 'MSWin32' ) { @@ -285,18 +288,6 @@ my %optional_features = ( } }, }, - mod_perl => { - description => 'mod_perl support under Apache', - prereqs => { - runtime => { - requires => { - 'mod_perl2' => '1.999022', - 'Apache2::SizeLimit' => '0.96', - 'Plack::Handler::Apache2' => 0, - } - } - } - }, inbound_email => { description => 'Inbound Email', prereqs => { @@ -468,7 +459,6 @@ sub is_bmo_feature { ^ (?: pg | oracle - | mod_perl | sqlite | auth_ldap | auth_radius diff --git a/README.rst b/README.rst index 05eb1df1f..2a0fa289c 100644 --- a/README.rst +++ b/README.rst @@ -333,41 +333,6 @@ BMO_use_mailer_queue Usually configured on the MTA section of the admin interface, you may change this here for testing purposes. Should be 1 or 0. If 1, the job queue will be used. For testing, only set to 0 if the BMO_mail_delivery_method is None or Test. -HTTPD_StartServers - Sets the number of child server processes created on startup. - As the number of processes is dynamically controlled depending on the load, - there is usually little reason to adjust this parameter. - Default: 8 - -HTTPD_MinSpareServers - Sets the desired minimum number of idle child server processes. An idle - process is one which is not handling a request. If there are fewer than - MinSpareServers idle, then the parent process creates new children at a - maximum rate of 1 per second. - Default: 5 - -HTTPD_MaxSpareServers - Sets the desired maximum number of idle child server processes. An idle - process is one which is not handling a request. If there are more than - MaxSpareServers idle, then the parent process will kill off the excess - processes. - Default: 20 - -HTTPD_MaxClients - Sets the maximum number of child processes that will be launched to serve requests. - Default: 256 - -HTTPD_ServerLimit - Sets the maximum configured value for MaxClients for the lifetime of the - Apache process. - Default: 256 - -HTTPD_MaxRequestsPerChild - Sets the limit on the number of requests that an individual child server - process will handle. After MaxRequestsPerChild requests, the child process - will die. If MaxRequestsPerChild is 0, then the process will never expire. - Default: 4000 - USE_NYTPROF Write `Devel::NYTProf`_ profiles out for each requests. These will be named /app/data/nytprof.$host.$script.$n.$pid, where $host is diff --git a/buglist.cgi b/buglist.cgi index 4d3ad1a86..019bf0d4e 100755 --- a/buglist.cgi +++ b/buglist.cgi @@ -41,6 +41,8 @@ my $vars = {}; my $user = Bugzilla->login(); $cgi->redirect_search_url(); +use Bugzilla::Logging; +DEBUG("After the redirect."); my $buffer = $cgi->query_string(); if (length($buffer) == 0) { @@ -103,27 +105,6 @@ my $agent = ($cgi->http('X-Moz') && $cgi->http('X-Moz') =~ /\bmicrosummary\b/); my $format = $template->get_format("list/list", scalar $cgi->param('format'), scalar $cgi->param('ctype')); -# Use server push to display a "Please wait..." message for the user while -# executing their query if their browser supports it and they are viewing -# the bug list as HTML and they have not disabled it by adding &serverpush=0 -# to the URL. -# -# Server push is a Netscape 3+ hack incompatible with MSIE, Lynx, and others. -# Even Communicator 4.51 has bugs with it, especially during page reload. -# http://www.browsercaps.org used as source of compatible browsers. -# Safari (WebKit) does not support it, despite a UA that says otherwise (bug 188712) -# MSIE 5+ supports it on Mac (but not on Windows) (bug 190370) -# -my $serverpush = - $format->{'extension'} eq "html" - && exists $ENV{'HTTP_USER_AGENT'} - && $ENV{'HTTP_USER_AGENT'} =~ /Mozilla.[3-9]/ - && (($ENV{'HTTP_USER_AGENT'} !~ /[Cc]ompatible/) || ($ENV{'HTTP_USER_AGENT'} =~ /MSIE 5.*Mac_PowerPC/)) - && $ENV{'HTTP_USER_AGENT'} !~ /(?:WebKit|Trident|KHTML)/ - && !$agent - && !defined($cgi->param('serverpush')) - || $cgi->param('serverpush'); - my $order = $cgi->param('order') || ""; # The params object to use for the actual query itself @@ -744,24 +725,6 @@ $params->delete('limit') if $vars->{'default_limited'}; # Time to use server push to display an interim message to the user until # the query completes and we can display the bug list. -if ($serverpush) { - print $cgi->multipart_init(); - print $cgi->multipart_start(-type => 'text/html'); - - # Generate and return the UI (HTML page) from the appropriate template. - $template->process("list/server-push.html.tmpl", $vars) - || ThrowTemplateError($template->error()); - - # Under mod_perl, flush stdout so that the page actually shows up. - if ($ENV{MOD_PERL}) { - require Apache2::RequestUtil; - Apache2::RequestUtil->request->rflush(); - } - - # Don't do multipart_end() until we're ready to display the replacement - # page, otherwise any errors that happen before then (like SQL errors) - # will result in a blank page being shown to the user instead of the error. -} # Connect to the shadow database if this installation is using one to improve # query performance. @@ -1140,11 +1103,4 @@ $cgi->close_standby_message($contenttype, $disposition, $disp_prefix, $format->{ # Generate and return the UI (HTML page) from the appropriate template. $template->process($format->{'template'}, $vars) - || ThrowTemplateError($template->error()); - - -################################################################################ -# Script Conclusion -################################################################################ - -print $cgi->multipart_final() if $serverpush; + || ThrowTemplateError($template->error()); \ No newline at end of file diff --git a/bugzilla.pl b/bugzilla.pl new file mode 100755 index 000000000..efcea293b --- /dev/null +++ b/bugzilla.pl @@ -0,0 +1,20 @@ +#!/usr/bin/perl +use 5.10.1; +use strict; +use warnings; + +use File::Basename qw(basename dirname); +use File::Spec::Functions qw(catdir); +use Cwd qw(realpath); + +BEGIN { + require lib; + my $dir = realpath( dirname(__FILE__) ); + lib->import( $dir, catdir( $dir, 'lib' ), catdir( $dir, qw(local lib perl5) ) ); +} +use Mojolicious::Commands; + +$ENV{MOJO_LISTEN} ||= $ENV{PORT} ? "http://*:$ENV{PORT}" : "http://*:3001"; + +# Start command line interface for application +Mojolicious::Commands->start_app('Bugzilla::Quantum'); diff --git a/checksetup.pl b/checksetup.pl index d3f08e024..7c9826ee3 100755 --- a/checksetup.pl +++ b/checksetup.pl @@ -135,7 +135,7 @@ require Bugzilla::Install::Localconfig; import Bugzilla::Install::Localconfig qw(update_localconfig); require Bugzilla::Install::Filesystem; -import Bugzilla::Install::Filesystem qw(update_filesystem create_htaccess +import Bugzilla::Install::Filesystem qw(update_filesystem fix_all_file_permissions); require Bugzilla::Install::DB; require Bugzilla::DB; @@ -201,7 +201,6 @@ unless ($switch{'no-database'}) { ########################################################################### update_filesystem({ index_html => $lc_hash->{'index_html'} }); -create_htaccess() if $lc_hash->{'create_htaccess'}; # Remove parameters from the params file that no longer exist in Bugzilla, # and set the defaults for new ones diff --git a/colchange.cgi b/colchange.cgi index d77782bec..46d25ecdf 100755 --- a/colchange.cgi +++ b/colchange.cgi @@ -136,26 +136,12 @@ if (defined $cgi->param('rememberedquery')) { $params->param('columnlist', join(",", @collist)); $vars->{'redirect_url'} = "buglist.cgi?".$params->query_string(); - # If we're running on Microsoft IIS, $cgi->redirect discards # the Set-Cookie lines. In mod_perl, $cgi->redirect with cookies # causes the page to be rendered as text/plain. # Workaround is to use the old-fashioned redirection mechanism. # See bug 214466 and bug 376044 for details. - if ($ENV{'MOD_PERL'} - || $ENV{'SERVER_SOFTWARE'} =~ /Microsoft-IIS/ - || $ENV{'SERVER_SOFTWARE'} =~ /Sun ONE Web/) - { - print $cgi->header(-type => "text/html", - -refresh => "0; URL=$vars->{'redirect_url'}"); - } - else { - print $cgi->redirect($vars->{'redirect_url'}); - exit; - } - - $template->process("global/message.html.tmpl", $vars) - || ThrowTemplateError($template->error()); + print $cgi->redirect($vars->{'redirect_url'}); exit; } diff --git a/conf/httpd.conf b/conf/httpd.conf deleted file mode 100644 index 539ab4231..000000000 --- a/conf/httpd.conf +++ /dev/null @@ -1,103 +0,0 @@ -ServerName 127.0.0.1 -ServerTokens Prod -ServerRoot "/etc/httpd" -ServerAdmin root@localhost - -PidFile /tmp/httpd.pid -Timeout 120 -LimitRequestLine 35000 -KeepAlive Off -MaxKeepAliveRequests 100 -KeepAliveTimeout 15 - -StartServers ${HTTPD_StartServers} -MinSpareServers ${HTTPD_MinSpareServers} -MaxSpareServers ${HTTPD_MaxSpareServers} -ServerLimit ${HTTPD_ServerLimit} -MaxClients ${HTTPD_MaxClients} -MaxRequestsPerChild ${HTTPD_MaxRequestsPerChild} - -Listen ${PORT} -User app -Group app - -LoadModule auth_basic_module modules/mod_auth_basic.so -LoadModule auth_digest_module modules/mod_auth_digest.so -LoadModule authn_file_module modules/mod_authn_file.so -LoadModule authn_alias_module modules/mod_authn_alias.so -LoadModule authn_anon_module modules/mod_authn_anon.so -LoadModule authn_dbm_module modules/mod_authn_dbm.so -LoadModule authn_default_module modules/mod_authn_default.so -LoadModule authz_host_module modules/mod_authz_host.so -LoadModule authz_user_module modules/mod_authz_user.so -LoadModule authz_owner_module modules/mod_authz_owner.so -LoadModule authz_groupfile_module modules/mod_authz_groupfile.so -LoadModule authz_default_module modules/mod_authz_default.so -LoadModule log_config_module modules/mod_log_config.so -LoadModule env_module modules/mod_env.so -LoadModule mime_magic_module modules/mod_mime_magic.so -LoadModule expires_module modules/mod_expires.so -LoadModule deflate_module modules/mod_deflate.so -LoadModule headers_module modules/mod_headers.so -LoadModule setenvif_module modules/mod_setenvif.so -LoadModule mime_module modules/mod_mime.so -LoadModule negotiation_module modules/mod_negotiation.so -LoadModule dir_module modules/mod_dir.so -LoadModule alias_module modules/mod_alias.so -LoadModule rewrite_module modules/mod_rewrite.so -LoadModule perl_module modules/mod_perl.so - -UseCanonicalName Off - - Options FollowSymLinks - AllowOverride None - -AccessFileName .htaccess - - Order allow,deny - Deny from all - Satisfy All - -TypesConfig /etc/mime.types -DefaultType text/plain -MIMEMagicFile conf/magic -HostnameLookups Off - - ErrorLog "|/usr/bin/nc localhost ${LOGGING_PORT}" - - TransferLog "|/usr/bin/nc localhost ${LOGGING_PORT}" - - - - ErrorLog /dev/stderr - - TransferLog /dev/stdout - - -LogLevel warn -LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined -LogFormat "%h %l %u %t \"%r\" %>s %b" common -LogFormat "%{Referer}i -> %U" referer -LogFormat "%{User-agent}i" agent -ServerSignature Off -AddDefaultCharset UTF-8 - -Include /app/conf/env.conf - -PerlSwitches -wT -PerlRequire /app/mod_perl.pl -PerlSetEnv LOG4PERL_STDERR_DISABLE 1 -DirectoryIndex index.cgi -DocumentRoot "/app" - -Alias "/bmo" "/app" - - - SetEnvIf X-Forwarded-Proto "https" HTTPS=on - - - Options -Indexes -FollowSymLinks - AllowOverride None - Order allow,deny - Allow from all - diff --git a/conf/log4perl-test.conf b/conf/log4perl-test.conf index 77fc00af8..1760279db 100644 --- a/conf/log4perl-test.conf +++ b/conf/log4perl-test.conf @@ -4,14 +4,14 @@ log4perl.appender.Cereal.PeerAddr=127.0.0.1 log4perl.appender.Cereal.PeerPort=5880 log4perl.appender.Cereal.defer_connection=1 log4perl.appender.Cereal.layout = Log::Log4perl::Layout::PatternLayout -log4perl.appender.Cereal.layout.ConversionPattern = %d %6p | %c | %m{chomp}%n +log4perl.appender.Cereal.layout.ConversionPattern = %X{request_id} %d %6p | %c | %m{chomp}%n log4perl.filter.LOG_TO_STDERR = sub { not $ENV{LOG4PERL_STDERR_DISABLE} } log4perl.appender.Screen = Log::Log4perl::Appender::Screen log4perl.appender.Screen.Filter = LOG_TO_STDERR log4perl.appender.Screen.stderr = 1 log4perl.appender.Screen.layout = Log::Log4perl::Layout::PatternLayout -log4perl.appender.Screen.layout.ConversionPattern = %d %6p | %c | %m{chomp}%n +log4perl.appender.Screen.layout.ConversionPattern = STDERR: %X{request_id} %d %6p | %c | %m{chomp}%n log4perl.appender.File = Log::Log4perl::Appender::File log4perl.appender.File.layout = Log::Log4perl::Layout::Mozilla diff --git a/docker-compose.yml b/docker-compose.yml index 1ca6f5c90..b00c6bc03 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,6 +19,7 @@ services: - LOCALCONFIG_ENV=1 - LOG4PERL_CONFIG_FILE=log4perl-docker.conf - BUGZILLA_UNSAFE_AUTH_DELEGATION=1 + - HTTP_BACKEND=simple - PORT=80 - BMO_db_host=bmo-db.vm - BMO_db_name=bugs diff --git a/docs/en/rst/administering/parameters.rst b/docs/en/rst/administering/parameters.rst index 338df3eb3..0492e0070 100644 --- a/docs/en/rst/administering/parameters.rst +++ b/docs/en/rst/administering/parameters.rst @@ -28,12 +28,6 @@ utf8 .. note:: If you turn this parameter from :paramval:`off` to :paramval:`on`, you must re-run :file:`checksetup.pl` immediately afterward. -shutdownhtml - If there is any text in this field, this Bugzilla installation will - be completely disabled and this text will appear instead of all - Bugzilla pages for all users, including Admins. Used in the event - of site maintenance or outage situations. - announcehtml Any text in this field will be displayed at the top of every HTML page in this Bugzilla installation. The text is not wrapped in any diff --git a/docs/en/rst/style.rst b/docs/en/rst/style.rst index aa3957b95..5058a51a3 100644 --- a/docs/en/rst/style.rst +++ b/docs/en/rst/style.rst @@ -65,7 +65,7 @@ Other block types: aware of. .. todo:: This is some documentation-related task that still needs doing. - + Use both of the above block types sparingly. Consider putting the information in the main text, omitting it, or (if long) placing it in a subsidiary file. @@ -103,9 +103,6 @@ Inline Directives * A command to type in the shell: :command:`command --arguments` -* A parameter name: - :param:`shutdownhtml` - * A parameter value: :paramval:`DB` diff --git a/editparams.cgi b/editparams.cgi index ac7976d99..495d53937 100755 --- a/editparams.cgi +++ b/editparams.cgi @@ -137,9 +137,6 @@ if ($action eq 'save' && $current_module) { } push(@changes, $name); SetParam($name, $value); - if (($name eq "shutdownhtml") && ($value ne "")) { - $vars->{'shutdown_is_active'} = 1; - } if ($name eq 'duplicate_or_move_bug_status') { Bugzilla::Status::add_missing_bug_status_transitions($value); } diff --git a/extensions/BMO/Extension.pm b/extensions/BMO/Extension.pm index 3b1c03eec..6c4ad2ef2 100644 --- a/extensions/BMO/Extension.pm +++ b/extensions/BMO/Extension.pm @@ -2719,4 +2719,105 @@ sub enter_bug_entrydefaultvars { } } +sub app_startup { + my ($self, $args) = @_; + my $app = $args->{app}; + my $r = $app->routes; + + $r->get( + '/favicon.ico' => sub { + my $c = shift; + $c->reply->file( + $c->app->home->child('extensions/BMO/web/images/favicon.ico') + ); + } + ); + + $r->any( '/:REWRITE_itrequest' => [ REWRITE_itrequest => qr{form[\.:]itrequest} ] ) + ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Infrastructure & Operations', 'format' => 'itrequest' } ); + $r->any( '/:REWRITE_mozlist' => [ REWRITE_mozlist => qr{form[\.:]mozlist} ] ) + ->to( 'CGI#enter_bug_cgi' => { 'product' => 'mozilla.org', 'format' => 'mozlist' } ); + $r->any( '/:REWRITE_poweredby' => [ REWRITE_poweredby => qr{form[\.:]poweredby} ] ) + ->to( 'CGI#enter_bug_cgi' => { 'product' => 'mozilla.org', 'format' => 'poweredby' } ); + $r->any( '/:REWRITE_presentation' => [ REWRITE_presentation => qr{form[\.:]presentation} ] ) + ->to( 'cgi#enter_bug_cgi' => { 'product' => 'mozilla.org', 'format' => 'presentation' } ); + $r->any( '/:REWRITE_trademark' => [ REWRITE_trademark => qr{form[\.:]trademark} ] ) + ->to( 'cgi#enter_bug_cgi' => { 'product' => 'mozilla.org', 'format' => 'trademark' } ); + $r->any( '/:REWRITE_recoverykey' => [ REWRITE_recoverykey => qr{form[\.:]recoverykey} ] ) + ->to( 'cgi#enter_bug_cgi' => { 'product' => 'mozilla.org', 'format' => 'recoverykey' } ); + $r->any( '/:REWRITE_legal' => [ REWRITE_legal => qr{form[\.:]legal} ] ) + ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Legal', 'format' => 'legal' }, ); + $r->any( '/:REWRITE_recruiting' => [ REWRITE_recruiting => qr{form[\.:]recruiting} ] ) + ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Recruiting', 'format' => 'recruiting' } ); + $r->any( '/:REWRITE_intern' => [ REWRITE_intern => qr{form[\.:]intern} ] ) + ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Recruiting', 'format' => 'intern' } ); + $r->any( '/:REWRITE_mozpr' => [ REWRITE_mozpr => qr{form[\.:]mozpr} ] ) + ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Mozilla PR', 'format' => 'mozpr' }, ); + $r->any( '/:REWRITE_reps_mentorship' => [ REWRITE_reps_mentorship => qr{form[\.:]reps[\.:]mentorship} ] ) + ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Mozilla Reps', 'format' => 'mozreps' }, ); + $r->any( '/:REWRITE_reps_budget' => [ REWRITE_reps_budget => qr{form[\.:]reps[\.:]budget} ] ) + ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Mozilla Reps', 'format' => 'remo-budget' } ); + $r->any( '/:REWRITE_reps_swag' => [ REWRITE_reps_swag => qr{form[\.:]reps[\.:]swag} ] ) + ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Mozilla Reps', 'format' => 'remo-swag' } ); + $r->any( '/:REWRITE_reps_it' => [ REWRITE_reps_it => qr{form[\.:]reps[\.:]it} ] ) + ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Mozilla Reps', 'format' => 'remo-it' } ); + $r->any( '/:REWRITE_reps_payment' => [ REWRITE_reps_payment => qr{form[\.:]reps[\.:]payment} ] ) + ->to( 'CGI#page_cgi' => { 'id' => 'remo-form-payment.html' } ); + $r->any( '/:REWRITE_csa_discourse' => [ REWRITE_csa_discourse => qr{form[\.:]csa[\.:]discourse} ] ) + ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Infrastructure & Operations', 'format' => 'csa-discourse' } ); + $r->any( '/:REWRITE_employee_incident' => [ REWRITE_employee_incident => qr{form[\.:]employee[\.\-:]incident} ] ) + ->to( 'CGI#enter_bug_cgi' => { 'product' => 'mozilla.org', 'format' => 'employee-incident' } ); + $r->any( '/:REWRITE_brownbag' => [ REWRITE_brownbag => qr{form[\.:]brownbag} ] ) + ->to( 'CGI#https_air_mozilla_org_requests' => {} ); + $r->any( '/:REWRITE_finance' => [ REWRITE_finance => qr{form[\.:]finance} ] ) + ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Finance', 'format' => 'finance' } ); + $r->any( + '/:REWRITE_moz_project_review' => [ REWRITE_moz_project_review => qr{form[\.:]moz[\.\-:]project[\.\-:]review} ] + )->to( 'CGI#enter_bug_cgi' => { 'product' => 'mozilla.org', 'format' => 'moz-project-review' } ); + $r->any( '/:REWRITE_docs' => [ REWRITE_docs => qr{form[\.:]docs?} ] ) + ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Developer Documentation', 'format' => 'doc' } ); + $r->any( '/:REWRITE_mdn' => [ REWRITE_mdn => qr{form[\.:]mdn?} ] ) + ->to( 'CGI#enter_bug_cgi' => { 'format' => 'mdn', 'product' => 'developer.mozilla.org' } ); + $r->any( '/:REWRITE_swag_gear' => [ REWRITE_swag_gear => qr{form[\.:](swag|gear)} ] ) + ->to( 'CGI#enter_bug_cgi' => { 'format' => 'swag', 'product' => 'Marketing' } ); + $r->any( '/:REWRITE_costume' => [ REWRITE_costume => qr{form[\.:]costume} ] ) + ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Marketing', 'format' => 'costume' } ); + $r->any( '/:REWRITE_ipp' => [ REWRITE_ipp => qr{form[\.:]ipp} ] ) + ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Internet Public Policy', 'format' => 'ipp' } ); + $r->any( '/:REWRITE_creative' => [ REWRITE_creative => qr{form[\.:]creative} ] ) + ->to( 'CGI#enter_bug_cgi' => { 'format' => 'creative', 'product' => 'Marketing' } ); + $r->any( '/:REWRITE_user_engagement' => [ REWRITE_user_engagement => qr{form[\.:]user[\.\-:]engagement} ] ) + ->to( 'CGI#enter_bug_cgi' => { 'format' => 'user-engagement', 'product' => 'Marketing' } ); + $r->any( '/:REWRITE_dev_engagement_event' => + [ REWRITE_dev_engagement_event => qr{form[\.:]dev[\.\-:]engagement[\.\-\:]event} ] ) + ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Developer Engagement', 'format' => 'dev-engagement-event' } ); + $r->any( '/:REWRITE_mobile_compat' => [ REWRITE_mobile_compat => qr{form[\.:]mobile[\.\-:]compat} ] ) + ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Tech Evangelism', 'format' => 'mobile-compat' } ); + $r->any( '/:REWRITE_web_bounty' => [ REWRITE_web_bounty => qr{form[\.:]web[\.:]bounty} ] ) + ->to( 'CGI#enter_bug_cgi' => { 'format' => 'web-bounty', 'product' => 'mozilla.org' } ); + $r->any( '/:REWRITE_automative' => [ REWRITE_automative => qr{form[\.:]automative} ] ) + ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Testing', 'format' => 'automative' } ); + $r->any( '/:REWRITE_comm_newsletter' => [ REWRITE_comm_newsletter => qr{form[\.:]comm[\.:]newsletter} ] ) + ->to( 'CGI#enter_bug_cgi' => { 'format' => 'comm-newsletter', 'product' => 'Marketing' } ); + $r->any( '/:REWRITE_screen_share_whitelist' => + [ REWRITE_screen_share_whitelist => qr{form[\.:]screen[\.:]share[\.:]whitelist} ] ) + ->to( 'CGI#enter_bug_cgi' => { 'format' => 'screen-share-whitelist', 'product' => 'Firefox' } ); + $r->any( '/:REWRITE_data_compliance' => [ REWRITE_data_compliance => qr{form[\.:]data[\.\-:]compliance} ] ) + ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Data Compliance', 'format' => 'data-compliance' } ); + $r->any( '/:REWRITE_fsa_budget' => [ REWRITE_fsa_budget => qr{form[\.:]fsa[\.:]budget} ] ) + ->to( 'CGI#enter_bug_cgi' => { 'product' => 'FSA', 'format' => 'fsa-budget' } ); + $r->any( '/:REWRITE_triage_request' => [ REWRITE_triage_request => qr{form[\.:]triage[\.\-]request} ] ) + ->to( 'CGI#page_cgi' => { 'id' => 'triage_request.html' } ); + $r->any( '/:REWRITE_crm_CRM' => [ REWRITE_crm_CRM => qr{form[\.:](crm|CRM)} ] ) + ->to( 'CGI#enter_bug_cgi' => { 'format' => 'crm', 'product' => 'Marketing' } ); + $r->any( '/:REWRITE_nda' => [ REWRITE_nda => qr{form[\.:]nda} ] ) + ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Legal', 'format' => 'nda' } ); + $r->any( '/:REWRITE_name_clearance' => [ REWRITE_name_clearance => qr{form[\.:]name[\.:]clearance} ] ) + ->to( 'CGI#enter_bug_cgi' => { 'format' => 'name-clearance', 'product' => 'Legal' } ); + $r->any( '/:REWRITE_shield_studies' => [ REWRITE_shield_studies => qr{form[\.:]shield[\.:]studies} ] ) + ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Shield', 'format' => 'shield-studies' } ); + $r->any( '/:REWRITE_client_bounty' => [ REWRITE_client_bounty => qr{form[\.:]client[\.:]bounty} ] ) + ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Firefox', 'format' => 'client-bounty' } ); +} + __PACKAGE__->NAME; diff --git a/heartbeat.cgi b/heartbeat.cgi index 3edbed371..493674c16 100755 --- a/heartbeat.cgi +++ b/heartbeat.cgi @@ -29,7 +29,6 @@ my $ok = eval { die "database not available" unless $database_ok; die "memcached server(s) not available" unless $memcached_ok; - die "mod_perl not configured?" unless $ENV{MOD_PERL}; if ($dbh->isa('Bugzilla::DB::Mysql') && Bugzilla->params->{utf8} eq 'utf8mb4') { my $mysql_var = $dbh->selectall_hashref(q{SHOW VARIABLES LIKE 'character_set%'}, 'Variable_name'); foreach my $name (qw( character_set_client character_set_connection character_set_database )) { @@ -45,11 +44,4 @@ FATAL("heartbeat error: $@") if !$ok && $@; my $cgi = Bugzilla->cgi; print $cgi->header(-type => 'text/plain', -status => $ok ? '200 OK' : '500 Internal Server Error'); -print $ok ? "Bugzilla OK\n" : "Bugzilla NOT OK\n"; - -if ($ENV{MOD_PERL}) { - my $r = $cgi->r; - # doing this supresses the error document, but does not change the http response code. - $r->rflush; - $r->status(200); -} +print $ok ? "Bugzilla OK\n" : "Bugzilla NOT OK\n"; \ No newline at end of file diff --git a/jobqueue-worker.pl b/jobqueue-worker.pl old mode 100644 new mode 100755 diff --git a/mod_perl.pl b/mod_perl.pl deleted file mode 100644 index 09e3bac38..000000000 --- a/mod_perl.pl +++ /dev/null @@ -1,204 +0,0 @@ -#!/usr/bin/perl -T -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. - -package Bugzilla::ModPerl; - -use 5.10.1; -use strict; -use warnings; - -# This sets up our libpath without having to specify it in the mod_perl -# configuration. -use File::Basename; -use File::Spec; -BEGIN { - require lib; - my $dir = dirname(__FILE__); - lib->import($dir, File::Spec->catdir($dir, "lib"), File::Spec->catdir($dir, qw(local lib perl5))); -} - -use Bugzilla::ModPerl::StartupFix; -use Taint::Util qw(untaint); - -use constant USE_NYTPROF => !! $ENV{USE_NYTPROF}; -use constant NYTPROF_DIR => do { - my $dir = $ENV{NYTPROF_DIR}; - untaint($dir); - $dir; -}; -BEGIN { - if (USE_NYTPROF) { - $ENV{NYTPROF} = "savesrc=0:start=no:addpid=1"; - } -} -use if USE_NYTPROF, 'Devel::NYTProf::Apache'; - -use Bugzilla::Constants (); - -# If you have an Apache2::Status handler in your Apache configuration, -# you need to load Apache2::Status *here*, so that any later-loaded modules -# can report information to Apache2::Status. -#use Apache2::Status (); - -# We don't want to import anything into the global scope during -# startup, so we always specify () after using any module in this -# file. - -use Apache2::Log (); -use Apache2::ServerUtil; -use Apache2::SizeLimit; -use ModPerl::RegistryLoader (); -use File::Basename (); -use File::Find (); -use English qw(-no_match_vars $OSNAME); - -# This loads most of our modules. -use Bugzilla (); -# Loading Bugzilla.pm doesn't load this, though, and we want it preloaded. -use Bugzilla::BugMail (); -use Bugzilla::CGI (); -use Bugzilla::Extension (); -use Bugzilla::Install::Requirements (); -use Bugzilla::Util (); -use Bugzilla::RNG (); -use Bugzilla::ModPerl (); -use Mojo::Loader qw(find_modules); -use Module::Runtime qw(require_module); -use Bugzilla::WebService::Server::REST; - -# Make warnings go to the virtual host's log and not the main -# server log. -BEGIN { *CORE::GLOBAL::warn = \&Apache2::ServerRec::warn; } - -# Pre-compile the CGI.pm methods that we're going to use. -Bugzilla::CGI->compile(qw(:cgi :push)); - -# This means that every httpd child will die after processing a request if it -# is taking up more than $apache_size_limit of RAM all by itself, not counting RAM it is -# sharing with the other httpd processes. -my $limit = Bugzilla->localconfig->{apache_size_limit}; -if ($OSNAME eq 'linux' && ! eval { require Linux::Smaps }) { - WARN('SizeLimit requires Linux::Smaps on linux. size limit set to 800MB'); - $limit = 800_000; -} -Apache2::SizeLimit->set_max_unshared_size($limit); - -my $cgi_path = Bugzilla::Constants::bz_locations()->{'cgi_path'}; - -# Set up the configuration for the web server -my $server = Apache2::ServerUtil->server; -my $conf = Bugzilla::ModPerl->apache_config($cgi_path); -$server->add_config([ grep { length $_ } split("\n", $conf)]); - -# Pre-load localconfig. It might already be loaded, but we need to make sure. -Bugzilla->localconfig; -if ($ENV{LOCALCONFIG_ENV}) { - delete @ENV{ (Bugzilla::Install::Localconfig::ENV_KEYS) }; -} - -# Pre-load all extensions -Bugzilla::Extension->load_all(); - -Bugzilla->preload_features(); - -require_module($_) for find_modules('Bugzilla::User::Setting'); - -Bugzilla::WebService::Server::REST->preload; - -# Force instantiation of template so Bugzilla::Template::PreloadProvider can do its magic. -Bugzilla->preload_templates; - -# Have ModPerl::RegistryLoader pre-compile all CGI scripts. -my $rl = new ModPerl::RegistryLoader(); -# If we try to do this in "new" it fails because it looks for a -# Bugzilla/ModPerl/ResponseHandler.pm -$rl->{package} = 'Bugzilla::ModPerl::ResponseHandler'; -my $feature_files = Bugzilla::Install::Requirements::map_files_to_features(); - -# Prevent "use lib" from doing anything when the .cgi files are compiled. -# This is important to prevent the current directory from getting into -# @INC and messing things up. (See bug 630750.) -no warnings 'redefine'; -local *lib::import = sub {}; -use warnings; - -foreach my $file (glob "$cgi_path/*.cgi") { - my $base_filename = File::Basename::basename($file); - if (my $feature = $feature_files->{$base_filename}) { - next if !Bugzilla->feature($feature); - } - Bugzilla::Util::trick_taint($file); - $rl->handler($file, $file); -} - -# Some items might already be loaded into the request cache -# best to make sure it starts out empty. -# Because of bug 1347335 we also do this in init_page(). -Bugzilla::clear_request_cache(); - -package Bugzilla::ModPerl::ResponseHandler; -use strict; -use base qw(ModPerl::Registry); -use Bugzilla; -use Bugzilla::Constants qw(USAGE_MODE_REST bz_locations); -use Time::HiRes; -use Sys::Hostname; - -sub handler : method { - my $class = shift; - - # $0 is broken under mod_perl before 2.0.2, so we have to set it - # here explicitly or init_page's shutdownhtml code won't work right. - $0 = $ENV{'SCRIPT_FILENAME'}; - - # Prevent "use lib" from modifying @INC in the case where a .cgi file - # is being automatically recompiled by mod_perl when Apache is - # running. (This happens if a file changes while Apache is already - # running.) - no warnings 'redefine'; - local *lib::import = sub {}; - use warnings; - - if (Bugzilla::ModPerl::USE_NYTPROF) { - state $count = {}; - state $dir = Bugzilla::ModPerl::NYTPROF_DIR // bz_locations()->{datadir}; - state $host = (split(/\./, hostname()))[0]; - my $script = File::Basename::basename($ENV{SCRIPT_FILENAME}); - $script =~ s/\.cgi$//; - my $file = $dir . "/nytprof.$host.$script." . ++$count->{$$}; - DB::enable_profile($file); - } - Bugzilla::init_page(); - my $result = $class->SUPER::handler(@_); - if (Bugzilla::ModPerl::USE_NYTPROF) { - DB::disable_profile(); - DB::finish_profile(); - } - - # When returning data from the REST api we must only return 200 or 304, - # which tells Apache not to append its error html documents to the - # response. - return Bugzilla->usage_mode == USAGE_MODE_REST && $result != 304 - ? Apache2::Const::OK - : $result; -} - - -package Bugzilla::ModPerl::CleanupHandler; -use strict; -use Apache2::Const -compile => qw(OK); - -sub handler { - my $r = shift; - - Bugzilla::_cleanup(); - - return Apache2::Const::OK; -} - -1; diff --git a/qa/t/lib/QA/Util.pm b/qa/t/lib/QA/Util.pm index 5d041d560..bf9151fee 100644 --- a/qa/t/lib/QA/Util.pm +++ b/qa/t/lib/QA/Util.pm @@ -18,6 +18,7 @@ use Sys::Hostname qw(hostname); use Socket qw(inet_ntoa); use WWW::Selenium::Util qw(server_is_running); use URI; +use URI::QueryParam; # Fixes wide character warnings BEGIN { @@ -50,6 +51,7 @@ use base qw(Exporter); get_selenium get_rpc_clients + check_page_load WAIT_TIME CHROME_MODE @@ -396,6 +398,32 @@ sub set_parameters { } } +my @ANY_KEYS = qw( t token ); + +sub check_page_load { + my ($sel, $wait, $expected) = @_; + my $expected_uri = URI->new($expected); + $sel->wait_for_page_to_load_ok($wait); + my $uri = URI->new($sel->get_location); + + foreach my $u ($expected_uri, $uri) { + $u->host('HOSTNAME'); + foreach my $any_key (@ANY_KEYS) { + if ($u->query_param($any_key)) { + $u->query_param($any_key => '__ANYTHING__'); + } + } + } + + if ($expected_uri->query_param('id')) { + if ($expected_uri->query_param('id') eq '__BUG_ID__') { + $uri->query_param('id' => '__BUG_ID__'); + } + } + my ($pkg, $file, $line) = caller; + is($uri, $expected_uri, "checking location on $file line $line"); +} + 1; __END__ diff --git a/qa/t/test_bug_edit.t b/qa/t/test_bug_edit.t index 07a64876b..01037aa1e 100644 --- a/qa/t/test_bug_edit.t +++ b/qa/t/test_bug_edit.t @@ -32,10 +32,10 @@ if ($sel->is_text_present("My bugs from QA_Selenium")) { # Just in case the test failed before completion previously, reset the CANEDIT bit. go_to_admin($sel); $sel->click_ok("link=Groups"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, q{http://HOSTNAME:8000/bmo/editgroups.cgi}); $sel->title_is("Edit Groups"); $sel->click_ok("link=Master"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, q{http://HOSTNAME:8000/bmo/editgroups.cgi?action=changeform&group=26}); $sel->title_is("Change Group: Master"); my $group_url = $sel->get_location(); $group_url =~ /group=(\d+)$/; @@ -52,7 +52,7 @@ $sel->select_ok("bug_severity", "label=critical"); $sel->type_ok("short_desc", "Test bug editing"); $sel->type_ok("comment", "ploc"); $sel->click_ok("commit"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, qq{http://HOSTNAME:8000/bmo/show_bug.cgi?id=__BUG_ID__}); my $bug1_id = $sel->get_value('//input[@name="id" and @type="hidden"]'); $sel->is_text_present_ok('has been added to the database', "Bug $bug1_id created"); @@ -67,28 +67,28 @@ $sel->type_ok("status_whiteboard", "[Selenium was here]"); $sel->type_ok("comment", "new comment from me :)"); $sel->select_ok("bug_status", "label=RESOLVED"); $sel->click_ok("commit"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, qq{http://HOSTNAME:8000/bmo/show_bug.cgi?id=$bug1_id}); $sel->is_text_present_ok("Changes submitted for bug $bug1_id"); # Now move the bug into another product, which has a mandatory group. $sel->click_ok("link=bug $bug1_id"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, qq{http://HOSTNAME:8000/bmo/show_bug.cgi?id=$bug1_id}); $sel->title_like(qr/^$bug1_id /); $sel->select_ok("product", "label=QA-Selenium-TEST"); $sel->type_ok("comment", "moving to QA-Selenium-TEST"); $sel->click_ok("commit"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, q{http://HOSTNAME:8000/bmo/process_bug.cgi}); $sel->title_is("Verify New Product Details..."); $sel->select_ok("component", "label=QA-Selenium-TEST"); $sel->is_element_present_ok('//input[@type="checkbox" and @name="groups" and @value="QA-Selenium-TEST"]'); ok(!$sel->is_editable('//input[@type="checkbox" and @name="groups" and @value="QA-Selenium-TEST"]'), "QA-Selenium-TEST group not editable"); $sel->is_checked_ok('//input[@type="checkbox" and @name="groups" and @value="QA-Selenium-TEST"]', "QA-Selenium-TEST group is selected"); $sel->click_ok("change_product"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, qq{http://HOSTNAME:8000/bmo/show_bug.cgi?id=$bug1_id}); $sel->is_text_present_ok("Changes submitted for bug $bug1_id"); $sel->click_ok("link=bug $bug1_id"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, qq{http://HOSTNAME:8000/bmo/show_bug.cgi?id=$bug1_id}); $sel->title_like(qr/^$bug1_id /); $sel->select_ok("bug_severity", "label=normal"); $sel->select_ok("priority", "label=High"); @@ -101,14 +101,14 @@ $sel->type_ok("comment", "Unchecking the reporter_accessible checkbox"); $sel->click_ok("reporter_accessible"); $sel->select_ok("bug_status", "label=VERIFIED"); $sel->click_ok("commit"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, qq{http://HOSTNAME:8000/bmo/show_bug.cgi?id=$bug1_id}); $sel->is_text_present_ok("Changes submitted for bug $bug1_id"); $sel->click_ok("link=bug $bug1_id"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, qq{http://HOSTNAME:8000/bmo/show_bug.cgi?id=$bug1_id}); $sel->title_like(qr/^$bug1_id /); $sel->type_ok("comment", "I am the reporter, but I can see the bug anyway as I belong to the mandatory group"); $sel->click_ok("commit"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, qq{http://HOSTNAME:8000/bmo/show_bug.cgi?id=$bug1_id}); $sel->is_text_present_ok("Changes submitted for bug $bug1_id"); logout($sel); @@ -125,16 +125,16 @@ $sel->click_ok("bz_assignee_edit_action"); $sel->type_ok("assigned_to", $config->{admin_user_login}); $sel->type_ok("comment", "I have editbugs privs. Taking!"); $sel->click_ok("commit"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, qq{http://HOSTNAME:8000/bmo/show_bug.cgi?id=$bug1_id}); $sel->is_text_present_ok("Changes submitted for bug $bug1_id"); $sel->click_ok("link=bug $bug1_id"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, qq{http://HOSTNAME:8000/bmo/show_bug.cgi?id=$bug1_id}); $sel->title_like(qr/^$bug1_id /); $sel->click_ok("cc_edit_area_showhide"); $sel->type_ok("newcc", $config->{unprivileged_user_login}); $sel->click_ok("commit"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, qq{http://HOSTNAME:8000/bmo/show_bug.cgi?id=$bug1_id}); $sel->is_text_present_ok("Changes submitted for bug $bug1_id"); logout($sel); @@ -153,7 +153,7 @@ go_to_bug($sel, $bug1_id); $sel->click_ok("cclist_accessible"); $sel->type_ok("comment", "I am allowed to turn off cclist_accessible despite not being in the mandatory group"); $sel->click_ok("commit"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, qq{http://HOSTNAME:8000/bmo/show_bug.cgi?id=$bug1_id}); $sel->is_text_present_ok("Changes submitted for bug $bug1_id"); logout($sel); @@ -162,7 +162,7 @@ logout($sel); log_in($sel, $config, 'unprivileged'); $sel->type_ok("quicksearch_top", $bug1_id); $sel->submit("header-search"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, qq{http://HOSTNAME:8000/bmo/show_bug.cgi?id=$bug1_id}); $sel->title_is("Access Denied"); $sel->is_text_present_ok("You are not authorized to access bug $bug1_id"); logout($sel); @@ -178,7 +178,7 @@ $sel->click_ok("set_default_assignee"); $sel->uncheck_ok("set_default_assignee"); $sel->type_ok("comment", "-> Moving back to Testproduct."); $sel->click_ok("commit"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, q{http://HOSTNAME:8000/bmo/process_bug.cgi}); $sel->title_is("Verify New Product Details..."); $sel->select_ok("component", "label=TestComponent"); $sel->is_text_present_ok("These groups are not legal for the 'TestProduct' product or you are not allowed to restrict bugs to these groups"); @@ -189,15 +189,15 @@ $sel->is_element_present_ok('//input[@type="checkbox" and @name="groups" and @va $sel->is_editable_ok('//input[@type="checkbox" and @name="groups" and @value="Master"]', "Master group is editable"); ok(!$sel->is_checked('//input[@type="checkbox" and @name="groups" and @value="Master"]'), "Master group not selected by default"); $sel->click_ok("change_product"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, qq{http://HOSTNAME:8000/bmo/show_bug.cgi?id=$bug1_id}); $sel->is_text_present_ok("Changes submitted for bug $bug1_id"); $sel->click_ok("link=bug $bug1_id"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, qq{http://HOSTNAME:8000/bmo/show_bug.cgi?id=$bug1_id}); $sel->title_like(qr/^$bug1_id /); $sel->click_ok("cclist_accessible"); $sel->type_ok("comment", "I am allowed to turn off cclist_accessible despite not being in the mandatory group"); $sel->click_ok("commit"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, qq{http://HOSTNAME:8000/bmo/show_bug.cgi?id=$bug1_id}); $sel->is_text_present_ok("Changes submitted for bug $bug1_id"); logout($sel); @@ -216,7 +216,7 @@ $sel->click_ok("cc_edit_area_showhide"); $sel->add_selection_ok("cc", "label=" . $config->{admin_user_login}); $sel->click_ok("removecc"); $sel->click_ok("commit"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, qq{http://HOSTNAME:8000/bmo/show_bug.cgi?id=$bug1_id}); $sel->is_text_present_ok("Changes submitted for bug $bug1_id"); logout($sel); @@ -225,11 +225,11 @@ logout($sel); log_in($sel, $config, 'admin'); edit_product($sel, "TestProduct"); $sel->click_ok("link=Edit Group Access Controls:"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, q{http://HOSTNAME:8000/bmo/editproducts.cgi?action=editgroupcontrols&product=TestProduct}); $sel->title_is("Edit Group Controls for TestProduct"); $sel->check_ok("canedit_$master_gid"); $sel->click_ok("submit"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, q{http://HOSTNAME:8000/bmo/editproducts.cgi}); $sel->title_is("Update group access controls for TestProduct"); # The user is in the master group, so he can comment. @@ -237,7 +237,7 @@ $sel->title_is("Update group access controls for TestProduct"); go_to_bug($sel, $bug1_id); $sel->type_ok("comment", "Do nothing except adding a comment..."); $sel->click_ok("commit"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, qq{http://HOSTNAME:8000/bmo/show_bug.cgi?id=$bug1_id}); $sel->is_text_present_ok("Changes submitted for bug $bug1_id"); logout($sel); @@ -247,7 +247,7 @@ log_in($sel, $config, 'QA_Selenium_TEST'); go_to_bug($sel, $bug1_id); $sel->type_ok("comment", "Just a comment too..."); $sel->click_ok("commit"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, q{http://HOSTNAME:8000/bmo/process_bug.cgi}); $sel->title_is("Product Edit Access Denied"); $sel->is_text_present_ok("You are not permitted to edit bugs in product TestProduct."); logout($sel); @@ -256,10 +256,12 @@ logout($sel); log_in($sel, $config, 'admin'); open_advanced_search_page($sel); +screenshot_page($sel, '/app/artifacts/line259.png'); $sel->remove_all_selections_ok("product"); $sel->add_selection_ok("product", "TestProduct"); $sel->remove_all_selections_ok("bug_status"); $sel->remove_all_selections_ok("resolution"); +screenshot_page($sel, '/app/artifacts/line264.png'); $sel->is_checked_ok("emailassigned_to1"); $sel->select_ok("emailtype1", "label=is"); $sel->type_ok("email1", $config->{admin_user_login}); @@ -268,21 +270,22 @@ $sel->check_ok("emailqa_contact2"); $sel->check_ok("emailcc2"); $sel->select_ok("emailtype2", "label=is"); $sel->type_ok("email2", $config->{QA_Selenium_TEST_user_login}); +screenshot_page($sel, '/app/artifacts/line271.png'); $sel->click_ok("Search"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, q{http://HOSTNAME:8000/bmo/buglist.cgi?emailreporter2=1&emailtype2=exact&order=Importance&list_id=15&emailtype1=exact&emailcc2=1&query_format=advanced&emailassigned_to1=1&emailqa_contact2=1&email2=QA-Selenium-TEST%40mozilla.test&email1=admin%40mozilla.test&emailassigned_to2=1&product=TestProduct}); $sel->title_is("Bug List"); - +screenshot_page($sel, '/app/artifacts/line275.png'); $sel->is_text_present_ok("One bug found."); $sel->type_ok("save_newqueryname", "My bugs from QA_Selenium"); $sel->click_ok("remember"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, q{http://HOSTNAME:8000/bmo/buglist.cgi?newquery=email1%3Dadmin%2540mozilla.test%26email2%3DQA-Selenium-TEST%2540mozilla.test%26emailassigned_to1%3D1%26emailassigned_to2%3D1%26emailcc2%3D1%26emailqa_contact2%3D1%26emailreporter2%3D1%26emailtype1%3Dexact%26emailtype2%3Dexact%26list_id%3D15%26product%3DTestProduct%26query_format%3Dadvanced%26order%3Dpriority%252Cbug_severity&cmdtype=doit&remtype=asnamed&token=1531926552-dc69995d79c786af046436ec6717000b&newqueryname=My%20bugs%20from%20QA_Selenium&list_id=16}); $sel->title_is("Search created"); $sel->is_text_present_ok("OK, you have a new search named My bugs from QA_Selenium."); $sel->click_ok("link=My bugs from QA_Selenium"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, q{http://HOSTNAME:8000/bmo/buglist.cgi?cmdtype=runnamed&namedcmd=My%20bugs%20from%20QA_Selenium&list_id=17}); $sel->title_is("Bug List: My bugs from QA_Selenium"); $sel->click_ok("long_format"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, q{http://HOSTNAME:8000/bmo/show_bug.cgi}); $sel->title_is("Full Text Bug Listing"); $sel->is_text_present_ok("Bug $bug1_id"); $sel->is_text_present_ok("Status: CONFIRMED"); @@ -303,25 +306,25 @@ $sel->type_ok("short_desc", "New bug from me"); # We turned on the CANEDIT bit for TestProduct. $sel->type_ok("comment", "I can enter a new bug, but not edit it, right?"); $sel->click_ok("commit"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, qq{http://HOSTNAME:8000/bmo/show_bug.cgi?id=__BUG_ID__}); my $bug2_id = $sel->get_value('//input[@name="id" and @type="hidden"]'); $sel->is_text_present_ok('has been added to the database', "Bug $bug2_id created"); # Clicking the "Back" button and resubmitting the form again should trigger a suspicous action error. $sel->go_back_ok(); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, q{http://HOSTNAME:8000/bmo/enter_bug.cgi?product=TestProduct&format=__default__}); $sel->title_is("Enter Bug: TestProduct"); $sel->click_ok("commit"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, q{http://HOSTNAME:8000/bmo/post_bug.cgi}); $sel->title_is("Suspicious Action"); $sel->is_text_present_ok("you have no valid token for the create_bug action"); $sel->click_ok('//input[@value="Confirm Changes"]'); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, q{http://HOSTNAME:8000/bmo/show_bug.cgi?id=15}); $sel->is_text_present_ok('has been added to the database', 'Bug created'); $sel->type_ok("comment", "New comment not allowed"); $sel->click_ok("commit"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, q{http://HOSTNAME:8000/bmo/process_bug.cgi}); $sel->title_is("Product Edit Access Denied"); $sel->is_text_present_ok("You are not permitted to edit bugs in product TestProduct."); logout($sel); @@ -334,42 +337,46 @@ $sel->click_ok("bz_assignee_edit_action"); $sel->type_ok("assigned_to", $config->{admin_user_login}); $sel->type_ok("comment", "Taking!"); $sel->click_ok("commit"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, qq{http://HOSTNAME:8000/bmo/show_bug.cgi?id=$bug2_id}); $sel->is_text_present_ok("Changes submitted for bug $bug2_id"); # Test mass-change. $sel->click_ok("link=My bugs from QA_Selenium"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +screenshot_page($sel, '/app/artifacts/line344.png'); +check_page_load($sel, WAIT_TIME, q{http://HOSTNAME:8000/bmo/buglist.cgi?cmdtype=runnamed&namedcmd=My%20bugs%20from%20QA_Selenium&list_id=19}); +screenshot_page($sel, '/app/artifacts/line346.png'); $sel->title_is("Bug List: My bugs from QA_Selenium"); +screenshot_page($sel, '/app/artifacts/line348.png'); $sel->is_text_present_ok("2 bugs found"); +screenshot_page($sel, '/app/artifacts/line350.png'); $sel->click_ok("link=Change Several Bugs at Once"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, q{http://HOSTNAME:8000/bmo/buglist.cgi?email1=admin%40mozilla.test&email2=QA-Selenium-TEST%40mozilla.test&emailassigned_to1=1&emailassigned_to2=1&emailcc2=1&emailqa_contact2=1&emailreporter2=1&emailtype1=exact&emailtype2=exact&product=TestProduct&query_format=advanced&order=priority%2Cbug_severity&tweak=1&list_id=20}); $sel->title_is("Bug List"); $sel->click_ok("check_all"); $sel->type_ok("comment", 'Mass change"'); $sel->select_ok("bug_status", "label=RESOLVED"); $sel->select_ok("resolution", "label=WORKSFORME"); $sel->click_ok("commit"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, q{http://HOSTNAME:8000/bmo/process_bug.cgi}); $sel->title_is("Bugs processed"); $sel->click_ok("link=bug $bug1_id"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, qq{http://HOSTNAME:8000/bmo/show_bug.cgi?id=$bug1_id}); $sel->title_like(qr/$bug1_id /); $sel->selected_label_is("resolution", "WORKSFORME"); $sel->select_ok("resolution", "label=INVALID"); $sel->click_ok("commit"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, qq{http://HOSTNAME:8000/bmo/show_bug.cgi?id=$bug1_id}); $sel->is_text_present_ok("Changes submitted for bug $bug1_id"); $sel->click_ok("link=bug $bug1_id"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, qq{http://HOSTNAME:8000/bmo/show_bug.cgi?id=$bug1_id}); $sel->title_like(qr/$bug1_id /); $sel->selected_label_is("resolution", "INVALID"); $sel->click_ok("link=History"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, qq{http://HOSTNAME:8000/bmo/show_activity.cgi?id=$bug1_id}); $sel->title_is("Changes made to bug $bug1_id"); $sel->is_text_present_ok("URL foo.cgi?action=bar"); $sel->is_text_present_ok("Severity critical blocker"); @@ -431,10 +438,10 @@ foreach my $params (["no_token_single_bug", ""], ["invalid_token_single_bug", "& $sel->title_is("Suspicious Action"); $sel->is_text_present_ok($token ? "an invalid token" : "web browser directly"); $sel->click_ok("confirm"); - $sel->wait_for_page_to_load_ok(WAIT_TIME); + check_page_load($sel, WAIT_TIME, qq{http://HOSTNAME:8000/bmo/show_bug.cgi?id=$bug1_id}); $sel->is_text_present_ok("Changes submitted for bug $bug1_id"); $sel->click_ok("link=bug $bug1_id"); - $sel->wait_for_page_to_load_ok(WAIT_TIME); + check_page_load($sel, WAIT_TIME, qq{http://HOSTNAME:8000/bmo/show_bug.cgi?id=$bug1_id}); $sel->title_like(qr/^$bug1_id /); $sel->is_text_present_ok($comment); } @@ -446,16 +453,16 @@ foreach my $params (["no_token_mass_change", ""], ["invalid_token_mass_change", $sel->title_is("Suspicious Action"); $sel->is_text_present_ok("no valid token for the buglist_mass_change action"); $sel->click_ok("confirm"); - $sel->wait_for_page_to_load_ok(WAIT_TIME); + check_page_load($sel, WAIT_TIME, q{http://HOSTNAME:8000/bmo/process_bug.cgi}); $sel->title_is("Bugs processed"); foreach my $bug_id ($bug1_id, $bug2_id) { $sel->click_ok("link=bug $bug_id"); - $sel->wait_for_page_to_load_ok(WAIT_TIME); + check_page_load($sel, WAIT_TIME, qq{http://HOSTNAME:8000/bmo/show_bug.cgi?id=$bug_id}); $sel->title_like(qr/^$bug_id /); $sel->is_text_present_ok($comment); next if $bug_id == $bug2_id; $sel->go_back_ok(); - $sel->wait_for_page_to_load_ok(WAIT_TIME); + check_page_load($sel, WAIT_TIME, q{http://HOSTNAME:8000/bmo/process_bug.cgi}); $sel->title_is("Bugs processed"); } } @@ -463,26 +470,26 @@ foreach my $params (["no_token_mass_change", ""], ["invalid_token_mass_change", # Now move these bugs out of our radar. $sel->click_ok("link=My bugs from QA_Selenium"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, q{http://HOSTNAME:8000/bmo/buglist.cgi?cmdtype=runnamed&namedcmd=My%20bugs%20from%20QA_Selenium&list_id=21}); $sel->title_is("Bug List: My bugs from QA_Selenium"); $sel->is_text_present_ok("2 bugs found"); $sel->click_ok("link=Change Several Bugs at Once"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, q{http://HOSTNAME:8000/bmo/buglist.cgi?email1=admin%40mozilla.test&email2=QA-Selenium-TEST%40mozilla.test&emailassigned_to1=1&emailassigned_to2=1&emailcc2=1&emailqa_contact2=1&emailreporter2=1&emailtype1=exact&emailtype2=exact&product=TestProduct&query_format=advanced&order=priority%2Cbug_severity&tweak=1&list_id=22}); $sel->title_is("Bug List"); $sel->click_ok("check_all"); $sel->type_ok("comment", "Reassigning to the reporter"); $sel->type_ok("assigned_to", $config->{QA_Selenium_TEST_user_login}); $sel->click_ok("commit"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, q{http://HOSTNAME:8000/bmo/process_bug.cgi}); $sel->title_is("Bugs processed"); # Now delete the saved search. $sel->click_ok("link=My bugs from QA_Selenium"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, q{http://HOSTNAME:8000/bmo/buglist.cgi?cmdtype=runnamed&namedcmd=My%20bugs%20from%20QA_Selenium&list_id=23}); $sel->title_is("Bug List: My bugs from QA_Selenium"); $sel->click_ok("link=Forget Search 'My bugs from QA_Selenium'"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, q{http://HOSTNAME:8000/bmo/buglist.cgi?cmdtype=dorem&remaction=forget&namedcmd=My%20bugs%20from%20QA_Selenium&token=1531926582-f228fa8ebc2f2b3970f2a791e54534ec&list_id=24}); $sel->title_is("Search is gone"); $sel->is_text_present_ok("OK, the My bugs from QA_Selenium search is gone"); @@ -495,10 +502,10 @@ sub clear_canedit_on_testproduct { edit_product($sel, "TestProduct"); $sel->click_ok("link=Edit Group Access Controls:"); - $sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, q{http://HOSTNAME:8000/bmo/editproducts.cgi?action=editgroupcontrols&product=TestProduct}); $sel->title_is("Edit Group Controls for TestProduct"); $sel->uncheck_ok("canedit_$master_gid"); $sel->click_ok("submit"); - $sel->wait_for_page_to_load_ok(WAIT_TIME); +check_page_load($sel, WAIT_TIME, q{http://HOSTNAME:8000/bmo/editproducts.cgi}); $sel->title_is("Update group access controls for TestProduct"); } diff --git a/qa/t/test_shutdown.t b/qa/t/test_shutdown.t deleted file mode 100644 index dc5cabd4a..000000000 --- a/qa/t/test_shutdown.t +++ /dev/null @@ -1,72 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. - -use strict; -use warnings; -use lib qw(lib ../../lib ../../local/lib/perl5); - -use Test::More "no_plan"; - -use QA::Util; - -my ($sel, $config) = get_selenium(); - -log_in($sel, $config, 'admin'); -set_parameters($sel, { "General" => {shutdownhtml => {type => "text", - value => "I'm down (set by test_shutdown.t)" } - } }); - -# None of the following pages should be accessible when Bugzilla is down. - -my @pages = qw(admin attachment buglist chart colchange config createaccount - describecomponents describekeywords duplicates - editclassifications editcomponents editfields editflagtypes - editgroups editkeywords editmilestones editproducts editsettings - editusers editvalues editversions editwhines editworkflow - enter_bug index long_list page post_bug process_bug query quips - relogin report reports request sanitycheck search_plugin - show_activity show_bug showattachment showdependencygraph - showdependencytree sidebar summarize_time token userprefs votes - xml xmlrpc); - -foreach my $page (@pages) { - $sel->open_ok("/$config->{bugzilla_installation}/${page}.cgi"); - $sel->title_is("Bugzilla is Down"); -} - -# Those have parameters passed to the page, so we put them here separately. - -@pages = ("query.cgi?format=report-table", "query.cgi?format=report-graph", - "votes.cgi?action=show_user", "votes.cgi?action=show_bug"); - -foreach my $page (@pages) { - $sel->open_ok("/$config->{bugzilla_installation}/$page"); - $sel->title_is("Bugzilla is Down"); -} - -# Clear 'shutdownhtml', to re-enable Bugzilla. -# At this point, the admin has been logged out. We cannot use log_in(), -# nor set_parameters(), due to shutdownhtml being active. - -$sel->open_ok("/$config->{bugzilla_installation}/editparams.cgi"); -$sel->title_is("Log in to Bugzilla"); -$sel->type_ok("Bugzilla_login", $config->{admin_user_login}, "Enter admin login name"); -$sel->type_ok("Bugzilla_password", $config->{admin_user_passwd}, "Enter admin password"); -$sel->click_ok("log_in"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); -$sel->title_is("Configuration: General"); -$sel->type_ok("shutdownhtml", ""); -$sel->click_ok('//input[@type="submit" and @value="Save Changes"]', undef, "Save Changes"); -$sel->wait_for_page_to_load_ok(WAIT_TIME); -$sel->title_is("Parameters Updated"); - -# Accessing index.cgi should work again now. - -$sel->click_ok('//*[@id="header-title"]//a'); -$sel->wait_for_page_to_load_ok(WAIT_TIME); -$sel->title_is("Bugzilla Main Page"); -logout($sel); diff --git a/scripts/block-ip.pl b/scripts/block-ip.pl index b767a1fd5..3fa66d336 100755 --- a/scripts/block-ip.pl +++ b/scripts/block-ip.pl @@ -12,8 +12,8 @@ use warnings; use lib qw(. lib local/lib/perl5); use Bugzilla; +use Bugzilla::Quantum; use Bugzilla::Constants; -use Bugzilla::ModPerl::BlockIP; use Getopt::Long; Bugzilla->usage_mode(USAGE_MODE_CMDLINE); @@ -23,10 +23,12 @@ GetOptions('unblock' => \$unblock); pod2usage("No IPs given") unless @ARGV; +my $app = Bugzilla::Quantum->new; + if ($unblock) { - Bugzilla::ModPerl::BlockIP->unblock_ip($_) for @ARGV; + $app->unblock_ip($_) for @ARGV; } else { - Bugzilla::ModPerl::BlockIP->block_ip($_) for @ARGV; + $app->block_ip($_) for @ARGV; } =head1 NAME @@ -52,4 +54,4 @@ If passed, the IPs will be unblocked instead of blocked. Use this to remove IPs =head1 DESCRIPTION -This is just a simple CLI inteface to L. +This is just a simple CLI inteface to L. diff --git a/scripts/entrypoint.pl b/scripts/entrypoint.pl index ce1cc795b..21d9aebb1 100755 --- a/scripts/entrypoint.pl +++ b/scripts/entrypoint.pl @@ -83,7 +83,6 @@ sub cmd_demo { sub cmd_httpd { check_data_dir(); wait_for_db(); - check_httpd_env(); my $httpd_exit_f = run_cereal_and_httpd(); assert_httpd()->get(); @@ -156,6 +155,7 @@ sub cmd_test_webservices { sub cmd_test_selenium { my $conf = require $ENV{BZ_QA_CONF_FILE}; + $ENV{HTTP_BACKEND} = 'simple'; check_data_dir(); copy_qa_extension(); @@ -207,8 +207,6 @@ sub cmd_test_bmo { sub run_prove { my (%param) = @_; - check_httpd_env(); - my $prove_cmd = $param{prove_cmd}; my $prove_dir = $param{prove_dir}; assert_httpd()->then(sub { @@ -268,16 +266,6 @@ sub check_env { die 'Missing required environmental variables: ', join(', ', @missing_env), "\n"; } } -sub check_httpd_env { - check_env(qw( - HTTPD_StartServers - HTTPD_MinSpareServers - HTTPD_MaxSpareServers - HTTPD_ServerLimit - HTTPD_MaxClients - HTTPD_MaxRequestsPerChild - )) -} sub fix_path { $ENV{PATH} = "/app/local/bin:$ENV{PATH}"; diff --git a/scripts/undo.pl b/scripts/undo.pl old mode 100644 new mode 100755 diff --git a/ses/index.cgi b/ses/index.cgi deleted file mode 100755 index e36956b1d..000000000 --- a/ses/index.cgi +++ /dev/null @@ -1,214 +0,0 @@ -#!/usr/bin/perl -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. - -use 5.10.1; -use strict; -use warnings; - -use lib qw(.. ../lib ../local/lib/perl5); - -use Bugzilla (); -use Bugzilla::Constants qw(ERROR_MODE_DIE); -use Bugzilla::Logging; -use Bugzilla::Mailer qw(MessageToMTA); -use Bugzilla::User (); -use Bugzilla::Util qw(html_quote remote_ip); -use JSON::MaybeXS qw(decode_json); -use LWP::UserAgent (); -use Try::Tiny qw(catch try); - -Bugzilla->error_mode(ERROR_MODE_DIE); -try { - main(); -} -catch { - FATAL("Fatal error: $_"); - respond( 500 => 'Internal Server Error' ); -}; - -sub main { - my $message = decode_json_wrapper( Bugzilla->cgi->param('POSTDATA') ) // return; - my $message_type = $ENV{HTTP_X_AMZ_SNS_MESSAGE_TYPE} // '(missing)'; - - if ( $message_type eq 'SubscriptionConfirmation' ) { - confirm_subscription($message); - } - - elsif ( $message_type eq 'Notification' ) { - my $notification = decode_json_wrapper( $message->{Message} ) // return; - unless ( - # https://docs.aws.amazon.com/ses/latest/DeveloperGuide/event-publishing-retrieving-sns-contents.html - handle_notification( $notification, 'eventType' ) - - # https://docs.aws.amazon.com/ses/latest/DeveloperGuide/notification-contents.html - || handle_notification( $notification, 'notificationType' ) - ) - { - WARN('Failed to find notification type'); - respond( 400 => 'Bad Request' ); - } - } - - else { - WARN("Unsupported message-type: $message_type"); - respond( 200 => 'OK' ); - } -} - -sub confirm_subscription { - my ($message) = @_; - - my $subscribe_url = $message->{SubscribeURL}; - if ( !$subscribe_url ) { - WARN('Bad SubscriptionConfirmation request: missing SubscribeURL'); - respond( 400 => 'Bad Request' ); - return; - } - - my $ua = ua(); - my $res = $ua->get( $message->{SubscribeURL} ); - if ( !$res->is_success ) { - WARN( 'Bad response from SubscribeURL: ' . $res->status_line ); - respond( 400 => 'Bad Request' ); - return; - } - - respond( 200 => 'OK' ); -} - -sub handle_notification { - my ( $notification, $type_field ) = @_; - - if ( !exists $notification->{$type_field} ) { - return 0; - } - my $type = $notification->{$type_field}; - - if ( $type eq 'Bounce' ) { - process_bounce($notification); - } - elsif ( $type eq 'Complaint' ) { - process_complaint($notification); - } - else { - WARN("Unsupported notification-type: $type"); - respond( 200 => 'OK' ); - } - return 1; -} - -sub process_bounce { - my ($notification) = @_; - - # disable each account that is bouncing - foreach my $recipient ( @{ $notification->{bounce}->{bouncedRecipients} } ) { - my $address = $recipient->{emailAddress}; - my $reason = sprintf '(%s) %s', $recipient->{action} // 'error', $recipient->{diagnosticCode} // 'unknown'; - - my $user = Bugzilla::User->new( { name => $address, cache => 1 } ); - if ($user) { - - # never auto-disable admin accounts - if ( $user->in_group('admin') ) { - Bugzilla->audit("ignoring bounce for admin <$address>: $reason"); - } - - else { - my $template = Bugzilla->template_inner(); - my $vars = { - mta => $notification->{bounce}->{reportingMTA} // 'unknown', - reason => $reason, - }; - my $disable_text; - $template->process( 'admin/users/bounce-disabled.txt.tmpl', $vars, \$disable_text ) - || die $template->error(); - - $user->set_disabledtext($disable_text); - $user->set_disable_mail(1); - $user->update(); - Bugzilla->audit( "bounce for <$address> disabled userid-" . $user->id . ": $reason" ); - } - } - - else { - Bugzilla->audit("bounce for <$address> has no user: $reason"); - } - } - - respond( 200 => 'OK' ); -} - -sub process_complaint { - - # email notification to bugzilla admin - my ($notification) = @_; - my $template = Bugzilla->template_inner(); - my $json = JSON::MaybeXS->new( - pretty => 1, - utf8 => 1, - canonical => 1, - ); - - foreach my $recipient ( @{ $notification->{complaint}->{complainedRecipients} } ) { - my $reason = $notification->{complaint}->{complaintFeedbackType} // 'unknown'; - my $address = $recipient->{emailAddress}; - Bugzilla->audit("complaint for <$address> for '$reason'"); - my $vars = { - email => $address, - user => Bugzilla::User->new( { name => $address, cache => 1 } ), - reason => $reason, - notification => $json->encode($notification), - }; - my $message; - $template->process( 'email/ses-complaint.txt.tmpl', $vars, \$message ) - || die $template->error(); - MessageToMTA($message); - } - - respond( 200 => 'OK' ); -} - -sub respond { - my ( $code, $message ) = @_; - print Bugzilla->cgi->header( -status => "$code $message" ); - - # apache will generate non-200 response pages for us - say html_quote($message) if $code == 200; -} - -sub decode_json_wrapper { - my ($json) = @_; - my $result; - if ( !defined $json ) { - WARN( 'Missing JSON from ' . remote_ip() ); - respond( 400 => 'Bad Request' ); - return undef; - } - my $ok = try { - $result = decode_json($json); - } - catch { - WARN( 'Malformed JSON from ' . remote_ip() ); - respond( 400 => 'Bad Request' ); - return undef; - }; - return $ok ? $result : undef; -} - -sub ua { - my $ua = LWP::UserAgent->new(); - $ua->timeout(10); - $ua->protocols_allowed( [ 'http', 'https' ] ); - if ( my $proxy_url = Bugzilla->params->{'proxy_url'} ) { - $ua->proxy( [ 'http', 'https' ], $proxy_url ); - } - else { - $ua->env_proxy; - } - return $ua; -} diff --git a/t/001compile.t b/t/001compile.t index e81162056..3d8d82bf4 100644 --- a/t/001compile.t +++ b/t/001compile.t @@ -35,7 +35,7 @@ sub compile_file { my ($file) = @_; # Don't allow CPAN.pm to modify the global @INC, which the version - # shipped with Perl 5.8.8 does. (It gets loaded by + # shipped with Perl 5.8.8 does. (It gets loaded by # Bugzilla::Install::CPAN.) local @INC = @INC; @@ -43,10 +43,6 @@ sub compile_file { skip "$file: extensions not tested", 1; return; } - if ($file =~ /ModPerl/) { - skip "$file: ModPerl stuff not tested", 1; - return; - } if ($file =~ s/\.pm$//) { $file =~ s{/}{::}g; @@ -82,7 +78,7 @@ my $file_features = map_files_to_features(); # Test the scripts by compiling them foreach my $file (@testitems) { # These were already compiled, above. - next if ($file eq 'Bugzilla.pm' + next if ($file eq 'Bugzilla.pm' or $file eq 'Bugzilla/Constants.pm' or $file eq 'Bugzilla/Install/Requirements.pm'); SKIP: { @@ -94,10 +90,10 @@ foreach my $file (@testitems) { skip "$file: $feature not enabled", 1; } - # Check that we have a DBI module to support the DB, if this + # Check that we have a DBI module to support the DB, if this # is a database module (but not Schema) if ($file =~ m{Bugzilla/DB/([^/]+)\.pm$} - and $file ne "Bugzilla/DB/Schema.pm") + and $file ne "Bugzilla/DB/Schema.pm") { my $module = lc($1); Bugzilla->feature($module) or skip "$file: Driver for $module not installed", 1; @@ -105,4 +101,4 @@ foreach my $file (@testitems) { compile_file($file); } -} +} diff --git a/t/002goodperl.t b/t/002goodperl.t index 5f201160b..80d7cf2b9 100644 --- a/t/002goodperl.t +++ b/t/002goodperl.t @@ -83,6 +83,7 @@ foreach my $file (@testitems) { my $found_use_strict = 0; my $found_use_warnings = 0; my $found_modern_perl = 0; + my $found_mojo = 0; $file =~ s/\s.*$//; # nuke everything after the first space (#comment) next if (!$file); # skip null entries @@ -92,13 +93,17 @@ foreach my $file (@testitems) { } while (my $file_line = ) { $found_modern_perl = 1 if $file_line =~ m/^use\s*(?:Moo|Role::Tiny)/; + $found_mojo = 1 if $file_line =~ m/^use\s(?:Mojo(?:licious::Lite|::Base)\b)/; $found_use_perl = 1 if $file_line =~ m/^\s*use 5.10.1/; $found_use_strict = 1 if $file_line =~ m/^\s*use strict/; $found_use_warnings = 1 if $file_line =~ m/^\s*use warnings/; - if ($found_modern_perl) { + if ($found_modern_perl || $found_mojo) { $found_use_strict = 1; $found_use_warnings = 1; } + if ($found_mojo) { + $found_use_perl = 1; + } last if ($found_use_perl && $found_use_strict && $found_use_warnings); } close (FILE); diff --git a/template/en/default/admin/params/editparams.html.tmpl b/template/en/default/admin/params/editparams.html.tmpl index 838bff7ef..6e8bc2257 100644 --- a/template/en/default/admin/params/editparams.html.tmpl +++ b/template/en/default/admin/params/editparams.html.tmpl @@ -21,7 +21,6 @@ [%# INTERFACE: # panels: array of hashes representing the panels available. # param_changed: array of parameters which have been changed. - # shutdown_is_active: boolean; is true when 'shutdownhtml' has been turned on. #%] [% PROCESS global/variables.none.tmpl %] diff --git a/template/en/default/admin/params/general.html.tmpl b/template/en/default/admin/params/general.html.tmpl index f1b86e19f..05e8f09a2 100644 --- a/template/en/default/admin/params/general.html.tmpl +++ b/template/en/default/admin/params/general.html.tmpl @@ -48,11 +48,6 @@ _ " "on", you must re-run checksetup.pl immediately" _ " afterward.

", - shutdownhtml => - "If this field is non-empty, then $terms.Bugzilla will be completely" - _ " disabled and this text will be displayed instead of all the" - _ " $terms.Bugzilla pages.", - announcehtml => "If this field is non-empty, then $terms.Bugzilla will" _ " display whatever is in this field at the top of every" diff --git a/template/en/default/global/field-descs.none.tmpl b/template/en/default/global/field-descs.none.tmpl index 3e0a528dc..cb6240c29 100644 --- a/template/en/default/global/field-descs.none.tmpl +++ b/template/en/default/global/field-descs.none.tmpl @@ -162,13 +162,11 @@ if ( $stash->get("in_template_var") ) { # database. If you want to override this for your language # or your installation, just use a hook. %] my $bug_fields = $stash->get("bug_fields"); - unless ( Bugzilla->params->{shutdownhtml} ) { - foreach my $bz_field ( values %$bug_fields ) { - $vars->{field_descs}{$bz_field->name} //= $bz_field->description; - } - - $context->process("bug/field-help.none.tmpl"); + foreach my $bz_field ( values %$bug_fields ) { + $vars->{field_descs}{$bz_field->name} //= $bz_field->description; } + + $context->process("bug/field-help.none.tmpl"); } [% END %] diff --git a/template/en/default/global/header.html.tmpl b/template/en/default/global/header.html.tmpl index bd9ec8bcb..9db9a1404 100644 --- a/template/en/default/global/header.html.tmpl +++ b/template/en/default/global/header.html.tmpl @@ -97,6 +97,8 @@ [% IF Param('utf8') %] [% END %] + [% USE Bugzilla %] + [% IF Bugzilla.cgi.should_block_referrer %] diff --git a/template/en/default/global/messages.html.tmpl b/template/en/default/global/messages.html.tmpl index 591617f3e..278b0f161 100644 --- a/template/en/default/global/messages.html.tmpl +++ b/template/en/default/global/messages.html.tmpl @@ -140,7 +140,7 @@ [% ELSIF message_tag == "buglist_updated_named_query" %] [% title = "Search updated" %] - Your search named [% queryname FILTER html %] has been updated. @@ -298,22 +298,22 @@ [% ELSIF message_tag == "email_change_canceled_reinstated" %] [% title = "Cancel Request to Change Email Address" %] The request to change the email address for the - account [%+ old_email FILTER html %] to + account [%+ old_email FILTER html %] to [%+ new_email FILTER html %] has been canceled. Your old account settings have been reinstated. [% ELSIF message_tag == "extension_created" %] An extension named [% name FILTER html %] has been created - in [% path FILTER html %]. Make sure you change "YOUR NAME" and + in [% path FILTER html %]. Make sure you change "YOUR NAME" and "YOUR EMAIL ADDRESS" in the code to your name and your email address. [% ELSIF message_tag == "field_value_created" %] [% title = "New Field Value Created" %] - The value [% value.name FILTER html %] has been added as a + The value [% value.name FILTER html %] has been added as a valid choice for the [% field.description FILTER html %] ([% field.name FILTER html %]) field. [% IF field.name == "bug_status" %] - You should now visit the status workflow + You should now visit the status workflow page to include your new [% terms.bug %] status. [% END %] @@ -331,7 +331,7 @@ ([% field.name FILTER html %]) field has been changed:
    [% IF changes.value %] -
  • Field value updated to +
  • Field value updated to [% changes.value.1 FILTER html %]. [% IF value.is_default %] (Note that this value is the default for this field. All @@ -340,16 +340,16 @@
  • [% END %] [% IF changes.sortkey %] -
  • Sortkey updated to +
  • Sortkey updated to [% changes.sortkey.1 FILTER html %].
  • [% END %] [% IF changes.visibility_value_id %] [% IF value.visibility_value.defined %] -
  • It only appears when +
  • It only appears when [%+ value.field.value_field.description FILTER html %] is set to '[%+ value.visibility_value.name FILTER html %]'.
  • [% ELSE %] -
  • It now always appears, no matter what +
  • It now always appears, no matter what [%+ value.field.value_field.description FILTER html %] is set to.
  • [% END %] @@ -535,7 +535,7 @@ Reading users... [% ELSIF message_tag == "migrate_translating_bugs" %] - Converting [% terms.bug %] values to be appropriate for + Converting [% terms.bug %] values to be appropriate for [%+ terms.Bugzilla %]... [% ELSIF message_tag == "migrate_user_created" %] @@ -594,12 +594,6 @@ No changes made. [% END %] - [% IF shutdown_is_active == 1 %] -
    - [% terms.Bugzilla %] has now been shut down. To re-enable the system, - clear the shutdownhtml field. - [% END%] - [% ELSIF message_tag == "password_change_canceled" %] [% title = "Cancel Request to Change Password" %] Your request has been canceled. @@ -731,7 +725,7 @@ [% ELSIF message_tag == "install_fk_invalid" %] ERROR: There are invalid values for the [% column FILTER html %] column in the [% table FILTER html %] - table. (These values do not exist in the [% foreign_table FILTER html %] table, in the + table. (These values do not exist in the [% foreign_table FILTER html %] table, in the [%+ foreign_column FILTER html %] column.) Before continuing with checksetup, you will need to fix these values, @@ -789,8 +783,8 @@ to be editable by both you and the web server must be world writable, and other files (including the localconfig file which stores your database password) must be world readable. This means that _anyone_ who can obtain - local access to this machine can do whatever they want to your - [%+ terms.Bugzilla %] installation, and is probably also able to run + local access to this machine can do whatever they want to your + [%+ terms.Bugzilla %] installation, and is probably also able to run arbitrary Perl code as the user that the web server runs as. You really, really, really need to change this setting. @@ -798,10 +792,10 @@ [% ELSIF message_tag == "install_webservergroup_not_in" %] Warning: you have entered a value for the "webservergroup" parameter in - localconfig, but you are not either a) running this script as [% constants.ROOT_USER FILTER html %]; - or b) a member of this group. This can cause permissions problems and + localconfig, but you are not either a) running this script as [% constants.ROOT_USER FILTER html %]; + or b) a member of this group. This can cause permissions problems and decreased security. If you experience problems running [% terms.Bugzilla %] - scripts, log in as [% constants.ROOT_USER FILTER html %] and re-run this script, become a + scripts, log in as [% constants.ROOT_USER FILTER html %] and re-run this script, become a member of the group, or remove the value of the "webservergroup" parameter. [% ELSIF message_tag == "install_webservergroup_windows" %] @@ -836,7 +830,7 @@ products you can choose from. [% ELSIF message_tag == "remaining_time_zeroed" %] - The [% field_descs.remaining_time FILTER html %] field has been + The [% field_descs.remaining_time FILTER html %] field has been set to zero automatically as part of closing this [% terms.bug %] or moving it from one closed state to another. @@ -856,21 +850,21 @@ [% ELSIF message_tag == "sudo_started" %] [% title = "Sudo session started" %] - The sudo session has been started. For the next 6 hours, or until you - end the session, everything you do you do as the user you are + The sudo session has been started. For the next 6 hours, or until you + end the session, everything you do you do as the user you are impersonating ([% target FILTER html %]). - + [% ELSIF message_tag == "sudo_ended" %] [% title = "Sudo session complete" %] - The sudo session has been ended. From this point forward, everything you + The sudo session has been ended. From this point forward, everything you do you do as yourself. [% ELSIF message_tag == "series_created" %] [% title = "Series Created" %] The series [% series.category FILTER html %] / - [%+ series.subcategory FILTER html %] / + [%+ series.subcategory FILTER html %] / [%+ series.name FILTER html %] - has been created. Note that you may need to wait up to + has been created. Note that you may need to wait up to [%+ series.frequency * 2 %] days before there will be enough data for a chart of this series to be produced. @@ -881,14 +875,6 @@ [%+ series.name FILTER html %] has been deleted. - [% ELSIF message_tag == "shutdown" %] - [% title = "$terms.Bugzilla is Down" %] - [% Param("shutdownhtml") %] - [% IF userid %] -

    For security reasons, you have been logged out automatically. - The cookie that was remembering your login is now gone. - [% END %] - [% ELSIF message_tag == "tag_updated" %] [% title = "Tag Updated" %] The '[% tag FILTER html %]' @@ -908,7 +894,7 @@ Some flags could not be set. Please check your changes. [% ELSIF message_tag == "user_match_failed" %] - You entered a username that did not match any known + You entered a username that did not match any known [% terms.Bugzilla %] users, so we have instead left the [% match_field FILTER html %] field blank. diff --git a/vagrant_support/apache.yml b/vagrant_support/apache.yml index 5031187e3..2d65965ab 100644 --- a/vagrant_support/apache.yml +++ b/vagrant_support/apache.yml @@ -6,8 +6,8 @@ mode: 0644 when: LAZY == 0 -- name: enable httpd - service: name=httpd enabled=yes +- name: disable httpd + service: name=httpd enabled=no when: LAZY == 0 - name: ensure bugzilla.log has right permissions @@ -16,5 +16,5 @@ - name: ensure bugzilla-json.log has right permissions file: path=/vagrant/logs/bugzilla-json.log state=touch owner=vagrant group=apache mode=0660 -- name: restart httpd - service: name=httpd state=restarted \ No newline at end of file +- name: stop httpd + service: name=httpd state=stopped diff --git a/vagrant_support/hypnotoad b/vagrant_support/hypnotoad new file mode 100755 index 000000000..5e8dc910e --- /dev/null +++ b/vagrant_support/hypnotoad @@ -0,0 +1,122 @@ +#!/bin/bash +# +# bugzilla-push This starts, stops, and restarts the Bugzilla push +# daemon, which manages syncronising bugzilla.mozilla.org and +# third party bugzilla installs. +# +# chkconfig: 345 85 15 +# description: Bugzilla push daemon +# +### BEGIN INIT INFO +# Provides: bugzilla-push +# Required-Start: $local_fs $syslog MTA mysqld +# Required-Stop: $local_fs $syslog MTA mysqld +# Default-Start: 3 5 +# Default-Stop: 0 1 2 6 +# Short-Description: Start and stop the Bugzilla push daemon. +# Description: The Bugzilla push daemon (bugzilla-pushd.pl) +# +# +# +### END INIT INFO + +NAME=`basename $0` + +################# +# Configuration # +################# + +# This should be the path to your Bugzilla +BUGZILLA=/vagrant +# Who owns the Bugzilla directory and files? +USER=vagrant + +PERL5LIB="$BUGZILLA:$BUGZILLA/lib:$BUGZILLA/local/lib/perl5" +export PERL5LIB +export MOJO_MODE + +if [[ x$PORT == x ]]; then + PORT=80 + export PORT +fi + +# If you want to pass any options to the daemon (like -d for debugging) +# specify it here. +OPTIONS="" + +# You can also override the configuration by creating a +# /etc/sysconfig/bugzilla-queue file so that you don't +# have to edit this script. +if [ -r /etc/sysconfig/$NAME ]; then + . /etc/sysconfig/$NAME +fi + +########## +# Script # +########## + +RETVAL=0 +BIN="$BUGZILLA/local/bin/hypnotoad" +PIDFILE="$BUGZILLA/hypnotoad.pid" + +# Source function library. +. /etc/rc.d/init.d/functions + +usage () +{ + echo "Usage: service $NAME {start|stop|status|restart|condrestart}" + RETVAL=1 +} + + +start () +{ + if [[ -f $PIDFILE ]]; then + checkpid "$(cat $PIDFILE)" && return 0 + fi + echo -n "Starting $NAME: " + daemon --pidfile=$PIDFILE --user=$USER "cd $BUGZILLA && $BIN bugzilla.pl >/dev/null" + ret=$? + [ $ret -eq "0" ] && touch /var/lock/subsys/$NAME + echo + return $ret +} + +stop () +{ + cd $BUGZILLA && $BIN bugzilla.pl --stop + rm -f /var/lock/subsys/$NAME +} + +restart () +{ + stop + start +} + +condrestart () +{ + [ -e /var/lock/subsys/$NAME ] && restart || return 0 +} + +status () +{ + if [[ -f $PIDFILE ]] && checkpid "$(cat $PIDFILE)"; then + echo hypnotoad is amazing + return 0 + else + echo hypnotoad is not running + return 1 + fi +} + +case "$1" in + start) start; RETVAL=$? ;; + stop) stop; RETVAL=$? ;; + status) status; RETVAL=$?;; + restart) restart; RETVAL=$? ;; + condrestart) condrestart; RETVAL=$? ;; + *) usage ; RETVAL=2 ;; +esac + +exit $RETVAL diff --git a/vagrant_support/hypnotoad.yml b/vagrant_support/hypnotoad.yml new file mode 100644 index 000000000..a66f9ba04 --- /dev/null +++ b/vagrant_support/hypnotoad.yml @@ -0,0 +1,27 @@ +- name: grant perl ability to listen to privledged ports + command: setcap 'cap_net_bind_service=+ep' /usr/bin/perl + +- name: hypnotoad daemon sysconfig + copy: + dest: /etc/sysconfig/hypnotoad + mode: 0644 + content: | + BUGZILLA=/vagrant + USER=vagrant + MOJO_MODE=development + +- name: hypnotoad daemon init file + template: + dest: /etc/init.d/hypnotoad + src: hypnotoad + owner: root + group: root + mode: 0755 + +- name: enable hypnotoad + service: name=hypnotoad enabled=yes + when: LAZY == 0 + +- name: restart hypnotoad + service: name=hypnotoad state=restarted + diff --git a/vagrant_support/playbook.yml b/vagrant_support/playbook.yml index c067eab05..3dd320f0b 100644 --- a/vagrant_support/playbook.yml +++ b/vagrant_support/playbook.yml @@ -170,9 +170,6 @@ dest: /usr/local/bin/cpanm mode: '0755' - - name: install more recent Apache2::SizeLimit - cpanm: name=Apache2::SizeLimit executable=/usr/local/bin/cpanm - - name: 'check /opt/bmo (failure is ok)' shell: test -d /opt/bmo && test -f /opt/bmo/local/lib/perl5/Plack.pm register: opt_bmo @@ -222,5 +219,8 @@ - include_tasks: apache.yml vars: LAZY: 0 + - include_tasks: hypnotoad.yml + vars: + LAZY: 0 - import_tasks: devtools.yml diff --git a/vagrant_support/update.yml b/vagrant_support/update.yml index c53a0554e..533da5b5e 100644 --- a/vagrant_support/update.yml +++ b/vagrant_support/update.yml @@ -20,4 +20,7 @@ LAZY: 1 - include_tasks: apache.yml vars: - LAZY: 1 \ No newline at end of file + LAZY: 1 + - include_tasks: hypnotoad.yml + vars: + LAZY: 1 -- cgit v1.2.3-24-g4f1b From 144234c61f9d0ea53069c9182f95a0fed7526206 Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Wed, 19 Sep 2018 14:16:49 -0400 Subject: no bug - bump Alien::libcmark_gfm --- .circleci/config.yml | 2 +- Bugzilla/Markdown/GFM.pm | 4 ++-- Dockerfile | 3 +-- Makefile.PL | 2 +- t/markdown.t | 6 ++++-- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f64524c80..224abfa22 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,7 +7,7 @@ version: 2 defaults: bmo_slim_image: &bmo_slim_image - image: mozillabteam/bmo-slim:20180809.1 + image: mozillabteam/bmo-slim:20180918.1 user: app mysql_image: &mysql_image diff --git a/Bugzilla/Markdown/GFM.pm b/Bugzilla/Markdown/GFM.pm index f3f24fc6a..367dc7a53 100644 --- a/Bugzilla/Markdown/GFM.pm +++ b/Bugzilla/Markdown/GFM.pm @@ -69,9 +69,9 @@ $FFI->attach(cmark_markdown_to_html => ['opaque', 'int', 'markdown_options_t'] = ); # This has to happen after something from the main lib is loaded -$FFI->attach('core_extensions_ensure_registered' => [] => 'void'); +$FFI->attach('cmark_gfm_core_extensions_ensure_registered' => [] => 'void'); -core_extensions_ensure_registered(); +cmark_gfm_core_extensions_ensure_registered(); Bugzilla::Markdown::GFM::SyntaxExtension->SETUP($FFI); Bugzilla::Markdown::GFM::SyntaxExtensionList->SETUP($FFI); diff --git a/Dockerfile b/Dockerfile index fd02f222d..ecea67ed7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM mozillabteam/bmo-slim:20180809.1 +FROM mozillabteam/bmo-slim:20180918.1 ARG CI ARG CIRCLE_SHA1 @@ -22,7 +22,6 @@ RUN mv /opt/bmo/local /app && \ chown -R app:app /app && \ perl -I/app -I/app/local/lib/perl5 -c -E 'use Bugzilla; BEGIN { Bugzilla->extensions }' && \ perl -c /app/scripts/entrypoint.pl && \ - setcap 'cap_net_bind_service=+ep' /usr/sbin/httpd && \ setcap 'cap_net_bind_service=+ep' /usr/bin/perl USER app diff --git a/Makefile.PL b/Makefile.PL index 7e3095867..495c07c58 100755 --- a/Makefile.PL +++ b/Makefile.PL @@ -33,7 +33,7 @@ BEGIN { # PREREQ_PM my %requires = ( - 'Alien::libcmark_gfm' => 0, + 'Alien::libcmark_gfm' => '3', 'Algorithm::BloomFilter' => '0.02', 'CGI' => '4.31', 'CGI::Compile' => 0, diff --git a/t/markdown.t b/t/markdown.t index 0344706c9..83e0dc051 100644 --- a/t/markdown.t +++ b/t/markdown.t @@ -9,7 +9,7 @@ use strict; use warnings; use lib qw( . lib local/lib/perl5 ); use Bugzilla; -use Test::More; +use Test2::V0; my $parser = Bugzilla->markdown_parser; @@ -61,7 +61,9 @@ my $table_html = <<'HTML'; val1 val2 - + + + HTML is( -- cgit v1.2.3-24-g4f1b From 7bf615124cd3380a6f7ea0fc553ba598a3ef05a5 Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Thu, 20 Sep 2018 09:54:28 -0400 Subject: Bug 1492850 - Remove places where headers are printed There's one place where some (unused?) debug code prints out headers without using Bugzilla->cgi, and testagent.cgi which does the same. The first thing is removed and testagent.cgi is also removed, with its route handled by a simple route. --- Bugzilla/Quantum.pm | 4 ++++ extensions/BMO/lib/Reports/ReleaseTracking.pm | 10 ---------- testagent.cgi | 26 -------------------------- 3 files changed, 4 insertions(+), 36 deletions(-) delete mode 100755 testagent.cgi diff --git a/Bugzilla/Quantum.pm b/Bugzilla/Quantum.pm index d2352f6d8..8af06c477 100644 --- a/Bugzilla/Quantum.pm +++ b/Bugzilla/Quantum.pm @@ -63,6 +63,10 @@ sub startup { $r->any('/')->to('CGI#index_cgi'); $r->any('/bug/')->to('CGI#show_bug_cgi'); $r->any('/')->to('CGI#show_bug_cgi'); + $r->get('/testagent.cgi' => sub { + my $c = shift; + $c->render(text => "OK Mojolicious"); + }); $r->any('/rest')->to('CGI#rest_cgi'); $r->any('/rest.cgi/*PATH_INFO')->to( 'CGI#rest_cgi' => { PATH_INFO => '' } ); diff --git a/extensions/BMO/lib/Reports/ReleaseTracking.pm b/extensions/BMO/lib/Reports/ReleaseTracking.pm index 42ef24b47..9fba1e14b 100644 --- a/extensions/BMO/lib/Reports/ReleaseTracking.pm +++ b/extensions/BMO/lib/Reports/ReleaseTracking.pm @@ -421,16 +421,6 @@ sub report { $query .= join("\nAND ", @where); - if ($input->{debug}) { - print "Content-Type: text/plain\n\n"; - $query =~ s/\?/\000/g; - foreach my $param (@params) { - $query =~ s/\000/'$param'/; - } - print "$query\n"; - exit; - } - my $bugs = $dbh->selectcol_arrayref($query, undef, @params); push @$bugs, 0 unless @$bugs; diff --git a/testagent.cgi b/testagent.cgi deleted file mode 100755 index a1c200e10..000000000 --- a/testagent.cgi +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/perl -T -# -*- Mode: perl; indent-tabs-mode: nil -*- -# -# The contents of this file are subject to the Mozilla Public -# License Version 1.1 (the "License"); you may not use this file -# except in compliance with the License. You may obtain a copy of -# the License at http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS -# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or -# implied. See the License for the specific language governing -# rights and limitations under the License. -# -# Contributor(s): Joel Peshkin - -# This script is used by servertest.pl to confirm that cgi scripts -# are being run instead of shown. This script does not rely on database access -# or correct params. - -use 5.10.1; -use strict; -use warnings; -print "content-type:text/plain\n\n"; -print "OK " . ($::ENV{MOD_PERL} || "mod_cgi") . "\n"; -exit; - -- cgit v1.2.3-24-g4f1b From 5e4f8b175bfc4a3283455d29315033d60632e9f7 Mon Sep 17 00:00:00 2001 From: Kohei Yoshino Date: Thu, 20 Sep 2018 09:55:15 -0400 Subject: Bug 1468489 - Documentation points to https://landfill.bugzilla.org that is no longer maintained and will be shut down --- docs/en/rst/about/index.rst | 15 ++++++++++----- docs/en/rst/integrating/templates.rst | 10 +++++----- docs/en/rst/using/creating-an-account.rst | 4 ++-- docs/en/rst/using/filing.rst | 13 ++++++------- docs/en/rst/using/finding.rst | 4 ++-- extensions/GuidedBugEntry/web/js/products.js | 4 ++-- qa/t/webservice_bug_get.t | 4 ++-- qa/t/webservice_bug_update.t | 4 ++-- qa/t/webservice_bug_update_see_also.t | 2 +- 9 files changed, 32 insertions(+), 28 deletions(-) diff --git a/docs/en/rst/about/index.rst b/docs/en/rst/about/index.rst index cdcea4d15..82e6830a0 100644 --- a/docs/en/rst/about/index.rst +++ b/docs/en/rst/about/index.rst @@ -17,11 +17,16 @@ The most current version of this document can always be found on the Evaluating Bugzilla ################### -If you want to try out Bugzilla to see if it meets your needs, you can do so -on `Landfill `_, our test -server. The `Bugzilla FAQ `_ may also -be helpful, as it answers a number of questions people sometimes have about -whether Bugzilla is for them. +If you want to try out Bugzilla to see if it meets your needs, you can do so on +`Mozilla’s Bugzilla (BMO) test server `_, +though it comes with various Mozilla-specific customizations. The easiest way to +explore the admin tools and more is `running a minimum local copy of BMO +`_ using Vagrant or +Docker. We are not offering any online vanilla test environment at this time. + +The `Bugzilla FAQ `_ may also be helpful, +as it answers a number of questions people sometimes have about whether Bugzilla +is for them. .. _getting-help: diff --git a/docs/en/rst/integrating/templates.rst b/docs/en/rst/integrating/templates.rst index b477a9553..e631b7364 100644 --- a/docs/en/rst/integrating/templates.rst +++ b/docs/en/rst/integrating/templates.rst @@ -49,7 +49,7 @@ modifications, and the method you plan to use to upgrade Bugzilla. #. You can use the hooks built into many of the templates to add or modify the UI from an :ref:`extension `. Hooks generally don't go away - and have a stable interface. + and have a stable interface. The third method is the best if there are hooks in the appropriate places and the change you want to do is possible using hooks. It's not very easy @@ -89,7 +89,7 @@ can't if you fork the file into the :file:`custom` directory. How To Edit Templates ===================== -.. note:: If you are making template changes that you intend on submitting +.. note:: If you are making template changes that you intend on submitting back for inclusion in standard Bugzilla, you should read the relevant sections of the `Developers' Guide `_. @@ -111,7 +111,7 @@ to cross-site scripting attacks. You should run :command:`./checksetup.pl` after editing any templates. Failure to do so may mean either that your changes are not picked up, or that the -permissions on the edited files are wrong so the webserver can't read them. +permissions on the edited files are wrong so the webserver can't read them. .. _template-formats: @@ -232,11 +232,11 @@ customizing for your installation. and have their values appear formatted in the initial comment. An example of this is the `guided bug submission form - `_. + `_. The code for this comes with the Bugzilla distribution as an example for you to copy. It can be found in the files :file:`create-guided.html.tmpl` and :file:`comment-guided.html.tmpl`. - + A hidden field that indicates the format should be added inside the form in order to make the template functional. Its value should be the suffix of the template filename. For example, if the file diff --git a/docs/en/rst/using/creating-an-account.rst b/docs/en/rst/using/creating-an-account.rst index 0ea42ee5d..3d70096f5 100644 --- a/docs/en/rst/using/creating-an-account.rst +++ b/docs/en/rst/using/creating-an-account.rst @@ -6,8 +6,8 @@ Creating an Account If you want to use a particular installation of Bugzilla, first you need to create an account. Ask the administrator responsible for your installation for the URL you should use to access it. If you're test-driving Bugzilla, -you can use one of the installations on -`Landfill `_. +you can use one of the installations on `Mozilla’s Bugzilla (BMO) test server +`_. The process of creating an account is similar to many other websites. diff --git a/docs/en/rst/using/filing.rst b/docs/en/rst/using/filing.rst index 788cebbd5..a76e3faed 100644 --- a/docs/en/rst/using/filing.rst +++ b/docs/en/rst/using/filing.rst @@ -7,18 +7,17 @@ Reporting a New Bug =================== Years of bug writing experience has been distilled for your -reading pleasure into the -`Bug Writing Guidelines `_. +reading pleasure into the `Bug report writing guidelines +`_. While some of the advice is Mozilla-specific, the basic principles of reporting Reproducible, Specific bugs and isolating the Product you are using, the Version of the Product, the Component which failed, the Hardware Platform, and Operating System you were using at the time of the failure go a long way toward ensuring accurate, responsible fixes for the bug that bit you. -.. note:: If you want to file a test bug to see how Bugzilla works, - you can do it on one of our test installations on - `Landfill `_. Please don't do it on anyone's - production Bugzilla installation. +.. note:: If you want to file a test bug to see how Bugzilla works, you can do + so on `Mozilla’s Bugzilla (BMO) test server `_. + Please don’t do it on any production Bugzilla installation. The procedure for filing a bug is as follows: @@ -46,7 +45,7 @@ The procedure for filing a bug is as follows: developers easier, and the probability that they consider your bug in a reasonable timeframe will be much higher. - .. note:: Try to make sure that everything in the Summary is also in the + .. note:: Try to make sure that everything in the Summary is also in the Description. Summaries are often updated and this will ensure your original information is easily accessible. diff --git a/docs/en/rst/using/finding.rst b/docs/en/rst/using/finding.rst index 44d4003dc..baf2d9076 100644 --- a/docs/en/rst/using/finding.rst +++ b/docs/en/rst/using/finding.rst @@ -43,8 +43,8 @@ Advanced Search =============== The Advanced Search page is used to produce a list of all bugs fitting -exact criteria. `You can play with it on -Landfill `_. +exact criteria. You can play with it on `Mozilla’s Bugzilla (BMO) test server +`_. Advanced Search has controls for selecting different possible values for all of the fields in a bug, as described above. For some diff --git a/extensions/GuidedBugEntry/web/js/products.js b/extensions/GuidedBugEntry/web/js/products.js index 8ef1ea0c0..65dc04393 100644 --- a/extensions/GuidedBugEntry/web/js/products.js +++ b/extensions/GuidedBugEntry/web/js/products.js @@ -122,12 +122,12 @@ var products = { "Bugzilla": { support: - 'Please use Bugzilla Landfill to file "test bugs".' + 'Please use our test server to file "test bugs".' }, "bugzilla.mozilla.org": { related: [ "Bugzilla" ], support: - 'Please use Bugzilla Landfill to file "test bugs".' + 'Please use our test server to file "test bugs".' } }; diff --git a/qa/t/webservice_bug_get.t b/qa/t/webservice_bug_get.t index 91de410c1..cad5c3893 100644 --- a/qa/t/webservice_bug_get.t +++ b/qa/t/webservice_bug_get.t @@ -38,7 +38,7 @@ $xmlrpc->bz_call_success('Bug.update', { is_creator_accessible => 0, keywords => { set => ['test-keyword-1', 'test-keyword-2'] }, see_also => { add => ["${base_url}show_bug.cgi?id=$public_id", - "https://landfill.bugzilla.org/show_bug.cgi?id=123456"] }, + "https://bugzilla-dev.allizom.org/show_bug.cgi?id=123456"] }, cf_qa_status => ['in progress', 'verified'], cf_single_select => 'two', }, 'Update the private bug'); @@ -53,7 +53,7 @@ $private_bug->{is_creator_accessible} = 0; $private_bug->{is_cc_accessible} = 1; $private_bug->{keywords} = ['test-keyword-1', 'test-keyword-2']; $private_bug->{see_also} = ["${base_url}show_bug.cgi?id=$public_id", - "https://landfill.bugzilla.org/show_bug.cgi?id=123456"]; + "https://bugzilla-dev.allizom.org/show_bug.cgi?id=123456"]; $private_bug->{cf_qa_status} = ['in progress', 'verified']; $private_bug->{cf_single_select} = 'two'; diff --git a/qa/t/webservice_bug_update.t b/qa/t/webservice_bug_update.t index 4e3b9985b..95af809e0 100644 --- a/qa/t/webservice_bug_update.t +++ b/qa/t/webservice_bug_update.t @@ -222,7 +222,7 @@ sub valid_values { { value => { remove => [$bug_uri . $second_id] }, removed => $bug_uri . $second_id, added => '', test => 'remove local bug URI' }, - { value => { remove => ['http://landfill.bugzilla.org/bugzilla-tip/show_bug.cgi?id=1'] }, + { value => { remove => ['https://bugzilla-dev.allizom.org/show_bug.cgi?id=1'] }, no_changes => 1, test => 'removing non-existent URI works' }, { value => { add => [''] }, @@ -524,7 +524,7 @@ sub invalid_values { { value => { add => [random_string(20)] }, error => 'It does not seem like bug number nor an alias to a bug.', test => 'random string fails in see_also' }, - { value => { add => ['http://landfill.bugzilla.org/'] }, + { value => { add => ['https://bugzilla-dev.allizom.org/'] }, error => 'See Also URLs should point to one of', test => 'no show_bug.cgi in see_also URI' }, ], diff --git a/qa/t/webservice_bug_update_see_also.t b/qa/t/webservice_bug_update_see_also.t index 460cb13e1..0af0e78d5 100644 --- a/qa/t/webservice_bug_update_see_also.t +++ b/qa/t/webservice_bug_update_see_also.t @@ -17,7 +17,7 @@ use QA::Tests qw(PRIVATE_BUG_USER STANDARD_BUG_TESTS); use Test::More tests => 117; my ($config, $xmlrpc, $jsonrpc, $jsonrpc_get) = get_rpc_clients(); -my $bug_url = 'http://landfill.bugzilla.org/bugzilla-tip/show_bug.cgi?id=100'; +my $bug_url = 'https://bugzilla-dev.allizom.org/show_bug.cgi?id=100'; # update_see_also doesn't support logged-out users. my @tests = grep { $_->{user} } @{ STANDARD_BUG_TESTS() }; -- cgit v1.2.3-24-g4f1b From 33b395b5761d143de7c06fc159cdb491a1f1654b Mon Sep 17 00:00:00 2001 From: dklawren Date: Thu, 20 Sep 2018 16:05:56 -0400 Subject: Bug 1492511 - Code to updating subscriber values for current private bugs is throwing errors in the phabbugz log --- extensions/PhabBugz/lib/Feed.pm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/extensions/PhabBugz/lib/Feed.pm b/extensions/PhabBugz/lib/Feed.pm index 2dbeef6a7..b338e9a72 100644 --- a/extensions/PhabBugz/lib/Feed.pm +++ b/extensions/PhabBugz/lib/Feed.pm @@ -636,6 +636,11 @@ sub process_new_user { foreach my $attachment (@attachments) { my ($revision_id) = ($attachment->filename =~ PHAB_ATTACHMENT_PATTERN); + if (!$revision_id) { + WARN("Skipping " . $attachment->filename . " on bug $bug_id. Filename should be fixed."); + next; + } + INFO("Processing revision D$revision_id"); my $revision = Bugzilla::Extension::PhabBugz::Revision->new_from_query( -- cgit v1.2.3-24-g4f1b From ab743ac5f848e2168110b1d9720c893a89c04e7e Mon Sep 17 00:00:00 2001 From: Kohei Yoshino Date: Sun, 23 Sep 2018 18:18:19 -0400 Subject: Bug 1470536 - Add new GeckoView product to easy product selector on Browse and Enter Bug pages --- .../BMO/template/en/default/global/choose-product.html.tmpl | 12 ++++++++---- extensions/BMO/web/styles/choose_product.css | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/extensions/BMO/template/en/default/global/choose-product.html.tmpl b/extensions/BMO/template/en/default/global/choose-product.html.tmpl index dfa9b5af4..163c71f5c 100644 --- a/extensions/BMO/template/en/default/global/choose-product.html.tmpl +++ b/extensions/BMO/template/en/default/global/choose-product.html.tmpl @@ -102,10 +102,18 @@ name="Toolkit" icon="component.png" %] + [% INCLUDE easyproduct + name="GeckoView" + icon="firefox_android.png" + %] [% INCLUDE easyproduct name="Mozilla Localizations" icon="localization.png" %] + [% INCLUDE easyproduct + name="Data Platform and Tools" + icon="telemetry.png" + %] [% INCLUDE easyproduct name="Thunderbird" icon="thunderbird.png" @@ -114,10 +122,6 @@ name="SeaMonkey" icon="seamonkey.png" %] - [% INCLUDE easyproduct - name="Data Platform and Tools" - icon="telemetry.png" - %]

    do("INSERT INTO flaginclusions(component_id, type_id, product_id) undef, $tgtprodid, $tgtprodid, $srcprodid); +# Tracking type flags +$dbh->do("INSERT INTO tracking_flags_visibility (tracking_flag_id, product_id, component_id) + SELECT tf1.tracking_flag_id, ?, tf1.component_id FROM tracking_flags_visibility tf1 + LEFT JOIN tracking_flags_visibility tf2 + ON tf1.tracking_flag_id = tf2.tracking_flag_id + AND tf2.product_id = ? + WHERE tf1.product_id = ? + AND tf2.tracking_flag_id IS NULL", + undef, + $tgtprodid, $tgtprodid, $srcprodid); + # It's complex to determine which items now need to be flushed from memcached. # As this is expected to be a rare event, we just flush the entire cache. Bugzilla->memcached->clear_all(); -- cgit v1.2.3-24-g4f1b From 8047b0395f5175da934f3aaf09b15a15d83da755 Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Mon, 24 Sep 2018 15:54:18 -0400 Subject: Bug 1492926 - Handle DBIx::Connectors more appropriately This is a bigger change than I anticipated, because the way we cached DBIx::Connector objects was bad. Now we cache the Bugzilla::DB instances in connect_main() and connect_shadow(). This is for maintaining a 1:1 mapping of Bugzilla::DB objects and DBIx::Connector objects. This is important because we want be able to inspect Bugzilla::DB->bz_in_transactions() from the 'connected' event. Note that we weaken the lexical variable $self in _build_connector() because it is referenced by the callback passed to DBI. Without this there would be a memory cycle and stuff would never be freed. (tested my understanding in this gist: https://gist.github.com/dylanwh/646574a027f7b7d92cb7586676da7468) --- Bugzilla/DB.pm | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/Bugzilla/DB.pm b/Bugzilla/DB.pm index cd5954219..142c241bf 100644 --- a/Bugzilla/DB.pm +++ b/Bugzilla/DB.pm @@ -12,13 +12,13 @@ use Moo; use DBI; use DBIx::Connector; -our %Connector; has 'connector' => ( is => 'lazy', handles => [ qw( dbh ) ], ); +use Bugzilla::Logging; use Bugzilla::Constants; use Bugzilla::Install::Requirements; use Bugzilla::Install::Util qw(install_string); @@ -31,7 +31,9 @@ use Bugzilla::DB::Schema; use Bugzilla::Version; use List::Util qw(max); +use Scalar::Util qw(weaken); use Storable qw(dclone); +use English qw(-no_match_vars); has [qw(dsn user pass attrs)] => ( is => 'ro', @@ -44,7 +46,7 @@ has [qw(dsn user pass attrs)] => ( # time we need a DBI handle to ensure the connection is alive. { my @DBI_METHODS = qw( - begin_work column_info commit disconnect do errstr get_info last_insert_id ping prepare + begin_work column_info commit do errstr get_info last_insert_id ping prepare primary_key quote_identifier rollback selectall_arrayref selectall_hashref selectcol_arrayref selectrow_array selectrow_arrayref selectrow_hashref table_info ); @@ -130,6 +132,12 @@ sub quote { ##################################################################### sub connect_shadow { + state $shadow_dbh; + if ($shadow_dbh && $shadow_dbh->bz_in_transaction) { + FATAL("Somehow in a transaction at connection time"); + $shadow_dbh->bz_rollback_transaction(); + } + return $shadow_dbh if $shadow_dbh; my $params = Bugzilla->params; die "Tried to connect to non-existent shadowdb" unless Bugzilla->get_param_with_override('shadowdb'); @@ -147,13 +155,16 @@ sub connect_shadow { $connect_params->{db_user} = Bugzilla->localconfig->{'shadowdb_user'}; $connect_params->{db_pass} = Bugzilla->localconfig->{'shadowdb_pass'}; } - - return _connect($connect_params); + return $shadow_dbh = _connect($connect_params); } sub connect_main { - my $lc = Bugzilla->localconfig; - return _connect(Bugzilla->localconfig); + state $main_dbh = _connect(Bugzilla->localconfig); + if ($main_dbh->bz_in_transaction) { + FATAL("Somehow in a transaction at connection time"); + $main_dbh->bz_rollback_transaction(); + } + return $main_dbh; } sub _connect { @@ -293,7 +304,6 @@ sub _get_no_db_connection { my $dbh; my %connect_params = %{ Bugzilla->localconfig }; $connect_params{db_name} = ''; - local %Connector = (); my $conn_success = eval { $dbh = _connect(\%connect_params); }; @@ -1304,13 +1314,18 @@ sub _build_connector { } } my $class = ref $self; - if ($class->can('on_dbi_connected')) { - $attributes->{Callbacks} = { - connected => sub { $class->on_dbi_connected(@_); return }, - } - } + weaken($self); + $attributes->{Callbacks} = { + connected => sub { + my ($dbh, $dsn) = @_; + INFO("$PROGRAM_NAME connected mysql $dsn"); + ThrowCodeError('not_in_transaction') if $self && $self->bz_in_transaction; + $class->on_dbi_connected(@_) if $class->can('on_dbi_connected'); + return + }, + }; - return $Connector{"$user.$dsn"} //= DBIx::Connector->new($dsn, $user, $pass, $attributes); + return DBIx::Connector->new($dsn, $user, $pass, $attributes); } ##################################################################### -- cgit v1.2.3-24-g4f1b From b3ad67dc224403c463fbea5ddb5f01dcacf3cb02 Mon Sep 17 00:00:00 2001 From: Kohei Yoshino Date: Mon, 24 Sep 2018 15:55:28 -0400 Subject: Bug 1473417 - Show often/recently used products/components on New Bug page --- .../en/default/global/choose-product.html.tmpl | 12 ++- extensions/BMO/web/js/new-bug-frequent-comp.js | 111 +++++++++++++++++++++ extensions/BMO/web/styles/choose_product.css | 19 ++++ template/en/default/global/header.html.tmpl | 4 + 4 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 extensions/BMO/web/js/new-bug-frequent-comp.js diff --git a/extensions/BMO/template/en/default/global/choose-product.html.tmpl b/extensions/BMO/template/en/default/global/choose-product.html.tmpl index 163c71f5c..74c9f7b0d 100644 --- a/extensions/BMO/template/en/default/global/choose-product.html.tmpl +++ b/extensions/BMO/template/en/default/global/choose-product.html.tmpl @@ -36,7 +36,10 @@ "extensions/BMO/web/styles/choose_product.css", "extensions/ProdCompSearch/web/styles/prod_comp_search.css", ]; - javascript_urls = [ "extensions/ProdCompSearch/web/js/prod_comp_search.js" ]; + javascript_urls = [ + "extensions/BMO/web/js/new-bug-frequent-comp.js", + "extensions/ProdCompSearch/web/js/prod_comp_search.js", + ]; cgi = Bugzilla.cgi; classification = cgi.param('classification'); @@ -68,6 +71,13 @@ %] +[% IF NOT is_describe %] + +[% END %] +

    or choose from the following selections

    diff --git a/extensions/BMO/web/js/new-bug-frequent-comp.js b/extensions/BMO/web/js/new-bug-frequent-comp.js new file mode 100644 index 000000000..4ca1fcf89 --- /dev/null +++ b/extensions/BMO/web/js/new-bug-frequent-comp.js @@ -0,0 +1,111 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This Source Code Form is "Incompatible With Secondary Licenses", as + * defined by the Mozilla Public License, v. 2.0. */ + +/** + * Reference or define the Bugzilla app namespace. + * @namespace + */ +var Bugzilla = Bugzilla || {}; // eslint-disable-line no-var + +/** + * Show the current user's most-used components on the New Bug page. + */ +Bugzilla.NewBugFrequentComp = class NewBugFrequentComp { + /** + * Initialize a new NewBugFrequentComp instance. + */ + constructor() { + this.$container = document.querySelector('#frequent-components'); + + if (this.$container && BUGZILLA.user.login) { + this.init(); + } + } + + /** + * Initialize the UI. + */ + async init() { + this.$results = this.$container.querySelector('.results'); + this.$message = this.$results.appendChild(document.createElement('p')); + this.$message.textContent = 'Loading...'; + this.$results.setAttribute('aria-busy', 'true'); + this.$container.hidden = false; + + try { + const results = await this.fetch(); + + this.$message.remove(); + this.$results.insertAdjacentHTML('beforeend', + `
    ` + ); + } catch (error) { + this.$message.textContent = error.message || 'Your frequent components could not be retrieved.'; + } + + this.$results.removeAttribute('aria-busy'); + } + + /** + * Retrieve frequently used components. + * @param {Number} [max=10] Maximum number of results. + * @returns {Promise} Results or error. + */ + async fetch(max = 10) { + const params = new URLSearchParams({ + email1: BUGZILLA.user.login, + emailreporter1: '1', + emailtype1: 'exact', + chfield: '[Bug creation]', + chfieldfrom: '-1y', + chfieldto: 'Now', + include_fields: 'product,component', + }); + + return new Promise((resolve, reject) => { + bugzilla_ajax({ + url: `/rest/bug?${params.toString()}` + }, response => { + if (!response.bugs) { + reject(new Error('Your frequent components could not be retrieved.')); + + return; + } + + if (!response.bugs.length) { + reject(new Error(('Your frequent components could not be found.'))); + + return; + } + + const results = []; + + for (const { product, component } of response.bugs) { + const index = results.findIndex(result => product === result.product && component === result.component); + + if (index > -1) { + results[index].count++; + } else { + results.push({ product, component, count: 1 }); + } + } + + // Sort in descending order + results.sort((a, b) => (a.count < b.count ? 1 : a.count > b.count ? -1 : 0)); + + resolve(results.slice(0, max)); + }, () => { + reject(new Error('Your frequent components could not be retrieved.')); + }); + }); + } +}; + +window.addEventListener('DOMContentLoaded', () => new Bugzilla.NewBugFrequentComp(), { once: true }); diff --git a/extensions/BMO/web/styles/choose_product.css b/extensions/BMO/web/styles/choose_product.css index 5fd8c49e7..a4ecf749f 100644 --- a/extensions/BMO/web/styles/choose_product.css +++ b/extensions/BMO/web/styles/choose_product.css @@ -16,6 +16,22 @@ text-align: left; } +#frequent-components ul { + display: flex; + flex-wrap: wrap; + justify-content: center; + margin: 8px auto; + padding: 0; + list-style-type: none; + white-space: nowrap; +} + +#frequent-components li { + flex: none; + margin: 8px 16px; + padding: 0; +} + #product-list { margin: 32px 0; } @@ -55,6 +71,7 @@ } @media screen and (min-width: 1024px) { + #frequent-components ul, #product-list .tile { width: 960px; } @@ -65,6 +82,7 @@ } @media screen and (min-width: 768px) and (max-width: 1023px) { + #frequent-components ul, #product-list .tile { width: 720px; } @@ -75,6 +93,7 @@ } @media screen and (max-width: 767px) { + #frequent-components ul, #product-list .tile { width: auto; max-width: 480px; diff --git a/template/en/default/global/header.html.tmpl b/template/en/default/global/header.html.tmpl index 9db9a1404..4c6069c74 100644 --- a/template/en/default/global/header.html.tmpl +++ b/template/en/default/global/header.html.tmpl @@ -107,6 +107,10 @@ [% END %] [%- js_BUGZILLA = { + user => { + # TODO: Move all properties form bug_modal/header.html.tmpl + login => user.login, + }, param => { maxattachmentsize => Param('maxattachmentsize'), maxusermatches => Param('maxusermatches'), -- cgit v1.2.3-24-g4f1b From 65aed407b07a5e8ef19ced43f958c14c046e6ed8 Mon Sep 17 00:00:00 2001 From: Kohei Yoshino Date: Sun, 23 Sep 2018 11:43:53 -0400 Subject: Bug 1490595 - Bugzilla update check should use https --- Bugzilla/Constants.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Bugzilla/Constants.pm b/Bugzilla/Constants.pm index 185a30390..525705ce1 100644 --- a/Bugzilla/Constants.pm +++ b/Bugzilla/Constants.pm @@ -210,7 +210,7 @@ sub BUGZILLA_VERSION { } # Location of the remote and local XML files to track new releases. -use constant REMOTE_FILE => 'http://updates.bugzilla.org/bugzilla-update.xml'; +use constant REMOTE_FILE => 'https://updates.bugzilla.org/bugzilla-update.xml'; use constant LOCAL_FILE => 'bugzilla-update.xml'; # Relative to datadir. # These are unique values that are unlikely to match a string or a number, -- cgit v1.2.3-24-g4f1b From f1d49e2e52ed971e50f7cfe37d70c38a46c5ff06 Mon Sep 17 00:00:00 2001 From: Kohei Yoshino Date: Mon, 24 Sep 2018 17:23:17 -0400 Subject: Bug 1491850 - restoreSavedBugComment takes a really long time (100ms) --- extensions/BugModal/web/bug_modal.js | 1 - 1 file changed, 1 deletion(-) diff --git a/extensions/BugModal/web/bug_modal.js b/extensions/BugModal/web/bug_modal.js index 55451d3bf..c15174c8b 100644 --- a/extensions/BugModal/web/bug_modal.js +++ b/extensions/BugModal/web/bug_modal.js @@ -1323,7 +1323,6 @@ $(function() { // finally switch to edit mode if we navigate back to a page that was editing $(window).on('pageshow', restoreEditMode); $(window).on('pageshow', restoreSavedBugComment); - $(window).on('focus', restoreSavedBugComment); restoreEditMode(); restoreSavedBugComment(); }); -- cgit v1.2.3-24-g4f1b From f0dd10b8e87731d7746f085b35e2074aa6e2ef73 Mon Sep 17 00:00:00 2001 From: Kohei Yoshino Date: Mon, 24 Sep 2018 17:23:53 -0400 Subject: Bug 1493295 - Add short URL to each bug --- .../template/en/default/hook/global/header-additional_header.html.tmpl | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/BMO/template/en/default/hook/global/header-additional_header.html.tmpl b/extensions/BMO/template/en/default/hook/global/header-additional_header.html.tmpl index 0566f48b3..6300b0d15 100644 --- a/extensions/BMO/template/en/default/hook/global/header-additional_header.html.tmpl +++ b/extensions/BMO/template/en/default/hook/global/header-additional_header.html.tmpl @@ -22,6 +22,7 @@ [% IF bug %] + [% END %] [%# *** Bug List Navigation *** %] -- cgit v1.2.3-24-g4f1b From 137a983d511bfed23a914946dde4b7cf1108a726 Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Mon, 24 Sep 2018 17:40:36 -0400 Subject: bump version to 20180925.1 --- Bugzilla.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Bugzilla.pm b/Bugzilla.pm index bb036fb06..bd79bbc8e 100644 --- a/Bugzilla.pm +++ b/Bugzilla.pm @@ -13,7 +13,7 @@ use warnings; use Bugzilla::Logging; -our $VERSION = '20180918.1'; +our $VERSION = '20180925.1'; use Bugzilla::Auth; use Bugzilla::Auth::Persist::Cookie; -- cgit v1.2.3-24-g4f1b From deec4ab75d6478f51d6c72a230343ab955116a6b Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Tue, 25 Sep 2018 17:30:28 -0400 Subject: Bug 1494065 - Add a basic test using Test::Mojo --- Bugzilla/Quantum.pm | 2 +- Bugzilla/Test/Util.pm | 20 +++++++++++++- Makefile.PL | 11 ++++---- t/mojo-example.t | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 102 insertions(+), 7 deletions(-) create mode 100644 t/mojo-example.t diff --git a/Bugzilla/Quantum.pm b/Bugzilla/Quantum.pm index 8af06c477..c64e57e13 100644 --- a/Bugzilla/Quantum.pm +++ b/Bugzilla/Quantum.pm @@ -34,7 +34,7 @@ sub startup { DEBUG('Starting up'); $self->plugin('Bugzilla::Quantum::Plugin::Glue'); - $self->plugin('Bugzilla::Quantum::Plugin::Hostage'); + $self->plugin('Bugzilla::Quantum::Plugin::Hostage') unless $ENV{BUGZILLA_DISABLE_HOSTAGE}; $self->plugin('Bugzilla::Quantum::Plugin::BlockIP'); $self->plugin('Bugzilla::Quantum::Plugin::BasicAuth'); diff --git a/Bugzilla/Test/Util.pm b/Bugzilla/Test/Util.pm index 02c842658..8124c25ee 100644 --- a/Bugzilla/Test/Util.pm +++ b/Bugzilla/Test/Util.pm @@ -12,9 +12,10 @@ use strict; use warnings; use base qw(Exporter); -our @EXPORT = qw(create_user); +our @EXPORT = qw(create_user issue_api_key); use Bugzilla::User; +use Bugzilla::User::APIKey; sub create_user { my ($login, $password, %extra) = @_; @@ -29,4 +30,21 @@ sub create_user { }); } +sub issue_api_key { + my ($login, $given_api_key) = @_; + my $user = Bugzilla::User->check({ name => $login }); + + my $params = { + user_id => $user->id, + description => 'Bugzilla::Test::Util::issue_api_key', + api_key => $given_api_key, + }; + + if ($given_api_key) { + return Bugzilla::User::APIKey->create_special($params); + } else { + return Bugzilla::User::APIKey->create($params); + } +} + 1; diff --git a/Makefile.PL b/Makefile.PL index 495c07c58..4aa352468 100755 --- a/Makefile.PL +++ b/Makefile.PL @@ -92,13 +92,14 @@ my %requires = ( my %build_requires = ( 'ExtUtils::MakeMaker' => '7.22', ); my %test_requires = ( - 'Test::More' => 0, + 'Capture::Tiny' => 0, + 'DBD::SQLite' => '1.29', + 'Perl::Critic::Freenode' => 0, 'Pod::Coverage' => 0, - 'Test::WWW::Selenium' => 0, - 'Test::Selenium::Firefox' => 0, + 'Test::More' => 0, 'Test::Perl::Critic::Progressive' => 0, - 'Perl::Critic::Freenode' => 0, - 'Capture::Tiny' => 0, + 'Test::Selenium::Firefox' => 0, + 'Test::WWW::Selenium' => 0, ); my %recommends = ( Safe => '2.30',); diff --git a/t/mojo-example.t b/t/mojo-example.t new file mode 100644 index 000000000..8ed4835da --- /dev/null +++ b/t/mojo-example.t @@ -0,0 +1,76 @@ +#!/usr/bin/perl +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. +use strict; +use warnings; +use 5.10.1; +use lib qw( . lib local/lib/perl5 ); + +BEGIN { + $ENV{LOG4PERL_CONFIG_FILE} = 'log4perl-t.conf'; + # There's a plugin called Hostage that makes the application require specific Host: headers. + # we disable that for these tests. + $ENV{BUGZILLA_DISABLE_HOSTAGE} = 1; +} + +# this provides a default urlbase. +# Most localconfig options the other Bugzilla::Test::Mock* modules take care for us. +use Bugzilla::Test::MockLocalconfig ( urlbase => 'http://bmo-web.vm' ); + +# This configures an in-memory sqlite database. +use Bugzilla::Test::MockDB; + +# This redirects reads and writes from the config file (data/params) +use Bugzilla::Test::MockParams ( + phabricator_enabled => 1, + announcehtml => '
    Mojo::Test is awesome
    ', +); + +# Util provides a few functions more making mock data in the DB. +use Bugzilla::Test::Util qw(create_user issue_api_key); + +use Test2::V0; +use Test2::Tools::Mock; +use Test::Mojo; + +my $api_user = create_user('api@mozilla.org', '*'); +my $api_key = issue_api_key('api@mozilla.org')->api_key; + +# Mojo::Test loads the application and provides methods for +# testing requests without having to run a server. +my $t = Test::Mojo->new('Bugzilla::Quantum'); + +# we ensure this file exists so the /__lbhearbeat__ test passes. +$t->app->home->child('__lbheartbeat__')->spurt('httpd OK'); + +# Method chaining is used extensively. +$t->get_ok('/__lbheartbeat__')->status_is(200)->content_is('httpd OK'); + +# this won't work until we can mock memcached. +# $t->get_ok('/__heartbeat__')->status_is(200); + +# we can use json_is or json_like to check APIs. +# The first pair to json_like is a JSON pointer (RFC 6901) +$t->get_ok('/bzapi/configuration')->status_is(200)->json_like( '/announcement' => qr/Mojo::Test is awesome/ ); + +# for web requests, you use text_like (or text_is) with CSS selectors. +$t->get_ok('/')->status_is(200)->text_like( '#announcement' => qr/Mojo::Test is awesome/ ); + +# Chaining is not magical, you can break up longer lines +# by calling methods on $t, as below. +$t->get_ok('/rest/whoami' => { 'X-Bugzilla-API-Key' => $api_key }); +$t->status_is(200); +$t->json_is('/name' => $api_user->login); +$t->json_is('/id' => $api_user->id); + +# Each time you call $t->get_ok, post_ok, etc the previous request is cleared. +$t->get_ok('/rest/whoami'); +$t->status_is(200); +$t->json_is('/name' => ''); +$t->json_is('/id' => 0); + +done_testing; -- cgit v1.2.3-24-g4f1b From 291bb971595489070ed8db3da1785f5a3977a15a Mon Sep 17 00:00:00 2001 From: Kohei Yoshino Date: Wed, 26 Sep 2018 11:29:19 -0400 Subject: Bug 1489718 - Insert form widgets for approval flag requests instead of free-form comment text --- .../default/hook/flag/type_comment-form.html.tmpl | 202 +++++++++++++++++++++ .../en/default/flag/type_comment.html.tmpl | 40 ++-- extensions/FlagTypeComment/web/js/ftc.js | 189 +++++++++++++++++++ extensions/FlagTypeComment/web/styles/ftc.css | 58 ++++++ skins/standard/attachment.css | 4 - skins/standard/global.css | 61 +++++++ template/en/default/flag/list.html.tmpl | 1 + 7 files changed, 522 insertions(+), 33 deletions(-) create mode 100644 extensions/BMO/template/en/default/hook/flag/type_comment-form.html.tmpl create mode 100644 extensions/FlagTypeComment/web/js/ftc.js create mode 100644 extensions/FlagTypeComment/web/styles/ftc.css diff --git a/extensions/BMO/template/en/default/hook/flag/type_comment-form.html.tmpl b/extensions/BMO/template/en/default/hook/flag/type_comment-form.html.tmpl new file mode 100644 index 000000000..7963af850 --- /dev/null +++ b/extensions/BMO/template/en/default/hook/flag/type_comment-form.html.tmpl @@ -0,0 +1,202 @@ +[%# This Source Code Form is subject to the terms of the Mozilla Public + # License, v. 2.0. If a copy of the MPL was not distributed with this + # file, You can obtain one at http://mozilla.org/MPL/2.0/. + # + # This Source Code Form is "Incompatible With Secondary Licenses", as + # defined by the Mozilla Public License, v. 2.0. + #%] + + + + + + + + diff --git a/extensions/FlagTypeComment/template/en/default/flag/type_comment.html.tmpl b/extensions/FlagTypeComment/template/en/default/flag/type_comment.html.tmpl index 88d9d4dd7..56e03040a 100644 --- a/extensions/FlagTypeComment/template/en/default/flag/type_comment.html.tmpl +++ b/extensions/FlagTypeComment/template/en/default/flag/type_comment.html.tmpl @@ -20,35 +20,17 @@ # byron jones #%] -[% IF ftc_flags.keys.size %] - - function ftc_on_change(ev) { - var id = ev.target.id.split('-')[1]; - var state = ev.target.value; - var commentEl = document.getElementById('comment'); - if (!commentEl) return; - [% FOREACH type_id = ftc_flags.keys %] - [% FOREACH state = ftc_states %] - if ([% type_id FILTER none %] == id && '[% state FILTER js %]' == state) { - var text = '[% ftc_flags.$type_id.$state FILTER js %]'; - var value = commentEl.value; - if (value == text) { - return; - } else if (value == '') { - commentEl.value = text; - } else { - commentEl.value = text + "\n\n" + value; - } - } - [% END %] +[% IF ftc_flags.keys.size %] + [%# plaintext templates from database %] + [% FOREACH type_id = ftc_flags.keys %] + [% FOREACH state = ftc_states %] + [% END %] - } - + [% END %] + [%# HTML form templates from extensions %] + [% Hook.process("form") %] [% END %] diff --git a/extensions/FlagTypeComment/web/js/ftc.js b/extensions/FlagTypeComment/web/js/ftc.js new file mode 100644 index 000000000..2682721bc --- /dev/null +++ b/extensions/FlagTypeComment/web/js/ftc.js @@ -0,0 +1,189 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This Source Code Form is "Incompatible With Secondary Licenses", as + * defined by the Mozilla Public License, v. 2.0. */ + +/** + * Reference or define the Bugzilla app namespace. + * @namespace + */ +var Bugzilla = Bugzilla || {}; // eslint-disable-line no-var + +/** + * Provide the ability to insert a comment template when a patch's approval flag is selected. + */ +Bugzilla.FlagTypeComment = class FlagTypeComment { + /** + * Initialize a new FlagTypeComment instance. + */ + constructor() { + this.templates = [...document.querySelectorAll('template.approval-request')]; + this.$flags = document.querySelector('#flags'); + this.$comment = document.querySelector('#comment'); + + if (this.$flags && this.$comment) { + this.selects = [...this.$flags.querySelectorAll('.flag_select')]; + this.selects.forEach($select => $select.addEventListener('change', () => this.flag_onselect($select))); + this.$comment.form.addEventListener('submit', () => this.form_onsubmit()); + } + } + + /** + * Check if a `
    ` is compatible with the given flag. For example, `approval‑mozilla‑beta` matches + * `
    ` while `approval‑mozilla‑esr60` + * matches `
    `. + * @param {String} name Flag name, such as `approval‑mozilla‑beta`. + * @param {(HTMLFieldSetElement|HTMLTemplateElement)} $element `
    ` or `