Commit b8ecdca2 authored by Simon Green's avatar Simon Green

Bug 1009013 - Require a user to change their password if they log in and their…

Bug 1009013 - Require a user to change their password if they log in and their current password does not meet the password complexity rules r=glob, a=sgreen
parent ab1b842b
...@@ -56,10 +56,19 @@ sub check_credentials { ...@@ -56,10 +56,19 @@ sub check_credentials {
}; };
} }
# Force the user to type a longer password if it's too short. # Force the user to change their password if it does not meet the current
if (length($password) < USER_PASSWORD_MIN_LENGTH) { # criteria. This should usually only happen if the criteria has changed.
return { failure => AUTH_ERROR, user_error => 'password_current_too_short', if (Bugzilla->usage_mode == USAGE_MODE_BROWSER &&
details => { locked_user => $user } }; Bugzilla->params->{password_check_on_login})
{
my $check = validate_password_check($password);
if ($check) {
return {
failure => AUTH_ERROR,
user_error => $check,
details => { locked_user => $user }
}
}
} }
# The user's credentials are okay, so delete any outstanding # The user's credentials are okay, so delete any outstanding
......
...@@ -114,7 +114,14 @@ sub get_param_list { ...@@ -114,7 +114,14 @@ sub get_param_list {
'letters_numbers_specialchars' ], 'letters_numbers_specialchars' ],
default => 'no_constraints', default => 'no_constraints',
checker => \&check_multi checker => \&check_multi
} ); },
{
name => 'password_check_on_login',
type => 'b',
default => '1'
},
);
return @param_list; return @param_list;
} }
......
...@@ -32,7 +32,7 @@ use URI::QueryParam; ...@@ -32,7 +32,7 @@ use URI::QueryParam;
use parent qw(Bugzilla::Object Exporter); use parent qw(Bugzilla::Object Exporter);
@Bugzilla::User::EXPORT = qw(is_available_username @Bugzilla::User::EXPORT = qw(is_available_username
login_to_id validate_password login_to_id validate_password validate_password_check
USER_MATCH_MULTIPLE USER_MATCH_FAILED USER_MATCH_SUCCESS USER_MATCH_MULTIPLE USER_MATCH_FAILED USER_MATCH_SUCCESS
MATCH_SKIP_CONFIRM MATCH_SKIP_CONFIRM
); );
...@@ -2449,29 +2449,35 @@ sub login_to_id { ...@@ -2449,29 +2449,35 @@ sub login_to_id {
} }
sub validate_password { sub validate_password {
my $check = validate_password_check(@_);
ThrowUserError($check) if $check;
return 1;
}
sub validate_password_check {
my ($password, $matchpassword) = @_; my ($password, $matchpassword) = @_;
if (length($password) < USER_PASSWORD_MIN_LENGTH) { if (length($password) < USER_PASSWORD_MIN_LENGTH) {
ThrowUserError('password_too_short'); return 'password_too_short';
} elsif ((defined $matchpassword) && ($password ne $matchpassword)) { } elsif ((defined $matchpassword) && ($password ne $matchpassword)) {
ThrowUserError('passwords_dont_match'); return 'passwords_dont_match';
} }
my $complexity_level = Bugzilla->params->{password_complexity}; my $complexity_level = Bugzilla->params->{password_complexity};
if ($complexity_level eq 'letters_numbers_specialchars') { if ($complexity_level eq 'letters_numbers_specialchars') {
ThrowUserError('password_not_complex') return 'password_not_complex'
if ($password !~ /[[:alpha:]]/ || $password !~ /\d/ || $password !~ /[[:punct:]]/); if ($password !~ /[[:alpha:]]/ || $password !~ /\d/ || $password !~ /[[:punct:]]/);
} elsif ($complexity_level eq 'letters_numbers') { } elsif ($complexity_level eq 'letters_numbers') {
ThrowUserError('password_not_complex') return 'password_not_complex'
if ($password !~ /[[:lower:]]/ || $password !~ /[[:upper:]]/ || $password !~ /\d/); if ($password !~ /[[:lower:]]/ || $password !~ /[[:upper:]]/ || $password !~ /\d/);
} elsif ($complexity_level eq 'mixed_letters') { } elsif ($complexity_level eq 'mixed_letters') {
ThrowUserError('password_not_complex') return 'password_not_complex'
if ($password !~ /[[:lower:]]/ || $password !~ /[[:upper:]]/); if ($password !~ /[[:lower:]]/ || $password !~ /[[:upper:]]/);
} }
# Having done these checks makes us consider the password untainted. # Having done these checks makes us consider the password untainted.
trick_taint($_[0]); trick_taint($_[0]);
return 1; return;
} }
...@@ -3142,12 +3148,23 @@ if you need more information about the user than just their ID. ...@@ -3142,12 +3148,23 @@ if you need more information about the user than just their ID.
=item C<validate_password($passwd1, $passwd2)> =item C<validate_password($passwd1, $passwd2)>
Returns true if a password is valid (i.e. meets Bugzilla's Returns true if a password is valid (i.e. meets Bugzilla's
requirements for length and content), else returns false. requirements for length and content), else throws an error.
Untaints C<$passwd1> if successful. Untaints C<$passwd1> if successful.
If a second password is passed in, this function also verifies that If a second password is passed in, this function also verifies that
the two passwords match. the two passwords match.
=item C<validate_password_check($passwd1, $passwd2)>
This sub routine is similair to C<validate_password>, except that it allows
the calling code to handle its own errors.
Returns undef and untaints C<$passwd1> if a password is valid (i.e. meets
Bugzilla's requirements for length and content), else returns the error.
If a second password is passed in, this function also verifies that
the two passwords match.
=item C<match_field($data, $fields, $behavior)> =item C<match_field($data, $fields, $behavior)>
=over =over
......
...@@ -142,7 +142,8 @@ use constant WS_ERROR_CODE => { ...@@ -142,7 +142,8 @@ use constant WS_ERROR_CODE => {
auth_invalid_email => 302, auth_invalid_email => 302,
extern_id_conflict => -303, extern_id_conflict => -303,
auth_failure => 304, auth_failure => 304,
password_current_too_short => 305, password_too_short => 305,
password_not_complex => 305,
api_key_not_valid => 306, api_key_not_valid => 306,
api_key_revoked => 306, api_key_revoked => 306,
auth_invalid_token => 307, auth_invalid_token => 307,
......
...@@ -133,5 +133,11 @@ ...@@ -133,5 +133,11 @@
"lower case letter and a number.</li>" _ "lower case letter and a number.</li>" _
"<li>letters_numbers_specialchars - Passwords must contain at least one " _ "<li>letters_numbers_specialchars - Passwords must contain at least one " _
"letter, a number and a special character.</li></ul>" "letter, a number and a special character.</li></ul>"
password_check_on_login =>
"If set, $terms.Bugzilla will check that the password meets the current " _
"complexity rules and minimum length requirements when the user logs " _
"into the $terms.Bugzilla web interface. If it doesn't, the user would " _
"not be able to log in, and recieve a message to reset their password."
} }
%] %]
...@@ -1459,18 +1459,14 @@ ...@@ -1459,18 +1459,14 @@
[% title = "Passwords Don't Match" %] [% title = "Passwords Don't Match" %]
The two passwords you entered did not match. The two passwords you entered did not match.
[% ELSIF error == "password_current_too_short" %]
[% title = "New Password Required" %]
Your password is currently less than
[%+ constants.USER_PASSWORD_MIN_LENGTH FILTER html %] characters long,
which is the new minimum length required for passwords.
You must <a href="token.cgi?a=reqpw&amp;loginname=[% locked_user.email FILTER uri %]">
request a new password</a> in order to log in again.
[% ELSIF error == "password_too_short" %] [% ELSIF error == "password_too_short" %]
[% title = "Password Too Short" %] [% title = "Password Too Short" %]
The password must be at least The password must be at least
[%+ constants.USER_PASSWORD_MIN_LENGTH FILTER html %] characters long. [%+ constants.USER_PASSWORD_MIN_LENGTH FILTER html %] characters long.
[% IF locked_user %]
You must <a href="token.cgi?a=reqpw&amp;loginname=[% locked_user.email FILTER uri %]&amp;token=[% issue_hash_token(['reqpw']) FILTER uri %]">
request a new password</a> in order to log in again.
[% END %]
[% ELSIF error == "password_not_complex" %] [% ELSIF error == "password_not_complex" %]
[% title = "Password Fails Requirements" %] [% title = "Password Fails Requirements" %]
...@@ -1488,6 +1484,10 @@ ...@@ -1488,6 +1484,10 @@
<li>digit</li> <li>digit</li>
[% END %] [% END %]
</ul> </ul>
[% IF locked_user %]
You must <a href="token.cgi?a=reqpw&amp;loginname=[% locked_user.email FILTER uri %]&amp;token=[% issue_hash_token(['reqpw']) FILTER uri %]">
request a new password</a> in order to log in again.
[% END %]
[% ELSIF error == "product_access_denied" %] [% ELSIF error == "product_access_denied" %]
[% title = "Product Access Denied" %] [% title = "Product Access Denied" %]
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment