diff --git a/Bugzilla/Auth.pm b/Bugzilla/Auth.pm index 56d48805e..cf7d27f98 100644 --- a/Bugzilla/Auth.pm +++ b/Bugzilla/Auth.pm @@ -169,7 +169,7 @@ sub _handle_login_result { # the password was just wrong. (This makes it harder for a cracker # to find account names by brute force) elsif ($fail_code == AUTH_LOGINFAILED or $fail_code == AUTH_NO_SUCH_USER) { - my $remaining_attempts = MAX_LOGIN_ATTEMPTS + my $remaining_attempts = Bugzilla->params->{max_login_attempts} - ($result->{failure_count} || 0); ThrowUserError("invalid_username_or_password", { remaining => $remaining_attempts }); @@ -188,8 +188,8 @@ sub _handle_login_result { # We want to know when the account will be unlocked. This is # determined by the 5th-from-last login failure (or more/less than - # 5th, if MAX_LOGIN_ATTEMPTS is not 5). - my $determiner = $attempts->[scalar(@$attempts) - MAX_LOGIN_ATTEMPTS]; + # 5th, if Bugzilla->params->{max_login_attempts} is not 5). + my $determiner = $attempts->[scalar(@$attempts) - Bugzilla->params->{max_login_attempts}]; my $unlock_at = datetime_from($determiner->{login_time}, Bugzilla->local_timezone); $unlock_at->add(minutes => Bugzilla->params->{login_lockout_interval}); diff --git a/Bugzilla/Config/Auth.pm b/Bugzilla/Config/Auth.pm index 299989bfd..36149488b 100644 --- a/Bugzilla/Config/Auth.pm +++ b/Bugzilla/Config/Auth.pm @@ -135,6 +135,13 @@ sub get_param_list { default => '' }, + { + name => 'max_login_attempts', + type => 't', + default => 5, + checker => sub { $_[0] =~ /^\d+$/so ? "" : "must be a positive integer value or 0 (means no limit)" }, + }, + { name => 'login_lockout_interval', type => 't', diff --git a/Bugzilla/Constants.pm b/Bugzilla/Constants.pm index 259ed5c34..096d16e4f 100644 --- a/Bugzilla/Constants.pm +++ b/Bugzilla/Constants.pm @@ -156,7 +156,6 @@ use Cwd qw(abs_path); MAX_TOKEN_AGE MAX_LOGINCOOKIE_AGE MAX_SUDO_TOKEN_AGE - MAX_LOGIN_ATTEMPTS SAFE_PROTOCOLS LEGAL_CONTENT_TYPES @@ -390,9 +389,6 @@ use constant MAX_LOGINCOOKIE_AGE => 30; # How many seconds (default is 6 hours) a sudo cookie remains valid. use constant MAX_SUDO_TOKEN_AGE => 21600; -# Maximum failed logins to lock account for this IP -use constant MAX_LOGIN_ATTEMPTS => 5; - # Protocols which are considered as safe. use constant SAFE_PROTOCOLS => ('afs', 'cid', 'ftp', 'gopher', 'http', 'https', 'irc', 'mid', 'news', 'nntp', 'prospero', 'telnet', diff --git a/Bugzilla/User.pm b/Bugzilla/User.pm index da0bbc135..89fdc5264 100644 --- a/Bugzilla/User.pm +++ b/Bugzilla/User.pm @@ -1761,7 +1761,7 @@ sub create { sub account_is_locked_out { my $self = shift; my $login_failures = scalar @{ $self->account_ip_login_failures }; - return $login_failures >= MAX_LOGIN_ATTEMPTS ? 1 : 0; + return Bugzilla->params->{max_login_attempts} && $login_failures >= Bugzilla->params->{max_login_attempts} ? 1 : 0; } sub note_login_failure { diff --git a/template/en/default/admin/params/auth.html.tmpl b/template/en/default/admin/params/auth.html.tmpl index e824de633..cb79476be 100644 --- a/template/en/default/admin/params/auth.html.tmpl +++ b/template/en/default/admin/params/auth.html.tmpl @@ -132,6 +132,8 @@ fof_sudo_mynetworks => "Comma-separated list of network masks in the form of xxx.xxx.xxx.xxx/xx " _ "(for example 127.0.0.1/32) from which FOF_Sudo authorization is allowed.", + max_login_attempts => "Maximum failed logins to lock account for one IP address. 0 means no limit.", + login_lockout_interval => "If the maximum login attempts occur during this many minutes, the account is locked.", } %] diff --git a/template/en/default/email/lockout.txt.tmpl b/template/en/default/email/lockout.txt.tmpl index ac6525779..55711a851 100644 --- a/template/en/default/email/lockout.txt.tmpl +++ b/template/en/default/email/lockout.txt.tmpl @@ -26,7 +26,7 @@ Subject: [[% terms.Bugzilla %]] Account Lock-Out: [% locked_user.login %] ([% at X-Bugzilla-Type: admin The IP address [% attempts.0.ip_addr %] failed too many login attempts ( -[%- constants.MAX_LOGIN_ATTEMPTS +%]) for +[%- Param('max_login_attempts') +%]) for the account [% locked_user.login %]. The login attempts occurred at these times: