From 2bcf51a7c3788a6fe592f03bffa2a0a11a72974e Mon Sep 17 00:00:00 2001 From: Rex Zhang Date: Tue, 19 Mar 2024 15:45:13 +0800 Subject: [PATCH 1/3] feat: inside page[WIP] --- asgi_webdav/dev/dev.py | 2 +- asgi_webdav/page/__init__.py | 0 asgi_webdav/page/base.py | 73 ++++++++++++++++++++++++++++++++++++ asgi_webdav/page/inside.py | 42 +++++++++++++++++++++ asgi_webdav/server.py | 21 ++++------- asgi_webdav/web_page.py | 41 -------------------- build_docker_and_test.sh | 1 + requirements/standalone.txt | 3 ++ 8 files changed, 127 insertions(+), 56 deletions(-) create mode 100644 asgi_webdav/page/__init__.py create mode 100644 asgi_webdav/page/base.py create mode 100644 asgi_webdav/page/inside.py delete mode 100644 asgi_webdav/web_page.py diff --git a/asgi_webdav/dev/dev.py b/asgi_webdav/dev/dev.py index 29550304..ef3bc302 100644 --- a/asgi_webdav/dev/dev.py +++ b/asgi_webdav/dev/dev.py @@ -78,7 +78,7 @@ # "enable_dir_browser": False, "logging": { "level": "DEBUG", # for debug - } + }, # "sentry_dsn": "http://public@127.0.0.1:5000/1", } diff --git a/asgi_webdav/page/__init__.py b/asgi_webdav/page/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/asgi_webdav/page/base.py b/asgi_webdav/page/base.py new file mode 100644 index 00000000..998a9be2 --- /dev/null +++ b/asgi_webdav/page/base.py @@ -0,0 +1,73 @@ +from collections.abc import Callable +from dataclasses import dataclass + +from asgi_webdav.request import DAVRequest +from asgi_webdav.response import DAVResponse + + +@dataclass +class PageURL: + path: str + view: Callable + require_user: bool = True + require_admin: bool = False + + +class PageResponseBase: + pass + + +class PageResponseHTML(PageResponseBase): + pass + + +class PageResponseJSON(PageResponseBase): + pass + + +class PageResponseRedirect(PageResponseBase): + pass + + +""" +HttpResponseBadRequest +HttpResponseNotAllowed +HttpResponseNotFound +HttpResponseForbidden +HttpResponseServerError +""" + + +class PageView: + async def __call__(self, request: DAVRequest) -> DAVResponse: + status, content = self._get_response(request) + return DAVResponse(status=status, content=content.encode("utf-8")) + + def _get_response(self, request: DAVRequest) -> (int, str): + raise NotImplementedError() + + +class PageTemplateView(PageView): + pass + + +class PageEntry: + urls: list[PageURL] + + def __init__(self): + self._data = dict() + for url_obj in self.urls: + self._data[url_obj.path] = url_obj + + async def enter(self, request: DAVRequest) -> DAVResponse: + url_obj = self._data.get(request.path.raw) + if url_obj is None: + return DAVResponse(404) + + if url_obj.require_user and request.user is None: + return DAVResponse(403, content=b"Requires user privileges") + + if url_obj.require_admin and not request.user.admin: + return DAVResponse(403, content=b"Requires administrator privileges") + + return await url_obj.view(request) diff --git a/asgi_webdav/page/inside.py b/asgi_webdav/page/inside.py new file mode 100644 index 00000000..07784466 --- /dev/null +++ b/asgi_webdav/page/inside.py @@ -0,0 +1,42 @@ +from jinja2 import DictLoader, Environment + +from asgi_webdav.log import get_log_messages +from asgi_webdav.page.base import PageEntry, PageURL, PageView +from asgi_webdav.request import DAVRequest + +jinja_template_mapping = { + "info.html": """System Info: +Session Info: +User: {{ username }} +""" +} +env = Environment(loader=DictLoader(jinja_template_mapping)) + + +class IndexView(PageView): + def _get_response(self, request: DAVRequest) -> (int, str): + return 200, 'Logging page' + + +class InfoView(PageView): + def _get_response(self, request: DAVRequest) -> (int, str): + return 200, env.get_template("info.html").render(username=request.user.username) + + +class LoggingView(PageView): + def _get_response(self, request: DAVRequest) -> (int, str): + data = "" + for message in get_log_messages(): + data += message + "
" + return 200, data + + +urls = [ + PageURL("/_", IndexView()), + PageURL("/_/info", InfoView(), require_admin=True), + PageURL("/_/admin/logging", LoggingView()), +] + + +class InsidePage(PageEntry): + urls = urls diff --git a/asgi_webdav/server.py b/asgi_webdav/server.py index fd71de61..e815aa82 100644 --- a/asgi_webdav/server.py +++ b/asgi_webdav/server.py @@ -14,14 +14,14 @@ init_config_from_file, init_config_from_obj, ) -from asgi_webdav.constants import AppEntryParameters, ASGIScope, DAVMethod, DevMode +from asgi_webdav.constants import AppEntryParameters, ASGIScope, DevMode from asgi_webdav.exception import DAVExceptionProviderInitFailed from asgi_webdav.log import get_dav_logging_config from asgi_webdav.middleware.cors import ASGIMiddlewareCORS +from asgi_webdav.page.inside import InsidePage from asgi_webdav.request import DAVRequest from asgi_webdav.response import DAVResponse from asgi_webdav.web_dav import WebDAV -from asgi_webdav.web_page import WebPage logger = getLogger(__name__) @@ -41,7 +41,7 @@ def __init__(self, config: Config): logger.info(_service_abnormal_exit_message) sys.exit(1) - self.web_page = WebPage() + self.inside_page = InsidePage() async def __call__(self, scope: ASGIScope, receive, send) -> None: request, response = await self.handle(scope, receive, send) @@ -70,18 +70,11 @@ async def handle( logger.debug(request) return request, self.dav_auth.create_response_401(request, message) - # process Admin request - if ( - request.method == DAVMethod.GET - and request.src_path.count >= 1 - and request.src_path.parts[0] == "_" - ): + # route to inside page + if request.src_path.count >= 1 and request.src_path.parts[0] == "_": # route /_ - status, data = await self.web_page.enter(request) - return request, DAVResponse( - status=status, - content=data.encode("utf-8"), - ) + response = await self.inside_page.enter(request) + return request, response # process WebDAV request try: diff --git a/asgi_webdav/web_page.py b/asgi_webdav/web_page.py deleted file mode 100644 index 469a6b98..00000000 --- a/asgi_webdav/web_page.py +++ /dev/null @@ -1,41 +0,0 @@ -from asgi_webdav.log import get_log_messages -from asgi_webdav.request import DAVRequest - - -class WebPage: - async def enter(self, request: DAVRequest): - if request.path.count <= 2: - # route - # /_ - # /_/admin - # /_/??? - return 200, self.get_index_page() - - # request.path.count > 2 - if not request.user.admin: - return 403, "Requires administrator privileges" - - if request.path.parts[1] != "admin": - return 404, "" - - # request.path == "/_/admin/???" - if request.path.parts[2] == "logging": - # route /_/admin/logging - status, data = await self.get_logging_page() - - else: - status, data = 500, "something wrong" - - return status, data - - @staticmethod - def get_index_page() -> str: - return 'Logging page' - - @staticmethod - async def get_logging_page() -> (int, str): - # return 200, "this is page /_/admin/logs" - data = "" - for message in get_log_messages(): - data += message + "
" - return 200, data diff --git a/build_docker_and_test.sh b/build_docker_and_test.sh index 1ab8814b..c491d37e 100755 --- a/build_docker_and_test.sh +++ b/build_docker_and_test.sh @@ -17,5 +17,6 @@ docker run -dit --restart unless-stopped \ -e DEBUG=true \ -e WEBDAV_LOGGING_LEVEL=DEBUG \ --name asgi-webdav cr.h.rexzhang.com/ray1ex/asgi-webdav + docker image prune -f docker container logs -f asgi-webdav diff --git a/requirements/standalone.txt b/requirements/standalone.txt index 1c982f05..94caa2fd 100644 --- a/requirements/standalone.txt +++ b/requirements/standalone.txt @@ -6,3 +6,6 @@ httptools # response compress brotli # c runtime + +# inside page +jinja2 From 747d077ffa30c783cb6b2abc41f8e20805a0a2ed Mon Sep 17 00:00:00 2001 From: Rex Zhang Date: Tue, 19 Mar 2024 16:39:47 +0800 Subject: [PATCH 2/3] Update pre-commit --- .pre-commit-config.yaml | 4 ++-- requirements/test.txt | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b373a4aa..ae27474a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,11 +1,11 @@ repos: - repo: https://github.com/asottile/pyupgrade - rev: v3.15.0 + rev: v3.15.1 hooks: - id: pyupgrade args: [ --py310-plus ] - repo: https://github.com/psf/black - rev: 24.1.0 + rev: 24.3.0 hooks: - id: black args: [ "--target-version", "py310" ] diff --git a/requirements/test.txt b/requirements/test.txt index 30c565af..edeb1739 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -3,5 +3,3 @@ pytest-asyncio pytest-cov icecream requests - -coveralls From 783c479b4ae1c1e600cce93c4de2d30c65c40d4b Mon Sep 17 00:00:00 2001 From: Rex Zhang Date: Tue, 19 Mar 2024 17:14:14 +0800 Subject: [PATCH 3/3] Add flake8 into pre-commit --- .pre-commit-config.yaml | 6 +- asgi_webdav/provider/dev_provider.py | 206 +++++++++++++-------------- asgi_webdav/request.py | 8 +- setup.cfg | 6 +- tests/by_hand/test_auth.py | 2 +- 5 files changed, 116 insertions(+), 112 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ae27474a..58844338 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,4 +13,8 @@ repos: rev: 5.13.2 hooks: - id: isort - name: isort (python) \ No newline at end of file + name: isort (python) + - repo: https://github.com/PyCQA/flake8 + rev: 7.0.0 + hooks: + - id: flake8 \ No newline at end of file diff --git a/asgi_webdav/provider/dev_provider.py b/asgi_webdav/provider/dev_provider.py index eb3b1eda..6571f339 100644 --- a/asgi_webdav/provider/dev_provider.py +++ b/asgi_webdav/provider/dev_provider.py @@ -81,19 +81,19 @@ def _create_data_lock_discovery(lock_info: DAVLockInfo) -> dict: """ https://tools.ietf.org/html/rfc4918#page-35 9.1.1. PROPFIND Status Codes - + This section, as with similar sections for other methods, provides some guidance on error codes and preconditions or postconditions (defined in Section 16) that might be particularly useful with PROPFIND. - + 403 Forbidden - A server MAY reject PROPFIND requests on collections with depth header of "Infinity", in which case it SHOULD use this error with the precondition code 'propfind-finite-depth' inside the error body. - + 9.1.2. Status Codes for Use in 'propstat' Element - + In PROPFIND responses, information about individual properties_list is returned inside 'propstat' elements (see Section 14.22), each containing an individual 'status' element containing information @@ -101,30 +101,30 @@ def _create_data_lock_discovery(lock_info: DAVLockInfo) -> dict: most common status codes used inside 'propstat'; however, clients should be prepared to handle other 2/3/4/5xx series status codes as well. - + 200 OK - A property exists and/or its value is successfully returned. - + 401 Unauthorized - The property cannot be viewed without appropriate authorization. - + 403 Forbidden - The property cannot be viewed regardless of authentication. - + 404 Not Found - The property does not exist. - + https://www.rfc-editor.org/rfc/rfc4918#section-11 11. Status Code Extensions to HTTP/1.1 - + The following status codes are added to those defined in HTTP/1.1 [RFC2616]. - + 11.1. 207 Multi-Status - + The 207 (Multi-Status) status code provides status for multiple independent operations (see Section 13 for more information). - + 11.2. 422 Unprocessable Entity - + The 422 (Unprocessable Entity) status code means the server understands the content type of the request entity (hence a 415(Unsupported Media Type) status code is inappropriate), and the @@ -133,24 +133,24 @@ def _create_data_lock_discovery(lock_info: DAVLockInfo) -> dict: instructions. For example, this error condition may occur if an XML request body contains well-formed (i.e., syntactically correct), but semantically erroneous, XML instructions. - + 11.3. 423 Locked - + The 423 (Locked) status code means the source or destination resource of a method is locked. This response SHOULD contain an appropriate precondition or postcondition code, such as 'lock-token-submitted' or 'no-conflicting-lock'. - - 11.4. 424 Failed Dependency + + 11.4. 424 Failed Dependency The 424 (Failed Dependency) status code means that the method could not be performed on the resource because the requested action depended on another action and that action failed. For example, if a command in a PROPPATCH method fails, then, at minimum, the rest of the commands will also fail with 424 (Failed Dependency). - + 11.5. 507 Insufficient Storage - + The 507 (Insufficient Storage) status code means the method could not be performed on the resource because the server is unable to store the representation needed to successfully complete the request. This @@ -265,7 +265,7 @@ async def create_propfind_response( https://tools.ietf.org/html/rfc4918#page-44 9.2. PROPPATCH Method 9.2.1. Status Codes for Use in 'propstat' Element - + In PROPPATCH responses, information about individual properties_list is returned inside 'propstat' elements (see Section 14.22), each containing an individual 'status' element containing information @@ -273,25 +273,25 @@ async def create_propfind_response( most common status codes used inside 'propstat'; however, clients should be prepared to handle other 2/3/4/5xx series status codes as well. - + 200 (OK) - The property set or change succeeded. Note that if this appears for one property, it appears for every property in the response, due to the atomicity of PROPPATCH. - + 403 (Forbidden) - The client, for reasons the server chooses not to specify, cannot alter one of the properties_list. - + 403 (Forbidden): The client has attempted to set a protected property, such as DAV:getetag. If returning this error, the server SHOULD use the precondition code 'cannot-modify-protected-property' inside the response body. - + 409 (Conflict) - The client has provided a value whose semantics are not appropriate for the property. - + 424 (Failed Dependency) - The property change could not be made because of another property change that failed. - + 507 (Insufficient Storage) - The server did not have sufficient space to record the property. """ @@ -346,29 +346,29 @@ def _create_proppatch_response( """ https://tools.ietf.org/html/rfc4918#page-46 9.3.1. MKCOL Status Codes - + In addition to the general status codes possible, the following status codes have specific applicability to MKCOL: - + 201 (Created) - The collection was created. - + 403 (Forbidden) - This indicates at least one of two conditions: 1) the server does not allow the creation of collections at the given location in its URL namespace, or 2) the parent collection of the Request-URI exists but cannot accept members. - + 405 (Method Not Allowed) - MKCOL can only be executed on an unmapped URL. - + 409 (Conflict) - A collection cannot be made at the Request-URI until one or more intermediate collections have been created. The server MUST NOT create those intermediate collections automatically. - + 415 (Unsupported Media Type) - The server does not support the request body type (although bodies are legal on MKCOL requests, since this specification doesn't define any, the server is likely not to support any given body type). - + 507 (Insufficient Storage) - The resource does not have sufficient space to record the state of the resource after the execution of this method. @@ -393,7 +393,7 @@ async def _do_mkcol(self, request: DAVRequest) -> int: """ https://tools.ietf.org/html/rfc4918#page-48 9.4. GET, HEAD for Collections - + The semantics of GET are unchanged when applied to a collection, since GET is defined as, "retrieve whatever information (in the form of an entity) is identified by the Request-URI" [RFC2616]. GET, when @@ -402,20 +402,20 @@ async def _do_mkcol(self, request: DAVRequest) -> int: something else altogether. Hence, it is possible that the result of a GET on a collection will bear no correlation to the membership of the collection. - + Similarly, since the definition of HEAD is a GET without a response message body, the semantics of HEAD are unmodified when applied to collection resources. - + https://tools.ietf.org/html/rfc2616#page-53 9.3 GET - + The GET method means retrieve whatever information (in the form of an entity) is identified by the Request-URI. If the Request-URI refers to a data-producing process, it is the produced data which shall be returned as the entity in the response and not the source text of the process, unless that text happens to be the output of the process. - + The semantics of the GET method change to a "conditional GET" if the request message includes an If-Modified-Since, If-Unmodified-Since, If-Match, If-None-Match, or If-Range header field. A conditional GET @@ -424,19 +424,19 @@ async def _do_mkcol(self, request: DAVRequest) -> int: conditional GET method is intended to reduce unnecessary network usage by allowing cached entities to be refreshed without requiring multiple requests or transferring data already held by the client. - + The semantics of the GET method change to a "partial GET" if the request message includes a Range header field. A partial GET requests that only part of the entity be transferred, as described in section 14.35. The partial GET method is intended to reduce unnecessary network usage by allowing partially-retrieved entities to be completed without transferring data already held by the client. - + The response to a GET request is cacheable if and only if it meets the requirements for HTTP caching described in section 13. - + See section 15.1.3 for security considerations when used for forms. - + https://datatracker.ietf.org/doc/html/rfc7233#section-4.2 4.2. Content-Range @@ -446,19 +446,19 @@ async def _do_mkcol(self, request: DAVRequest) -> int: part of a multipart 206 response to indicate the range enclosed within each body part, and sent in 416 (Range Not Satisfiable) responses to provide information about the selected representation. - + Content-Range = byte-content-range / other-content-range - + byte-content-range = bytes-unit SP ( byte-range-resp / unsatisfied-range ) - + byte-range-resp = byte-range "/" ( complete-length / "*" ) byte-range = first-byte-pos "-" last-byte-pos unsatisfied-range = "*/" complete-length - + complete-length = 1*DIGIT - + other-content-range = other-range-unit SP other-range-resp other-range-resp = *CHAR """ @@ -502,41 +502,41 @@ async def _do_head( """ https://tools.ietf.org/html/rfc4918#page-48 9.6. DELETE Requirements - + DELETE is defined in [RFC2616], Section 9.7, to "delete the resource identified by the Request-URI". However, WebDAV changes some DELETE handling requirements. - + A server processing a successful DELETE request: - + MUST destroy locks rooted on the deleted resource - + MUST remove the mapping from the Request-URI to any resource. - + Thus, after a successful DELETE operation (and in the absence of other actions), a subsequent GET/HEAD/PROPFIND request to the target Request-URI MUST return 404 (Not Found). - + 9.6.1. DELETE for Collections - + The DELETE method on a collection MUST act as if a "Depth: infinity" header was used on it. A client MUST NOT submit a Depth header with a DELETE on a collection with any value but infinity. - + DELETE instructs that the collection specified in the Request-URI and all resources identified by its internal member URLs are to be deleted. - + If any resource identified by a member URL cannot be deleted, then all of the member's ancestors MUST NOT be deleted, so as to maintain URL namespace consistency. - + Any headers included with DELETE MUST be applied in processing every resource to be deleted. - + When the DELETE method has completed processing, it MUST result in a consistent URL namespace. - + If an error occurs deleting a member resource (a resource other than the resource identified in the Request-URI), then the response can be a 207 (Multi-Status). Multi-Status is used here to indicate which @@ -544,10 +544,10 @@ async def _do_head( which should help the client understand which resources caused the failure. For example, the Multi-Status body could include a response with status 423 (Locked) if an internal resource was locked. - + The server MAY return a 4xx status response, rather than a 207, if the request failed completely. - + 424 (Failed Dependency) status codes SHOULD NOT be in the 207 (Multi- Status) response for DELETE. They can be safely left out because the client will know that the ancestors of a resource could not be @@ -555,10 +555,10 @@ async def _do_head( Additionally, 204 (No Content) errors SHOULD NOT be returned in the 207 (Multi-Status). The reason for this prohibition is that 204 (No Content) is the default success code. - + https://tools.ietf.org/html/rfc2616#section-9.7 9.7 DELETE - + The DELETE method requests that the origin server delete the resource identified by the Request-URI. This method MAY be overridden by human intervention (or other means) on the origin server. The client cannot @@ -568,12 +568,12 @@ async def _do_head( indicate success unless, at the time the response is given, it intends to delete the resource or move it to an inaccessible location. - + A successful response SHOULD be 200 (OK) if the response includes an entity describing the status, 202 (Accepted) if the action has not yet been enacted, or 204 (No Content) if the action has been enacted but the response does not include an entity. - + If the request passes through a cache and the Request-URI identifies one or more currently cached entities, those entries SHOULD be treated as stale. Responses to this method are not cacheable. @@ -598,27 +598,27 @@ async def _do_delete(self, request: DAVRequest) -> int: """ https://tools.ietf.org/html/rfc4918#page-50 9.7. PUT Requirements - + 9.7.1. PUT for Non-Collection Resources - + A PUT performed on an existing resource replaces the GET response entity of the resource. Properties defined on the resource may be recomputed during PUT processing but are not otherwise affected. For example, if a server recognizes the content type of the request body, it may be able to automatically extract information that could be profitably exposed as properties_list. - + A PUT that would result in the creation of a resource without an appropriately scoped parent collection MUST fail with a 409 (Conflict). - + A PUT request allows a client to indicate what media type an entity body has, and whether it should change if overwritten. Thus, a client SHOULD provide a Content-Type for a new resource if any is known. If the client does not provide a Content-Type for a new resource, the server MAY create a resource with no Content-Type assigned, or it MAY attempt to assign a Content-Type. - + Note that although a recipient ought generally to treat metadata supplied with an HTTP request as authoritative, in practice there's no guarantee that a server will accept client-supplied metadata @@ -627,13 +627,13 @@ async def _do_delete(self, request: DAVRequest) -> int: the first place. Thus, clients can't always rely on the ability to directly influence the content type by including a Content-Type request header. - + 9.7.2. PUT for Collections - + This specification does not define the behavior of the PUT method for existing collections. A PUT request to an existing collection MAY be treated as an error (405 Method Not Allowed). - + The MKCOL method is defined to create collections. """ @@ -671,16 +671,16 @@ async def _do_get_etag(self, request: DAVRequest) -> str: https://tools.ietf.org/html/rfc4918#page-51 9.8. COPY Method 9.8.5. Status Codes - + In addition to the general status codes possible, the following status codes have specific applicability to COPY: - + 201 (Created) - The source resource was successfully copied. The COPY operation resulted in the creation of a new resource. - + 204 (No Content) - The source resource was successfully copied to a preexisting destination resource. - + 207 (Multi-Status) - Multiple resources were to be affected by the COPY, but errors on some of them prevented the operation from taking place. Specific error messages, together with the most appropriate @@ -688,29 +688,29 @@ async def _do_get_etag(self, request: DAVRequest) -> str: status response. For example, if a destination resource was locked and could not be overwritten, then the destination resource URL appears with the 423 (Locked) status. - + 403 (Forbidden) - The operation is forbidden. A special case for COPY could be that the source and destination resources are the same resource. - + 409 (Conflict) - A resource cannot be created at the destination until one or more intermediate collections have been created. The server MUST NOT create those intermediate collections automatically. - + 412 (Precondition Failed) - A precondition header check failed, e.g., the Overwrite header is "F" and the destination URL is already mapped to a resource. - + 423 (Locked) - The destination resource, or resource within the destination collection, was locked. This response SHOULD contain the 'lock-token-submitted' precondition element. - + 502 (Bad Gateway) - This may occur when the destination is on another server, repository, or URL namespace. Either the source namespace does not support copying to the destination namespace, or the destination namespace refuses to accept the resource. The client may wish to try GET/PUT and PROPFIND/PROPPATCH instead. - + 507 (Insufficient Storage) - The destination resource does not have sufficient space to record the state of the resource after the execution of this method. @@ -740,16 +740,16 @@ async def _do_copy(self, request: DAVRequest) -> int: https://tools.ietf.org/html/rfc4918#page-56 9.9. MOVE Method 9.9.4. Status Codes - + In addition to the general status codes possible, the following status codes have specific applicability to MOVE: - + 201 (Created) - The source resource was successfully moved, and a new URL mapping was created at the destination. - + 204 (No Content) - The source resource was successfully moved to a URL that was already mapped. - + 207 (Multi-Status) - Multiple resources were to be affected by the MOVE, but errors on some of them prevented the operation from taking place. Specific error messages, together with the most appropriate @@ -757,27 +757,27 @@ async def _do_copy(self, request: DAVRequest) -> int: status response. For example, if a source resource was locked and could not be moved, then the source resource URL appears with the 423 (Locked) status. - + 403 (Forbidden) - Among many possible reasons for forbidding a MOVE operation, this status code is recommended for use when the source and destination resources are the same. - + 409 (Conflict) - A resource cannot be created at the destination until one or more intermediate collections have been created. The server MUST NOT create those intermediate collections automatically. Or, the server was unable to preserve the behavior of the live properties_list and still move the resource to the destination (see 'preserved-live-properties_list' postcondition). - + 412 (Precondition Failed) - A condition header failed. Specific to MOVE, this could mean that the Overwrite header is "F" and the destination URL is already mapped to a resource. - + 423 (Locked) - The source or the destination resource, the source or destination resource parent, or some resource within the source or destination collection, was locked. This response SHOULD contain the 'lock-token-submitted' precondition element. - + 502 (Bad Gateway) - This may occur when the destination is on another server and the destination server refuses to accept the resource. This could also occur when the destination is on another sub-section @@ -808,13 +808,13 @@ async def _do_move(self, request: DAVRequest) -> int: https://tools.ietf.org/html/rfc4918#page-61 9.10. LOCK Method 9.10.6. LOCK Responses - + In addition to the general status codes possible, the following status codes have specific applicability to LOCK: - + 200 (OK) - The LOCK request succeeded and the value of the DAV: lockdiscovery property is included in the response body. - + 201 (Created) - The LOCK request was to an unmapped URL, the request succeeded and resulted in the creation of a new resource, and the value of the DAV:lockdiscovery property is included in the response @@ -823,11 +823,11 @@ async def _do_move(self, request: DAVRequest) -> int: 409 (Conflict) - A resource cannot be created at the destination until one or more intermediate collections have been created. The server MUST NOT create those intermediate collections automatically. - + 423 (Locked), potentially with 'no-conflicting-lock' precondition code - There is already a lock on the resource that is not compatible with the requested lock (see lock compatibility table above). - + 412 (Precondition Failed), with 'lock-token-matches-request-uri' precondition code - The LOCK request was made with an If header, indicating that the client wishes to refresh the given lock. @@ -883,19 +883,19 @@ def _create_lock_response(self, lock_info: DAVLockInfo) -> bytes: https://tools.ietf.org/html/rfc4918#page-68 9.11. UNLOCK Method 9.11.1. Status Codes - + In addition to the general status codes possible, the following status codes have specific applicability to UNLOCK: - + 204 (No Content) - Normal success response (rather than 200 OK, since 200 OK would imply a response body, and an UNLOCK success response does not normally contain a body). - + 400 (Bad Request) - No lock token was provided. - + 403 (Forbidden) - The currently authenticated principal does not have permission to remove the lock. - + 409 (Conflict), with 'lock-token-matches-request-uri' precondition - The resource was not locked, or the request was made to a Request-URI that was not within the scope of the lock. diff --git a/asgi_webdav/request.py b/asgi_webdav/request.py index 250043b3..6971d399 100644 --- a/asgi_webdav/request.py +++ b/asgi_webdav/request.py @@ -162,22 +162,22 @@ def __post_init__(self): """ https://tools.ietf.org/html/rfc4918#page-78 10.7. Timeout Request Header - + TimeOut = "Timeout" ":" 1#TimeType TimeType = ("Second-" DAVTimeOutVal | "Infinite") ; No LWS allowed within TimeType DAVTimeOutVal = 1*DIGIT - + Clients MAY include Timeout request headers in their LOCK requests. However, the server is not required to honor or even consider these requests. Clients MUST NOT submit a Timeout request header with any method other than a LOCK method. - + The "Second" TimeType specifies the number of seconds that will elapse between granting of the lock at the server, and the automatic removal of the lock. The timeout value for TimeType "Second" MUST NOT be greater than 2^32-1. - + See Section 6.6 for a description of lock timeout behavior. """ timeout = self.headers.get(b"timeout") diff --git a/setup.cfg b/setup.cfg index 75d3865f..3f7a9156 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [flake8] # https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#flake8 -# W293 blank line contains whitespace # E203 Whitespace before ':' -max-line-length = 88 -extend-ignore = E203, E704 +# E704 Multiple statements on one line (def) +# E501 line too long +extend-ignore = E203, E704, E501 diff --git a/tests/by_hand/test_auth.py b/tests/by_hand/test_auth.py index f6cf08ed..aa0f79e1 100644 --- a/tests/by_hand/test_auth.py +++ b/tests/by_hand/test_auth.py @@ -14,7 +14,7 @@ class Connect: method: str path: str - headers: Optional[dict[str, str]] + headers: dict[str, str] | None auth: AuthBase