File:  [LON-CAPA] / loncom / auth / lonwebdavauth.pm
Revision 1.2: download - view: text, annotated - select for diffs
Fri Jun 1 11:39:29 2012 UTC (11 years, 10 months ago) by raeburn
Branches: MAIN
CVS tags: version_2_11_0_RC3, version_2_11_0_RC2, version_2_11_0_RC1, version_2_11_0, HEAD
- webDAV access can be controlled by user status and/or overridden for
  specific users.

# The LearningOnline Network
# Authentication Handler for webDAV access to Authoring Space.
#
# $Id: lonwebdavauth.pm,v 1.2 2012/06/01 11:39:29 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/
#

=head1 NAME

Apache::lonwebdavauth - webDAV Authentication Handler

=head1 SYNOPSIS

Invoked for /+webdav/[\w\-]+/[\w\-]+/ by 
/etc/httpd/conf/loncapa_apache.conf:

PerlAuthenHandler	Apache::lonwebdavauth

=head1 INTRODUCTION

This module employs Apache Basic Auth authentication and is used
to provide authentication for webDAV client access to author
space(s). Note: because Apache Basic Auth is used, webDAV access
is only available for servers running Apache with SSL.

If the webDAV client supports cookies then whenever the client  
sends the cookie back, a check is made to see if a corresponding
valid session file exists in the server's webDAV session directory.
Support for cookies by webDAV clients is optional.

If a valid session file exists, a cookie is returned containing
the session ID,  otherwise a new session file is created and
returned in the cookie.

The perlvar "lonDAVsessDir" in /etc/httpd/conf/loncapa_apache.conf
provides the directory location: /home/httpd/webdav/sessionIDs.

If the session is stale, or the cookie is missing or invalid, 
the user is re-challenged for login information. If the perlvar
lonOtherAuthen has been set, Single Sign On will be used, otherwise
an Apache Basic Auth request will be sent to the client.

If Apache Basic Auth is used, successful authentication will
result in creation of a webDAV session file containing a 
minimal set of information about the user which will also be 
loaded into the user's environment.  The environment persists
until the end of the Apache request, when it is cleaned up
by lonacc::cleanup, as is the case for requests for non-webdav URLs.

If a valid session file exists, a cookie is returned containing
the session ID, otherwise a new session file is created and
returned in the cookie.

This is part of the LearningOnline Network with CAPA project
described at http://www.lon-capa.org.

=head1 HANDLER SUBROUTINE

This routine is called by Apache and mod_perl.

=over 4

=item *

Check for valid webDAV session

=item *

No session? - if SSO enabled: return DECLINED

=item *

No session? - if SSO not enabled: return AUTH_REQUIRED
which will prompt webDAV client to authenticate user 
(via Apache Basic Auth).

=item *

If authenticated, call &init_webdav_env() to create sessionID
and populate environment, and send header containing cookie.

=back

=head1 NOTABLE SUBROUTINES

=over

=item * init_webdav_env()

=over

=item *

Checks for valid session files in webDAV session directory.

=item * 

Calls Apache::lonnet::transfer_profile_to_env() to create
%env for user if session is valid. 

=item * 

Otherwise creates new session file and populates %env. 

=item *

Session ID file is a GDBM file containing:

=over

=item * user.name, user.domain, user.home, user.environment

=item * user.role entries for:

active author, co-author, and assistant co-author roles
in domains hosted on this machine.

=back

=back

=back

=cut 

package Apache::lonwebdavauth;

use strict;
use GDBM_File;
use Apache::Constants qw(:common :http :methods);
use Apache::lonnet;
use LONCAPA qw(:DEFAULT :match);

sub handler {
    my $r = shift;
    my $now = time;
    my $timetolive = 600;
    my $sessiondir=$r->dir_config('lonDAVsessDir');
    my ($uname,$udom,$uhome);

# Check for cookie
    my $handle = &Apache::lonnet::check_for_valid_session($r,'lonDAV');
    if ($handle ne '') {
        my $sesstime;
        ($uname,$udom,$sesstime,$uhome) =  
            ($handle =~ /^($match_username)_($match_domain)_(\d+)_\d+_(.+)$/);
        if ($sesstime) {
            if ($now-$sesstime < $timetolive) {
                if (&Apache::lonnet::homeserver($uname,$udom) eq $uhome) {
                    &Apache::lonnet::transfer_profile_to_env($sessiondir,$handle);
                    if (&Apache::lonnet::usertools_access($uname,$udom,'webdav')) {
                        return OK;
                    } else {
                        return FORBIDDEN;
                    }
                }
            }
        }
    }

    if ($r->dir_config('lonOtherAuthen') eq 'yes') {
        if (defined($r->dir_config('lonOtherAuthenType'))) {
            $r->auth_type($r->dir_config('lonOtherAuthenType'));
        }
        return DECLINED;
    }

    my ($status,$upass) = $r->get_basic_auth_pw;
    return $status unless ($status == 0 || $status == OK);

    if ($r->user =~ /,/) {
        ($uname,$udom) = split(/,/,$r->user);
        unless (($uname =~ /^$match_username$/) && ($udom =~ /^$match_domain$/)) {
            $r->note_basic_auth_failure;
            return AUTH_REQUIRED;
        }
    } else {
        $uname = $r->user;
        ($udom) = ($r->uri =~ m{^/webdav/($match_domain)/});
        unless (($udom ne '' ) && ($uname =~ /^$match_username$/)) {
            $r->note_basic_auth_failure;
            return AUTH_REQUIRED;
        }
    }
    if (&Apache::lonnet::domain($udom) ne '') {
        if (&Apache::lonnet::homeserver($uname,$udom) ne 'no_host') {
            my $uhome = &Apache::lonnet::authenticate($uname,$upass,$udom);
            if (($uhome ne 'no_host') && 
                (&Apache::lonnet::hostname($uhome) ne '')) {
                $handle = &init_webdav_env($sessiondir,$uname,$udom,
                                           $uhome,$now,$timetolive);
                if ($handle ne '') {
                    if (&Apache::lonnet::usertools_access($uname,$udom,'webdav')) {
                        my $cookie = "lonDAV=$handle; path=/webdav/; secure; HttpOnly;";
                        $r->header_out('Set-cookie' => $cookie);
                        $r->send_http_header;
                        return OK;
                    } else {
                        return FORBIDDEN;
                    }
                }
            }
        }
    }
    $r->note_basic_auth_failure;
    return AUTH_REQUIRED;
}

sub init_webdav_env {
    my ($sessiondir,$uname,$udom,$uhome,$now,$timetolive) = @_;
    my $handle;
    my $currnewest = 0;
    if ($sessiondir ne '') {
        if (opendir(DIR,$sessiondir)) {
            while (my $filename=readdir(DIR)) {
                if ($filename=~/^($uname\_$udom\_(\d+)\_\d+\_$uhome)\.id$/) {
                    my $oldhandle = $1;
                    my $sesstime = $2;
                    if ($now-$sesstime < $timetolive) {
                        if ($sesstime > $currnewest) {
                            $currnewest = $sesstime;
                            if ($handle ne '') {
                                unlink("$sessiondir/$handle.id");
                            }
                            $handle = $oldhandle;
                            next;
                        }
                    }
                    unlink($sessiondir.'/'.$filename);
                }
            }
            closedir(DIR);
        }
    }
    if ($handle ne '') {
        &Apache::lonnet::transfer_profile_to_env($sessiondir,$handle);
        return $handle;
    } else {
        my $id = $$.int(rand(10000000));
        $handle = "$uname\_$udom\_$now\_$id\_$uhome";
        my $sessionfile = "$sessiondir/$handle.id";
        if (tie(my %disk_env,'GDBM_File',"$sessionfile",
                &GDBM_WRCREAT(),0640)) {
            $disk_env{'user.name'} = $uname;
            $disk_env{'user.domain'} = $udom;
            $disk_env{'user.home'} = $uhome;
            my %userenv = &Apache::lonnet::get('environment',['inststatus','tools.webdav'],
                                               $udom,$uname);
            my ($tmp) = keys(%userenv);
            if ($tmp =~ /^(con_lost|error|no_such_host)/i) {
                $disk_env{'environment.inststatus'} = $userenv{'inststatus'};
                $disk_env{'environment.tools.webdav'} = $userenv{'tools.webdav'};
            }
            $disk_env{'user.environment'} = $sessionfile;
            my $possroles = ['au','ca','aa'];
            my @possdoms = &Apache::lonnet::current_machine_domains();
            my %cstr_roles =
                &Apache::lonnet::get_my_roles($uname,$udom,'userroles',
                                              undef,$possroles,\@possdoms);
            if (keys(%cstr_roles) > 0) {
                $disk_env{'user.adv'} = 1;
                $disk_env{'user.author'} = 1;
                foreach my $item (keys(%cstr_roles)) {
                    my ($aname,$adom,$role) = split(/:/,$item);
                    if ($role eq 'au') {
                        $disk_env{"user.role.$role./$adom/"} = $cstr_roles{$item};
                    } else {
                        $disk_env{"user.role.$role./$adom/$aname"} = $cstr_roles{$item};
                    }
                }
            }
            my %is_adv = ( is_adv => $disk_env{'user.adv'} );
            my %domdef = &Apache::lonnet::get_domain_defaults($udom);
            $disk_env{'environment.availabletools.webdav'} =
                &Apache::lonnet::usertools_access($uname,$udom,'webdav','reload',undef,
                                                  \%userenv,\%domdef,\%is_adv);
            @env{keys(%disk_env)} = @disk_env{keys(%disk_env)};
            untie(%disk_env);
        }
        return $handle;
    }
    return;
}

1;

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