diff --git a/lms/djangoapps/certificates/apis/v0/tests/test_views.py b/lms/djangoapps/certificates/apis/v0/tests/test_views.py index 1a32dde024..49e787bb5a 100644 --- a/lms/djangoapps/certificates/apis/v0/tests/test_views.py +++ b/lms/djangoapps/certificates/apis/v0/tests/test_views.py @@ -1,7 +1,10 @@ """ Tests for the Certificate REST APIs. """ +from datetime import datetime, timedelta + from django.core.urlresolvers import reverse +from oauth2_provider import models as dot_models from rest_framework import status from rest_framework.test import APITestCase @@ -12,6 +15,8 @@ from student.tests.factories import UserFactory from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory +USER_PASSWORD = 'test' + class CertificatesRestApiTest(SharedModuleStoreTestCase, APITestCase): """ @@ -29,9 +34,9 @@ class CertificatesRestApiTest(SharedModuleStoreTestCase, APITestCase): def setUp(self): super(CertificatesRestApiTest, self).setUp() - self.student = UserFactory.create(password='test') - self.student_no_cert = UserFactory.create(password='test') - self.staff_user = UserFactory.create(password='test', is_staff=True) + self.student = UserFactory.create(password=USER_PASSWORD) + self.student_no_cert = UserFactory.create(password=USER_PASSWORD) + self.staff_user = UserFactory.create(password=USER_PASSWORD, is_staff=True) GeneratedCertificateFactory.create( user=self.student, @@ -44,6 +49,23 @@ class CertificatesRestApiTest(SharedModuleStoreTestCase, APITestCase): self.namespaced_url = 'certificates_api:v0:certificates:detail' + # create a configuration for django-oauth-toolkit (DOT) + dot_app_user = UserFactory.create(password=USER_PASSWORD) + dot_app = dot_models.Application.objects.create( + name='test app', + user=dot_app_user, + client_type='confidential', + authorization_grant_type='authorization-code', + redirect_uris='http://localhost:8079/complete/edxorg/' + ) + self.dot_access_token = dot_models.AccessToken.objects.create( + user=self.student, + application=dot_app, + expires=datetime.utcnow() + timedelta(weeks=1), + scope='read write', + token='16MGyP3OaQYHmpT1lK7Q6MMNAZsjwF' + ) + def get_url(self, username): """ Helper function to create the url for certificates @@ -56,6 +78,15 @@ class CertificatesRestApiTest(SharedModuleStoreTestCase, APITestCase): } ) + def assert_oauth_status(self, access_token, expected_status): + """ + Helper method for requests with OAUTH token + """ + self.client.logout() + auth_header = "Bearer {0}".format(access_token) + response = self.client.get(self.get_url(self.student.username), HTTP_AUTHORIZATION=auth_header) + self.assertEqual(response.status_code, expected_status) + def test_permissions(self): """ Test that only the owner of the certificate can access the url @@ -65,7 +96,7 @@ class CertificatesRestApiTest(SharedModuleStoreTestCase, APITestCase): self.assertEqual(resp.status_code, status.HTTP_401_UNAUTHORIZED) # another student - self.client.login(username=self.student_no_cert.username, password='test') + self.client.login(username=self.student_no_cert.username, password=USER_PASSWORD) resp = self.client.get(self.get_url(self.student.username)) # gets 404 instead of 403 for security reasons self.assertEqual(resp.status_code, status.HTTP_404_NOT_FOUND) @@ -73,21 +104,57 @@ class CertificatesRestApiTest(SharedModuleStoreTestCase, APITestCase): self.client.logout() # same student of the certificate - self.client.login(username=self.student.username, password='test') + self.client.login(username=self.student.username, password=USER_PASSWORD) resp = self.client.get(self.get_url(self.student.username)) self.assertEqual(resp.status_code, status.HTTP_200_OK) self.client.logout() # staff user - self.client.login(username=self.staff_user.username, password='test') + self.client.login(username=self.staff_user.username, password=USER_PASSWORD) resp = self.client.get(self.get_url(self.student.username)) self.assertEqual(resp.status_code, status.HTTP_200_OK) + def test_inactive_user_access(self): + """ + Verify inactive users - those who have not verified their email addresses - + are allowed to access the endpoint. + """ + self.client.login(username=self.student.username, password=USER_PASSWORD) + + self.student.is_active = False + self.student.save() + + resp = self.client.get(self.get_url(self.student.username)) + self.assertEqual(resp.status_code, status.HTTP_200_OK) + + def test_dot_valid_accesstoken(self): + """ + Verify access with a valid Django Oauth Toolkit access token. + """ + self.assert_oauth_status(self.dot_access_token, status.HTTP_200_OK) + + def test_dot_invalid_accesstoken(self): + """ + Verify the endpoint is inaccessible for authorization + attempts made with an invalid OAuth access token. + """ + self.assert_oauth_status("fooooooooooToken", status.HTTP_401_UNAUTHORIZED) + + def test_dot_expired_accesstoken(self): + """ + Verify the endpoint is inaccessible for authorization + attempts made with an expired OAuth access token. + """ + # set the expiration date in the past + self.dot_access_token.expires = datetime.utcnow() - timedelta(weeks=1) + self.dot_access_token.save() + self.assert_oauth_status(self.dot_access_token, status.HTTP_401_UNAUTHORIZED) + def test_no_certificate_for_user(self): """ Test for case with no certificate available """ - self.client.login(username=self.student_no_cert.username, password='test') + self.client.login(username=self.student_no_cert.username, password=USER_PASSWORD) resp = self.client.get(self.get_url(self.student_no_cert.username)) self.assertEqual(resp.status_code, status.HTTP_404_NOT_FOUND) self.assertIn('error_code', resp.data) # pylint: disable=no-member @@ -100,7 +167,7 @@ class CertificatesRestApiTest(SharedModuleStoreTestCase, APITestCase): """ Tests case user that pulls her own certificate """ - self.client.login(username=self.student.username, password='test') + self.client.login(username=self.student.username, password=USER_PASSWORD) resp = self.client.get(self.get_url(self.student.username)) self.assertEqual(resp.status_code, status.HTTP_200_OK) self.assertEqual( diff --git a/lms/djangoapps/certificates/apis/v0/views.py b/lms/djangoapps/certificates/apis/v0/views.py index 22644e6a82..abf8bbc052 100644 --- a/lms/djangoapps/certificates/apis/v0/views.py +++ b/lms/djangoapps/certificates/apis/v0/views.py @@ -3,14 +3,16 @@ import logging from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import CourseKey -from rest_framework.authentication import SessionAuthentication from rest_framework.generics import GenericAPIView from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response -from rest_framework_oauth.authentication import OAuth2Authentication from lms.djangoapps.certificates.api import get_certificate_for_user -from openedx.core.lib.api import permissions +from openedx.core.lib.api import ( + authentication, + permissions, +) + log = logging.getLogger(__name__) @@ -64,8 +66,14 @@ class CertificatesDetailView(GenericAPIView): } """ - authentication_classes = (OAuth2Authentication, SessionAuthentication,) - permission_classes = (IsAuthenticated, permissions.IsUserInUrlOrStaff) + authentication_classes = ( + authentication.OAuth2AuthenticationAllowInactiveUser, + authentication.SessionAuthenticationAllowInactiveUser, + ) + permission_classes = ( + IsAuthenticated, + permissions.IsUserInUrlOrStaff + ) def get(self, request, username, course_id): """