--- loncom/interface/lonuserutils.pm	2019/08/22 19:31:20	1.184.4.4
+++ loncom/interface/lonuserutils.pm	2020/02/10 17:12:54	1.205
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # Utility functions for managing LON-CAPA user accounts
 #
-# $Id: lonuserutils.pm,v 1.184.4.4 2019/08/22 19:31:20 raeburn Exp $
+# $Id: lonuserutils.pm,v 1.205 2020/02/10 17:12:54 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -550,6 +550,7 @@ END
             case 'fsys':
                 alertmsg = '$alert{'ipass'}';
                 break;
+            case 'lti':
             default:
                 alertmsg = '';
         }
@@ -655,10 +656,12 @@ sub passwd_validation_js {
     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+$/) {
-            $min = $passwdconf{'min'};
-            $numrules ++;
+            if ($passwdconf{'min'} > $min) {
+                $min = $passwdconf{'min'};
+            }
         }
         if ($passwdconf{'max'} =~ /^\d+$/) {
             $max = $passwdconf{'max'};
@@ -668,11 +671,11 @@ sub passwd_validation_js {
         if (@chars) {
             $numrules ++;
         }
-    } else {
-        $min = 7;
+    }
+    if ($min > 0) {
         $numrules ++;
     }
-    if (($min ne '') || ($max ne '') || (@chars > 0)) {
+    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';
@@ -1036,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);
 
@@ -1064,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',
@@ -1081,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'))
@@ -3659,6 +3669,8 @@ END
         setSections(formname,'$crstype');
         if (seccheck == 'ok') {
             opener.document.$callingform.newsecs.value = formname.sections.value;
+        } else {
+            return;
         }
 END
     } else {
@@ -4284,17 +4296,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();
@@ -4305,7 +4319,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;
     }
@@ -4317,7 +4371,6 @@ sub upfile_drop_add {
         if (! exists($home_servers{$desiredhost})) {
             $r->print('<p class="LC_error">'.&mt('Error').': '.
                       &mt('Invalid home server specified').'</p>');
-            $r->print(&Apache::loncommon::end_page());
             return 'invalidhome';
         }
     }
@@ -4345,6 +4398,8 @@ sub upfile_drop_add {
         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 '') {
@@ -4497,6 +4552,7 @@ sub upfile_drop_add {
         my (%existinguser,%userinfo,%disallow,%rulematch,%inst_results,%alerts,%checkuname,
             %showpasswdrules,$haspasswdmap);
         my $counter = -1;
+        my (%willtrust,%trustchecked);
         foreach my $line (@userdata) {
             $counter ++;
             my @secs;
@@ -4544,6 +4600,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'}};
@@ -4728,7 +4806,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>');
@@ -4972,16 +5050,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) {
@@ -5627,7 +5705,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) =
@@ -5649,6 +5727,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;
@@ -5993,6 +6076,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;
@@ -6115,10 +6262,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;