1: # The LearningOnline Network
2: # Authentication Handler for webDAV access to Authoring Space.
3: #
4: # $Id: lonwebdavauth.pm,v 1.1 2012/02/27 03:06:33 raeburn Exp $
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:
35: Invoked for /+webdav/[\w\-]+/[\w\-]+/ by
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,
60: the user is re-challenged for login information. If the perlvar
61: lonOtherAuthen has been set, Single Sign On will be used, otherwise
62: an Apache Basic Auth request will be sent to the client.
63:
64: If Apache Basic Auth is used, successful authentication will
65: result in creation of a webDAV session file containing a
66: minimal set of information about the user which will also be
67: loaded into the user's environment. The environment persists
68: until the end of the Apache request, when it is cleaned up
69: by lonacc::cleanup, as is the case for requests for non-webdav URLs.
70:
71: If a valid session file exists, a cookie is returned containing
72: the session ID, otherwise a new session file is created and
73: returned in the cookie.
74:
75: This is part of the LearningOnline Network with CAPA project
76: described at http://www.lon-capa.org.
77:
78: =head1 HANDLER SUBROUTINE
79:
80: This routine is called by Apache and mod_perl.
81:
82: =over 4
83:
84: =item *
85:
86: Check for valid webDAV session
87:
88: =item *
89:
90: No session? - if SSO enabled: return DECLINED
91:
92: =item *
93:
94: No session? - if SSO not enabled: return AUTH_REQUIRED
95: which will prompt webDAV client to authenticate user
96: (via Apache Basic Auth).
97:
98: =item *
99:
100: If authenticated, call &init_webdav_env() to create sessionID
101: and populate environment, and send header containing cookie.
102:
103: =back
104:
105: =head1 NOTABLE SUBROUTINES
106:
107: =over
108:
109: =item * init_webdav_env()
110:
111: =over
112:
113: =item *
114:
115: Checks for valid session files in webDAV session directory.
116:
117: =item *
118:
119: Calls Apache::lonnet::transfer_profile_to_env() to create
120: %env for user if session is valid.
121:
122: =item *
123:
124: Otherwise creates new session file and populates %env.
125:
126: =item *
127:
128: Session ID file is a GDBM file containing:
129:
130: =over
131:
132: =item * user.name, user.domain, user.home, user.environment
133:
134: =item * user.role entries for:
135:
136: active author, co-author, and assistant co-author roles
137: in domains hosted on this machine.
138:
139: =back
140:
141: =back
142:
143: =back
144:
145: =cut
146:
147: package Apache::lonwebdavauth;
148:
149: use strict;
150: use GDBM_File;
151: use Apache::Constants qw(:common :http :methods);
152: use Apache::lonnet;
153: use LONCAPA qw(:DEFAULT :match);
154:
155: sub handler {
156: my $r = shift;
157: my $now = time;
158: my $timetolive = 600;
159: my $sessiondir=$r->dir_config('lonDAVsessDir');
160: my ($uname,$udom,$uhome);
161:
162: # Check for cookie
163: my $handle = &Apache::lonnet::check_for_valid_session($r,'lonDAV');
164: if ($handle ne '') {
165: my $sesstime;
166: ($uname,$udom,$sesstime,$uhome) =
167: ($handle =~ /^($match_username)_($match_domain)_(\d+)_\d+_(.+)$/);
168: if ($sesstime) {
169: if ($now-$sesstime < $timetolive) {
170: if (&Apache::lonnet::homeserver($uname,$udom) eq $uhome) {
171: &Apache::lonnet::transfer_profile_to_env($sessiondir,$handle);
172: return OK;
173: }
174: }
175: }
176: }
177:
178: if ($r->dir_config('lonOtherAuthen') eq 'yes') {
179: if (defined($r->dir_config('lonOtherAuthenType'))) {
180: $r->auth_type($r->dir_config('lonOtherAuthenType'));
181: }
182: return DECLINED;
183: }
184:
185: my ($status,$upass) = $r->get_basic_auth_pw;
186: return $status unless ($status == 0 || $status == OK);
187:
188: if ($r->user =~ /,/) {
189: ($uname,$udom) = split(/,/,$r->user);
190: unless (($uname =~ /^$match_username$/) && ($udom =~ /^$match_domain$/)) {
191: $r->note_basic_auth_failure;
192: return AUTH_REQUIRED;
193: }
194: } else {
195: $uname = $r->user;
196: ($udom) = ($r->uri =~ m{^/webdav/($match_domain)/});
197: unless (($udom ne '' ) && ($uname =~ /^$match_username$/)) {
198: $r->note_basic_auth_failure;
199: return AUTH_REQUIRED;
200: }
201: }
202: if (&Apache::lonnet::domain($udom) ne '') {
203: if (&Apache::lonnet::homeserver($uname,$udom) ne 'no_host') {
204: my $uhome = &Apache::lonnet::authenticate($uname,$upass,$udom);
205: if (($uhome ne 'no_host') &&
206: (&Apache::lonnet::hostname($uhome) ne '')) {
207: $handle = &init_webdav_env($sessiondir,$uname,$udom,
208: $uhome,$now,$timetolive);
209: if ($handle ne '') {
210: my $cookie = "lonDAV=$handle; path=/webdav/; secure; HttpOnly;";
211: $r->header_out('Set-cookie' => $cookie);
212: $r->send_http_header;
213: }
214: return OK;
215: }
216: }
217: }
218: $r->note_basic_auth_failure;
219: return AUTH_REQUIRED;
220: }
221:
222: sub init_webdav_env {
223: my ($sessiondir,$uname,$udom,$uhome,$now,$timetolive) = @_;
224: my $handle;
225: my $currnewest = 0;
226: if ($sessiondir ne '') {
227: if (opendir(DIR,$sessiondir)) {
228: while (my $filename=readdir(DIR)) {
229: if ($filename=~/^($uname\_$udom\_(\d+)\_\d+\_$uhome)\.id$/) {
230: my $oldhandle = $1;
231: my $sesstime = $2;
232: if ($now-$sesstime < $timetolive) {
233: if ($sesstime > $currnewest) {
234: $currnewest = $sesstime;
235: if ($handle ne '') {
236: unlink("$sessiondir/$handle.id");
237: }
238: $handle = $oldhandle;
239: next;
240: }
241: }
242: unlink($sessiondir.'/'.$filename);
243: }
244: }
245: closedir(DIR);
246: }
247: }
248: if ($handle ne '') {
249: &Apache::lonnet::transfer_profile_to_env($sessiondir,$handle);
250: return $handle;
251: } else {
252: my $id = $$.int(rand(10000000));
253: $handle = "$uname\_$udom\_$now\_$id\_$uhome";
254: my $sessionfile = "$sessiondir/$handle.id";
255: if (tie(my %disk_env,'GDBM_File',"$sessionfile",
256: &GDBM_WRCREAT(),0640)) {
257: $disk_env{'user.name'} = $uname;
258: $disk_env{'user.domain'} = $udom;
259: $disk_env{'user.home'} = $uhome;
260: $disk_env{'user.environment'} = $sessionfile;
261: my $possroles = ['au','ca','aa'];
262: my @possdoms = &Apache::lonnet::current_machine_domains();
263: my %cstr_roles =
264: &Apache::lonnet::get_my_roles($uname,$udom,'userroles',
265: undef,$possroles,\@possdoms);
266: foreach my $item (keys(%cstr_roles)) {
267: my ($aname,$adom,$role) = split(/:/,$item);
268: if ($role eq 'au') {
269: $disk_env{"user.role.$role./$adom/"} = $cstr_roles{$item};
270: } else {
271: $disk_env{"user.role.$role./$adom/$aname"} = $cstr_roles{$item};
272: }
273: }
274: @env{keys(%disk_env)} = @disk_env{keys(%disk_env)};
275: untie(%disk_env);
276: }
277: return $handle;
278: }
279: return;
280: }
281:
282: 1;
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>