mfa.py 1.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
  1. from rest_framework import serializers
  2. from rest_framework.validators import UniqueTogetherValidator
  3. from desecapi.models import BaseFactor, TOTPFactor
  4. class TOTPFactorSerializer(serializers.ModelSerializer):
  5. user = serializers.HiddenField(default=serializers.CurrentUserDefault())
  6. class Meta:
  7. model = TOTPFactor
  8. fields = ("id", "created", "last_used", "name", "secret", "uri", "user")
  9. read_only_fields = ("id", "created", "last_used", "secret", "uri", "user")
  10. extra_kwargs = {
  11. # needed for uniqueness, https://github.com/encode/django-rest-framework/issues/7489
  12. "name": {"default": ""}
  13. }
  14. validators = [
  15. UniqueTogetherValidator(
  16. queryset=BaseFactor.objects.all(),
  17. fields=["user", "name"],
  18. message="An authentication factor with this name already exists.",
  19. )
  20. ]
  21. def __init__(self, *args, include_secret=False, **kwargs):
  22. self.include_secret = include_secret
  23. return super().__init__(*args, **kwargs)
  24. def get_fields(self):
  25. fields = super().get_fields()
  26. if not self.include_secret:
  27. fields.pop("secret")
  28. fields.pop("uri")
  29. return fields
  30. def to_representation(self, instance):
  31. ret = super().to_representation(instance)
  32. if "secret" in ret:
  33. ret["secret"] = instance.base32_secret
  34. return ret
  35. class TOTPCodeSerializer(serializers.Serializer):
  36. # length requirements preserve leading zeros
  37. code = serializers.RegexField("^[0-9]+$", max_length=6, min_length=6)
  38. class Meta:
  39. fields = ("code",)
  40. def validate_code(self, value):
  41. factor = self.context["view"].get_object()
  42. if not factor.verify(value):
  43. raise serializers.ValidationError("Invalid code.")
  44. return value