File:
[LON-CAPA] /
loncom /
Lond.pm
Revision
1.25:
download - view:
text,
annotated -
select for diffs
Thu Dec 28 18:14:08 2023 UTC (11 months, 4 weeks ago) by
raeburn
Branches:
MAIN
CVS tags:
version_2_12_X,
HEAD
- Internal name for conditional portfolio sharing for specific IP addresses
ranges when set by a user is now userip, to distinguish from those set
for externalresponse (inrenal name: ip).
- Portfolio shareability setting in domain defaults (by affiliation), with
possible override for an individual user will apply to portfolio file
requests to a LON-CAPA node running 2.11, when the file owner's homeserver
is 2.12 (or later).
# The LearningOnline Network
#
# $Id: Lond.pm,v 1.25 2023/12/28 18:14:08 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
# This file is part of the LearningOnline Network with CAPA (LON-CAPA).
#
# LON-CAPA is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# LON-CAPA is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with LON-CAPA; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# /home/httpd/html/adm/gpl.txt
#
# http://www.lon-capa.org/
#
###
#NOTE perldoc at the end of file
#TODO move remaining lond functions into this
package LONCAPA::Lond;
use strict;
use lib '/home/httpd/lib/perl/';
use LONCAPA;
use Apache::lonnet;
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 {
my ( $tail, $clientversion ) = @_;
my ( $udom, $uname, $namespace, $regexp, $range ) =
split /:/, $tail;
$regexp = $regexp ? unescape($regexp) : '.';
my ($start,$end);
if (defined($range)) {
if ($range =~ /^(\d+)\-(\d+)$/) {
($start,$end) = ($1,$2);
} elsif ($range =~/^(\d+)$/) {
($start,$end) = (0,$1);
} else {
undef($range);
}
}
#
# 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
return "error: ".($!+0)." tie(GDBM) Failed while attempting dump";
my $qresult = '';
my $count = 0;
#
# When dump is for roles.db, determine if LON-CAPA version checking is needed.
# Sessions on 2.10 and later do not require version checking, as that occurs
# on the server hosting the user session, when constructing the roles/courses
# screen).
#
my $skipcheck;
my @ids = &Apache::lonnet::current_machine_ids();
my %homecourses;
#
# If dump is for roles.db from a pre-2.10 server, determine the LON-CAPA
# version on the server which requested the data.
#
if ($namespace eq 'roles') {
if ($clientversion =~ /^\'?(\d+)\.(\d+)\.[\w.\-]+\'?/) {
$major = $1;
$minor = $2;
}
if (($major > 2) || (($major == 2) && ($minor > 9))) {
$skipcheck = 1;
}
$now = time;
}
while (my ($key,$value) = each(%$hashref)) {
if ($namespace eq 'roles' && (!$skipcheck)) {
if ($key =~ m{^/($LONCAPA::match_domain)/($LONCAPA::match_courseid)(/?[^_]*)_(cc|co|in|ta|ep|ad|st|cr)$}) {
my $cdom = $1;
my $cnum = $2;
my ($role,$roleend,$rolestart) = split(/\_/,$value);
if (!$roleend || $roleend > $now) {
#
# For active course roles, check that requesting server is running a LON-CAPA
# version which meets any version requirements for the course. Do not include
# the role amongst the results returned if the requesting server's version is
# too old.
#
# This determination is handled differently depending on whether the course's
# homeserver is the current server, or whether it is a different server.
# In both cases, the course's version requirement needs to be retrieved.
#
next unless (&releasereqd_check($cnum,$cdom,$key,$value,$major,
$minor,\%homecourses,\@ids));
}
}
}
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 '.') {
$count++;
if (defined($range) && $count >= $end) { last; }
if (defined($range) && $count < $start) { next; }
$qresult.=$key.'='.$value.'&';
} else {
my $unescapeKey = &unescape($key);
if (eval('$unescapeKey=~/$regexp/')) {
$count++;
if (defined($range) && $count >= $end) { last; }
if (defined($range) && $count < $start) { next; }
$qresult.="$key=$value&";
}
}
}
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
return "error: ".($!+0)." untie(GDBM) Failed while attempting dump";
#
# If dump is for roles.db from a pre-2.10 server, check if the LON-CAPA
# version requirements for courses for which the current server is the home
# server permit course roles to be usable on the client server hosting the
# user's session. If so, include those role results in the data returned to
# the client server.
#
if (($namespace eq 'roles') && (!$skipcheck)) {
if (keys(%homecourses) > 0) {
$qresult .= &check_homecourses(\%homecourses,$regexp,$count,
$range,$start,$end,$major,$minor);
}
}
chop($qresult);
return $qresult;
}
sub releasereqd_check {
my ($cnum,$cdom,$key,$value,$major,$minor,$homecourses,$ids) = @_;
my $home = &Apache::lonnet::homeserver($cnum,$cdom);
return if ($home eq 'no_host');
my ($reqdmajor,$reqdminor,$displayrole);
if ($cnum =~ /$LONCAPA::match_community/) {
if ($major eq '' && $minor eq '') {
return unless ((ref($ids) eq 'ARRAY') &&
(grep(/^\Q$home\E$/,@{$ids})));
} else {
$reqdmajor = 2;
$reqdminor = 9;
return unless (&useable_role($reqdmajor,$reqdminor,$major,$minor));
}
}
my $hashid = $cdom.':'.$cnum;
my ($courseinfo,$cached) =
&Apache::lonnet::is_cached_new('courseinfo',$hashid);
if (defined($cached)) {
if (ref($courseinfo) eq 'HASH') {
if (exists($courseinfo->{'releaserequired'})) {
my ($reqdmajor,$reqdminor) = split(/\./,$courseinfo->{'releaserequired'});
return unless (&useable_role($reqdmajor,$reqdminor,$major,$minor));
}
}
} else {
if (ref($ids) eq 'ARRAY') {
if (grep(/^\Q$home\E$/,@{$ids})) {
if (ref($homecourses) eq 'HASH') {
if (ref($homecourses->{$cdom}) eq 'HASH') {
if (ref($homecourses->{$cdom}{$cnum}) eq 'HASH') {
if (ref($homecourses->{$cdom}{$cnum}) eq 'ARRAY') {
push(@{$homecourses->{$cdom}{$cnum}},{$key=>$value});
} else {
$homecourses->{$cdom}{$cnum} = [{$key=>$value}];
}
} else {
$homecourses->{$cdom}{$cnum} = [{$key=>$value}];
}
} else {
$homecourses->{$cdom}{$cnum} = [{$key=>$value}];
}
}
return;
}
}
my $courseinfo = &get_courseinfo_hash($cnum,$cdom,$home);
if (ref($courseinfo) eq 'HASH') {
if (exists($courseinfo->{'releaserequired'})) {
my ($reqdmajor,$reqdminor) = split(/\./,$courseinfo->{'releaserequired'});
return unless (&useable_role($reqdmajor,$reqdminor,$major,$minor));
}
} else {
return;
}
}
return 1;
}
sub check_homecourses {
my ($homecourses,$regexp,$count,$range,$start,$end,$major,$minor) = @_;
my ($result,%addtocache);
my $yesterday = time - 24*3600;
if (ref($homecourses) eq 'HASH') {
my (%okcourses,%courseinfo,%recent);
foreach my $domain (keys(%{$homecourses})) {
my $hashref =
&tie_domain_hash($domain, "nohist_courseids", &GDBM_WRCREAT());
if (ref($hashref) eq 'HASH') {
while (my ($key,$value) = each(%$hashref)) {
my $unesc_key = &unescape($key);
if ($unesc_key =~ /^lasttime:(\w+)$/) {
my $cid = $1;
$cid =~ s/_/:/;
if ($value > $yesterday ) {
$recent{$cid} = 1;
}
next;
}
my $items = &Apache::lonnet::thaw_unescape($value);
if (ref($items) eq 'HASH') {
my ($cdom,$cnum) = split(/_/,$unesc_key);
my $hashid = $cdom.':'.$cnum;
$courseinfo{$hashid} = $items;
if (ref($homecourses->{$cdom}{$cnum}) eq 'ARRAY') {
my ($reqdmajor,$reqdminor) = split(/\./,$items->{'releaserequired'});
if (&useable_role($reqdmajor,$reqdminor,$major,$minor)) {
$okcourses{$hashid} = 1;
}
}
}
}
unless (&untie_domain_hash($hashref)) {
&Apache::lonnet::logthis("Failed to untie tied hash for nohist_courseids.db for $domain");
}
} else {
&Apache::lonnet::logthis("Failed to tie hash for nohist_courseids.db for $domain");
}
}
foreach my $hashid (keys(%recent)) {
my ($result,$cached)=&Apache::lonnet::is_cached_new('courseinfo',$hashid);
unless ($cached) {
&Apache::lonnet::do_cache_new('courseinfo',$hashid,$courseinfo{$hashid},600);
}
}
foreach my $cdom (keys(%{$homecourses})) {
if (ref($homecourses->{$cdom}) eq 'HASH') {
foreach my $cnum (keys(%{$homecourses->{$cdom}})) {
my $hashid = $cdom.':'.$cnum;
next if ($recent{$hashid});
&Apache::lonnet::do_cache_new('courseinfo',$hashid,$courseinfo{$hashid},600);
}
}
}
foreach my $hashid (keys(%okcourses)) {
my ($cdom,$cnum) = split(/:/,$hashid);
if ((ref($homecourses->{$cdom}) eq 'HASH') &&
(ref($homecourses->{$cdom}{$cnum}) eq 'ARRAY')) {
foreach my $role (@{$homecourses->{$cdom}{$cnum}}) {
if (ref($role) eq 'HASH') {
while (my ($key,$value) = each(%{$role})) {
if ($regexp eq '.') {
$count++;
if (defined($range) && $count >= $end) { last; }
if (defined($range) && $count < $start) { next; }
$result.=$key.'='.$value.'&';
} else {
my $unescapeKey = &unescape($key);
if (eval('$unescapeKey=~/$regexp/')) {
$count++;
if (defined($range) && $count >= $end) { last; }
if (defined($range) && $count < $start) { next; }
$result.="$key=$value&";
}
}
}
}
}
}
}
}
return $result;
}
sub useable_role {
my ($reqdmajor,$reqdminor,$major,$minor) = @_;
if ($reqdmajor ne '' && $reqdminor ne '') {
return if (($major eq '' && $minor eq '') ||
($major < $reqdmajor) ||
(($major == $reqdmajor) && ($minor < $reqdminor)));
}
return 1;
}
sub get_courseinfo_hash {
my ($cnum,$cdom,$home) = @_;
my %info;
eval {
local($SIG{ALRM}) = sub { die "timeout\n"; };
local($SIG{__DIE__})='DEFAULT';
alarm(3);
%info = &Apache::lonnet::courseiddump($cdom,'.',1,'.','.',$cnum,1,[$home],'.');
alarm(0);
};
if ($@) {
if ($@ eq "timeout\n") {
&Apache::lonnet::logthis("<font color='blue'>WARNING courseiddump for $cnum:$cdom from $home timedout</font>");
} else {
&Apache::lonnet::logthis("<font color='yellow'>WARNING unexpected error during eval of call for courseiddump from $home</font>");
}
} else {
if (ref($info{$cdom.'_'.$cnum}) eq 'HASH') {
my $hashid = $cdom.':'.$cnum;
return &Apache::lonnet::do_cache_new('courseinfo',$hashid,$info{$cdom.'_'.$cnum},600);
}
}
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 {
my ($tail) = @_;
my ($udom,$since,$description,$instcodefilter,$ownerfilter,$coursefilter,
$typefilter,$regexp_ok,$rtn_as_hash,$selfenrollonly,$catfilter,$showhidden,
$caller,$cloner,$cc_clone_list,$cloneonly,$createdbefore,$createdafter,
$creationcontext,$domcloner,$hasuniquecode,$reqcrsdom,$reqinstcode) = split(/:/,$tail);
my $now = time;
my ($cloneruname,$clonerudom,%cc_clone);
if (defined($description)) {
$description=&unescape($description);
} else {
$description='.';
}
if (defined($instcodefilter)) {
$instcodefilter=&unescape($instcodefilter);
} else {
$instcodefilter='.';
}
my ($ownerunamefilter,$ownerdomfilter);
if (defined($ownerfilter)) {
$ownerfilter=&unescape($ownerfilter);
if ($ownerfilter ne '.' && defined($ownerfilter)) {
if ($ownerfilter =~ /^([^:]*):([^:]*)$/) {
$ownerunamefilter = $1;
$ownerdomfilter = $2;
} else {
$ownerunamefilter = $ownerfilter;
$ownerdomfilter = '';
}
}
} else {
$ownerfilter='.';
}
if (defined($coursefilter)) {
$coursefilter=&unescape($coursefilter);
} else {
$coursefilter='.';
}
if (defined($typefilter)) {
$typefilter=&unescape($typefilter);
} else {
$typefilter='.';
}
if (defined($regexp_ok)) {
$regexp_ok=&unescape($regexp_ok);
}
if (defined($catfilter)) {
$catfilter=&unescape($catfilter);
}
if (defined($cloner)) {
$cloner = &unescape($cloner);
($cloneruname,$clonerudom) = ($cloner =~ /^($LONCAPA::match_username):($LONCAPA::match_domain)$/);
}
if (defined($cc_clone_list)) {
$cc_clone_list = &unescape($cc_clone_list);
my @cc_cloners = split('&',$cc_clone_list);
foreach my $cid (@cc_cloners) {
my ($clonedom,$clonenum) = split(':',$cid);
next if ($clonedom ne $udom);
$cc_clone{$clonedom.'_'.$clonenum} = 1;
}
}
if ($createdbefore ne '') {
$createdbefore = &unescape($createdbefore);
} else {
$createdbefore = 0;
}
if ($createdafter ne '') {
$createdafter = &unescape($createdafter);
} else {
$createdafter = 0;
}
if ($creationcontext ne '') {
$creationcontext = &unescape($creationcontext);
} else {
$creationcontext = '.';
}
unless ($hasuniquecode) {
$hasuniquecode = '.';
}
if ($reqinstcode ne '') {
$reqinstcode = &unescape($reqinstcode);
}
my $unpack = 1;
if ($description eq '.' && $instcodefilter eq '.' && $ownerfilter eq '.' &&
$typefilter eq '.') {
$unpack = 0;
}
if (!defined($since)) { $since=0; }
my (%gotcodedefaults,%otcodedefaults);
my $qresult='';
my $hashref = &tie_domain_hash($udom, "nohist_courseids", &GDBM_WRCREAT())
or return "error: ".($!+0)." tie(GDBM) Failed while attempting courseiddump";
while (my ($key,$value) = each(%$hashref)) {
my ($unesc_key,$lasttime_key,$lasttime,$is_hash,%val,
%unesc_val,$selfenroll_end,$selfenroll_types,$created,
$context);
$unesc_key = &unescape($key);
if ($unesc_key =~ /^lasttime:/) {
next;
} else {
$lasttime_key = &escape('lasttime:'.$unesc_key);
}
if ($hashref->{$lasttime_key} ne '') {
$lasttime = $hashref->{$lasttime_key};
next if ($lasttime<$since);
}
my ($canclone,$valchange,$clonefromcode);
my $items = &Apache::lonnet::thaw_unescape($value);
if (ref($items) eq 'HASH') {
if ($hashref->{$lasttime_key} eq '') {
next if ($since > 1);
}
if ($items->{'inst_code'}) {
$clonefromcode = $items->{'inst_code'};
}
$is_hash = 1;
if ($domcloner) {
$canclone = 1;
} elsif (defined($clonerudom)) {
if ($items->{'cloners'}) {
my @cloneable = split(',',$items->{'cloners'});
if (@cloneable) {
if (grep(/^\*$/,@cloneable)) {
$canclone = 1;
} elsif (grep(/^\*:\Q$clonerudom\E$/,@cloneable)) {
$canclone = 1;
} elsif (grep(/^\Q$cloneruname\E:\Q$clonerudom\E$/,@cloneable)) {
$canclone = 1;
}
}
unless ($canclone) {
if ($cloneruname ne '' && $clonerudom ne '') {
if ($cc_clone{$unesc_key}) {
$canclone = 1;
$items->{'cloners'} .= ','.$cloneruname.':'.
$clonerudom;
$valchange = 1;
}
}
}
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)) {
if ($cc_clone{$unesc_key}) {
$canclone = 1;
$items->{'cloners'} = $cloneruname.':'.$clonerudom;
$valchange = 1;
}
unless ($canclone) {
if ($items->{'owner'} =~ /:/) {
if ($items->{'owner'} eq $cloner) {
$canclone = 1;
}
} elsif ($cloner eq $items->{'owner'}.':'.$udom) {
$canclone = 1;
}
if ($canclone) {
$items->{'cloners'} = $cloneruname.':'.$clonerudom;
$valchange = 1;
}
}
}
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) {
$unesc_val{'descr'} = $items->{'description'};
$unesc_val{'inst_code'} = $items->{'inst_code'};
$unesc_val{'owner'} = $items->{'owner'};
$unesc_val{'type'} = $items->{'type'};
$unesc_val{'cloners'} = $items->{'cloners'};
$unesc_val{'created'} = $items->{'created'};
$unesc_val{'context'} = $items->{'context'};
}
$selfenroll_types = $items->{'selfenroll_types'};
$selfenroll_end = $items->{'selfenroll_end_date'};
$created = $items->{'created'};
$context = $items->{'context'};
if ($selfenrollonly) {
next if (!$selfenroll_types);
if (($selfenroll_end > 0) && ($selfenroll_end <= $now)) {
next;
}
}
if ($creationcontext ne '.') {
next if (($context ne '') && ($context ne $creationcontext));
}
if ($createdbefore > 0) {
next if (($created eq '') || ($created > $createdbefore));
}
if ($createdafter > 0) {
next if (($created eq '') || ($created <= $createdafter));
}
if ($catfilter ne '') {
next if ($items->{'categories'} eq '');
my @categories = split('&',$items->{'categories'});
next if (@categories == 0);
my @subcats = split('&',$catfilter);
my $matchcat = 0;
foreach my $cat (@categories) {
if (grep(/^\Q$cat\E$/,@subcats)) {
$matchcat = 1;
last;
}
}
next if (!$matchcat);
}
if ($caller eq 'coursecatalog') {
if ($items->{'hidefromcat'} eq 'yes') {
next if !$showhidden;
}
}
if ($hasuniquecode ne '.') {
next unless ($items->{'uniquecode'});
}
} else {
next if ($catfilter ne '');
next if ($selfenrollonly);
next if ($createdbefore || $createdafter);
next if ($creationcontext ne '.');
if ((defined($clonerudom)) && (defined($cloneruname))) {
if ($cc_clone{$unesc_key}) {
$canclone = 1;
$val{'cloners'} = &escape($cloneruname.':'.$clonerudom);
}
}
$is_hash = 0;
my @courseitems = split(/:/,$value);
$lasttime = pop(@courseitems);
if ($hashref->{$lasttime_key} eq '') {
next if ($lasttime<$since);
}
($val{'descr'},$val{'inst_code'},$val{'owner'},$val{'type'}) = @courseitems;
}
if ($cloneonly) {
next unless ($canclone);
}
my $match = 1;
if ($description ne '.') {
if (!$is_hash) {
$unesc_val{'descr'} = &unescape($val{'descr'});
}
if (eval{$unesc_val{'descr'} !~ /\Q$description\E/i}) {
$match = 0;
}
}
if ($instcodefilter ne '.') {
if (!$is_hash) {
$unesc_val{'inst_code'} = &unescape($val{'inst_code'});
}
if ($regexp_ok == 1) {
if (eval{$unesc_val{'inst_code'} !~ /$instcodefilter/}) {
$match = 0;
}
} elsif ($regexp_ok == -1) {
if (eval{$unesc_val{'inst_code'} =~ /$instcodefilter/}) {
$match = 0;
}
} else {
if (eval{$unesc_val{'inst_code'} !~ /\Q$instcodefilter\E/i}) {
$match = 0;
}
}
}
if ($ownerfilter ne '.') {
if (!$is_hash) {
$unesc_val{'owner'} = &unescape($val{'owner'});
}
if (($ownerunamefilter ne '') && ($ownerdomfilter ne '')) {
if ($unesc_val{'owner'} =~ /:/) {
if (eval{$unesc_val{'owner'} !~
/\Q$ownerunamefilter\E:\Q$ownerdomfilter\E$/i}) {
$match = 0;
}
} else {
if (eval{$unesc_val{'owner'} !~ /\Q$ownerunamefilter\E/i}) {
$match = 0;
}
}
} elsif ($ownerunamefilter ne '') {
if ($unesc_val{'owner'} =~ /:/) {
if (eval{$unesc_val{'owner'} !~ /\Q$ownerunamefilter\E:[^:]+$/i}) {
$match = 0;
}
} else {
if (eval{$unesc_val{'owner'} !~ /\Q$ownerunamefilter\E/i}) {
$match = 0;
}
}
} elsif ($ownerdomfilter ne '') {
if ($unesc_val{'owner'} =~ /:/) {
if (eval{$unesc_val{'owner'} !~ /^[^:]+:\Q$ownerdomfilter\E/}) {
$match = 0;
}
} else {
if ($ownerdomfilter ne $udom) {
$match = 0;
}
}
}
}
if ($coursefilter ne '.') {
if (eval{$unesc_key !~ /^$udom(_)\Q$coursefilter\E$/}) {
$match = 0;
}
}
if ($typefilter ne '.') {
if (!$is_hash) {
$unesc_val{'type'} = &unescape($val{'type'});
}
if ($unesc_val{'type'} eq '') {
if ($typefilter ne 'Course') {
$match = 0;
}
} else {
if (eval{$unesc_val{'type'} !~ /^\Q$typefilter\E$/}) {
$match = 0;
}
}
}
if ($match == 1) {
if ($rtn_as_hash) {
if ($is_hash) {
if ($valchange) {
my $newvalue = &Apache::lonnet::freeze_escape($items);
$qresult.=$key.'='.$newvalue.'&';
} else {
$qresult.=$key.'='.$value.'&';
}
} else {
my %rtnhash = ( 'description' => &unescape($val{'descr'}),
'inst_code' => &unescape($val{'inst_code'}),
'owner' => &unescape($val{'owner'}),
'type' => &unescape($val{'type'}),
'cloners' => &unescape($val{'cloners'}),
);
my $items = &Apache::lonnet::freeze_escape(\%rtnhash);
$qresult.=$key.'='.$items.'&';
}
} else {
if ($is_hash) {
$qresult .= $key.'='.&escape($unesc_val{'descr'}).':'.
&escape($unesc_val{'inst_code'}).':'.
&escape($unesc_val{'owner'}).'&';
} else {
$qresult .= $key.'='.$val{'descr'}.':'.$val{'inst_code'}.
':'.$val{'owner'}.'&';
}
}
}
}
&untie_domain_hash($hashref) or
return "error: ".($!+0)." untie(GDBM) Failed while attempting courseiddump";
chop($qresult);
return $qresult;
}
sub dump_profile_database {
my ($tail) = @_;
my ($udom,$uname,$namespace) = split(/:/,$tail);
my $hashref = &tie_user_hash($udom, $uname, $namespace, &GDBM_READER()) or
return "error: ".($!+0)." tie(GDBM) Failed while attempting currentdump";
# Structure of %data:
# $data{$symb}->{$parameter}=$value;
# $data{$symb}->{'v.'.$parameter}=$version;
# since $parameter will be unescaped, we do not
# have to worry about silly parameter names...
my $qresult='';
my %data = (); # A hash of anonymous hashes..
while (my ($key,$value) = each(%$hashref)) {
my ($v,$symb,$param) = split(/:/,$key);
next if ($v eq 'version' || $symb eq 'keys');
next if (exists($data{$symb}) &&
exists($data{$symb}->{$param}) &&
$data{$symb}->{'v.'.$param} > $v);
$data{$symb}->{$param}=$value;
$data{$symb}->{'v.'.$param}=$v;
}
&untie_user_hash($hashref) or
return "error: ".($!+0)." untie(GDBM) Failed while attempting currentdump";
while (my ($symb,$param_hash) = each(%data)) {
while(my ($param,$value) = each (%$param_hash)){
next if ($param =~ /^v\./); # Ignore versions...
#
# Just dump the symb=value pairs separated by &
#
$qresult.=$symb.':'.$param.'='.$value.'&';
}
}
chop($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;
__END__
=head1 NAME
LONCAPA::Lond.pm
=head1 SYNOPSIS
#TODO
=head1 DESCRIPTION
#TODO
=head1 METHODS
=over 4
=item dump_with_regexp( $tail, $client )
Dump a profile database with an optional regular expression to match against
the keys. In this dump, no effort is made to separate symb from version
information. Presumably the databases that are dumped by this command are of a
different structure. Need to look at this and improve the documentation of
both this and the currentdump handler.
$tail a colon separated list containing
=over
=item domain
=item user
identifying the user.
=item namespace
identifying the database.
=item regexp
optional regular expression that is matched against database keywords to do
selective dumps.
=item range
optional range of entries e.g., 10-20 would return the 10th to 19th items, etc.
=back
$client is the channel open on the client.
Returns: 1 (Continue processing).
Side effects: response is written to $client.
=item dump_course_id_handler
#TODO copy from lond
=item dump_profile_database
#TODO copy from lond
=item releasereqd_check( $cnum, $cdom, $key, $value, $major, $minor,
$homecourses, $ids )
releasereqd_check() will determine if a LON-CAPA version (defined in the
$major,$minor args passed) is not too old to allow use of a role in a
course ($cnum,$cdom args passed), if at least one of the following applies:
(a) the course is a Community, (b) the course's home server is *not* the
current server, or (c) cached course information is not stale.
For the case where none of these apply, the course is added to the
$homecourse hash ref (keys = courseIDs, values = array of a hash of roles).
The $homecourse hash ref is for courses for which the current server is the
home server. LON-CAPA version requirements are checked elsewhere for the
items in $homecourse.
=item check_homecourses( $homecourses, $regexp, $count, $range, $start, $end,
$major, $minor )
check_homecourses() will retrieve course information for those courses which
are keys of the $homecourses hash ref (first arg). The nohist_courseids.db
GDBM file is tied and course information for each course retrieved. Last
visit (lasttime key) is also retrieved for each, and cached values updated
for any courses last visited less than 24 hours ago. Cached values are also
updated for any courses included in the $homecourses hash ref.
The reason for the 24 hours constraint is that the cron entry in
/etc/cron.d/loncapa for /home/httpd/perl/refresh_courseids_db.pl causes
cached course information to be updated nightly for courses with activity
within the past 24 hours.
Role information for the user (included in a ref to an array of hashes as the
value for each key in $homecourses) is appended to the result returned by the
routine, which will in turn be appended to the string returned to the client
hosting the user's session.
=item useable_role( $reqdmajor, $reqdminor, $major, $minor )
useable_role() will compare the LON-CAPA version required by a course with
the version available on the client server. If the client server's version
is compatible, 1 will be returned.
=item get_courseinfo_hash( $cnum, $cdom, $home )
get_courseinfo_hash() is used to retrieve course information from the db
file: nohist_courseids.db for a course for which the current server is *not*
the home server.
A hash of a hash will be retrieved. The outer hash contains a single key --
courseID -- for the course for which the data are being requested.
The contents of the inner hash, for that single item in the outer hash
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
=head1 BUGS
No known bugs at this time.
=head1 SEE ALSO
L<Apache::lonnet>, L<lond>
=cut
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>