Diff for /loncom/Lond.pm between versions 1.6 and 1.25

version 1.6, 2014/01/01 17:41:37 version 1.25, 2023/12/28 18:14:08
Line 37  use lib '/home/httpd/lib/perl/'; Line 37  use lib '/home/httpd/lib/perl/';
 use LONCAPA;  use LONCAPA;
 use Apache::lonnet;  use Apache::lonnet;
 use GDBM_File;  use GDBM_File;
   use MIME::Base64;
   use Crypt::OpenSSL::X509;
   use Crypt::X509::CRL;
   use Crypt::PKCS10;
   use Net::OAuth;
   use Crypt::CBC;
   use Digest::SHA;
   use Digest::MD5 qw(md5_hex);
   
 sub dump_with_regexp {  sub dump_with_regexp {
     my ( $tail, $clientversion ) = @_;      my ( $tail, $clientversion ) = @_;
Line 58  sub dump_with_regexp { Line 65  sub dump_with_regexp {
         }          }
     }      }
   
   #
   # If dump is for file_permissions.db from a pre-2.12 server and
   # $uname:$udom is not a course, determine if value of portaccess
   # in effect for $uname:$udom allows portfolio files to be shared.
   # If sharing is not allowed, records returned for accesscontrol
   # are restricted to those based on ip (i.e., for externalresponse).
   #
   # Note: for 2.12 or later session-hosting server, determination
   # of portaccess value in effect occurs client-side.
   #
       my ($check_portaccess,$access,$now,$major,$minor,%by_ip);
       if ($namespace eq 'file_permissions') {
           if ($clientversion =~ /^\'?(\d+)\.(\d+)\.[\w.\-]+\'?/) {
               $major = $1;
               $minor = $2;
           }
           unless ((($major > 2) || (($major == 2) && ($minor > 11))) ||
                   &is_course($udom,$uname)) {
               $check_portaccess = 1;
               $access = &portfolio_is_shareable($udom,$uname);
           }
           $now = time;
       }
   
     my $hashref = &tie_user_hash($udom, $uname, $namespace, &GDBM_READER()) or       my $hashref = &tie_user_hash($udom, $uname, $namespace, &GDBM_READER()) or 
         return "error: ".($!+0)." tie(GDBM) Failed while attempting dump";          return "error: ".($!+0)." tie(GDBM) Failed while attempting dump";
   
Line 71  sub dump_with_regexp { Line 102  sub dump_with_regexp {
 #   # 
     my $skipcheck;      my $skipcheck;
     my @ids = &Apache::lonnet::current_machine_ids();      my @ids = &Apache::lonnet::current_machine_ids();
     my (%homecourses, $major, $minor, $now);      my %homecourses;
 #   # 
 # If dump is for roles.db from a pre-2.10 server, determine the LON-CAPA     # If dump is for roles.db from a pre-2.10 server, determine the LON-CAPA   
 # version on the server which requested the data.   # version on the server which requested the data. 
Line 109  sub dump_with_regexp { Line 140  sub dump_with_regexp {
                     }                      }
                 }                  }
             }              }
               if ($namespace eq 'file_permissions') {
                   if ($check_portaccess) {
                       unless ($access) {
                           my $unesckey = &unescape($key);
                           if ($unesckey =~ m{\0((\d+)_\d+_\d+:([a-z]+)_(\d+)_(\d+))$}) {
                               my ($acl,$timestamp,$scope,$end,$start) = ($1,$2,$3,$4);
                               if ($scope eq 'ip') {
                                   unless (($start > $now) &&
                                           ($end && $end<$now)) {
                                       my ($path) = split(/\0/,$unesckey);
                                       push(@{$by_ip{$path}},{$acl => $timestamp});
                                   }
                               }
                               next;
                           } elsif ($unesckey =~ m{\0accesscontrol$}) {
                               next;
                           }
                       }
                   }
               }
         if ($regexp eq '.') {          if ($regexp eq '.') {
             $count++;              $count++;
             if (defined($range) && $count >= $end)   { last; }              if (defined($range) && $count >= $end)   { last; }
Line 124  sub dump_with_regexp { Line 175  sub dump_with_regexp {
             }              }
         }          }
     }      }
       if (($namespace eq 'file_permissions') && ($check_portaccess) && (!$access)) {
           if (keys(%by_ip)) {
               my %accesscontrol;
               foreach my $path (keys(%by_ip)) {
                   if (ref($by_ip{$path}) eq 'ARRAY') {
                       foreach my $item (@{$by_ip{$path}}) {
                           if (ref($item) eq 'HASH') {
                               my ($acl,$timestamp) = each(%$item);
                               my $key = &escape("$path\0$acl");
                               my $value = $hashref->{$key};
                               $qresult.= "$key=$value&";
                               $accesscontrol{"$path\0accesscontrol"}{$acl} = $timestamp;
                           }
                       }
                   }
               }
               if (keys(%accesscontrol)) {
                   while (my ($key,$value) = each(%accesscontrol)) {
                       $qresult.= &escape($key).'='.&Apache::lonnet::freeze_escape($value).'&';
                   }
               }
           }
       }
     &untie_user_hash($hashref) or       &untie_user_hash($hashref) or 
         return "error: ".($!+0)." untie(GDBM) Failed while attempting dump";          return "error: ".($!+0)." untie(GDBM) Failed while attempting dump";
 #  #
Line 239  sub check_homecourses { Line 312  sub check_homecourses {
                     }                      }
                 }                  }
                 unless (&untie_domain_hash($hashref)) {                  unless (&untie_domain_hash($hashref)) {
                     &logthis("Failed to untie tied hash for nohist_courseids.db for $domain");                      &Apache::lonnet::logthis("Failed to untie tied hash for nohist_courseids.db for $domain");
                 }                  }
             } else {              } else {
                 &logthis("Failed to tie hash for nohist_courseids.db for $domain");                  &Apache::lonnet::logthis("Failed to tie hash for nohist_courseids.db for $domain");
             }              }
         }          }
         foreach my $hashid (keys(%recent)) {          foreach my $hashid (keys(%recent)) {
Line 314  sub get_courseinfo_hash { Line 387  sub get_courseinfo_hash {
     };      };
     if ($@) {      if ($@) {
         if ($@ eq "timeout\n") {          if ($@ eq "timeout\n") {
             &logthis("<font color='blue'>WARNING courseiddump for $cnum:$cdom from $home timedout</font>");              &Apache::lonnet::logthis("<font color='blue'>WARNING courseiddump for $cnum:$cdom from $home timedout</font>");
         } else {          } else {
             &logthis("<font color='yellow'>WARNING unexpected error during eval of call for courseiddump from $home</font>");              &Apache::lonnet::logthis("<font color='yellow'>WARNING unexpected error during eval of call for courseiddump from $home</font>");
         }          }
     } else {      } else {
         if (ref($info{$cdom.'_'.$cnum}) eq 'HASH') {          if (ref($info{$cdom.'_'.$cnum}) eq 'HASH') {
Line 327  sub get_courseinfo_hash { Line 400  sub get_courseinfo_hash {
     return;      return;
 }  }
   
   sub portfolio_is_shareable {
       my ($udom,$uname) = @_;
       my $check_portaccess = 1;
       my ($userportaccess,$inststatus,$access);
       my $hashref = &tie_user_hash($udom, $uname, 'environment', &GDBM_READER());
       if (ref($hashref) eq 'HASH') {
           my $accesskey = &escape('tools.portaccess');
           $userportaccess = $hashref->{$accesskey};
           $inststatus = $hashref->{'inststatus'};
           &untie_user_hash($hashref);
       }
       if ($userportaccess ne '') {
           $access = $userportaccess;
       } else {
           my %domdefs = &Apache::lonnet::get_domain_defaults($udom);
           if (ref($domdefs{'portaccess'}) eq 'HASH') {
               if (($domdefs{'portaccess'}{'_LC_adv'} ne '') &&
                   (&Apache::lonnet::is_advanced_user($udom,$uname))) {
                   if ($domdefs{'portaccess'}{'_LC_adv'}) {
                       $access = 1;
                   } else {
                       $access = 0;
                   }
               } elsif ($inststatus ne '') {
                   my ($hasaccess,$hasnoaccess);
                   foreach my $affiliation (split(/:/,$inststatus)) {
                       if ($domdefs{'portaccess'}{$affiliation} ne '') {
                           if ($domdefs{'portaccess'}{$affiliation}) {
                               $hasaccess = 1;
                           } else {
                               $hasnoaccess = 1;
                           }
                       }
                   }
                   if ($hasaccess || $hasnoaccess) {
                       if ($hasaccess) {
                           $access = 1;
                       } elsif ($hasnoaccess) {
                           $access = 0;
                       }
                   }
               } else {
                   if ($domdefs{'portaccess'}{'default'} ne '') {
                       if ($domdefs{'portaccess'}{'default'}) {
                           $access = 1;
                       } elsif ($domdefs{'portaccess'}{'default'} == 0) {
                           $access = 0;
                       }
                   }
               }
           } else {
               $access = 1;
           }
       }
       return $access;
   }
   
 sub dump_course_id_handler {  sub dump_course_id_handler {
     my ($tail) = @_;      my ($tail) = @_;
   
     my ($udom,$since,$description,$instcodefilter,$ownerfilter,$coursefilter,      my ($udom,$since,$description,$instcodefilter,$ownerfilter,$coursefilter,
         $typefilter,$regexp_ok,$rtn_as_hash,$selfenrollonly,$catfilter,$showhidden,          $typefilter,$regexp_ok,$rtn_as_hash,$selfenrollonly,$catfilter,$showhidden,
         $caller,$cloner,$cc_clone_list,$cloneonly,$createdbefore,$createdafter,          $caller,$cloner,$cc_clone_list,$cloneonly,$createdbefore,$createdafter,
         $creationcontext,$domcloner,$hasuniquecode) = split(/:/,$tail);          $creationcontext,$domcloner,$hasuniquecode,$reqcrsdom,$reqinstcode) = split(/:/,$tail);
     my $now = time;      my $now = time;
     my ($cloneruname,$clonerudom,%cc_clone);      my ($cloneruname,$clonerudom,%cc_clone);
     if (defined($description)) {      if (defined($description)) {
Line 409  sub dump_course_id_handler { Line 539  sub dump_course_id_handler {
     unless ($hasuniquecode) {      unless ($hasuniquecode) {
         $hasuniquecode = '.';          $hasuniquecode = '.';
     }      }
       if ($reqinstcode ne '') {
           $reqinstcode = &unescape($reqinstcode);
       }
     my $unpack = 1;      my $unpack = 1;
     if ($description eq '.' && $instcodefilter eq '.' && $ownerfilter eq '.' &&       if ($description eq '.' && $instcodefilter eq '.' && $ownerfilter eq '.' && 
         $typefilter eq '.') {          $typefilter eq '.') {
         $unpack = 0;          $unpack = 0;
     }      }
     if (!defined($since)) { $since=0; }      if (!defined($since)) { $since=0; }
       my (%gotcodedefaults,%otcodedefaults);
     my $qresult='';      my $qresult='';
   
     my $hashref = &tie_domain_hash($udom, "nohist_courseids", &GDBM_WRCREAT())      my $hashref = &tie_domain_hash($udom, "nohist_courseids", &GDBM_WRCREAT())
Line 434  sub dump_course_id_handler { Line 568  sub dump_course_id_handler {
                 $lasttime = $hashref->{$lasttime_key};                  $lasttime = $hashref->{$lasttime_key};
                 next if ($lasttime<$since);                  next if ($lasttime<$since);
             }              }
             my ($canclone,$valchange);              my ($canclone,$valchange,$clonefromcode);
             my $items = &Apache::lonnet::thaw_unescape($value);              my $items = &Apache::lonnet::thaw_unescape($value);
             if (ref($items) eq 'HASH') {              if (ref($items) eq 'HASH') {
                 if ($hashref->{$lasttime_key} eq '') {                  if ($hashref->{$lasttime_key} eq '') {
                     next if ($since > 1);                      next if ($since > 1);
                 }                  }
                   if ($items->{'inst_code'}) {
                       $clonefromcode = $items->{'inst_code'};
                   }
                 $is_hash =  1;                  $is_hash =  1;
                 if ($domcloner) {                  if ($domcloner) {
                     $canclone = 1;                      $canclone = 1;
Line 465  sub dump_course_id_handler { Line 602  sub dump_course_id_handler {
                                 }                                  }
                             }                              }
                         }                          }
                           unless ($canclone) {
                               if (($reqcrsdom eq $udom) && ($reqinstcode) && ($clonefromcode)) {
                                   if (grep(/\=/,@cloneable))  {
                                       foreach my $cloner (@cloneable) {
                                           if (($cloner ne '*') && ($cloner !~ /^\*\:$LONCAPA::match_domain$/) &&
                                               ($cloner !~ /^$LONCAPA::match_username\:$LONCAPA::match_domain$/) && ($cloner ne '')) {
                                               if ($cloner =~ /=/) {
                                                   my (%codedefaults,@code_order);
                                                   if (ref($gotcodedefaults{$udom}) eq 'HASH') {
                                                       if (ref($gotcodedefaults{$udom}{'defaults'}) eq 'HASH') {
                                                           %codedefaults = %{$gotcodedefaults{$udom}{'defaults'}};
                                                       }
                                                       if (ref($gotcodedefaults{$udom}{'order'}) eq 'ARRAY') {
                                                           @code_order = @{$gotcodedefaults{$udom}{'order'}};
                                                       }
                                                   } else {
                                                       &Apache::lonnet::auto_instcode_defaults($udom,
                                                                                               \%codedefaults,
                                                                                               \@code_order);
                                                       $gotcodedefaults{$udom}{'defaults'} = \%codedefaults;
                                                       $gotcodedefaults{$udom}{'order'} = \@code_order;
                                                   }
                                                   if (@code_order > 0) {
                                                       if (&Apache::lonnet::check_instcode_cloning(\%codedefaults,\@code_order,
                                                                                                   $cloner,$clonefromcode,$reqinstcode)) {
                                                           $canclone = 1;
                                                           last;
                                                       }
                                                   }
                                               }
                                           }
                                       }
                                   }
                               }
                           }
                     } elsif (defined($cloneruname)) {                      } elsif (defined($cloneruname)) {
                         if ($cc_clone{$unesc_key}) {                          if ($cc_clone{$unesc_key}) {
                             $canclone = 1;                              $canclone = 1;
Line 485  sub dump_course_id_handler { Line 657  sub dump_course_id_handler {
                             }                              }
                         }                          }
                     }                      }
                       unless (($canclone) || ($items->{'cloners'})) {
                           my %domdefs = &Apache::lonnet::get_domain_defaults($udom);
                           if ($domdefs{'canclone'}) {
                               unless ($domdefs{'canclone'} eq 'none') {
                                   if ($domdefs{'canclone'} eq 'domain') {
                                       if ($clonerudom eq $udom) {
                                           $canclone = 1;
                                       }
                                   } elsif (($clonefromcode) && ($reqinstcode) &&
                                            ($udom eq $reqcrsdom)) {
                                       if (&Apache::lonnet::default_instcode_cloning($udom,$domdefs{'canclone'},
                                                                                     $clonefromcode,$reqinstcode)) {
                                           $canclone = 1;
                                       }
                                   }
                               }
                           }
                       }
                 }                  }
                 if ($unpack || !$rtn_as_hash) {                  if ($unpack || !$rtn_as_hash) {
                     $unesc_val{'descr'} = $items->{'description'};                      $unesc_val{'descr'} = $items->{'description'};
Line 722  sub dump_profile_database { Line 912  sub dump_profile_database {
     return $qresult;      return $qresult;
 }  }
   
   sub is_course {
       my ($cdom,$cnum) = @_;
   
       return unless (($cdom =~ /^$LONCAPA::match_domain$/) &&
                      ($cnum =~ /^$LONCAPA::match_courseid$/));
       my $hashid = $cdom.':'.$cnum;
       my ($iscourse,$cached) =
           &Apache::lonnet::is_cached_new('iscourse',$hashid);
       unless (defined($cached)) {
           my $hashref =
               &tie_domain_hash($cdom, "nohist_courseids", &GDBM_WRCREAT());
           if (ref($hashref) eq 'HASH') {
               my $esc_key = &escape($cdom.'_'.$cnum);
               if (exists($hashref->{$esc_key})) {
                   $iscourse = 1;
               } else {
                   $iscourse = 0;
               }
               &Apache::lonnet::do_cache_new('iscourse',$hashid,$iscourse,3600);
               unless (&untie_domain_hash($hashref)) {
                   &Apache::lonnet::logthis("Failed to untie tied hash for nohist_courseids.db for $cdom");
               }
           } else {
               &Apache::lonnet::logthis("Failed to tie hash for nohist_courseids.db for $cdom");
           }
       }
       return $iscourse;
   }
   
   sub server_certs {
       my ($perlvar,$lonhost,$hostname) = @_;
       my %pemfiles = (
                        key      => 'lonnetPrivateKey',
                        host     => 'lonnetCertificate',
                        hostname => 'lonnetHostnameCertificate',
                        ca       => 'lonnetCertificateAuthority',
                        crl      => 'lonnetCertRevocationList',
                      );
       my (%md5hash,%expected_cn,%expired,%revoked,%wrongcn,%info,$crlfile,$cafile,
           %rvkcerts,$numrvk);
       %info = (
                   key => {},
                   ca  => {},
                   host => {},
                   hostname => {},
                   crl => {},
               );
       my @ordered = ('crl','key','ca','host','hostname');
       if (ref($perlvar) eq 'HASH') {
           $expected_cn{'host'} = $Apache::lonnet::serverhomeIDs{$hostname};
           $expected_cn{'hostname'} = 'internal-'.$hostname;
           my $certsdir = $perlvar->{'lonCertificateDirectory'};
           if (-d $certsdir) {
               $crlfile = $certsdir.'/'.$perlvar->{$pemfiles{'crl'}};
               $cafile = $certsdir.'/'.$perlvar->{$pemfiles{'ca'}};
               foreach my $key (@ordered) {
                   if ($perlvar->{$pemfiles{$key}}) {
                       my $file = $certsdir.'/'.$perlvar->{$pemfiles{$key}};
                       if (-e $file) {
                           if ($key eq 'crl') {
                                if ((-e $crlfile) && (-e $cafile)) {
                                    if (open(PIPE,"openssl crl -in $crlfile -inform pem -CAfile $cafile -noout 2>&1 |")) {
                                        my $crlstatus = <PIPE>;
                                        close(PIPE);
                                        chomp($crlstatus);
                                        if ($crlstatus =~ /OK/) {
                                            $info{$key}{'status'} = 'ok';
                                            $info{$key}{'details'} = 'CRL valid for CA';
                                        }
                                    }
                                }
                                if (open(my $fh,'<',$crlfile)) {
                                    my $pem_crl = '';
                                    while (my $line=<$fh>) {
                                        chomp($line);
                                        next if ($line eq '-----BEGIN X509 CRL-----');
                                        next if ($line eq '-----END X509 CRL-----');
                                        $pem_crl .= $line;
                                    }
                                    close($fh);
                                    my $der_crl = MIME::Base64::decode_base64($pem_crl);
                                    if ($der_crl ne '') {
                                        my $decoded = Crypt::X509::CRL->new( crl => $der_crl );
                                        if ($decoded->error) {
                                            $info{$key}{'status'} = 'error';
                                        } elsif (ref($decoded)) {
                                            $info{$key}{'start'} = $decoded->this_update;
                                            $info{$key}{'end'} = $decoded->next_update;
                                            $info{$key}{'alg'} = $decoded->SigEncAlg.' '.$decoded->SigHashAlg;
                                            $info{$key}{'cn'} = $decoded->issuer_cn;
                                            $info{$key}{'email'} = $decoded->issuer_email;
                                            $info{$key}{'size'} = $decoded->signature_length;
                                            my $rlref = $decoded->revocation_list;
                                            if (ref($rlref) eq 'HASH') {
                                                foreach my $key (keys(%{$rlref})) {
                                                    my $hkey = sprintf("%X",$key);
                                                    $rvkcerts{$hkey} = 1;
                                                }
                                                $numrvk = scalar(keys(%{$rlref}));
                                                if ($numrvk) {
                                                    $info{$key}{'details'} .= " ($numrvk revoked)"; 
                                                }
                                            }
                                        }
                                    }
                               }
                           } elsif ($key eq 'key') {
                               if (open(PIPE,"openssl rsa -noout -in $file -check |")) {
                                   my $check = <PIPE>;
                                   close(PIPE);
                                   chomp($check);
                                   $info{$key}{'status'} = $check;
                               }
                               if (open(PIPE,"openssl rsa -noout -modulus -in $file | openssl md5 |")) {
                                   $md5hash{$key} = <PIPE>;
                                   close(PIPE);
                                   chomp($md5hash{$key});
                               }
                           } else {
                               if ($key eq 'ca') {
                                   if (open(PIPE,"openssl verify -CAfile $file $file |")) {
                                       my $check = <PIPE>;
                                       close(PIPE);
                                       chomp($check);
                                       if ($check eq "$file: OK") {
                                           $info{$key}{'status'} = 'ok';
                                       } else {
                                           $check =~ s/^\Q$file\E\:?\s*//;
                                           $info{$key}{'status'} = $check;
                                       }
                                   }
                               } else {
                                   if (open(PIPE,"openssl x509 -noout -modulus -in $file | openssl md5 |")) {
                                       $md5hash{$key} = <PIPE>;
                                       close(PIPE);
                                       chomp($md5hash{$key});
                                   }
                               }
                               my $x509 = Crypt::OpenSSL::X509->new_from_file($file);
                               my @items = split(/,\s+/,$x509->subject());
                               foreach my $item (@items) {
                                   my ($name,$value) = split(/=/,$item);
                                   if ($name eq 'CN') {
                                       $info{$key}{'cn'} = $value;
                                   }
                               }
                               $info{$key}{'start'} = $x509->notBefore();
                               $info{$key}{'end'} = $x509->notAfter();
                               $info{$key}{'alg'} = $x509->sig_alg_name();
                               $info{$key}{'size'} = $x509->bit_length();
                               $info{$key}{'email'} = $x509->email();
                               $info{$key}{'serial'} = uc($x509->serial());
                               $info{$key}{'issuerhash'} = $x509->issuer_hash();
                               if ($x509->checkend(0)) {
                                   $expired{$key} = 1;
                               }
                               if (($key eq 'host') || ($key eq 'hostname')) {
                                   if ($info{$key}{'cn'} ne $expected_cn{$key}) {
                                       $wrongcn{$key} = 1;
                                   }
                                   if (($numrvk) && ($info{$key}{'serial'})) {
                                       if ($rvkcerts{$info{$key}{'serial'}}) {
                                           $revoked{$key} = 1;
                                       }
                                   }
                               }
                           }
                       }
                       if (($key eq 'host') || ($key eq 'hostname')) {
                           my $csrfile = $file;
                           $csrfile =~ s/\.pem$/.csr/;
                           if (-e $csrfile) {
                               if (open(PIPE,"openssl req -noout -modulus -in $csrfile |openssl md5 |")) {
                                   my $csrhash = <PIPE>;
                                   close(PIPE);
                                   chomp($csrhash);
                                   if ((!-e $file) || ($csrhash ne $md5hash{$key}) || ($expired{$key}) ||
                                       ($wrongcn{$key}) || ($revoked{$key})) {
                                       Crypt::PKCS10->setAPIversion(1);
                                       my $decoded = Crypt::PKCS10->new( $csrfile,(PEMonly => 1, readFile => 1));
                                       if (ref($decoded)) {
                                           if ($decoded->commonName() eq $expected_cn{$key}) {
                                               $info{$key.'-csr'}{'cn'} = $decoded->commonName();
                                               $info{$key.'-csr'}{'alg'} = $decoded->pkAlgorithm();
                                               $info{$key.'-csr'}{'email'} = $decoded->emailAddress();
                                               my $params = $decoded->subjectPublicKeyParams();
                                               if (ref($params) eq 'HASH') {
                                                   $info{$key.'-csr'}{'size'} = $params->{keylen};
                                               }
                                               $md5hash{$key.'-csr'} = $csrhash;
                                           }
                                       }
                                   }
                               }
                           }
                       }
                   }
               }
           }
       }
       foreach my $key ('host','hostname') {
           if ($md5hash{$key}) {
               if ($md5hash{$key} eq $md5hash{'key'}) {
                   if ($revoked{$key}) {
                       $info{$key}{'status'} = 'revoked';
                   } elsif ($expired{$key}) {
                       $info{$key}{'status'} = 'expired';
                   } elsif ($wrongcn{$key}) {
                       $info{$key}{'status'} = 'wrongcn';
                   } elsif ((exists($info{'ca'}{'issuerhash'})) &&
                            ($info{'ca'}{'issuerhash'} ne $info{$key}{'issuerhash'})) {
                       $info{$key}{'status'} = 'mismatch';
                   } else {
                       $info{$key}{'status'} = 'ok';
                   }
               } elsif ($info{'key'}{'status'} =~ /ok/) {
                   $info{$key}{'status'} = 'otherkey';
               } else {
                   $info{$key}{'status'} = 'nokey';
               }
           }
           if ($md5hash{$key.'-csr'}) {
               if ($md5hash{$key.'-csr'} eq $md5hash{'key'}) {
                   $info{$key.'-csr'}{'status'} = 'ok';
               } elsif ($info{'key'}{'status'} =~ /ok/) {
                   $info{$key.'-csr'}{'status'} = 'otherkey';
               } else {
                   $info{$key.'-csr'}{'status'} = 'nokey';
               }
           }
       }
       my $result;
       foreach my $key (keys(%info)) {
           $result .= &escape($key).'='.&Apache::lonnet::freeze_escape($info{$key}).'&';
       }
       $result =~ s/\&$//;
       return $result;
   }
   
   sub get_dom {
       my ($userinput) = @_;
       my ($cmd,$udom,$namespace,$what) =split(/:/,$userinput,4); 
       my $hashref = &tie_domain_hash($udom,$namespace,&GDBM_READER()) or
           return "error: ".($!+0)." tie(GDBM) Failed while attempting $cmd";
       my $qresult='';
       if (ref($hashref)) {
           chomp($what);
           my @queries=split(/\&/,$what);
           for (my $i=0;$i<=$#queries;$i++) {
               $qresult.="$hashref->{$queries[$i]}&";
           }
           $qresult=~s/\&$//;
       }
       &untie_user_hash($hashref) or
           return "error: ".($!+0)." untie(GDBM) Failed while attempting $cmd";
       return $qresult;
   }
   
   sub store_dom {
       my ($userinput) = @_;
       my ($cmd,$dom,$namespace,$rid,$what) =split(/:/,$userinput);
       my $hashref  = &tie_domain_hash($dom,$namespace,&GDBM_WRCREAT(),"S","$rid:$what") or
           return "error: ".($!+0)." tie(GDBM) Failed while attempting $cmd";
       $hashref->{"version:$rid"}++;
       my $version=$hashref->{"version:$rid"};
       my $allkeys='';
       my @pairs=split(/\&/,$what);
       foreach my $pair (@pairs) {
           my ($key,$value)=split(/=/,$pair);
           $allkeys.=$key.':';
           $hashref->{"$version:$rid:$key"}=$value;
       }
       my $now = time;
       $hashref->{"$version:$rid:timestamp"}=$now;
       $allkeys.='timestamp';
       $hashref->{"$version:keys:$rid"}=$allkeys;
       &untie_user_hash($hashref) or
           return "error: ".($!+0)." untie(GDBM) Failed while attempting $cmd";
       return 'ok';
   }
   
   sub restore_dom {
       my ($userinput) = @_;
       my ($cmd,$dom,$namespace,$rid) = split(/:/,$userinput);
       my $hashref = &tie_domain_hash($dom,$namespace,&GDBM_READER()) or
           return "error: ".($!+0)." tie(GDBM) Failed while attempting $cmd";
       my $qresult='';
       if (ref($hashref)) {
           chomp($rid);
           my $version=$hashref->{"version:$rid"};
           $qresult.="version=$version&";
           my $scope;
           for ($scope=1;$scope<=$version;$scope++) {
               my $vkeys=$hashref->{"$scope:keys:$rid"};
               my @keys=split(/:/,$vkeys);
               my $key;
               $qresult.="$scope:keys=$vkeys&";
               foreach $key (@keys) {
                   $qresult.="$scope:$key=".$hashref->{"$scope:$rid:$key"}."&";
               }
           }
           $qresult=~s/\&$//;
       }
       &untie_user_hash($hashref) or
           return "error: ".($!+0)." untie(GDBM) Failed while attempting $cmd";
       return $qresult;
   }
   
   sub crslti_itemid {
       my ($cdom,$cnum,$url,$method,$params,$loncaparev) = @_;
       unless (ref($params) eq 'HASH') {
           return;
       }
       if (($cdom eq '') || ($cnum eq '')) {
           return;
       }
       my ($itemid,$consumer_key,$secret);
   
       if (exists($params->{'oauth_callback'})) {
           $Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0A;
       } else {
           $Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0;
       }
   
       my $consumer_key = $params->{'oauth_consumer_key'};
       return if ($consumer_key eq '');
   
       my (%crslti,%crslti_by_key);
       my $hashid=$cdom.'_'.$cnum;
       my ($result,$cached)=&Apache::lonnet::is_cached_new('courseltienc',$hashid);
       if (defined($cached)) {
           if (ref($result) eq 'HASH') {
               %crslti = %{$result};
           }
       } else {
           my $reply = &dump_with_regexp(join(":",($cdom,$cnum,'nohist_ltienc','','')),$loncaparev);
           %crslti = %{&Apache::lonnet::unserialize($reply)};
           my $cachetime = 24*60*60;
           &Apache::lonnet::do_cache_new('courseltienc',$hashid,\%crslti,$cachetime);
       }
   
       return if (!keys(%crslti));
   
       foreach my $id (keys(%crslti)) {
           if (ref($crslti{$id}) eq 'HASH') {
               my $key = $crslti{$id}{'key'};
               if (($key ne '') && ($crslti{$id}{'secret'} ne '')) {
                   push(@{$crslti_by_key{$key}},$id);
               }
           }
       }
   
       return if (!keys(%crslti_by_key));
   
       my %courselti = &Apache::lonnet::get_course_lti($cnum,$cdom,'provider');
   
       if (ref($crslti_by_key{$consumer_key}) eq 'ARRAY') {
           foreach my $id (@{$crslti_by_key{$consumer_key}}) {
               my $secret = $crslti{$id}{'secret'};
               if (ref($courselti{$id}) eq 'HASH') {
                   if ((exists($courselti{$id}{'cipher'})) &&
                       ($courselti{$id}{'cipher'} =~ /^\d+$/)) {
                       my $keynum = $courselti{$id}{'cipher'};
                       my $privkey = &get_dom("getdom:$cdom:private:$keynum:lti:key");
                       if ($privkey ne '') {
                           my $cipher = new Crypt::CBC($privkey);
                           $secret = $cipher->decrypt_hex($secret);
                       }
                   }
               }
               my $request = Net::OAuth->request('request token')->from_hash($params,
                                                 request_url => $url,
                                                 request_method => $method,
                                                 consumer_secret => $secret,);
               if ($request->verify()) {
                   $itemid = $id;
                   last;
               }
           }
       }
       return $itemid;
   }
   
   sub domlti_itemid {
       my ($dom,$context,$url,$method,$params,$loncaparev) = @_;
       unless (ref($params) eq 'HASH') {
           return;
       }
       if ($dom eq '') {
           return;
       }
       my ($itemid,$consumer_key,$secret);
   
       if (exists($params->{'oauth_callback'})) {
           $Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0A;
       } else {
           $Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0;
       }
   
       my $consumer_key = $params->{'oauth_consumer_key'};
       return if ($consumer_key eq '');
   
       my ($name,$cachename);
       if ($context eq 'linkprot') {
           $name = $context;
       } else {
           $name = 'lti';
           if ($context eq '') {
               $context = 'provider';
           }
       }
       $cachename = $name.'enc';
       my %ltienc;
       my ($encresult,$enccached)=&Apache::lonnet::is_cached_new($cachename,$dom);
       if (defined($enccached)) {
           if (ref($encresult) eq 'HASH') {
               %ltienc = %{$encresult};
           }
       } else {
           my $reply = &get_dom("getdom:$dom:encconfig:$name");
           my $ltiencref = &Apache::lonnet::thaw_unescape($reply);
           if (ref($ltiencref) eq 'HASH') {
               %ltienc = %{$ltiencref};
           }
           my $cachetime = 24*60*60;
           &Apache::lonnet::do_cache_new($cachename,$dom,\%ltienc,$cachetime);
       }
   
       return if (!keys(%ltienc));
   
       my %lti_by_key;
       foreach my $id (keys(%ltienc)) {
           if (ref($ltienc{$id}) eq 'HASH') {
               my $key = $ltienc{$id}{'key'};
               if (($key ne '') && ($ltienc{$id}{'secret'} ne '')) {
                   push(@{$lti_by_key{$key}},$id);
               }
           }
       }
       return if (!keys(%lti_by_key));
   
       my %lti = &Apache::lonnet::get_domain_lti($dom,$context);
   
       if (ref($lti_by_key{$consumer_key}) eq 'ARRAY') {
           foreach my $id (@{$lti_by_key{$consumer_key}}) {
               my $secret = $ltienc{$id}{'secret'};
               if (ref($lti{$id}) eq 'HASH') {
                   if ((exists($lti{$id}{'cipher'})) &&
                       ($lti{$id}{'cipher'} =~ /^\d+$/)) {
                       my $keynum = $lti{$id}{'cipher'};
                       my $privkey = &get_dom("getdom:$dom:private:$keynum:lti:key");
                       if ($privkey ne '') {
                           my $cipher = new Crypt::CBC($privkey);
                           $secret = $cipher->decrypt_hex($secret);
                       }
                   }
               }
               my $request = Net::OAuth->request('request token')->from_hash($params,
                                                 request_url => $url,
                                                 request_method => $method,
                                                 consumer_secret => $secret,);
               if ($request->verify()) {
                   $itemid = $id;
                   last;
               }
           }
       }
       return $itemid;
   }
   
   sub sign_lti_payload {
       my ($cdom,$cnum,$crsdef,$type,$context,$url,$idx,$keynum,$loncaparev,$paramsref,$inforef) = @_;
       return unless (ref($paramsref) eq 'HASH');
       my ($sigmethod,$callback,$reqtype,$reqmethod,$respfmt,$bodyhash);
       if (ref($inforef) eq 'HASH') {
           if (exists($inforef->{'method'})) {
               $sigmethod = $inforef->{'method'};
           }
           if (exists($inforef->{'cb'})) {
               $callback = $inforef->{'cb'};
           }
           if (exists($inforef->{'reqtype'})) {
               $reqtype = $inforef->{'reqtype'};
           }
           if (exists($inforef->{'reqmethod'})) {
               $reqmethod = $inforef->{'reqmethod'};
           }
           if (exists($inforef->{'body_hash'})) {
               $bodyhash = $inforef->{'body_hash'};
           }
           if (exists($inforef->{'respfmt'})) {
               $respfmt = $inforef->{'respfmt'};
           }
       }
       my ($key,$secret) = &get_lti_credentials($cdom,$cnum,$crsdef,$type,$idx,$keynum,$loncaparev);
       return if (($key eq '') || ($secret eq ''));
       if ($sigmethod eq '') {
           $sigmethod = 'HMAC-SHA1';
       }
       if ($callback eq '') {
           $callback = 'about:blank',
       }
       if ($reqtype eq '') {
           $reqtype = 'request token';
       }
       if ($reqmethod eq '') {
           $reqmethod = 'POST';
       }
       srand( time() ^ ($$ + ($$ << 15))  ); # Seed rand.
       my $nonce = Digest::SHA::sha1_hex(sprintf("%06x%06x",rand(0xfffff0),rand(0xfffff0)));
       my $request;
       if (($context eq 'grade') && ($reqtype eq 'consumer') && ($bodyhash ne '')) { 
           $request = Net::OAuth->request($reqtype)->new(
                              consumer_key => $key,
                              consumer_secret => $secret,
                              request_url => $url,
                              request_method => $reqmethod,
                              signature_method => $sigmethod,
                              timestamp => time(),
                              nonce => $nonce,
                              body_hash => $bodyhash,
           );
           $request->add_required_message_params('body_hash');
       } else {
           $request = Net::OAuth->request($reqtype)->new(
               consumer_key => $key,
               consumer_secret => $secret,
               request_url => $url,
               request_method => 'POST',
               signature_method => $sigmethod,
               timestamp => time,
               nonce => $nonce,
               callback => $callback,
               extra_params => $paramsref,
               version      => '1.0',
               );
       }
       $request->sign();
       if ($respfmt eq 'to_post_body') {
           return $request->to_post_body();
       } elsif ($respfmt eq 'to_authorization_header') {
           return $request->to_authorization_header();
       } else {
           return $request->to_hash();
       }
   }
   
   sub get_lti_credentials {
       my ($cdom,$cnum,$crsdef,$type,$idx,$keynum,$loncaparev) = @_;
       my ($dbname,$name,$cachename,$hashid,$key,$secret,%ltienc);
       if ($crsdef) {
           $hashid = $cdom.'_'.$cnum;
       } else {
           $hashid = $cdom;
       }
       if ($type eq 'tools') {
           if ($crsdef) {
               $dbname = 'nohist_toolsenc';
               $cachename = 'crsltitoolsenc';
           } else {
               $name = 'ltitools';
               $dbname = 'encconfig';
               $cachename = 'ltitoolsenc';
           }
       } elsif ($type eq 'linkprot') {
           if ($crsdef) {
               $dbname = 'nohist_ltienc';
               $cachename = 'courseltienc';
           } else {
               $name = 'linkprot';
               $dbname = 'encconfig';
               $cachename = 'linkprotenc';
           }
       } elsif ($type eq 'lti') {
           $name = 'lti';
           $dbname = 'encconfig';
           $cachename = 'ltienc';
       }
       my ($encresult,$enccached)=&Apache::lonnet::is_cached_new($cachename,$hashid);
       if (defined($enccached)) {
           if (ref($encresult) eq 'HASH') {
               %ltienc = %{$encresult};
           }
       } else {
           if ($crsdef) {
               my $reply = &dump_with_regexp(join(":",($cdom,$cnum,$dbname,'','')),$loncaparev);
               %ltienc = %{&Apache::lonnet::unserialize($reply)};
           } else {
               my $reply = &get_dom("getdom:$cdom:$dbname:$name");
               my $encref = &Apache::lonnet::thaw_unescape($reply);
               if (ref($encref) eq 'HASH') {
                   %ltienc = %{$encref};
               }
           }
           my $cachetime = 24*60*60;
           &Apache::lonnet::do_cache_new($cachename,$hashid,\%ltienc,$cachetime);
       }
       if (!keys(%ltienc)) {
            return ();
       } elsif (exists($ltienc{$idx})) {
           if (ref($ltienc{$idx}) eq 'HASH') {
               if (exists($ltienc{$idx}{'key'})) {
                   $key = $ltienc{$idx}{'key'};
               }
               if (exists($ltienc{$idx}{'secret'})) {
                   $secret = $ltienc{$idx}{'secret'};
                   if ($keynum =~ /^\d+$/) {
                       my $privhost;
                       my $privname = 'ltitools';
                       if (($type eq 'lti') || ($type eq 'linkprot')) {
                           $privname = 'lti';
                       }
                       if ($crsdef) {
                           my $primary = &Apache::lonnet::domain($cdom,'primary');
                           my @ids = &Apache::lonnet::current_machine_ids();
                           unless (grep(/^\Q$primary\E$/,@ids)) {
                               $privhost = $primary;
                               my ($result,$plainsecret) = &decrypt_secret($privhost,$secret,$keynum,$privname);
                               if ($result eq 'ok') {
                                   $secret = $plainsecret;
                               } else {
                                   undef($secret);
                               }
                           }
                       }
                       unless ($privhost) {
                           my $privkey = &get_dom("getdom:$cdom:private:$keynum:$privname:key");
                           if (($privkey ne '') && ($secret ne '')) {
                               my $cipher = new Crypt::CBC($privkey);
                               $secret = $cipher->decrypt_hex($secret);
                           } else {
                               undef($secret);
                           }
                       }
                   }
               }
           }
       }
       return ($key,$secret);
   }
   
   sub decrypt_secret {
       my ($privhost,$secret,$keynum,$type) = @_;
       return;
   }
   
 1;  1;
   
Line 845  courseID -- for the course for which the Line 1680  courseID -- for the course for which the
 The contents of the inner hash, for that single item in the outer hash  The contents of the inner hash, for that single item in the outer hash
 are returned (and cached in memcache for 10 minutes).  are returned (and cached in memcache for 10 minutes).
   
   =item get_dom ( $userinput )
   
   get_dom() will retrieve domain configuration information from a GDBM file
   in /home/httpd/lonUsers/$dom on the primary library server in a domain.
   The single argument passed is the string: $cmd:$udom:$namespace:$what
   where $cmd is the command historically passed to lond - i.e., getdom
   or egetdom, $udom is the domain, $namespace is the name of the GDBM file
   (encconfig or configuration), and $what is a string containing names of 
   items to retrieve from the db file (each item name is escaped and separated
   from the next item name with an ampersand). The return value is either:
   error: followed by an error message, or a string containing the value (escaped)
   for each item, again separated from the next item with an ampersand.
   
 =back  =back
   

Removed from v.1.6  
changed lines
  Added in v.1.25


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>