1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253 |
- from __future__ import annotations
- import secrets
- import string
- import uuid
- from django.conf import settings
- from django.db import models
- from django.utils import timezone
- from django_prometheus.models import ExportModelOperationsMixin
- from desecapi import metrics
- def captcha_default_content(kind: str) -> str:
- if kind == Captcha.Kind.IMAGE:
- alphabet = (string.ascii_uppercase + string.digits).translate(
- {ord(c): None for c in "IO0"}
- )
- length = 5
- elif kind == Captcha.Kind.AUDIO:
- alphabet = string.digits
- length = 8
- else:
- raise ValueError(f"Unknown Captcha kind: {kind}")
- content = "".join([secrets.choice(alphabet) for _ in range(length)])
- metrics.get("desecapi_captcha_content_created").labels(kind).inc()
- return content
- class Captcha(ExportModelOperationsMixin("Captcha"), models.Model):
- class Kind(models.TextChoices):
- IMAGE = "image"
- AUDIO = "audio"
- id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
- created = models.DateTimeField(auto_now_add=True)
- content = models.CharField(max_length=24, default="")
- kind = models.CharField(choices=Kind.choices, default=Kind.IMAGE, max_length=24)
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- if not self.content:
- self.content = captcha_default_content(self.kind)
- def verify(self, solution: str):
- age = timezone.now() - self.created
- self.delete()
- return (
- str(solution).upper().strip() == self.content # solution correct
- and age <= settings.CAPTCHA_VALIDITY_PERIOD # not expired
- )
|