serializers.py 3.91 KB
Newer Older
1 2
""" Credit API Serializers """

Clinton Blackburn committed
3
from __future__ import unicode_literals
4

Clinton Blackburn committed
5 6
import datetime
import logging
7

Clinton Blackburn committed
8
import pytz
9
from django.conf import settings
Clinton Blackburn committed
10 11 12
from rest_framework import serializers
from rest_framework.exceptions import PermissionDenied

13
from openedx.core.djangoapps.credit.models import CreditCourse, CreditEligibility, CreditProvider, CreditRequest
Clinton Blackburn committed
14
from openedx.core.djangoapps.credit.signature import get_shared_secret_key, signature
15
from openedx.core.lib.api.serializers import CourseKeyField
Clinton Blackburn committed
16 17 18
from util.date_utils import from_timestamp

log = logging.getLogger(__name__)
19 20 21 22 23


class CreditCourseSerializer(serializers.ModelSerializer):
    """ CreditCourse Serializer """

24 25
    course_key = CourseKeyField()

26
    class Meta(object):
27 28
        model = CreditCourse
        exclude = ('id',)
Clinton Blackburn committed
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106


class CreditProviderSerializer(serializers.ModelSerializer):
    """ CreditProvider """
    id = serializers.CharField(source='provider_id')  # pylint:disable=invalid-name
    description = serializers.CharField(source='provider_description')
    status_url = serializers.URLField(source='provider_status_url')
    url = serializers.URLField(source='provider_url')

    class Meta(object):
        model = CreditProvider
        fields = ('id', 'display_name', 'url', 'status_url', 'description', 'enable_integration',
                  'fulfillment_instructions', 'thumbnail_url',)


class CreditEligibilitySerializer(serializers.ModelSerializer):
    """ CreditEligibility serializer. """
    course_key = serializers.SerializerMethodField()

    def get_course_key(self, obj):
        """ Returns the course key associated with the course. """
        return unicode(obj.course.course_key)

    class Meta(object):
        model = CreditEligibility
        fields = ('username', 'course_key', 'deadline',)


class CreditProviderCallbackSerializer(serializers.Serializer):  # pylint:disable=abstract-method
    """
    Serializer for input to the CreditProviderCallback view.

    This is used solely for validating the input.
    """
    request_uuid = serializers.CharField(required=True)
    status = serializers.ChoiceField(required=True, choices=CreditRequest.REQUEST_STATUS_CHOICES)
    timestamp = serializers.IntegerField(required=True)
    signature = serializers.CharField(required=True)

    def __init__(self, **kwargs):
        self.provider = kwargs.pop('provider', None)
        super(CreditProviderCallbackSerializer, self).__init__(**kwargs)

    def validate_timestamp(self, value):
        """ Ensure the request has been received in a timely manner. """
        date_time = from_timestamp(value)

        # Ensure we converted the timestamp to a datetime
        if not date_time:
            msg = '[{}] is not a valid timestamp'.format(value)
            log.warning(msg)
            raise serializers.ValidationError(msg)

        elapsed = (datetime.datetime.now(pytz.UTC) - date_time).total_seconds()
        if elapsed > settings.CREDIT_PROVIDER_TIMESTAMP_EXPIRATION:
            msg = '[{value}] is too far in the past (over [{elapsed}] seconds).'.format(value=value, elapsed=elapsed)
            log.warning(msg)
            raise serializers.ValidationError(msg)

        return value

    def validate_signature(self, value):
        """ Validate the signature and ensure the provider is setup properly. """
        provider_id = self.provider.provider_id
        secret_key = get_shared_secret_key(provider_id)
        if secret_key is None:
            msg = 'Could not retrieve secret key for credit provider [{}]. ' \
                  'Unable to validate requests from provider.'.format(provider_id)
            log.error(msg)
            raise PermissionDenied(msg)

        data = self.initial_data
        actual_signature = data["signature"]
        if signature(data, secret_key) != actual_signature:
            msg = 'Request from credit provider [{}] had an invalid signature.'.format(provider_id)
            raise PermissionDenied(msg)

        return value