Annotation of loncom/auth/lonwebdavauth.pm, revision 1.8

1.1       raeburn     1: # The LearningOnline Network
                      2: # Authentication Handler for webDAV access to Authoring Space.
                      3: #
1.8     ! raeburn     4: # $Id: lonwebdavauth.pm,v 1.7 2017/01/30 00:16:31 raeburn Exp $
1.1       raeburn     5: #
                      6: # Copyright Michigan State University Board of Trustees
                      7: #
                      8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
                      9: #
                     10: # LON-CAPA is free software; you can redistribute it and/or modify
                     11: # it under the terms of the GNU General Public License as published by
                     12: # the Free Software Foundation; either version 2 of the License, or
                     13: # (at your option) any later version.
                     14: #
                     15: # LON-CAPA is distributed in the hope that it will be useful,
                     16: # but WITHOUT ANY WARRANTY; without even the implied warranty of
                     17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     18: # GNU General Public License for more details.
                     19: #
                     20: # You should have received a copy of the GNU General Public License
                     21: # along with LON-CAPA; if not, write to the Free Software
                     22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
                     23: #
                     24: # /home/httpd/html/adm/gpl.txt
                     25: #
                     26: # http://www.lon-capa.org/
                     27: #
                     28: 
                     29: =head1 NAME
                     30: 
                     31: Apache::lonwebdavauth - webDAV Authentication Handler
                     32: 
                     33: =head1 SYNOPSIS
                     34: 
1.3       raeburn    35: Invoked for ^/+webdav/[\w\-.]+/\w[\w.\-\@]+/ by 
1.1       raeburn    36: /etc/httpd/conf/loncapa_apache.conf:
                     37: 
                     38: PerlAuthenHandler	Apache::lonwebdavauth
                     39: 
                     40: =head1 INTRODUCTION
                     41: 
                     42: This module employs Apache Basic Auth authentication and is used
                     43: to provide authentication for webDAV client access to author
                     44: space(s). Note: because Apache Basic Auth is used, webDAV access
                     45: is only available for servers running Apache with SSL.
                     46: 
                     47: If the webDAV client supports cookies then whenever the client  
                     48: sends the cookie back, a check is made to see if a corresponding
                     49: valid session file exists in the server's webDAV session directory.
                     50: Support for cookies by webDAV clients is optional.
                     51: 
                     52: If a valid session file exists, a cookie is returned containing
                     53: the session ID,  otherwise a new session file is created and
                     54: returned in the cookie.
                     55: 
                     56: The perlvar "lonDAVsessDir" in /etc/httpd/conf/loncapa_apache.conf
                     57: provides the directory location: /home/httpd/webdav/sessionIDs.
                     58: 
                     59: If the session is stale, or the cookie is missing or invalid, 
1.4       raeburn    60: the user is re-challenged for login information, by sending
                     61: an Apache Basic Auth request to the client.
1.1       raeburn    62: 
1.4       raeburn    63: If Apache Basic Auth is successful authentication will
1.1       raeburn    64: result in creation of a webDAV session file containing a 
                     65: minimal set of information about the user which will also be 
                     66: loaded into the user's environment.  The environment persists
                     67: until the end of the Apache request, when it is cleaned up
                     68: by lonacc::cleanup, as is the case for requests for non-webdav URLs.
                     69: 
                     70: If a valid session file exists, a cookie is returned containing
                     71: the session ID, otherwise a new session file is created and
                     72: returned in the cookie.
                     73: 
                     74: This is part of the LearningOnline Network with CAPA project
                     75: described at http://www.lon-capa.org.
                     76: 
                     77: =head1 HANDLER SUBROUTINE
                     78: 
                     79: This routine is called by Apache and mod_perl.
                     80: 
                     81: =over 4
                     82: 
                     83: =item *
                     84: 
                     85: Check for valid webDAV session
                     86: 
                     87: =item *
                     88: 
1.4       raeburn    89: No session? return AUTH_REQUIRED which will prompt 
                     90: webDAV client to authenticate user (via Apache Basic Auth). 
1.1       raeburn    91: 
                     92: =item *
                     93: 
                     94: If authenticated, call &init_webdav_env() to create sessionID
                     95: and populate environment, and send header containing cookie.
                     96: 
                     97: =back
                     98: 
                     99: =head1 NOTABLE SUBROUTINES
                    100: 
                    101: =over
                    102: 
                    103: =item * init_webdav_env()
                    104: 
                    105: =over
                    106: 
                    107: =item *
                    108: 
                    109: Checks for valid session files in webDAV session directory.
                    110: 
                    111: =item * 
                    112: 
                    113: Calls Apache::lonnet::transfer_profile_to_env() to create
                    114: %env for user if session is valid. 
                    115: 
                    116: =item * 
                    117: 
                    118: Otherwise creates new session file and populates %env. 
                    119: 
                    120: =item *
                    121: 
                    122: Session ID file is a GDBM file containing:
                    123: 
                    124: =over
                    125: 
                    126: =item * user.name, user.domain, user.home, user.environment
                    127: 
                    128: =item * user.role entries for:
                    129: 
                    130: active author, co-author, and assistant co-author roles
                    131: in domains hosted on this machine.
                    132: 
                    133: =back
                    134: 
                    135: =back
                    136: 
                    137: =back
                    138: 
                    139: =cut 
                    140: 
                    141: package Apache::lonwebdavauth;
                    142: 
                    143: use strict;
                    144: use GDBM_File;
                    145: use Apache::Constants qw(:common :http :methods);
                    146: use Apache::lonnet;
                    147: use LONCAPA qw(:DEFAULT :match);
                    148: 
                    149: sub handler {
                    150:     my $r = shift;
                    151:     my $now = time;
                    152:     my $timetolive = 600;
                    153:     my $sessiondir=$r->dir_config('lonDAVsessDir');
                    154:     my ($uname,$udom,$uhome);
                    155: 
                    156: # Check for cookie
                    157:     my $handle = &Apache::lonnet::check_for_valid_session($r,'lonDAV');
                    158:     if ($handle ne '') {
                    159:         my $sesstime;
                    160:         ($uname,$udom,$sesstime,$uhome) =  
                    161:             ($handle =~ /^($match_username)_($match_domain)_(\d+)_\d+_(.+)$/);
                    162:         if ($sesstime) {
                    163:             if ($now-$sesstime < $timetolive) {
                    164:                 if (&Apache::lonnet::homeserver($uname,$udom) eq $uhome) {
                    165:                     &Apache::lonnet::transfer_profile_to_env($sessiondir,$handle);
1.2       raeburn   166:                     if (&Apache::lonnet::usertools_access($uname,$udom,'webdav')) {
1.7       raeburn   167:                         if ($r->user() eq '') {
                    168:                             if ($env{'user.domain'} eq $r->dir_config('lonDefDomain')) {
                    169:                                 $r->user($env{'user.name'});
                    170:                             } else {
                    171:                                 $r->user($env{'user.name'}.':'.$env{'user.domain'});
                    172:                             }
                    173:                         }
1.2       raeburn   174:                         return OK;
                    175:                     } else {
                    176:                         return FORBIDDEN;
                    177:                     }
1.1       raeburn   178:                 }
                    179:             }
                    180:         }
                    181:     }
                    182: 
                    183:     my ($status,$upass) = $r->get_basic_auth_pw;
                    184:     return $status unless ($status == 0 || $status == OK);
                    185: 
                    186:     if ($r->user =~ /,/) {
                    187:         ($uname,$udom) = split(/,/,$r->user);
1.5       raeburn   188:         $uname =~ s/^\s+//;
                    189:         $uname =~ s/\s+$//;
                    190:         $udom =~ s/^\s+//;
                    191:         $udom =~ s/\s+$//;
1.1       raeburn   192:         unless (($uname =~ /^$match_username$/) && ($udom =~ /^$match_domain$/)) {
                    193:             $r->note_basic_auth_failure;
                    194:             return AUTH_REQUIRED;
                    195:         }
                    196:     } else {
                    197:         $uname = $r->user;
1.5       raeburn   198:         $uname =~ s/^\s+//;
                    199:         $uname =~ s/\s+$//;
1.1       raeburn   200:         ($udom) = ($r->uri =~ m{^/webdav/($match_domain)/});
1.4       raeburn   201:         unless (($udom ne '' ) && ($uname =~ /^$match_username$/) && ($upass ne '')) {
1.1       raeburn   202:             $r->note_basic_auth_failure;
                    203:             return AUTH_REQUIRED;
                    204:         }
                    205:     }
                    206:     if (&Apache::lonnet::domain($udom) ne '') {
                    207:         if (&Apache::lonnet::homeserver($uname,$udom) ne 'no_host') {
                    208:             my $uhome = &Apache::lonnet::authenticate($uname,$upass,$udom);
                    209:             if (($uhome ne 'no_host') && 
                    210:                 (&Apache::lonnet::hostname($uhome) ne '')) {
1.3       raeburn   211:                 my ($author) = ($r->uri =~ m{^/webdav/($match_domain/$match_username)/});
                    212:                 $handle = &init_webdav_env($r,$sessiondir,$uname,$udom,
                    213:                                            $uhome,$now,$timetolive,$author);
1.1       raeburn   214:                 if ($handle ne '') {
1.2       raeburn   215:                     if (&Apache::lonnet::usertools_access($uname,$udom,'webdav')) {
                    216:                         my $cookie = "lonDAV=$handle; path=/webdav/; secure; HttpOnly;";
                    217:                         $r->header_out('Set-cookie' => $cookie);
                    218:                         $r->send_http_header;
                    219:                         return OK;
                    220:                     } else {
                    221:                         return FORBIDDEN;
                    222:                     }
1.1       raeburn   223:                 }
                    224:             }
                    225:         }
                    226:     }
                    227:     $r->note_basic_auth_failure;
                    228:     return AUTH_REQUIRED;
                    229: }
                    230: 
                    231: sub init_webdav_env {
1.3       raeburn   232:     my ($r,$sessiondir,$uname,$udom,$uhome,$now,$timetolive,$author) = @_;
1.1       raeburn   233:     my $handle;
                    234:     my $currnewest = 0;
                    235:     if ($sessiondir ne '') {
                    236:         if (opendir(DIR,$sessiondir)) {
                    237:             while (my $filename=readdir(DIR)) {
                    238:                 if ($filename=~/^($uname\_$udom\_(\d+)\_\d+\_$uhome)\.id$/) {
                    239:                     my $oldhandle = $1;
                    240:                     my $sesstime = $2;
                    241:                     if ($now-$sesstime < $timetolive) {
                    242:                         if ($sesstime > $currnewest) {
                    243:                             $currnewest = $sesstime;
                    244:                             if ($handle ne '') {
                    245:                                 unlink("$sessiondir/$handle.id");
                    246:                             }
                    247:                             $handle = $oldhandle;
                    248:                             next;
                    249:                         }
                    250:                     }
                    251:                     unlink($sessiondir.'/'.$filename);
                    252:                 }
                    253:             }
                    254:             closedir(DIR);
                    255:         }
                    256:     }
                    257:     if ($handle ne '') {
                    258:         &Apache::lonnet::transfer_profile_to_env($sessiondir,$handle);
                    259:         return $handle;
                    260:     } else {
                    261:         my $id = $$.int(rand(10000000));
                    262:         $handle = "$uname\_$udom\_$now\_$id\_$uhome";
                    263:         my $sessionfile = "$sessiondir/$handle.id";
                    264:         if (tie(my %disk_env,'GDBM_File',"$sessionfile",
                    265:                 &GDBM_WRCREAT(),0640)) {
                    266:             $disk_env{'user.name'} = $uname;
                    267:             $disk_env{'user.domain'} = $udom;
                    268:             $disk_env{'user.home'} = $uhome;
1.2       raeburn   269:             my %userenv = &Apache::lonnet::get('environment',['inststatus','tools.webdav'],
                    270:                                                $udom,$uname);
                    271:             my ($tmp) = keys(%userenv);
                    272:             if ($tmp =~ /^(con_lost|error|no_such_host)/i) {
                    273:                 $disk_env{'environment.inststatus'} = $userenv{'inststatus'};
                    274:                 $disk_env{'environment.tools.webdav'} = $userenv{'tools.webdav'};
                    275:             }
1.1       raeburn   276:             $disk_env{'user.environment'} = $sessionfile;
                    277:             my $possroles = ['au','ca','aa'];
                    278:             my @possdoms = &Apache::lonnet::current_machine_domains();
                    279:             my %cstr_roles =
                    280:                 &Apache::lonnet::get_my_roles($uname,$udom,'userroles',
                    281:                                               undef,$possroles,\@possdoms);
1.2       raeburn   282:             if (keys(%cstr_roles) > 0) {
                    283:                 $disk_env{'user.adv'} = 1;
                    284:                 $disk_env{'user.author'} = 1;
                    285:                 foreach my $item (keys(%cstr_roles)) {
                    286:                     my ($aname,$adom,$role) = split(/:/,$item);
                    287:                     if ($role eq 'au') {
                    288:                         $disk_env{"user.role.$role./$adom/"} = $cstr_roles{$item};
                    289:                     } else {
                    290:                         $disk_env{"user.role.$role./$adom/$aname"} = $cstr_roles{$item};
                    291:                     }
1.1       raeburn   292:                 }
                    293:             }
1.2       raeburn   294:             my %is_adv = ( is_adv => $disk_env{'user.adv'} );
                    295:             my %domdef = &Apache::lonnet::get_domain_defaults($udom);
                    296:             $disk_env{'environment.availabletools.webdav'} =
                    297:                 &Apache::lonnet::usertools_access($uname,$udom,'webdav','reload',undef,
                    298:                                                   \%userenv,\%domdef,\%is_adv);
1.1       raeburn   299:             @env{keys(%disk_env)} = @disk_env{keys(%disk_env)};
                    300:             untie(%disk_env);
1.8     ! raeburn   301:             my $ip = &Apache::lonnet::get_requestor_ip($r);
1.3       raeburn   302:             &Apache::lonnet::log($udom,$uname,$uhome,
                    303:                                  "Login webdav/$author $ip");
1.1       raeburn   304:         }
                    305:         return $handle;
                    306:     }
                    307:     return;
                    308: }
                    309: 
                    310: 1;

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