From ba9cb5e1e50a76093f589e024e06e0741dfe6f42 Mon Sep 17 00:00:00 2001 From: Christophe Maudoux Date: Fri, 13 May 2022 00:15:38 +0200 Subject: [PATCH 1/9] Improve doc --- doc/sources/admin/exportedvars.rst | 13 ++++++++++--- doc/sources/admin/portalcustom.rst | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/doc/sources/admin/exportedvars.rst b/doc/sources/admin/exportedvars.rst index 3f0354e7b..c5d8074a8 100644 --- a/doc/sources/admin/exportedvars.rst +++ b/doc/sources/admin/exportedvars.rst @@ -51,13 +51,20 @@ portal: - macros are used to extend (or rewrite) :doc:`exported variables`. A macro is stored as attributes: it can contain boolean results or any string -- macros can also be used to import environment variables *(these +- macros can also be used for importing environment variables *(these variables are in CGI format)*. Example: ``$ENV{HTTP_COOKIE}`` - groups are stored as a string with values separated by ''; '' - (default values separator) in the special attribute ``groups``: it - contains the names of groups whose rules were returned true for the + (default multivalues separator) in the special attribute ``groups``: it + contains names of groups whose rules were returned true for the current user. For example: +.. danger:: + + Macros can be used for rewriting or overloading exported variables + but it can lead to some side effects. Be aware of alphabetical order + and keep in mind that exported variables are set then macros and + groups are computed. + .. code-block:: perl $groups = group3; admin diff --git a/doc/sources/admin/portalcustom.rst b/doc/sources/admin/portalcustom.rst index 8c100d830..002e27612 100644 --- a/doc/sources/admin/portalcustom.rst +++ b/doc/sources/admin/portalcustom.rst @@ -361,7 +361,7 @@ Password Policy - **Minimal upper characters**: leave 0 to bypass the check - **Minimal digit characters**: leave 0 to bypass the check - **Minimal special characters**: leave 0 to bypass the check -- **Allowed special characters**: set '__ALL__' value to allow ALL special characters. A blanck value forbids ALL special characters (Note that ``_`` is not a special character) +- **Allowed special characters**: set '__ALL__' value to allow ALL special characters. A blank value forbids ALL special characters (Note that ``_`` is not a special character) .. _portalcustom-other-parameters: From c979adb9d21954f8bb8e0ffca30dbdf741e3c2e5 Mon Sep 17 00:00:00 2001 From: Christophe Maudoux Date: Fri, 13 May 2022 00:17:43 +0200 Subject: [PATCH 2/9] Display password policy only if enabled (#2733) --- .../lib/Lemonldap/NG/Portal/Main/Display.pm | 44 ++++++++++++------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Display.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Display.pm index 91807576c..bd2aff221 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Display.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Display.pm @@ -10,11 +10,12 @@ use Mouse; use JSON; use URI; -has isPP => ( is => 'rw' ); -has speChars => ( is => 'rw' ); -has skinRules => ( is => 'rw' ); -has stayConnected => ( is => 'rw', default => sub { 0 } ); -has requireOldPwd => ( is => 'rw', default => sub { 1 } ); +has isPP => ( is => 'rw' ); +has speChars => ( is => 'rw' ); +has skinRules => ( is => 'rw' ); +has stayConnected => ( is => 'rw', default => sub { 0 } ); +has requireOldPwd => ( is => 'rw', default => sub { 1 } ); +has passwordPolicyActivation => ( is => 'rw', default => sub { 0 } ); sub displayInit { my ($self) = @_; @@ -48,6 +49,14 @@ sub displayInit { $self->logger->error("Bad stayConnected rule: $error"); } $self->stayConnected($rule); + $rule = + HANDLER->buildSub( + HANDLER->substitute( $self->conf->{passwordPolicyActivation} ) ); + unless ($rule) { + my $error = HANDLER->tsv->{jail}->error || 'Unable to compile rule'; + $self->logger->error("Bad passwordPolicyActivation rule: $error"); + } + $self->passwordPolicyActivation($rule); my $speChars = $self->conf->{passwordPolicySpecialChar} eq '__ALL__' @@ -259,12 +268,13 @@ sub display { DONT_STORE_PASSWORD => $self->conf->{browsersDontStorePassword}, HIDE_OLDPASSWORD => 0, PPOLICY_NOPOLICY => !$self->isPP(), - DISPLAY_PPOLICY => $self->conf->{portalDisplayPasswordPolicy}, - PPOLICY_MINSIZE => $self->conf->{passwordPolicyMinSize}, - PPOLICY_MINLOWER => $self->conf->{passwordPolicyMinLower}, - PPOLICY_MINUPPER => $self->conf->{passwordPolicyMinUpper}, - PPOLICY_MINDIGIT => $self->conf->{passwordPolicyMinDigit}, - PPOLICY_MINSPECHAR => $self->conf->{passwordPolicyMinSpeChar}, + DISPLAY_PPOLICY => $self->conf->{portalDisplayPasswordPolicy} + && $self->passwordPolicyActivation->( $req, $req->sessionInfo ), + PPOLICY_MINSIZE => $self->conf->{passwordPolicyMinSize}, + PPOLICY_MINLOWER => $self->conf->{passwordPolicyMinLower}, + PPOLICY_MINUPPER => $self->conf->{passwordPolicyMinUpper}, + PPOLICY_MINDIGIT => $self->conf->{passwordPolicyMinDigit}, + PPOLICY_MINSPECHAR => $self->conf->{passwordPolicyMinSpeChar}, ( $self->requireOldPwd->( $req, $req->userData ) ? ( REQUIRE_OLDPASSWORD => 1 ) @@ -479,11 +489,13 @@ sub display { HIDE_OLDPASSWORD => $self->conf->{hideOldPassword}, DONT_STORE_PASSWORD => $self->conf->{browsersDontStorePassword}, PPOLICY_NOPOLICY => !$self->isPP(), - DISPLAY_PPOLICY => $self->conf->{portalDisplayPasswordPolicy}, - PPOLICY_MINSIZE => $self->conf->{passwordPolicyMinSize}, - PPOLICY_MINLOWER => $self->conf->{passwordPolicyMinLower}, - PPOLICY_MINUPPER => $self->conf->{passwordPolicyMinUpper}, - PPOLICY_MINDIGIT => $self->conf->{passwordPolicyMinDigit}, + DISPLAY_PPOLICY => $self->conf->{portalDisplayPasswordPolicy} + && $self->passwordPolicyActivation->( $req, + $req->sessionInfo ), + PPOLICY_MINSIZE => $self->conf->{passwordPolicyMinSize}, + PPOLICY_MINLOWER => $self->conf->{passwordPolicyMinLower}, + PPOLICY_MINUPPER => $self->conf->{passwordPolicyMinUpper}, + PPOLICY_MINDIGIT => $self->conf->{passwordPolicyMinDigit}, PPOLICY_MINSPECHAR => $self->conf->{passwordPolicyMinSpeChar}, ( $self->conf->{passwordPolicyMinSpeChar} || $self->speChars() From 362072578761351ef9ef17ab5e5bc158057695f7 Mon Sep 17 00:00:00 2001 From: Christophe Maudoux Date: Fri, 13 May 2022 00:18:19 +0200 Subject: [PATCH 3/9] Improve log --- .../lib/Lemonldap/NG/Portal/Password/Base.pm | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Password/Base.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Password/Base.pm index ab4786887..2d9ad26bc 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Password/Base.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Password/Base.pm @@ -17,7 +17,7 @@ use Lemonldap::NG::Portal::Main::Constants qw( extends 'Lemonldap::NG::Portal::Main::Plugin'; -our $VERSION = '2.0.12'; +our $VERSION = '2.0.14'; # INITIALIZATION @@ -59,7 +59,8 @@ sub _modifyPassword { ) ); unless ($oldPwdRule) { - my $error = $self->p->HANDLER->tsv->{jail}->error || '???'; + my $error = + $self->p->HANDLER->tsv->{jail}->error || 'Unable to compile rule'; } my $pwdPolicyRule = $self->p->HANDLER->buildSub( @@ -68,7 +69,8 @@ sub _modifyPassword { ) ); unless ($pwdPolicyRule) { - my $error = $self->p->HANDLER->tsv->{jail}->error || '???'; + my $error = + $self->p->HANDLER->tsv->{jail}->error || 'Unable to compile rule'; } # Check if portal require old password From a79d78325761c0c5651d4f85f225e4f4aacebef4 Mon Sep 17 00:00:00 2001 From: Christophe Maudoux Date: Fri, 13 May 2022 00:21:53 +0200 Subject: [PATCH 4/9] Display password policy & use it to compile password regex (#2733) --- .../NG/Portal/Plugins/MailPasswordReset.pm | 81 ++++++++++++++----- 1 file changed, 62 insertions(+), 19 deletions(-) diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/MailPasswordReset.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/MailPasswordReset.pm index ae507284f..eb40eb588 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/MailPasswordReset.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/MailPasswordReset.pm @@ -32,7 +32,7 @@ use Lemonldap::NG::Portal::Main::Constants qw( PE_PP_INSUFFICIENT_PASSWORD_QUALITY ); -our $VERSION = '2.0.12'; +our $VERSION = '2.0.14'; extends qw( Lemonldap::NG::Portal::Lib::SMTP @@ -58,6 +58,9 @@ has ott => ( # Captcha generator has captcha => ( is => 'rw' ); +# Password policy activation rule +has passwordPolicyActivationRule => ( is => 'rw', default => sub { 0 } ); + # INITIALIZATION sub init { @@ -70,6 +73,15 @@ sub init { if ( $self->conf->{captcha_mail_enabled} ) { $self->captcha( $self->p->loadModule('::Lib::Captcha') ) or return 0; } + + # Parse password policy activation rule + $self->passwordPolicyActivationRule( + $self->p->buildRule( + $self->conf->{passwordPolicyActivation}, + 'passwordPolicyActivation' + ) + ); + return 0 unless $self->passwordPolicyActivationRule; return 1; } @@ -442,8 +454,28 @@ sub changePwd { "Reset password request for $req->{sessionInfo}->{_user}"); # Generate a complex password - my $password = - $self->gen_password( $self->conf->{randomPasswordRegexp} ); + my $pwdRegEx; + if ( $self->passwordPolicyActivationRule->( $req, $req->sessionInfo ) + && !$self->conf->{randomPasswordRegexp} ) + { + my $uppers = $self->conf->{passwordPolicyMinUpper} || 3; + my $lowers = $self->conf->{passwordPolicyMinLower} || 5; + my $digits = $self->conf->{passwordPolicyMinDigit} || 2; + my $chars = + $self->conf->{passwordPolicyMinSize} - + $self->conf->{passwordPolicyMinUpper} - + $self->conf->{passwordPolicyMinLower} - + $self->conf->{passwordPolicyMinDigit}; + $chars = 1 if $chars < 1; + $pwdRegEx = "[A-Z]{$uppers}[a-z]{$lowers}\\d{$digits}.{$chars}"; + $self->logger->debug("Generated password RegEx: $pwdRegEx"); + } + else { + $pwdRegEx = + $self->conf->{randomPasswordRegexp} || '[A-Z]{3}[a-z]{5}.\d{2}'; + $self->logger->debug("Used password RegEx: $pwdRegEx"); + } + my $password = $self->gen_password($pwdRegEx); $self->logger->debug("Generated password: $password"); $req->data->{newpassword} = $password; $req->data->{confirmpassword} = $password; @@ -467,11 +499,13 @@ sub changePwd { } } - # Check password quality + # Check password quality if enabled require Lemonldap::NG::Portal::Password::Base; my $cpq = - $self->Lemonldap::NG::Portal::Password::Base::checkPasswordQuality( - $req->data->{newpassword} ); + $self->passwordPolicyActivationRule->( $req, $req->sessionInfo ) + ? $self->Lemonldap::NG::Portal::Password::Base::checkPasswordQuality( + $req->data->{newpassword} ) + : PE_OK; unless ( $cpq == PE_OK ) { $self->ott->setToken( $req, $req->sessionInfo ); return $cpq; @@ -555,9 +589,19 @@ sub setSecurity { sub display { my ( $self, $req ) = @_; - my $speChars = $self->conf->{passwordPolicySpecialChar}; + my $speChars = + $self->conf->{passwordPolicySpecialChar} eq '__ALL__' + ? '' + : $self->conf->{passwordPolicySpecialChar}; $speChars =~ s/\s+/ /g; $speChars =~ s/(?:^\s|\s$)//g; + my $isPP = + $self->conf->{passwordPolicyMinSize} + || $self->conf->{passwordPolicyMinLower} + || $self->conf->{passwordPolicyMinUpper} + || $self->conf->{passwordPolicyMinDigit} + || $self->conf->{passwordPolicyMinSpeChar} + || $speChars; $self->logger->debug( 'Display called with code: ' . $req->error ); my %tplPrm = ( @@ -576,7 +620,8 @@ sub display { STARTMAILTIME => $req->data->{startMailTime}, MAILALREADYSENT => $req->data->{mailAlreadySent}, MAIL => ( - $self->p->checkXSSAttack( 'mail', $req->{user} ) ? '' + $self->p->checkXSSAttack( 'mail', $req->{user} ) + ? '' : $req->{user} ), DISPLAY_FORM => 0, @@ -584,17 +629,15 @@ sub display { DISPLAY_CONFIRMMAILSENT => 0, DISPLAY_MAILSENT => 0, DISPLAY_PASSWORD_FORM => 0, - DISPLAY_PPOLICY => $self->conf->{portalDisplayPasswordPolicy}, - PPOLICY_MINSIZE => $self->conf->{passwordPolicyMinSize}, - PPOLICY_MINLOWER => $self->conf->{passwordPolicyMinLower}, - PPOLICY_MINUPPER => $self->conf->{passwordPolicyMinUpper}, - PPOLICY_MINDIGIT => $self->conf->{passwordPolicyMinDigit}, - PPOLICY_ALLOWEDSPECHAR => $speChars, - ( - $speChars - ? ( PPOLICY_MINSPECHAR => $self->conf->{passwordPolicyMinSpeChar} ) - : () - ), + DISPLAY_PPOLICY => $isPP + && $self->conf->{portalDisplayPasswordPolicy} + && $self->passwordPolicyActivationRule->( $req, $req->sessionInfo ), + PPOLICY_MINSIZE => $self->conf->{passwordPolicyMinSize}, + PPOLICY_MINLOWER => $self->conf->{passwordPolicyMinLower}, + PPOLICY_MINUPPER => $self->conf->{passwordPolicyMinUpper}, + PPOLICY_MINDIGIT => $self->conf->{passwordPolicyMinDigit}, + PPOLICY_MINSPECHAR => $self->conf->{passwordPolicyMinSpeChar}, + PPOLICY_ALLOWEDSPECHAR => $speChars, DISPLAY_GENERATE_PASSWORD => $self->conf->{portalDisplayGeneratePassword}, ); From 1bc427043ad1bd028f2514ac2dabcbf0fbfd7888 Mon Sep 17 00:00:00 2001 From: Christophe Maudoux Date: Fri, 13 May 2022 00:22:10 +0200 Subject: [PATCH 5/9] Update doc (#2733) --- doc/sources/admin/resetpassword.rst | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/doc/sources/admin/resetpassword.rst b/doc/sources/admin/resetpassword.rst index 448a38d3d..b4f0c36db 100644 --- a/doc/sources/admin/resetpassword.rst +++ b/doc/sources/admin/resetpassword.rst @@ -84,8 +84,6 @@ Then go in Manager, ``General Parameters`` » ``Plugins`` » - **Display generate password box**: display a checkbox to allow user to generate a new password instead of choosing one (default: disabled) - -:: - - * **Regexp for password generation**: Regular expression used to generate the password (default: [A-Z]{3}[a-z]{5}.\d{2}) + - **Regexp for password generation**: Regular expression used to generate the password. Set a blank value to use + password policy if enabled or default regexp will be employed: [A-Z]{3}[a-z]{5}.\d{2} From dcc6f2cbc9b8e7bcb1826fe43d4075349a26115f Mon Sep 17 00:00:00 2001 From: Christophe Maudoux Date: Fri, 13 May 2022 22:08:28 +0200 Subject: [PATCH 6/9] Display policy if enabled (#2733) --- .../lib/Lemonldap/NG/Portal/Main/Display.pm | 27 +++++++++---------- .../NG/Portal/Plugins/MailPasswordReset.pm | 12 ++++----- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Display.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Display.pm index bd2aff221..0c76b0c75 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Display.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Display.pm @@ -268,13 +268,12 @@ sub display { DONT_STORE_PASSWORD => $self->conf->{browsersDontStorePassword}, HIDE_OLDPASSWORD => 0, PPOLICY_NOPOLICY => !$self->isPP(), - DISPLAY_PPOLICY => $self->conf->{portalDisplayPasswordPolicy} - && $self->passwordPolicyActivation->( $req, $req->sessionInfo ), - PPOLICY_MINSIZE => $self->conf->{passwordPolicyMinSize}, - PPOLICY_MINLOWER => $self->conf->{passwordPolicyMinLower}, - PPOLICY_MINUPPER => $self->conf->{passwordPolicyMinUpper}, - PPOLICY_MINDIGIT => $self->conf->{passwordPolicyMinDigit}, - PPOLICY_MINSPECHAR => $self->conf->{passwordPolicyMinSpeChar}, + DISPLAY_PPOLICY => $self->conf->{portalDisplayPasswordPolicy}, + PPOLICY_MINSIZE => $self->conf->{passwordPolicyMinSize}, + PPOLICY_MINLOWER => $self->conf->{passwordPolicyMinLower}, + PPOLICY_MINUPPER => $self->conf->{passwordPolicyMinUpper}, + PPOLICY_MINDIGIT => $self->conf->{passwordPolicyMinDigit}, + PPOLICY_MINSPECHAR => $self->conf->{passwordPolicyMinSpeChar}, ( $self->requireOldPwd->( $req, $req->userData ) ? ( REQUIRE_OLDPASSWORD => 1 ) @@ -484,18 +483,16 @@ sub display { CHOICE_PARAM => $self->conf->{authChoiceParam}, CHOICE_VALUE => $req->data->{_authChoice}, OLDPASSWORD => $self->checkXSSAttack( 'oldpassword', - $req->data->{oldpassword} ) ? "" + $req->data->{oldpassword} ) ? '' : $req->data->{oldpassword}, HIDE_OLDPASSWORD => $self->conf->{hideOldPassword}, DONT_STORE_PASSWORD => $self->conf->{browsersDontStorePassword}, PPOLICY_NOPOLICY => !$self->isPP(), - DISPLAY_PPOLICY => $self->conf->{portalDisplayPasswordPolicy} - && $self->passwordPolicyActivation->( $req, - $req->sessionInfo ), - PPOLICY_MINSIZE => $self->conf->{passwordPolicyMinSize}, - PPOLICY_MINLOWER => $self->conf->{passwordPolicyMinLower}, - PPOLICY_MINUPPER => $self->conf->{passwordPolicyMinUpper}, - PPOLICY_MINDIGIT => $self->conf->{passwordPolicyMinDigit}, + DISPLAY_PPOLICY => $self->conf->{portalDisplayPasswordPolicy}, + PPOLICY_MINSIZE => $self->conf->{passwordPolicyMinSize}, + PPOLICY_MINLOWER => $self->conf->{passwordPolicyMinLower}, + PPOLICY_MINUPPER => $self->conf->{passwordPolicyMinUpper}, + PPOLICY_MINDIGIT => $self->conf->{passwordPolicyMinDigit}, PPOLICY_MINSPECHAR => $self->conf->{passwordPolicyMinSpeChar}, ( $self->conf->{passwordPolicyMinSpeChar} || $self->speChars() diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/MailPasswordReset.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/MailPasswordReset.pm index eb40eb588..13be761b8 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/MailPasswordReset.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/MailPasswordReset.pm @@ -629,13 +629,11 @@ sub display { DISPLAY_CONFIRMMAILSENT => 0, DISPLAY_MAILSENT => 0, DISPLAY_PASSWORD_FORM => 0, - DISPLAY_PPOLICY => $isPP - && $self->conf->{portalDisplayPasswordPolicy} - && $self->passwordPolicyActivationRule->( $req, $req->sessionInfo ), - PPOLICY_MINSIZE => $self->conf->{passwordPolicyMinSize}, - PPOLICY_MINLOWER => $self->conf->{passwordPolicyMinLower}, - PPOLICY_MINUPPER => $self->conf->{passwordPolicyMinUpper}, - PPOLICY_MINDIGIT => $self->conf->{passwordPolicyMinDigit}, + DISPLAY_PPOLICY => $self->conf->{portalDisplayPasswordPolicy} && $isPP, + PPOLICY_MINSIZE => $self->conf->{passwordPolicyMinSize}, + PPOLICY_MINLOWER => $self->conf->{passwordPolicyMinLower}, + PPOLICY_MINUPPER => $self->conf->{passwordPolicyMinUpper}, + PPOLICY_MINDIGIT => $self->conf->{passwordPolicyMinDigit}, PPOLICY_MINSPECHAR => $self->conf->{passwordPolicyMinSpeChar}, PPOLICY_ALLOWEDSPECHAR => $speChars, DISPLAY_GENERATE_PASSWORD => From 5490f60f7465ea846f61045c5cfb09928dd5d663 Mon Sep 17 00:00:00 2001 From: Christophe Maudoux Date: Fri, 13 May 2022 23:27:52 +0200 Subject: [PATCH 7/9] Improve unit tests (#2733) --- .../t/43-MailPasswordReset-DBI.t | 23 ++++++-- .../t/43-MailPasswordReset-with-captcha.t | 15 ++++- lemonldap-ng-portal/t/43-MailPasswordReset.t | 57 +++++++++++++++---- 3 files changed, 75 insertions(+), 20 deletions(-) diff --git a/lemonldap-ng-portal/t/43-MailPasswordReset-DBI.t b/lemonldap-ng-portal/t/43-MailPasswordReset-DBI.t index 3293c6806..760841348 100644 --- a/lemonldap-ng-portal/t/43-MailPasswordReset-DBI.t +++ b/lemonldap-ng-portal/t/43-MailPasswordReset-DBI.t @@ -13,7 +13,7 @@ BEGIN { } my ( $res, $user, $pwd ); -my $maintests = 17; +my $maintests = 19; my $mailSend = 0; my $mail2 = 0; @@ -54,7 +54,12 @@ SKIP: { dbiAuthPasswordHash => '', dbiDynamicHashEnabled => 0, dbiMailCol => 'mail', + portalDisplayPasswordPolicy => 1, + passwordPolicyActivation => 0, passwordResetAllowedRetries => 4, + passwordPolicyMinDigit => 2, + passwordPolicyMinSpeChar => 1, + passwordPolicySpecialChar => '__ALL__' } } ); @@ -141,8 +146,16 @@ SKIP: { # Post new password ( $host, $url, $query ) = expectForm( $res, '#', undef, 'token' ); ok( $res->[2]->[0] =~ /newpassword/s, ' Ask for a new password #4' ); - - $query .= '&newpassword=zz&confirmpassword=zz'; + ok( + $res->[2]->[0] !~ /passwordPolicySpecialChar/, + ' Password special char list not found' + ); + ok( + $res->[2]->[0] =~ +/Minimal digit characters:<\/span> 2/, + ' Found password policy min digit == 2' + ); + $query .= '&newpassword=zz11#&confirmpassword=zz11#'; ok( $res = $client->_post( '/resetpwd', IO::String->new($query), @@ -157,8 +170,8 @@ SKIP: { ok( $res = $client->_post( '/', - IO::String->new('user=dwho&password=zz'), - length => 21 + IO::String->new('user=dwho&password=zz11#'), + length => 24 ), 'Auth query' ); diff --git a/lemonldap-ng-portal/t/43-MailPasswordReset-with-captcha.t b/lemonldap-ng-portal/t/43-MailPasswordReset-with-captcha.t index dbc765290..81e960e96 100644 --- a/lemonldap-ng-portal/t/43-MailPasswordReset-with-captcha.t +++ b/lemonldap-ng-portal/t/43-MailPasswordReset-with-captcha.t @@ -10,7 +10,7 @@ BEGIN { } my ( $res, $host, $url, $query ); -my $maintests = 16; +my $maintests = 18; my $mailSend = 0; my $mail2 = 0; @@ -33,6 +33,13 @@ SKIP: { requireToken => 1, portalDisplayResetPassword => 1, portalMainLogo => 'common/logos/logo_llng_old.png', + passwordPolicyActivation => 1, + passwordPolicyMinUpper => 1, + passwordPolicyMinLower => 1, + passwordPolicyMinDigit => 2, + passwordPolicyMinSpeChar => 1, + randomPasswordRegexp => '', + passwordPolicySpecialChar => '*#@' } } ); @@ -104,7 +111,7 @@ m#[2]->[0] =~ /newpassword/s, ' Ask for a new password' ); - $query .= '&newpassword=zz&confirmpassword=zz'; + $query .= '&reset=1'; # Post new password ok( @@ -115,7 +122,9 @@ m#Your new password is<\/span>/, 'New password sent' ); + ok( mail() =~ /(.+?)<\/b>/s, 'New generated password found' ); + ok( $1 =~ /[A-Z]{1}[a-z]{1}\d{2}[*#@]{1}/, 'New generated password matches' ); #print STDERR Dumper($query); } diff --git a/lemonldap-ng-portal/t/43-MailPasswordReset.t b/lemonldap-ng-portal/t/43-MailPasswordReset.t index bc456ca8a..7504c4226 100644 --- a/lemonldap-ng-portal/t/43-MailPasswordReset.t +++ b/lemonldap-ng-portal/t/43-MailPasswordReset.t @@ -10,7 +10,7 @@ BEGIN { } my ( $res, $user, $pwd ); -my $maintests = 12; +my $maintests = 18; SKIP: { eval @@ -21,15 +21,22 @@ SKIP: { my $client = LLNG::Manager::Test->new( { ini => { - logLevel => 'error', - useSafeJail => 1, - portalDisplayRegister => 1, - authentication => 'Demo', - userDB => 'Same', - passwordDB => 'Demo', - captcha_mail_enabled => 0, - portalDisplayResetPassword => 1, - portalMainLogo => 'common/logos/logo_llng_old.png', + logLevel => 'error', + useSafeJail => 1, + portalDisplayRegister => 1, + authentication => 'Demo', + userDB => 'Same', + passwordDB => 'Demo', + captcha_mail_enabled => 0, + portalDisplayResetPassword => 1, + portalMainLogo => 'common/logos/logo_llng_old.png', + portalDisplayPasswordPolicy => 1, + passwordPolicyActivation => 1, + passwordPolicyMinUpper => 1, + passwordPolicyMinLower => 1, + passwordPolicyMinDigit => 2, + passwordPolicyMinSpeChar => 1, + passwordPolicySpecialChar => '&%#' } } ); @@ -87,8 +94,34 @@ SKIP: { ); ( $host, $url, $query ) = expectForm( $res, '#', undef, 'token' ); ok( $res->[2]->[0] =~ /newpassword/s, ' Ask for a new password' ); - - $query .= '&newpassword=zz&confirmpassword=zz'; + ok( $res->[2]->[0] =~ //, + ' Found password policy' ); + ok( + $res->[2]->[0] =~ +/Minimal lower characters:<\/span> 1/, + ' Found password policy min lower == 1' + ); + ok( + $res->[2]->[0] =~ +/Minimal upper characters:<\/span> 1/, + ' Found password policy min upper == 1' + ); + ok( + $res->[2]->[0] =~ +/Minimal digit characters:<\/span> 2/, + ' Found password policy min digit == 2' + ); + ok( + $res->[2]->[0] =~ +/Minimal special characters:<\/span> 1/, + ' Found password policy min speChar == 1' + ); + ok( + $res->[2]->[0] =~ +/Allowed special characters:<\/span> &%#/, + ' Found password special char list' + ); + $query .= '&newpassword=zZ11#&confirmpassword=zZ11#'; # Post new password ok( From 7424751919ec7da4d74f870020e799b852c950cb Mon Sep 17 00:00:00 2001 From: Christophe Maudoux Date: Fri, 13 May 2022 23:28:09 +0200 Subject: [PATCH 8/9] Fix regex generator (#2733) --- .../lib/Lemonldap/NG/Portal/Plugins/MailPasswordReset.pm | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/MailPasswordReset.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/MailPasswordReset.pm index 13be761b8..4b93481ec 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/MailPasswordReset.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/MailPasswordReset.pm @@ -467,7 +467,11 @@ sub changePwd { $self->conf->{passwordPolicyMinLower} - $self->conf->{passwordPolicyMinDigit}; $chars = 1 if $chars < 1; - $pwdRegEx = "[A-Z]{$uppers}[a-z]{$lowers}\\d{$digits}.{$chars}"; + $pwdRegEx = "[A-Z]{$uppers}[a-z]{$lowers}\\d{$digits}"; + $pwdRegEx .= + $self->conf->{passwordPolicySpecialChar} eq '__ALL__' + ? "\\W{$chars}" + : "[$self->{conf}->{passwordPolicySpecialChar}]{$chars}"; $self->logger->debug("Generated password RegEx: $pwdRegEx"); } else { From 46c0cdd1bb0652f2f1f230f9390c0a0bb94ff237 Mon Sep 17 00:00:00 2001 From: Christophe Maudoux Date: Fri, 13 May 2022 23:28:21 +0200 Subject: [PATCH 9/9] Update doc --- doc/sources/admin/contribute.rst | 2 +- doc/sources/admin/prereq.rst | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/sources/admin/contribute.rst b/doc/sources/admin/contribute.rst index d5feef67e..fc7135182 100644 --- a/doc/sources/admin/contribute.rst +++ b/doc/sources/admin/contribute.rst @@ -58,7 +58,7 @@ As *root:* apt install aptitude aptitude install vim make devscripts yui-compressor git git-gui libjs-uglify coffeescript cpanminus autopkgtest pkg-perl-autopkgtest - aptitude install libauth-yubikey-webclient-perl libnet-smtp-server-perl libtime-fake-perl libtest-output-perl libtest-pod-perl libtest-leaktrace-perl libtest-mockobject-perl uglifyjs + aptitude install libauth-yubikey-webclient-perl libnet-smtp-server-perl libtime-fake-perl libtest-output-perl libtest-pod-perl libtest-leaktrace-perl libtest-mockobject-perl uglifyjs libdbd-sqlite3-perl libauthen-webauthn-perl libauthen-oath-perl cpanm Authen::U2F Authen::U2F::Tester Crypt::U2F::Server::Simple diff --git a/doc/sources/admin/prereq.rst b/doc/sources/admin/prereq.rst index 0173f9097..469b24c3f 100644 --- a/doc/sources/admin/prereq.rst +++ b/doc/sources/admin/prereq.rst @@ -95,6 +95,8 @@ Second factor - Crypt::U2F::Server::Simple (U2F keys) - Convert::Base32 (TOTP) +- Authen::WebAuthn (FIDO2 WebAuthen) +- Authen::OATH (OTP) Specific authentication backends ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -138,6 +140,7 @@ Unit tests - Authen::U2F::Tester - Crypt::U2F::Server - Test::MockObject +- DBD::SQLite - Test::Output - Test::POD - Time::Fake