File:
[LON-CAPA] /
loncom /
auth /
lonwebdavauth.pm
Revision
1.8:
download - view:
text,
annotated -
select for diffs
Fri Dec 18 15:23:03 2020 UTC (3 years, 7 months ago) by
raeburn
Branches:
MAIN
CVS tags:
version_2_12_X,
version_2_11_X,
version_2_11_5_msu,
version_2_11_5,
version_2_11_4_uiuc,
version_2_11_4_msu,
version_2_11_4,
version_2_11_3_uiuc,
version_2_11_3_msu,
version_2_11_3,
HEAD
- Retrieval of requestor's IP address centralized in lonnet::get_requestor_ip()
- Domain configuration to allow domain's LON-CAPA nodes to operate behind a
WAF/Reverse Proxy using aliased hostname (CNAME).
- Web requests from other nodes bypass the WAF as their requests are made
directly to the server hostname (A record); same for internal LON-CAPA
connections for lonc -> lond.
1: # The LearningOnline Network
2: # Authentication Handler for webDAV access to Authoring Space.
3: #
4: # $Id: lonwebdavauth.pm,v 1.8 2020/12/18 15:23:03 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[\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, by sending
61: an Apache Basic Auth request to the client.
62:
63: If Apache Basic Auth is successful authentication will
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:
89: No session? return AUTH_REQUIRED which will prompt
90: webDAV client to authenticate user (via Apache Basic Auth).
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);
166: if (&Apache::lonnet::usertools_access($uname,$udom,'webdav')) {
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: }
174: return OK;
175: } else {
176: return FORBIDDEN;
177: }
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);
188: $uname =~ s/^\s+//;
189: $uname =~ s/\s+$//;
190: $udom =~ s/^\s+//;
191: $udom =~ s/\s+$//;
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;
198: $uname =~ s/^\s+//;
199: $uname =~ s/\s+$//;
200: ($udom) = ($r->uri =~ m{^/webdav/($match_domain)/});
201: unless (($udom ne '' ) && ($uname =~ /^$match_username$/) && ($upass ne '')) {
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 '')) {
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);
214: if ($handle ne '') {
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: }
223: }
224: }
225: }
226: }
227: $r->note_basic_auth_failure;
228: return AUTH_REQUIRED;
229: }
230:
231: sub init_webdav_env {
232: my ($r,$sessiondir,$uname,$udom,$uhome,$now,$timetolive,$author) = @_;
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;
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: }
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);
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: }
292: }
293: }
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);
299: @env{keys(%disk_env)} = @disk_env{keys(%disk_env)};
300: untie(%disk_env);
301: my $ip = &Apache::lonnet::get_requestor_ip($r);
302: &Apache::lonnet::log($udom,$uname,$uhome,
303: "Login webdav/$author $ip");
304: }
305: return $handle;
306: }
307: return;
308: }
309:
310: 1;
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>