diff options
Diffstat (limited to 'Bugzilla/Quantum')
-rw-r--r-- | Bugzilla/Quantum/CGI.pm | 27 | ||||
-rw-r--r-- | Bugzilla/Quantum/Home.pm | 3 | ||||
-rw-r--r-- | Bugzilla/Quantum/Plugin/BasicAuth.pm | 40 | ||||
-rw-r--r-- | Bugzilla/Quantum/Plugin/Glue.pm | 3 | ||||
-rw-r--r-- | Bugzilla/Quantum/Plugin/Hostage.pm | 107 | ||||
-rw-r--r-- | Bugzilla/Quantum/SES.pm | 364 |
6 files changed, 267 insertions, 277 deletions
diff --git a/Bugzilla/Quantum/CGI.pm b/Bugzilla/Quantum/CGI.pm index 79fbcfde6..5a654111e 100644 --- a/Bugzilla/Quantum/CGI.pm +++ b/Bugzilla/Quantum/CGI.pm @@ -54,8 +54,7 @@ sub load_one { open STDIN, '<', $stdin->path or die "STDIN @{[$stdin->path]}: $!" if -s $stdin->path; - tie *STDOUT, 'Bugzilla::Quantum::Stdout', - controller => $c; ## no critic (tie) + tie *STDOUT, 'Bugzilla::Quantum::Stdout', controller => $c; ## no critic (tie) # the finally block calls cleanup. $c->stash->{cleanup_guard}->dismiss; @@ -129,19 +128,17 @@ sub _ENV { 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 + 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__, ); } diff --git a/Bugzilla/Quantum/Home.pm b/Bugzilla/Quantum/Home.pm index 48d5e47bd..6a3021f64 100644 --- a/Bugzilla/Quantum/Home.pm +++ b/Bugzilla/Quantum/Home.pm @@ -16,8 +16,7 @@ sub index { my ($c) = @_; $c->bugzilla->login(LOGIN_REQUIRED) or return; try { - ThrowUserError('invalid_username', {login => 'batman'}) - if $c->param('error'); + ThrowUserError('invalid_username', {login => 'batman'}) if $c->param('error'); $c->render(handler => 'bugzilla', template => 'index'); } catch { diff --git a/Bugzilla/Quantum/Plugin/BasicAuth.pm b/Bugzilla/Quantum/Plugin/BasicAuth.pm index e17273404..e0d4e8ecc 100644 --- a/Bugzilla/Quantum/Plugin/BasicAuth.pm +++ b/Bugzilla/Quantum/Plugin/BasicAuth.pm @@ -12,29 +12,29 @@ use Bugzilla::Logging; use Carp; sub register { - my ( $self, $app, $conf ) = @_; + 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 =~ /^([^:]+):(.*)/; + $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 ($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; - } + 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; - } - ); + return 1; + } + ); } -1;
\ No newline at end of file +1; diff --git a/Bugzilla/Quantum/Plugin/Glue.pm b/Bugzilla/Quantum/Plugin/Glue.pm index f04b9c025..de016356c 100644 --- a/Bugzilla/Quantum/Plugin/Glue.pm +++ b/Bugzilla/Quantum/Plugin/Glue.pm @@ -157,8 +157,7 @@ sub register { ); $app->log(MojoX::Log::Log4perl::Tiny->new( - logger => Log::Log4perl->get_logger(ref $app) - )); + logger => Log::Log4perl->get_logger(ref $app))); } 1; diff --git a/Bugzilla/Quantum/Plugin/Hostage.pm b/Bugzilla/Quantum/Plugin/Hostage.pm index 418b09a0c..df3e40ec1 100644 --- a/Bugzilla/Quantum/Plugin/Hostage.pm +++ b/Bugzilla/Quantum/Plugin/Hostage.pm @@ -3,78 +3,77 @@ use 5.10.1; use Mojo::Base 'Mojolicious::Plugin'; sub _attachment_root { - my ($base) = @_; - return undef unless $base; - return $base =~ m{^https?://(?:bug)?\%bugid\%\.([a-zA-Z\.-]+)} - ? $1 - : undef; + 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; + 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 ) = @_; + my ($self, $app, $conf) = @_; - $app->hook(before_routes => \&_before_routes); + $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 ($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; + my $stash = $c->stash; + my $req = $c->req; + my $url = $req->url->to_abs; - return if $stash->{'mojo.static'}; + return if $stash->{'mojo.static'}; - my $hostname = $url->host; - return if $hostname eq $urlbase_host; + my $hostname = $url->host; + return if $hostname eq $urlbase_host; - my $path = $url->path; - return if $path eq '/__lbheartbeat__'; + my $path = $url->path; + return if $path eq '/__lbheartbeat__'; - if ($attachment_base && $hostname eq $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); - $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); - $c->redirect_to($new_uri); - return; + if ($attachment_base && $hostname eq $attachment_root) { + $c->redirect_to($urlbase); + return; + } + elsif ($attachment_base && $hostname =~ $attachment_host_regex) { + if ($path =~ m{^/attachment\.cgi}s) { + return; } else { - $c->redirect_to($urlbase); - return; + my $new_uri = $url->clone; + $new_uri->scheme($urlbase_uri->scheme); + $new_uri->host($urlbase_host); + $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); + $c->redirect_to($new_uri); + return; + } + else { + $c->redirect_to($urlbase); + return; + } } 1; diff --git a/Bugzilla/Quantum/SES.pm b/Bugzilla/Quantum/SES.pm index 03916075d..9d2149978 100644 --- a/Bugzilla/Quantum/SES.pm +++ b/Bugzilla/Quantum/SES.pm @@ -1,4 +1,5 @@ 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/. @@ -22,233 +23,228 @@ use Types::Standard qw( :all ); use Type::Utils; use Type::Params qw( compile ); -my $Invocant = class_type { class => __PACKAGE__ }; +my $Invocant = class_type {class => __PACKAGE__}; sub main { - my ($self) = @_; - try { - $self->_main; - } - catch { - FATAL("Error in SES Handler: ", $_); - $self->_respond( 400 => 'Bad Request' ); - }; + 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); + 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'); } + } - 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' ); - } + 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' ); + 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, +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; + 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 $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, - ], + 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"); - } + 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' ); + $self->_respond(200 => 'OK'); } -my $ComplainedRecipients = ArrayRef[Dict[ emailAddress => Str, slurpy Any ]]; -my $ComplaintNotification = Dict[ - complaint => Dict [ - complainedRecipients => $ComplainedRecipients, - complaintFeedbackType => Str, - slurpy Any, - ], +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); - } + 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' ); + $self->_respond(200 => 'OK'); } sub _respond { - my ( $self, $code, $message ) = @_; - $self->render(text => "$message\n", status => $code); + 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; + 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; + 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; |