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 |
import ivle.conf |
|
32 |
from ivle import util |
|
33 |
||
34 |
# Make a Subversion client object (for published)
|
|
35 |
svnclient = pysvn.Client() |
|
36 |
||
37 |
def url_to_local(urlpath): |
|
38 |
"""Given a URL path (part of a URL query string, see below), returns a
|
|
39 |
tuple of
|
|
40 |
* the username of the student whose directory is being browsed
|
|
41 |
* the absolute path in the file system where that file will be
|
|
42 |
found within the student directories.
|
|
43 |
||
44 |
urlpath: Part of the URL, but only the part *after* the application. For
|
|
45 |
instance, given the URL "/ivle/browse/joe/mydir/myfile", urlpath will
|
|
46 |
be just "joe/mydir/myfile". The expected result is something like
|
|
47 |
("joe", "/home/informatics/jails/joe/home/joe/mydir/myfile").
|
|
48 |
Note that the actual location is not guaranteed by this interface (this
|
|
49 |
function serves as a single point of control as to how URLs map onto
|
|
50 |
student directories).
|
|
51 |
||
52 |
Returns (None, None) if the path is empty.
|
|
53 |
||
54 |
See also: ivle.conf.jail_base
|
|
55 |
"""
|
|
56 |
# First normalise the path
|
|
57 |
urlpath = os.path.normpath(urlpath) |
|
58 |
# Now if it begins with ".." or separator, then it's illegal
|
|
59 |
if urlpath.startswith("..") or urlpath.startswith(os.sep): |
|
60 |
return (None, None) |
|
61 |
# Note: User can be a group name. There is absolutely no difference in our
|
|
62 |
# current directory scheme.
|
|
63 |
(user, subpath) = util.split_path(urlpath) |
|
64 |
if user is None: return (None, None) |
|
65 |
||
66 |
# Join the user onto 'home' then the full path specified.
|
|
67 |
# This results in the user's name being repeated twice, which is in
|
|
68 |
# accordance with our directory scheme.
|
|
69 |
# (The first time is the name of the jail, the second is the user's home
|
|
70 |
# directory within the jail).
|
|
71 |
path = os.path.join(ivle.conf.jail_base, user, 'home', urlpath) |
|
72 |
||
73 |
return (user, path) |
|
74 |
||
75 |
def url_to_jailpaths(urlpath): |
|
76 |
"""Given a URL path (part of a URL query string), returns a tuple of
|
|
77 |
* the username of the student whose directory is being browsed
|
|
78 |
* the absolute path where the jail will be located.
|
|
79 |
* the path of the file relative to the jail.
|
|
80 |
||
81 |
urlpath: See urlpath in url_to_local.
|
|
82 |
||
83 |
>>> url_to_jailpaths("joe/mydir/myfile")
|
|
1099.1.7
by William Grant
ivle.studpath.url_to_jailpaths: Fix the doctest to use new paths. |
84 |
('joe', '/var/lib/ivle/jailmounts/joe', '/home/joe/mydir/myfile')
|
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
85 |
|
86 |
>>> url_to_jailpaths("")
|
|
87 |
(None, None, None)
|
|
88 |
"""
|
|
89 |
# First normalise the path
|
|
90 |
urlpath = os.path.normpath(urlpath) |
|
91 |
# Now if it begins with ".." then it's illegal
|
|
92 |
if urlpath.startswith(".."): |
|
93 |
return (None, None, None) |
|
94 |
# Note: User can be a group name. There is absolutely no difference in our
|
|
95 |
# current directory scheme.
|
|
96 |
(user, subpath) = util.split_path(urlpath) |
|
97 |
if user is None: return (None, None, None) |
|
98 |
||
99 |
jail = os.path.join(ivle.conf.jail_base, user) |
|
100 |
path = os.path.join('/home', urlpath) |
|
101 |
||
102 |
return (user, jail, path) |
|
103 |
||
104 |
def svnpublished(path): |
|
105 |
"""Given a path on the LOCAL file system, determines whether the path has
|
|
106 |
its "ivle:published" property active (in subversion). Returns True
|
|
107 |
or False."""
|
|
108 |
# Read SVN properties for this path
|
|
109 |
try: |
|
110 |
props = svnclient.propget("ivle:published", path, recurse=False) |
|
111 |
except pysvn.ClientError: |
|
112 |
# Not under version control? Then it isn't published.
|
|
113 |
return False |
|
114 |
return len(props) > 0 |
|
115 |
||
116 |
def published(path): |
|
117 |
"""Given a path on the LOCAL file system, determines whether the path has a
|
|
118 |
'.published' file. Returns True or False."""
|
|
119 |
publish_file_path = os.path.join(path,'.published') |
|
120 |
return os.access(publish_file_path,os.F_OK) |
|
121 |
||
122 |
def worldreadable(path): |
|
123 |
"""Given a path on the LOCAL file system, determines whether the path is
|
|
124 |
world readble. Returns True or False."""
|
|
125 |
try: |
|
126 |
mode = os.stat(path).st_mode |
|
127 |
if mode & stat.S_IROTH: |
|
128 |
return True |
|
129 |
else: |
|
130 |
return False |
|
131 |
except OSError, e: |
|
132 |
return False |
|
133 |
||
134 |
||
135 |
def authorize(req): |
|
136 |
"""Given a request, checks whether req.user is allowed to
|
|
137 |
access req.path. Returns None on authorization success. Raises
|
|
138 |
HTTP_FORBIDDEN on failure.
|
|
139 |
||
140 |
This is for general authorization (assuming not in public mode; this is
|
|
141 |
the standard auth code for fileservice, download and serve).
|
|
142 |
"""
|
|
143 |
# TODO: Groups
|
|
144 |
# First normalise the path
|
|
145 |
urlpath = os.path.normpath(req.path) |
|
146 |
# Now if it begins with ".." or separator, then it's illegal
|
|
147 |
if urlpath.startswith("..") or urlpath.startswith(os.sep): |
|
148 |
req.throw_error(req.HTTP_FORBIDDEN) |
|
149 |
||
150 |
(owner, _) = util.split_path(urlpath) |
|
151 |
if req.user.login != owner: |
|
152 |
req.throw_error(req.HTTP_FORBIDDEN) |
|
153 |
||
154 |
def authorize_public(req): |
|
155 |
"""A different kind of authorization. Rather than making sure the
|
|
156 |
logged-in user owns the file, this checks if the file is in a published
|
|
157 |
directory.
|
|
158 |
||
159 |
This is for the "public mode" of the serve app.
|
|
160 |
||
161 |
Same interface as "authorize" - None on success, HTTP_FORBIDDEN exception
|
|
162 |
raised on failure.
|
|
163 |
"""
|
|
164 |
_, path = url_to_local(req.path) |
|
165 |
dirpath, _ = os.path.split(path) |
|
166 |
if not (worldreadable(dirpath) and published(dirpath)): |
|
167 |
req.throw_error(req.HTTP_FORBIDDEN) |