Authentication and Authorization for Cert Rest APIs fixed

parent 18cddf6e
""" """
Tests for the Certificate REST APIs. Tests for the Certificate REST APIs.
""" """
from datetime import datetime, timedelta
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from oauth2_provider import models as dot_models
from rest_framework import status from rest_framework import status
from rest_framework.test import APITestCase from rest_framework.test import APITestCase
...@@ -12,6 +15,8 @@ from student.tests.factories import UserFactory ...@@ -12,6 +15,8 @@ from student.tests.factories import UserFactory
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory from xmodule.modulestore.tests.factories import CourseFactory
USER_PASSWORD = 'test'
class CertificatesRestApiTest(SharedModuleStoreTestCase, APITestCase): class CertificatesRestApiTest(SharedModuleStoreTestCase, APITestCase):
""" """
...@@ -29,9 +34,9 @@ class CertificatesRestApiTest(SharedModuleStoreTestCase, APITestCase): ...@@ -29,9 +34,9 @@ class CertificatesRestApiTest(SharedModuleStoreTestCase, APITestCase):
def setUp(self): def setUp(self):
super(CertificatesRestApiTest, self).setUp() super(CertificatesRestApiTest, self).setUp()
self.student = UserFactory.create(password='test') self.student = UserFactory.create(password=USER_PASSWORD)
self.student_no_cert = UserFactory.create(password='test') self.student_no_cert = UserFactory.create(password=USER_PASSWORD)
self.staff_user = UserFactory.create(password='test', is_staff=True) self.staff_user = UserFactory.create(password=USER_PASSWORD, is_staff=True)
GeneratedCertificateFactory.create( GeneratedCertificateFactory.create(
user=self.student, user=self.student,
...@@ -44,6 +49,23 @@ class CertificatesRestApiTest(SharedModuleStoreTestCase, APITestCase): ...@@ -44,6 +49,23 @@ class CertificatesRestApiTest(SharedModuleStoreTestCase, APITestCase):
self.namespaced_url = 'certificates_api:v0:certificates:detail' 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): def get_url(self, username):
""" """
Helper function to create the url for certificates Helper function to create the url for certificates
...@@ -56,6 +78,15 @@ class CertificatesRestApiTest(SharedModuleStoreTestCase, APITestCase): ...@@ -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): def test_permissions(self):
""" """
Test that only the owner of the certificate can access the url Test that only the owner of the certificate can access the url
...@@ -65,7 +96,7 @@ class CertificatesRestApiTest(SharedModuleStoreTestCase, APITestCase): ...@@ -65,7 +96,7 @@ class CertificatesRestApiTest(SharedModuleStoreTestCase, APITestCase):
self.assertEqual(resp.status_code, status.HTTP_401_UNAUTHORIZED) self.assertEqual(resp.status_code, status.HTTP_401_UNAUTHORIZED)
# another student # 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)) resp = self.client.get(self.get_url(self.student.username))
# gets 404 instead of 403 for security reasons # gets 404 instead of 403 for security reasons
self.assertEqual(resp.status_code, status.HTTP_404_NOT_FOUND) self.assertEqual(resp.status_code, status.HTTP_404_NOT_FOUND)
...@@ -73,21 +104,57 @@ class CertificatesRestApiTest(SharedModuleStoreTestCase, APITestCase): ...@@ -73,21 +104,57 @@ class CertificatesRestApiTest(SharedModuleStoreTestCase, APITestCase):
self.client.logout() self.client.logout()
# same student of the certificate # 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)) resp = self.client.get(self.get_url(self.student.username))
self.assertEqual(resp.status_code, status.HTTP_200_OK) self.assertEqual(resp.status_code, status.HTTP_200_OK)
self.client.logout() self.client.logout()
# staff user # 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)) resp = self.client.get(self.get_url(self.student.username))
self.assertEqual(resp.status_code, status.HTTP_200_OK) 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): def test_no_certificate_for_user(self):
""" """
Test for case with no certificate available 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)) resp = self.client.get(self.get_url(self.student_no_cert.username))
self.assertEqual(resp.status_code, status.HTTP_404_NOT_FOUND) self.assertEqual(resp.status_code, status.HTTP_404_NOT_FOUND)
self.assertIn('error_code', resp.data) # pylint: disable=no-member self.assertIn('error_code', resp.data) # pylint: disable=no-member
...@@ -100,7 +167,7 @@ class CertificatesRestApiTest(SharedModuleStoreTestCase, APITestCase): ...@@ -100,7 +167,7 @@ class CertificatesRestApiTest(SharedModuleStoreTestCase, APITestCase):
""" """
Tests case user that pulls her own certificate 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)) resp = self.client.get(self.get_url(self.student.username))
self.assertEqual(resp.status_code, status.HTTP_200_OK) self.assertEqual(resp.status_code, status.HTTP_200_OK)
self.assertEqual( self.assertEqual(
......
...@@ -3,14 +3,16 @@ import logging ...@@ -3,14 +3,16 @@ import logging
from opaque_keys import InvalidKeyError from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.keys import CourseKey
from rest_framework.authentication import SessionAuthentication
from rest_framework.generics import GenericAPIView from rest_framework.generics import GenericAPIView
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework_oauth.authentication import OAuth2Authentication
from lms.djangoapps.certificates.api import get_certificate_for_user 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__) log = logging.getLogger(__name__)
...@@ -64,8 +66,14 @@ class CertificatesDetailView(GenericAPIView): ...@@ -64,8 +66,14 @@ class CertificatesDetailView(GenericAPIView):
} }
""" """
authentication_classes = (OAuth2Authentication, SessionAuthentication,) authentication_classes = (
permission_classes = (IsAuthenticated, permissions.IsUserInUrlOrStaff) authentication.OAuth2AuthenticationAllowInactiveUser,
authentication.SessionAuthenticationAllowInactiveUser,
)
permission_classes = (
IsAuthenticated,
permissions.IsUserInUrlOrStaff
)
def get(self, request, username, course_id): def get(self, request, username, course_id):
""" """
......
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