From 62412db14081dd66cd5b2701b598b5af9eb31528 Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Tue, 2 Oct 2018 14:22:05 -0400 Subject: add helpers for handling logins and error handling --- Bugzilla.pm | 3 + Bugzilla/Constants.pm | 4 + Bugzilla/Error.pm | 12 ++- Bugzilla/Error/Base.pm | 21 +++++ Bugzilla/Error/Code.pm | 14 +++ Bugzilla/Error/User.pm | 13 +++ Bugzilla/Quantum.pm | 2 + Bugzilla/Quantum/CGI.pm | 3 +- Bugzilla/Quantum/Home.pm | 26 ++++++ Bugzilla/Quantum/Plugin/Glue.pm | 111 ++++++++++++++++++++---- template/en/default/global/code-error.html.tmpl | 14 +-- template/en/default/global/header.html.tmpl | 10 +-- template/en/default/global/user-error.html.tmpl | 14 +-- 13 files changed, 211 insertions(+), 36 deletions(-) create mode 100644 Bugzilla/Error/Base.pm create mode 100644 Bugzilla/Error/Code.pm create mode 100644 Bugzilla/Error/User.pm create mode 100644 Bugzilla/Quantum/Home.pm diff --git a/Bugzilla.pm b/Bugzilla.pm index 05b231dcb..56020b230 100644 --- a/Bugzilla.pm +++ b/Bugzilla.pm @@ -483,6 +483,9 @@ sub usage_mode { elsif ($newval == USAGE_MODE_REST) { $class->error_mode(ERROR_MODE_REST); } + elsif ($newval == USAGE_MODE_MOJO) { + $class->error_mode(ERROR_MODE_MOJO); + } else { ThrowCodeError('usage_mode_invalid', {'invalid_usage_mode', $newval}); diff --git a/Bugzilla/Constants.pm b/Bugzilla/Constants.pm index 525705ce1..d0b74b5e3 100644 --- a/Bugzilla/Constants.pm +++ b/Bugzilla/Constants.pm @@ -131,6 +131,7 @@ use Memoize; USAGE_MODE_JSON USAGE_MODE_TEST USAGE_MODE_REST + USAGE_MODE_MOJO ERROR_MODE_WEBPAGE ERROR_MODE_DIE @@ -138,6 +139,7 @@ use Memoize; ERROR_MODE_JSON_RPC ERROR_MODE_TEST ERROR_MODE_REST + ERROR_MODE_MOJO COLOR_ERROR COLOR_SUCCESS @@ -488,6 +490,7 @@ use constant USAGE_MODE_EMAIL => 3; use constant USAGE_MODE_JSON => 4; use constant USAGE_MODE_TEST => 5; use constant USAGE_MODE_REST => 6; +use constant USAGE_MODE_MOJO => 7; # Error modes. Default set by Bugzilla->usage_mode (so ERROR_MODE_WEBPAGE # usually). Use with Bugzilla->error_mode. @@ -497,6 +500,7 @@ use constant ERROR_MODE_DIE_SOAP_FAULT => 2; use constant ERROR_MODE_JSON_RPC => 3; use constant ERROR_MODE_TEST => 4; use constant ERROR_MODE_REST => 5; +use constant ERROR_MODE_MOJO => 6; # The ANSI colors of messages that command-line scripts use use constant COLOR_ERROR => 'red'; diff --git a/Bugzilla/Error.pm b/Bugzilla/Error.pm index f932294b0..70430d40d 100644 --- a/Bugzilla/Error.pm +++ b/Bugzilla/Error.pm @@ -20,6 +20,8 @@ our @EXPORT = qw( ThrowCodeError ThrowTemplateError ThrowUserError ThrowErrorPag use Bugzilla::Constants; use Bugzilla::WebService::Constants; use Bugzilla::Util; +use Bugzilla::Error::User; +use Bugzilla::Error::Code; use Carp; use Data::Dumper; @@ -40,7 +42,6 @@ sub _in_eval { sub _throw_error { my ($name, $error, $vars, $logfunc) = @_; $vars ||= {}; - $vars->{error} = $error; # Make sure any transaction is rolled back (if supported). # If we are within an eval(), do not roll back transactions as we are @@ -48,6 +49,15 @@ sub _throw_error { my $dbh = eval { Bugzilla->dbh }; $dbh->bz_rollback_transaction() if ($dbh && $dbh->bz_in_transaction() && !_in_eval()); + if (Bugzilla->error_mode == ERROR_MODE_MOJO) { + my ($type) = $name =~ /^global\/(user|code)-error/; + my $class = $type ? 'Bugzilla::Error::' . ucfirst($type) : 'Mojo::Exception'; + my $e = $class->new($error)->trace(2); + $e->vars($vars) if $e->can('vars'); + CORE::die $e->inspect; + } + + $vars->{error} = $error; my $template = Bugzilla->template; my $message; diff --git a/Bugzilla/Error/Base.pm b/Bugzilla/Error/Base.pm new file mode 100644 index 000000000..ea44c272a --- /dev/null +++ b/Bugzilla/Error/Base.pm @@ -0,0 +1,21 @@ +# 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::Error::Base; + +use 5.10.1; +use Mojo::Base 'Mojo::Exception'; + +has 'vars' => sub { {} }; + +has 'template' => sub { + my $self = shift; + my $type = lc( (split(/::/, ref $self))[-1] ); + return "global/$type-error"; +}; + +1; diff --git a/Bugzilla/Error/Code.pm b/Bugzilla/Error/Code.pm new file mode 100644 index 000000000..27393fd17 --- /dev/null +++ b/Bugzilla/Error/Code.pm @@ -0,0 +1,14 @@ +# 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::Error::Code; + +use 5.10.1; +use Mojo::Base 'Bugzilla::Error::Base'; + + +1; diff --git a/Bugzilla/Error/User.pm b/Bugzilla/Error/User.pm new file mode 100644 index 000000000..aa87c9752 --- /dev/null +++ b/Bugzilla/Error/User.pm @@ -0,0 +1,13 @@ +# 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::Error::User; + +use 5.10.1; +use Mojo::Base 'Bugzilla::Error::Base'; + +1; diff --git a/Bugzilla/Quantum.pm b/Bugzilla/Quantum.pm index e9e7713e2..f454a78c5 100644 --- a/Bugzilla/Quantum.pm +++ b/Bugzilla/Quantum.pm @@ -22,6 +22,7 @@ use Bugzilla::Install::Requirements (); use Bugzilla::Logging; use Bugzilla::Quantum::CGI; use Bugzilla::Quantum::SES; +use Bugzilla::Quantum::Home; use Bugzilla::Quantum::Static; use Mojo::Loader qw( find_modules ); use Module::Runtime qw( require_module ); @@ -98,6 +99,7 @@ sub setup_routes { Bugzilla::Quantum::CGI->load_all($r); Bugzilla::Quantum::CGI->load_one( 'bzapi_cgi', 'extensions/BzAPI/bin/rest.cgi' ); + $r->get('/home')->to('Home#index'); $r->any('/')->to('CGI#index_cgi'); $r->any('/bug/')->to('CGI#show_bug_cgi'); $r->any('/')->to('CGI#show_bug_cgi'); diff --git a/Bugzilla/Quantum/CGI.pm b/Bugzilla/Quantum/CGI.pm index 945a87d5b..317c189cc 100644 --- a/Bugzilla/Quantum/CGI.pm +++ b/Bugzilla/Quantum/CGI.pm @@ -19,7 +19,7 @@ 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); +use Bugzilla::Constants qw(bz_locations USAGE_MODE_BROWSER); our $C; my %SEEN; @@ -61,6 +61,7 @@ sub load_one { # the finally block calls cleanup. $c->stash->{cleanup_guard}->dismiss; + Bugzilla->usage_mode(USAGE_MODE_BROWSER); try { Bugzilla->init_page(); $inner->(); diff --git a/Bugzilla/Quantum/Home.pm b/Bugzilla/Quantum/Home.pm new file mode 100644 index 000000000..b3f1ec1d1 --- /dev/null +++ b/Bugzilla/Quantum/Home.pm @@ -0,0 +1,26 @@ +# 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::Home; +use Mojo::Base 'Mojolicious::Controller'; + +use Bugzilla::Error; +use Try::Tiny; +use Bugzilla::Constants; + +sub index { + my ($c) = @_; + $c->bugzilla->login(LOGIN_REQUIRED) or return; + try { + ThrowUserError('invalid_username', { login => 'batman' }) if $c->param('error'); + $c->render(handler => 'bugzilla', template => 'index'); + } catch { + $c->bugzilla->error_page($_); + }; +} + +1; diff --git a/Bugzilla/Quantum/Plugin/Glue.pm b/Bugzilla/Quantum/Plugin/Glue.pm index e9d0056d0..8f4144589 100644 --- a/Bugzilla/Quantum/Plugin/Glue.pm +++ b/Bugzilla/Quantum/Plugin/Glue.pm @@ -13,7 +13,10 @@ use Try::Tiny; use Bugzilla::Constants; use Bugzilla::Logging; use Bugzilla::RNG (); -use JSON::MaybeXS qw(decode_json); +use Bugzilla::Util qw(with_writable_database); +use Mojo::Util qw(secure_compare); +use Mojo::JSON qw(decode_json); +use Scalar::Util qw(blessed); use Scope::Guard; sub register { @@ -44,34 +47,110 @@ sub register { } Log::Log4perl::MDC->put( request_id => $c->req->request_id ); $c->stash->{cleanup_guard} = Scope::Guard->new( \&Bugzilla::cleanup ); + Bugzilla->usage_mode(USAGE_MODE_MOJO); } ); - $app->secrets( [ Bugzilla->localconfig->{side_wide_secret} ] ); $app->renderer->add_handler( 'bugzilla' => sub { my ( $renderer, $c, $output, $options ) = @_; - my $vars = delete $c->stash->{vars}; + my %params; # Helpers - my %helper; - foreach my $method ( grep {m/^\w+\z/} keys %{ $renderer->helpers } ) { - my $sub = $renderer->helpers->{$method}; - $helper{$method} = sub { $c->$sub(@_) }; + foreach my $method (grep { m/^\w+\z/ } keys %{$renderer->helpers}) { + my $sub = $renderer->helpers->{$method}; + $params{$method} = sub { $c->$sub(@_) }; } - $vars->{helper} = \%helper; + # Stash values + $params{$_} = $c->stash->{$_} for grep { m/^\w+\z/ } keys %{$c->stash}; - # The controller - $vars->{c} = $c; - my $name = $options->{template}; - unless ( $name =~ /\./ ) { - $name = sprintf '%s.%s.tmpl', $options->{template}, $options->{format}; - } + $params{self} = $params{c} = $c; + + my $name = sprintf '%s.%s.tmpl', $options->{template}, $options->{format}; my $template = Bugzilla->template; - $template->process( $name, $vars, $output ) - or die $template->error; + $template->process( $name, \%params, $output ) + or die $template->error; + } + ); + $app->helper( + 'bugzilla.login_redirect_if_required' => sub { + my ( $c, $type ) = @_; + + if ( $type == LOGIN_REQUIRED ) { + $c->redirect_to('/login'); + return undef; + } + else { + return Bugzilla->user; + } + } + ); + $app->helper( + 'bugzilla.login' => sub { + my ( $c, $type ) = @_; + $type //= LOGIN_NORMAL; + + return Bugzilla->user if Bugzilla->user->id; + + $type = LOGIN_REQUIRED if $c->param('GoAheadAndLogIn') || Bugzilla->params->{requirelogin}; + + # Allow templates to know that we're in a page that always requires + # login. + if ( $type == LOGIN_REQUIRED ) { + Bugzilla->request_cache->{page_requires_login} = 1; + } + + my $login_cookie = $c->cookie("Bugzilla_logincookie"); + my $user_id = $c->cookie("Bugzilla_login"); + my $ip_addr = $c->tx->remote_address; + + return $c->bugzilla->login_redirect_if_required($type) unless ( $login_cookie && $user_id ); + + my $db_cookie = Bugzilla->dbh->selectrow_array( + q{ + SELECT cookie + FROM logincookies + WHERE cookie = ? + AND userid = ? + AND (restrict_ipaddr = 0 OR ipaddr = ?) + }, + undef, + ( $login_cookie, $user_id, $ip_addr ) + ); + + if ( defined $db_cookie && secure_compare( $login_cookie, $db_cookie ) ) { + my $user = Bugzilla::User->check( { id => $user_id, cache => 1 } ); + + # If we logged in successfully, then update the lastused + # time on the login cookie + with_writable_database { + Bugzilla->dbh->do( q{ UPDATE logincookies SET lastused = NOW() WHERE cookie = ? }, + undef, $login_cookie ); + }; + Bugzilla->set_user($user); + return $user; + } + else { + return $c->bugzilla->login_redirect_if_required($type); + } + } + ); + $app->helper( + 'bugzilla.error_page' => sub { + my ( $c, $error ) = @_; + if ( blessed $error && $error->isa('Bugzilla::Error::Base') ) { + $c->render( + handler => 'bugzilla', + template => $error->template, + error => $error->message, + %{ $error->vars } + ); + } + else { + $c->reply->exception($error); + } } ); diff --git a/template/en/default/global/code-error.html.tmpl b/template/en/default/global/code-error.html.tmpl index 23d7c203b..8aaf10127 100644 --- a/template/en/default/global/code-error.html.tmpl +++ b/template/en/default/global/code-error.html.tmpl @@ -514,13 +514,15 @@ [%# We only want HTML error messages for ERROR_MODE_WEBPAGE %] [% USE Bugzilla %] -[% IF Bugzilla.error_mode != constants.ERROR_MODE_WEBPAGE %] - [% IF Bugzilla.usage_mode == constants.USAGE_MODE_BROWSER %] - [% error_message FILTER none %] - [% ELSE %] - [% error_message FILTER txt %] +[% IF Bugzilla.usage_mode != constants.USAGE_MODE_MOJO %] + [% IF Bugzilla.error_mode != constants.ERROR_MODE_WEBPAGE %] + [% IF Bugzilla.usage_mode == constants.USAGE_MODE_BROWSER %] + [% error_message FILTER none %] + [% ELSE %] + [% error_message FILTER txt %] + [% END %] + [% RETURN %] [% END %] - [% RETURN %] [% END %] [% UNLESS header_done %] diff --git a/template/en/default/global/header.html.tmpl b/template/en/default/global/header.html.tmpl index 4c6069c74..1cb46a07a 100644 --- a/template/en/default/global/header.html.tmpl +++ b/template/en/default/global/header.html.tmpl @@ -346,12 +346,10 @@
  • Preferences
  • - [% IF user.authorizer.can_logout %] -
  • -
  • - Log out -
  • - [% END %] +
  • +
  • + Log out +
  • [% IF sudoer %]
  • End sudo session impersonating [% user.login FILTER html %] diff --git a/template/en/default/global/user-error.html.tmpl b/template/en/default/global/user-error.html.tmpl index c6efb5649..9b0583009 100644 --- a/template/en/default/global/user-error.html.tmpl +++ b/template/en/default/global/user-error.html.tmpl @@ -1982,13 +1982,15 @@ [%# We only want HTML error messages for ERROR_MODE_WEBPAGE %] [% USE Bugzilla %] -[% IF Bugzilla.error_mode != constants.ERROR_MODE_WEBPAGE %] - [% IF Bugzilla.usage_mode == constants.USAGE_MODE_BROWSER %] - [% error_message FILTER none %] - [% ELSE %] - [% error_message FILTER txt %] +[% IF Bugzilla.usage_mode != constants.USAGE_MODE_MOJO %] + [% IF Bugzilla.error_mode != constants.ERROR_MODE_WEBPAGE %] + [% IF Bugzilla.usage_mode == constants.USAGE_MODE_BROWSER %] + [% error_message FILTER none %] + [% ELSE %] + [% error_message FILTER txt %] + [% END %] + [% RETURN %] [% END %] - [% RETURN %] [% END %] [% UNLESS header_done %] -- cgit v1.2.3-24-g4f1b