Skip to content

fix(webdav): require credentials and apply the property permission result - #37179

Open
swicken wants to merge 2 commits into
mainfrom
issue-672-webdav-request-authorization
Open

fix(webdav): require credentials and apply the property permission result#37179
swicken wants to merge 2 commits into
mainfrom
issue-672-webdav-request-authorization

Conversation

@swicken

@swicken swicken commented Aug 24, 2026

Copy link
Copy Markdown
Member

Summary

The WebDAV endpoints are served by a third-party library whose method handlers do not all
consult Resource.authorise(...). Two things follow from that, both measured against a running
instance.

A request carrying no Authorization header is handled by the handlers that run their own
sequence instead of the shared one. Anonymous PROPPATCH answered 207, anonymous OPTIONS
answered 200 on every /webdav/ mount point, and anonymous LOCK on a legacy language path
answered 500. WebDAV in dotCMS resolves every resource against a user, so there is nothing on
these endpoints to serve a caller who has not identified themselves. This adds a filter on
/webdav/* that answers those with the RFC 7235 challenge instead.

Separately, the permission check for PROPPATCH reports one problem per property named in the
request, so a body naming none reports an empty list of problems, which the caller reads as no
problem: the resource's answer is obtained and then discarded. WebdavPropertyAuthoriser reports
the refusal either way, and DotWebdavServlet installs it through the library's own extension
point, which reaches both handlers that check property permissions.

Behaviour

  • A request to /webdav/* with no Authorization header, or a blank one, gets 401 with
    WWW-Authenticate: Basic. Every method, whatever the body.
  • The status is set rather than raised as a container error, because web.xml maps 401 to
    /html/error/custom-error-page.jsp, which redirects, and a WebDAV client has nothing it can do
    with a 302 to a login page.
  • OPTIONS is not exempt. Being challenged on the first request is how a WebDAV client learns to
    send credentials at all.
  • Refusals go to the security log, alongside comparable events such as a back-end URL requested
    without a session.
  • Authenticated traffic is unchanged: PROPFIND with and without a body, OPTIONS, MKCOL,
    PUT, GET, PROPPATCH, LOCK and DELETE all behave as they did before.
  • A PROPPATCH whose body names no property is now refused for a caller holding no permission on
    the resource, instead of answering 207.

Merge order with #37167

Both branches add a <filter> and a <filter-mapping> immediately after NormalizationFilter,
so web.xml will conflict. #37167 should land first. When this branch is rebased, keep both
filters and map WebDavAuthenticationFilter before WebDavXmlValidationFilter: there is no
reason to buffer and parse a body for a caller who is about to be refused. The <servlet-class>
change here is in a part of the file #37167 does not touch and merges cleanly.

Two filters on the same path is deliberate. One asks whether there is a caller at all, by reading
a single header; the other asks whether the body is shaped the way RFC 4918 says, and has to
buffer and parse to answer. Merging them would put an XML parse behind a credentials check in one
class for no gain.

Testing

11 unit tests. WebDavAuthenticationFilterTest (6) covers the challenge and its header, every
WebDAV method, a blank Authorization header, non-HTTP pass-through, and pass-through for a
request that does carry credentials. WebdavPropertyAuthoriserTest (5) covers refusal when no
property was named, when the name set is null, one refusal per named property, and the allowed
case reporting nothing.

Against a running instance, before and after the change: anonymous PROPPATCH on three mount
points with three different no-property bodies went 207401, anonymous OPTIONS 200
401, anonymous LOCK on the legacy language path 500401, and nine functional checks
covering the traffic a real client sends are unchanged. Each guard was also verified by reverting
it and confirming the tests fail.

Not covered: no real desktop client was tested. Finder, Explorer and Office now meet a challenge
on OPTIONS where they previously got a 200. That is the standard handshake and every client
implements it, but it is reasoning rather than a measurement. There is also no CI coverage for
these methods — the WebDav Postman collection sends Basic auth on every request, so it
exercises the authenticated path only.

Closes dotCMS/private-issues#672

…rmission answer

The WebDAV endpoints are served by a third-party library whose method handlers do
not all consult Resource.authorise(). Two consequences, both measured against a
running instance:

A request carrying no Authorization header was handled by the handlers that run
their own sequence rather than the shared one. Anonymous PROPPATCH answered 207,
anonymous OPTIONS answered 200 on every /webdav/ mount, and anonymous LOCK on a
legacy language path answered 500. WebDavAuthenticationFilter answers those with
the RFC 7235 challenge instead, for every method and whatever the body. The
status is set rather than raised as a container error, because web.xml maps 401
to an error page that redirects and a WebDAV client cannot act on a 302.
Refusals go to the security log alongside comparable events.

PROPPATCH's permission check reported one problem per property named in the
request, so a body naming none reported an empty list of problems, which the
caller reads as no problem: the resource's answer was obtained and then
discarded. WebdavPropertyAuthoriser reports the refusal either way, and
DotWebdavServlet installs it, which reaches both handlers that check property
permissions.

Closes dotCMS/private-issues#672
@swicken swicken added the Area : Backend PR changes Java/Maven backend code label Aug 24, 2026
@claude

claude Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Claude finished @swicken's task in 2m 52s —— View job


Code Review

  • Read changed files
  • Analyze for bugs, security, conventions
  • Post review

I reviewed the diff against origin/main. This is a well-scoped, well-documented change. Two defensive fixes: a /webdav/* filter that challenges credential-less requests, and a PropertyAuthoriser that reports a refusal even for a property-less body.

Findings

No blocking issues. Verified the parts I could reach in-repo:

  • WebDavAuthenticationFilter uses UtilMethods.isSet(String) (UtilMethods.java:360), which trims — so "" and " " correctly resolve to "no credentials." The anEmptyAuthorizationHeaderIsNotCredentials test is sound.
  • The /webdav/* mapping covers all four servlet mounts (autopub, nonpub, live, working at web.xml:527–541). Nothing else is mapped outside /webdav/, so there's no uncovered mount point.
  • Filter-mapping placement (web.xml:158) is second, right after NormalizationFilter and before all other /* filters, so it runs early as intended. When credentials are present it passes through untouched, so downstream filters are unaffected.
  • setStatus (not sendError) is the right call given the 401 → custom-error-page.jsp redirect mapping — a WebDAV client can't act on a 302. Good rationale, and it's tested.
  • Only method/URI/remote-addr are logged (WebDavAuthenticationFilter.java:74–76) — no credentials leak into the security log.
  • WebdavPropertyAuthoriser returns null on the clean path (matching the library's "no problems" contract) and substitutes NO_PROPERTY_NAMED only when fields is null/empty, so a refusal can never come back as an empty set.

🟡 Medium (non-blocking, verification note)

  • DotWebdavServlet.java:22httpManager.setPropertyPermissionService(...) and the inherited httpManager field. I could not inspect milton-servlet/milton-api 1.8.1.4 (the sandbox blocks ~/.m2), so I can't independently confirm that this method name wires the authoriser into both the PROPPATCH and PROPFIND handlers as the javadoc at line 21 claims, nor that the field is accessible from a subclass.
    • Assumption: the method exists, is public, and both handlers consult the registered PropertyAuthoriser.
    • What to verify: it's compile-checked (build would fail otherwise) and the PR reports live testing of PROPPATCH, so this is low-risk — but there is no unit/integration test asserting the authoriser is actually installed and reached by PROPFIND (only WebdavPropertyAuthoriser in isolation is tested). If PROPFIND wiring regresses, nothing in CI would catch it. Consider a small note in the PR confirming PROPFIND was exercised live, or an integration test if practical.

Notes (no action required)

  • The PR body already flags the web.xml merge conflict with fix(webdav): reject document type declarations in WebDAV XML request bodies #37167 and the absence of CI coverage for anonymous WebDAV methods (the WebDav Postman collection always sends Basic auth). Both are acknowledged; nothing to add.
  • Test coverage for the two new units is thorough (challenge + header, every method, blank header, null/empty field sets, per-property refusal, allow path, non-HTTP pass-through).

· branch issue-672-webdav-request-authorization

The filter decides on one header, so the three bodies were three identical
runs of the test above it, and the name pointed the reader at the authoriser's
behaviour rather than the filter's. WebdavPropertyAuthoriserTest covers the
case a body naming no property actually exercises.
@swicken
swicken enabled auto-merge August 24, 2026 21:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI: Safe To Rollback Area : Backend PR changes Java/Maven backend code

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants