from django.contrib.auth.tokens import default_token_generator from django.contrib.contenttypes.models import ContentType from django.contrib.sites.models import Site from django.utils.encoding import force_bytes from django.utils.http import urlsafe_base64_encode from rest_framework.exceptions import PermissionDenied def encode_pk(resource): """Encode the primary key of a resource with Base64 for usage in URLs.""" return urlsafe_base64_encode(force_bytes(resource.pk)) class AlreadyConfirmed(Exception): """The given resource has already been confirmed.""" pass class Confirmation: """ Base class for handling a confirmation process. """ def __init__(self, user, resource): self.user = user self.resource = resource def check(self): if not self.has_permission(self.user, self.resource): raise PermissionDenied() if self.is_confirmed(self.resource): raise AlreadyConfirmed() self.confirm(self.resource) def confirm(self, resource): """Overwrite this method to supply operations to confirm the resource.""" pass def has_permission(self, user, resource) -> bool: """Overwrite this method returning if a user may confirm a resource.""" return False def is_confirmed(self, resource) -> bool: """Overwrite this method to tell if a resource is confirmed.""" return False def send_request(self): if self.is_confirmed(self.resource): return site = Site.objects.get_current() uid = encode_pk(self.user) token = default_token_generator.make_token(self.user) obj_type = ContentType.objects.get_for_model(self.resource) type_id = encode_pk(obj_type) obj_id = encode_pk(self.resource) confirmation_url = ( f"https://{site.domain}/confirm/{uid}/{token}/{type_id}/{obj_id}" ) self.user.email_user( f"{site.name}: Bestätigung der Anfrage", f"Bitte bestätige, dass du deine E-Mail-Adresse auf der Seite {site.name} ({site.domain}) eingegeben hast. " f"Kopiere dazu folgende URL in deinen Webbrowser:\n\n{confirmation_url}", )