from base64 import b64encode from captcha.audio import AudioCaptcha from captcha.image import ImageCaptcha from rest_framework import serializers from api import settings from desecapi.models import Captcha class CaptchaSerializer(serializers.ModelSerializer): challenge = serializers.SerializerMethodField() class Meta: model = Captcha fields = ( ("id", "challenge", "kind") if not settings.DEBUG else ("id", "challenge", "kind", "content") ) def get_challenge(self, obj: Captcha): # TODO Does this need to be stored in the object instance, in case this method gets called twice? if obj.kind == Captcha.Kind.IMAGE: challenge = ImageCaptcha().generate(obj.content).getvalue() elif obj.kind == Captcha.Kind.AUDIO: challenge = AudioCaptcha().generate(obj.content) else: raise ValueError(f"Unknown captcha type {obj.kind}") return b64encode(challenge) class CaptchaSolutionSerializer(serializers.Serializer): id = serializers.PrimaryKeyRelatedField( queryset=Captcha.objects.all(), error_messages={"does_not_exist": "CAPTCHA does not exist."}, ) solution = serializers.CharField(write_only=True, required=True) def validate(self, attrs): captcha = attrs["id"] # Note that this already is the Captcha object if not captcha.verify(attrs["solution"]): raise serializers.ValidationError( "CAPTCHA could not be validated. Please obtain a new one and try again." ) return attrs