1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
1 |
# IVLE - Informatics Virtual Learning Environment
|
2 |
# Copyright (C) 2007-2008 The University of Melbourne
|
|
3 |
#
|
|
4 |
# This program is free software; you can redistribute it and/or modify
|
|
5 |
# it under the terms of the GNU General Public License as published by
|
|
6 |
# the Free Software Foundation; either version 2 of the License, or
|
|
7 |
# (at your option) any later version.
|
|
8 |
#
|
|
9 |
# This program is distributed in the hope that it will be useful,
|
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12 |
# GNU General Public License for more details.
|
|
13 |
#
|
|
14 |
# You should have received a copy of the GNU General Public License
|
|
15 |
# along with this program; if not, write to the Free Software
|
|
16 |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
17 |
||
18 |
# Module: studpath
|
|
19 |
# Author: Matt Giuca
|
|
20 |
# Date: 14/12/2007
|
|
21 |
||
22 |
# Provides functions for translating URLs into physical locations in the
|
|
23 |
# student directories in the local file system.
|
|
24 |
# Also performs common authorization, disallowing students from visiting paths
|
|
25 |
# they dont own.
|
|
26 |
||
27 |
import os |
|
28 |
import stat |
|
29 |
||
30 |
from ivle import util |
|
31 |
||
1268
by William Grant
Make url_to_local take a config, and test it! |
32 |
def url_to_local(config, urlpath): |
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
33 |
"""Given a URL path (part of a URL query string, see below), returns a
|
34 |
tuple of
|
|
35 |
* the username of the student whose directory is being browsed
|
|
36 |
* the absolute path in the file system where that file will be
|
|
37 |
found within the student directories.
|
|
38 |
||
39 |
urlpath: Part of the URL, but only the part *after* the application. For
|
|
40 |
instance, given the URL "/ivle/browse/joe/mydir/myfile", urlpath will
|
|
41 |
be just "joe/mydir/myfile". The expected result is something like
|
|
42 |
("joe", "/home/informatics/jails/joe/home/joe/mydir/myfile").
|
|
43 |
Note that the actual location is not guaranteed by this interface (this
|
|
44 |
function serves as a single point of control as to how URLs map onto
|
|
45 |
student directories).
|
|
46 |
||
47 |
Returns (None, None) if the path is empty.
|
|
48 |
||
1268
by William Grant
Make url_to_local take a config, and test it! |
49 |
>>> stubconfig = {'paths': {'jails': {'mounts': '/jails'}}}
|
1280
by William Grant
Improve tests of ivle.studpath. |
50 |
|
1268
by William Grant
Make url_to_local take a config, and test it! |
51 |
>>> url_to_local(stubconfig, 'joe/foo/bar/baz')
|
52 |
('joe', '/jails/joe/home/joe/foo/bar/baz')
|
|
1280
by William Grant
Improve tests of ivle.studpath. |
53 |
>>> url_to_local(stubconfig, 'joe')
|
54 |
('joe', '/jails/joe/home/joe')
|
|
55 |
>>> url_to_local(stubconfig, 'joe/')
|
|
56 |
('joe', '/jails/joe/home/joe')
|
|
57 |
||
58 |
We have some protection from various potential attacks. An empty,
|
|
59 |
absolute, or ..-prefixed path yields a special result.
|
|
60 |
||
61 |
>>> url_to_local(stubconfig, '')
|
|
62 |
(None, None)
|
|
63 |
>>> url_to_local(stubconfig, '/foo')
|
|
64 |
(None, None)
|
|
65 |
>>> url_to_local(stubconfig, '../bar')
|
|
66 |
(None, None)
|
|
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
67 |
"""
|
1268
by William Grant
Make url_to_local take a config, and test it! |
68 |
|
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
69 |
# First normalise the path
|
70 |
urlpath = os.path.normpath(urlpath) |
|
71 |
# Now if it begins with ".." or separator, then it's illegal
|
|
72 |
if urlpath.startswith("..") or urlpath.startswith(os.sep): |
|
73 |
return (None, None) |
|
74 |
# Note: User can be a group name. There is absolutely no difference in our
|
|
75 |
# current directory scheme.
|
|
76 |
(user, subpath) = util.split_path(urlpath) |
|
77 |
if user is None: return (None, None) |
|
78 |
||
79 |
# Join the user onto 'home' then the full path specified.
|
|
80 |
# This results in the user's name being repeated twice, which is in
|
|
81 |
# accordance with our directory scheme.
|
|
82 |
# (The first time is the name of the jail, the second is the user's home
|
|
83 |
# directory within the jail).
|
|
1268
by William Grant
Make url_to_local take a config, and test it! |
84 |
path = os.path.join(config['paths']['jails']['mounts'], |
85 |
user, 'home', urlpath) |
|
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
86 |
|
87 |
return (user, path) |
|
88 |
||
1273
by William Grant
Remove ivle.conf usage from ivle.studpath. |
89 |
def url_to_jailpaths(config, urlpath): |
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
90 |
"""Given a URL path (part of a URL query string), returns a tuple of
|
91 |
* the username of the student whose directory is being browsed
|
|
92 |
* the absolute path where the jail will be located.
|
|
93 |
* the path of the file relative to the jail.
|
|
94 |
||
95 |
urlpath: See urlpath in url_to_local.
|
|
96 |
||
1280
by William Grant
Improve tests of ivle.studpath. |
97 |
>>> stubconfig = {'paths': {'jails': {'mounts': '/jails'}}}
|
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
98 |
|
1280
by William Grant
Improve tests of ivle.studpath. |
99 |
>>> url_to_jailpaths(stubconfig, "joe/mydir//myfile/.././myfile")
|
100 |
('joe', '/jails/joe', '/home/joe/mydir/myfile')
|
|
101 |
>>> url_to_jailpaths(stubconfig, "")
|
|
102 |
(None, None, None)
|
|
103 |
>>> url_to_jailpaths(stubconfig, "../foo")
|
|
104 |
(None, None, None)
|
|
105 |
>>> url_to_jailpaths(stubconfig, "/foo")
|
|
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
106 |
(None, None, None)
|
107 |
"""
|
|
108 |
# First normalise the path
|
|
109 |
urlpath = os.path.normpath(urlpath) |
|
1280
by William Grant
Improve tests of ivle.studpath. |
110 |
# Now if it begins with "..", or is absolute, then it's illegal
|
111 |
if urlpath.startswith("..") or os.path.isabs(urlpath): |
|
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
112 |
return (None, None, None) |
113 |
# Note: User can be a group name. There is absolutely no difference in our
|
|
114 |
# current directory scheme.
|
|
115 |
(user, subpath) = util.split_path(urlpath) |
|
116 |
if user is None: return (None, None, None) |
|
117 |
||
1273
by William Grant
Remove ivle.conf usage from ivle.studpath. |
118 |
jail = os.path.join(config['paths']['jails']['mounts'], user) |
1270
by William Grant
Rename to to_home_path, and use it in ivle.interpret. |
119 |
path = to_home_path(urlpath) |
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
120 |
|
121 |
return (user, jail, path) |
|
122 |
||
1270
by William Grant
Rename to to_home_path, and use it in ivle.interpret. |
123 |
def to_home_path(urlpath): |
124 |
"""Given a URL path (eg. joe/foo/bar/baz), returns a path within the home.
|
|
1269
by William Grant
Factor out the in-jail path bits of url_to_jailpaths. |
125 |
|
1270
by William Grant
Rename to to_home_path, and use it in ivle.interpret. |
126 |
>>> to_home_path('joe/foo/bar/baz')
|
1269
by William Grant
Factor out the in-jail path bits of url_to_jailpaths. |
127 |
'/home/joe/foo/bar/baz'
|
1280
by William Grant
Improve tests of ivle.studpath. |
128 |
>>> to_home_path('joe/foo//bar/baz/../../')
|
129 |
'/home/joe/foo'
|
|
130 |
>>> to_home_path('joe/foo//bar/baz/../../../../../') is None
|
|
131 |
True
|
|
1269
by William Grant
Factor out the in-jail path bits of url_to_jailpaths. |
132 |
"""
|
1277
by William Grant
Normalise and block .. paths in to_home_path. |
133 |
|
134 |
urlpath = os.path.normpath(urlpath) |
|
135 |
# If it begins with '..', it's illegal.
|
|
136 |
if urlpath.startswith(".."): |
|
137 |
return None |
|
138 |
||
1269
by William Grant
Factor out the in-jail path bits of url_to_jailpaths. |
139 |
return os.path.join('/home', urlpath) |
140 |
||
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
141 |
def published(path): |
142 |
"""Given a path on the LOCAL file system, determines whether the path has a
|
|
143 |
'.published' file. Returns True or False."""
|
|
144 |
publish_file_path = os.path.join(path,'.published') |
|
145 |
return os.access(publish_file_path,os.F_OK) |
|
146 |
||
147 |
def worldreadable(path): |
|
148 |
"""Given a path on the LOCAL file system, determines whether the path is
|
|
149 |
world readble. Returns True or False."""
|
|
150 |
try: |
|
151 |
mode = os.stat(path).st_mode |
|
152 |
if mode & stat.S_IROTH: |
|
153 |
return True |
|
154 |
else: |
|
155 |
return False |
|
156 |
except OSError, e: |
|
157 |
return False |
|
158 |
||
159 |
||
1099.3.6
by William Grant
Move serve over to the new framework. It sort of works, except not. |
160 |
def authorize(req, user): |
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
161 |
"""Given a request, checks whether req.user is allowed to
|
162 |
access req.path. Returns None on authorization success. Raises
|
|
163 |
HTTP_FORBIDDEN on failure.
|
|
164 |
||
165 |
This is for general authorization (assuming not in public mode; this is
|
|
166 |
the standard auth code for fileservice, download and serve).
|
|
167 |
"""
|
|
168 |
# TODO: Groups
|
|
169 |
# First normalise the path
|
|
170 |
urlpath = os.path.normpath(req.path) |
|
171 |
# Now if it begins with ".." or separator, then it's illegal
|
|
172 |
if urlpath.startswith("..") or urlpath.startswith(os.sep): |
|
1099.3.6
by William Grant
Move serve over to the new framework. It sort of works, except not. |
173 |
return False |
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
174 |
|
175 |
(owner, _) = util.split_path(urlpath) |
|
1099.3.6
by William Grant
Move serve over to the new framework. It sort of works, except not. |
176 |
if user.login != owner: |
177 |
return False |
|
178 |
return True |
|
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
179 |
|
180 |
def authorize_public(req): |
|
181 |
"""A different kind of authorization. Rather than making sure the
|
|
182 |
logged-in user owns the file, this checks if the file is in a published
|
|
183 |
directory.
|
|
184 |
||
185 |
This is for the "public mode" of the serve app.
|
|
186 |
||
187 |
Same interface as "authorize" - None on success, HTTP_FORBIDDEN exception
|
|
188 |
raised on failure.
|
|
189 |
"""
|
|
1268
by William Grant
Make url_to_local take a config, and test it! |
190 |
_, path = url_to_local(req.config, req.path) |
1099.1.148
by William Grant
Fix public mode serve authorization to traverse to the deepest directory, |
191 |
|
192 |
# Walk up the tree, and find the deepest directory.
|
|
193 |
while not os.path.isdir(path): |
|
194 |
path = os.path.dirname(path) |
|
195 |
||
196 |
if not (worldreadable(path) and published(path)): |
|
1099.3.6
by William Grant
Move serve over to the new framework. It sort of works, except not. |
197 |
return False |
198 |
return True |