[Rt-commit] rt branch, 4.6/password-complexity, created. rt-4.4.1-118-g8d7a9e6
Aaron Kondziela
aaron at bestpractical.com
Mon Sep 26 22:02:38 EDT 2016
The branch, 4.6/password-complexity has been created
at 8d7a9e693090bb6be580a1c4a961323fa413e133 (commit)
- Log -----------------------------------------------------------------
commit 8d7a9e693090bb6be580a1c4a961323fa413e133
Author: Aaron Kondziela <aaron at bestpractical.com>
Date: Mon Sep 26 21:50:28 2016 -0400
Improve password complexity configuration
This expands the basic MinimumPasswordLength configuration option into a
set of options under the new PasswordPolicy key. The new options allow
checks for a minimum number of various classes of character.
The web interface password fields are longer, to encourage use of a long
passphrase. Some basic guidance for selecting a good password is displayed.
The password requirements, as configured, are displayed to the user when
they are entering a new password.
Fixes: T#161950
diff --git a/etc/RT_Config.pm.in b/etc/RT_Config.pm.in
index bf9f01e..68cf655 100644
--- a/etc/RT_Config.pm.in
+++ b/etc/RT_Config.pm.in
@@ -2454,14 +2454,43 @@ requirements.
Set($WebHttpOnlyCookies, 1);
-=item C<$MinimumPasswordLength>
-
-C<$MinimumPasswordLength> defines the minimum length for user
-passwords. Setting it to 0 disables this check.
-
-=cut
-
-Set($MinimumPasswordLength, 5);
+=item C<%PasswordPolicy>
+
+C<%PasswordPolicy> sets the requirements for a user's password. The options
+and their default values are:
+
+ MinimumLength => 8,
+ Uppercase => 0,
+ Lowercase => 0,
+ Digits => 0,
+ Symbols => 0,
+ UppercaseRegex => qr/\p{XPosixUpper}/,
+ LowercaseRegex => qr/\p{XPosixLower}/,
+ DigitsRegex => qr/\p{XPosixDigit}/,
+ SymbolsRegex => qr/\p{XPosixPunct}/,
+
+For C<Uppercase>, C<Lowercase>, C<Digits>, and C<Symbols>, set to zero to
+disable that specific check. The value indicates the number of each class
+of characters that must be present in a valid password. If other than zero,
+the web interface will inform the user of each requirement in the web
+interface.
+
+The regex definitions are exposed as an aid to localisation. Don't change
+these values unless you have a specific need to do so.
+
+=cut
+
+Set(%PasswordPolicy, (
+ MinimumLength => 8,
+ Uppercase => 0,
+ Lowercase => 0,
+ Digits => 0,
+ Symbols => 0,
+ UppercaseRegex => qr/\p{XPosixUpper}/,
+ LowercaseRegex => qr/\p{XPosixLower}/,
+ DigitsRegex => qr/\p{XPosixDigit}/,
+ SymbolsRegex => qr/\p{XPosixPunct}/,
+));
=back
diff --git a/lib/RT/Installer.pm b/lib/RT/Installer.pm
index 5b43ace..40e0f06 100644
--- a/lib/RT/Installer.pm
+++ b/lib/RT/Installer.pm
@@ -138,12 +138,6 @@ my %Meta = (
Hints => 'RT will use this string to uniquely identify your installation and looks for it in the subject of emails to decide what ticket a message applies to. We recommend that you set this to your internet domain. (ex: example.com)' #loc
},
},
- MinimumPasswordLength => {
- Widget => '/Widgets/Form/Integer',
- WidgetArguments => {
- Description => 'Minimum password length', #loc
- },
- },
Password => {
SkipWrite => 1,
Widget => '/Widgets/Form/String',
diff --git a/lib/RT/User.pm b/lib/RT/User.pm
index 29de4ae..c4e8c07 100644
--- a/lib/RT/User.pm
+++ b/lib/RT/User.pm
@@ -283,7 +283,8 @@ sub ValidateName {
=head2 ValidatePassword STRING
Returns either (0, "failure reason") or 1 depending on whether the given
-password is valid.
+password is valid. Tests for vailidity are configured by the C<%PasswordPolicy>
+settings.
=cut
@@ -291,8 +292,23 @@ sub ValidatePassword {
my $self = shift;
my $password = shift;
- if ( length($password) < RT->Config->Get('MinimumPasswordLength') ) {
- return ( 0, $self->loc("Password needs to be at least [quant,_1,character,characters] long", RT->Config->Get('MinimumPasswordLength')) );
+ if ( length($password) < (my $limit = RT->Config->Get('PasswordPolicy')->{'MinimumLength'}) ) {
+ return ( 0, $self->loc("Password needs to be at least [quant,_1,character,characters] long", $limit) );
+ }
+
+ my @checks = (
+ ['Uppercase', 'UppercaseRegex', 'Password must include at least [quant,_1,uppercase character,uppercase characters]'],
+ ['Lowercase', 'LowercaseRegex', 'Password must include at least [quant,_1,lowercase character,lowercase characters]'],
+ ['Digits', 'DigitsRegex', 'Password must include at least [quant,_1,digit,digits]'],
+ ['Symbols', 'SymbolsRegex', 'Password must include at least [quant,_1,punctuation mark or symbol,punctuation marks or symbols]'],
+ );
+
+ foreach my $check (@checks) {
+ if ( my $limit = RT->Config->Get('PasswordPolicy')->{@$check[0]} ) {
+ my $re = RT->Config->Get('PasswordPolicy')->{@$check[1]};
+ my $count = () = $password =~ m/$re/g;
+ return ( 0, $self->loc(@$check[2], $limit) ) if $count < $limit;
+ }
}
return 1;
@@ -849,9 +865,8 @@ sub SetRandomPassword {
return ( 0, $self->loc("Permission Denied") );
}
-
- my $min = ( RT->Config->Get('MinimumPasswordLength') > 6 ? RT->Config->Get('MinimumPasswordLength') : 6);
- my $max = ( RT->Config->Get('MinimumPasswordLength') > 8 ? RT->Config->Get('MinimumPasswordLength') : 8);
+ my $min = ( RT->Config->Get('PasswordPolicy')->{'MinimumLength'} > 6 ? RT->Config->Get('PasswordPolicy')->{'MinimumLength'} : 6);
+ my $max = ( RT->Config->Get('PasswordPolicy')->{'MinimumLength'} > 8 ? RT->Config->Get('PasswordPolicy')->{'MinimumLength'} : 8);
my $pass = $self->GenerateRandomPassword( $min, $max) ;
diff --git a/sbin/rt-setup-database.in b/sbin/rt-setup-database.in
index 951d7f6..d32d3b5 100644
--- a/sbin/rt-setup-database.in
+++ b/sbin/rt-setup-database.in
@@ -114,7 +114,7 @@ if ( $args{'root-password-file'} ) {
or die "Couldn't open 'args{'root-password-file'}' for reading: $!";
$root_password = <$fh>;
chomp $root_password;
- my $min_length = RT->Config->Get('MinimumPasswordLength');
+ my $min_length = RT->Config->Get('PasswordPolicy')->{'MinimumLength'};
if ($min_length) {
die
"password needs to be at least $min_length long, please check file '$args{'root-password-file'}'"
diff --git a/share/html/Admin/Tools/Configuration.html b/share/html/Admin/Tools/Configuration.html
index 4535827..f7b9731 100644
--- a/share/html/Admin/Tools/Configuration.html
+++ b/share/html/Admin/Tools/Configuration.html
@@ -87,7 +87,7 @@ foreach my $key ( RT->Config->Options( Overridable => undef, Sorted => 0 ) ) {
<tr class="<% $index_conf%2 ? 'oddline' : 'evenline'%>">
<td class="collection-as-table"><% $key %></td>
<td class="collection-as-table">
-% if ( $key =~ /Password/i and $key !~ /MinimumPasswordLength|AllowLoginPasswordAutoComplete/ ) {
+% if ( $key =~ /Password/i and $key !~ /PasswordPolicy|AllowLoginPasswordAutoComplete/ ) {
<em><% loc('Password not printed' ) %></em>\
% } else {
<% stringify($val) |n %>\
diff --git a/share/html/Elements/EditPassword b/share/html/Elements/EditPassword
index e074d7a..5b2d08f 100644
--- a/share/html/Elements/EditPassword
+++ b/share/html/Elements/EditPassword
@@ -53,18 +53,18 @@
% if ( $cond{'RequireCurrent'} ) {
<tr>
<td class="label"><&|/l, $session{'CurrentUser'}->Name()&>[_1]'s current password</&>:</td>
-<td class="value"><input type="password" name="<% $Name[0] %>" size="16" autocomplete="off" /></td>
+<td class="value"><input type="password" name="<% $Name[0] %>" size="40" autocomplete="off" /></td>
</tr>
% }
<tr>
<td class="label"><&|/l&>New password</&>:</td>
-<td class="value"><input type="password" name="<% $Name[1] %>" size="16" autocomplete="off" /></td>
+<td class="value"><input type="password" name="<% $Name[1] %>" size="40" autocomplete="off" /></td>
</tr>
<tr>
<td class="label"><&|/l&>Retype Password</&>:</td>
-<td class="value"><input type="password" name="<% $Name[2] %>" size="16" autocomplete="off" /></td>
+<td class="value"><input type="password" name="<% $Name[2] %>" size="40" autocomplete="off" /></td>
</tr>
</table>
diff --git a/share/html/Prefs/AboutMe.html b/share/html/Prefs/AboutMe.html
index d83cc39..749a281 100644
--- a/share/html/Prefs/AboutMe.html
+++ b/share/html/Prefs/AboutMe.html
@@ -117,6 +117,35 @@
<td valign="top" class="boxcontainer">
<&| /Widgets/TitleBox, title => loc('Password'), id => "user-prefs-password" &>
+
+<p><&|/l&>Tips to help you choose a more secure password:</&></p>
+<ul>
+<li><&|/l&><b>Use a full phrase.</b> Describe a vivid image or memory, so it's easy to recall.</&></li>
+<li><&|/l&><b>Avoid exact quotations</b> from movies, books, or elsewhere.</&></li>
+<li><&|/l&><b>Avoid personal information</b> like names and birthdays.</&></li>
+<li><&|/l&><b>Use each password on one site only.</b> Never re-use the same password on other sites!</&></li>
+<li><&|/l&>Poor example: <tt>Monkey123!</tt></&></li>
+<li><&|/l&>Good example: <tt>MamaLovedThe'57Chevy</tt></&></li>
+</ul>
+
+<p><&|/l&>Password requirements:</&></p>
+<ul>
+% {
+% my @checks = (
+% ['MinimumLength', 'At least [quant,_1,character,characters] long'],
+% ['Uppercase', 'Include at least [quant,_1,uppercase character,uppercase characters]'],
+% ['Lowercase', 'Include at least [quant,_1,lowercase character,lowercase characters]'],
+% ['Digits', 'Include at least [quant,_1,digit,digits]'],
+% ['Symbols', 'Include at least [quant,_1,punctuation mark or symbol,punctuation marks or symbols]'],
+% );
+% foreach my $check (@checks) {
+% if ( my $limit = RT->Config->Get('PasswordPolicy')->{@$check[0]} ) {
+<li><%loc(@$check[1],$limit)%></li>
+% }
+% }
+% }
+</ul>
+
% if ( $UserObj->__Value('Password') ne '*NO-PASSWORD*' ) {
<& /Elements/EditPassword,
User => $UserObj,
-----------------------------------------------------------------------
More information about the rt-commit
mailing list