--- loncom/Lond.pm 2022/02/14 02:48:49 1.19 +++ loncom/Lond.pm 2023/05/22 21:10:56 1.22 @@ -1,6 +1,6 @@ # The LearningOnline Network # -# $Id: Lond.pm,v 1.19 2022/02/14 02:48:49 raeburn Exp $ +# $Id: Lond.pm,v 1.22 2023/05/22 21:10:56 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -42,6 +42,10 @@ use Crypt::OpenSSL::X509; use Crypt::X509::CRL; use Crypt::PKCS10; use Net::OAuth; +use Crypt::CBC; +use Net::OAuth; +use Digest::SHA; +use Digest::MD5 qw(md5_hex); sub dump_with_regexp { my ( $tail, $clientversion ) = @_; @@ -1063,7 +1067,7 @@ sub store_dom { $allkeys.='timestamp'; $hashref->{"$version:keys:$rid"}=$allkeys; &untie_user_hash($hashref) or - return "error: ".($!+0)." untie(GDBM) Failed while attempting $cmd"; + return "error: ".($!+0)." untie(GDBM) Failed while attempting $cmd"; return 'ok'; } @@ -1140,9 +1144,22 @@ sub crslti_itemid { 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, @@ -1175,65 +1192,58 @@ sub domlti_itemid { my $consumer_key = $params->{'oauth_consumer_key'}; return if ($consumer_key eq ''); + my ($name,$cachename); + if ($context eq 'linkprot') { + $name = $context; + } else { + $name = 'lti'; + } + $cachename = $name.'enc'; my %ltienc; - my ($encresult,$enccached)=&Apache::lonnet::is_cached_new('ltienc',$dom); + 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:lti"); + 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('ltienc',$dom,\%ltienc,$cachetime); + &Apache::lonnet::do_cache_new($cachename,$dom,\%ltienc,$cachetime); } return if (!keys(%ltienc)); - my %lti; - if ($context eq 'deeplink') { - my ($result,$cached)=&Apache::lonnet::is_cached_new('lti',$dom); - if (defined($cached)) { - if (ref($result) eq 'HASH') { - %lti = %{$result}; - } - } else { - my $reply = &get_dom("getdom:$dom:configuration:lti"); - my $ltiref = &Apache::lonnet::thaw_unescape($reply); - if (ref($ltiref) eq 'HASH') { - %lti = %{$ltiref}; - } - my $cachetime = 24*60*60; - &Apache::lonnet::do_cache_new('lti',$dom,\%lti,$cachetime); - } - } - return if (!keys(%lti)); - 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 '')) { - if ($context eq 'deeplink') { - if (ref($lti{$id}) eq 'HASH') { - if (!$lti{$id}{'requser'}) { - push(@{$lti_by_key{$key}},$id); - } - } - } else { - push(@{$lti_by_key{$key}},$id); - } + 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, @@ -1247,6 +1257,122 @@ sub domlti_itemid { return $itemid; } +sub sign_params { + my ($cdom,$cnum,$crstool,$url,$idx,$keynum,$post,$loncaparev,$paramsref,$inforef) = @_; + return unless (ref($paramsref) eq 'HASH'); + my ($sigmethod,$type,$callback); + if (ref($inforef) eq 'HASH') { + if (exists($inforef->{'method'})) { + $sigmethod = $inforef->{'method'}; + } + if (exists($inforef->{'cb'})) { + $callback = $inforef->{'cb'}; + } + if (exists($inforef->{'type'})) { + $type = $inforef->{'type'}; + } + } + my ($cachename,$hashid,$key,$secret,%ltitoolsenc); + if ($crstool) { + $cachename = 'crsltitoolsenc'; + $hashid = $cdom.'_'.$cnum; + } else { + $cachename = 'ltitoolsenc'; + $hashid = $cdom; + } + my ($encresult,$enccached)=&Apache::lonnet::is_cached_new($cachename,$hashid); + if (defined($enccached)) { + if (ref($encresult) eq 'HASH') { + %ltitoolsenc = %{$encresult}; + } + } else { + if ($crstool) { + my $reply = &dump_with_regexp(join(":",($cdom,$cnum,'nohist_toolsenc','','')),$loncaparev); + %ltitoolsenc = %{&Apache::lonnet::unserialize($reply)}; + } else { + my $reply = &get_dom("getdom:$cdom:encconfig:ltitools"); + my $ltitoolsencref = &Apache::lonnet::thaw_unescape($reply); + if (ref($ltitoolsencref) eq 'HASH') { + %ltitoolsenc = %{$ltitoolsencref}; + } + } + my $cachetime = 24*60*60; + &Apache::lonnet::do_cache_new($cachename,$hashid,\%ltitoolsenc,$cachetime); + } + if (!keys(%ltitoolsenc)) { + return; + } elsif (exists($ltitoolsenc{$idx})) { + if (ref($ltitoolsenc{$idx}) eq 'HASH') { + if (exists($ltitoolsenc{$idx}{'key'})) { + $key = $ltitoolsenc{$idx}{'key'}; + } + if (exists($ltitoolsenc{$idx}{'secret'})) { + $secret = $ltitoolsenc{$idx}{'secret'}; + my $privhost; + if ($keynum =~ /^\d+$/) { + if ($crstool) { + 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,'ltitools'); + if ($result eq 'ok') { + $secret = $plainsecret; + } else { + undef($secret); + } + } + } + unless ($privhost) { + my $privkey = &get_dom("getdom:$cdom:private:$keynum:ltitools:key"); + if (($privkey ne '') && ($secret ne '')) { + my $cipher = new Crypt::CBC($privkey); + $secret = $cipher->decrypt_hex($secret); + } else { + undef($secret); + } + } + } + } + } + } + return if (($key eq '') || ($secret eq '')); + if ($sigmethod eq '') { + $sigmethod = 'HMAC-SHA1'; + } + if ($type eq '') { + $type = 'request token'; + } + if ($callback eq '') { + $callback = 'about:blank', + } + srand( time() ^ ($$ + ($$ << 15)) ); # Seed rand. + my $nonce = Digest::SHA::sha1_hex(sprintf("%06x%06x",rand(0xfffff0),rand(0xfffff0))); + my $request = Net::OAuth->request($type)->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 ($post) { + return $request->to_post_body(); + } else { + return $request->to_hash(); + } +} + +sub decrypt_secret { + my ($privhost,$secret,$keynum,$type) = @_; + return; +} + 1; __END__