From d3bb19800c1e88014ea058b701e891f0e7c34a15 Mon Sep 17 00:00:00 2001 From: Tom Hacohen Date: Mon, 14 Sep 2020 19:17:45 +0300 Subject: [PATCH] Web: add support for the POST HTTP method. (#1097) * Web: add support for the POST HTTP method. This patch adds support for POST in addition to the already supported GET. This is needed for implementing more complex web modules that also support configuration modifications and advanced queries. * Base web: return METHOD_NOT_ALLOWED when method isn't implemenetd. Co-authored-by: Unrud --- radicale/app/__init__.py | 4 +++- radicale/app/post.py | 30 ++++++++++++++++++++++++++++++ radicale/tests/__init__.py | 5 +++++ radicale/tests/custom/web.py | 3 +++ radicale/tests/test_web.py | 3 +++ radicale/web/__init__.py | 16 ++++++++++++++-- 6 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 radicale/app/post.py diff --git a/radicale/app/__init__.py b/radicale/app/__init__.py index e138362..0f9ba95 100644 --- a/radicale/app/__init__.py +++ b/radicale/app/__init__.py @@ -49,6 +49,7 @@ from radicale.app.mkcalendar import ApplicationMkcalendarMixin from radicale.app.mkcol import ApplicationMkcolMixin from radicale.app.move import ApplicationMoveMixin from radicale.app.options import ApplicationOptionsMixin +from radicale.app.post import ApplicationPostMixin from radicale.app.propfind import ApplicationPropfindMixin from radicale.app.proppatch import ApplicationProppatchMixin from radicale.app.put import ApplicationPutMixin @@ -63,7 +64,8 @@ class Application( ApplicationMkcalendarMixin, ApplicationMkcolMixin, ApplicationMoveMixin, ApplicationOptionsMixin, ApplicationPropfindMixin, ApplicationProppatchMixin, - ApplicationPutMixin, ApplicationReportMixin): + ApplicationPostMixin, ApplicationPutMixin, + ApplicationReportMixin): """WSGI application.""" diff --git a/radicale/app/post.py b/radicale/app/post.py new file mode 100644 index 0000000..723b8e9 --- /dev/null +++ b/radicale/app/post.py @@ -0,0 +1,30 @@ +# This file is part of Radicale Server - Calendar Server +# Copyright © 2008 Nicolas Kandel +# Copyright © 2008 Pascal Halter +# Copyright © 2008-2017 Guillaume Ayoub +# Copyright © 2017-2018 Unrud +# Copyright © 2020 Tom Hacohen +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Radicale. If not, see . + +from radicale import httputils + + +class ApplicationPostMixin: + def do_POST(self, environ, base_prefix, path, user): + """Manage POST request.""" + if path == "/.web" or path.startswith("/.web/"): + return self._web.post(environ, base_prefix, path, user) + + return httputils.METHOD_NOT_ALLOWED diff --git a/radicale/tests/__init__.py b/radicale/tests/__init__.py index 1e18615..1824bd1 100644 --- a/radicale/tests/__init__.py +++ b/radicale/tests/__init__.py @@ -104,6 +104,11 @@ class BaseTest: self._check_status(status, 200, check) return status, answer + def post(self, path, check=True, **args): + status, _, answer = self.request("POST", path, **args) + self._check_status(status, 200, check) + return status, answer + def put(self, path, data, check=True, **args): status, _, answer = self.request("PUT", path, data, **args) self._check_status(status, 201, check) diff --git a/radicale/tests/custom/web.py b/radicale/tests/custom/web.py index ff45feb..7d11902 100644 --- a/radicale/tests/custom/web.py +++ b/radicale/tests/custom/web.py @@ -27,3 +27,6 @@ from radicale import web class Web(web.BaseWeb): def get(self, environ, base_prefix, path, user): return client.OK, {"Content-Type": "text/plain"}, "custom" + + def post(self, environ, base_prefix, path, user): + return client.OK, {"Content-Type": "text/plain"}, "custom post" diff --git a/radicale/tests/test_web.py b/radicale/tests/test_web.py index 578897a..f5fed54 100644 --- a/radicale/tests/test_web.py +++ b/radicale/tests/test_web.py @@ -63,3 +63,6 @@ class TestBaseWebRequests(BaseTest): self.application = Application(self.configuration) _, answer = self.get("/.web") assert answer == "custom" + + _, answer = self.post("/.web") + assert answer == "custom post" diff --git a/radicale/web/__init__.py b/radicale/web/__init__.py index 0a40285..6d87833 100644 --- a/radicale/web/__init__.py +++ b/radicale/web/__init__.py @@ -21,7 +21,7 @@ Take a look at the class ``BaseWeb`` if you want to implement your own. """ -from radicale import utils +from radicale import httputils, utils INTERNAL_TYPES = ("none", "internal") @@ -52,4 +52,16 @@ class BaseWeb: ``user`` is empty for anonymous users. """ - raise NotImplementedError + return httputils.METHOD_NOT_ALLOWED + + def post(self, environ, base_prefix, path, user): + """POST request. + + ``base_prefix`` is sanitized and never ends with "/". + + ``path`` is sanitized and always starts with "/.web" + + ``user`` is empty for anonymous users. + + """ + return httputils.METHOD_NOT_ALLOWED