Commit 12e26506 by Clinton Blackburn Committed by Vedran Karacic

Added photo verification status API endpoint

ECOM-2470 & SOL-2126
parent 47a22b23
...@@ -2,8 +2,10 @@ ...@@ -2,8 +2,10 @@
Django REST Framework serializers for the User API application Django REST Framework serializers for the User API application
""" """
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.utils.timezone import now
from rest_framework import serializers from rest_framework import serializers
from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification
from student.models import UserProfile from student.models import UserProfile
from .models import UserPreference from .models import UserPreference
...@@ -90,3 +92,20 @@ class CountryTimeZoneSerializer(serializers.Serializer): # pylint: disable=abst ...@@ -90,3 +92,20 @@ class CountryTimeZoneSerializer(serializers.Serializer): # pylint: disable=abst
""" """
time_zone = serializers.CharField() time_zone = serializers.CharField()
description = serializers.CharField() description = serializers.CharField()
class SoftwareSecurePhotoVerificationSerializer(serializers.ModelSerializer):
"""
Serializer that generates a representation of a user's photo verification status.
"""
is_verified = serializers.SerializerMethodField()
def get_is_verified(self, obj):
"""
Return a boolean indicating if a the user is verified.
"""
return obj.status == 'approved' and obj.expiration_datetime > now()
class Meta(object):
fields = ('status', 'expiration_datetime', 'is_verified')
model = SoftwareSecurePhotoVerification
...@@ -8,6 +8,7 @@ from django.conf.urls import patterns, url ...@@ -8,6 +8,7 @@ from django.conf.urls import patterns, url
from ..profile_images.views import ProfileImageView from ..profile_images.views import ProfileImageView
from .accounts.views import AccountViewSet from .accounts.views import AccountViewSet
from .preferences.views import PreferencesView, PreferencesDetailView from .preferences.views import PreferencesView, PreferencesDetailView
from .verification_api.views import PhotoVerificationStatusView
ACCOUNT_LIST = AccountViewSet.as_view({ ACCOUNT_LIST = AccountViewSet.as_view({
...@@ -26,16 +27,21 @@ urlpatterns = patterns( ...@@ -26,16 +27,21 @@ urlpatterns = patterns(
url( url(
r'^v1/accounts/{}/image$'.format(settings.USERNAME_PATTERN), r'^v1/accounts/{}/image$'.format(settings.USERNAME_PATTERN),
ProfileImageView.as_view(), ProfileImageView.as_view(),
name="accounts_profile_image_api" name='accounts_profile_image_api'
),
url(
r'^v1/accounts/{}/verification_status/$'.format(settings.USERNAME_PATTERN),
PhotoVerificationStatusView.as_view(),
name='verification_status'
), ),
url( url(
r'^v1/preferences/{}$'.format(settings.USERNAME_PATTERN), r'^v1/preferences/{}$'.format(settings.USERNAME_PATTERN),
PreferencesView.as_view(), PreferencesView.as_view(),
name="preferences_api" name='preferences_api'
), ),
url( url(
r'^v1/preferences/{}/(?P<preference_key>[a-zA-Z0-9_]+)$'.format(settings.USERNAME_PATTERN), r'^v1/preferences/{}/(?P<preference_key>[a-zA-Z0-9_]+)$'.format(settings.USERNAME_PATTERN),
PreferencesDetailView.as_view(), PreferencesDetailView.as_view(),
name="preferences_detail_api" name='preferences_detail_api'
), ),
) )
""" Tests for API endpoints. """
from __future__ import unicode_literals
import datetime
import freezegun
import json
from django.conf import settings
from django.core.urlresolvers import reverse
from django.test import TestCase
from django.test.utils import override_settings
from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification
from student.tests.factories import UserFactory
FROZEN_TIME = '2015-01-01'
VERIFY_STUDENT = {'DAYS_GOOD_FOR': 365}
@freezegun.freeze_time(FROZEN_TIME)
@override_settings(VERIFY_STUDENT=VERIFY_STUDENT)
class PhotoVerificationStatusViewTests(TestCase):
""" Tests for the PhotoVerificationStatusView endpoint. """
CREATED_AT = datetime.datetime.strptime(FROZEN_TIME, '%Y-%m-%d')
PASSWORD = 'test'
def setUp(self):
super(PhotoVerificationStatusViewTests, self).setUp()
self.user = UserFactory.create(password=self.PASSWORD)
self.staff = UserFactory.create(is_staff=True, password=self.PASSWORD)
self.verification = SoftwareSecurePhotoVerification.objects.create(user=self.user, status='submitted')
self.path = reverse('verification_status', kwargs={'username': self.user.username})
self.client.login(username=self.staff.username, password=self.PASSWORD)
def assert_path_not_found(self, path):
""" Assert the path returns HTTP 404. """
response = self.client.get(path)
self.assertEqual(response.status_code, 404)
def assert_verification_returned(self, verified=False):
""" Assert the path returns HTTP 200 and returns appropriately-serialized data. """
response = self.client.get(self.path)
self.assertEqual(response.status_code, 200)
expected_expires = self.CREATED_AT + datetime.timedelta(settings.VERIFY_STUDENT['DAYS_GOOD_FOR'])
expected = {
'status': self.verification.status,
'expiration_datetime': '{}Z'.format(expected_expires.isoformat()),
'is_verified': verified
}
self.assertEqual(json.loads(response.content), expected)
def test_non_existent_user(self):
""" The endpoint should return HTTP 404 if the user does not exist. """
path = reverse('verification_status', kwargs={'username': 'abc123'})
self.assert_path_not_found(path)
def test_no_verifications(self):
""" The endpoint should return HTTP 404 if the user has no verifications. """
user = UserFactory.create()
path = reverse('verification_status', kwargs={'username': user.username})
self.assert_path_not_found(path)
def test_authentication_required(self):
""" The endpoint should return HTTP 403 if the user is not authenticated. """
self.client.logout()
response = self.client.get(self.path)
self.assertEqual(response.status_code, 401)
def test_staff_user(self):
""" The endpoint should be accessible to staff users. """
self.client.login(username=self.staff.username, password=self.PASSWORD)
self.assert_verification_returned()
def test_owner(self):
""" The endpoint should be accessible to the user who submitted the verification request. """
self.client.login(username=self.user.username, password=self.user.password)
self.assert_verification_returned()
def test_non_owner_or_staff_user(self):
""" The endpoint should NOT be accessible if the request is not made by the submitter or staff user. """
user = UserFactory.create()
self.client.login(username=user.username, password=self.PASSWORD)
response = self.client.get(self.path)
self.assertEqual(response.status_code, 403)
def test_approved_verification(self):
""" The endpoint should return that the user is verified if the user's verification is accepted. """
self.verification.status = 'approved'
self.verification.save()
self.client.login(username=self.user.username, password=self.user.password)
self.assert_verification_returned(verified=True)
""" Verification API v1 views. """
from django.http import Http404
from edx_rest_framework_extensions.authentication import JwtAuthentication
from rest_framework.authentication import SessionAuthentication
from rest_framework.generics import RetrieveAPIView
from rest_framework_oauth.authentication import OAuth2Authentication
from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification
from openedx.core.djangoapps.user_api.serializers import SoftwareSecurePhotoVerificationSerializer
from openedx.core.lib.api.permissions import IsStaffOrOwner
class PhotoVerificationStatusView(RetrieveAPIView):
""" PhotoVerificationStatus detail endpoint. """
authentication_classes = (JwtAuthentication, OAuth2Authentication, SessionAuthentication,)
permission_classes = (IsStaffOrOwner,)
serializer_class = SoftwareSecurePhotoVerificationSerializer
def get_object(self):
username = self.kwargs['username']
verifications = SoftwareSecurePhotoVerification.objects.filter(user__username=username).order_by('-updated_at')
if len(verifications) > 0:
verification = verifications[0]
self.check_object_permissions(self.request, verification)
return verification
raise Http404
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