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
- )
|