Commit 3a005c49 by Clinton Blackburn Committed by Clinton Blackburn

Disabled anonymous access for tabs that require enrollment

parent f8269175
...@@ -60,7 +60,8 @@ def tabs_handler(request, course_key_string): ...@@ -60,7 +60,8 @@ def tabs_handler(request, course_key_string):
# present in the same order they are displayed in LMS # present in the same order they are displayed in LMS
tabs_to_render = [] tabs_to_render = []
for tab in CourseTabList.iterate_displayable(course_item, inline_collections=False): for tab in CourseTabList.iterate_displayable(course_item, user=request.user, inline_collections=False,
include_hidden=True):
if isinstance(tab, StaticTab): if isinstance(tab, StaticTab):
# static tab needs its locator information to render itself as an xmodule # static tab needs its locator information to render itself as an xmodule
static_tab_loc = course_key.make_usage_key('static_tab', tab.url_slug) static_tab_loc = course_key.make_usage_key('static_tab', tab.url_slug)
......
...@@ -442,13 +442,13 @@ class CourseTabList(List): ...@@ -442,13 +442,13 @@ class CourseTabList(List):
return next((tab for tab in tab_list if tab.tab_id == tab_id), None) return next((tab for tab in tab_list if tab.tab_id == tab_id), None)
@staticmethod @staticmethod
def iterate_displayable(course, user=None, inline_collections=True): def iterate_displayable(course, user=None, inline_collections=True, include_hidden=False):
""" """
Generator method for iterating through all tabs that can be displayed for the given course and Generator method for iterating through all tabs that can be displayed for the given course and
the given user with the provided access settings. the given user with the provided access settings.
""" """
for tab in course.tabs: for tab in course.tabs:
if tab.is_enabled(course, user=user) and not (user and tab.is_hidden): if tab.is_enabled(course, user=user) and (include_hidden or not (user and tab.is_hidden)):
if tab.is_collection: if tab.is_collection:
# If rendering inline that add each item in the collection, # If rendering inline that add each item in the collection,
# else just show the tab itself as long as it is not empty. # else just show the tab itself as long as it is not empty.
......
...@@ -19,9 +19,8 @@ class EnrolledTab(CourseTab): ...@@ -19,9 +19,8 @@ class EnrolledTab(CourseTab):
""" """
@classmethod @classmethod
def is_enabled(cls, course, user=None): def is_enabled(cls, course, user=None):
if user is None: return user and user.is_authenticated() and \
return True bool(CourseEnrollment.is_enrolled(user, course.id) or has_access(user, 'staff', course, course.id))
return bool(CourseEnrollment.is_enrolled(user, course.id) or has_access(user, 'staff', course, course.id))
class CoursewareTab(EnrolledTab): class CoursewareTab(EnrolledTab):
......
...@@ -7,7 +7,6 @@ from django.http import Http404 ...@@ -7,7 +7,6 @@ from django.http import Http404
from milestones.tests.utils import MilestonesTestCaseMixin from milestones.tests.utils import MilestonesTestCaseMixin
from mock import MagicMock, Mock, patch from mock import MagicMock, Mock, patch
from nose.plugins.attrib import attr from nose.plugins.attrib import attr
from waffle.testutils import override_flag
from courseware.courses import get_course_by_id from courseware.courses import get_course_by_id
from courseware.tabs import ( from courseware.tabs import (
...@@ -651,11 +650,10 @@ class CourseTabListTestCase(TabListTestCase): ...@@ -651,11 +650,10 @@ class CourseTabListTestCase(TabListTestCase):
self.course.tabs = self.all_valid_tab_list self.course.tabs = self.all_valid_tab_list
# enumerate the tabs with no user # enumerate the tabs with no user
for i, tab in enumerate(xmodule_tabs.CourseTabList.iterate_displayable( expected = [tab.type for tab in
self.course, xmodule_tabs.CourseTabList.iterate_displayable(self.course, inline_collections=False)]
inline_collections=False actual = [tab.type for tab in self.course.tabs if tab.is_enabled(self.course, user=None)]
)): assert actual == expected
self.assertEquals(tab.type, self.course.tabs[i].type)
# enumerate the tabs with a staff user # enumerate the tabs with a staff user
user = UserFactory(is_staff=True) user = UserFactory(is_staff=True)
......
...@@ -30,10 +30,10 @@ def edxnotes(cls): ...@@ -30,10 +30,10 @@ def edxnotes(cls):
# - the feature flag or `edxnotes` setting of the course is set to False # - the feature flag or `edxnotes` setting of the course is set to False
# - the user is not authenticated # - the user is not authenticated
user = self.runtime.get_real_user(self.runtime.anonymous_student_id) user = self.runtime.get_real_user(self.runtime.anonymous_student_id)
if is_studio or not is_feature_enabled(course) or not user.is_authenticated():
if is_studio or not is_feature_enabled(course, user):
return original_get_html(self, *args, **kwargs) return original_get_html(self, *args, **kwargs)
else: else:
return render_to_string("edxnotes_wrapper.html", { return render_to_string("edxnotes_wrapper.html", {
"content": original_get_html(self, *args, **kwargs), "content": original_get_html(self, *args, **kwargs),
"uid": generate_uid(), "uid": generate_uid(),
......
...@@ -423,8 +423,8 @@ def generate_uid(): ...@@ -423,8 +423,8 @@ def generate_uid():
return uuid4().int # pylint: disable=no-member return uuid4().int # pylint: disable=no-member
def is_feature_enabled(course): def is_feature_enabled(course, user):
""" """
Returns True if Student Notes feature is enabled for the course, False otherwise. Returns True if Student Notes feature is enabled for the course, False otherwise.
""" """
return EdxNotesTab.is_enabled(course) return EdxNotesTab.is_enabled(course, user)
...@@ -22,7 +22,6 @@ class EdxNotesTab(EnrolledTab): ...@@ -22,7 +22,6 @@ class EdxNotesTab(EnrolledTab):
Args: Args:
course (CourseDescriptor): the course using the feature course (CourseDescriptor): the course using the feature
settings (dict): a dict of configuration settings
user (User): the user interacting with the course user (User): the user interacting with the course
""" """
if not super(EdxNotesTab, cls).is_enabled(course, user=user): if not super(EdxNotesTab, cls).is_enabled(course, user=user):
......
...@@ -9,6 +9,7 @@ from unittest import skipUnless ...@@ -9,6 +9,7 @@ from unittest import skipUnless
import ddt import ddt
import jwt import jwt
import pytest
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import AnonymousUser from django.contrib.auth.models import AnonymousUser
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
...@@ -76,8 +77,8 @@ class TestProblem(object): ...@@ -76,8 +77,8 @@ class TestProblem(object):
def __init__(self, course, user=None): def __init__(self, course, user=None):
self.system = MagicMock(is_author_mode=False) self.system = MagicMock(is_author_mode=False)
self.scope_ids = MagicMock(usage_id="test_usage_id") self.scope_ids = MagicMock(usage_id="test_usage_id")
self.user = user or UserFactory() user = user or UserFactory()
self.runtime = MagicMock(course_id=course.id, get_real_user=lambda anon_id: self.user) self.runtime = MagicMock(course_id=course.id, get_real_user=lambda __: user)
self.descriptor = MagicMock() self.descriptor = MagicMock()
self.descriptor.runtime.modulestore.get_course.return_value = course self.descriptor.runtime.modulestore.get_course.return_value = course
...@@ -104,7 +105,7 @@ class EdxNotesDecoratorTest(ModuleStoreTestCase): ...@@ -104,7 +105,7 @@ class EdxNotesDecoratorTest(ModuleStoreTestCase):
self.course = CourseFactory(edxnotes=True, default_store=ModuleStoreEnum.Type.mongo) self.course = CourseFactory(edxnotes=True, default_store=ModuleStoreEnum.Type.mongo)
self.user = UserFactory() self.user = UserFactory()
self.client.login(username=self.user.username, password=UserFactory._DEFAULT_PASSWORD) self.client.login(username=self.user.username, password=UserFactory._DEFAULT_PASSWORD)
self.problem = TestProblem(self.course) self.problem = TestProblem(self.course, self.user)
@patch.dict("django.conf.settings.FEATURES", {'ENABLE_EDXNOTES': True}) @patch.dict("django.conf.settings.FEATURES", {'ENABLE_EDXNOTES': True})
@patch("edxnotes.helpers.get_public_endpoint", autospec=True) @patch("edxnotes.helpers.get_public_endpoint", autospec=True)
...@@ -116,18 +117,23 @@ class EdxNotesDecoratorTest(ModuleStoreTestCase): ...@@ -116,18 +117,23 @@ class EdxNotesDecoratorTest(ModuleStoreTestCase):
Tests if get_html is wrapped when feature flag is on and edxnotes are Tests if get_html is wrapped when feature flag is on and edxnotes are
enabled for the course. enabled for the course.
""" """
course = CourseFactory(edxnotes=True)
enrollment = CourseEnrollmentFactory(course_id=course.id)
user = enrollment.user
problem = TestProblem(course, user)
mock_generate_uid.return_value = "uid" mock_generate_uid.return_value = "uid"
mock_get_id_token.return_value = "token" mock_get_id_token.return_value = "token"
mock_get_token_url.return_value = "/tokenUrl" mock_get_token_url.return_value = "/tokenUrl"
mock_get_endpoint.return_value = "/endpoint" mock_get_endpoint.return_value = "/endpoint"
enable_edxnotes_for_the_course(self.course, self.user.id) enable_edxnotes_for_the_course(course, user.id)
expected_context = { expected_context = {
"content": "original_get_html", "content": "original_get_html",
"uid": "uid", "uid": "uid",
"edxnotes_visibility": "true", "edxnotes_visibility": "true",
"params": { "params": {
"usageId": u"test_usage_id", "usageId": "test_usage_id",
"courseId": unicode(self.course.id).encode("utf-8"), "courseId": course.id,
"token": "token", "token": "token",
"tokenUrl": "/tokenUrl", "tokenUrl": "/tokenUrl",
"endpoint": "/endpoint", "endpoint": "/endpoint",
...@@ -136,7 +142,7 @@ class EdxNotesDecoratorTest(ModuleStoreTestCase): ...@@ -136,7 +142,7 @@ class EdxNotesDecoratorTest(ModuleStoreTestCase):
}, },
} }
self.assertEqual( self.assertEqual(
self.problem.get_html(), problem.get_html(),
render_to_string("edxnotes_wrapper.html", expected_context), render_to_string("edxnotes_wrapper.html", expected_context),
) )
...@@ -225,8 +231,8 @@ class EdxNotesHelpersTest(ModuleStoreTestCase): ...@@ -225,8 +231,8 @@ class EdxNotesHelpersTest(ModuleStoreTestCase):
self.child_vertical = self.store.get_item(self.child_vertical.location) self.child_vertical = self.store.get_item(self.child_vertical.location)
self.child_html_module = self.store.get_item(self.child_html_module.location) self.child_html_module = self.store.get_item(self.child_html_module.location)
self.user = UserFactory.create(username="Joe", email="joe@example.com", password="edx") self.user = UserFactory()
self.client.login(username=self.user.username, password="edx") self.client.login(username=self.user.username, password=UserFactory._DEFAULT_PASSWORD)
self.request = RequestFactory().request() self.request = RequestFactory().request()
self.request.user = self.user self.request.user = self.user
...@@ -246,29 +252,17 @@ class EdxNotesHelpersTest(ModuleStoreTestCase): ...@@ -246,29 +252,17 @@ class EdxNotesHelpersTest(ModuleStoreTestCase):
""" """
Tests that edxnotes are disabled when Harvard Annotation Tool is enabled. Tests that edxnotes are disabled when Harvard Annotation Tool is enabled.
""" """
self.course.advanced_modules = ["foo", "imageannotation", "boo"] self.course.advanced_modules = ['imageannotation', 'textannotation', 'videoannotation']
self.assertFalse(helpers.is_feature_enabled(self.course)) assert not helpers.is_feature_enabled(self.course, self.user)
self.course.advanced_modules = ["foo", "boo", "videoannotation"]
self.assertFalse(helpers.is_feature_enabled(self.course))
self.course.advanced_modules = ["textannotation", "foo", "boo"] @ddt.data(True, False)
self.assertFalse(helpers.is_feature_enabled(self.course)) def test_is_feature_enabled(self, enabled):
self.course.advanced_modules = ["textannotation", "videoannotation", "imageannotation"]
self.assertFalse(helpers.is_feature_enabled(self.course))
@ddt.unpack
@ddt.data(
{'_edxnotes': True},
{'_edxnotes': False}
)
def test_is_feature_enabled(self, _edxnotes):
""" """
Tests that is_feature_enabled shows correct behavior. Tests that is_feature_enabled shows correct behavior.
""" """
self.course.edxnotes = _edxnotes course = CourseFactory(edxnotes=enabled)
self.assertEqual(helpers.is_feature_enabled(self.course), _edxnotes) enrollment = CourseEnrollmentFactory(course_id=course.id)
assert helpers.is_feature_enabled(course, enrollment.user) == enabled
@ddt.data( @ddt.data(
helpers.get_public_endpoint, helpers.get_public_endpoint,
...@@ -947,10 +941,10 @@ class EdxNotesViewsTest(ModuleStoreTestCase): ...@@ -947,10 +941,10 @@ class EdxNotesViewsTest(ModuleStoreTestCase):
def setUp(self): def setUp(self):
ClientFactory(name="edx-notes") ClientFactory(name="edx-notes")
super(EdxNotesViewsTest, self).setUp() super(EdxNotesViewsTest, self).setUp()
self.course = CourseFactory.create(edxnotes=True) self.course = CourseFactory(edxnotes=True)
self.user = UserFactory.create(username="Bob", email="bob@example.com", password="edx") self.user = UserFactory()
CourseEnrollmentFactory.create(user=self.user, course_id=self.course.id) CourseEnrollmentFactory(user=self.user, course_id=self.course.id)
self.client.login(username=self.user.username, password="edx") self.client.login(username=self.user.username, password=UserFactory._DEFAULT_PASSWORD)
self.notes_page_url = reverse("edxnotes", args=[unicode(self.course.id)]) self.notes_page_url = reverse("edxnotes", args=[unicode(self.course.id)])
self.notes_url = reverse("notes", args=[unicode(self.course.id)]) self.notes_url = reverse("notes", args=[unicode(self.course.id)])
self.get_token_url = reverse("get_token", args=[unicode(self.course.id)]) self.get_token_url = reverse("get_token", args=[unicode(self.course.id)])
...@@ -1144,38 +1138,27 @@ class EdxNotesPluginTest(ModuleStoreTestCase): ...@@ -1144,38 +1138,27 @@ class EdxNotesPluginTest(ModuleStoreTestCase):
def setUp(self): def setUp(self):
super(EdxNotesPluginTest, self).setUp() super(EdxNotesPluginTest, self).setUp()
self.course = CourseFactory.create(edxnotes=True) self.course = CourseFactory.create(edxnotes=True)
self.user = UserFactory.create(username="ma", email="ma@ma.info", password="edx") self.user = UserFactory()
CourseEnrollmentFactory.create(user=self.user, course_id=self.course.id) CourseEnrollmentFactory.create(user=self.user, course_id=self.course.id)
def test_edxnotes_tab_with_unauthorized_user(self): def test_edxnotes_tab_with_unenrolled_user(self):
""" user = UserFactory()
Verify EdxNotesTab visibility when user is unauthroized. assert not EdxNotesTab.is_enabled(self.course, user=user)
"""
user = UserFactory.create(username="ma1", email="ma1@ma1.info", password="edx")
self.assertFalse(EdxNotesTab.is_enabled(self.course, user=user))
@ddt.unpack @ddt.data(True, False)
@ddt.data( def test_edxnotes_tab_with_feature_flag(self, enabled):
{'enable_edxnotes': False},
{'enable_edxnotes': True}
)
def test_edxnotes_tab_with_feature_flag(self, enable_edxnotes):
""" """
Verify EdxNotesTab visibility when ENABLE_EDXNOTES feature flag is enabled/disabled. Verify EdxNotesTab visibility when ENABLE_EDXNOTES feature flag is enabled/disabled.
""" """
FEATURES['ENABLE_EDXNOTES'] = enable_edxnotes FEATURES['ENABLE_EDXNOTES'] = enabled
with override_settings(FEATURES=FEATURES): with override_settings(FEATURES=FEATURES):
self.assertEqual(EdxNotesTab.is_enabled(self.course), enable_edxnotes) assert EdxNotesTab.is_enabled(self.course, self.user) == enabled
@ddt.unpack @ddt.data(True, False)
@ddt.data(
{'harvard_notes_enabled': False},
{'harvard_notes_enabled': True}
)
def test_edxnotes_tab_with_harvard_notes(self, harvard_notes_enabled): def test_edxnotes_tab_with_harvard_notes(self, harvard_notes_enabled):
""" """
Verify EdxNotesTab visibility when harvard notes feature is enabled/disabled. Verify EdxNotesTab visibility when harvard notes feature is enabled/disabled.
""" """
with patch("edxnotes.plugins.is_harvard_notes_enabled") as mock_harvard_notes_enabled: with patch("edxnotes.plugins.is_harvard_notes_enabled") as mock_harvard_notes_enabled:
mock_harvard_notes_enabled.return_value = harvard_notes_enabled mock_harvard_notes_enabled.return_value = harvard_notes_enabled
self.assertEqual(EdxNotesTab.is_enabled(self.course), not harvard_notes_enabled) assert EdxNotesTab.is_enabled(self.course, self.user) == (not harvard_notes_enabled)
...@@ -45,7 +45,7 @@ def edxnotes(request, course_id): ...@@ -45,7 +45,7 @@ def edxnotes(request, course_id):
course_key = CourseKey.from_string(course_id) course_key = CourseKey.from_string(course_id)
course = get_course_with_access(request.user, "load", course_key) course = get_course_with_access(request.user, "load", course_key)
if not is_feature_enabled(course): if not is_feature_enabled(course, request.user):
raise Http404 raise Http404
notes_info = get_notes(request, course) notes_info = get_notes(request, course)
...@@ -149,7 +149,7 @@ def notes(request, course_id): ...@@ -149,7 +149,7 @@ def notes(request, course_id):
course_key = CourseKey.from_string(course_id) course_key = CourseKey.from_string(course_id)
course = get_course_with_access(request.user, 'load', course_key) course = get_course_with_access(request.user, 'load', course_key)
if not is_feature_enabled(course): if not is_feature_enabled(course, request.user):
raise Http404 raise Http404
page = request.GET.get('page') or DEFAULT_PAGE page = request.GET.get('page') or DEFAULT_PAGE
...@@ -191,7 +191,7 @@ def edxnotes_visibility(request, course_id): ...@@ -191,7 +191,7 @@ def edxnotes_visibility(request, course_id):
request.user, request, course, field_data_cache, course_key, course=course request.user, request, course, field_data_cache, course_key, course=course
) )
if not is_feature_enabled(course): if not is_feature_enabled(course, request.user):
raise Http404 raise Http404
try: try:
......
...@@ -36,7 +36,7 @@ ${static.get_page_title_breadcrumbs(course_name())} ...@@ -36,7 +36,7 @@ ${static.get_page_title_breadcrumbs(course_name())}
<%static:css group='style-course-vendor'/> <%static:css group='style-course-vendor'/>
<%static:css group='style-course'/> <%static:css group='style-course'/>
## Utility: Notes ## Utility: Notes
% if is_edxnotes_enabled(course): % if is_edxnotes_enabled(course, request.user):
<%static:css group='style-student-notes'/> <%static:css group='style-student-notes'/>
% endif % endif
...@@ -76,10 +76,10 @@ ${HTML(fragment.foot_html())} ...@@ -76,10 +76,10 @@ ${HTML(fragment.foot_html())}
</main> </main>
</section> </section>
</div> </div>
% if course.show_calculator or is_edxnotes_enabled(course): % if course.show_calculator or is_edxnotes_enabled(course, request.user):
<nav class="nav-utilities ${"has-utility-calculator" if course.show_calculator else ""}" aria-label="${_('Course Utilities')}"> <nav class="nav-utilities ${"has-utility-calculator" if course.show_calculator else ""}" aria-label="${_('Course Utilities')}">
## Utility: Notes ## Utility: Notes
% if is_edxnotes_enabled(course): % if is_edxnotes_enabled(course, request.user):
<%include file="/edxnotes/toggle_notes.html" args="course=course"/> <%include file="/edxnotes/toggle_notes.html" args="course=course"/>
% endif % endif
......
...@@ -51,7 +51,7 @@ from openedx.features.course_experience import course_home_page_title, COURSE_OU ...@@ -51,7 +51,7 @@ from openedx.features.course_experience import course_home_page_title, COURSE_OU
<%static:css group='style-course-vendor'/> <%static:css group='style-course-vendor'/>
<%static:css group='style-course'/> <%static:css group='style-course'/>
## Utility: Notes ## Utility: Notes
% if is_edxnotes_enabled(course): % if is_edxnotes_enabled(course, request.user):
<%static:css group='style-student-notes'/> <%static:css group='style-student-notes'/>
% endif % endif
...@@ -250,10 +250,10 @@ ${HTML(fragment.foot_html())} ...@@ -250,10 +250,10 @@ ${HTML(fragment.foot_html())}
</div> </div>
% endif % endif
</div> </div>
% if course.show_calculator or is_edxnotes_enabled(course): % if course.show_calculator or is_edxnotes_enabled(course, request.user):
<nav class="nav-utilities ${"has-utility-calculator" if course.show_calculator else ""}" aria-label="${_('Course Utilities')}"> <nav class="nav-utilities ${"has-utility-calculator" if course.show_calculator else ""}" aria-label="${_('Course Utilities')}">
## Utility: Notes ## Utility: Notes
% if is_edxnotes_enabled(course): % if is_edxnotes_enabled(course, request.user):
<%include file="/edxnotes/toggle_notes.html" args="course=course"/> <%include file="/edxnotes/toggle_notes.html" args="course=course"/>
% endif % endif
......
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