--- loncom/lti/ltipassback.pm 2017/12/15 17:07:09 1.5 +++ loncom/lti/ltipassback.pm 2018/08/14 21:42:36 1.6 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # LTI Consumer Module to receive grades passed back by Provider # -# $Id: ltipassback.pm,v 1.5 2017/12/15 17:07:09 raeburn Exp $ +# $Id: ltipassback.pm,v 1.6 2018/08/14 21:42:36 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -29,6 +29,7 @@ package Apache::ltipassback; use strict; +use URI::Escape; use Apache::Constants qw(:common :http); use Apache::lonnet; use Apache::loncommon; @@ -38,14 +39,56 @@ use LONCAPA::ltiutils; sub handler { my $r = shift; my %errors; + my $params = {}; + my ($oauthtype,$authheader,$xmlbody); +# +# Retrieve content type from headers +# + my $content_type = $r->headers_in->get('Content-Type'); + if ($content_type eq 'application/xml') { + $oauthtype = 'consumer'; +# +# Retrieve OAuth data from Authorization header sent by LTI Provider +# + $authheader = $r->headers_in->get('Authorization'); + my ($authtype,$valuestr) = ($authheader =~ /^(OAuth)\s+(.+)$/i); + if (lc($authtype) eq 'oauth') { + foreach my $pair (split(/\s*,\s*/,$valuestr)) { + my ($key,$value) = split(/=/,$pair); + $value =~ s /(^"|"$)//g; + $params->{$key} = URI::Escape::uri_unescape($value); + } + } +# +# Retrieve message body +# + my $length = $r->headers_in->get('Content-length'); + if ($length) { + $r->read($xmlbody,$length,0); + if ($xmlbody ne '') { + my %grades = &LONCAPA::ltiutils::parse_grade_xml($xmlbody); + foreach my $num (sort { $a <=> $b } (keys(%grades))) { + if (ref($grades{$num}) eq 'HASH') { + if (($grades{$num}{'sourcedid'} ne '') && ($grades{$num}{'score'} ne '')) { + $params->{'sourcedid'} = $grades{$num}{'sourcedid'}; + $params->{'result_resultscore_textstring'} = $grades{$num}{'score'}; + $params->{'result_resultscore_language'} = $grades{$num}{'language'}; + $params->{'result_resultvaluesourcedid'} = 'decimal'; + } + } + } + } + } + } else { + $oauthtype = 'request token'; # # Retrieve data POSTed by LTI Provider # - &Apache::lonacc::get_posted_cgi($r); - my $params = {}; - foreach my $key (sort(keys(%env))) { - if ($key =~ /^form\.(.+)$/) { - $params->{$1} = $env{$key}; + &Apache::lonacc::get_posted_cgi($r); + foreach my $key (sort(keys(%env))) { + if ($key =~ /^form\.(.+)$/) { + $params->{$1} = $env{$key}; + } } } @@ -119,6 +162,11 @@ sub handler { $marker,$symb,$cdom,$cnum, \%toolsettings,\%ltitools,\%errors); + if (keys(%errors) > 0) { + &invalid_request($r,$params,\%errors); + return OK; + } + # # Verify the signed request using the consumer_key and # secret for the specific LTI Provider. @@ -128,9 +176,24 @@ sub handler { if ($ENV{'SERVER_PORT'} == 443) { $protocol = 'https'; } - unless (LONCAPA::ltiutils::verify_request($params,$protocol,$r->hostname,$r->uri, - $env{'request.method'},$consumer_secret, - \%errors)) { + + unless (LONCAPA::ltiutils::verify_request($oauthtype,$protocol,$r->hostname,$r->uri, + $r->method,$consumer_secret,$params, + $authheader,\%errors)) { + &invalid_request($r,$params,\%errors); + return OK; + } + +# +# Verify XML in request body has not been tampered with +# + + my $bodyhash = Digest::SHA::sha1_base64($xmlbody); + while (length($bodyhash) % 4) { + $bodyhash .= '='; + } + unless ($bodyhash eq $params->{oauth_body_hash}) { + $errors{16} = 1; &invalid_request($r,$params,\%errors); return OK; } @@ -138,10 +201,11 @@ sub handler { # # Determine if nonce in POSTed data has expired. # If unexpired, confirm it has not already been used. +# unless (&LONCAPA::ltiutils::check_nonce($params->{'oauth_nonce'},$params->{'oauth_timestamp'}, $ltitools{'lifetime'},$cdom,$r->dir_config('lonLTIDir'))) { - $errors{16} = 1; + $errors{17} = 1; &invalid_request($r,$params,\%errors); return OK; } @@ -168,7 +232,7 @@ sub handler { %maproles = %{$ltitools{'roles'}}; } unless (keys(%maproles)) { - $errors{21} = 1; + $errors{22} = 1; &invalid_request($r,$params,\%errors); return OK; } @@ -205,12 +269,12 @@ sub handler { } } unless ($hasrole) { - $errors{22} = 1; + $errors{23} = 1; &invalid_request($r,$params,\%errors); return OK; } } else { - $errors{23} = 1; + $errors{24} = 1; &invalid_request($r,$params,\%errors); return OK; } @@ -219,7 +283,6 @@ sub handler { # Store result if one was sent in a valid format. # - my ($result,$resulttype,$lang,$pcf); if (exists($params->{'result_resultvaluesourcedid'})) { $resulttype = $params->{'result_resultvaluesourcedid'};