diff options
author | Dylan William Hardison <dylan@hardison.net> | 2017-09-25 20:14:31 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-09-25 20:14:31 +0200 |
commit | 94d888356469f2d920835f9c6d4eba944e429f62 (patch) | |
tree | a4a123f21ae538261bf53d3593e12fb8d1a3d26b | |
parent | d827379894e2a5415cdbbb6b30aad0448ba82fb0 (diff) | |
download | bugzilla-94d888356469f2d920835f9c6d4eba944e429f62.tar.gz bugzilla-94d888356469f2d920835f9c6d4eba944e429f62.tar.xz |
Bug 1401463 - In bugzilla "you must reset password" state, all bug pages are force-redirected to password reset page, which loses "to-do" information that I have encoded as open tabs viewing particular bug pages
-rw-r--r-- | Bugzilla.pm | 7 | ||||
-rw-r--r-- | Bugzilla/Token.pm | 22 | ||||
-rwxr-xr-x | reset_password.cgi | 58 | ||||
-rw-r--r-- | t/hash-sig.t | 24 | ||||
-rw-r--r-- | template/en/default/account/reset-password.html.tmpl | 16 | ||||
-rw-r--r-- | template/en/default/global/user-error.html.tmpl | 3 |
6 files changed, 119 insertions, 11 deletions
diff --git a/Bugzilla.pm b/Bugzilla.pm index 1f01979ea..9bce73010 100644 --- a/Bugzilla.pm +++ b/Bugzilla.pm @@ -396,7 +396,12 @@ sub login { # (tokens handles the 'forgot password' process) # otherwise redirect user to the reset-password page. if ( $ENV{SCRIPT_NAME} !~ m#/(?:reset_password|token)\.cgi$# ) { - print $cgi->redirect('reset_password.cgi'); + my $self_url = trim($cgi->self_url); + my $sig_type = 'prev_url:' . $authenticated_user->id; + my $self_url_sig = issue_hash_sig($sig_type, $self_url); + my $redir_url = URI->new( correct_urlbase() . "reset_password.cgi" ); + $redir_url->query_form(prev_url => $self_url, prev_url_sig => $self_url_sig); + print $cgi->redirect($redir_url); exit; } } diff --git a/Bugzilla/Token.pm b/Bugzilla/Token.pm index c6288f491..4b12f836b 100644 --- a/Bugzilla/Token.pm +++ b/Bugzilla/Token.pm @@ -32,6 +32,7 @@ use base qw(Exporter); issue_auth_delegation_token check_auth_delegation_token check_token_data delete_token issue_hash_token check_hash_token + issue_hash_sig check_hash_sig set_token_extra_data get_token_extra_data); # 128 bits password: @@ -221,6 +222,27 @@ sub issue_short_lived_session_token { return _create_token($user->id ? $user->id : undef, 'session.short', $data); } +sub issue_hash_sig { + my ($type, $data, $salt) = @_; + $data //= ""; + $salt //= generate_random_password(16); + + my $hmac = hmac_sha256_base64( + $salt, + $type, + $data, + Bugzilla->localconfig->{site_wide_secret} + ); + return sprintf("%s|%s|%x", $salt, $hmac, length($data)); +} + +sub check_hash_sig { + my ($type, $sig, $data) = @_; + return 0 unless defined $sig && defined $data; + my ($salt, undef, $len) = split(/\|/, $sig, 3); + return length($data) == hex($len) && $sig eq issue_hash_sig($type, $data, $salt); +} + sub issue_hash_token { my ($data, $time) = @_; $data ||= []; diff --git a/reset_password.cgi b/reset_password.cgi index 86ace9e12..a79fea063 100755 --- a/reset_password.cgi +++ b/reset_password.cgi @@ -17,14 +17,34 @@ use Bugzilla; use Bugzilla::Constants; use Bugzilla::Error; use Bugzilla::Token; -use Bugzilla::Util qw( bz_crypt ); +use Bugzilla::Util qw( bz_crypt trim ); +use Data::Dumper; -my $cgi = Bugzilla->cgi; -my $user = Bugzilla->login(LOGIN_REQUIRED); -my $template = Bugzilla->template; -my $dbh = Bugzilla->dbh; +my $cgi = Bugzilla->cgi; +my $user = Bugzilla->login(LOGIN_REQUIRED); +my $template = Bugzilla->template; +my $dbh = Bugzilla->dbh; +my $prev_url = $cgi->param('prev_url'); +my $prev_url_sig = $cgi->param('prev_url_sig'); +my $sig_type = 'prev_url:' . $user->id; +my $prev_url_ok = check_hash_sig($sig_type, $prev_url_sig, $prev_url ); -ThrowUserError('reset_password_denied') unless $user->password_change_required; +unless ($prev_url_ok) { + open my $fh, '>', '/tmp/dump.pl' or die $!; + print $fh Dumper([$prev_url, $prev_url_sig]); + close $fh or die $!; +} + +unless ($user->password_change_required) { + ThrowUserError( + 'reset_password_denied', + { + prev_url_ok => $prev_url_ok, + prev_url => $prev_url, + } + ); + +} if ($cgi->param('do_save')) { my $token = $cgi->param('token'); @@ -64,14 +84,32 @@ if ($cgi->param('do_save')) { # done print $cgi->header(); - $template->process('index.html.tmpl', { message => 'password_changed' }) - || ThrowTemplateError($template->error()); + $template->process( + 'account/reset-password.html.tmpl', + { + message => 'password_changed', + prev_url => $prev_url, + prev_url_ok => $prev_url_ok, + password_changed => 1 + } + ) || ThrowTemplateError( $template->error() ); + } else { my $token = issue_session_token('reset_password'); print $cgi->header(); - $template->process('account/reset-password.html.tmpl', { token => $token }) - || ThrowTemplateError($template->error()); + $template->process( + 'account/reset-password.html.tmpl', + { + token => $token, + prev_url => $prev_url, + prev_url_ok => $prev_url_ok, + prev_url_sig => $prev_url_sig, + sig_type => $sig_type, + } + ) || ThrowTemplateError( $template->error() ); + + } diff --git a/t/hash-sig.t b/t/hash-sig.t new file mode 100644 index 000000000..30d3098d4 --- /dev/null +++ b/t/hash-sig.t @@ -0,0 +1,24 @@ +# 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::Util qw(generate_random_password); +use Bugzilla::Token qw(issue_hash_sig check_hash_sig); +use Test::More; + +my $localconfig = { site_wide_secret => generate_random_password(256) }; +{ + package Bugzilla; + sub localconfig { $localconfig } +} + +my $sig = issue_hash_sig("hero", "batman"); +ok(check_hash_sig("hero", $sig, "batman"), "sig for batman checks out"); + +done_testing();
\ No newline at end of file diff --git a/template/en/default/account/reset-password.html.tmpl b/template/en/default/account/reset-password.html.tmpl index ca60c5772..a2bec34fd 100644 --- a/template/en/default/account/reset-password.html.tmpl +++ b/template/en/default/account/reset-password.html.tmpl @@ -71,6 +71,20 @@ $(function() { <h1>Password Reset</h1> +[% BLOCK link %] + <a href="[% prev_url FILTER html %]">[% prev_url FILTER html %]</a> +[% END %] + +[% IF password_changed && prev_url_ok %] + <p>Continue to [% PROCESS link %]</p> + [% RETURN %] +[% ELSIF prev_url_ok %] + <p> + If you've already reset your password, you may continue to [% PROCESS link %] + </p> +[% END %] + + <p> [% user.password_change_reason || "You are required to update your password." FILTER html %] </p> @@ -82,6 +96,8 @@ $(function() { <form method="POST" action="reset_password.cgi"> <input type="hidden" name="token" value="[% token FILTER html %]"> <input type="hidden" name="do_save" value="1"> +<input type="hidden" name="prev_url" value="[% prev_url FILTER html %]"> +<input type="hidden" name="prev_url_sig" value="[% prev_url_sig FILTER html %]"> <div class="flex"> <div id="password-reset" class="flex-left"> diff --git a/template/en/default/global/user-error.html.tmpl b/template/en/default/global/user-error.html.tmpl index 3e4d7c4a0..9eefbcb73 100644 --- a/template/en/default/global/user-error.html.tmpl +++ b/template/en/default/global/user-error.html.tmpl @@ -1402,6 +1402,9 @@ [% ELSIF error == "reset_password_denied" %] [% title = "Reset Password Denied" %] You cannot reset your password without administrative permission. + [% IF prev_url_ok %] + Continue to <a href="[% prev_url FILTER html %]">[% prev_url FILTER html %]</a>. + [% END %] [% ELSIF error == "no_axes_defined" %] [% title = "No Axes Defined" %] |