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

« back to all changes in this revision

Viewing changes to ivle/interpret.py

  • Committer: David Coles
  • Date: 2010-07-16 06:47:19 UTC
  • Revision ID: coles.david@gmail.com-20100716064719-j68ejmcf6akblw8a
Fix typo in rebuild_svn_config that caused Storm query to return limited or no 
submissions.

Fixes issue where lectueres and tutors did not have permission to access 
submissions by students in their subjects.

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
 
22
22
# Runs a student script in a safe execution environment.
23
23
 
 
24
import ivle
24
25
from ivle import studpath
25
 
from ivle.util import IVLEError, IVLEJailError, split_path
26
 
import ivle.conf
 
26
from ivle.util import IVLEJailError, split_path
27
27
 
28
28
import functools
29
29
 
37
37
# working on smaller output
38
38
 
39
39
CGI_BLOCK_SIZE = 65535
 
40
PATH = "/usr/local/bin:/usr/bin:/bin"
40
41
 
41
42
def interpret_file(req, owner, jail_dir, filename, interpreter, gentle=True):
42
43
    """Serves a file by interpreting it using one of IVLE's builtin
73
74
    # (Note that paths "relative" to the jail actually begin with a '/' as
74
75
    # they are absolute in the jailspace)
75
76
 
76
 
    return interpreter(owner.unixid, jail_dir, working_dir, filename_abs, req,
 
77
    return interpreter(owner, jail_dir, working_dir, filename_abs, req,
77
78
                       gentle)
78
79
 
79
80
class CGIFlags:
88
89
        self.linebuf = ""
89
90
        self.headers = {}       # Header names : values
90
91
 
91
 
def execute_cgi(interpreter, trampoline, uid, jail_dir, working_dir,
92
 
                script_path, req, gentle):
 
92
def execute_cgi(interpreter, owner, jail_dir, working_dir, script_path,
 
93
                req, gentle):
93
94
    """
94
95
    trampoline: Full path on the local system to the CGI wrapper program
95
96
        being executed.
96
 
    uid: User ID of the owner of the file.
 
97
    owner: User object of the owner of the file.
97
98
    jail_dir: Absolute path of owner's jail directory.
98
99
    working_dir: Directory containing the script file relative to owner's
99
100
        jail.
105
106
    its environment.
106
107
    """
107
108
 
 
109
    trampoline = os.path.join(req.config['paths']['lib'], 'trampoline')
 
110
 
108
111
    # Support no-op trampoline runs.
109
112
    if interpreter is None:
110
113
        interpreter = '/bin/true'
127
130
        f.seek(0)       # Rewind, for reading
128
131
 
129
132
    # Set up the environment
130
 
    # This automatically asks mod_python to load up the CGI variables into the
131
 
    # environment (which is a good first approximation)
132
 
    old_env = os.environ.copy()
133
 
    for k in os.environ.keys():
134
 
        del os.environ[k]
135
 
    for (k,v) in req.get_cgi_environ().items():
136
 
        os.environ[k] = v
137
 
    fixup_environ(req, script_path)
 
133
    environ = cgi_environ(req, script_path, owner)
138
134
 
139
135
    # usage: tramp uid jail_dir working_dir script_path
140
 
    pid = subprocess.Popen(
141
 
        [trampoline, str(uid), ivle.conf.jail_base, ivle.conf.jail_src_base,
142
 
         ivle.conf.jail_system, jail_dir, working_dir, interpreter,
143
 
        script_path],
 
136
    cmd_line = [trampoline, str(owner.unixid),
 
137
            req.config['paths']['jails']['mounts'],
 
138
            req.config['paths']['jails']['src'],
 
139
            req.config['paths']['jails']['template'],
 
140
            jail_dir, working_dir, interpreter, script_path]
 
141
    # Popen doesn't like unicode strings. It hateses them.
 
142
    cmd_line = [(s.encode('utf-8') if isinstance(s, unicode) else s)
 
143
                for s in cmd_line]
 
144
    pid = subprocess.Popen(cmd_line,
144
145
        stdin=f, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
145
 
        cwd=tramp_dir)
146
 
 
147
 
    # Restore the environment
148
 
    for k in os.environ.keys():
149
 
        del os.environ[k]
150
 
    for (k,v) in old_env.items():
151
 
        os.environ[k] = v
 
146
        cwd=tramp_dir, env=environ)
152
147
 
153
148
    # We don't want any output! Bail out after the process terminates.
154
149
    if noop:
220
215
            if len(split) == 1:
221
216
                split = headers.split('\n', 1)
222
217
 
223
 
        # Is this an internal IVLE error condition?
224
 
        hs = cgiflags.headers
225
 
        if 'X-IVLE-Error-Type' in hs:
226
 
            t = hs['X-IVLE-Error-Type']
227
 
            if t == IVLEError.__name__:
228
 
                raise IVLEError(int(hs['X-IVLE-Error-Code']),
229
 
                                hs['X-IVLE-Error-Message'])
230
 
            else:
 
218
        # If not executing in gentle mode (which presents CGI violations
 
219
        # to users nicely), check if this an internal IVLE error
 
220
        # condition.
 
221
        if not cgiflags.gentle:
 
222
            hs = cgiflags.headers
 
223
            if 'X-IVLE-Error-Type' in hs:
231
224
                try:
232
225
                    raise IVLEJailError(hs['X-IVLE-Error-Type'],
233
226
                                        hs['X-IVLE-Error-Message'],
234
227
                                        hs['X-IVLE-Error-Info'])
235
228
                except KeyError:
236
 
                    raise IVLEError(500, 'bad error headers written by CGI')
 
229
                    raise AssertionError("Bad error headers written by CGI.")
237
230
 
238
231
        # Check to make sure the required headers were written
239
232
        if cgiflags.wrote_html_warning or not cgiflags.gentle:
342
335
    <pre>
343
336
""" % (warning, text))
344
337
 
345
 
location_cgi_python = os.path.join(ivle.conf.lib_path, "trampoline")
346
 
 
347
338
# Mapping of interpreter names (as given in conf/app/server.py) to
348
339
# interpreter functions.
349
340
 
350
341
interpreter_objects = {
351
342
    'cgi-python'
352
 
        : functools.partial(execute_cgi, "/usr/bin/python",
353
 
            location_cgi_python),
 
343
        : functools.partial(execute_cgi, "/usr/bin/python"),
354
344
    'noop'
355
 
        : functools.partial(execute_cgi, None,
356
 
            location_cgi_python),
 
345
        : functools.partial(execute_cgi, None),
357
346
    # Should also have:
358
347
    # cgi-generic
359
348
    # python-server-page
360
349
}
361
350
 
362
 
def fixup_environ(req, script_path):
363
 
    """Assuming os.environ has been written with the CGI variables from
364
 
    apache, make a few changes for security and correctness.
 
351
def cgi_environ(req, script_path, user):
 
352
    """Gets CGI variables from apache and makes a few changes for security and 
 
353
    correctness.
365
354
 
366
355
    Does not modify req, only reads it.
367
356
    """
368
 
    env = os.environ
 
357
    env = {}
369
358
    # Comments here are on the heavy side, explained carefully for security
370
359
    # reasons. Please read carefully before making changes.
 
360
    
 
361
    # This automatically asks mod_python to load up the CGI variables into the
 
362
    # environment (which is a good first approximation)
 
363
    for (k,v) in req.get_cgi_environ().items():
 
364
        env[k] = v
371
365
 
372
366
    # Remove DOCUMENT_ROOT and SCRIPT_FILENAME. Not part of CGI spec and
373
367
    # exposes unnecessary details about server.
413
407
 
414
408
    # SERVER_SOFTWARE is actually not Apache but IVLE, since we are
415
409
    # custom-making the CGI request.
416
 
    env['SERVER_SOFTWARE'] = "IVLE/" + str(ivle.conf.ivle_version)
 
410
    env['SERVER_SOFTWARE'] = "IVLE/" + ivle.__version__
417
411
 
418
412
    # Additional environment variables
419
 
    username = split_path(req.path)[0]
 
413
    username = user.login
420
414
    env['HOME'] = os.path.join('/home', username)
421
415
 
 
416
    return env
 
417
 
422
418
class ExecutionError(Exception):
423
419
    pass
424
420
 
425
 
def execute_raw(user, jail_dir, working_dir, binary, args):
 
421
def execute_raw(config, user, jail_dir, working_dir, binary, args):
426
422
    '''Execute a binary in a user's jail, returning the raw output.
427
423
 
428
424
    The binary is executed in the given working directory with the given
429
425
    args. A tuple of (stdout, stderr) is returned.
430
426
    '''
431
427
 
432
 
    tramp = location_cgi_python
433
 
    tramp_dir = os.path.split(location_cgi_python)[0]
 
428
    tramp = os.path.join(config['paths']['lib'], 'trampoline')
 
429
    tramp_dir = os.path.split(tramp)[0]
434
430
 
435
431
    # Fire up trampoline. Vroom, vroom.
436
 
    proc = subprocess.Popen(
437
 
        [tramp, str(user.unixid), ivle.conf.jail_base,
438
 
         ivle.conf.jail_src_base, ivle.conf.jail_system, jail_dir,
439
 
         working_dir, binary] + args,
 
432
    cmd_line = [tramp, str(user.unixid), config['paths']['jails']['mounts'],
 
433
         config['paths']['jails']['src'],
 
434
         config['paths']['jails']['template'],
 
435
         jail_dir, working_dir, binary] + args
 
436
    # Popen doesn't like unicode strings. It hateses them.
 
437
    cmd_line = [(s.encode('utf-8') if isinstance(s, unicode) else s)
 
438
                for s in cmd_line]
 
439
    proc = subprocess.Popen(cmd_line,
440
440
        stdin=subprocess.PIPE, stdout=subprocess.PIPE,
441
 
        stderr=subprocess.PIPE, cwd=tramp_dir, close_fds=True)
 
441
        stderr=subprocess.PIPE, cwd=tramp_dir, close_fds=True,
 
442
        env={'HOME': os.path.join('/home', user.login),
 
443
             'PATH': PATH,
 
444
             'USER': user.login,
 
445
             'LOGNAME': user.login})
442
446
 
443
447
    (stdout, stderr) = proc.communicate()
444
448
    exitcode = proc.returncode
445
449
 
446
450
    if exitcode != 0:
447
 
        raise ExecutionError('subprocess ended with code %d, stderr %s' %
448
 
                             (exitcode, proc.stderr.read()))
 
451
        raise ExecutionError('subprocess ended with code %d, stderr: "%s"' %
 
452
                             (exitcode, stderr))
449
453
    return (stdout, stderr)