30
29
from storm.locals import create_database, Store, Int, Unicode, DateTime, \
31
30
Reference, ReferenceSet, Bool, Storm, Desc
216
215
Semester.id == Offering.semester_id,
217
216
(not active_only) or (Semester.state == u'current'),
218
217
Enrolment.offering_id == Offering.id,
219
Enrolment.user_id == self.id,
220
Enrolment.active == True)
218
Enrolment.user_id == self.id)
223
221
def hash_password(password):
229
227
"""Find a user in a store by login name."""
230
228
return store.find(cls, cls.login == unicode(login)).one()
232
def get_permissions(self, user, config):
230
def get_permissions(self, user):
233
231
"""Determine privileges held by a user over this object.
235
233
If the user requesting privileges is this user or an admin,
259
257
def __repr__(self):
260
258
return "<%s '%s'>" % (type(self).__name__, self.short_name)
262
def get_permissions(self, user, config):
260
def get_permissions(self, user):
263
261
"""Determine privileges held by a user over this object.
265
263
If the user requesting privileges is an admin, they may edit.
373
371
Enrolment.offering_id == self.id).one()
374
372
Store.of(enrolment).remove(enrolment)
376
def get_permissions(self, user, config):
374
def get_permissions(self, user):
378
376
if user is not None:
379
377
enrolment = self.get_enrolment(user)
380
378
if enrolment or user.admin:
381
379
perms.add('view')
382
if enrolment and enrolment.role == u'tutor':
383
perms.add('view_project_submissions')
384
# Site-specific policy on the role of tutors
385
if config['policy']['tutors_can_enrol_students']:
387
perms.add('enrol_student')
388
if config['policy']['tutors_can_edit_worksheets']:
389
perms.add('edit_worksheets')
390
if config['policy']['tutors_can_admin_groups']:
391
perms.add('admin_groups')
392
if (enrolment and enrolment.role in (u'lecturer')) or user.admin:
393
perms.add('view_project_submissions')
394
perms.add('admin_groups')
395
perms.add('edit_worksheets')
396
perms.add('edit') # Can edit projects & details
380
if (enrolment and enrolment.role in (u'tutor', u'lecturer')) \
383
# XXX Bug #493945 -- should tutors have these permissions?
384
# Potentially move into the next category (lecturer & admin)
397
385
perms.add('enrol') # Can see enrolment screen at all
398
386
perms.add('enrol_student') # Can enrol students
387
if (enrolment and enrolment.role in (u'lecturer')) or user.admin:
399
388
perms.add('enrol_tutor') # Can enrol tutors
401
390
perms.add('enrol_lecturer') # Can enrol lecturers
426
415
# XXX: Respect extensions.
427
416
return self.projects.find(Project.deadline > datetime.datetime.now())
429
def clone_worksheets(self, source):
430
"""Clone all worksheets from the specified source to this offering."""
431
import ivle.worksheet.utils
432
for worksheet in source.worksheets:
434
newws.seq_no = worksheet.seq_no
435
newws.identifier = worksheet.identifier
436
newws.name = worksheet.name
437
newws.assessable = worksheet.assessable
438
newws.data = worksheet.data
439
newws.format = worksheet.format
440
newws.offering = self
441
Store.of(self).add(newws)
442
ivle.worksheet.utils.update_exerciselist(newws)
445
418
class Enrolment(Storm):
446
419
"""An enrolment of a user in an offering.
473
446
return "<%s %r in %r>" % (type(self).__name__, self.user,
476
def get_permissions(self, user, config):
477
# A user can edit any enrolment that they could have created.
479
if ('enrol_' + str(self.role)) in self.offering.get_permissions(
485
"""Delete this enrolment."""
486
Store.of(self).remove(self)
491
451
class ProjectSet(Storm):
511
471
return "<%s %d in %r>" % (type(self).__name__, self.id,
514
def get_permissions(self, user, config):
515
return self.offering.get_permissions(user, config)
474
def get_permissions(self, user):
475
return self.offering.get_permissions(user)
517
477
def get_groups_for_user(self, user):
518
478
"""List all groups in this offering of which the user is a member."""
609
569
a = Assessed.get(Store.of(self), principal, self)
610
570
ps = ProjectSubmission()
611
# Raise SubmissionError if the path is illegal
612
ps.path = ProjectSubmission.test_and_normalise_path(path)
613
572
ps.revision = revision
614
573
ps.date_submitted = datetime.datetime.now()
620
def get_permissions(self, user, config):
621
return self.project_set.offering.get_permissions(user, config)
579
def get_permissions(self, user):
580
return self.project_set.offering.get_permissions(user)
624
583
def latest_submissions(self):
702
661
(not active_only) or (Semester.state == u'current'))
705
def get_permissions(self, user, config):
664
def get_permissions(self, user):
706
665
if user.admin or user in self.members:
707
666
return set(['submit_project'])
815
774
approver = Reference(approver_id, User.id)
816
775
notes = Unicode()
818
class SubmissionError(Exception):
819
"""Denotes a validation error during submission."""
822
777
class ProjectSubmission(Storm):
823
778
"""A submission from a user or group repository to a particular project.
854
809
return "/files/%s/%s/%s?r=%d" % (user.login,
855
810
self.assessed.checkout_location, submitpath, self.revision)
858
def test_and_normalise_path(path):
859
"""Test that path is valid, and normalise it. This prevents possible
860
injections using malicious paths.
861
Returns the updated path, if successful.
862
Raises SubmissionError if invalid.
864
# Ensure the path is absolute to prevent being tacked onto working
866
# Prevent '\n' because it will break all sorts of things.
867
# Prevent '[' and ']' because they can be used to inject into the
869
# Normalise to avoid resulting in ".." path segments.
870
if not os.path.isabs(path):
871
raise SubmissionError("Path is not absolute")
872
if any(c in path for c in "\n[]"):
873
raise SubmissionError("Path must not contain '\\n', '[' or ']'")
874
return os.path.normpath(path)
876
812
# WORKSHEETS AND EXERCISES #
878
814
class Exercise(Storm):
908
844
def __repr__(self):
909
845
return "<%s %s>" % (type(self).__name__, self.name)
911
def get_permissions(self, user, config):
912
return self.global_permissions(user, config)
915
def global_permissions(user, config):
916
"""Gets the set of permissions this user has over *all* exercises.
917
This is used to determine who may view the exercises list, and create
847
def get_permissions(self, user):
921
850
if user is not None:
925
854
elif u'lecturer' in set((e.role for e in user.active_enrolments)):
926
855
perms.add('edit')
927
856
perms.add('view')
928
elif (config['policy']['tutors_can_edit_worksheets']
929
and u'tutor' in set((e.role for e in user.active_enrolments))):
930
# Site-specific policy on the role of tutors
857
elif u'tutor' in set((e.role for e in user.active_enrolments)):
931
858
perms.add('edit')
932
859
perms.add('view')
994
921
store.find(WorksheetExercise,
995
922
WorksheetExercise.worksheet == self).remove()
997
def get_permissions(self, user, config):
998
# Almost the same permissions as for the offering itself
999
perms = self.offering.get_permissions(user, config)
1000
# However, "edit" permission is derived from the "edit_worksheets"
1001
# permission of the offering
1002
if 'edit_worksheets' in perms:
1005
perms.discard('edit')
924
def get_permissions(self, user):
925
return self.offering.get_permissions(user)
1008
927
def get_xml(self):
1009
928
"""Returns the xml of this worksheet, converts from rst if required."""
1054
973
return "<%s %s in %s>" % (type(self).__name__, self.exercise.name,
1055
974
self.worksheet.identifier)
1057
def get_permissions(self, user, config):
1058
return self.worksheet.get_permissions(user, config)
976
def get_permissions(self, user):
977
return self.worksheet.get_permissions(user)
1061
980
class ExerciseSave(Storm):
1108
1027
complete = Bool()
1109
1028
active = Bool()
1111
def get_permissions(self, user, config):
1030
def get_permissions(self, user):
1112
1031
return set(['view']) if user is self.user else set()
1114
1033
class TestSuite(Storm):