Commit 59ffd6af by Awais Committed by Awais Qureshi

Decorator checking user is part of publisher app group.

Create a mixing using decorator so that It can be use in drf also.
Only authenticated users can access the create course page.

Ecom-6094
parent 0455b87a
from functools import wraps
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.http import HttpResponseForbidden, HttpResponseRedirect from django.http import HttpResponseForbidden, HttpResponseRedirect
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from course_discovery.apps.publisher.models import Course, Seat from course_discovery.apps.publisher.models import Course, Seat
from course_discovery.apps.publisher.utils import is_publisher_admin, is_internal_user from course_discovery.apps.publisher.utils import (
is_publisher_admin, is_internal_user, is_publisher_user
)
class ViewPermissionMixin(object): class ViewPermissionMixin(object):
...@@ -81,3 +84,27 @@ def check_course_organization_permission(user, course, permission): ...@@ -81,3 +84,27 @@ def check_course_organization_permission(user, course, permission):
for org in course.organizations.all() for org in course.organizations.all()
] ]
) )
def publisher_user_required(func):
"""
View decorator that requires that the user is part any publisher group
permissions.
"""
@wraps(func)
def wrapped(request, *args, **kwargs): # pylint: disable=missing-docstring
if is_publisher_user(request.user):
return func(request, *args, **kwargs)
else:
return HttpResponseForbidden(u"Must be Publisher user to perform this action.")
return wrapped
class PublisherUserRequiredMixin(object):
"""
Mixin to view the user is part of any publisher app group.
"""
@method_decorator(publisher_user_required)
def dispatch(self, request, *args, **kwargs):
return super(PublisherUserRequiredMixin, self).dispatch(request, *args, **kwargs)
""" Tests publisher.utils""" """ Tests publisher.utils"""
from django.contrib.auth.models import Group from django.contrib.auth.models import Group
from django.test import TestCase from django.test import TestCase, RequestFactory
from guardian.shortcuts import assign_perm from mock import Mock
from guardian.shortcuts import assign_perm
from course_discovery.apps.core.tests.factories import UserFactory from course_discovery.apps.core.tests.factories import UserFactory
from course_discovery.apps.publisher.constants import ( from course_discovery.apps.publisher.constants import (
ADMIN_GROUP_NAME, INTERNAL_USER_GROUP_NAME, PARTNER_COORDINATOR_GROUP_NAME REVIEWER_GROUP_NAME, ADMIN_GROUP_NAME, INTERNAL_USER_GROUP_NAME,
PARTNER_COORDINATOR_GROUP_NAME
)
from course_discovery.apps.publisher.mixins import (
check_course_organization_permission, check_roles_access,
publisher_user_required
) )
from course_discovery.apps.publisher.mixins import check_course_organization_permission, check_roles_access
from course_discovery.apps.publisher.models import OrganizationExtension from course_discovery.apps.publisher.models import OrganizationExtension
from course_discovery.apps.publisher.tests import factories from course_discovery.apps.publisher.tests import factories
from course_discovery.apps.publisher.utils import ( from course_discovery.apps.publisher.utils import (
is_email_notification_enabled, is_publisher_admin, is_internal_user, is_email_notification_enabled, is_publisher_admin, is_internal_user,
get_internal_users, is_partner_coordinator_user get_internal_users, is_partner_coordinator_user, is_publisher_user
) )
...@@ -152,3 +157,39 @@ class PublisherUtilsTests(TestCase): ...@@ -152,3 +157,39 @@ class PublisherUtilsTests(TestCase):
self.assertTrue( self.assertTrue(
check_course_organization_permission(self.user, self.course, OrganizationExtension.VIEW_COURSE) check_course_organization_permission(self.user, self.course, OrganizationExtension.VIEW_COURSE)
) )
def test_is_publisher_user(self):
""" Verify the function returns a boolean indicating if the user
is part of any publisher app group.
"""
self.assertFalse(is_publisher_user(self.user))
self.user.groups.add(Group.objects.get(name=REVIEWER_GROUP_NAME))
self.assertTrue(is_publisher_user(self.user))
def test_require_is_publisher_user_without_group(self):
"""
Verify that decorator returns the error message if user is not part
of any publisher group.
"""
func = Mock()
decorated_func = publisher_user_required(func)
request = RequestFactory()
request.user = self.user
response = decorated_func(request, self.user)
self.assertContains(response, "Must be Publisher user to perform this action.", status_code=403)
self.assertFalse(func.called)
def test_is_publisher_user_with_publisher_group(self):
"""
Verify that decorator works fine with user is part of publisher
app group.
"""
func = Mock()
decorated_func = publisher_user_required(func)
request = RequestFactory()
request.user = self.user
self.user.groups.add(self.internal_user_group)
decorated_func(request, self.user)
self.assertTrue(func.called)
...@@ -46,6 +46,9 @@ class CreateUpdateCourseViewTests(TestCase): ...@@ -46,6 +46,9 @@ class CreateUpdateCourseViewTests(TestCase):
def setUp(self): def setUp(self):
super(CreateUpdateCourseViewTests, self).setUp() super(CreateUpdateCourseViewTests, self).setUp()
self.user = UserFactory() self.user = UserFactory()
self.internal_user_group = Group.objects.get(name=INTERNAL_USER_GROUP_NAME)
self.user.groups.add(self.internal_user_group)
self.organization_extension = factories.OrganizationExtensionFactory() self.organization_extension = factories.OrganizationExtensionFactory()
self.group = self.organization_extension.group self.group = self.organization_extension.group
self.user.groups.add(self.group) self.user.groups.add(self.group)
...@@ -83,6 +86,18 @@ class CreateUpdateCourseViewTests(TestCase): ...@@ -83,6 +86,18 @@ class CreateUpdateCourseViewTests(TestCase):
target_status_code=302 target_status_code=302
) )
def test_page_without_publisher_group_access(self):
"""
Verify that user can't access new course form page if user is not the
part of any group.
"""
self.client.logout()
self.client.login(username=UserFactory().username, password=USER_PASSWORD)
response = self.client.get(reverse('publisher:publisher_courses_new'))
self.assertContains(
response, "Must be Publisher user to perform this action.", status_code=403
)
def test_create_course_and_course_run_and_seat_with_errors(self): def test_create_course_and_course_run_and_seat_with_errors(self):
""" Verify that without providing required data course and other """ Verify that without providing required data course and other
objects cannot be created. objects cannot be created.
...@@ -180,28 +195,12 @@ class CreateUpdateCourseViewTests(TestCase): ...@@ -180,28 +195,12 @@ class CreateUpdateCourseViewTests(TestCase):
self.assertContains(response, 'Add new comment') self.assertContains(response, 'Add new comment')
self.assertContains(response, comment.comment) self.assertContains(response, comment.comment)
def test_course_edit_page_without_admin_rights(self): def test_update_course_without_publisher_admin_rights(self):
""" Verify that non publisher admin user can't access course edit page without rights. """ """ Verify that non-admin users cannot update the course. """
self.client.logout()
course_dict = model_to_dict(self.course) user = UserFactory()
updated_course_title = 'Updated {}'.format(self.course.title) self.client.login(username=user.username, password=USER_PASSWORD)
course_dict['title'] = updated_course_title
self.assertNotEqual(self.course.title, updated_course_title)
response = self.client.get(
reverse('publisher:publisher_courses_edit', kwargs={'pk': self.course.id})
)
self.assertEqual(response.status_code, 403)
self.user.groups.add(Group.objects.get(name=ADMIN_GROUP_NAME))
response = self.client.get(
reverse('publisher:publisher_courses_edit', kwargs={'pk': self.course.id})
)
self.assertEqual(response.status_code, 200)
def test_update_course_without_admin_rights(self):
""" Tests for update course with non staff user. """
course_dict = model_to_dict(self.course) course_dict = model_to_dict(self.course)
course_dict.pop('verification_deadline') course_dict.pop('verification_deadline')
course_dict.pop('image') course_dict.pop('image')
...@@ -217,7 +216,7 @@ class CreateUpdateCourseViewTests(TestCase): ...@@ -217,7 +216,7 @@ class CreateUpdateCourseViewTests(TestCase):
# verify that non staff user can't update course without permission # verify that non staff user can't update course without permission
self.assertEqual(response.status_code, 403) self.assertEqual(response.status_code, 403)
self.user.groups.add(Group.objects.get(name=ADMIN_GROUP_NAME)) user.groups.add(Group.objects.get(name=ADMIN_GROUP_NAME))
response = self.client.post( response = self.client.post(
reverse('publisher:publisher_courses_edit', kwargs={'pk': self.course.id}), reverse('publisher:publisher_courses_edit', kwargs={'pk': self.course.id}),
course_dict course_dict
...@@ -234,7 +233,7 @@ class CreateUpdateCourseViewTests(TestCase): ...@@ -234,7 +233,7 @@ class CreateUpdateCourseViewTests(TestCase):
self.assertEqual(course.title, updated_course_title) self.assertEqual(course.title, updated_course_title)
# add new and check the comment on edit page. # add new and check the comment on edit page.
comment = CommentFactory(content_object=self.course, user=self.user, site=self.site) comment = CommentFactory(content_object=self.course, user=user, site=self.site)
response = self.client.get(reverse('publisher:publisher_courses_edit', kwargs={'pk': self.course.id})) response = self.client.get(reverse('publisher:publisher_courses_edit', kwargs={'pk': self.course.id}))
self.assertContains(response, 'Total Comments 1') self.assertContains(response, 'Total Comments 1')
self.assertContains(response, 'Add new comment') self.assertContains(response, 'Add new comment')
......
...@@ -64,3 +64,15 @@ def is_partner_coordinator_user(user): ...@@ -64,3 +64,15 @@ def is_partner_coordinator_user(user):
bool: True, if user is an PC user; otherwise, False. bool: True, if user is an PC user; otherwise, False.
""" """
return user.groups.filter(name=PARTNER_COORDINATOR_GROUP_NAME).exists() return user.groups.filter(name=PARTNER_COORDINATOR_GROUP_NAME).exists()
def is_publisher_user(user):
""" Returns True if the user is part of any group.
Arguments:
user (:obj:`User`): User whose permissions should be checked.
Returns:
bool: True, if user is an publisher user; otherwise, False.
"""
return user.groups.exists()
...@@ -149,7 +149,7 @@ class CourseRunDetailView(mixins.LoginRequiredMixin, mixins.ViewPermissionMixin, ...@@ -149,7 +149,7 @@ class CourseRunDetailView(mixins.LoginRequiredMixin, mixins.ViewPermissionMixin,
# pylint: disable=attribute-defined-outside-init # pylint: disable=attribute-defined-outside-init
class CreateCourseView(mixins.LoginRequiredMixin, CreateView): class CreateCourseView(mixins.LoginRequiredMixin, mixins.PublisherUserRequiredMixin, CreateView):
""" Create Course View.""" """ Create Course View."""
model = Course model = Course
course_form = CustomCourseForm course_form = CustomCourseForm
...@@ -239,7 +239,7 @@ class CreateCourseView(mixins.LoginRequiredMixin, CreateView): ...@@ -239,7 +239,7 @@ class CreateCourseView(mixins.LoginRequiredMixin, CreateView):
return render(request, self.template_name, ctx, status=400) return render(request, self.template_name, ctx, status=400)
class UpdateCourseView(mixins.LoginRequiredMixin, mixins.ViewPermissionMixin, mixins.FormValidMixin, UpdateView): class UpdateCourseView(mixins.ViewPermissionMixin, mixins.FormValidMixin, UpdateView):
""" Update Course View.""" """ Update Course View."""
model = Course model = Course
form_class = CourseForm form_class = CourseForm
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment