diff --git a/entries/from_url.py b/entries/from_url.py new file mode 100644 index 0000000..b19475f --- /dev/null +++ b/entries/from_url.py @@ -0,0 +1,35 @@ +from urllib.parse import urlparse + +from django.contrib.sites.models import Site +from django.http import HttpResponse +from django.urls import resolve, Resolver404 +from micropub.views import error +from lemoncurry.middleware import ResponseException + +from .models import Entry + + +def from_url(url: str) -> Entry: + domain = Site.objects.get_current().domain + if not url: + raise ResponseException(error.bad_req('url parameter required')) + if '//' not in url: + url = '//' + url + parts = urlparse(url, scheme='https') + if parts.scheme not in ('http', 'https') or parts.netloc != domain: + raise ResponseException(error.bad_req('url does not point to this site')) + + try: + match = resolve(parts.path) + except Resolver404: + raise ResponseException(error.bad_req('url does not point to a valid page on this site')) + + if match.view_name != 'entries:entry': + raise ResponseException(error.bad_req('url does not point to an entry on this site')) + + try: + entry = Entry.objects.get(pk=match.kwargs['id']) + except Entry.DoesNotExist: + raise ResponseException(error.bad_req('url does not point to an existing entry')) + + return entry diff --git a/lemoncurry/middleware.py b/lemoncurry/middleware.py new file mode 100644 index 0000000..02221bc --- /dev/null +++ b/lemoncurry/middleware.py @@ -0,0 +1,14 @@ +from django.http import HttpRequest, HttpResponse +from django.utils.deprecation import MiddlewareMixin + + +class ResponseException(Exception): + def __init__(self, response: HttpResponse) -> None: + self.response = response + + +class ResponseExceptionMiddleware(MiddlewareMixin): + def process_exception(self, request: HttpRequest, exception: Exception) -> HttpResponse: + if isinstance(exception, ResponseException): + return exception.response + raise exception diff --git a/lemoncurry/settings/base.py b/lemoncurry/settings/base.py index 38ccee7..efc25ae 100644 --- a/lemoncurry/settings/base.py +++ b/lemoncurry/settings/base.py @@ -118,6 +118,7 @@ MIDDLEWARE = [ 'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.sites.middleware.CurrentSiteMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'lemoncurry.middleware.ResponseExceptionMiddleware', ] ROOT_URLCONF = 'lemoncurry.urls' diff --git a/micropub/views/delete.py b/micropub/views/delete.py index 5daa696..4fc1f3e 100644 --- a/micropub/views/delete.py +++ b/micropub/views/delete.py @@ -1,10 +1,8 @@ from django.http import HttpResponse -from django.urls import resolve, Resolver404 -from urllib.parse import urlparse from ronkyuu import webmention +from entries.from_url import from_url from entries.jobs import ping_hub, send_mentions -from entries.models import Entry from . import error @@ -18,27 +16,7 @@ def delete(request): if request.content_type not in normalise: return error.unsupported_type(request.content_type) url = normalise[request.content_type](request) - if not url: - return error.bad_req('url parameter required') - - if '//' not in url: - url = '//' + url - url = urlparse(url, scheme='https') - - if url.scheme not in ('http', 'https') or url.netloc != request.site.domain: - return error.bad_req('url does not point to this site') - try: - match = resolve(url.path) - except Resolver404: - return error.bad_req('url does not point to a valid page on this site') - - if match.view_name != 'entries:entry': - return error.bad_req('url does not point to an entry on this site') - - try: - entry = Entry.objects.get(pk=match.kwargs['id']) - except Entry.DoesNotExist: - return error.bad_req('url does not point to an existing entry') + entry = from_url(url) if entry.author != request.token.user: return error.forbid('entry belongs to another user')