--- loncom/interface/lonuserutils.pm	2017/04/10 23:18:54	1.183
+++ loncom/interface/lonuserutils.pm	2021/08/23 20:10:40	1.208
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # Utility functions for managing LON-CAPA user accounts
 #
-# $Id: lonuserutils.pm,v 1.183 2017/04/10 23:18:54 raeburn Exp $
+# $Id: lonuserutils.pm,v 1.208 2021/08/23 20:10:40 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -438,7 +438,7 @@ sub javascript_validations {
             } elsif ($context eq 'domain') {
                 $setsection_call = 'setCourse()';
                 $setsections_js = &dc_setcourse_js($param{'formname'},$mode,
-                                                   $context,$showcredits);
+                                                   $context,$showcredits,$domain);
             }
             $finish = "  var checkSec = $setsection_call\n".
                       "  if (checkSec == 'ok') {\n".
@@ -531,21 +531,26 @@ END
 /* regexp here to check for non \d \. in credits */
 END
     } else {
+        my ($numrules,$intargjs) =
+            &passwd_validation_js('vf.elements[current.argfield].value',$domain);
         $auth_checks .= (<<END);
     foundatype=1;
     if (current.argfield == null || current.argfield == '') {
+        // The login radiobutton checked does not have an associated textbox
+    } else if (vf.elements[current.argfield].value == '') {
         var alertmsg = '';
         switch (current.radiovalue) {
             case 'krb':
                 alertmsg = '$alert{'krb'}';
                 break;
             case 'loc':
-            case 'fsys':
+            case 'int':
                 alertmsg = '$alert{'ipass'}';
                 break;
             case 'fsys':
-                alertmsg = '';
+                alertmsg = '$alert{'ipass'}';
                 break;
+            case 'lti':
             default:
                 alertmsg = '';
         }
@@ -553,6 +558,10 @@ END
             alert(alertmsg);
             return;
         }
+    } else if (current.radiovalue == 'int') {
+        if ($numrules > 0) {
+$intargjs
+        }
     }
 END
     }
@@ -641,6 +650,138 @@ END
                  $section_checks.$authheader;
     return $result;
 }
+
+sub passwd_validation_js {
+    my ($currpasswdval,$domain) = @_;
+    my %passwdconf = &Apache::lonnet::get_passwdconf($domain);
+    my ($min,$max,@chars,$numrules,$intargjs,%alert);
+    $numrules = 0;
+    $min = $Apache::lonnet::passwdmin;
+    if (ref($passwdconf{'chars'}) eq 'ARRAY') {
+        if ($passwdconf{'min'} =~ /^\d+$/) {
+            if ($passwdconf{'min'} > $min) {
+                $min = $passwdconf{'min'};
+            }
+        }
+        if ($passwdconf{'max'} =~ /^\d+$/) {
+            $max = $passwdconf{'max'};
+            $numrules ++;
+        }
+        @chars = @{$passwdconf{'chars'}};
+        if (@chars) {
+            $numrules ++;
+        }
+    }
+    if ($min > 0) {
+        $numrules ++;
+    }
+    if (($min > 0) || ($max ne '') || (@chars > 0)) {
+        my $alertmsg = &mt('Initial password did not satisfy requirement(s):').'\n\n';
+        if ($min) {
+            $alert{'min'} = &mt('minimum [quant,_1,character]',$min).'\n';
+        }
+        if ($max) {
+            $alert{'max'} = &mt('maximum [quant,_1,character]',$max).'\n';
+        }
+        my (@charalerts,@charrules);
+        if (@chars) {
+            if (grep(/^uc$/,@chars)) {
+                push(@charalerts,&mt('contain at least one upper case letter'));
+                push(@charrules,'uc');
+            }
+            if (grep(/^lc$/,@chars)) {
+                push(@charalerts,&mt('contain at least one lower case letter'));
+                push(@charrules,'lc');
+            }
+            if (grep(/^num$/,@chars)) {
+                push(@charalerts,&mt('contain at least one number'));
+                push(@charrules,'num');
+            }
+            if (grep(/^spec$/,@chars)) {
+                push(@charalerts,&mt('contain at least one non-alphanumeric'));
+                push(@charrules,'spec');
+            }
+        }
+        $intargjs = qq|            var rulesmsg = '';\n|.
+                    qq|            var currpwval = $currpasswdval;\n|;
+            if ($min) {
+                $intargjs .= qq|
+            if (currpwval.length < $min) {
+                rulesmsg += ' - $alert{min}';
+            }
+|;
+            }
+            if ($max) {
+                $intargjs .= qq|
+            if (currpwval.length > $max) {
+                rulesmsg += ' - $alert{max}';
+            }
+|;
+            }
+            if (@chars > 0) {
+                my $charrulestr = '"'.join('","',@charrules).'"';
+                my $charalertstr = '"'.join('","',@charalerts).'"';
+                $intargjs .= qq|            var brokerules = new Array();\n|.
+                             qq|            var charrules = new Array($charrulestr);\n|.
+                             qq|            var charalerts = new Array($charalertstr);\n|;
+                my %rules;
+                map { $rules{$_} = 1; } @chars;
+                if ($rules{'uc'}) {
+                    $intargjs .= qq|
+            var ucRegExp = /[A-Z]/;
+            if (!ucRegExp.test(currpwval)) {
+                brokerules.push('uc');
+            }
+|;
+                }
+                if ($rules{'lc'}) {
+                    $intargjs .= qq|
+            var lcRegExp = /[a-z]/;
+            if (!lcRegExp.test(currpwval)) {
+                brokerules.push('lc');
+            }
+|;
+                }
+                if ($rules{'num'}) {
+                     $intargjs .= qq|
+            var numRegExp = /[0-9]/;
+            if (!numRegExp.test(currpwval)) {
+                brokerules.push('num');
+            }
+|;
+                }
+                if ($rules{'spec'}) {
+                     $intargjs .= q|
+            var specRegExp = /[!"#$%&'()*+,\-.\/:;<=>?@[\\^\]_`{\|}~]/;
+            if (!specRegExp.test(currpwval)) {
+                brokerules.push('spec');
+            }
+|;
+                }
+                $intargjs .= qq|
+            if (brokerules.length > 0) {
+                for (var i=0; i<brokerules.length; i++) {
+                    for (var j=0; j<charrules.length; j++) {
+                        if (brokerules[i] == charrules[j]) {
+                            rulesmsg += ' - '+charalerts[j]+'\\n';
+                            break;
+                        }
+                    }
+                }
+            }
+|;
+            }
+            $intargjs .= qq|
+            if (rulesmsg != '') {
+                rulesmsg = '$alertmsg'+rulesmsg;
+                alert(rulesmsg);
+                return false;
+            }
+|;
+    }
+    return ($numrules,$intargjs);
+}
+
 ###############################################################
 ###############################################################
 sub upload_manager_javascript_forward_associate {
@@ -898,6 +1039,7 @@ sub print_upload_manager_footer {
     my $krbform = &Apache::loncommon::authform_kerberos(%param);
     my $intform = &Apache::loncommon::authform_internal(%param);
     my $locform = &Apache::loncommon::authform_local(%param);
+    my $ltiform = &Apache::loncommon::authform_lti(%param);
     my $date_table = &date_setting_table(undef,undef,$context,undef,
                                          $formname,$permission,$crstype);
 
@@ -926,7 +1068,7 @@ sub print_upload_manager_footer {
             &Apache::loncommon::help_open_topic('Auth_Options').
             "</p>\n";
     }
-    $Str .= &set_login($defdom,$krbform,$intform,$locform);
+    $Str .= &set_login($defdom,$krbform,$intform,$locform,$ltiform);
 
     my ($home_server_pick,$numlib) =
         &Apache::loncommon::home_server_form_item($defdom,'lcserver',
@@ -943,8 +1085,14 @@ sub print_upload_manager_footer {
                 &Apache::lonhtmlcommon::row_closure();
     }
 
+    my ($trusted,$untrusted);
+    if ($context eq 'course') {
+        ($trusted,$untrusted) = &Apache::lonnet::trusted_domains('enroll',$defdom);
+    } elsif ($context eq 'author') {
+        ($trusted,$untrusted) = &Apache::lonnet::trusted_domains('othcoau',$defdom);
+    }
     $Str .= &Apache::lonhtmlcommon::row_title(&mt('Default domain'))
-           .&Apache::loncommon::select_dom_form($defdom,'defaultdomain',undef,1)
+           .&Apache::loncommon::select_dom_form($defdom,'defaultdomain',undef,1,undef,$trusted,$untrusted)
            .&Apache::lonhtmlcommon::row_closure();
 
     $Str .= &Apache::lonhtmlcommon::row_title(&mt('Starting and Ending Dates'))
@@ -1121,8 +1269,15 @@ sub print_upload_manager_form {
     if (!$env{'form.datatoken'}) {
         $datatoken=&Apache::loncommon::upfile_store($r);
     } else {
-        $datatoken=$env{'form.datatoken'};
-        &Apache::loncommon::load_tmp_file($r);
+        $datatoken=&Apache::loncommon::valid_datatoken($env{'form.datatoken'});
+        if ($datatoken ne '') {
+            &Apache::loncommon::load_tmp_file($r,$datatoken);
+        }
+    }
+    if ($datatoken eq '') {
+        $r->print('<p class="LC_error">'.&mt('Error').': '.
+                  &mt('Invalid datatoken').'</p>');
+        return 'missingdata';
     }
     my @records=&Apache::loncommon::upfile_record_sep();
     if($env{'form.noFirstLine'}){
@@ -1206,6 +1361,7 @@ sub print_upload_manager_form {
     }
     &print_upload_manager_footer($r,$i,$keyfields,$defdom,$today,$halfyear,
                                  $context,$permission,$crstype,$showcredits);
+    return 'ok';
 }
 
 sub setup_date_selectors {
@@ -1553,10 +1709,11 @@ sub my_custom_roles {
     my %rolehash=&Apache::lonnet::dump('roles',$udom,$uname);
     foreach my $key (keys(%rolehash)) {
         if ($key=~/^rolesdef\_(\w+)$/) {
+            my $role = $1;
             if ($crstype eq 'Community') {
                 next if ($rolehash{$key} =~ /bre\&S/); 
             }
-            $returnhash{$1}=$1;
+            $returnhash{$role}=$role;
         }
     }
     return %returnhash;
@@ -2279,7 +2436,6 @@ sub build_user_record {
 
 sub courses_selector {
     my ($cdom,$formname) = @_;
-    my %coursecodes = ();
     my %codes = ();
     my @codetitles = ();
     my %cat_titles = ();
@@ -2292,14 +2448,15 @@ sub courses_selector {
     my $jscript = '';
 
     my $totcodes = 0;
-    $totcodes =
-        &Apache::courseclassifier::retrieve_instcodes(\%coursecodes,
-                                                      $cdom,$totcodes);
-    if ($totcodes > 0) {
-        $format_reply =
-             &Apache::lonnet::auto_instcode_format($caller,$cdom,\%coursecodes,
-                                \%codes,\@codetitles,\%cat_titles,\%cat_order);
-        if ($format_reply eq 'ok') {
+    my $instcats = &Apache::lonnet::get_dom_instcats($cdom);
+    if (ref($instcats) eq 'HASH') {
+        if ((ref($instcats->{'codetitles'}) eq 'ARRAY') && (ref($instcats->{'codes'}) eq 'HASH') &&
+            (ref($instcats->{'cat_titles'}) eq 'HASH') && (ref($instcats->{'cat_order'}) eq 'HASH')) {
+            %codes = %{$instcats->{'codes'}};
+            @codetitles = @{$instcats->{'codetitles'}};
+            %cat_titles = %{$instcats->{'cat_titles'}};
+            %cat_order = %{$instcats->{'cat_order'}};
+            $totcodes = scalar(keys(%codes));
             my $numtypes = @codetitles;
             &Apache::courseclassifier::build_code_selections(\%codes,\@codetitles,\%cat_titles,\%cat_order,\%idlist,\%idnums,\%idlist_titles);
             my ($scripttext,$longtitles) = &Apache::courseclassifier::javascript_definitions(\@codetitles,\%idlist,\%idlist_titles,\%idnums,\%cat_titles);
@@ -2336,7 +2493,8 @@ function setCourseCat(formname) {
     }
     courseSet('$codetitles[1]');
     for (var j=0; j<formname.Department.length; j++) {
-        if (formname.Department.options[j].value == "$env{'form.Department'}") {            formname.Department.options[j].selected = true;
+        if (formname.Department.options[j].value == "$env{'form.Department'}") {
+            formname.Department.options[j].selected = true;
         }
     }
     if (formname.Department.options[formname.Department.selectedIndex].value == -1) {
@@ -2757,7 +2915,7 @@ END
                .'<input type="checkbox" name="userwin" value="1"'.$checkwin.' />'.$lt{'owin'}
                .'</label></span></td></tr></table></fieldset></div>';
         }
-        $output .= "\n".'<br clear="all" />'."\n".
+        $output .= "\n".'<div style="padding:0;clear:both;margin:0;border:0"></div>'."\n".
                   &Apache::loncommon::start_data_table().
                   &Apache::loncommon::start_data_table_header_row();
         if ($mode eq 'autoenroll') {
@@ -3512,6 +3670,8 @@ END
         setSections(formname,'$crstype');
         if (seccheck == 'ok') {
             opener.document.$callingform.newsecs.value = formname.sections.value;
+        } else {
+            return;
         }
 END
     } else {
@@ -4096,7 +4256,7 @@ sub print_first_users_upload_form {
            .&Apache::lonhtmlcommon::end_pick_box();
 
     $str .= '<p>'
-           .'<input type="submit" name="fileupload" value="'.&mt('Next').'"'
+           .'<input type="button" name="fileupload" value="'.&mt('Next').'"'
            .' onclick="javascript:checkUpload(this.form);" />'
            .'</p>';
 
@@ -4107,7 +4267,10 @@ sub print_first_users_upload_form {
 # ================================================= Drop/Add from uploaded file
 sub upfile_drop_add {
     my ($r,$context,$permission,$showcredits) = @_;
-    &Apache::loncommon::load_tmp_file($r);
+    my $datatoken = &Apache::loncommon::valid_datatoken($env{'form.datatoken'});
+    if ($datatoken ne '') {
+        &Apache::loncommon::load_tmp_file($r,$datatoken);
+    }
     my @userdata=&Apache::loncommon::upfile_record_sep();
     if($env{'form.noFirstLine'}){shift(@userdata);}
     my @keyfields = split(/\,/,$env{'form.keyfields'});
@@ -4121,10 +4284,6 @@ sub upfile_drop_add {
             $fields{$env{'form.f'.$i}}=$keyfields[$i];
         }
     }
-    if ($env{'form.fullup'} ne 'yes') {
-        $r->print('<form name="studentform" method="post" action="/adm/createuser">'."\n".
-                  '<input type="hidden" name="action" value="'.$env{'form.action'}.'" />');
-    }
     #
     # Store the field choices away
     my @storefields = qw/username names fname mname lname gen id 
@@ -4138,17 +4297,19 @@ sub upfile_drop_add {
         $fieldstype{$field.'_choice'} = 'scalar';
     }
     &Apache::loncommon::store_course_settings('enrollment_upload',\%fieldstype);
-    my ($cid,$crstype,$setting);
+    my ($cid,$crstype,$setting,$crsdom);
     if ($context eq 'domain') {
         $setting = $env{'form.roleaction'};
     }
     if ($env{'request.course.id'} ne '') {
         $cid = $env{'request.course.id'};
         $crstype = &Apache::loncommon::course_type();
+        $crsdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
     } elsif ($setting eq 'course') {
         if (&Apache::lonnet::is_course($env{'form.dcdomain'},$env{'form.dccourse'})) {
             $cid = $env{'form.dcdomain'}.'_'.$env{'form.dccourse'};
             $crstype = &Apache::loncommon::course_type($cid);
+            $crsdom = $env{'form.dcdomain'};
         }
     }
     my ($startdate,$enddate) = &get_dates_from_form();
@@ -4159,7 +4320,47 @@ sub upfile_drop_add {
     my $defdom=$env{'request.role.domain'};
     my $domain;
     if ($env{'form.defaultdomain'} ne '') {
-        $domain = $env{'form.defaultdomain'};
+        if (($context eq 'course') || ($setting eq 'course')) {
+            if ($env{'form.defaultdomain'} eq $crsdom) {
+                $domain = $env{'form.defaultdomain'};
+            } else {
+                if (&Apache::lonnet::will_trust('enroll',$crsdom,$env{'form.defaultdomain'})) {
+                    $domain = $env{'form.defaultdomain'};
+                } else {
+                    $r->print('<span class="LC_error">'.&mt('Error').': '.
+                              &mt('Enrollment of users not permitted for specified default domain: [_1].',
+                                  &Apache::lonnet::domain($env{'form.defaultdomain'},'description')).'</span>');
+                    return 'untrusted';
+                }
+            }
+        } elsif ($context eq 'author') {
+            if ($env{'form.defaultdomain'} eq $defdom) {
+                $domain = $env{'form.defaultdomain'}; 
+            } else {
+                if ((&Apache::lonnet::will_trust('othcoau',$defdom,$env{'form.defaultdomain'})) &&
+                    (&Apache::lonnet::will_trust('coaurem',$env{'form.defaultdomain'},$defdom))) {
+                    $domain = $env{'form.defaultdomain'};
+                } else {
+                    $r->print('<span class="LC_error">'.&mt('Error').': '.
+                              &mt('Addition of users not permitted for specified default domain: [_1].',
+                                  &Apache::lonnet::domain($env{'form.defaultdomain'},'description')).'</span>');
+                    return 'untrusted';
+                }
+            }
+        } elsif (($context eq 'domain') && ($setting eq 'domain')) {
+            if ($env{'form.defaultdomain'} eq $defdom) {
+                $domain = $env{'form.defaultdomain'};
+            } else {
+                if (&Apache::lonnet::will_trust('domroles',$defdom,$env{'form.defaultdomain'})) {
+                    $domain = $env{'form.defaultdomain'};
+                } else {
+                    $r->print('<span class="LC_error">'.&mt('Error').': '.
+                              &mt('Addition of users not permitted for specified default domain: [_1].',
+                                  &Apache::lonnet::domain($env{'form.defaultdomain'},'description')).'</span>');
+                    return 'untrusted';
+                }
+            }
+        }
     } else {
         $domain = $defdom;
     }
@@ -4169,10 +4370,9 @@ sub upfile_drop_add {
     } else {
         my %home_servers = &Apache::lonnet::get_servers($defdom,'library');
         if (! exists($home_servers{$desiredhost})) {
-            $r->print('<span class="LC_error">'.&mt('Error').
-                      &mt('Invalid home server specified').'</span>');
-            $r->print(&Apache::loncommon::end_page());
-            return;
+            $r->print('<p class="LC_error">'.&mt('Error').': '.
+                      &mt('Invalid home server specified').'</p>');
+            return 'invalidhome';
         }
     }
     # Determine authentication mechanism
@@ -4182,6 +4382,7 @@ sub upfile_drop_add {
     }
     my $amode  = '';
     my $genpwd = '';
+    my @genpwdfail;
     if ($env{'form.login'} eq 'krb') {
         $amode='krb';
         $amode.=$env{'form.krbver'};
@@ -4190,12 +4391,16 @@ sub upfile_drop_add {
         $amode='internal';
         if ((defined($env{'form.intarg'})) && ($env{'form.intarg'})) {
             $genpwd=$env{'form.intarg'};
+            @genpwdfail =
+                &Apache::loncommon::check_passwd_rules($domain,$genpwd);
         }
     } elsif ($env{'form.login'} eq 'loc') {
         $amode='localauth';
         if ((defined($env{'form.locarg'})) && ($env{'form.locarg'})) {
             $genpwd=$env{'form.locarg'};
         }
+    } elsif ($env{'form.login'} eq 'lti') {
+        $amode='lti';
     }
     if ($amode =~ /^krb/) {
         if (! defined($genpwd) || $genpwd eq '') {
@@ -4268,10 +4473,14 @@ sub upfile_drop_add {
                                                   \@statuses,\@poss_roles);
                 &gather_userinfo($context,'view',\%userlist,$indexhash,\%info,
                              \%cstr_roles,$permission);
-
             }
         }
     }
+    if ($datatoken eq '') {
+        $r->print('<p class="LC_error">'.&mt('Error').': '.
+                  &mt('Invalid datatoken').'</p>');
+        return 'missingdata';
+    }
     if ( $domain eq &LONCAPA::clean_domain($domain)
         && ($amode ne '')) {
         #######################################
@@ -4341,8 +4550,10 @@ sub upfile_drop_add {
         my $newuserdom = $env{'request.role.domain'};
         map { $cancreate{$_} = &can_create_user($newuserdom,$context,$_); } keys(%longtypes);
         # Get new users list
-        my (%existinguser,%userinfo,%disallow,%rulematch,%inst_results,%alerts,%checkuname);
+        my (%existinguser,%userinfo,%disallow,%rulematch,%inst_results,%alerts,%checkuname,
+            %showpasswdrules,$haspasswdmap);
         my $counter = -1;
+        my (%willtrust,%trustchecked);
         foreach my $line (@userdata) {
             $counter ++;
             my @secs;
@@ -4390,6 +4601,28 @@ sub upfile_drop_add {
                                 '"<b>'.$entries{$fields{'domain'}}.'</b>"',
                                 $fname,$mname,$lname,$gen);
                         next;
+                    } elsif ($entries{$fields{'domain'}} ne $domain) {
+                        my $possdom = $entries{$fields{'domain'}};
+                        if ($context eq 'course' || $setting eq 'course') {
+                            unless ($trustchecked{$possdom}) {
+                                $willtrust{$possdom} = &Apache::lonnet::will_trust('enroll',$domain,$possdom);
+                                $trustchecked{$possdom} = 1;
+                            }
+                        } elsif ($context eq 'author') {
+                            unless ($trustchecked{$possdom}) {
+                                $willtrust{$possdom} = &Apache::lonnet::will_trust('othcoau',$domain,$possdom);
+                            }
+                            if ($willtrust{$possdom}) {
+                                $willtrust{$possdom} = &Apache::lonnet::will_trust('coaurem',$possdom,$domain); 
+                            }
+                        }
+                        unless ($willtrust{$possdom}) {
+                            $disallow{$counter} =
+                                &mt('Unacceptable domain [_1] for user [_2] [_3] [_4] [_5]',
+                                    '"<b>'.$possdom.'</b>"',
+                                    $fname,$mname,$lname,$gen);
+                            next;
+                        }
                     }
                     my $username = $entries{$fields{'username'}};
                     my $userdomain = $entries{$fields{'domain'}};
@@ -4469,11 +4702,43 @@ sub upfile_drop_add {
                         }
                     }
                     # determine user password
-                    my $password = $genpwd;
+                    my $password;
+                    my $passwdfromfile;
                     if (defined($fields{'ipwd'})) {
                         if ($entries{$fields{'ipwd'}}) {
                             $password=$entries{$fields{'ipwd'}};
+                            $passwdfromfile = 1;
+                            if ($env{'form.login'} eq 'int') {
+                                my $uhome=&Apache::lonnet::homeserver($username,$userdomain);
+                                if (($uhome eq 'no_host') || ($changeauth)) {
+                                    my @brokepwdrules =
+                                        &Apache::loncommon::check_passwd_rules($domain,$password);
+                                    if (@brokepwdrules) {
+                                        $disallow{$counter} = &mt('[_1]: Password included in file for this user did not meet requirements.',
+                                                                  '<b>'.$username.'</b>');
+                                        map { $showpasswdrules{$_} = 1; } @brokepwdrules;
+                                        next;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    unless ($passwdfromfile) {
+                        if ($env{'form.login'} eq 'int') {
+                            if (@genpwdfail) {
+                                my $uhome=&Apache::lonnet::homeserver($username,$userdomain);
+                                if (($uhome eq 'no_host') || ($changeauth)) {
+                                    $disallow{$counter} = &mt('[_1]: No specific password in file for this user; default password did not meet requirements',
+                                                              '<b>'.$username.'</b>');
+                                    unless ($haspasswdmap) {
+                                        map { $showpasswdrules{$_} = 1; } @genpwdfail;
+                                        $haspasswdmap = 1;
+                                    }
+                                }
+                                next;
+                            }
                         }
+                        $password = $genpwd;
                     }
                     # determine user role
                     my $role = '';
@@ -4542,7 +4807,7 @@ sub upfile_drop_add {
                                 &mt('The user does not already exist, and you may not create a new user in a different domain.');
                             next;
                         } else {
-                            unless ($password || $env{'form.login'} eq 'loc') {
+                            unless (($password ne '') || ($env{'form.login'} eq 'loc') || ($env{'form.login'} eq 'lti')) {
                                 $disallow{$counter} =
                                     &mt('[_1]: This is a new user but no default password was provided, and the authentication type requires one.',
                                         '<b>'.$username.'</b>');
@@ -4745,6 +5010,16 @@ sub upfile_drop_add {
                     my (%userres,%authres,%roleres,%idres);
                     my $singlesec = '';
                     if ($role eq 'st') {
+                        if (($context eq 'domain') && ($changeauth eq 'Yes') && (!$newuser)) {
+                            if ((&Apache::lonnet::allowed('mau',$userdomain)) &&
+                                (&Apache::lonnet::homeserver($username,$userdomain) ne 'no_host')) {
+                                if ((($amode =~ /^krb4|krb5|internal$/) && $password ne '') ||
+                                     ($amode eq 'localauth')) {
+                                    $authresult =
+                                        &Apache::lonnet::modifyuserauth($userdomain,$username,$amode,$password);
+                                }
+                            }
+                        }
                         my $sec;
                         if (ref($userinfo{$i}{'sections'}) eq 'ARRAY') {
                             if (@secs > 0) {
@@ -4786,16 +5061,16 @@ sub upfile_drop_add {
                                     }
                                 }
                             }
-                            if (!$multiple) {
-                                ($userresult,$authresult,$roleresult,$idresult) = 
-                                    &modifyuserrole($context,$setting,
-                                                    $changeauth,$cid,$userdomain,$username, 
-                                                    $id,$amode,$password,$fname,
-                                                    $mname,$lname,$gen,$singlesec,
-                                                    $env{'form.forceid'},$desiredhost,
-                                                    $email,$role,$enddate,$startdate,
-                                                    $checkid,$inststatus);
-                            }
+                        }
+                        if (!$multiple) {
+                            ($userresult,$authresult,$roleresult,$idresult) = 
+                                &modifyuserrole($context,$setting,
+                                                $changeauth,$cid,$userdomain,$username, 
+                                                $id,$amode,$password,$fname,
+                                                $mname,$lname,$gen,$singlesec,
+                                                $env{'form.forceid'},$desiredhost,
+                                                $email,$role,$enddate,$startdate,
+                                                $checkid,$inststatus);
                         }
                     }
                     if ($multiple) {
@@ -4824,7 +5099,9 @@ sub upfile_drop_add {
                   "</p>\n");
         if ($counts{'role'} > 0) {
             $r->print("<p>\n".
-                      &mt('Roles added for [quant,_1,user].',$counts{'role'}).' '.&mt('If a user is currently logged-in to LON-CAPA, any new roles which are active will be available when the user next logs in.')."</p>\n");
+                      &mt('Roles added for [quant,_1,user].',$counts{'role'}).' '.
+                      &mt('If a user is currently logged-in to LON-CAPA, any new roles which are active will be available when the user next logs in.').
+                      "</p>\n");
         } else {
             $r->print('<p>'.&mt('No roles added').'</p>');
         }
@@ -4834,6 +5111,7 @@ sub upfile_drop_add {
                           $counts{'auth'})."</p>\n");
         }
         $r->print(&print_namespacing_alerts($domain,\%alerts,\%curr_rules));
+        $r->print(&passwdrule_alerts($domain,\%showpasswdrules));
         #####################################
         # Display list of students to drop  #
         #####################################
@@ -4842,10 +5120,9 @@ sub upfile_drop_add {
             #  Get current classlist
             my $classlist = &Apache::loncoursedata::get_classlist();
             if (! defined($classlist)) {
-                $r->print('<form name="studentform" method="post" action="/adm/createuser">'.
-                          '<input type="hidden" name="action" value="'.$env{'form.action'}.'" />'.
-                          '<p class="LC_info">'.&mt('There are no students with current/future access to the course.').'</p>'.
-                          '</form>'."\n");
+                $r->print('<p class="LC_info">'.
+                          &mt('There are no students with current/future access to the course.').
+                          '</p>'."\n");
             } elsif (ref($classlist) eq 'HASH') {
                 # Remove the students we just added from the list of students.
                 foreach my $line (@userdata) {
@@ -4861,9 +5138,7 @@ sub upfile_drop_add {
             }
         }
     } # end of unless
-    if ($env{'form.fullup'} ne 'yes') {
-        $r->print('</form>');
-    }
+    return 'ok';
 }
 
 sub print_namespacing_alerts {
@@ -4906,6 +5181,42 @@ sub print_namespacing_alerts {
     }
 }
 
+sub passwdrule_alerts {
+    my ($domain,$passwdrules) = @_;
+    my $warning;
+    if (ref($passwdrules) eq 'HASH') {
+        my %showrules = %{$passwdrules};
+        if (keys(%showrules)) {
+            my %passwdconf = &Apache::lonnet::get_passwdconf($domain);
+            $warning = '<b>'.&mt('Password requirement(s) unmet for one or more users:').'</b><ul>';
+            if ($showrules{'min'}) {
+                my $min = $passwdconf{'min'};
+                if ($min eq '') {
+                    $min = $Apache::lonnet::passwdmin;
+                }
+                $warning .= '<li>'.&mt('minimum [quant,_1,character]',$min).'</li>';
+            }
+            if ($showrules{'max'}) {
+                $warning .= '<li>'.&mt('maximum [quant,_1,character]',$passwdconf{'max'}).'</li>';
+            }
+            if ($showrules{'uc'}) {
+                $warning .= '<li>'.&mt('contain at least one upper case letter').'</li>';
+            }
+            if ($showrules{'lc'}) {
+                $warning .= '<li>'.&mt('contain at least one lower case letter').'</li>';
+            }
+            if ($showrules{'num'}) {
+                $warning .= '<li>'.&mt('contain at least one number').'</li>';
+            }
+            if ($showrules{'spec'}) {
+                $warning .= '<li>'.&mt('contain at least one non-alphanumeric').'</li>';
+            }
+            $warning .= '</ul>';
+        }
+    }
+    return $warning;
+}
+
 sub user_change_result {
     my ($r,$userresult,$authresult,$roleresult,$idresult,$counts,$flushc,
         $username,$userdomain,$userchg) = @_;
@@ -5409,7 +5720,7 @@ END
 }
 
 sub set_login {
-    my ($dom,$authformkrb,$authformint,$authformloc) = @_;
+    my ($dom,$authformkrb,$authformint,$authformloc,$authformlti) = @_;
     my %domconfig = &Apache::lonnet::get_dom('configuration',['usercreation'],$dom);
     my $response;
     my ($authnum,%can_assign) =
@@ -5431,6 +5742,11 @@ sub set_login {
                          '<td>'.$authformloc.'</td>'.
                          &Apache::loncommon::end_data_table_row()."\n";
         }
+        if ($can_assign{'lti'}) {
+            $response .= &Apache::loncommon::start_data_table_row().
+                         '<td>'.$authformlti.'</td>'.
+                         &Apache::loncommon::end_data_table_row()."\n";
+        }
         $response .= &Apache::loncommon::end_data_table();
     }
     return $response;
@@ -5775,6 +6091,70 @@ sub can_modify_userinfo {
     return %canmodify;
 }
 
+sub can_change_internalpass {
+    my ($uname,$udom,$crstype,$permission) = @_;
+    my $canchange;
+    if (&Apache::lonnet::allowed('mau',$udom)) {
+        $canchange = 1;
+    } elsif ((ref($permission) eq 'HASH') && ($permission->{'mip'}) &&
+             ($udom eq $env{'request.role.domain'})) {
+        unless ($env{'course.'.$env{'request.course.id'}.'.internal.nopasswdchg'}) {
+            my ($cnum,$cdom) = &get_course_identity();
+            if ((&Apache::lonnet::is_course_owner($cdom,$cnum)) && ($udom eq $env{'user.domain'})) {
+                my @userstatuses = ('default');
+                my %userenv = &Apache::lonnet::userenvironment($udom,$uname,'inststatus');
+                if ($userenv{'inststatus'} ne '') {
+                    @userstatuses =  split(/:/,$userenv{'inststatus'});
+                }
+                my $noupdate = 1;
+                my %passwdconf = &Apache::lonnet::get_passwdconf($cdom);
+                if (ref($passwdconf{'crsownerchg'}) eq 'HASH') {
+                    if (ref($passwdconf{'crsownerchg'}{'for'}) eq 'ARRAY') {
+                        foreach my $status (@userstatuses) {
+                            if (grep(/^\Q$status\E$/,@{$passwdconf{'crsownerchg'}{'for'}})) {
+                                undef($noupdate);
+                                last;
+                            }
+                        }
+                    }
+                }
+                if ($noupdate) {
+                    return;
+                }
+                my %owned = &Apache::lonnet::courseiddump($cdom,'.',1,'.',
+                                                          $env{'user.name'}.':'.$env{'user.domain'},
+                                                          undef,undef,undef,'.');
+                my %roleshash = &Apache::lonnet::get_my_roles($uname,$udom,'userroles',
+                                                              ['active','future']);
+                foreach my $key (keys(%roleshash)) {
+                    my ($name,$domain,$role) = split(/:/,$key);
+                    if ($role eq 'st') {
+                        next if (($name eq $cnum) && ($domain eq $cdom));
+                        if ($owned{$domain.'_'.$name}) {
+                            if (ref($owned{$domain.'_'.$name}) eq 'HASH') {
+                                if ($owned{$domain.'_'.$name}{'nopasswdchg'}) {
+                                    $noupdate = 1;
+                                    last;
+                                }
+                            }
+                        } else {
+                            $noupdate = 1;
+                            last;
+                        }
+                    } else {
+                        $noupdate = 1;
+                        last;
+                    }
+                }
+                unless ($noupdate) {
+                    $canchange = 1;
+                }
+            }
+        }
+    }
+    return $canchange;
+}
+
 sub check_usertype {
     my ($dom,$uname,$rules,$curr_rules,$got_rules) = @_;
     my $usertype;
@@ -5897,10 +6277,16 @@ sub get_permission {
             }
         }
         if ($env{'request.course.id'}) {
-            my $user = $env{'user.name'}.':'.$env{'user.domain'};
+            my $user;
+            if (($env{'user.name'} ne '') && ($env{'user.domain'} ne '')) {
+                $user = $env{'user.name'}.':'.$env{'user.domain'};
+            }
             if (($user ne '') && ($env{'course.'.$env{'request.course.id'}.'.internal.courseowner'} eq
                                   $user)) {
                 $permission{'owner'} = 1;
+                if (&Apache::lonnet::allowed('mip',$env{'request.course.id'})) {
+                    $permission{'mip'} = 1;
+                }
             } elsif (($user ne '') && ($env{'course.'.$env{'request.course.id'}.'.internal.co-owners'} ne '')) {
                 if (grep(/^\Q$user\E$/,split(/,/,$env{'course.'.$env{'request.course.id'}.'.internal.co-owners'}))) {
                     $permission{'co-owner'} = 1;
@@ -5987,7 +6373,7 @@ sub get_course_identity {
 }
 
 sub dc_setcourse_js {
-    my ($formname,$mode,$context,$showcredits) = @_;
+    my ($formname,$mode,$context,$showcredits,$domain) = @_;
     my ($dc_setcourse_code,$authen_check);
     my $cctext = &Apache::lonnet::plaintext('cc');
     my $cotext = &Apache::lonnet::plaintext('co');
@@ -5996,7 +6382,7 @@ sub dc_setcourse_js {
     if ($mode eq 'upload') {
         $role = 'courserole';
     } else {
-        $authen_check = &verify_authen($formname,$context);
+        $authen_check = &verify_authen($formname,$context,$domain);
     }
     $dc_setcourse_code = (<<"SCRIPTTOP");
 $authen_check
@@ -6140,12 +6526,14 @@ ENDSCRIPT
 }
 
 sub verify_authen {
-    my ($formname,$context) = @_;
+    my ($formname,$context,$domain) = @_;
     my %alerts = &authcheck_alerts();
     my $finish = "return 'ok';";
     if ($context eq 'author') {
         $finish = "document.$formname.submit();";
     }
+    my ($numrules,$intargjs) =
+        &passwd_validation_js('argpicked',$domain);
     my $outcome = <<"ENDSCRIPT";
 
 function auth_check() {
@@ -6179,6 +6567,7 @@ function auth_check() {
                 break;
             case 'int':
                 alertmsg = '$alerts{'ipass'}';
+                break;
             case 'fsys':
                 alertmsg = '$alerts{'ipass'}';
                 break;
@@ -6192,6 +6581,11 @@ function auth_check() {
             alert(alertmsg);
             return;
         }
+    } else if (logintype == 'int') {
+        var numrules = $numrules;
+        if (numrules > 0) {
+$intargjs
+        }
     }
     $finish
 }