From a0a0c5e98ce149f0eebd6f02f82d3d44b7acc7f4 Mon Sep 17 00:00:00 2001 From: luh Date: Mon, 29 May 2017 22:32:09 -0500 Subject: [PATCH 01/15] "initial commit for implementing low-level permission" --- .project | 17 ++++++ .pydevproject | 5 ++ .settings/org.eclipse.core.resources.prefs | 12 +++++ course/admin.py | 55 +++++++++++++------- course/migrations/0102_auto_20170529_2106.py | 43 +++++++++++++++ course/models.py | 16 ++++++ course/rules.py | 41 +++++++++++++++ course/test_perm.py | 14 +++++ course/views.py | 10 +++- relate/settings.py | 3 ++ requirements.txt | 2 +- 11 files changed, 196 insertions(+), 22 deletions(-) create mode 100644 .project create mode 100644 .pydevproject create mode 100644 .settings/org.eclipse.core.resources.prefs create mode 100644 course/migrations/0102_auto_20170529_2106.py create mode 100644 course/rules.py create mode 100644 course/test_perm.py diff --git a/.project b/.project new file mode 100644 index 00000000..1018cac0 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + relate + + + + + + org.python.pydev.PyDevBuilder + + + + + + org.python.pydev.pythonNature + + diff --git a/.pydevproject b/.pydevproject new file mode 100644 index 00000000..40e9f40a --- /dev/null +++ b/.pydevproject @@ -0,0 +1,5 @@ + + +Default +python 2.7 + diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000..c81d4a9a --- /dev/null +++ b/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,12 @@ +eclipse.preferences.version=1 +encoding//accounts/admin.py=utf-8 +encoding//accounts/models.py=utf-8 +encoding//course/admin.py=utf-8 +encoding//course/analytics.py=utf-8 +encoding//course/calendar.py=utf-8 +encoding//course/exam.py=utf-8 +encoding//course/models.py=utf-8 +encoding//course/views.py=utf-8 +encoding//formats/__init__.py=utf-8 +encoding//relate/urls.py=utf-8 +encoding/setup.py=utf-8 diff --git a/course/admin.py b/course/admin.py index a0256372..6b5e6990 100644 --- a/course/admin.py +++ b/course/admin.py @@ -46,6 +46,7 @@ from course.constants import ( participation_permission as pperm, exam_ticket_states ) +from rules.contrib.admin import ObjectPermissionsModelAdmin if False: from typing import Any # noqa @@ -101,7 +102,7 @@ class CourseAdminForm(forms.ModelForm): exclude = () -class CourseAdmin(admin.ModelAdmin): +class CourseAdmin(ObjectPermissionsModelAdmin): list_display = ( "identifier", "number", @@ -134,6 +135,8 @@ class CourseAdmin(admin.ModelAdmin): "number", "name", "time_period") + + filter_horizontal = ('course_director','course_staff') form = CourseAdminForm @@ -145,12 +148,12 @@ class CourseAdmin(admin.ModelAdmin): # These are created only through the course creation form. return False - def get_queryset(self, request): - qs = super(CourseAdmin, self).get_queryset(request) - return _filter_courses_for_user(qs, request.user) +# def get_queryset(self, request): +# qs = super(CourseAdmin, self).get_queryset(request) +# return _filter_courses_for_user(qs, request.user) # }}} - + pass admin.site.register(Course, CourseAdmin) @@ -159,7 +162,7 @@ admin.site.register(Course, CourseAdmin) # {{{ events -class EventAdmin(admin.ModelAdmin): +class EventAdmin(ObjectPermissionsModelAdmin): list_display = ( "course", "kind", @@ -182,23 +185,37 @@ class EventAdmin(admin.ModelAdmin): if six.PY3: __str__ = __unicode__ - list_editable = ("ordinal", "time", "end_time", "shown_in_calendar") +# list_editable = ("ordinal", "time", "end_time", "shown_in_calendar") # {{{ permissions - - def get_queryset(self, request): - qs = super(EventAdmin, self).get_queryset(request) - return _filter_course_linked_obj_for_user(qs, request.user) - - def formfield_for_foreignkey(self, db_field, request, **kwargs): - if db_field.name == "course": - kwargs["queryset"] = _filter_courses_for_user( - Course.objects, request.user) - return super(EventAdmin, self).formfield_for_foreignkey( - db_field, request, **kwargs) + +# def get_queryset(self, request): +# qs = super(EventAdmin, self).get_queryset(request) +# return _filter_course_linked_obj_for_user(qs, request.user) +# +# def formfield_for_foreignkey(self, db_field, request, **kwargs): +# if db_field.name == "course": +# kwargs["queryset"] = _filter_courses_for_user( +# Course.objects, request.user) +# return super(EventAdmin, self).formfield_for_foreignkey( +# db_field, request, **kwargs) # }}} + + # Overrides for row-level permission + + def get_form(self, request, *args, **kwargs): + form = super(EventAdmin, self).get_form(request, *args, **kwargs) + form.base_fields['creator'].initial = request.user + form.base_fields['creator'].widget = forms.HiddenInput() + return form + def save_model(self, request, obj, form, change): + obj.creator = request.user + super(EventAdmin, self).save_model(request, obj, form, change) + pass + + pass admin.site.register(Event, EventAdmin) @@ -335,7 +352,7 @@ class ParticipationAdmin(admin.ModelAdmin): def get_queryset(self, request): qs = super(ParticipationAdmin, self).get_queryset(request) return _filter_course_linked_obj_for_user(qs, request.user) - + def formfield_for_foreignkey(self, db_field, request, **kwargs): if db_field.name == "course": kwargs["queryset"] = _filter_courses_for_user( diff --git a/course/migrations/0102_auto_20170529_2106.py b/course/migrations/0102_auto_20170529_2106.py new file mode 100644 index 00000000..fe00f774 --- /dev/null +++ b/course/migrations/0102_auto_20170529_2106.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-05-30 02:06 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('course', '0101_add_ticket_facility_and_validity'), + ] + + operations = [ + migrations.AddField( + model_name='course', + name='course_director', + field=models.ManyToManyField(related_name='directors', to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='course', + name='course_staff', + field=models.ManyToManyField(related_name='staffs', to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='event', + name='creator', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='participationpermission', + name='permission', + field=models.CharField(choices=[('edit_course', 'Edit course'), ('use_admin_interface', 'Use admin interface'), ('impersonate_role', 'Impersonate role'), ('set_fake_time', 'Set fake time'), ('set_pretend_facility', 'Pretend to be in facility'), ('edit_course_permissions', 'Edit course permissions'), ('view_hidden_course_page', 'View hidden course page'), ('view_calendar', 'View calendar'), ('send_instant_message', 'Send instant message'), ('access_files_for', 'Access files for'), ('included_in_grade_statistics', 'Included in grade statistics'), ('edit_exam', 'Edit exam'), ('issue_exam_ticket', 'Issue exam ticket'), ('batch_issue_exam_ticket', 'Batch issue exam ticket'), ('view_participant_masked_profile', "View participants' masked profile only"), ('view_flow_sessions_from_role', 'View flow sessions from role'), ('view_gradebook', 'View gradebook'), ('edit_grading_opportunity', 'Edit grading opportunity'), ('assign_grade', 'Assign grade'), ('view_grader_stats', 'View grader stats'), ('batch_import_grade', 'Batch-import grades'), ('batch_export_grade', 'Batch-export grades'), ('batch_download_submission', 'Batch-download submissions'), ('impose_flow_session_deadline', 'Impose flow session deadline'), ('batch_impose_flow_session_deadline', 'Batch-impose flow session deadline'), ('end_flow_session', 'End flow session'), ('batch_end_flow_session', 'Batch-end flow sessions'), ('regrade_flow_session', 'Regrade flow session'), ('batch_regrade_flow_session', 'Batch-regrade flow sessions'), ('recalculate_flow_session_grade', 'Recalculate flow session grade'), ('batch_recalculate_flow_session_grade', 'Batch-recalculate flow sesssion grades'), ('reopen_flow_session', 'Reopen flow session'), ('grant_exception', 'Grant exception'), ('view_analytics', 'View analytics'), ('preview_content', 'Preview content'), ('update_content', 'Update content'), ('use_markup_sandbox', 'Use markup sandbox'), ('use_page_sandbox', 'Use page sandbox'), ('test_flow', 'Test flow'), ('edit_events', 'Edit events'), ('query_participation', 'Query participation'), ('edit_participation', 'Edit participation'), ('preapprove_participation', 'Preapprove participation'), ('manage_instant_flow_requests', 'Manage instant flow requests')], db_index=True, max_length=200, verbose_name='Permission'), + ), + migrations.AlterField( + model_name='participationrolepermission', + name='permission', + field=models.CharField(choices=[('edit_course', 'Edit course'), ('use_admin_interface', 'Use admin interface'), ('impersonate_role', 'Impersonate role'), ('set_fake_time', 'Set fake time'), ('set_pretend_facility', 'Pretend to be in facility'), ('edit_course_permissions', 'Edit course permissions'), ('view_hidden_course_page', 'View hidden course page'), ('view_calendar', 'View calendar'), ('send_instant_message', 'Send instant message'), ('access_files_for', 'Access files for'), ('included_in_grade_statistics', 'Included in grade statistics'), ('edit_exam', 'Edit exam'), ('issue_exam_ticket', 'Issue exam ticket'), ('batch_issue_exam_ticket', 'Batch issue exam ticket'), ('view_participant_masked_profile', "View participants' masked profile only"), ('view_flow_sessions_from_role', 'View flow sessions from role'), ('view_gradebook', 'View gradebook'), ('edit_grading_opportunity', 'Edit grading opportunity'), ('assign_grade', 'Assign grade'), ('view_grader_stats', 'View grader stats'), ('batch_import_grade', 'Batch-import grades'), ('batch_export_grade', 'Batch-export grades'), ('batch_download_submission', 'Batch-download submissions'), ('impose_flow_session_deadline', 'Impose flow session deadline'), ('batch_impose_flow_session_deadline', 'Batch-impose flow session deadline'), ('end_flow_session', 'End flow session'), ('batch_end_flow_session', 'Batch-end flow sessions'), ('regrade_flow_session', 'Regrade flow session'), ('batch_regrade_flow_session', 'Batch-regrade flow sessions'), ('recalculate_flow_session_grade', 'Recalculate flow session grade'), ('batch_recalculate_flow_session_grade', 'Batch-recalculate flow sesssion grades'), ('reopen_flow_session', 'Reopen flow session'), ('grant_exception', 'Grant exception'), ('view_analytics', 'View analytics'), ('preview_content', 'Preview content'), ('update_content', 'Update content'), ('use_markup_sandbox', 'Use markup sandbox'), ('use_page_sandbox', 'Use page sandbox'), ('test_flow', 'Test flow'), ('edit_events', 'Edit events'), ('query_participation', 'Query participation'), ('edit_participation', 'Edit participation'), ('preapprove_participation', 'Preapprove participation'), ('manage_instant_flow_requests', 'Manage instant flow requests')], db_index=True, max_length=200, verbose_name='Permission'), + ), + ] diff --git a/course/models.py b/course/models.py index 6b4c0f32..0519de8f 100644 --- a/course/models.py +++ b/course/models.py @@ -40,6 +40,8 @@ from django.dispatch import receiver from django.conf import settings +from django import forms + from course.constants import ( # noqa user_status, USER_STATUS_CHOICES, participation_status, PARTICIPATION_STATUS_CHOICES, @@ -215,6 +217,12 @@ class Course(models.Model): participants = models.ManyToManyField(settings.AUTH_USER_MODEL, through='Participation') + + + # Extra_attribute for row-level permission + + course_director = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='directors') + course_staff = models.ManyToManyField(settings.AUTH_USER_MODEL,related_name='staffs') class Meta: verbose_name = _("Course") @@ -276,6 +284,14 @@ class Event(models.Model): shown_in_calendar = models.BooleanField(default=True, verbose_name=_('Shown in calendar')) + + # Extra attribute for row-level permission + + creator = models.ForeignKey(settings.AUTH_USER_MODEL,default=1) + + def clean(self): + if self.creator not in self.course.course_staff.all() and self.creator not in self.course.course_director.all(): + raise forms.ValidationError("You cannot add event to this course.") class Meta: verbose_name = _("Event") diff --git a/course/rules.py b/course/rules.py new file mode 100644 index 00000000..c991f57c --- /dev/null +++ b/course/rules.py @@ -0,0 +1,41 @@ +from __future__ import absolute_import + +import rules + + +# Predicates + +@rules.predicate +def is_course_director(user, course): + if not course: + return False + return user in course.course_director.all() + +@rules.predicate +def is_course_staff(user, course): + if not course: + return False + return user in course.course_staff.all() + +@rules.predicate +def is_event_staff(user, event): + if not event: + return False + course = event.course + return user in course.course_staff.all() or user in course.course_director.all() + + +# Rules + +rules.add_rule('change_course', is_course_director) +rules.add_rule('delete_course', is_course_director) +rules.add_rule('change_event', is_event_staff) +rules.add_rule('delete_event',is_event_staff) +# Permissions + +rules.add_perm('course.change_course', is_course_director) +rules.add_perm('course.delete_course', is_course_director) +rules.add_perm('course', rules.always_allow) + +rules.add_perm('course.delete_event', is_event_staff) +rules.add_perm('course.change_event', is_event_staff) diff --git a/course/test_perm.py b/course/test_perm.py new file mode 100644 index 00000000..9d8f0de7 --- /dev/null +++ b/course/test_perm.py @@ -0,0 +1,14 @@ +from course.models import Course, Event +from django.contrib.auth.models import User +import rules +luh = User.objects.get(username = "luh") +course1 = Course.objects.get(identifier='cs999-sp20') +course2 = Course.objects.get(identifier='math999-sp20') +staff1 = User.objects.get(username='staff_1') + +staff2 = User.objects.get(username='staff_2') + + +print(luh.has_perm('course.change_course', course1)) +print(staff1.has_perm('course.change_course', course1)) +print(staff2.has_perm('course.change_course', course2)) \ No newline at end of file diff --git a/course/views.py b/course/views.py index 6f1f14cd..06188e05 100644 --- a/course/views.py +++ b/course/views.py @@ -81,6 +81,9 @@ from course.utils import ( # noqa render_course_page, CoursePageContext) +from django.shortcuts import get_object_or_404 +from rules.contrib.views import permission_required + # {{{ for mypy if False: @@ -1356,11 +1359,14 @@ class EditCourseForm(StyledModelForm): @course_view + def edit_course(pctx): - if not pctx.has_permission(pperm.edit_course): + + request = pctx.request + if not pctx.has_permission(pperm.edit_course) or not request.user.has_perm('course.change_course', pctx.course) : raise PermissionDenied() - request = pctx.request + if request.method == 'POST': form = EditCourseForm(request.POST, instance=pctx.course) diff --git a/relate/settings.py b/relate/settings.py index 207cc667..44f876f8 100644 --- a/relate/settings.py +++ b/relate/settings.py @@ -54,6 +54,7 @@ INSTALLED_APPS = ( "accounts", "course", + 'rules.apps.AutodiscoverRulesConfig' ) if local_settings["RELATE_SIGN_IN_BY_SAML2_ENABLED"]: @@ -87,8 +88,10 @@ AUTHENTICATION_BACKENDS = ( "course.auth.TokenBackend", "course.exam.ExamTicketBackend", "django.contrib.auth.backends.ModelBackend", + 'rules.permissions.ObjectPermissionBackend' ) + if local_settings["RELATE_SIGN_IN_BY_SAML2_ENABLED"]: AUTHENTICATION_BACKENDS = AUTHENTICATION_BACKENDS + ( # type: ignore 'course.auth.Saml2Backend', diff --git a/requirements.txt b/requirements.txt index b79796bb..3e15d409 100644 --- a/requirements.txt +++ b/requirements.txt @@ -40,7 +40,7 @@ paramiko git+https://github.com/inducer/django-bootstrap3-datetimepicker.git # For in-class instant messaging -# dnspython # Py2 +#dnspython # Py2 dnspython3 # Py3 # Py2 broken was broken in 1.3.1 git+https://github.com/fritzy/SleekXMPP.git@6e27f28c854ce4ae1d9f0cc8ee407bda8de97d3b -- GitLab From b71a694973e82a342bca355ad4a959480fd7609f Mon Sep 17 00:00:00 2001 From: Paul Date: Mon, 29 May 2017 23:59:59 -0400 Subject: [PATCH 02/15] Update requirements.txt rules --- requirements.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/requirements.txt b/requirements.txt index 3e15d409..51f72729 100644 --- a/requirements.txt +++ b/requirements.txt @@ -110,3 +110,6 @@ pytools typing # vim: foldmethod=marker + +# row-level permission +rules -- GitLab From 714ed2d2fd75f851c2bcde776701045359014a08 Mon Sep 17 00:00:00 2001 From: luh Date: Mon, 29 May 2017 23:28:51 -0500 Subject: [PATCH 03/15] row-level permission commit. Add 'from __future__ import absolute_import'. --- .project | 1 + .pydevproject | 4 ++++ course/admin.py | 1 + course/views.py | 1 + 4 files changed, 7 insertions(+) diff --git a/.project b/.project index 1018cac0..e0e2651c 100644 --- a/.project +++ b/.project @@ -13,5 +13,6 @@ org.python.pydev.pythonNature + org.python.pydev.django.djangoNature diff --git a/.pydevproject b/.pydevproject index 40e9f40a..2a704e1c 100644 --- a/.pydevproject +++ b/.pydevproject @@ -2,4 +2,8 @@ Default python 2.7 + +DJANGO_SETTINGS_MODULE +relate.settings + diff --git a/course/admin.py b/course/admin.py index 6b5e6990..317e009e 100644 --- a/course/admin.py +++ b/course/admin.py @@ -46,6 +46,7 @@ from course.constants import ( participation_permission as pperm, exam_ticket_states ) +from __future__ import absolute_import from rules.contrib.admin import ObjectPermissionsModelAdmin if False: diff --git a/course/views.py b/course/views.py index 06188e05..b5a49638 100644 --- a/course/views.py +++ b/course/views.py @@ -82,6 +82,7 @@ from course.utils import ( # noqa CoursePageContext) from django.shortcuts import get_object_or_404 +from __future__ import absolute_import from rules.contrib.views import permission_required # {{{ for mypy -- GitLab From 260509664ac76fc371d3d0a67a1b7c2b655bca10 Mon Sep 17 00:00:00 2001 From: luh Date: Mon, 29 May 2017 23:35:15 -0500 Subject: [PATCH 04/15] Moved 'from __future__ import absolute_import' to the top. --- course/admin.py | 4 ++-- course/views.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/course/admin.py b/course/admin.py index 317e009e..505d75af 100644 --- a/course/admin.py +++ b/course/admin.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- - +from __future__ import absolute_import __copyright__ = "Copyright (C) 2014 Andreas Kloeckner" __license__ = """ @@ -46,7 +46,7 @@ from course.constants import ( participation_permission as pperm, exam_ticket_states ) -from __future__ import absolute_import + from rules.contrib.admin import ObjectPermissionsModelAdmin if False: diff --git a/course/views.py b/course/views.py index b5a49638..86718581 100644 --- a/course/views.py +++ b/course/views.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -from __future__ import division +from __future__ import division, absolute_import __copyright__ = "Copyright (C) 2014 Andreas Kloeckner" @@ -82,7 +82,7 @@ from course.utils import ( # noqa CoursePageContext) from django.shortcuts import get_object_or_404 -from __future__ import absolute_import + from rules.contrib.views import permission_required # {{{ for mypy -- GitLab From a2ca75c8e308912cba05a709866d9339659d6c7c Mon Sep 17 00:00:00 2001 From: luh Date: Tue, 30 May 2017 14:59:24 -0500 Subject: [PATCH 05/15] Fixing styles. --- course/admin.py | 17 +++++++++-------- course/models.py | 20 ++++++++++---------- course/rules.py | 8 +++++--- course/test_perm.py | 8 ++++---- course/views.py | 7 ++----- 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/course/admin.py b/course/admin.py index 505d75af..71a86e9b 100644 --- a/course/admin.py +++ b/course/admin.py @@ -136,8 +136,8 @@ class CourseAdmin(ObjectPermissionsModelAdmin): "number", "name", "time_period") - - filter_horizontal = ('course_director','course_staff') + + filter_horizontal = ('course_director', 'course_staff') form = CourseAdminForm @@ -156,6 +156,7 @@ class CourseAdmin(ObjectPermissionsModelAdmin): # }}} pass + admin.site.register(Course, CourseAdmin) # }}} @@ -189,11 +190,10 @@ class EventAdmin(ObjectPermissionsModelAdmin): # list_editable = ("ordinal", "time", "end_time", "shown_in_calendar") # {{{ permissions - + # def get_queryset(self, request): # qs = super(EventAdmin, self).get_queryset(request) # return _filter_course_linked_obj_for_user(qs, request.user) -# # def formfield_for_foreignkey(self, db_field, request, **kwargs): # if db_field.name == "course": # kwargs["queryset"] = _filter_courses_for_user( @@ -202,9 +202,9 @@ class EventAdmin(ObjectPermissionsModelAdmin): # db_field, request, **kwargs) # }}} - + # Overrides for row-level permission - + def get_form(self, request, *args, **kwargs): form = super(EventAdmin, self).get_form(request, *args, **kwargs) form.base_fields['creator'].initial = request.user @@ -215,9 +215,10 @@ class EventAdmin(ObjectPermissionsModelAdmin): obj.creator = request.user super(EventAdmin, self).save_model(request, obj, form, change) pass - + pass + admin.site.register(Event, EventAdmin) # }}} @@ -353,7 +354,7 @@ class ParticipationAdmin(admin.ModelAdmin): def get_queryset(self, request): qs = super(ParticipationAdmin, self).get_queryset(request) return _filter_course_linked_obj_for_user(qs, request.user) - + def formfield_for_foreignkey(self, db_field, request, **kwargs): if db_field.name == "course": kwargs["queryset"] = _filter_courses_for_user( diff --git a/course/models.py b/course/models.py index 0519de8f..4cca2f36 100644 --- a/course/models.py +++ b/course/models.py @@ -217,12 +217,11 @@ class Course(models.Model): participants = models.ManyToManyField(settings.AUTH_USER_MODEL, through='Participation') - - # Extra_attribute for row-level permission - - course_director = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='directors') - course_staff = models.ManyToManyField(settings.AUTH_USER_MODEL,related_name='staffs') + course_director = models.ManyToManyField(settings.AUTH_USER_MODEL, + related_name='directors') + course_staff = models.ManyToManyField(settings.AUTH_USER_MODEL, + related_name='staffs') class Meta: verbose_name = _("Course") @@ -284,13 +283,14 @@ class Event(models.Model): shown_in_calendar = models.BooleanField(default=True, verbose_name=_('Shown in calendar')) - + # Extra attribute for row-level permission - - creator = models.ForeignKey(settings.AUTH_USER_MODEL,default=1) - + + creator = models.ForeignKey(settings.AUTH_USER_MODEL, default=1) + def clean(self): - if self.creator not in self.course.course_staff.all() and self.creator not in self.course.course_director.all(): + if (self.creator not in self.course.course_staff.all() and + self.creator not in self.course.course_director.all()): raise forms.ValidationError("You cannot add event to this course.") class Meta: diff --git a/course/rules.py b/course/rules.py index c991f57c..3434b5cd 100644 --- a/course/rules.py +++ b/course/rules.py @@ -9,13 +9,15 @@ import rules def is_course_director(user, course): if not course: return False - return user in course.course_director.all() + return user in course.course_director.all() + @rules.predicate def is_course_staff(user, course): if not course: return False - return user in course.course_staff.all() + return user in course.course_staff.all() + @rules.predicate def is_event_staff(user, event): @@ -30,7 +32,7 @@ def is_event_staff(user, event): rules.add_rule('change_course', is_course_director) rules.add_rule('delete_course', is_course_director) rules.add_rule('change_event', is_event_staff) -rules.add_rule('delete_event',is_event_staff) +rules.add_rule('delete_event', is_event_staff) # Permissions rules.add_perm('course.change_course', is_course_director) diff --git a/course/test_perm.py b/course/test_perm.py index 9d8f0de7..7e691066 100644 --- a/course/test_perm.py +++ b/course/test_perm.py @@ -1,7 +1,7 @@ -from course.models import Course, Event +from course.models import Course from django.contrib.auth.models import User -import rules -luh = User.objects.get(username = "luh") + +luh = User.objects.get(username="luh") course1 = Course.objects.get(identifier='cs999-sp20') course2 = Course.objects.get(identifier='math999-sp20') staff1 = User.objects.get(username='staff_1') @@ -11,4 +11,4 @@ staff2 = User.objects.get(username='staff_2') print(luh.has_perm('course.change_course', course1)) print(staff1.has_perm('course.change_course', course1)) -print(staff2.has_perm('course.change_course', course2)) \ No newline at end of file +print(staff2.has_perm('course.change_course', course2)) diff --git a/course/views.py b/course/views.py index 86718581..e755fe74 100644 --- a/course/views.py +++ b/course/views.py @@ -1360,15 +1360,12 @@ class EditCourseForm(StyledModelForm): @course_view - def edit_course(pctx): - request = pctx.request - if not pctx.has_permission(pperm.edit_course) or not request.user.has_perm('course.change_course', pctx.course) : + if (not pctx.has_permission(pperm.edit_course) or + not request.user.has_perm('course.change_course', pctx.course)): raise PermissionDenied() - - if request.method == 'POST': form = EditCourseForm(request.POST, instance=pctx.course) if form.is_valid(): -- GitLab From 0d5f21e515f14fa81fae98b738ee0d4c4f356574 Mon Sep 17 00:00:00 2001 From: luh Date: Tue, 30 May 2017 15:36:15 -0500 Subject: [PATCH 06/15] Fixing style --- course/views.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/course/views.py b/course/views.py index e755fe74..a5386fa2 100644 --- a/course/views.py +++ b/course/views.py @@ -81,9 +81,7 @@ from course.utils import ( # noqa render_course_page, CoursePageContext) -from django.shortcuts import get_object_or_404 -from rules.contrib.views import permission_required # {{{ for mypy -- GitLab From 68bafa7ff5e455c69cfe834c7da2a6f67244b198 Mon Sep 17 00:00:00 2001 From: luh Date: Tue, 30 May 2017 15:41:38 -0500 Subject: [PATCH 07/15] Fixing style --- course/views.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/course/views.py b/course/views.py index a5386fa2..788f1a42 100644 --- a/course/views.py +++ b/course/views.py @@ -81,8 +81,6 @@ from course.utils import ( # noqa render_course_page, CoursePageContext) - - # {{{ for mypy if False: @@ -1360,7 +1358,7 @@ class EditCourseForm(StyledModelForm): @course_view def edit_course(pctx): request = pctx.request - if (not pctx.has_permission(pperm.edit_course) or + if (not pctx.has_permission(pperm.edit_course) or not request.user.has_perm('course.change_course', pctx.course)): raise PermissionDenied() -- GitLab From 1e1dfb754f77905ebecb9bd63bafad2225a0ae4a Mon Sep 17 00:00:00 2001 From: luh Date: Sat, 3 Jun 2017 17:30:02 -0500 Subject: [PATCH 08/15] "changed to original permission system but still need to add a creator in event" --- .project | 18 ------------ .pydevproject | 9 ------ .settings/org.eclipse.core.resources.prefs | 12 -------- course/admin.py | 26 ++++++++--------- course/models.py | 16 ++++++----- course/rules.py | 33 +++++++++++++++------- course/test_perm.py | 14 --------- 7 files changed, 44 insertions(+), 84 deletions(-) delete mode 100644 .project delete mode 100644 .pydevproject delete mode 100644 .settings/org.eclipse.core.resources.prefs delete mode 100644 course/test_perm.py diff --git a/.project b/.project deleted file mode 100644 index e0e2651c..00000000 --- a/.project +++ /dev/null @@ -1,18 +0,0 @@ - - - relate - - - - - - org.python.pydev.PyDevBuilder - - - - - - org.python.pydev.pythonNature - org.python.pydev.django.djangoNature - - diff --git a/.pydevproject b/.pydevproject deleted file mode 100644 index 2a704e1c..00000000 --- a/.pydevproject +++ /dev/null @@ -1,9 +0,0 @@ - - -Default -python 2.7 - -DJANGO_SETTINGS_MODULE -relate.settings - - diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index c81d4a9a..00000000 --- a/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,12 +0,0 @@ -eclipse.preferences.version=1 -encoding//accounts/admin.py=utf-8 -encoding//accounts/models.py=utf-8 -encoding//course/admin.py=utf-8 -encoding//course/analytics.py=utf-8 -encoding//course/calendar.py=utf-8 -encoding//course/exam.py=utf-8 -encoding//course/models.py=utf-8 -encoding//course/views.py=utf-8 -encoding//formats/__init__.py=utf-8 -encoding//relate/urls.py=utf-8 -encoding/setup.py=utf-8 diff --git a/course/admin.py b/course/admin.py index 71a86e9b..a1e0150f 100644 --- a/course/admin.py +++ b/course/admin.py @@ -137,8 +137,6 @@ class CourseAdmin(ObjectPermissionsModelAdmin): "name", "time_period") - filter_horizontal = ('course_director', 'course_staff') - form = CourseAdminForm save_on_top = True @@ -149,9 +147,9 @@ class CourseAdmin(ObjectPermissionsModelAdmin): # These are created only through the course creation form. return False -# def get_queryset(self, request): -# qs = super(CourseAdmin, self).get_queryset(request) -# return _filter_courses_for_user(qs, request.user) + def get_queryset(self, request): + qs = super(CourseAdmin, self).get_queryset(request) + return _filter_courses_for_user(qs, request.user) # }}} pass @@ -191,15 +189,15 @@ class EventAdmin(ObjectPermissionsModelAdmin): # {{{ permissions -# def get_queryset(self, request): -# qs = super(EventAdmin, self).get_queryset(request) -# return _filter_course_linked_obj_for_user(qs, request.user) -# def formfield_for_foreignkey(self, db_field, request, **kwargs): -# if db_field.name == "course": -# kwargs["queryset"] = _filter_courses_for_user( -# Course.objects, request.user) -# return super(EventAdmin, self).formfield_for_foreignkey( -# db_field, request, **kwargs) + def get_queryset(self, request): + qs = super(EventAdmin, self).get_queryset(request) + return _filter_course_linked_obj_for_user(qs, request.user) + def formfield_for_foreignkey(self, db_field, request, **kwargs): + if db_field.name == "course": + kwargs["queryset"] = _filter_courses_for_user( + Course.objects, request.user) + return super(EventAdmin, self).formfield_for_foreignkey( + db_field, request, **kwargs) # }}} diff --git a/course/models.py b/course/models.py index 4cca2f36..e057be28 100644 --- a/course/models.py +++ b/course/models.py @@ -217,11 +217,7 @@ class Course(models.Model): participants = models.ManyToManyField(settings.AUTH_USER_MODEL, through='Participation') - # Extra_attribute for row-level permission - course_director = models.ManyToManyField(settings.AUTH_USER_MODEL, - related_name='directors') - course_staff = models.ManyToManyField(settings.AUTH_USER_MODEL, - related_name='staffs') + class Meta: verbose_name = _("Course") @@ -289,8 +285,14 @@ class Event(models.Model): creator = models.ForeignKey(settings.AUTH_USER_MODEL, default=1) def clean(self): - if (self.creator not in self.course.course_staff.all() and - self.creator not in self.course.course_director.all()): + isInstructor = ParticipationRole.objects.filter(course=self.course, + name="Instructor") + isTa = ParticipationRole.objects.filter(course=self.course, + name="Teaching Assistant") + if ((self.creator not in self.course.participants + .filter(participations__roles=isInstructor) and + self.creator not in self.course.participants + .filter(participations__roles=isTa))): raise forms.ValidationError("You cannot add event to this course.") class Meta: diff --git a/course/rules.py b/course/rules.py index 3434b5cd..57b9806a 100644 --- a/course/rules.py +++ b/course/rules.py @@ -1,42 +1,55 @@ from __future__ import absolute_import import rules - +from course.models import ParticipationRole # Predicates + @rules.predicate -def is_course_director(user, course): +def is_course_insturctor(user, course): if not course: return False - return user in course.course_director.all() + roles = ParticipationRole.objects.filter(course=course, name="Instructor") + return user in course.participants.filter(participations__roles=roles) @rules.predicate -def is_course_staff(user, course): +def is_course_ta(user, course): if not course: return False - return user in course.course_staff.all() + roles = ParticipationRole.objects.filter(course=course,name="Teaching Assistant") + return user in course.participants.filter(participations__roles=roles) +def is_course_staff(user,course): + if not course: + return False + isInstructor = ParticipationRole.objects.filter(course=course,name="Instructor") + isTa = ParticipationRole.objects.filter(course=course,name="Teaching Assistant") + return (user in course.participants.filter(participations__roles=isInstructor) + or user in course.participants.filter(participations__roles=isTa)) @rules.predicate def is_event_staff(user, event): if not event: return False course = event.course - return user in course.course_staff.all() or user in course.course_director.all() + isInstructor = ParticipationRole.objects.filter(course=course,name="Instructor") + isTa = ParticipationRole.objects.filter(course=course,name="Teaching Assistant") + return (user in course.participants.filter(participations__roles=isInstructor) or + user in course.participants.filter(participations__roles=isTa)) # Rules -rules.add_rule('change_course', is_course_director) -rules.add_rule('delete_course', is_course_director) +rules.add_rule('change_course', is_course_insturctor) +rules.add_rule('delete_course', is_course_ta) rules.add_rule('change_event', is_event_staff) rules.add_rule('delete_event', is_event_staff) # Permissions -rules.add_perm('course.change_course', is_course_director) -rules.add_perm('course.delete_course', is_course_director) +rules.add_perm('course.change_course', is_course_insturctor) +rules.add_perm('course.delete_course', is_course_insturctor) rules.add_perm('course', rules.always_allow) rules.add_perm('course.delete_event', is_event_staff) diff --git a/course/test_perm.py b/course/test_perm.py deleted file mode 100644 index 7e691066..00000000 --- a/course/test_perm.py +++ /dev/null @@ -1,14 +0,0 @@ -from course.models import Course -from django.contrib.auth.models import User - -luh = User.objects.get(username="luh") -course1 = Course.objects.get(identifier='cs999-sp20') -course2 = Course.objects.get(identifier='math999-sp20') -staff1 = User.objects.get(username='staff_1') - -staff2 = User.objects.get(username='staff_2') - - -print(luh.has_perm('course.change_course', course1)) -print(staff1.has_perm('course.change_course', course1)) -print(staff2.has_perm('course.change_course', course2)) -- GitLab From 3ecc293f32b69ce3b3d0f0f91bd888b7fe669d74 Mon Sep 17 00:00:00 2001 From: luh Date: Sat, 3 Jun 2017 17:43:25 -0500 Subject: [PATCH 09/15] "fixing styles" --- course/admin.py | 1 + course/models.py | 7 ++++--- course/rules.py | 14 ++++++++------ 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/course/admin.py b/course/admin.py index a1e0150f..6870ea14 100644 --- a/course/admin.py +++ b/course/admin.py @@ -192,6 +192,7 @@ class EventAdmin(ObjectPermissionsModelAdmin): def get_queryset(self, request): qs = super(EventAdmin, self).get_queryset(request) return _filter_course_linked_obj_for_user(qs, request.user) + def formfield_for_foreignkey(self, db_field, request, **kwargs): if db_field.name == "course": kwargs["queryset"] = _filter_courses_for_user( diff --git a/course/models.py b/course/models.py index e057be28..77eba3d9 100644 --- a/course/models.py +++ b/course/models.py @@ -218,7 +218,6 @@ class Course(models.Model): participants = models.ManyToManyField(settings.AUTH_USER_MODEL, through='Participation') - class Meta: verbose_name = _("Course") verbose_name_plural = _("Courses") @@ -285,10 +284,12 @@ class Event(models.Model): creator = models.ForeignKey(settings.AUTH_USER_MODEL, default=1) def clean(self): + instructor = "Instructor" + ta = "Teaching Assistant" isInstructor = ParticipationRole.objects.filter(course=self.course, - name="Instructor") + name=instructor) isTa = ParticipationRole.objects.filter(course=self.course, - name="Teaching Assistant") + name=ta) if ((self.creator not in self.course.participants .filter(participations__roles=isInstructor) and self.creator not in self.course.participants diff --git a/course/rules.py b/course/rules.py index 57b9806a..87ab59b5 100644 --- a/course/rules.py +++ b/course/rules.py @@ -5,12 +5,14 @@ from course.models import ParticipationRole # Predicates +instructor = "Instructor" +ta = "Teaching Assistant" @rules.predicate def is_course_insturctor(user, course): if not course: return False - roles = ParticipationRole.objects.filter(course=course, name="Instructor") + roles = ParticipationRole.objects.filter(course=course, name=instructor) return user in course.participants.filter(participations__roles=roles) @@ -18,14 +20,14 @@ def is_course_insturctor(user, course): def is_course_ta(user, course): if not course: return False - roles = ParticipationRole.objects.filter(course=course,name="Teaching Assistant") + roles = ParticipationRole.objects.filter(course=course, name=ta) return user in course.participants.filter(participations__roles=roles) def is_course_staff(user,course): if not course: return False - isInstructor = ParticipationRole.objects.filter(course=course,name="Instructor") - isTa = ParticipationRole.objects.filter(course=course,name="Teaching Assistant") + isInstructor = ParticipationRole.objects.filter(course=course,name=instructor) + isTa = ParticipationRole.objects.filter(course=course,name=ta) return (user in course.participants.filter(participations__roles=isInstructor) or user in course.participants.filter(participations__roles=isTa)) @@ -34,8 +36,8 @@ def is_event_staff(user, event): if not event: return False course = event.course - isInstructor = ParticipationRole.objects.filter(course=course,name="Instructor") - isTa = ParticipationRole.objects.filter(course=course,name="Teaching Assistant") + isInstructor = ParticipationRole.objects.filter(course=course,name=instructor) + isTa = ParticipationRole.objects.filter(course=course,name=ta) return (user in course.participants.filter(participations__roles=isInstructor) or user in course.participants.filter(participations__roles=isTa)) -- GitLab From f68679d3a6958ffcc4d71cf9200412ba384c40fc Mon Sep 17 00:00:00 2001 From: luh Date: Mon, 5 Jun 2017 10:47:32 -0500 Subject: [PATCH 10/15] fixing styles --- course/models.py | 15 +++++++-------- course/rules.py | 19 +++++++++++-------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/course/models.py b/course/models.py index 77eba3d9..c8603ec6 100644 --- a/course/models.py +++ b/course/models.py @@ -284,16 +284,15 @@ class Event(models.Model): creator = models.ForeignKey(settings.AUTH_USER_MODEL, default=1) def clean(self): - instructor = "Instructor" - ta = "Teaching Assistant" - isInstructor = ParticipationRole.objects.filter(course=self.course, - name=instructor) - isTa = ParticipationRole.objects.filter(course=self.course, - name=ta) + + is_instructor = ParticipationRole.objects.filter(course=self.course, + name="Instructor") + is_ta = ParticipationRole.objects.filter(course=self.course, + name="Teaching Assistant") if ((self.creator not in self.course.participants - .filter(participations__roles=isInstructor) and + .filter(participations__roles=is_instructor) and self.creator not in self.course.participants - .filter(participations__roles=isTa))): + .filter(participations__roles=is_ta))): raise forms.ValidationError("You cannot add event to this course.") class Meta: diff --git a/course/rules.py b/course/rules.py index 87ab59b5..41c42ca5 100644 --- a/course/rules.py +++ b/course/rules.py @@ -8,6 +8,7 @@ from course.models import ParticipationRole instructor = "Instructor" ta = "Teaching Assistant" + @rules.predicate def is_course_insturctor(user, course): if not course: @@ -23,23 +24,25 @@ def is_course_ta(user, course): roles = ParticipationRole.objects.filter(course=course, name=ta) return user in course.participants.filter(participations__roles=roles) + def is_course_staff(user,course): if not course: return False - isInstructor = ParticipationRole.objects.filter(course=course,name=instructor) - isTa = ParticipationRole.objects.filter(course=course,name=ta) - return (user in course.participants.filter(participations__roles=isInstructor) - or user in course.participants.filter(participations__roles=isTa)) + is_instructor = ParticipationRole.objects.filter(course=course, name=instructor) + is_ta = ParticipationRole.objects.filter(course=course, name=ta) + return (user in course.participants.filter(participations__roles=is_instructor) + or user in course.participants.filter(participations__roles=is_ta)) + @rules.predicate def is_event_staff(user, event): if not event: return False course = event.course - isInstructor = ParticipationRole.objects.filter(course=course,name=instructor) - isTa = ParticipationRole.objects.filter(course=course,name=ta) - return (user in course.participants.filter(participations__roles=isInstructor) or - user in course.participants.filter(participations__roles=isTa)) + is_instructor = ParticipationRole.objects.filter(course=course, name=instructor) + is_ta = ParticipationRole.objects.filter(course=course, name=ta) + return (user in course.participants.filter(participations__roles=is_instructor) or + user in course.participants.filter(participations__roles=is_ta)) # Rules -- GitLab From 9204c0812da92c9b9539f22b43b1279973b44363 Mon Sep 17 00:00:00 2001 From: luh Date: Mon, 5 Jun 2017 10:53:39 -0500 Subject: [PATCH 11/15] fixing styles --- course/rules.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/course/rules.py b/course/rules.py index 41c42ca5..84b1bd03 100644 --- a/course/rules.py +++ b/course/rules.py @@ -25,7 +25,7 @@ def is_course_ta(user, course): return user in course.participants.filter(participations__roles=roles) -def is_course_staff(user,course): +def is_course_staff(user, course): if not course: return False is_instructor = ParticipationRole.objects.filter(course=course, name=instructor) @@ -39,9 +39,10 @@ def is_event_staff(user, event): if not event: return False course = event.course - is_instructor = ParticipationRole.objects.filter(course=course, name=instructor) + is_instructor = ParticipationRole.objects.filter(course=course, name=instructor) is_ta = ParticipationRole.objects.filter(course=course, name=ta) - return (user in course.participants.filter(participations__roles=is_instructor) or + return (user in + course.participants.filter(participations__roles=is_instructor) or user in course.participants.filter(participations__roles=is_ta)) -- GitLab From 392e5c68f98fc8338d8e58acb36da0fa3b5f7024 Mon Sep 17 00:00:00 2001 From: luh Date: Mon, 5 Jun 2017 14:00:14 -0500 Subject: [PATCH 12/15] remove migrations file --- course/migrations/0102_auto_20170529_2106.py | 43 -------------------- 1 file changed, 43 deletions(-) delete mode 100644 course/migrations/0102_auto_20170529_2106.py diff --git a/course/migrations/0102_auto_20170529_2106.py b/course/migrations/0102_auto_20170529_2106.py deleted file mode 100644 index fe00f774..00000000 --- a/course/migrations/0102_auto_20170529_2106.py +++ /dev/null @@ -1,43 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.7 on 2017-05-30 02:06 -from __future__ import unicode_literals - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('course', '0101_add_ticket_facility_and_validity'), - ] - - operations = [ - migrations.AddField( - model_name='course', - name='course_director', - field=models.ManyToManyField(related_name='directors', to=settings.AUTH_USER_MODEL), - ), - migrations.AddField( - model_name='course', - name='course_staff', - field=models.ManyToManyField(related_name='staffs', to=settings.AUTH_USER_MODEL), - ), - migrations.AddField( - model_name='event', - name='creator', - field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), - ), - migrations.AlterField( - model_name='participationpermission', - name='permission', - field=models.CharField(choices=[('edit_course', 'Edit course'), ('use_admin_interface', 'Use admin interface'), ('impersonate_role', 'Impersonate role'), ('set_fake_time', 'Set fake time'), ('set_pretend_facility', 'Pretend to be in facility'), ('edit_course_permissions', 'Edit course permissions'), ('view_hidden_course_page', 'View hidden course page'), ('view_calendar', 'View calendar'), ('send_instant_message', 'Send instant message'), ('access_files_for', 'Access files for'), ('included_in_grade_statistics', 'Included in grade statistics'), ('edit_exam', 'Edit exam'), ('issue_exam_ticket', 'Issue exam ticket'), ('batch_issue_exam_ticket', 'Batch issue exam ticket'), ('view_participant_masked_profile', "View participants' masked profile only"), ('view_flow_sessions_from_role', 'View flow sessions from role'), ('view_gradebook', 'View gradebook'), ('edit_grading_opportunity', 'Edit grading opportunity'), ('assign_grade', 'Assign grade'), ('view_grader_stats', 'View grader stats'), ('batch_import_grade', 'Batch-import grades'), ('batch_export_grade', 'Batch-export grades'), ('batch_download_submission', 'Batch-download submissions'), ('impose_flow_session_deadline', 'Impose flow session deadline'), ('batch_impose_flow_session_deadline', 'Batch-impose flow session deadline'), ('end_flow_session', 'End flow session'), ('batch_end_flow_session', 'Batch-end flow sessions'), ('regrade_flow_session', 'Regrade flow session'), ('batch_regrade_flow_session', 'Batch-regrade flow sessions'), ('recalculate_flow_session_grade', 'Recalculate flow session grade'), ('batch_recalculate_flow_session_grade', 'Batch-recalculate flow sesssion grades'), ('reopen_flow_session', 'Reopen flow session'), ('grant_exception', 'Grant exception'), ('view_analytics', 'View analytics'), ('preview_content', 'Preview content'), ('update_content', 'Update content'), ('use_markup_sandbox', 'Use markup sandbox'), ('use_page_sandbox', 'Use page sandbox'), ('test_flow', 'Test flow'), ('edit_events', 'Edit events'), ('query_participation', 'Query participation'), ('edit_participation', 'Edit participation'), ('preapprove_participation', 'Preapprove participation'), ('manage_instant_flow_requests', 'Manage instant flow requests')], db_index=True, max_length=200, verbose_name='Permission'), - ), - migrations.AlterField( - model_name='participationrolepermission', - name='permission', - field=models.CharField(choices=[('edit_course', 'Edit course'), ('use_admin_interface', 'Use admin interface'), ('impersonate_role', 'Impersonate role'), ('set_fake_time', 'Set fake time'), ('set_pretend_facility', 'Pretend to be in facility'), ('edit_course_permissions', 'Edit course permissions'), ('view_hidden_course_page', 'View hidden course page'), ('view_calendar', 'View calendar'), ('send_instant_message', 'Send instant message'), ('access_files_for', 'Access files for'), ('included_in_grade_statistics', 'Included in grade statistics'), ('edit_exam', 'Edit exam'), ('issue_exam_ticket', 'Issue exam ticket'), ('batch_issue_exam_ticket', 'Batch issue exam ticket'), ('view_participant_masked_profile', "View participants' masked profile only"), ('view_flow_sessions_from_role', 'View flow sessions from role'), ('view_gradebook', 'View gradebook'), ('edit_grading_opportunity', 'Edit grading opportunity'), ('assign_grade', 'Assign grade'), ('view_grader_stats', 'View grader stats'), ('batch_import_grade', 'Batch-import grades'), ('batch_export_grade', 'Batch-export grades'), ('batch_download_submission', 'Batch-download submissions'), ('impose_flow_session_deadline', 'Impose flow session deadline'), ('batch_impose_flow_session_deadline', 'Batch-impose flow session deadline'), ('end_flow_session', 'End flow session'), ('batch_end_flow_session', 'Batch-end flow sessions'), ('regrade_flow_session', 'Regrade flow session'), ('batch_regrade_flow_session', 'Batch-regrade flow sessions'), ('recalculate_flow_session_grade', 'Recalculate flow session grade'), ('batch_recalculate_flow_session_grade', 'Batch-recalculate flow sesssion grades'), ('reopen_flow_session', 'Reopen flow session'), ('grant_exception', 'Grant exception'), ('view_analytics', 'View analytics'), ('preview_content', 'Preview content'), ('update_content', 'Update content'), ('use_markup_sandbox', 'Use markup sandbox'), ('use_page_sandbox', 'Use page sandbox'), ('test_flow', 'Test flow'), ('edit_events', 'Edit events'), ('query_participation', 'Query participation'), ('edit_participation', 'Edit participation'), ('preapprove_participation', 'Preapprove participation'), ('manage_instant_flow_requests', 'Manage instant flow requests')], db_index=True, max_length=200, verbose_name='Permission'), - ), - ] -- GitLab From 90d65ce40be31788f4bd63b1f6910a53f2e87d5c Mon Sep 17 00:00:00 2001 From: luh Date: Mon, 5 Jun 2017 14:13:37 -0500 Subject: [PATCH 13/15] "add migrations files" --- course/migrations/0102_auto_20170529_2106.py | 43 ++++++++++++++++++++ course/migrations/0103_auto_20170605_1352.py | 23 +++++++++++ 2 files changed, 66 insertions(+) create mode 100644 course/migrations/0102_auto_20170529_2106.py create mode 100644 course/migrations/0103_auto_20170605_1352.py diff --git a/course/migrations/0102_auto_20170529_2106.py b/course/migrations/0102_auto_20170529_2106.py new file mode 100644 index 00000000..fe00f774 --- /dev/null +++ b/course/migrations/0102_auto_20170529_2106.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-05-30 02:06 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('course', '0101_add_ticket_facility_and_validity'), + ] + + operations = [ + migrations.AddField( + model_name='course', + name='course_director', + field=models.ManyToManyField(related_name='directors', to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='course', + name='course_staff', + field=models.ManyToManyField(related_name='staffs', to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='event', + name='creator', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='participationpermission', + name='permission', + field=models.CharField(choices=[('edit_course', 'Edit course'), ('use_admin_interface', 'Use admin interface'), ('impersonate_role', 'Impersonate role'), ('set_fake_time', 'Set fake time'), ('set_pretend_facility', 'Pretend to be in facility'), ('edit_course_permissions', 'Edit course permissions'), ('view_hidden_course_page', 'View hidden course page'), ('view_calendar', 'View calendar'), ('send_instant_message', 'Send instant message'), ('access_files_for', 'Access files for'), ('included_in_grade_statistics', 'Included in grade statistics'), ('edit_exam', 'Edit exam'), ('issue_exam_ticket', 'Issue exam ticket'), ('batch_issue_exam_ticket', 'Batch issue exam ticket'), ('view_participant_masked_profile', "View participants' masked profile only"), ('view_flow_sessions_from_role', 'View flow sessions from role'), ('view_gradebook', 'View gradebook'), ('edit_grading_opportunity', 'Edit grading opportunity'), ('assign_grade', 'Assign grade'), ('view_grader_stats', 'View grader stats'), ('batch_import_grade', 'Batch-import grades'), ('batch_export_grade', 'Batch-export grades'), ('batch_download_submission', 'Batch-download submissions'), ('impose_flow_session_deadline', 'Impose flow session deadline'), ('batch_impose_flow_session_deadline', 'Batch-impose flow session deadline'), ('end_flow_session', 'End flow session'), ('batch_end_flow_session', 'Batch-end flow sessions'), ('regrade_flow_session', 'Regrade flow session'), ('batch_regrade_flow_session', 'Batch-regrade flow sessions'), ('recalculate_flow_session_grade', 'Recalculate flow session grade'), ('batch_recalculate_flow_session_grade', 'Batch-recalculate flow sesssion grades'), ('reopen_flow_session', 'Reopen flow session'), ('grant_exception', 'Grant exception'), ('view_analytics', 'View analytics'), ('preview_content', 'Preview content'), ('update_content', 'Update content'), ('use_markup_sandbox', 'Use markup sandbox'), ('use_page_sandbox', 'Use page sandbox'), ('test_flow', 'Test flow'), ('edit_events', 'Edit events'), ('query_participation', 'Query participation'), ('edit_participation', 'Edit participation'), ('preapprove_participation', 'Preapprove participation'), ('manage_instant_flow_requests', 'Manage instant flow requests')], db_index=True, max_length=200, verbose_name='Permission'), + ), + migrations.AlterField( + model_name='participationrolepermission', + name='permission', + field=models.CharField(choices=[('edit_course', 'Edit course'), ('use_admin_interface', 'Use admin interface'), ('impersonate_role', 'Impersonate role'), ('set_fake_time', 'Set fake time'), ('set_pretend_facility', 'Pretend to be in facility'), ('edit_course_permissions', 'Edit course permissions'), ('view_hidden_course_page', 'View hidden course page'), ('view_calendar', 'View calendar'), ('send_instant_message', 'Send instant message'), ('access_files_for', 'Access files for'), ('included_in_grade_statistics', 'Included in grade statistics'), ('edit_exam', 'Edit exam'), ('issue_exam_ticket', 'Issue exam ticket'), ('batch_issue_exam_ticket', 'Batch issue exam ticket'), ('view_participant_masked_profile', "View participants' masked profile only"), ('view_flow_sessions_from_role', 'View flow sessions from role'), ('view_gradebook', 'View gradebook'), ('edit_grading_opportunity', 'Edit grading opportunity'), ('assign_grade', 'Assign grade'), ('view_grader_stats', 'View grader stats'), ('batch_import_grade', 'Batch-import grades'), ('batch_export_grade', 'Batch-export grades'), ('batch_download_submission', 'Batch-download submissions'), ('impose_flow_session_deadline', 'Impose flow session deadline'), ('batch_impose_flow_session_deadline', 'Batch-impose flow session deadline'), ('end_flow_session', 'End flow session'), ('batch_end_flow_session', 'Batch-end flow sessions'), ('regrade_flow_session', 'Regrade flow session'), ('batch_regrade_flow_session', 'Batch-regrade flow sessions'), ('recalculate_flow_session_grade', 'Recalculate flow session grade'), ('batch_recalculate_flow_session_grade', 'Batch-recalculate flow sesssion grades'), ('reopen_flow_session', 'Reopen flow session'), ('grant_exception', 'Grant exception'), ('view_analytics', 'View analytics'), ('preview_content', 'Preview content'), ('update_content', 'Update content'), ('use_markup_sandbox', 'Use markup sandbox'), ('use_page_sandbox', 'Use page sandbox'), ('test_flow', 'Test flow'), ('edit_events', 'Edit events'), ('query_participation', 'Query participation'), ('edit_participation', 'Edit participation'), ('preapprove_participation', 'Preapprove participation'), ('manage_instant_flow_requests', 'Manage instant flow requests')], db_index=True, max_length=200, verbose_name='Permission'), + ), + ] diff --git a/course/migrations/0103_auto_20170605_1352.py b/course/migrations/0103_auto_20170605_1352.py new file mode 100644 index 00000000..c017852c --- /dev/null +++ b/course/migrations/0103_auto_20170605_1352.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-06-05 18:52 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('course', '0102_auto_20170529_2106'), + ] + + operations = [ + migrations.RemoveField( + model_name='course', + name='course_director', + ), + migrations.RemoveField( + model_name='course', + name='course_staff', + ), + ] -- GitLab From d5c159f854d321ceb8e180f0ab23d2309dfb86d0 Mon Sep 17 00:00:00 2001 From: luh Date: Mon, 12 Jun 2017 22:02:09 -0500 Subject: [PATCH 14/15] changing approach for row-level validation for event --- course/admin.py | 3 +- ...0529_2106.py => 0102_add_event_creator.py} | 10 ---- course/migrations/0103_auto_20170605_1352.py | 23 ------- course/models.py | 13 ++-- course/rules.py | 60 +++++++------------ 5 files changed, 26 insertions(+), 83 deletions(-) rename course/migrations/{0102_auto_20170529_2106.py => 0102_add_event_creator.py} (93%) delete mode 100644 course/migrations/0103_auto_20170605_1352.py diff --git a/course/admin.py b/course/admin.py index 6870ea14..fe24a860 100644 --- a/course/admin.py +++ b/course/admin.py @@ -202,7 +202,7 @@ class EventAdmin(ObjectPermissionsModelAdmin): # }}} - # Overrides for row-level permission + # Overrides for row-level permission {{{ def get_form(self, request, *args, **kwargs): form = super(EventAdmin, self).get_form(request, *args, **kwargs) @@ -217,6 +217,7 @@ class EventAdmin(ObjectPermissionsModelAdmin): pass + # }}} admin.site.register(Event, EventAdmin) diff --git a/course/migrations/0102_auto_20170529_2106.py b/course/migrations/0102_add_event_creator.py similarity index 93% rename from course/migrations/0102_auto_20170529_2106.py rename to course/migrations/0102_add_event_creator.py index fe00f774..3c1464af 100644 --- a/course/migrations/0102_auto_20170529_2106.py +++ b/course/migrations/0102_add_event_creator.py @@ -15,16 +15,6 @@ class Migration(migrations.Migration): ] operations = [ - migrations.AddField( - model_name='course', - name='course_director', - field=models.ManyToManyField(related_name='directors', to=settings.AUTH_USER_MODEL), - ), - migrations.AddField( - model_name='course', - name='course_staff', - field=models.ManyToManyField(related_name='staffs', to=settings.AUTH_USER_MODEL), - ), migrations.AddField( model_name='event', name='creator', diff --git a/course/migrations/0103_auto_20170605_1352.py b/course/migrations/0103_auto_20170605_1352.py deleted file mode 100644 index c017852c..00000000 --- a/course/migrations/0103_auto_20170605_1352.py +++ /dev/null @@ -1,23 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.7 on 2017-06-05 18:52 -from __future__ import unicode_literals - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('course', '0102_auto_20170529_2106'), - ] - - operations = [ - migrations.RemoveField( - model_name='course', - name='course_director', - ), - migrations.RemoveField( - model_name='course', - name='course_staff', - ), - ] diff --git a/course/models.py b/course/models.py index c8603ec6..e6b67a53 100644 --- a/course/models.py +++ b/course/models.py @@ -284,15 +284,10 @@ class Event(models.Model): creator = models.ForeignKey(settings.AUTH_USER_MODEL, default=1) def clean(self): - - is_instructor = ParticipationRole.objects.filter(course=self.course, - name="Instructor") - is_ta = ParticipationRole.objects.filter(course=self.course, - name="Teaching Assistant") - if ((self.creator not in self.course.participants - .filter(participations__roles=is_instructor) and - self.creator not in self.course.participants - .filter(participations__roles=is_ta))): + event_edit_staff = [i.user for i in Participation.objects + .filter(course=self.course) + if i.has_permission('edit_events', None)] + if (self.creator not in event_edit_staff): raise forms.ValidationError("You cannot add event to this course.") class Meta: diff --git a/course/rules.py b/course/rules.py index 84b1bd03..924cc6c7 100644 --- a/course/rules.py +++ b/course/rules.py @@ -1,62 +1,42 @@ from __future__ import absolute_import import rules -from course.models import ParticipationRole +from course.models import Participation # Predicates -instructor = "Instructor" -ta = "Teaching Assistant" - @rules.predicate -def is_course_insturctor(user, course): +def has_edit_course_perm(user, course): if not course: return False - roles = ParticipationRole.objects.filter(course=course, name=instructor) - return user in course.participants.filter(participations__roles=roles) + course_edit_staff = [i.user + for i in Participation.objects.filter(course=course) + if i.has_permission('edit_course', None)] + return user in course_edit_staff @rules.predicate -def is_course_ta(user, course): - if not course: - return False - roles = ParticipationRole.objects.filter(course=course, name=ta) - return user in course.participants.filter(participations__roles=roles) - - -def is_course_staff(user, course): - if not course: - return False - is_instructor = ParticipationRole.objects.filter(course=course, name=instructor) - is_ta = ParticipationRole.objects.filter(course=course, name=ta) - return (user in course.participants.filter(participations__roles=is_instructor) - or user in course.participants.filter(participations__roles=is_ta)) - - -@rules.predicate -def is_event_staff(user, event): +def has_edit_event_perm(user, event): if not event: return False course = event.course - is_instructor = ParticipationRole.objects.filter(course=course, name=instructor) - is_ta = ParticipationRole.objects.filter(course=course, name=ta) - return (user in - course.participants.filter(participations__roles=is_instructor) or - user in course.participants.filter(participations__roles=is_ta)) - - + event_edit_staff = [i.user + for i in Participation.objects.filter(course=course) + if i.has_permission('edit_events', None)] + return user in event_edit_staff # Rules -rules.add_rule('change_course', is_course_insturctor) -rules.add_rule('delete_course', is_course_ta) -rules.add_rule('change_event', is_event_staff) -rules.add_rule('delete_event', is_event_staff) + +rules.add_rule('change_course', has_edit_course_perm) +rules.add_rule('delete_course', has_edit_course_perm) +rules.add_rule('change_event', has_edit_event_perm) +rules.add_rule('delete_event', has_edit_event_perm) # Permissions -rules.add_perm('course.change_course', is_course_insturctor) -rules.add_perm('course.delete_course', is_course_insturctor) +rules.add_perm('course.change_course', has_edit_course_perm) +rules.add_perm('course.delete_course', has_edit_course_perm) rules.add_perm('course', rules.always_allow) -rules.add_perm('course.delete_event', is_event_staff) -rules.add_perm('course.change_event', is_event_staff) +rules.add_perm('course.delete_event', has_edit_event_perm) +rules.add_perm('course.change_event', has_edit_event_perm) -- GitLab From 4986f3ede6678e2b159699b619cb081ec849badb Mon Sep 17 00:00:00 2001 From: luh Date: Mon, 12 Jun 2017 22:05:22 -0500 Subject: [PATCH 15/15] changing approach for row-level validation for event --- course/admin.py | 1 + 1 file changed, 1 insertion(+) diff --git a/course/admin.py b/course/admin.py index fe24a860..6ba71b40 100644 --- a/course/admin.py +++ b/course/admin.py @@ -219,6 +219,7 @@ class EventAdmin(ObjectPermissionsModelAdmin): # }}} + admin.site.register(Event, EventAdmin) # }}} -- GitLab