25
25
# our safe execution environment.
30
from ivle import (util, studpath, interpret)
31
from ivle import (studpath, interpret)
32
33
from ivle.database import User
34
serveservice_path = os.path.join(ivle.conf.share_path, 'services/serveservice')
35
interpretservice_path = os.path.join(ivle.conf.share_path,
36
'services/interpretservice')
38
# Serve all files as application/octet-stream so the browser presents them as
40
default_mimetype = "application/octet-stream"
41
zip_mimetype = "application/zip"
44
"""Handler for the Server application which serves pages."""
45
req.write_html_head_foot = False
47
# Get the username of the student whose work we are browsing, and the path
48
# on the local machine where the file is stored.
49
(login, path) = studpath.url_to_local(req.path)
51
owner = User.get_by_login(req.store, login)
54
req.throw_error(req.HTTP_NOT_FOUND,
55
"The path specified is invalid.")
57
serve_file(req, owner, path)
34
from ivle.webapp.base.views import BaseView
35
from ivle.webapp.base.plugins import ViewPlugin
36
from ivle.webapp.errors import NotFound, Unauthorized, Forbidden
38
class ServeView(BaseView):
39
def __init__(self, req, path):
42
def authorize(self, req):
43
return req.user is not None
45
def render(self, req):
46
"""Handler for the Server application which serves pages."""
47
# Get the username of the student whose work we are browsing, and the
48
# path on the local machine where the file is stored.
49
(login, jail, path) = studpath.url_to_jailpaths(self.path)
51
owner = User.get_by_login(req.store, login)
56
self.serve(req, owner, jail, path)
58
def serve(self, req, owner, jail, path):
59
serve_file(req, owner, jail, path)
61
class DownloadView(ServeView):
62
def __init__(self, req, path):
63
super(DownloadView, self).__init__(req, path)
64
filelist = req.get_fieldstorage().getlist('path')
66
self.files = [f.value for f in filelist]
70
def serve(self, req, owner, jail, path):
71
serve_file(req, owner, jail, path, download=True, files=self.files)
59
73
def authorize(req):
60
74
"""Given a request, checks whether req.username is allowed to
61
access req.path. Returns None on authorization success. Raises
62
HTTP_FORBIDDEN on failure.
75
access req.path. Returns True on authorization success, False on failure.
77
# TODO: Reactivate public mode.
65
79
# Public mode authorization: any user can access any other user's
66
80
# files, BUT the accessed file needs to have its "ivle:published" flag
67
81
# turned on in the SVN status.
68
studpath.authorize_public(req)
70
# Private mode authorization: standard (only logged in user can access
71
# their own files, and can access all of them).
72
studpath.authorize(req)
74
def serve_file(req, owner, filename, download=False):
82
# studpath.authorize_public(req)
84
# Private mode authorization: standard (only logged in user can access
85
# their own files, and can access all of them).
86
return studpath.authorize(req, req.user)
88
def serve_file(req, owner, jail, path, download=False, files=None):
75
89
"""Serves a file, using one of three possibilities: interpreting the file,
76
90
serving it directly, or denying it and returning a 403 Forbidden error.
77
91
No return value. Writes to req (possibly throwing a server error exception
80
94
req: An IVLE request object.
81
95
owner: The user who owns the file being served.
82
filename: Filename in the local file system.
96
jail: The user's jail.
97
path: Filename in the jail.
83
98
download: Should the file be viewed in browser or downloaded
86
101
# We need a no-op trampoline run to ensure that the jail is mounted.
87
102
# Otherwise we won't be able to authorise for public mode!
88
103
noop_object = interpret.interpreter_objects["noop"]
89
user_jail_dir = os.path.join(ivle.conf.jail_base, owner.login)
90
interpret.interpret_file(req, owner, user_jail_dir, '', noop_object)
104
interpret.interpret_file(req, owner, jail, '', noop_object)
92
106
# Authorize access. If failure, this throws a HTTP_FORBIDDEN error.
107
if not authorize(req):
96
interp_object = interpret.interpreter_objects["cgi-python"]
98
req.headers_out["Content-Disposition"] = "attachment"
99
interpret.interpret_file(req, owner, user_jail_dir,
100
serveservice_path, interp_object, gentle=False)
114
if files and download:
115
args += [os.path.join(path, f) for f in files]
102
interpret.interpret_file(req, owner, user_jail_dir,
103
interpretservice_path, interp_object, gentle=True)
105
def serve_file_direct(req, filename, type):
106
"""Serves a file by directly writing it out to the response.
108
req: An IVLE request object.
109
filename: Filename in the local file system.
110
type: String. Mime type to serve the file with.
112
if not os.access(filename, os.R_OK):
113
req.throw_error(req.HTTP_NOT_FOUND,
114
"The specified file does not exist.")
115
req.content_type = type
116
req.sendfile(filename)
119
(out, err) = ivle.interpret.execute_raw(req.user, jail, '/home',
120
os.path.join(ivle.conf.share_path, 'services/serveservice'),
124
# Remove the JSON from the front of the response, and decode it.
125
json = out.split('\n', 1)[0]
126
out = out[len(json) + 1:]
127
response = cjson.decode(json)
129
if 'error' in response:
130
if response['error'] == 'not-found':
132
elif response['error'] in ('is-directory', 'forbidden'):
134
elif response['error'] == 'is-executable':
135
# We need to execute it. Just run it with Python in the jail.
136
interp_object = interpret.interpreter_objects["cgi-python"]
137
interpret.interpret_file(req, owner, jail, response['path'],
138
interp_object, gentle=True)
141
raise AssertionError('Unknown error from serveservice: %s' %
145
req.headers_out["Content-Disposition"] = \
146
"attachment; filename=%s" % response['name']
147
req.content_type = response['type']
150
class Plugin(ViewPlugin):
152
('serve/*path', ServeView),
153
('download/*path', DownloadView),