~azzar1/unity/add-show-desktop-key

« back to all changes in this revision

Viewing changes to ivle/webapp/filesystem/serve/__init__.py

Merge my migration of serve and download to the new framework.

Poor fileservice is all alone.

Show diffs side-by-side

added added

removed removed

Lines of Context:
25
25
# our safe execution environment.
26
26
 
27
27
import os
28
 
import mimetypes
29
 
 
30
 
from ivle import (util, studpath, interpret)
 
28
 
 
29
import cjson
 
30
 
 
31
from ivle import (studpath, interpret)
31
32
import ivle.conf
32
33
from ivle.database import User
33
 
 
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')
37
 
 
38
 
# Serve all files as application/octet-stream so the browser presents them as
39
 
# a download.
40
 
default_mimetype = "application/octet-stream"
41
 
zip_mimetype = "application/zip"
42
 
 
43
 
def handle(req):
44
 
    """Handler for the Server application which serves pages."""
45
 
    req.write_html_head_foot = False
46
 
 
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)
50
 
 
51
 
    owner = User.get_by_login(req.store, login)
52
 
    if not owner:
53
 
        # There is no user.
54
 
        req.throw_error(req.HTTP_NOT_FOUND,
55
 
            "The path specified is invalid.")
56
 
 
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
 
37
 
 
38
class ServeView(BaseView):
 
39
    def __init__(self, req, path):
 
40
        self.path = path
 
41
 
 
42
    def authorize(self, req):
 
43
        return req.user is not None
 
44
 
 
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)
 
50
 
 
51
        owner = User.get_by_login(req.store, login)
 
52
        if not owner:
 
53
            # There is no user.
 
54
            raise NotFound()
 
55
 
 
56
        self.serve(req, owner, jail, path)
 
57
 
 
58
    def serve(self, req, owner, jail, path):
 
59
        serve_file(req, owner, jail, path)
 
60
 
 
61
class DownloadView(ServeView):
 
62
    def __init__(self, req, path):
 
63
        super(DownloadView, self).__init__(req, path)
 
64
        filelist = req.get_fieldstorage().getlist('path')
 
65
        if filelist:
 
66
            self.files = [f.value for f in filelist]
 
67
        else:
 
68
            self.files = None
 
69
 
 
70
    def serve(self, req, owner, jail, path):
 
71
        serve_file(req, owner, jail, path, download=True, files=self.files)
58
72
 
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.
63
76
    """
64
 
    if req.publicmode:
 
77
    # TODO: Reactivate public mode.
 
78
    #if req.publicmode:
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)
69
 
    else:
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)
73
 
 
74
 
def serve_file(req, owner, filename, download=False):
 
82
    #    studpath.authorize_public(req)
 
83
 
 
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)
 
87
 
 
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
79
93
 
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
84
99
    """
85
100
 
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)
91
105
 
92
106
    # Authorize access. If failure, this throws a HTTP_FORBIDDEN error.
93
 
    authorize(req)
 
107
    if not authorize(req):
 
108
        raise Unauthorized()
94
109
 
95
 
    # Jump into the jail
96
 
    interp_object = interpret.interpreter_objects["cgi-python"]
 
110
    args = []
97
111
    if download:
98
 
        req.headers_out["Content-Disposition"] = "attachment"
99
 
        interpret.interpret_file(req, owner, user_jail_dir,
100
 
            serveservice_path, interp_object, gentle=False)
 
112
        args.append('-d')
 
113
 
 
114
    if files and download:
 
115
        args += [os.path.join(path, f) for f in files]
101
116
    else:
102
 
        interpret.interpret_file(req, owner, user_jail_dir,
103
 
            interpretservice_path, interp_object, gentle=True)
104
 
 
105
 
def serve_file_direct(req, filename, type):
106
 
    """Serves a file by directly writing it out to the response.
107
 
 
108
 
    req: An IVLE request object.
109
 
    filename: Filename in the local file system.
110
 
    type: String. Mime type to serve the file with.
111
 
    """
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)
 
117
        args.append(path)
 
118
 
 
119
    (out, err) = ivle.interpret.execute_raw(req.user, jail, '/home',
 
120
                os.path.join(ivle.conf.share_path, 'services/serveservice'),
 
121
                args)
 
122
    assert not err
 
123
 
 
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)
 
128
 
 
129
    if 'error' in response:
 
130
        if response['error'] == 'not-found':
 
131
            raise NotFound()
 
132
        elif response['error'] in ('is-directory', 'forbidden'):
 
133
            raise 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)
 
139
            return
 
140
        else:
 
141
            raise AssertionError('Unknown error from serveservice: %s' %
 
142
                                 response['error'])
 
143
 
 
144
    if download:
 
145
        req.headers_out["Content-Disposition"] = \
 
146
                     "attachment; filename=%s" % response['name']
 
147
    req.content_type = response['type']
 
148
    req.write(out)
 
149
 
 
150
class Plugin(ViewPlugin):
 
151
    urls = [
 
152
        ('serve/*path', ServeView),
 
153
        ('download/*path', DownloadView),
 
154
    ]