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 |
import pysvn |
|
30 |
||
31 |
from ivle import util |
|
32 |
||
33 |
# Make a Subversion client object (for published)
|
|
34 |
svnclient = pysvn.Client() |
|
35 |
||
1268
by William Grant
Make url_to_local take a config, and test it! |
36 |
def url_to_local(config, urlpath): |
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
37 |
"""Given a URL path (part of a URL query string, see below), returns a
|
38 |
tuple of
|
|
39 |
* the username of the student whose directory is being browsed
|
|
40 |
* the absolute path in the file system where that file will be
|
|
41 |
found within the student directories.
|
|
42 |
||
43 |
urlpath: Part of the URL, but only the part *after* the application. For
|
|
44 |
instance, given the URL "/ivle/browse/joe/mydir/myfile", urlpath will
|
|
45 |
be just "joe/mydir/myfile". The expected result is something like
|
|
46 |
("joe", "/home/informatics/jails/joe/home/joe/mydir/myfile").
|
|
47 |
Note that the actual location is not guaranteed by this interface (this
|
|
48 |
function serves as a single point of control as to how URLs map onto
|
|
49 |
student directories).
|
|
50 |
||
51 |
Returns (None, None) if the path is empty.
|
|
52 |
||
1268
by William Grant
Make url_to_local take a config, and test it! |
53 |
>>> stubconfig = {'paths': {'jails': {'mounts': '/jails'}}}
|
1280
by William Grant
Improve tests of ivle.studpath. |
54 |
|
1268
by William Grant
Make url_to_local take a config, and test it! |
55 |
>>> url_to_local(stubconfig, 'joe/foo/bar/baz')
|
56 |
('joe', '/jails/joe/home/joe/foo/bar/baz')
|
|
1280
by William Grant
Improve tests of ivle.studpath. |
57 |
>>> url_to_local(stubconfig, 'joe')
|
58 |
('joe', '/jails/joe/home/joe')
|
|
59 |
>>> url_to_local(stubconfig, 'joe/')
|
|
60 |
('joe', '/jails/joe/home/joe')
|
|
61 |
||
62 |
We have some protection from various potential attacks. An empty,
|
|
63 |
absolute, or ..-prefixed path yields a special result.
|
|
64 |
||
65 |
>>> url_to_local(stubconfig, '')
|
|
66 |
(None, None)
|
|
67 |
>>> url_to_local(stubconfig, '/foo')
|
|
68 |
(None, None)
|
|
69 |
>>> url_to_local(stubconfig, '../bar')
|
|
70 |
(None, None)
|
|
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
71 |
"""
|
1268
by William Grant
Make url_to_local take a config, and test it! |
72 |
|
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
73 |
# First normalise the path
|
74 |
urlpath = os.path.normpath(urlpath) |
|
75 |
# Now if it begins with ".." or separator, then it's illegal
|
|
76 |
if urlpath.startswith("..") or urlpath.startswith(os.sep): |
|
77 |
return (None, None) |
|
78 |
# Note: User can be a group name. There is absolutely no difference in our
|
|
79 |
# current directory scheme.
|
|
80 |
(user, subpath) = util.split_path(urlpath) |
|
81 |
if user is None: return (None, None) |
|
82 |
||
83 |
# Join the user onto 'home' then the full path specified.
|
|
84 |
# This results in the user's name being repeated twice, which is in
|
|
85 |
# accordance with our directory scheme.
|
|
86 |
# (The first time is the name of the jail, the second is the user's home
|
|
87 |
# directory within the jail).
|
|
1268
by William Grant
Make url_to_local take a config, and test it! |
88 |
path = os.path.join(config['paths']['jails']['mounts'], |
89 |
user, 'home', urlpath) |
|
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
90 |
|
91 |
return (user, path) |
|
92 |
||
1273
by William Grant
Remove ivle.conf usage from ivle.studpath. |
93 |
def url_to_jailpaths(config, urlpath): |
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
94 |
"""Given a URL path (part of a URL query string), returns a tuple of
|
95 |
* the username of the student whose directory is being browsed
|
|
96 |
* the absolute path where the jail will be located.
|
|
97 |
* the path of the file relative to the jail.
|
|
98 |
||
99 |
urlpath: See urlpath in url_to_local.
|
|
100 |
||
1280
by William Grant
Improve tests of ivle.studpath. |
101 |
>>> stubconfig = {'paths': {'jails': {'mounts': '/jails'}}}
|
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
102 |
|
1280
by William Grant
Improve tests of ivle.studpath. |
103 |
>>> url_to_jailpaths(stubconfig, "joe/mydir//myfile/.././myfile")
|
104 |
('joe', '/jails/joe', '/home/joe/mydir/myfile')
|
|
105 |
>>> url_to_jailpaths(stubconfig, "")
|
|
106 |
(None, None, None)
|
|
107 |
>>> url_to_jailpaths(stubconfig, "../foo")
|
|
108 |
(None, None, None)
|
|
109 |
>>> url_to_jailpaths(stubconfig, "/foo")
|
|
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
110 |
(None, None, None)
|
111 |
"""
|
|
112 |
# First normalise the path
|
|
113 |
urlpath = os.path.normpath(urlpath) |
|
1280
by William Grant
Improve tests of ivle.studpath. |
114 |
# Now if it begins with "..", or is absolute, then it's illegal
|
115 |
if urlpath.startswith("..") or os.path.isabs(urlpath): |
|
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
116 |
return (None, None, None) |
117 |
# Note: User can be a group name. There is absolutely no difference in our
|
|
118 |
# current directory scheme.
|
|
119 |
(user, subpath) = util.split_path(urlpath) |
|
120 |
if user is None: return (None, None, None) |
|
121 |
||
1273
by William Grant
Remove ivle.conf usage from ivle.studpath. |
122 |
jail = os.path.join(config['paths']['jails']['mounts'], user) |
1270
by William Grant
Rename to to_home_path, and use it in ivle.interpret. |
123 |
path = to_home_path(urlpath) |
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
124 |
|
125 |
return (user, jail, path) |
|
126 |
||
1270
by William Grant
Rename to to_home_path, and use it in ivle.interpret. |
127 |
def to_home_path(urlpath): |
128 |
"""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. |
129 |
|
1270
by William Grant
Rename to to_home_path, and use it in ivle.interpret. |
130 |
>>> to_home_path('joe/foo/bar/baz')
|
1269
by William Grant
Factor out the in-jail path bits of url_to_jailpaths. |
131 |
'/home/joe/foo/bar/baz'
|
1280
by William Grant
Improve tests of ivle.studpath. |
132 |
>>> to_home_path('joe/foo//bar/baz/../../')
|
133 |
'/home/joe/foo'
|
|
134 |
>>> to_home_path('joe/foo//bar/baz/../../../../../') is None
|
|
135 |
True
|
|
1269
by William Grant
Factor out the in-jail path bits of url_to_jailpaths. |
136 |
"""
|
1277
by William Grant
Normalise and block .. paths in to_home_path. |
137 |
|
138 |
urlpath = os.path.normpath(urlpath) |
|
139 |
# If it begins with '..', it's illegal.
|
|
140 |
if urlpath.startswith(".."): |
|
141 |
return None |
|
142 |
||
1269
by William Grant
Factor out the in-jail path bits of url_to_jailpaths. |
143 |
return os.path.join('/home', urlpath) |
144 |
||
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
145 |
def svnpublished(path): |
146 |
"""Given a path on the LOCAL file system, determines whether the path has
|
|
147 |
its "ivle:published" property active (in subversion). Returns True
|
|
148 |
or False."""
|
|
149 |
# Read SVN properties for this path
|
|
150 |
try: |
|
151 |
props = svnclient.propget("ivle:published", path, recurse=False) |
|
152 |
except pysvn.ClientError: |
|
153 |
# Not under version control? Then it isn't published.
|
|
154 |
return False |
|
155 |
return len(props) > 0 |
|
156 |
||
157 |
def published(path): |
|
158 |
"""Given a path on the LOCAL file system, determines whether the path has a
|
|
159 |
'.published' file. Returns True or False."""
|
|
160 |
publish_file_path = os.path.join(path,'.published') |
|
161 |
return os.access(publish_file_path,os.F_OK) |
|
162 |
||
163 |
def worldreadable(path): |
|
164 |
"""Given a path on the LOCAL file system, determines whether the path is
|
|
165 |
world readble. Returns True or False."""
|
|
166 |
try: |
|
167 |
mode = os.stat(path).st_mode |
|
168 |
if mode & stat.S_IROTH: |
|
169 |
return True |
|
170 |
else: |
|
171 |
return False |
|
172 |
except OSError, e: |
|
173 |
return False |
|
174 |
||
175 |
||
1099.3.6
by William Grant
Move serve over to the new framework. It sort of works, except not. |
176 |
def authorize(req, user): |
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
177 |
"""Given a request, checks whether req.user is allowed to
|
178 |
access req.path. Returns None on authorization success. Raises
|
|
179 |
HTTP_FORBIDDEN on failure.
|
|
180 |
||
181 |
This is for general authorization (assuming not in public mode; this is
|
|
182 |
the standard auth code for fileservice, download and serve).
|
|
183 |
"""
|
|
184 |
# TODO: Groups
|
|
185 |
# First normalise the path
|
|
186 |
urlpath = os.path.normpath(req.path) |
|
187 |
# Now if it begins with ".." or separator, then it's illegal
|
|
188 |
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. |
189 |
return False |
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
190 |
|
191 |
(owner, _) = util.split_path(urlpath) |
|
1099.3.6
by William Grant
Move serve over to the new framework. It sort of works, except not. |
192 |
if user.login != owner: |
193 |
return False |
|
194 |
return True |
|
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
195 |
|
196 |
def authorize_public(req): |
|
197 |
"""A different kind of authorization. Rather than making sure the
|
|
198 |
logged-in user owns the file, this checks if the file is in a published
|
|
199 |
directory.
|
|
200 |
||
201 |
This is for the "public mode" of the serve app.
|
|
202 |
||
203 |
Same interface as "authorize" - None on success, HTTP_FORBIDDEN exception
|
|
204 |
raised on failure.
|
|
205 |
"""
|
|
1268
by William Grant
Make url_to_local take a config, and test it! |
206 |
_, 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, |
207 |
|
208 |
# Walk up the tree, and find the deepest directory.
|
|
209 |
while not os.path.isdir(path): |
|
210 |
path = os.path.dirname(path) |
|
211 |
||
212 |
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. |
213 |
return False |
214 |
return True |