[LON-CAPA-cvs] cvs: loncom /interface lonpreferences.pm resetpw.pm

raeburn raeburn at source.lon-capa.org
Sat Feb 8 23:43:21 EST 2020


raeburn		Sun Feb  9 04:43:21 2020 EDT

  Modified files:              
    /loncom/interface	lonpreferences.pm resetpw.pm 
  Log:
  - Rules for length and/or characters in password (internal) checked client-
    side when user sets new password via Preferences or Forgot Password utility.
  - Prompt retry setting new password after following e-mailed time-limited link
    (Forgot Password utility) when invalid username and/or e-mail address
    submitted.
  
  
-------------- next part --------------
Index: loncom/interface/lonpreferences.pm
diff -u loncom/interface/lonpreferences.pm:1.235 loncom/interface/lonpreferences.pm:1.236
--- loncom/interface/lonpreferences.pm:1.235	Wed Aug 21 22:41:13 2019
+++ loncom/interface/lonpreferences.pm	Sun Feb  9 04:43:20 2020
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # Preferences
 #
-# $Id: lonpreferences.pm,v 1.235 2019/08/21 22:41:13 raeburn Exp $
+# $Id: lonpreferences.pm,v 1.236 2020/02/09 04:43:20 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -1273,18 +1273,18 @@
         $r->print(Apache::loncommon::start_page('Personal Data'));
         $r->print(Apache::lonhtmlcommon::breadcrumbs('Change Password'));
     }
-    my ($blocked,$blocktext) =
-        &Apache::loncommon::blocking_status('passwd');
-    if ($blocked) {
-        $r->print('<p class="LC_warning">'.$blocktext.'</p>');
-        return;
-    }
     if ((!defined($caller)) || ($caller eq 'preferences')) {
         $user = $env{'user.name'};
         $domain = $env{'user.domain'};
         if (!defined($caller)) {
             $caller = 'preferences';
         }
+        my ($blocked,$blocktext) =
+            &Apache::loncommon::blocking_status('passwd');
+        if ($blocked) {
+            $r->print('<p class="LC_warning">'.$blocktext.'</p>');
+            return;
+        }
     } elsif ($caller eq 'reset_by_email') {
         my %data = &Apache::lonnet::tmpget($mailtoken);
         if (keys(%data) == 0) {
@@ -1301,6 +1301,12 @@
                 $user = $data{'username'};
                 $domain = $data{'domain'};
                 $currentpass = $data{'temppasswd'};
+                my ($blocked,$blocktext) =
+                    &Apache::loncommon::blocking_status('passwd',$user,$domain);
+                if ($blocked) {
+                    $r->print('<p class="LC_warning">'.$blocktext.'</p>');
+                    return;
+                }
             } else {
                 $r->print(
                     '<p class="LC_warning">'
@@ -1360,7 +1366,7 @@
 	my $jsh=Apache::File->new($include."/londes.js");
 	$r->print(<$jsh>);
     }
-    $r->print(&jscript_send($caller,$extrafields));
+    $r->print(&jscript_send($caller,$domain,$currentauth,$extrafields));
     $r->print(<<ENDFORM);
 $errormessage
 
@@ -1377,11 +1383,105 @@
 }
 
 sub jscript_send {
-    my ($caller,$extrafields) = @_;
+    my ($caller,$domain,$currentauth,$extrafields) = @_;
+    my ($min,$max,$rulestr,$numrules);
+    $min = $Apache::lonnet::passwdmin;
+    my %js_lt = &Apache::lonlocal::texthash(
+              uc => 'New password needs at least one upper case letter',
+              lc => 'New password needs at least one lower case letter',
+              num => 'New password needs at least one number',
+              spec => 'New password needs at least one non-alphanumeric',
+              blank1 => 'Empty Password field',
+              blank2 => 'Empty Confirm Password field',
+              mismatch => 'Contents of Password and Confirm Password fields must match',
+              fail => 'Please fix the following:',
+    );
+    &js_escape(\%js_lt);
+    if ($currentauth eq 'internal:') {
+        if ($domain ne '') {
+            my %passwdconf = &Apache::lonnet::get_passwdconf($domain);
+            if (keys(%passwdconf)) {
+                if ($passwdconf{min}) {
+                    $min = $passwdconf{min};
+                }
+                if ($passwdconf{max}) {
+                    $max = $passwdconf{max};
+                    $js_lt{'long'} = &js_escape(&mt('Maximum password length: [_1]',$max));
+                }
+                if (ref($passwdconf{chars}) eq 'ARRAY') {
+                    if (@{$passwdconf{chars}}) {
+                        $rulestr =  join('","',@{$passwdconf{chars}});
+                        $numrules = scalar(@{$passwdconf{chars}});
+                    }
+                }
+            }
+        }
+    }
+    $js_lt{'short'} = &js_escape(&mt('Minimum password length: [_1]',$min));
+
+    my $passwdcheck = <<"ENDJS";
+        var errors = new Array();
+        var min = parseInt("$min") || 0;
+        var currauth = "$currentauth";
+        if (this.document.client.elements.newpass_1.value == '') {
+            errors.push("$js_lt{'blank1'}");
+        }
+        if (this.document.client.elements.newpass_2.value == '') {
+            errors.push("$js_lt{'blank2'}");
+        }
+        if (errors.length == 0) {
+            if (this.document.client.elements.newpass_1.value !=  this.document.client.elements.newpass_2.value) {
+                errors.push("$js_lt{'mismatch'}");
+            }
+            var posspass = this.document.client.elements.newpass_1.value;
+            if (min > 0) {
+                if (posspass.length < min) {
+                    errors.push("$js_lt{'short'}");
+                }
+            }
+            if (currauth == 'internal:') {
+                var max = parseInt("$max") || 0;
+                if (max > 0) {
+                    if (posspass.length > max) {
+                        errors.push("$js_lt{'long'}");
+                    }
+                }
+                var numrules = parseInt("$numrules") || 0;
+                if (numrules > 0) {
+                    var rules = new Array("$rulestr");
+                    for (var i=0; i<rules.length; i++) {
+                        if (rules[i] == 'uc') {
+                            if (!posspass.match(/[A-Z]/)) {
+                                errors.push("$js_lt{'uc'}");
+                            }
+                        } else if (rules[i] == 'lc') {
+                            if (!posspass.match(/[a-z]/)) {
+                                errors.push("$js_lt{'lc'}");
+                            }
+                        } else if (rules[i] == 'num') {
+                            if (!posspass.match(/\\d/)) {
+                                errors.push("$js_lt{'num'}");
+                            }
+                        } else if (rules[i] == 'spec') {
+                            var pattern = /^[!@#$%^&*()_+\\-=\\[\\]{};':"\\\|,.<a>\\/?]/;
+                            if (!posspass.match(pattern)) {
+                                errors.push("$js_lt{'spec'}");
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        if (errors.length > 0) {
+            alert("$js_lt{'fail'}"+"\\n\\n"+errors.join("\\n"));
+            return;
+        }
+ENDJS
     my $output = qq|
 <script type="text/javascript" language="JavaScript">
 
     function send() {
+$passwdcheck
         uextkey=this.document.client.elements.ukey_cpass.value;
         lextkey=this.document.client.elements.lkey_cpass.value;
         initkeys();
@@ -1522,14 +1622,8 @@
 }
 
 sub verify_and_change_password {
-    my ($r,$caller,$mailtoken,$ended) = @_;
+    my ($r,$caller,$mailtoken,$timelimit,$extrafields,$ended) = @_;
     my ($user,$domain,$homeserver);
-    my ($blocked,$blocktext) =
-        &Apache::loncommon::blocking_status('passwd');
-    if ($blocked) {
-        $r->print('<p class="LC_warning">'.$blocktext.'</p>');
-        return;
-    }
     if ($caller eq 'reset_by_email') {
         $user       = $env{'form.uname'};
         $domain     = $env{'form.udom'};
@@ -1538,20 +1632,30 @@
             if ($homeserver eq 'no_host') {
         &passwordchanger($r,"<p>\n<span class='LC_error'>".
                          &mt("Invalid username and/or domain")."</span>\n</p>",
-                         $caller,$mailtoken);
-                return 1;
+                         $caller,$mailtoken,$timelimit,$extrafields);
+                return 'no_host';
             }
         } else {
             &passwordchanger($r,"<p>\n<span class='LC_error'>".
                              &mt("Username and domain were blank")."</span>\n</p>",
-                             $caller,$mailtoken);
-            return 1;
+                             $caller,$mailtoken,$timelimit,$extrafields);
+            return 'missingdata';
         }
     } else {
         $user       = $env{'user.name'};
         $domain     = $env{'user.domain'};
         $homeserver = $env{'user.home'};
     }
+    my ($blocked,$blocktext) =
+        &Apache::loncommon::blocking_status('passwd',$user,$domain);
+    if ($blocked) {
+        $r->print('<p class="LC_warning">'.$blocktext.'</p>');
+        if ($caller eq 'reset_by_email') {
+            return 'blocked';
+        } else {
+            return;
+        }
+    }
     my $currentauth=&Apache::lonnet::queryauthenticate($user,$domain);
     # Check for authentication types that allow changing of the password.
     if ($currentauth !~ /^(unix|internal):/) {
@@ -1559,8 +1663,8 @@
             &passwordchanger($r,"<p>\n<span class='LC_error'>".
                              &mt("Authentication type for this user can not be changed by this mechanism").
                              "</span>\n</p>",
-                              $caller,$mailtoken);
-            return 1;
+                              $caller,$mailtoken,$timelimit,$extrafields);
+            return 'otherauth';
         } else {
             return;
         }
@@ -1576,8 +1680,12 @@
 	    defined($newpass2)    ){
 	&passwordchanger($r,"<p>\n<span class='LC_error'>".
 			 &mt("One or more password fields were blank").
-                         "</span>\n</p>",$caller,$mailtoken);
-	return;
+                         "</span>\n</p>",$caller,$mailtoken,$timelimit,$extrafields);
+        if ($caller eq 'reset_by_email') {
+            return 'missingdata';
+        } else {
+            return;
+        }
     }
     # Get the keys
     my $lonhost = $r->dir_config('lonHostID');
@@ -1595,7 +1703,11 @@
 </p>
 ENDERROR
         # Probably should log an error here
-        return 1;
+        if ($caller eq 'reset_by_email') {
+            return 'internalerror';
+        } else {
+            return;
+        }
     }
     my ($ckey,$n1key,$n2key)=split(/&/,$tmpinfo);
     #
@@ -1609,31 +1721,39 @@
             &passwordchanger($r,
                          '<span class="LC_error">'.
                          &mt('Could not verify current authentication.').'  '.
-                         &mt('Please try again.').'</span>',$caller,$mailtoken);
-            return 1;
+                         &mt('Please try again.').'</span>',$caller,$mailtoken,$timelimit,$extrafields);
+            return 'emptydata';
         }
         if ($currentpass ne $data{'temppasswd'}) {
             &passwordchanger($r,
                          '<span class="LC_error">'.
                          &mt('Could not verify current authentication.').'  '.
-                         &mt('Please try again.').'</span>',$caller,$mailtoken);
-            return 1;
+                         &mt('Please try again.').'</span>',$caller,$mailtoken,$timelimit,$extrafields);
+            return 'missingtemp';
         }
     }
     if ($newpass1 ne $newpass2) {
 	&passwordchanger($r,
 			 '<span class="LC_warning">'.
 			 &mt('The new passwords you entered do not match.').'  '.
-			 &mt('Please try again.').'</span>',$caller,$mailtoken);
-	return 1;
+			 &mt('Please try again.').'</span>',$caller,$mailtoken,$timelimit,$extrafields);
+        if ($caller eq 'reset_by_email') {
+            return 'mismatch';
+        } else {
+            return;
+        }
     }
     if ($currentauth eq 'unix:') {
         if (length($newpass1) < 7) {
             &passwordchanger($r,
                              '<span class="LC_warning">'.
                              &mt('Passwords must be a minimum of 7 characters long.').'  '.
-                             &mt('Please try again.').'</span>',$caller,$mailtoken);
-            return 1;
+                             &mt('Please try again.').'</span>',$caller,$mailtoken,$timelimit,$extrafields);
+            if ($caller eq 'reset_by_email') {
+                return 'length';
+            } else {
+                return;
+            }
         }
     } else {
         my $warning = &Apache::loncommon::check_passwd_rules($domain,$newpass1);
@@ -1641,8 +1761,12 @@
             &passwordchanger($r,'<span class="LC_warning">'.
                             $warning.
                             &mt('Please try again.').'</span>',
-                            $caller,$mailtoken);
-            return 1;
+                            $caller,$mailtoken,$timelimit,$extrafields);
+            if ($caller eq 'reset_by_email') {
+                return 'rules';
+            } else {
+                return;
+            }
         }
     }
     #
@@ -1662,8 +1786,12 @@
 ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_\`abcdefghijklmnopqrstuvwxyz{|}~
 </pre></span>
 ENDERROR
-        &passwordchanger($r,$errormessage,$caller,$mailtoken);
-        return 1;
+        &passwordchanger($r,$errormessage,$caller,$mailtoken,$timelimit,$extrafields);
+        if ($caller eq 'reset_by_email') {
+            return 'badchars';
+        } else {
+            return;
+        }
     }
     # 
     # Change the password (finally)
@@ -1686,7 +1814,7 @@
 	# error error: run in circles, scream and shout
         if ($caller eq 'reset_by_email') {
             if (!$result) {
-                return 1;
+                return 'error';
             } else {
                 return $result;
             }
@@ -2318,7 +2446,7 @@
     }elsif($env{'form.action'} eq 'changepass'){
         &passwordchanger($r);
     }elsif($env{'form.action'} eq 'verify_and_change_pass'){
-        &verify_and_change_password($r,'preferences','',\$ended);
+        &verify_and_change_password($r,'preferences','','','',\$ended);
     }elsif($env{'form.action'} eq 'changescreenname'){
         &screennamechanger($r);
     }elsif($env{'form.action'} eq 'verify_and_change_screenname'){
Index: loncom/interface/resetpw.pm
diff -u loncom/interface/resetpw.pm:1.46 loncom/interface/resetpw.pm:1.47
--- loncom/interface/resetpw.pm:1.46	Fri Aug 30 00:09:39 2019
+++ loncom/interface/resetpw.pm	Sun Feb  9 04:43:20 2020
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # Allow access to password changing via a token sent to user's e-mail. 
 #
-# $Id: resetpw.pm,v 1.46 2019/08/30 00:09:39 raeburn Exp $
+# $Id: resetpw.pm,v 1.47 2020/02/09 04:43:20 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -639,7 +639,9 @@
                             $invalidinfo = "||$env{'form.uname'}|| ||$env{'form.udom'}|| ";
                         }
                     } else {
-                        unless ((lc($env{'form.uname'}) eq lc($data{'username'})) && (lc($env{'form.udom'}) eq lc($data{'domain'}))) {
+                        if ((lc($env{'form.uname'}) eq lc($data{'username'})) && (lc($env{'form.udom'}) eq lc($data{'domain'}))) {
+                            $env{'form.uname'} = $data{'username'};
+                        } else {
                             $invalidinfo = "||$env{'form.uname'}|| ||$env{'form.udom'}|| ";
                         }
                     }
@@ -661,7 +663,53 @@
                 }
                 if ($invalidinfo) {
                     &Apache::lonnet::logthis("Forgot Password -- token data: ||$data{'username'}|| ||$data{'domain'}|| ||$data{'email'}|| differs from form: $invalidinfo");
-                    $r->print(&generic_failure_msg($contact_name,$contact_email));
+                    my $retry;
+                    $r->print(
+                              '<p class="LC_warning">'
+                             .&mt('A problem occurred when attempting to reset'
+                             .' the password for your account.').'</p>');
+                    if (($formfields{'username'}) && ($formfields{'email'})) {
+                        if ($needscase) {
+                            $r->print('<p>'
+                                     .&mt('Please verify you entered the correct username and e-mail address, '
+                                     .'including the correct lower and/or upper case letters')
+                                     .'</p>');
+                        } else {
+                            $r->print('<p>'
+                                     .&mt('Please verify you entered the correct username and e-mail address.')
+                                     .'</p>');
+                        }
+                        $retry = 1;
+                    } elsif ($formfields{'username'}) {
+                        if ($needscase) {
+                            $r->print('<p>'
+                                     .&mt('Please verify you entered the correct username, '
+                                     .'including the correct lower and/or upper case letters')
+                                     .'</p>');
+                        } else {
+                            $r->print('<p>'
+                                     .&mt('Please verify you entered the correct username.')
+                                     .'</p>');
+                        }
+                        $retry = 1;
+                    } elsif ($formfields{'email'}) {
+                        if ($needscase) {
+                            $r->print('<p>'
+                                     .&mt('Please verify you entered the correct e-mail address, '
+                                     .'including the correct lower and/or upper case letters')
+                                     .'</p>');
+                        } else {
+                            $r->print('<p>'
+                                     .&mt('Please verify you entered the correct e-mail address.')
+                                     .'</p>');
+                        }
+                        $retry = 1;
+                    }
+                    if ($retry) {
+                         &Apache::lonpreferences::passwordchanger($r,'','reset_by_email',$token,$timelimit,\%formfields);
+                    } else {
+                        $r->print(&generic_failure_msg($contact_name,$contact_email));
+                    }
                     unless ($formfields{'username'}) {
                         delete($env{'form.uname'});
                         delete($env{'form.udom'});
@@ -669,7 +717,7 @@
                     return;
                 }
                 my $change_failed =
-		    &Apache::lonpreferences::verify_and_change_password($r,'reset_by_email',$token);
+		    &Apache::lonpreferences::verify_and_change_password($r,'reset_by_email',$token,$timelimit,\%formfields);
                 if (!$change_failed) {
                     my $delete = &Apache::lonnet::tmpdel($token);
                     my $now = &Apache::lonlocal::locallocaltime(time);
@@ -729,7 +777,8 @@
                          .'</p>'
                          .&display_actions($contact_email,$domdesc,$token)
                     );
-                } else {
+                } elsif (($change_failed eq 'internalerror') || ($change_failed eq 'missingtemp') ||
+                         ($change_failed eq 'error')) {
                     $r->print(&generic_failure_msg($contact_name,$contact_email));
                 }
                 unless ($formfields{'username'}) {
@@ -749,7 +798,20 @@
                     if ($needscase) {
                         $r->print(' '.&mt('User data entered must match LON-CAPA account information (including case).'));
                     }
-                    $r->print(' ');
+                    $r->print('<br />');
+                }
+                my ($min,$max,$minrule,$maxrule);
+                if ($passwdconf->{min}) {
+                    $min = $passwdconf->{min};
+                } else {
+                    $min = $Apache::lonnet::passwdmin;
+                }
+                if ($min) {
+                    $minrule = &mt('Minimum password length: [_1]',$min);
+                }
+                if ($passwdconf->{max}) {
+                    $max = $passwdconf->{max};
+                    $maxrule = &mt('Maximum password length: [_1]',$max);
                 }
                 if (ref($passwdconf->{chars}) eq 'ARRAY') {
                     my %rules;
@@ -766,9 +828,24 @@
                             $r->print('<li>'.$rulenames{$poss}.'</li>');
                         }
                     }
+                    if ($min) {
+                        $r->print('<li>'.$minrule.'</li>');
+                    }
+                    if ($max) {
+                        $r->print('<li>'.$maxrule.'</li>');
+                    }
                     $r->print('</ul>');
                 } else {
-                    $r->print(&mt('The new password must contain at least 7 characters.').' ');
+                    if ($min && $max) {
+                        $r->print(&mt('The new password must satisfy the following:').'<ul>'."\n".
+                                  '<li>'.$minrule.'</li>'."\n".
+                                  '<li>'.$maxrule.'</li>'."\n".
+                                  '</ul>'."\n");
+                    } elsif ($min) {
+                        $r->print($minrule.'<br />');
+                    } elsif ($max) {
+                        $r->print($maxrule.'<br />');
+                    }
                 }
                 $r->print(&mt('Your new password will be sent to the LON-CAPA server in an encrypted form.').'<br />');
                 &Apache::lonpreferences::passwordchanger($r,'','reset_by_email',$token,$timelimit,\%formfields);
@@ -776,7 +853,8 @@
         } else {
             $r->print(
                 '<p class="LC_warning">'
-               .&mt('Sorry, the token generated when you requested a password reset has expired. Please submit a [_1]new request[_2], and follow the link to the web page included in the new e-mail that will be sent to you, to allow you to enter a new password.'
+               .&mt('Sorry, the token generated when you requested a password reset has expired.').'<br />'
+               .&mt('Please submit a [_1]new request[_2], and follow the link to the web page included in the new e-mail that will be sent to you, to allow you to enter a new password.'
                     ,'<a href="/adm/resetpw">','</a>')
                .'</p>'
             );


More information about the LON-CAPA-cvs mailing list